From 461af95c312fc7aff85551c014fb93dc7a89cecd Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sun, 10 Nov 2019 16:28:48 -0500 Subject: first commit --- src/client/util/SettingsManager.tsx | 58 ++++++++++++++++++++++ src/client/views/MainView.tsx | 5 ++ .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/client/util/SettingsManager.tsx (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx new file mode 100644 index 000000000..5bcfd2449 --- /dev/null +++ b/src/client/util/SettingsManager.tsx @@ -0,0 +1,58 @@ +import { observable, runInAction, action } from "mobx"; +import * as React from "react"; +import MainViewModal from "../views/MainViewModal"; +import { observer } from "mobx-react"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import * as fa from '@fortawesome/free-solid-svg-icons'; +import { DocumentView } from "../views/nodes/DocumentView"; +import { SelectionManager } from "./SelectionManager"; + +library.add(fa.faCopy); + +@observer +export default class SettingsManager extends React.Component<{}> { + public static Instance: SettingsManager; + @observable private isOpen = false; + @observable private dialogueBoxOpacity = 1; + @observable private overlayOpacity = 0.4; + + public open = action(() => { + SelectionManager.DeselectAll(); + this.isOpen = true; + console.log('oppin'); + }); + + // public open = (target: DocumentView) => { + // SelectionManager.DeselectAll(); + // } + + public close = action(() => { + this.isOpen = false; + }); + + constructor(props: {}) { + super(props); + SettingsManager.Instance = this; + } + + private get settingsInterface() { + return ( +
+

sdfsldkfhlksdjf

+
+ ); + } + + render() { + return ( + + ); + } + +} \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 773da05df..294e781b7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { PreviewCursor } from './PreviewCursor'; import { Scripting } from '../util/Scripting'; import { LinkManager } from '../util/LinkManager'; import { AudioBox } from './nodes/AudioBox'; +import SettingsManager from '../util/SettingsManager'; @observer export class MainView extends React.Component { @@ -412,6 +413,9 @@ export class MainView extends React.Component { zoomToScale={emptyFunction} getScale={returnOne}> + @@ -507,6 +511,7 @@ export class MainView extends React.Component { return (
+ {this.mainContent} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index a035bdc3d..83638b240 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -84,7 +84,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.dataProvider ? this.dataProvider.height : this.panelHeight(); render() { - trace(); + // trace(); return
(Docu } render() { if (!this.props.Document) return (null); - trace(); + // trace(); const animDims = this.Document.animateToDimensions ? Array.from(this.Document.animateToDimensions) : undefined; const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined; const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; -- cgit v1.2.3-70-g09d2 From 272bc257aba8bd2f1ac2e1f3248c482c4da7edcb Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 12 Nov 2019 17:22:42 -0500 Subject: fixed button issue --- src/client/util/SettingsManager.scss | 55 ++++++++++++++++++++++++++++++++++++ src/client/util/SettingsManager.tsx | 8 ++---- src/client/views/MainView.scss | 37 ++++++++++++++++++------ 3 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 src/client/util/SettingsManager.scss (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss new file mode 100644 index 000000000..2b3e455e0 --- /dev/null +++ b/src/client/util/SettingsManager.scss @@ -0,0 +1,55 @@ +.settings-interface { + display: flex; + flex-direction: column; + + .focus-span { + text-decoration: underline; + } + + p { + font-size: 20px; + text-align: left; + font-style: italic; + padding: 0; + margin: 0 0 20px 0; + } + + .container { + display: block; + position: relative; + margin-top: 10px; + margin-bottom: 10px; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 700px; + min-width: 700px; + max-width: 700px; + text-align: left; + font-style: normal; + font-size: 15; + font-weight: normal; + padding: 0; + + .padding { + padding: 0 0 0 20px; + color: black; + } + + .close-button { + border-radius: 5px; + margin-top: 20px; + padding: 10px 0; + background: aliceblue; + transition: 0.5s ease all; + border: 1px solid; + border-color: aliceblue; + } + + .close-button:hover { + border-color: black; + } + } +} \ No newline at end of file diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 5bcfd2449..4701c5f88 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -4,8 +4,8 @@ import MainViewModal from "../views/MainViewModal"; import { observer } from "mobx-react"; import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; -import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; +import "./SettingsManager.scss"; library.add(fa.faCopy); @@ -19,13 +19,8 @@ export default class SettingsManager extends React.Component<{}> { public open = action(() => { SelectionManager.DeselectAll(); this.isOpen = true; - console.log('oppin'); }); - // public open = (target: DocumentView) => { - // SelectionManager.DeselectAll(); - // } - public close = action(() => { this.isOpen = false; }); @@ -39,6 +34,7 @@ export default class SettingsManager extends React.Component<{}> { return (

sdfsldkfhlksdjf

+
Done
); } diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index a858a73c7..a940e6889 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -4,8 +4,9 @@ .mainView-tabButtons { position: relative; - width:100%; + width: 100%; } + // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; @@ -22,21 +23,25 @@ overflow: auto; z-index: 1; } + .mainView-mainContent { - width:100%; - height:100%; - position:absolute; + width: 100%; + height: 100%; + position: absolute; } -.mainView-flyoutContainer{ - display:flex; + +.mainView-flyoutContainer { + display: flex; flex-direction: column; position: absolute; - width:100%; - height:100%; + width: 100%; + height: 100%; + .documentView-node-topmost { background: lightgrey; } } + .mainView-mainDiv { width: 100%; height: 100%; @@ -46,6 +51,18 @@ overflow: hidden; } + +.mainView-settings { + position: absolute; + left: 0; + bottom: 0; + font-size: 8px; +} + +.mainView-settings:hover { + transform: none !important; +} + .mainView-logout { position: absolute; right: 0; @@ -53,6 +70,10 @@ font-size: 8px; } +.mainView-logout:hover { + transform: none !important; +} + .mainView-libraryFlyout { height: 100%; position: absolute; -- cgit v1.2.3-70-g09d2 From 79260cec351f1f76505c44c338222082dc4a666e Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 12 Nov 2019 18:54:17 -0500 Subject: css changes --- src/client/util/SettingsManager.scss | 35 ++++++++++++++++++++--------------- src/client/util/SettingsManager.tsx | 19 ++++++++++++++++--- 2 files changed, 36 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 2b3e455e0..1e44a6a3c 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -1,19 +1,35 @@ +@import "../views/globalCssVariables"; + .settings-interface { display: flex; - flex-direction: column; .focus-span { text-decoration: underline; } p { - font-size: 20px; text-align: left; - font-style: italic; padding: 0; margin: 0 0 20px 0; } + h1 { + color: $dark-color; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + } + + .close-button { + position: absolute; + right: 0; + top: 0; + } + + .close-button:hover { + border-color: black; + } + .container { display: block; position: relative; @@ -38,18 +54,7 @@ color: black; } - .close-button { - border-radius: 5px; - margin-top: 20px; - padding: 10px 0; - background: aliceblue; - transition: 0.5s ease all; - border: 1px solid; - border-color: aliceblue; - } - .close-button:hover { - border-color: black; - } + } } \ No newline at end of file diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 4701c5f88..ca9124ef6 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -6,8 +6,9 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { SelectionManager } from "./SelectionManager"; import "./SettingsManager.scss"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -library.add(fa.faCopy); +library.add(fa.faWindowClose); @observer export default class SettingsManager extends React.Component<{}> { @@ -33,8 +34,20 @@ export default class SettingsManager extends React.Component<{}> { private get settingsInterface() { return (
-

sdfsldkfhlksdjf

-
Done
+
+ +
+

settings

+ +
toggle + second toggle +
+
hellow + heree + s + a + lot of information hahahahahahahah +
); } -- cgit v1.2.3-70-g09d2 From b4a23b21bbe0a44df1328419c7d94b97a772e54f Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 19 Nov 2019 19:06:47 -0500 Subject: progress w password --- src/client/util/SettingsManager.scss | 37 +++++++++++++++++++++++------------ src/client/util/SettingsManager.tsx | 38 ++++++++++++++++++++++++------------ src/server/index.ts | 10 ++++++++++ 3 files changed, 61 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 1e44a6a3c..cd9d2569a 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -2,16 +2,33 @@ .settings-interface { display: flex; + flex-direction: column; - .focus-span { - text-decoration: underline; + .settings-body { + display: flex; + flex-direction: row; + + .settings-type { + display: flex; + flex-direction: column; + flex-basis: 30%; + } + + .settings-content { + padding-left: 1em; + display: flex; + justify-content: space-between; + } } - p { - text-align: left; - padding: 0; - margin: 0 0 20px 0; + .focus-span { + text-decoration: underline; } +p { + text-align: left; + padding: 0; + margin: 0 0 20px 0; +} h1 { color: $dark-color; @@ -22,12 +39,8 @@ .close-button { position: absolute; - right: 0; - top: 0; - } - - .close-button:hover { - border-color: black; + right: 1em; + top: 1em; } .container { diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index ca9124ef6..c7d4d1786 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -7,6 +7,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { SelectionManager } from "./SelectionManager"; import "./SettingsManager.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Identified } from "../Network"; library.add(fa.faWindowClose); @@ -16,6 +17,7 @@ export default class SettingsManager extends React.Component<{}> { @observable private isOpen = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; + private curr_password_ref = React.createRef(); public open = action(() => { SelectionManager.DeselectAll(); @@ -31,23 +33,35 @@ export default class SettingsManager extends React.Component<{}> { SettingsManager.Instance = this; } + private dispatchRequest = async () => { + const curr_pass = this.curr_password_ref.current!.value; + const { error: resultError, ...others } = await Identified.PostToServer('/internalResetPassword', { curr_pass }); + if (resultError) { + // we failed + } + // do stuff with response + } + private get settingsInterface() { return (
-
- +
+

settings

+
+ +
-

settings

- -
toggle - second toggle -
-
hellow - heree - s - a - lot of information hahahahahahahah +
+
+

changeable settings

+

static data

+
+
+ + this changes with what you select! +
+
); } diff --git a/src/server/index.ts b/src/server/index.ts index 1595781dc..d5dbe8913 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -668,6 +668,16 @@ addSecureRoute({ onRejection: (_req, res) => res.send(JSON.stringify({ id: "__guest__", email: "" })) }); +addSecureRoute({ + method: Method.POST, + subscribers: '/internalResetPassword', + onValidation: (user, _req, res) => { + // user password auth + // new pass same + // do extra stuff + } +}); + const ServicesApiKeyMap = new Map([ ["face", process.env.FACE], ["vision", process.env.VISION], -- cgit v1.2.3-70-g09d2 From 56b83d89f37a5523ab319977e3385f539ecaf996 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sat, 23 Nov 2019 16:59:53 -0500 Subject: pushing progress...need to figure out bcrypt.compare" --- src/client/util/SettingsManager.scss | 16 ++++++++---- src/client/util/SettingsManager.tsx | 22 +++++++++++++---- src/server/index.ts | 48 +++++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index cd9d2569a..0d637868b 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -17,18 +17,24 @@ .settings-content { padding-left: 1em; display: flex; + flex-direction: column; justify-content: space-between; + + input { + min-width: 100%; + } } } .focus-span { text-decoration: underline; } -p { - text-align: left; - padding: 0; - margin: 0 0 20px 0; -} + + p { + text-align: left; + padding: 0; + margin: 0 0 20px 0; + } h1 { color: $dark-color; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 76f4bb964..ee2d9ff21 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -18,6 +18,8 @@ export default class SettingsManager extends React.Component<{}> { @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; private curr_password_ref = React.createRef(); + private new_password_ref = React.createRef(); + private new_confirm_ref = React.createRef(); public open = action(() => { SelectionManager.DeselectAll(); @@ -35,12 +37,19 @@ export default class SettingsManager extends React.Component<{}> { private dispatchRequest = async () => { const curr_pass = this.curr_password_ref.current!.value; - const { error: resultError, ...others } = await Identified.PostToServer('/internalResetPassword', { curr_pass }); - if (resultError) { + const new_pass = this.new_password_ref.current!.value; + const new_confirm = this.new_confirm_ref.current!.value; + console.log('ready!'); + // const { error, hello } = await Identified.PostToServer('/internalResetPassword', { curr_pass, new_pass, new_confirm }); + const resp = await Identified.PostToServer('/internalResetPassword', { curr_pass, new_pass, new_confirm }); + console.log('set!'); + console.log('response', resp); + console.log('hm', resp.hm); + if (resp.error) { // we failed - console.log(resultError); + console.log(resp.error); } - console.log(others); + console.log('go!'); // do stuff with response } @@ -59,7 +68,10 @@ export default class SettingsManager extends React.Component<{}> {

static data

- + + + + this changes with what you select!
diff --git a/src/server/index.ts b/src/server/index.ts index d96bd4d9a..df9edac50 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -19,7 +19,7 @@ import { Socket } from 'socket.io'; import * as webpack from 'webpack'; import * as wdm from 'webpack-dev-middleware'; import * as whm from 'webpack-hot-middleware'; -import { Utils } from '../Utils'; +import { Utils, returnEmptyString } from '../Utils'; import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller'; import { DashUserModel } from './authentication/models/user_model'; import { Client } from './Client'; @@ -56,6 +56,8 @@ import { reject } from 'bluebird'; import { ExifData } from 'exif'; import { Result } from '../client/northstar/model/idea/idea'; import RouteSubscriber from './RouteSubscriber'; +//@ts-ignore +import * as bcrypt from "bcrypt-nodejs"; const download = (url: string, dest: fs.PathLike) => request.get(url).pipe(fs.createWriteStream(dest)); let youtubeApiKey: string; @@ -671,10 +673,51 @@ addSecureRoute({ addSecureRoute({ method: Method.POST, subscribers: '/internalResetPassword', - onValidation: (user, _req, res) => { + onValidation: async (user, req, res) => { + const result: any = {}; + // perhaps should assert whether curr password is entered correctly + const validated = await new Promise>(resolve => { + bcrypt.compare(req.body.curr_pass, user.password, (err, result_1) => { + if (err) { + result.error = "Incorrect current password"; + res.send(result); + resolve(undefined); + } else { + result.hm = err; + resolve(result_1); + } + }); + }); + + if (validated === undefined) { + return; + } + + result.hello = validated; + req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); + req.assert("new_confirm", "Passwords do not match").equals(req.body.new_pass); + + // was there error in validating new passwords? + if (req.validationErrors()) { + // was there error? + result.error = req.validationErrors(); + result.pass = user.password; + } + + user.password = req.body.password; + user.passwordResetToken = undefined; + user.passwordResetExpires = undefined; + + user.save(function (err) { + result.error = "saving"; + // was there error? + }); + // user password auth // new pass same // do extra stuff + // + res.send(result); } }); @@ -1173,7 +1216,6 @@ const suffixMap: { [type: string]: (string | [string, string | ((json: any) => a "pdf": ["_t", "url"], "audio": ["_t", "url"], "web": ["_t", "url"], - "RichTextField": ["_t", value => value.Text], "date": ["_d", value => new Date(value.date).toISOString()], "proxy": ["_i", "fieldId"], "list": ["_l", list => { -- cgit v1.2.3-70-g09d2 From 88a716d8b7abb0255feea5bc32843ba68910eff5 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 5 Dec 2019 11:57:15 -0500 Subject: password reset live --- package.json | 2 +- src/client/util/SettingsManager.scss | 34 +++++++++++++++++++++++- src/client/util/SettingsManager.tsx | 50 +++++++++++++++++++++-------------- src/server/ApiManagers/UserManager.ts | 49 ++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 499aefdb5..574c7e7fa 100644 --- a/package.json +++ b/package.json @@ -229,4 +229,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} \ No newline at end of file +} diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 0d637868b..228625182 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -1,17 +1,45 @@ @import "../views/globalCssVariables"; +.dialogue-box { + background-color: whitesmoke !important; + color: grey; + + button { + background: $lighter-alt-accent; + outline: none; + border-radius: 5px; + border: 0px; + color: #fcfbf7; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + padding: 10px; + transition: transform 0.2s; + margin: 2px; + } +} + .settings-interface { display: flex; flex-direction: column; + input { + border-radius: 5px; + border: none; + padding: 4px 4px 4px 10px; + margin: 2px; + } + .settings-body { display: flex; flex-direction: row; + .settings-type { display: flex; flex-direction: column; flex-basis: 30%; + } .settings-content { @@ -20,6 +48,10 @@ flex-direction: column; justify-content: space-between; + button { + background: $darker-alt-accent; + } + input { min-width: 100%; } @@ -40,7 +72,7 @@ color: $dark-color; text-transform: uppercase; letter-spacing: 2px; - font-size: 75%; + font-size: 120%; } .close-button { diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index ee2d9ff21..0fcb80a3f 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -7,7 +7,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { SelectionManager } from "./SelectionManager"; import "./SettingsManager.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Identified } from "../Network"; +import { Networking } from "../Network"; library.add(fa.faWindowClose); @@ -36,21 +36,31 @@ export default class SettingsManager extends React.Component<{}> { } private dispatchRequest = async () => { - const curr_pass = this.curr_password_ref.current!.value; - const new_pass = this.new_password_ref.current!.value; - const new_confirm = this.new_confirm_ref.current!.value; - console.log('ready!'); - // const { error, hello } = await Identified.PostToServer('/internalResetPassword', { curr_pass, new_pass, new_confirm }); - const resp = await Identified.PostToServer('/internalResetPassword', { curr_pass, new_pass, new_confirm }); - console.log('set!'); - console.log('response', resp); - console.log('hm', resp.hm); - if (resp.error) { - // we failed - console.log(resp.error); + const curr_pass = this.curr_password_ref.current?.value; + const new_pass = this.new_password_ref.current?.value; + const new_confirm = this.new_confirm_ref.current?.value; + + if (!(curr_pass && new_pass && new_confirm)) { + alert("Hey we're missing some fields!"); + return; + } + + const passwordBundle = { + curr_pass, + new_pass, + new_confirm + }; + const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle); + if (error) { + alert("Uh oh! " + error); + return; } - console.log('go!'); - // do stuff with response + + alert("Password successfully updated!"); + } + + onClick = (event: any) => { + console.log(event); } private get settingsInterface() { @@ -64,13 +74,13 @@ export default class SettingsManager extends React.Component<{}> {
-

changeable settings

-

static data

+ +
- - - + + + this changes with what you select!
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 0f7d14320..7e8ceb189 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -2,6 +2,8 @@ import ApiManager, { Registration } from "./ApiManager"; import { Method } from "../RouteManager"; import { Database } from "../database"; import { msToTime } from "../ActionUtilities"; +import * as bcrypt from "bcrypt-nodejs"; +import { Opt } from "../../new_fields/Doc"; export const timeMap: { [id: string]: number } = {}; interface ActivityUnit { @@ -36,6 +38,53 @@ export default class UserManager extends ApiManager { onUnauthenticated: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" })) }); + register({ + method: Method.POST, + subscription: '/internalResetPassword', + onValidation: async ({ user, req, res }) => { + const result: any = {}; + const { curr_pass, new_pass, new_confirm } = req.body; + // perhaps should assert whether curr password is entered correctly + const validated = await new Promise>(resolve => { + bcrypt.compare(curr_pass, user.password, (err, passwords_match) => { + if (err) { + result.error = "Incorrect current password"; + res.send(result); + resolve(undefined); + } else { + resolve(passwords_match); + } + }); + }); + + if (validated === undefined) { + return; + } + + req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); + req.assert("new_confirm", "Passwords do not match").equals(new_pass); + + // was there error in validating new passwords? + if (req.validationErrors()) { + // was there error? + result.error = req.validationErrors(); + } + + user.password = new_pass; + user.passwordResetToken = undefined; + user.passwordResetExpires = undefined; + + user.save(err => { + if (err) { + result.error = "saving"; + } + }); + + res.send(result); + } + }); + + register({ method: Method.GET, subscription: "/activity", -- cgit v1.2.3-70-g09d2 From a9dab5e6befa36c54afd1e46507f266fda30a42e Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sat, 7 Dec 2019 17:07:36 -0500 Subject: progress --- src/client/util/SettingsManager.scss | 9 ++++++ src/client/util/SettingsManager.tsx | 49 +++++++++++++++++++++++++-------- src/server/ApiManagers/DeleteManager.ts | 17 +++++++++++- src/server/ApiManagers/UserManager.ts | 12 +++++--- 4 files changed, 70 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 228625182..5839fa748 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -47,6 +47,7 @@ display: flex; flex-direction: column; justify-content: space-between; + text-align: left; button { background: $darker-alt-accent; @@ -55,6 +56,14 @@ input { min-width: 100%; } + + .error-text { + color: #C40233; + } + + .success-text { + color: #009F6B; + } } } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 0fcb80a3f..e475cac1f 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -17,6 +17,9 @@ export default class SettingsManager extends React.Component<{}> { @observable private isOpen = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; + @observable private settingsContent = "settings"; + @observable private errorText = ""; + @observable private successText = ""; private curr_password_ref = React.createRef(); private new_password_ref = React.createRef(); private new_confirm_ref = React.createRef(); @@ -35,13 +38,15 @@ export default class SettingsManager extends React.Component<{}> { SettingsManager.Instance = this; } + @action private dispatchRequest = async () => { const curr_pass = this.curr_password_ref.current?.value; const new_pass = this.new_password_ref.current?.value; const new_confirm = this.new_confirm_ref.current?.value; if (!(curr_pass && new_pass && new_confirm)) { - alert("Hey we're missing some fields!"); + this.changeAlertText("Hey, we're missing some fields!", ""); + // alert("Hey we're missing some fields!"); return; } @@ -50,17 +55,31 @@ export default class SettingsManager extends React.Component<{}> { new_pass, new_confirm }; - const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle); + + const res = await Networking.PostToServer('/internalResetPassword', passwordBundle); + const error = res.error; + console.log(res, "is res"); if (error) { - alert("Uh oh! " + error); + console.log(error, error[0].msg); + this.changeAlertText("Uh oh! " + error[0].msg + "...", ""); + // alert("Uh oh! " + error.msg); return; } - alert("Password successfully updated!"); + this.changeAlertText("", "Password successfully updated!"); + console.log('success!'); + // alert("Password successfully updated!"); + } + + @action + private changeAlertText = (errortxt: string, successtxt: string) => { + this.errorText = errortxt; + this.successText = successtxt; } + @action onClick = (event: any) => { - console.log(event); + this.settingsContent = event.currentTarget.value; } private get settingsInterface() { @@ -77,13 +96,19 @@ export default class SettingsManager extends React.Component<{}> {
-
- - - - - this changes with what you select! -
+ {this.settingsContent === "settings" ? +
+ change password here: + + + + {this.errorText ?
{this.errorText}
: undefined} + {this.successText ?
{this.successText}
: undefined} + + +
+ : +
hello?
}
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts index 71818c673..1fdc7cc36 100644 --- a/src/server/ApiManagers/DeleteManager.ts +++ b/src/server/ApiManagers/DeleteManager.ts @@ -1,5 +1,5 @@ import ApiManager, { Registration } from "./ApiManager"; -import { Method, _permission_denied } from "../RouteManager"; +import { Method, _permission_denied, OnUnauthenticated } from "../RouteManager"; import { WebSocket } from "../Websocket/Websocket"; import { Database } from "../database"; @@ -31,6 +31,21 @@ export default class DeleteManager extends ApiManager { } }); + const hi: OnUnauthenticated = async ({ res, isRelease }) => { + if (isRelease) { + return _permission_denied(res, deletionPermissionError); + } + await Database.Instance.deleteAll('users'); + res.redirect("/home"); + }; + + // register({ + // method: Method.GET, + // subscription: "/deleteUsers", + // onValidation: hi, + // onUnauthenticated: hi + // }); + register({ method: Method.GET, diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 7e8ceb189..3ae2a5d30 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -47,8 +47,8 @@ export default class UserManager extends ApiManager { // perhaps should assert whether curr password is entered correctly const validated = await new Promise>(resolve => { bcrypt.compare(curr_pass, user.password, (err, passwords_match) => { - if (err) { - result.error = "Incorrect current password"; + if (err || !passwords_match) { + result.error = [{ msg: "Incorrect current password" }]; res.send(result); resolve(undefined); } else { @@ -61,9 +61,13 @@ export default class UserManager extends ApiManager { return; } - req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); + // req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); req.assert("new_confirm", "Passwords do not match").equals(new_pass); + if (req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 })) { + result.inch = "interesting"; + } + // was there error in validating new passwords? if (req.validationErrors()) { // was there error? @@ -76,7 +80,7 @@ export default class UserManager extends ApiManager { user.save(err => { if (err) { - result.error = "saving"; + result.error = [{ msg: "Error while saving new password" }]; } }); -- cgit v1.2.3-70-g09d2 From 130aaa1a8f2525db12479fcfef2204ed85a2b58b Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sat, 7 Dec 2019 17:21:40 -0500 Subject: rolled back to working state, saving new changes --- src/client/util/SettingsManager.tsx | 49 ++++++++++++++++++++++++++--------- src/server/ApiManagers/UserManager.ts | 13 +++++++--- 2 files changed, 46 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 0fcb80a3f..e475cac1f 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -17,6 +17,9 @@ export default class SettingsManager extends React.Component<{}> { @observable private isOpen = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; + @observable private settingsContent = "settings"; + @observable private errorText = ""; + @observable private successText = ""; private curr_password_ref = React.createRef(); private new_password_ref = React.createRef(); private new_confirm_ref = React.createRef(); @@ -35,13 +38,15 @@ export default class SettingsManager extends React.Component<{}> { SettingsManager.Instance = this; } + @action private dispatchRequest = async () => { const curr_pass = this.curr_password_ref.current?.value; const new_pass = this.new_password_ref.current?.value; const new_confirm = this.new_confirm_ref.current?.value; if (!(curr_pass && new_pass && new_confirm)) { - alert("Hey we're missing some fields!"); + this.changeAlertText("Hey, we're missing some fields!", ""); + // alert("Hey we're missing some fields!"); return; } @@ -50,17 +55,31 @@ export default class SettingsManager extends React.Component<{}> { new_pass, new_confirm }; - const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle); + + const res = await Networking.PostToServer('/internalResetPassword', passwordBundle); + const error = res.error; + console.log(res, "is res"); if (error) { - alert("Uh oh! " + error); + console.log(error, error[0].msg); + this.changeAlertText("Uh oh! " + error[0].msg + "...", ""); + // alert("Uh oh! " + error.msg); return; } - alert("Password successfully updated!"); + this.changeAlertText("", "Password successfully updated!"); + console.log('success!'); + // alert("Password successfully updated!"); + } + + @action + private changeAlertText = (errortxt: string, successtxt: string) => { + this.errorText = errortxt; + this.successText = successtxt; } + @action onClick = (event: any) => { - console.log(event); + this.settingsContent = event.currentTarget.value; } private get settingsInterface() { @@ -77,13 +96,19 @@ export default class SettingsManager extends React.Component<{}> { -
- - - - - this changes with what you select! -
+ {this.settingsContent === "settings" ? +
+ change password here: + + + + {this.errorText ?
{this.errorText}
: undefined} + {this.successText ?
{this.successText}
: undefined} + + +
+ : +
hello?
} diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 7e8ceb189..3a7e924ee 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -47,8 +47,8 @@ export default class UserManager extends ApiManager { // perhaps should assert whether curr password is entered correctly const validated = await new Promise>(resolve => { bcrypt.compare(curr_pass, user.password, (err, passwords_match) => { - if (err) { - result.error = "Incorrect current password"; + if (err || !passwords_match) { + result.error = [{ msg: "Incorrect current password" }]; res.send(result); resolve(undefined); } else { @@ -61,9 +61,13 @@ export default class UserManager extends ApiManager { return; } - req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); + // req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); req.assert("new_confirm", "Passwords do not match").equals(new_pass); + // if (req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 })) { + // result.inch = "interesting"; + // } + // was there error in validating new passwords? if (req.validationErrors()) { // was there error? @@ -76,7 +80,7 @@ export default class UserManager extends ApiManager { user.save(err => { if (err) { - result.error = "saving"; + result.error = [{ msg: "Error while saving new password" }]; } }); @@ -85,6 +89,7 @@ export default class UserManager extends ApiManager { }); + register({ method: Method.GET, subscription: "/activity", -- cgit v1.2.3-70-g09d2 From 4e3037dac61105f01397d55febb38b2d5bfa50f1 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sat, 7 Dec 2019 18:04:34 -0500 Subject: added forgot password indication --- src/client/util/SettingsManager.tsx | 10 ++-------- src/server/ApiManagers/UserManager.ts | 23 +++++++++-------------- views/login.pug | 2 +- 3 files changed, 12 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index e475cac1f..4872d7280 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -46,7 +46,6 @@ export default class SettingsManager extends React.Component<{}> { if (!(curr_pass && new_pass && new_confirm)) { this.changeAlertText("Hey, we're missing some fields!", ""); - // alert("Hey we're missing some fields!"); return; } @@ -56,19 +55,13 @@ export default class SettingsManager extends React.Component<{}> { new_confirm }; - const res = await Networking.PostToServer('/internalResetPassword', passwordBundle); - const error = res.error; - console.log(res, "is res"); + const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle); if (error) { - console.log(error, error[0].msg); this.changeAlertText("Uh oh! " + error[0].msg + "...", ""); - // alert("Uh oh! " + error.msg); return; } this.changeAlertText("", "Password successfully updated!"); - console.log('success!'); - // alert("Password successfully updated!"); } @action @@ -105,6 +98,7 @@ export default class SettingsManager extends React.Component<{}> { {this.errorText ?
{this.errorText}
: undefined} {this.successText ?
{this.successText}
: undefined} + forgot password? : diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 6997cb192..4556e01ea 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -61,28 +61,23 @@ export default class UserManager extends ApiManager { return; } - // req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); + req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 }); req.assert("new_confirm", "Passwords do not match").equals(new_pass); - -<<<<<<< HEAD - // if (req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 })) { - // result.inch = "interesting"; - // } -======= - if (req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 })) { - result.inch = "interesting"; + if (curr_pass === new_pass) { + result.error = [{ msg: "Current and new password are the same" }]; } ->>>>>>> a9dab5e6befa36c54afd1e46507f266fda30a42e - // was there error in validating new passwords? if (req.validationErrors()) { // was there error? result.error = req.validationErrors(); } - user.password = new_pass; - user.passwordResetToken = undefined; - user.passwordResetExpires = undefined; + // will only change password if there are no errors. + if (!result.error) { + user.password = new_pass; + user.passwordResetToken = undefined; + user.passwordResetExpires = undefined; + } user.save(err => { if (err) { diff --git a/views/login.pug b/views/login.pug index 26da5e29e..98816e9c8 100644 --- a/views/login.pug +++ b/views/login.pug @@ -9,7 +9,7 @@ block content .overlay(id='overlay_login') a(href="/signup") img(id='new_user', src="https://bit.ly/2EuqPb4", alt="") - a(href="/forgot") + a(href="/forgotPassword") img(id='forgot', src="https://bit.ly/2XjHpSo", alt="") .inner.login h3.auth_header Log In -- cgit v1.2.3-70-g09d2 From 5cde81d8c6b4dcd8d0796f8669b668763957f395 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sat, 7 Dec 2019 18:30:54 -0500 Subject: pushing commit for today --- src/client/util/SettingsManager.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 4872d7280..652af438b 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -17,7 +17,7 @@ export default class SettingsManager extends React.Component<{}> { @observable private isOpen = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; - @observable private settingsContent = "settings"; + @observable private settingsContent = "password"; @observable private errorText = ""; @observable private successText = ""; private curr_password_ref = React.createRef(); @@ -86,10 +86,10 @@ export default class SettingsManager extends React.Component<{}> {
- - + +
- {this.settingsContent === "settings" ? + {this.settingsContent === "password" ?
change password here: @@ -97,12 +97,12 @@ export default class SettingsManager extends React.Component<{}> { {this.errorText ?
{this.errorText}
: undefined} {this.successText ?
{this.successText}
: undefined} - - forgot password? - + forgot password?
- : -
hello?
} + : undefined} + {this.settingsContent === "data" ? +
hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+ : undefined}
-- cgit v1.2.3-70-g09d2 From cef6852d597ce67637466afb36c3498dc84211f6 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Wed, 8 Jan 2020 19:48:28 -0500 Subject: gestures are now overlayed and can span collections/panes! --- src/client/documents/Documents.ts | 3 +- src/client/views/CollectionLinearView.tsx | 4 +- src/client/views/GestureOverlay.scss | 8 + src/client/views/GestureOverlay.tsx | 159 +++++++++++++++++++ src/client/views/InkSelectDecorations.scss | 5 - src/client/views/InkSelectDecorations.tsx | 55 ------- src/client/views/InkingStroke.tsx | 6 +- src/client/views/MainView.scss | 7 +- src/client/views/MainView.tsx | 7 +- .../views/collections/CollectionStackingView.tsx | 6 +- src/client/views/collections/CollectionSubView.tsx | 13 +- .../views/collections/CollectionTreeView.scss | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 171 +++++++-------------- src/client/views/nodes/DocumentView.tsx | 19 ++- src/pen-gestures/GestureUtils.ts | 28 +++- 15 files changed, 299 insertions(+), 198 deletions(-) create mode 100644 src/client/views/GestureOverlay.scss create mode 100644 src/client/views/GestureOverlay.tsx delete mode 100644 src/client/views/InkSelectDecorations.scss delete mode 100644 src/client/views/InkSelectDecorations.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e149963b9..1853be529 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -29,7 +29,7 @@ import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -import { UndoManager } from "../util/UndoManager"; +import { UndoManager, undoBatch } from "../util/UndoManager"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { LinkManager } from "../util/LinkManager"; @@ -730,6 +730,7 @@ export namespace DocUtils { }); } + @undoBatch export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 5ca861f71..5d6a58656 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -36,7 +36,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { { fireImmediately: true } ); } - protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); @@ -55,7 +55,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { render() { const guid = Utils.GenerateGuid(); return
-
+
this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} /> diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss new file mode 100644 index 000000000..cbc1b6e7d --- /dev/null +++ b/src/client/views/GestureOverlay.scss @@ -0,0 +1,8 @@ +.gestureOverlay-cont { + width: 100vw; + height: 100vw; + position: absolute; + top: 0; + left: 0; + touch-action: none; +} \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx new file mode 100644 index 000000000..c88e3f7ae --- /dev/null +++ b/src/client/views/GestureOverlay.tsx @@ -0,0 +1,159 @@ +import React = require("react"); +import { Touchable } from "./Touchable"; +import { observer } from "mobx-react"; +import "./GestureOverlay.scss" +import { computed, observable, action } from "mobx"; +import { CreatePolyline } from "./InkingStroke"; +import { GestureUtils } from "../../pen-gestures/GestureUtils"; +import { InteractionUtils } from "../util/InteractionUtils"; +import { InkingControl } from "./InkingControl"; +import { InkTool } from "../../new_fields/InkField"; +import { Doc } from "../../new_fields/Doc"; +import { LinkManager } from "../util/LinkManager"; +import { DocUtils } from "../documents/Documents"; +import { undoBatch } from "../util/UndoManager"; + +@observer +export default class GestureOverlay extends Touchable { + static Instance: GestureOverlay; + + @observable private _points: { X: number, Y: number }[] = []; + + private _d1: Doc | undefined; + + constructor(props: Readonly<{}>) { + super(props); + + GestureOverlay.Instance = this; + } + + @action + onPointerDown = (e: React.PointerEvent) => { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + } + } + + @action + onPointerMove = (e: PointerEvent) => { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onPointerUp = (e: PointerEvent) => { + 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 })); + + const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); + let actionPerformed = false; + if (result && result.Score > 0.7) { + switch (result.Name) { + case GestureUtils.Gestures.Box: + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent(new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Box, + bounds: B + } + })); + actionPerformed = true; + break; + case GestureUtils.Gestures.Line: + const ep1 = this._points[0]; + const ep2 = this._points[this._points.length - 1]; + const target1 = document.elementFromPoint(ep1.X, ep1.Y); + const target2 = document.elementFromPoint(ep2.X, ep2.Y); + const callback = (doc: Doc) => { + if (!this._d1) { + this._d1 = doc; + } + else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { + DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); + actionPerformed = true; + } + } + const ge = new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Line, + bounds: B, + callbackFn: callback + } + }) + target1?.dispatchEvent(ge); + target2?.dispatchEvent(ge); + break; + } + if (actionPerformed) { + this._points = []; + } + } + + if (!actionPerformed) { + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent( + new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Stroke, + bounds: B + } + } + ) + ) + this._points = []; + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + @computed get svgBounds() { + const xs = this._points.map(p => p.X); + const ys = this._points.map(p => p.Y); + const right = Math.max(...xs); + const left = Math.min(...xs); + const bottom = Math.max(...ys); + const top = Math.min(...ys); + return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; + } + + @computed get currentStroke() { + if (this._points.length <= 1) { + return (null); + } + + const B = this.svgBounds; + + return ( + + {CreatePolyline(this._points, B.left, B.top)} + + ); + } + + render() { + return ( +
+ {this.props.children} + {this.currentStroke} +
); + } +} \ No newline at end of file diff --git a/src/client/views/InkSelectDecorations.scss b/src/client/views/InkSelectDecorations.scss deleted file mode 100644 index daff58fd6..000000000 --- a/src/client/views/InkSelectDecorations.scss +++ /dev/null @@ -1,5 +0,0 @@ -.inkSelectDecorations { - position: absolute; - border: black 1px solid; - z-index: 9001; -} \ No newline at end of file diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx deleted file mode 100644 index 3ad50762d..000000000 --- a/src/client/views/InkSelectDecorations.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React = require("react"); -import { Touchable } from "./Touchable"; -import { PointData } from "../../new_fields/InkField"; -import { observer } from "mobx-react"; -import { computed, observable, action, runInAction } from "mobx"; -import "./InkSelectDecorations.scss"; - -@observer -export default class InkSelectDecorations extends Touchable { - static Instance: InkSelectDecorations; - - @observable private _selectedInkNodes: Map = new Map(); - - constructor(props: Readonly<{}>) { - super(props); - - InkSelectDecorations.Instance = this; - } - - @action - public SetSelected = (inkNodes: Map, keepOld: boolean = false) => { - if (!keepOld) { - this._selectedInkNodes = new Map(); - } - inkNodes.forEach((value: any, key: any) => { - runInAction(() => this._selectedInkNodes.set(key, value)); - }); - } - - @computed - get Bounds(): { x: number, y: number, b: number, r: number } { - const left = Number.MAX_VALUE; - const top = Number.MAX_VALUE; - const right = -Number.MAX_VALUE; - const bottom = -Number.MAX_VALUE; - this._selectedInkNodes.forEach((value: PointData, key: string) => { - // value.pathData.map(val => { - // left = Math.min(val.x, left); - // top = Math.min(val.y, top); - // right = Math.max(val.x, right); - // bottom = Math.max(val.y, bottom); - // }); - }); - return { x: left, y: top, b: bottom, r: right }; - } - - render() { - const bounds = this.Bounds; - return
; - } -} \ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index a413eebc9..8b346d5d9 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -9,6 +9,7 @@ import { InkingControl } from "./InkingControl"; import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); +import { TraceMobx } from "../../new_fields/util"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -35,6 +36,7 @@ export class InkingStroke extends DocExtendableComponent p.X); const ys = data.map(p => p.Y); @@ -42,7 +44,7 @@ export class InkingStroke extends DocExtendableComponent diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 4c8c95529..2a1cc3303 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -9,8 +9,8 @@ .mainContent-div { position: relative; - width:100%; - height:100%; + width: 100%; + height: 100%; } // add nodes menu. Note that the + button is actually an input label, not an actual button. @@ -28,6 +28,7 @@ top: 0; left: 0; z-index: 1; + touch-action: none; } .mainView-mainContent { @@ -66,7 +67,7 @@ .mainView-libraryFlyout { height: 100%; - width:100%; + width: 100%; position: absolute; display: flex; flex-direction: column; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a1196ee1c..14c1803c0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -36,7 +36,7 @@ import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu'; -import InkSelectDecorations from './InkSelectDecorations'; +import GestureOverlay from './GestureOverlay'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; import { TraceMobx } from '../../new_fields/util'; @@ -510,8 +510,9 @@ export class MainView extends React.Component { - - {this.mainContent} + + {this.mainContent} + diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e71e11b48..992820fc7 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -153,7 +153,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; - this.createDropTarget(ele!); //so the whole grid is the drop target? + this.createDropAndGestureTarget(ele!); //so the whole grid is the drop target? } overlays = (doc: Doc) => { @@ -309,7 +309,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropTarget} + createDropTarget={this.createDropAndGestureTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} />; } @@ -342,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropTarget} + createDropTarget={this.createDropAndGestureTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} setDocHeight={this.setDocHeight} />; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 062521690..73dc7edc6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -23,6 +23,7 @@ import { basename } from 'path'; import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { Networking } from "../../Network"; +import { GestureUtils } from "../../../pen-gestures/GestureUtils"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc) => boolean; @@ -47,15 +48,18 @@ export interface SubCollectionViewProps extends CollectionViewProps { export function CollectionSubView(schemaCtor: (doc: Doc) => T) { class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; + private gestureDisposer?: GestureUtils.GestureEventDisposer; private _childLayoutDisposer?: IReactionDisposer; - protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer && this.dropDisposer(); + this.gestureDisposer && this.gestureDisposer(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); } } protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view - this.createDropTarget(ele); + this.createDropAndGestureTarget(ele); } componentDidMount() { @@ -129,6 +133,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } } + @undoBatch + protected onGesture(e: Event, ge: GestureUtils.GestureEvent) { + + } + @undoBatch @action protected drop(e: Event, de: DragManager.DropEvent): boolean { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 0b9dc2eb2..2fa6813d7 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -7,9 +7,9 @@ border-radius: inherit; box-sizing: border-box; height: 100%; - width:100%; + width: 100%; position: relative; - top:0; + top: 0; padding-left: 10px; padding-right: 10px; background: $light-color-secondary; @@ -17,6 +17,7 @@ overflow: auto; user-select: none; cursor: default; + touch-action: pan-y; ul { list-style: none; @@ -115,6 +116,7 @@ .treeViewItem-header { border: transparent 1px solid; display: flex; + .editableView-container-editing-oneLine { min-width: 15px; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eb5a074bb..9656debf3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -272,11 +272,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return clusterColor; } - @observable private _points: { X: number, Y: number }[] = []; @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble) return; + if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + return; + } this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { document.removeEventListener("pointermove", this.onPointerMove); @@ -284,14 +285,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); // if physically using a pen or we're in pen or highlighter mode - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { - e.stopPropagation(); - e.preventDefault(); - const point = this.getTransform().transformPoint(e.pageX, e.pageY); - this._points.push({ X: point[0], Y: point[1] }); - } + // if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + // e.stopPropagation(); + // e.preventDefault(); + // const point = this.getTransform().transformPoint(e.pageX, e.pageY); + // this._points.push({ X: point[0], Y: point[1] }); + // } // if not using a pen and in no ink mode - else if (InkingControl.Instance.selectedTool === InkTool.None) { + if (InkingControl.Instance.selectedTool === InkTool.None) { this._lastX = e.pageX; this._lastY = e.pageY; } @@ -332,13 +333,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { document.addEventListener("touchmove", this.onTouch); document.removeEventListener("touchend", this.onTouchEnd); document.addEventListener("touchend", this.onTouchEnd); - if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { - e.stopPropagation(); - e.preventDefault(); - const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); - this._points.push({ X: point[0], Y: point[1] }); - } - else if (InkingControl.Instance.selectedTool === InkTool.None) { + // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { + // e.stopPropagation(); + // e.preventDefault(); + // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); + // this._points.push({ X: point[0], Y: point[1] }); + // } + if (InkingControl.Instance.selectedTool === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.stopPropagation(); @@ -352,72 +353,44 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - @action - onPointerUp = (e: PointerEvent): void => { - if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return; - - 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 })); - - const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); - let actionPerformed = false; - if (result && result.Score > 0.7) { - switch (result.Name) { - case GestureUtils.Gestures.Box: - const bounds = { x: Math.min(...this._points.map(p => p.X)), r: Math.max(...this._points.map(p => p.X)), y: Math.min(...this._points.map(p => p.Y)), b: Math.max(...this._points.map(p => p.Y)) }; - const sel = this.getActiveDocuments().filter(doc => { - const l = NumCast(doc.x); - const r = l + doc[WidthSym](); - const t = NumCast(doc.y); - const b = t + doc[HeightSym](); - const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t); - if (pass) { - doc.x = l - B.left - B.width / 2; - doc.y = t - B.top - B.height / 2; - } - return pass; - }); - this.addDocument(Docs.Create.FreeformDocument(sel, { x: B.left, y: B.top, width: B.width, height: B.height, panX: 0, panY: 0 })); - sel.forEach(d => this.props.removeDocument(d)); - actionPerformed = true; - break; - case GestureUtils.Gestures.Line: - const ep1 = this._points[0]; - const ep2 = this._points[this._points.length - 1]; - let d1: Doc | undefined; - let d2: Doc | undefined; - this.getActiveDocuments().map(doc => { - const l = NumCast(doc.x); - const r = l + doc[WidthSym](); - const t = NumCast(doc.y); - const b = t + doc[HeightSym](); - if (!d1 && l < ep1.X && r > ep1.X && t < ep1.Y && b > ep1.Y) { - d1 = doc; - } - else if (!d2 && l < ep2.X && r > ep2.X && t < ep2.Y && b > ep2.Y) { - d2 = doc; - } - }); - if (d1 && d2) { - if (!LinkManager.Instance.doesLinkExist(d1, d2)) { - DocUtils.MakeLink({ doc: d1 }, { doc: d2 }); - actionPerformed = true; - } - } - break; - } - if (actionPerformed) { - this._points = []; - } - } - - if (!actionPerformed) { - const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top }); + @undoBatch + onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { + switch (ge.gesture) { + case GestureUtils.Gestures.Stroke: + const points = ge.points; + const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); + const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { x: B.x, y: B.y, width: B.width, height: B.height }); this.addDocument(inkDoc); - this._points = []; - } + e.stopPropagation(); + break; + case GestureUtils.Gestures.Box: + const lt = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y))); + const rb = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y))); + const bounds = { x: lt[0], r: rb[0], y: lt[1], b: rb[1] }; + const bWidth = bounds.r - bounds.x; + const bHeight = bounds.b - bounds.y; + const sel = this.getActiveDocuments().filter(doc => { + const l = NumCast(doc.x); + const r = l + doc[WidthSym](); + const t = NumCast(doc.y); + const b = t + doc[HeightSym](); + const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t); + if (pass) { + doc.x = l - bounds.x - bWidth / 2; + doc.y = t - bounds.y - bHeight / 2; + } + return pass; + }); + this.addDocument(Docs.Create.FreeformDocument(sel, { x: bounds.x, y: bounds.y, width: bWidth, height: bHeight, panX: 0, panY: 0 })); + sel.forEach(d => this.props.removeDocument(d)); + break; + } + } + + @action + onPointerUp = (e: PointerEvent): void => { + if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -473,11 +446,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } if (!e.cancelBubble) { const selectedTool = InkingControl.Instance.selectedTool; - if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - const point = this.getTransform().transformPoint(e.clientX, e.clientY); - this._points.push({ X: point[0], Y: point[1] }); - } - else if (selectedTool === InkTool.None) { + if (selectedTool === InkTool.None) { if (this._hitCluster && this.tryDragCluster(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(); @@ -508,10 +477,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.pan(pt); } - else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) { - const point = this.getTransform().transformPoint(pt.clientX, pt.clientY); - this._points.push({ X: point[0], Y: point[1] }); - } } e.stopPropagation(); e.preventDefault(); @@ -942,34 +907,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } - @computed get svgBounds() { - const xs = this._points.map(p => p.X); - const ys = this._points.map(p => p.Y); - const right = Math.max(...xs); - const left = Math.min(...xs); - const bottom = Math.max(...ys); - const top = Math.min(...ys); - return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; - } - - @computed get currentStroke() { - if (this._points.length <= 1) { - return (null); - } - - const B = this.svgBounds; - - return ( - - {CreatePolyline(this._points, B.left, B.top)} - - ); - } - children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); - this.currentStroke && (eles.push(this.currentStroke)); + // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); return eles; } @@ -998,7 +939,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document if (!this.extensionDoc) return (null); // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale; - return
{!this.Document.LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a14f69f71..b544319d7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -45,6 +45,7 @@ import { InkTool } from '../../../new_fields/InkField'; import { TraceMobx } from '../../../new_fields/util'; import { List } from '../../../new_fields/List'; import { FormattedTextBoxComment } from './FormattedTextBoxComment'; +import { GestureUtils } from '../../../pen-gestures/GestureUtils'; library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -94,6 +95,7 @@ export class DocumentView extends DocComponent(Docu private _hitTemplateDrag = false; private _mainCont = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; + private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; private _titleRef = React.createRef(); public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -107,6 +109,7 @@ export class DocumentView extends DocComponent(Docu @action componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); + this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this); } @@ -365,10 +368,11 @@ export class DocumentView extends DocComponent(Docu // console.log(e.button) // console.log(e.nativeEvent) // continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document) + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + return; + } if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { - if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - e.stopPropagation(); - } + e.stopPropagation(); return; } if ((!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart)) { @@ -424,6 +428,15 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } + onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { + switch (ge.gesture) { + case GestureUtils.Gestures.Line: + ge.callbackFn && ge.callbackFn(this.props.Document); + e.stopPropagation(); + break; + } + } + @undoBatch deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 59a85b66b..4edcfa623 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -1,19 +1,43 @@ import { NDollarRecognizer } from "./ndollar"; import { Type } from "typescript"; -import { InkField } from "../new_fields/InkField"; +import { InkField, PointData } from "../new_fields/InkField"; import { Docs } from "../client/documents/Documents"; import { Doc, WidthSym, HeightSym } from "../new_fields/Doc"; import { NumCast } from "../new_fields/Types"; import { CollectionFreeFormView } from "../client/views/collections/collectionFreeForm/CollectionFreeFormView"; +import { Rect } from "react-measure"; export namespace GestureUtils { namespace GestureDataTypes { export type BoxData = Array; } + export class GestureEvent { + constructor( + readonly gesture: Gestures, + readonly points: PointData[], + readonly bounds: Rect, + readonly callbackFn?: Function + ) { } + } + + export interface GestureEventDisposer { (): void; } + + export function MakeGestureTarget( + element: HTMLElement, + func: (e: Event, ge: GestureEvent) => void + ): GestureEventDisposer { + const handler = (e: Event) => func(e, (e as CustomEvent).detail); + element.addEventListener("dashOnGesture", handler); + return () => { + element.removeEventListener("dashOnGesture", handler); + } + } + export enum Gestures { Box = "box", - Line = "line" + Line = "line", + Stroke = "stroke" } export const GestureRecognizer = new NDollarRecognizer(false); -- cgit v1.2.3-70-g09d2 From 01fc2726d439127baebc214bc1c0c7a0c1554fa2 Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Wed, 8 Jan 2020 17:11:33 -0800 Subject: setting up architecture for radial menu --- package-lock.json | 31 +++++++++++++++++++++++-------- package.json | 1 + src/client/views/nodes/DocumentView.tsx | 9 +++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 649f40a97..44a40483b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4316,14 +4316,6 @@ "minimalistic-crypto-utils": "^1.0.0" } }, - "emit-logger": { - "version": "github:chocolateboy/emit-logger#b9d25a2d939e42f29c940861e9648bd0fb810070", - "from": "github:chocolateboy/emit-logger#better-emitter-name", - "requires": { - "chalk": "^1.1.1", - "moment": "^2.10.6" - } - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -7077,6 +7069,16 @@ "lodash": "^4.17.4", "semver": "^5.0.3", "source-map-support": "^0.5.9" + }, + "dependencies": { + "emit-logger": { + "version": "github:chocolateboy/emit-logger#b9d25a2d939e42f29c940861e9648bd0fb810070", + "from": "github:chocolateboy/emit-logger#better-emitter-name", + "requires": { + "chalk": "^1.1.1", + "moment": "^2.10.6" + } + } } }, "is-accessor-descriptor": { @@ -8559,6 +8561,14 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, + "mongod": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mongod/-/mongod-2.0.0.tgz", + "integrity": "sha1-zuxLcjBRkOhdb4+smS8VpH6xjHQ=", + "requires": { + "promise-queue": "^2.2.3" + } + }, "mongodb": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", @@ -13284,6 +13294,11 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promise-queue": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" + }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", diff --git a/package.json b/package.json index 9bd2273f8..ab273f352 100644 --- a/package.json +++ b/package.json @@ -170,6 +170,7 @@ "mobx-react": "^5.3.5", "mobx-react-devtools": "^6.1.1", "mobx-utils": "^5.5.2", + "mongod": "^2.0.0", "mongodb": "^3.1.13", "mongoose": "^5.6.4", "node-sass": "^4.12.0", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a14f69f71..e1b68cdf7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -82,6 +82,7 @@ export interface DocumentViewProps { ChromeHeight?: () => number; dontRegisterView?: boolean; layoutKey?: string; + radialMenu?: String[]; } @@ -104,6 +105,14 @@ export class DocumentView extends DocComponent(Docu @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } + constructor(props: any) { + super(props); + } + + handle1PointerHoldStart= (e: React.TouchEvent): any =>{ + console.log("yeet") + } + @action componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); -- cgit v1.2.3-70-g09d2 From 0e7d688da6fde96908bee6f0b62b02aae2b95872 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Wed, 8 Jan 2020 20:16:21 -0500 Subject: some bugfixes re previous commit --- src/client/views/nodes/DocumentView.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b544319d7..7d6b8bf01 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -368,14 +368,17 @@ export class DocumentView extends DocComponent(Docu // console.log(e.button) // console.log(e.nativeEvent) // continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document) - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { - return; - } - if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { - e.stopPropagation(); + if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + e.stopPropagation(); + } return; } - if ((!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart)) { + // if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { + // e.stopPropagation(); + // return; + // } + if (!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart) { // if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) // // return if we're inking, and not selecting a button document // || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick) -- cgit v1.2.3-70-g09d2 From e3466bdb86fdc3096e86f2437a0c8e2000b2252a Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Wed, 8 Jan 2020 22:32:09 -0800 Subject: menu appears on click --- src/client/views/MainView.tsx | 2 + src/client/views/Touchable.tsx | 9 ++++- src/client/views/globalCssVariables.scss | 2 + src/client/views/nodes/DocumentView.tsx | 69 +++++++++++++++++++++++++++++--- 4 files changed, 75 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a1196ee1c..5bf5dbcc1 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -40,6 +40,7 @@ import InkSelectDecorations from './InkSelectDecorations'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; import { TraceMobx } from '../../new_fields/util'; +import { RadialMenu } from './nodes/RadialMenu'; @observer export class MainView extends React.Component { @@ -514,6 +515,7 @@ export class MainView extends React.Component { {this.mainContent} + diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 251cd41e5..49b2116f1 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; import { action } from 'mobx'; import { InteractionUtils } from '../util/InteractionUtils'; +import { RadialMenu } from './nodes/RadialMenu'; const HOLD_DURATION = 1000; export abstract class Touchable extends React.Component { - private holdTimer: NodeJS.Timeout | undefined; + //private holdTimer: NodeJS.Timeout | undefined; + holdTimer: NodeJS.Timeout | undefined; protected _touchDrag: boolean = false; protected prevPoints: Map = new Map(); @@ -54,6 +56,7 @@ export abstract class Touchable extends React.Component { if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { + console.log("CLEAR") clearTimeout(this.holdTimer); } switch (myTouches.length) { @@ -89,6 +92,7 @@ export abstract class Touchable extends React.Component { } if (this.holdTimer) { clearTimeout(this.holdTimer); + console.log("clear"); } this._touchDrag = false; e.stopPropagation(); @@ -136,5 +140,8 @@ export abstract class Touchable extends React.Component { console.log("Hold"); e.stopPropagation(); e.preventDefault(); + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchend", this.onTouchEnd); } + } \ No newline at end of file diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index 6dffee586..019f931f9 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -25,6 +25,8 @@ $search-thumnail-size: 175; // dragged items $contextMenu-zindex: 100000; // context menu shows up over everything +$radialMenu-zindex: 100000; // context menu shows up over everything + $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc $docDecorations-zindex: 998; // then doc decorations appear over everything else $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e1b68cdf7..d5442bd48 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -45,6 +45,9 @@ import { InkTool } from '../../../new_fields/InkField'; import { TraceMobx } from '../../../new_fields/util'; import { List } from '../../../new_fields/List'; import { FormattedTextBoxComment } from './FormattedTextBoxComment'; +import { RadialMenu } from './RadialMenu'; +import { RadialMenuProps } from './RadialMenuItem'; + library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -85,7 +88,6 @@ export interface DocumentViewProps { radialMenu?: String[]; } - @observer export class DocumentView extends DocComponent(Document) { private _downX: number = 0; @@ -105,12 +107,65 @@ export class DocumentView extends DocComponent(Docu @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } - constructor(props: any) { - super(props); - } handle1PointerHoldStart= (e: React.TouchEvent): any =>{ - console.log("yeet") + this.onRadialMenu(e); + + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchend", this.onTouchEnd); + document.addEventListener("touchmove", this.handle1PointerHoldMove); + document.addEventListener("touchend", this.handle1PointerHoldEnd); + } + + handle1PointerHoldMove = (e: TouchEvent): void => { + console.log("YUH") + document.removeEventListener("touchmove", this.handle1PointerHoldMove); + document.addEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); + document.addEventListener("touchend", this.handle1PointerHoldEnd); + } + + handle1PointerHoldEnd = (e: TouchEvent): void => { + RadialMenu.Instance.closeMenu(); + document.removeEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); + } + + @action + onRadialMenu = async (event: React.TouchEvent): Promise => { + console.log("YUH") + // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 + // if (e.button === 0) { + // e.preventDefault(); + // return; + // } + let e = event.touches[0]; + // if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 || + // // event.isDefaultPrevented()) { + // // event.preventDefault(); + // return; + // } + // event.preventDefault(); + + let rm = RadialMenu.Instance; + rm.openMenu(); + + rm.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); + runInAction(() => { + // cm.addItem({ + // description: "Share", + // event: () => SharingManager.Instance.open(this), + // icon: "external-link-alt" + // }); + + if (!this.topMost) { + // DocumentViews should stop propagation of this event + } + RadialMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + if (!SelectionManager.IsSelected(this, true)) { + SelectionManager.SelectDoc(this, false); + } + }); } @action @@ -371,6 +426,7 @@ export class DocumentView extends DocComponent(Docu } onPointerDown = (e: React.PointerEvent): void => { + // console.log(e.button) // console.log(e.nativeEvent) // continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document) @@ -388,7 +444,6 @@ export class DocumentView extends DocComponent(Docu // || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) { // return; // } - this._downX = e.clientX; this._downY = e.clientY; this._hitTemplateDrag = false; @@ -404,11 +459,13 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); + if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); } } } onPointerMove = (e: PointerEvent): void => { + if ((e as any).formattedHandled) { e.stopPropagation(); return; } if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) -- cgit v1.2.3-70-g09d2 From f3c332c982e32d94609bf5ab9c077f602db0d206 Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Thu, 9 Jan 2020 01:13:22 -0800 Subject: circle appears for menu that can be split into smaller pie pieces --- src/client/views/nodes/DocumentView.tsx | 265 +++++++++++++++++--------------- 1 file changed, 139 insertions(+), 126 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d5442bd48..b4ebe626f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -107,9 +107,15 @@ export class DocumentView extends DocComponent(Docu @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } + private _firstX:number=0; + private _firstY:number=0; + handle1PointerHoldStart= (e: React.TouchEvent): any =>{ this.onRadialMenu(e); + let page =e.touches[0]; + this._firstX=page.pageX; + this._firstY=page.pageY; document.removeEventListener("touchmove", this.onTouch); document.removeEventListener("touchend", this.onTouchEnd); @@ -117,14 +123,21 @@ export class DocumentView extends DocComponent(Docu document.addEventListener("touchend", this.handle1PointerHoldEnd); } - handle1PointerHoldMove = (e: TouchEvent): void => { - console.log("YUH") + handle1PointerHoldMove = (event: TouchEvent): void => { + let e=event.touches[0]; + Math.abs(e.pageX-this._firstX)>175 ||Math.abs(e.pageY-this._firstY)>175? this.handleRelease():null; document.removeEventListener("touchmove", this.handle1PointerHoldMove); document.addEventListener("touchmove", this.handle1PointerHoldMove); document.removeEventListener("touchend", this.handle1PointerHoldEnd); document.addEventListener("touchend", this.handle1PointerHoldEnd); } + handleRelease(){ + RadialMenu.Instance.closeMenu() + document.removeEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); + } + handle1PointerHoldEnd = (e: TouchEvent): void => { RadialMenu.Instance.closeMenu(); document.removeEventListener("touchmove", this.handle1PointerHoldMove); @@ -300,130 +313,130 @@ export class DocumentView extends DocComponent(Docu } } - handle1PointerDown = (e: React.TouchEvent) => { - if (!e.nativeEvent.cancelBubble) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; - this._downX = touch.clientX; - this._downY = touch.clientY; - this._hitTemplateDrag = false; - for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { - if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { - this._hitTemplateDrag = true; - } - } - if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); - if ((e.nativeEvent as any).formattedHandled) e.stopPropagation(); - console.log("down") - } - } - - handle1PointerMove = (e: TouchEvent) => { - if ((e as any).formattedHandled) { e.stopPropagation; return; } - if (e.cancelBubble && this.active) { - document.removeEventListener("touchmove", this.onTouch); - } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; - if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { - if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); - } - } - 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(); - - } - } - - handle2PointersDown = (e: React.TouchEvent) => { - if (!e.nativeEvent.cancelBubble && !this.isSelected()) { - e.stopPropagation(); - e.preventDefault(); - - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); - } - } - - @action - handle2PointersMove = (e: TouchEvent) => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); - const pt1 = myTouches[0]; - const pt2 = myTouches[1]; - const oldPoint1 = this.prevPoints.get(pt1.identifier); - const oldPoint2 = this.prevPoints.get(pt2.identifier); - const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!); - if (pinching !== 0 && oldPoint1 && oldPoint2) { - // let dX = (Math.min(pt1.clientX, pt2.clientX) - Math.min(oldPoint1.clientX, oldPoint2.clientX)); - // let dY = (Math.min(pt1.clientY, pt2.clientY) - Math.min(oldPoint1.clientY, oldPoint2.clientY)); - // let dX = Math.sign(Math.abs(pt1.clientX - oldPoint1.clientX) - Math.abs(pt2.clientX - oldPoint2.clientX)); - // let dY = Math.sign(Math.abs(pt1.clientY - oldPoint1.clientY) - Math.abs(pt2.clientY - oldPoint2.clientY)); - // let dW = -dX; - // let dH = -dY; - const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX)); - const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY)); - const dX = -1 * Math.sign(dW); - const dY = -1 * Math.sign(dH); - - if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { - const doc = PositionDocument(this.props.Document); - const layoutDoc = PositionDocument(Doc.Layout(this.props.Document)); - let nwidth = layoutDoc.nativeWidth || 0; - let nheight = layoutDoc.nativeHeight || 0; - const width = (layoutDoc.width || 0); - const height = (layoutDoc.height || (nheight / nwidth * width)); - const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling(); - const actualdW = Math.max(width + (dW * scale), 20); - const actualdH = Math.max(height + (dH * scale), 20); - doc.x = (doc.x || 0) + dX * (actualdW - width); - doc.y = (doc.y || 0) + dY * (actualdH - height); - const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); - if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { - layoutDoc.ignoreAspect = false; - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; - } - if (fixedAspect && (!nwidth || !nheight)) { - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; - } - if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { - if (Math.abs(dW) > Math.abs(dH)) { - if (!fixedAspect) { - layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); - } - layoutDoc.width = actualdW; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; - else layoutDoc.height = actualdH; - } - else { - if (!fixedAspect) { - layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); - } - layoutDoc.height = actualdH; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; - else layoutDoc.width = actualdW; - } - } else { - dW && (layoutDoc.width = actualdW); - dH && (layoutDoc.height = actualdH); - dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); - } - } - // let newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX)) - // this.props.Document.width = newWidth; - e.stopPropagation(); - e.preventDefault(); - } - } + // handle1PointerDown = (e: React.TouchEvent) => { + // if (!e.nativeEvent.cancelBubble) { + // const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + // this._downX = touch.clientX; + // this._downY = touch.clientY; + // this._hitTemplateDrag = false; + // for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { + // if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { + // this._hitTemplateDrag = true; + // } + // } + // if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); + // document.removeEventListener("touchmove", this.onTouch); + // document.addEventListener("touchmove", this.onTouch); + // document.removeEventListener("touchend", this.onTouchEnd); + // document.addEventListener("touchend", this.onTouchEnd); + // if ((e.nativeEvent as any).formattedHandled) e.stopPropagation(); + // console.log("down") + // } + // } + + // handle1PointerMove = (e: TouchEvent) => { + // if ((e as any).formattedHandled) { e.stopPropagation; return; } + // if (e.cancelBubble && this.active) { + // document.removeEventListener("touchmove", this.onTouch); + // } + // else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { + // const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + // if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { + // if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { + // document.removeEventListener("touchmove", this.onTouch); + // document.removeEventListener("touchend", this.onTouchEnd); + // this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); + // } + // } + // 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(); + + // } + // } + + // handle2PointersDown = (e: React.TouchEvent) => { + // if (!e.nativeEvent.cancelBubble && !this.isSelected()) { + // e.stopPropagation(); + // e.preventDefault(); + + // document.removeEventListener("touchmove", this.onTouch); + // document.addEventListener("touchmove", this.onTouch); + // document.removeEventListener("touchend", this.onTouchEnd); + // document.addEventListener("touchend", this.onTouchEnd); + // } + // } + + // @action + // handle2PointersMove = (e: TouchEvent) => { + // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + // const pt1 = myTouches[0]; + // const pt2 = myTouches[1]; + // const oldPoint1 = this.prevPoints.get(pt1.identifier); + // const oldPoint2 = this.prevPoints.get(pt2.identifier); + // const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!); + // if (pinching !== 0 && oldPoint1 && oldPoint2) { + // // let dX = (Math.min(pt1.clientX, pt2.clientX) - Math.min(oldPoint1.clientX, oldPoint2.clientX)); + // // let dY = (Math.min(pt1.clientY, pt2.clientY) - Math.min(oldPoint1.clientY, oldPoint2.clientY)); + // // let dX = Math.sign(Math.abs(pt1.clientX - oldPoint1.clientX) - Math.abs(pt2.clientX - oldPoint2.clientX)); + // // let dY = Math.sign(Math.abs(pt1.clientY - oldPoint1.clientY) - Math.abs(pt2.clientY - oldPoint2.clientY)); + // // let dW = -dX; + // // let dH = -dY; + // const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX)); + // const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY)); + // const dX = -1 * Math.sign(dW); + // const dY = -1 * Math.sign(dH); + + // if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { + // const doc = PositionDocument(this.props.Document); + // const layoutDoc = PositionDocument(Doc.Layout(this.props.Document)); + // let nwidth = layoutDoc.nativeWidth || 0; + // let nheight = layoutDoc.nativeHeight || 0; + // const width = (layoutDoc.width || 0); + // const height = (layoutDoc.height || (nheight / nwidth * width)); + // const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling(); + // const actualdW = Math.max(width + (dW * scale), 20); + // const actualdH = Math.max(height + (dH * scale), 20); + // doc.x = (doc.x || 0) + dX * (actualdW - width); + // doc.y = (doc.y || 0) + dY * (actualdH - height); + // const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); + // if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { + // layoutDoc.ignoreAspect = false; + // layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + // layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + // } + // if (fixedAspect && (!nwidth || !nheight)) { + // layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + // layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + // } + // if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { + // if (Math.abs(dW) > Math.abs(dH)) { + // if (!fixedAspect) { + // layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); + // } + // layoutDoc.width = actualdW; + // if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; + // else layoutDoc.height = actualdH; + // } + // else { + // if (!fixedAspect) { + // layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); + // } + // layoutDoc.height = actualdH; + // if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; + // else layoutDoc.width = actualdW; + // } + // } else { + // dW && (layoutDoc.width = actualdW); + // dH && (layoutDoc.height = actualdH); + // dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); + // } + // } + // // let newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX)) + // // this.props.Document.width = newWidth; + // e.stopPropagation(); + // e.preventDefault(); + // } + // } onPointerDown = (e: React.PointerEvent): void => { -- cgit v1.2.3-70-g09d2 From 145c6ae63cf6682032df1336c41589f4993cd79e Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Thu, 9 Jan 2020 15:07:28 -0800 Subject: menu handles any number of items and adjusts scale accordingly --- src/client/views/nodes/DocumentView.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b4ebe626f..19f058b1a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -125,7 +125,7 @@ export class DocumentView extends DocComponent(Docu handle1PointerHoldMove = (event: TouchEvent): void => { let e=event.touches[0]; - Math.abs(e.pageX-this._firstX)>175 ||Math.abs(e.pageY-this._firstY)>175? this.handleRelease():null; + Math.abs(e.pageX-this._firstX)>150 ||Math.abs(e.pageY-this._firstY)>150? this.handleRelease():null; document.removeEventListener("touchmove", this.handle1PointerHoldMove); document.addEventListener("touchmove", this.handle1PointerHoldMove); document.removeEventListener("touchend", this.handle1PointerHoldEnd); @@ -162,8 +162,23 @@ export class DocumentView extends DocComponent(Docu let rm = RadialMenu.Instance; rm.openMenu(); + const one = RadialMenu.Instance.findByDescription("one..."); + const two = RadialMenu.Instance.findByDescription("two..."); + const three = RadialMenu.Instance.findByDescription("three..."); + const four= RadialMenu.Instance.findByDescription("four..."); + const five= RadialMenu.Instance.findByDescription("five..."); + const six= RadialMenu.Instance.findByDescription("six..."); + const seven= RadialMenu.Instance.findByDescription("seven..."); + + + !one?rm.addItem({ description: "one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - rm.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); runInAction(() => { // cm.addItem({ // description: "Share", -- cgit v1.2.3-70-g09d2 From 36eed6cc3a3387a1f7755a848aea4e19e2645e14 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Thu, 9 Jan 2020 19:55:52 -0500 Subject: some stuff that ive played around with today. mobile interface, five fingers, other stuff --- package-lock.json | 27 +++++++++ package.json | 1 + src/client/views/GestureOverlay.scss | 2 +- src/client/views/GestureOverlay.tsx | 69 +++++++++++++--------- src/client/views/InkingControl.tsx | 21 ++++--- src/client/views/MainView.tsx | 4 +- src/client/views/Palette.scss | 18 ++++++ src/client/views/Palette.tsx | 25 ++++++++ src/client/views/Touchable.tsx | 18 ++++++ .../views/collections/CollectionSchemaView.scss | 14 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 54 +++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 17 +----- src/mobile/ImageUpload.tsx | 31 +++++++--- src/mobile/MobileInterface.tsx | 69 ++++++++++++++++++++++ src/new_fields/InkField.ts | 3 +- src/pen-gestures/GestureUtils.ts | 3 +- .../authentication/models/current_user_utils.ts | 26 ++++++++ 17 files changed, 335 insertions(+), 67 deletions(-) create mode 100644 src/client/views/Palette.scss create mode 100644 src/client/views/Palette.tsx create mode 100644 src/mobile/MobileInterface.tsx (limited to 'src') diff --git a/package-lock.json b/package-lock.json index d807d75a1..244b2e7e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2040,6 +2040,11 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "avl": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/avl/-/avl-1.4.4.tgz", + "integrity": "sha512-vMjF4jhT2N0naO1fsBTA/lZ2AB70ECBv41na7oBe8B5PCgMVvNrWG+VRKMlbG5Mk5UGbMxzUD9fXK/IBsmmO5A==" + }, "awesome-typescript-loader": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", @@ -2314,6 +2319,14 @@ "tweetnacl": "^0.14.3" } }, + "bentley-ottman-sweepline": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/bentley-ottman-sweepline/-/bentley-ottman-sweepline-0.1.4.tgz", + "integrity": "sha512-Qptw9uoNLM0zLiJfA+9PqFrF5b6pMiJNyoWgqONq4/wVml4NC2N8S4RXYXn4xXR4JEBhHF+euVwwML7D9X1LqA==", + "requires": { + "avl": "^1.4.0" + } + }, "better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -4316,6 +4329,14 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emit-logger": { + "version": "github:chocolateboy/emit-logger#b9d25a2d939e42f29c940861e9648bd0fb810070", + "from": "github:chocolateboy/emit-logger#better-emitter-name", + "requires": { + "chalk": "^1.1.1", + "moment": "^2.10.6" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -7065,6 +7086,7 @@ "babel-runtime": "^6.22.0", "bluebird": "^3.4.7", "boolify-string": "^2.0.2", + "emit-logger": "github:chocolateboy/emit-logger#better-emitter-name", "lodash": "^4.17.4", "semver": "^5.0.3", "source-map-support": "^0.5.9" @@ -8545,6 +8567,11 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, "mongodb": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", diff --git a/package.json b/package.json index 9bd2273f8..2a72fac80 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "async": "^2.6.2", "babel-runtime": "^6.26.0", "bcrypt-nodejs": "0.0.3", + "bentley-ottman-sweepline": "^0.1.4", "bluebird": "^3.5.3", "body-parser": "^1.18.3", "bootstrap": "^4.3.1", diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index cbc1b6e7d..31601efd4 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -1,6 +1,6 @@ .gestureOverlay-cont { width: 100vw; - height: 100vw; + height: 100vh; position: absolute; top: 0; left: 0; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index c88e3f7ae..848927912 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -43,9 +43,43 @@ export default class GestureOverlay extends Touchable { @action onPointerMove = (e: PointerEvent) => { - this._points.push({ X: e.clientX, Y: e.clientY }); - e.stopPropagation(); - e.preventDefault(); + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + } + } + + handleLineGesture = (): boolean => { + let actionPerformed = false; + const B = this.svgBounds; + const ep1 = this._points[0]; + const ep2 = this._points[this._points.length - 1]; + + const target1 = document.elementFromPoint(ep1.X, ep1.Y); + const target2 = document.elementFromPoint(ep2.X, ep2.Y); + const callback = (doc: Doc) => { + if (!this._d1) { + this._d1 = doc; + } + else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { + DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); + actionPerformed = true; + } + } + const ge = new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Line, + bounds: B, + callbackFn: callback + } + }); + target1?.dispatchEvent(ge); + target2?.dispatchEvent(ge); + return actionPerformed; } @action @@ -72,31 +106,10 @@ export default class GestureOverlay extends Touchable { actionPerformed = true; break; case GestureUtils.Gestures.Line: - const ep1 = this._points[0]; - const ep2 = this._points[this._points.length - 1]; - const target1 = document.elementFromPoint(ep1.X, ep1.Y); - const target2 = document.elementFromPoint(ep2.X, ep2.Y); - const callback = (doc: Doc) => { - if (!this._d1) { - this._d1 = doc; - } - else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { - DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); - actionPerformed = true; - } - } - const ge = new CustomEvent("dashOnGesture", - { - bubbles: true, - detail: { - points: this._points, - gesture: GestureUtils.Gestures.Line, - bounds: B, - callbackFn: callback - } - }) - target1?.dispatchEvent(ge); - target2?.dispatchEvent(ge); + actionPerformed = this.handleLineGesture(); + break; + case GestureUtils.Gestures.Scribble: + console.log("scribble"); break; } if (actionPerformed) { diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index e33f193b8..243123352 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -4,19 +4,18 @@ import { Doc } from "../../new_fields/Doc"; import { InkTool } from "../../new_fields/InkField"; import { List } from "../../new_fields/List"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast, FieldValue } from "../../new_fields/Types"; import { Utils } from "../../Utils"; import { Scripting } from "../util/Scripting"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; - export class InkingControl { @observable static Instance: InkingControl; - @observable private _selectedTool: InkTool = InkTool.None; - @observable private _selectedColor: string = "rgb(244, 67, 54)"; - @observable private _selectedWidth: string = "5"; + @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; } + @computed private get _selectedColor(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } + @computed private get _selectedWidth(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } @observable public _open: boolean = false; constructor() { @@ -24,7 +23,8 @@ export class InkingControl { } switchTool = action((tool: InkTool): void => { - this._selectedTool = tool; + // this._selectedTool = tool; + CurrentUserUtils.UserDocument.inkTool = tool; }); decimalToHexString(number: number) { if (number < 0) { @@ -36,7 +36,7 @@ export class InkingControl { @undoBatch switchColor = action((color: ColorState): void => { - this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); + CurrentUserUtils.UserDocument.inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { const selected = SelectionManager.SelectedDocuments(); @@ -99,7 +99,8 @@ export class InkingControl { }); @action switchWidth = (width: string): void => { - this._selectedWidth = width; + // this._selectedWidth = width; + CurrentUserUtils.UserDocument.inkWidth = width; } @computed @@ -114,7 +115,8 @@ export class InkingControl { @action updateSelectedColor(value: string) { - this._selectedColor = value; + // this._selectedColor = value; + CurrentUserUtils.UserDocument.inkColor = value; } @computed @@ -127,6 +129,7 @@ Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { Ink 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 activateScrubber(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Scrubber : InkTool.None); }); +Scripting.addGlobal(function activateStamp(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Stamp : InkTool.None); }); Scripting.addGlobal(function deactivateInk() { return InkingControl.Instance.switchTool(InkTool.None); }); Scripting.addGlobal(function setInkWidth(width: any) { return InkingControl.Instance.switchWidth(width); }); Scripting.addGlobal(function setInkColor(color: any) { return InkingControl.Instance.updateSelectedColor(color); }); \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 14c1803c0..f463ca306 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, - faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt + faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -135,6 +135,8 @@ export class MainView extends React.Component { library.add(faChevronRight); library.add(faEllipsisV); library.add(faMusic); + library.add(faPhone); + library.add(faStamp); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss new file mode 100644 index 000000000..60004c81f --- /dev/null +++ b/src/client/views/Palette.scss @@ -0,0 +1,18 @@ +.palette-container { + .palette-thumb { + width: 300px; + height: 300px; + touch-action: pan-x; + overflow: scroll; + + .palette-thumbContent { + width: 100%; + height: 100%; + + .palette-button { + width: 100px; + height: 100px; + } + } + } +} \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx new file mode 100644 index 000000000..390b7e2c2 --- /dev/null +++ b/src/client/views/Palette.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; +import "./Palette.scss"; +import { PointData } from "../../new_fields/InkField"; + +export interface PaletteProps { + x: number; + y: number; + thumb: number[]; +} + +export default class Palette extends React.Component { + render() { + return ( +
+
+
+
console.log("hi")}>1
+
2
+
3
+
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 251cd41e5..3eb66ff72 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { action } from 'mobx'; import { InteractionUtils } from '../util/InteractionUtils'; +import { SelectionManager } from '../util/SelectionManager'; const HOLD_DURATION = 1000; @@ -34,11 +35,19 @@ export abstract class Touchable extends React.Component { case 1: this.handle1PointerDown(e); e.persist(); + if (this.holdTimer) { + clearTimeout(this.holdTimer) + this.holdTimer = undefined; + } this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(e), HOLD_DURATION); + console.log(this.holdTimer); break; case 2: this.handle2PointersDown(e); break; + case 5: + this.handleHandDown(e); + break; } } } @@ -54,7 +63,9 @@ export abstract class Touchable extends React.Component { if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { + console.log("clear"); clearTimeout(this.holdTimer); + this.holdTimer = undefined; } switch (myTouches.length) { case 1: @@ -88,7 +99,9 @@ export abstract class Touchable extends React.Component { } } if (this.holdTimer) { + console.log("clear"); clearTimeout(this.holdTimer); + this.holdTimer = undefined; } this._touchDrag = false; e.stopPropagation(); @@ -137,4 +150,9 @@ export abstract class Touchable extends React.Component { e.stopPropagation(); e.preventDefault(); } + + handleHandDown = (e: React.TouchEvent) => { + e.stopPropagation(); + e.preventDefault(); + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index cb95dcbbc..8b3d332af 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -15,6 +15,11 @@ display: flex; justify-content: space-between; flex-wrap: nowrap; + touch-action: none; + + div { + touch-action: none; + } .collectionSchemaView-tableContainer { @@ -49,7 +54,7 @@ .rt-table { height: 100%; display: -webkit-inline-box; - direction: ltr; + direction: ltr; overflow: visible; } @@ -202,7 +207,7 @@ button.add-column { border-bottom: 1px solid lightgray; &:first-child { - padding-top : 0; + padding-top: 0; } &:last-child { @@ -231,7 +236,7 @@ button.add-column { transition: background-color 0.2s; &:hover { - background-color: $light-color-secondary; + background-color: $light-color-secondary; } &.active { @@ -267,7 +272,7 @@ button.add-column { overflow-y: scroll; position: absolute; top: 28px; - box-shadow: 0 10px 16px rgba(0,0,0,0.1); + box-shadow: 0 10px 16px rgba(0, 0, 0, 0.1); .key-option { background-color: $light-color; @@ -380,6 +385,7 @@ button.add-column { &.editing { padding: 0; + input { outline: 0; border: none; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9656debf3..d7cccc036 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,6 +44,7 @@ import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { LinkManager } from "../../../util/LinkManager"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; +import Palette from "../../Palette"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -444,6 +445,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return; } + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + return; + } if (!e.cancelBubble) { const selectedTool = InkingControl.Instance.selectedTool; if (selectedTool === InkTool.None) { @@ -844,6 +848,53 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], inkData); } + private thumbIdentifier?: number; + private hand?: React.Touch[]; + + @action + handleHandDown = (e: React.TouchEvent) => { + const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + this.hand = fingers; + const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + this.thumbIdentifier = thumb?.identifier; + const others = fingers.filter(f => f !== thumb); + const minX = Math.min(...others.map(f => f.clientX)); + const minY = Math.min(...others.map(f => f.clientY)); + const t = this.getTransform().transformPoint(minX, minY); + const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); + this._palette = ; + + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchmove", this.handleHandMove); + document.addEventListener("touchmove", this.handleHandMove); + document.removeEventListener("touchend", this.handleHandUp); + document.addEventListener("touchend", this.handleHandUp); + } + + @action + handleHandMove = (e: TouchEvent) => { + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.changedTouches.item(i); + if (pt?.identifier === this.thumbIdentifier) { + } + } + } + + @action + handleHandUp = (e: TouchEvent) => { + console.log(e.changedTouches.length); + this.onTouchEnd(e); + if (this.prevPoints.size < 3) { + if (this.hand) { + for (const h of this.hand) { + this.prevPoints.has(h.identifier) && this.prevPoints.delete(h.identifier); + } + } + this._palette = undefined; + document.removeEventListener("touchend", this.handleHandUp); + } + } + onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; @@ -907,9 +958,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } + @observable private _palette?: JSX.Element; + children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); + this._palette && (eles.push(this._palette)); // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); return eles; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7d6b8bf01..415d0e2ef 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -256,7 +256,6 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("touchend", this.onTouchEnd); document.addEventListener("touchend", this.onTouchEnd); if ((e.nativeEvent as any).formattedHandled) e.stopPropagation(); - console.log("down") } } @@ -327,6 +326,7 @@ export class DocumentView extends DocComponent(Docu const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; + layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; } @@ -357,8 +357,6 @@ export class DocumentView extends DocComponent(Docu dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } - // let newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX)) - // this.props.Document.width = newWidth; e.stopPropagation(); e.preventDefault(); } @@ -374,19 +372,7 @@ export class DocumentView extends DocComponent(Docu } return; } - // if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { - // e.stopPropagation(); - // return; - // } if (!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart) { - // if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) - // // return if we're inking, and not selecting a button document - // || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick) - // // return if using pen or eraser - // || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) { - // return; - // } - this._downX = e.clientX; this._downY = e.clientY; this._hitTemplateDrag = false; @@ -408,6 +394,7 @@ export class DocumentView extends DocComponent(Docu onPointerMove = (e: PointerEvent): void => { if ((e as any).formattedHandled) { e.stopPropagation(); return; } + if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) return; if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index ec698b151..0b0280519 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -11,6 +11,8 @@ import { List } from '../new_fields/List'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; import { Utils } from '../Utils'; +import MobileInterface from './MobileInterface'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; @@ -104,10 +106,25 @@ class Uploader extends React.Component { } -DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); - -ReactDOM.render(( - -), - document.getElementById('root') -); \ No newline at end of file +// DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); +(async () => { + const info = await CurrentUserUtils.loadCurrentUser(); + DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + "mobile"); + await Docs.Prototypes.initialize(); + if (info.id !== "__guest__") { + // a guest will not have an id registered + await CurrentUserUtils.loadUserDocument(info); + } + document.getElementById('root')!.addEventListener('wheel', event => { + if (event.ctrlKey) { + event.preventDefault(); + } + }, true); + ReactDOM.render(( + // + + ), + document.getElementById('root') + ); +} +)(); \ No newline at end of file diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx new file mode 100644 index 000000000..ea2fc917f --- /dev/null +++ b/src/mobile/MobileInterface.tsx @@ -0,0 +1,69 @@ +import React = require('react'); +import { observer } from 'mobx-react'; +import { computed, action } from 'mobx'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { FieldValue, Cast } from '../new_fields/Types'; +import { Doc } from '../new_fields/Doc'; +import { Docs } from '../client/documents/Documents'; +import { CollectionView } from '../client/views/collections/CollectionView'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from '../Utils'; +import { Transform } from '../client/util/Transform'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faPenNib, faHighlighter, faEraser, faMousePointer } from '@fortawesome/free-solid-svg-icons'; + +@observer +export default class MobileInterface extends React.Component { + @computed private get userDoc() { return CurrentUserUtils.UserDocument; } + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } + + @action + componentDidMount = () => { + library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]); + + if (this.userDoc && !this.mainContainer) { + const doc = CurrentUserUtils.setupMobileDoc(this.userDoc); + this.userDoc.activeMobile = doc; + } + } + + @computed + get mainContent() { + if (this.mainContainer) { + return window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + ; + } + return "hello"; + } + + render() { + return ( +
+ {this.mainContent} +
+ ); + } +} \ No newline at end of file diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 83d631958..20dbf4b17 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -9,7 +9,8 @@ export enum InkTool { Pen, Highlighter, Eraser, - Scrubber + Scrubber, + Stamp } export interface PointData { diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 4edcfa623..062604458 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -37,7 +37,8 @@ export namespace GestureUtils { export enum Gestures { Box = "box", Line = "line", - Stroke = "stroke" + Stroke = "stroke", + Scribble = "scribble" } export const GestureRecognizer = new NDollarRecognizer(false); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 220c37e2b..71dd34e68 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -28,6 +28,7 @@ export class CurrentUserUtils { @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; + @observable public static GuestMobile: Doc | undefined; // a default set of note types .. not being used yet... static setupNoteTypes(doc: Doc) { @@ -55,8 +56,10 @@ export class CurrentUserUtils { { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, { title: "presentation", icon: "tv", ignoreClick: true, drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, + { title: "mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc }, { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, @@ -90,6 +93,29 @@ export class CurrentUserUtils { } } + static setupMobileButtons(doc: Doc, buttons?: string[]) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" }, + { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, + { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc }, + { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, + ]; + return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({ + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, + })); + } + + static setupMobileDoc(userDoc: Doc) { + return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), { + columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + }); + } + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators -- cgit v1.2.3-70-g09d2 From e2a2db0dfe05e0d17521d3f5bfae6fbf9b03bc3c Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Thu, 9 Jan 2020 20:54:36 -0800 Subject: now fully functional, just needs bugfixing for implementation in different classes --- src/client/views/nodes/DocumentView.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 19f058b1a..c9a56b46f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -171,13 +171,13 @@ export class DocumentView extends DocComponent(Docu const seven= RadialMenu.Instance.findByDescription("seven..."); - !one?rm.addItem({ description: "one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; - !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }):null; + !one?rm.addItem({ description: "one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; + !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; runInAction(() => { // cm.addItem({ -- cgit v1.2.3-70-g09d2 From 11abadccd0d12ce39b059c9df87b771ddec21936 Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Thu, 9 Jan 2020 20:58:54 -0800 Subject: Adding missing files --- src/client/views/nodes/RadialMenu.scss | 83 ++++++++++++ src/client/views/nodes/RadialMenu.tsx | 218 ++++++++++++++++++++++++++++++ src/client/views/nodes/RadialMenuItem.tsx | 96 +++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 src/client/views/nodes/RadialMenu.scss create mode 100644 src/client/views/nodes/RadialMenu.tsx create mode 100644 src/client/views/nodes/RadialMenuItem.tsx (limited to 'src') diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss new file mode 100644 index 000000000..ce0c263ef --- /dev/null +++ b/src/client/views/nodes/RadialMenu.scss @@ -0,0 +1,83 @@ +@import "../globalCssVariables"; + +.radialMenu-cont { + position: absolute; + z-index: $radialMenu-zindex; + flex-direction: column; +} + +.radialMenu-subMenu-cont { + position: absolute; + display: flex; + z-index: 1000; + flex-direction: column; + border-radius: 15px; + padding-top: 10px; + padding-bottom: 10px; +} + +.radialMenu-item { + // width: 11vw; //10vw + display: flex; //comment out to allow search icon to be inline with search text + align-items: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-style: none; + white-space: nowrap; + font-size: 13px; + letter-spacing: 2px; + text-transform: uppercase; +} + +s +.radialMenu-itemSelected { + border-style: none; +} + +.radialMenu-group { + // width: 11vw; //10vw + display: flex; //comment out to allow search icon to be inline with search text + justify-content: left; + align-items: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-width: .11px; + border-style: none; + border-color: $intermediate-color; // rgb(187, 186, 186); + // padding: 10px 0px 10px 0px; + white-space: nowrap; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 2px; + padding-left: 5px; +} + + +.radialMenu-description { + margin-left: 5px; + text-align: left; + display: inline; //need this? +} + + + +.icon-background { + pointer-events: all; + height:100%; + margin-top: 15px; + background-color: transparent; + width: 35px; + text-align: center; + font-size: 20px; + margin-left: 5px; +} \ No newline at end of file diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx new file mode 100644 index 000000000..4fbb40c5f --- /dev/null +++ b/src/client/views/nodes/RadialMenu.tsx @@ -0,0 +1,218 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { action, observable, computed, IReactionDisposer, reaction, runInAction } from "mobx"; +import { RadialMenuItem, RadialMenuProps, OriginalMenuProps } from "./RadialMenuItem"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import Measure from "react-measure"; +import "./RadialMenu.scss"; + + + +@observer +export class RadialMenu extends React.Component { + static Instance: RadialMenu; + static readonly buffer = 20; + + constructor(props: Readonly<{}>) { + super(props); + + RadialMenu.Instance = this; + } + + @observable private _mouseX: number = -1; + @observable private _mouseY: number = -1; + @observable private _shouldDisplay: boolean = false; + @observable private _mouseDown: boolean = false; + private _reactionDisposer?: IReactionDisposer; + + + @action + onPointerDown = (e: PointerEvent) => { + this._mouseDown = true; + this._mouseX = e.clientX; + this._mouseY = e.clientY; + document.addEventListener("pointermove", this.onPointerMove); + } + + @observable + private _closest:number=-1; + + @action + onPointerMove = (e: PointerEvent) => { + const curX = e.clientX; + const curY = e.clientY; + const deltX = this._mouseX-curX + const deltY = this._mouseY-curY + const scale = Math.hypot(deltY,deltX) + + if (scale <150 && scale > 50){ + const rad = Math.atan2(deltY,deltX)+Math.PI; + let closest =0; + let closestval = 999999999; + for (let x =0; x0){ + closestval=rad-curmin + closest=x; + } + } + this._closest=closest; + } + else{ + this._closest=-1; + } + } + @action + onPointerUp = (e: PointerEvent) => { + this._mouseDown = false; + const curX = e.clientX; + const curY = e.clientY; + if (this._mouseX !== curX || this._mouseY !== curY) { + this._shouldDisplay = false; + } + this._shouldDisplay && (this._display = true); + document.removeEventListener("pointermove", this.onPointerMove); + if (this._closest!==-1){ + this._items[this._closest].event(); + } + } + componentWillUnmount() { + document.removeEventListener("pointerdown", this.onPointerDown); + + document.removeEventListener("pointerup", this.onPointerUp); + this._reactionDisposer && this._reactionDisposer(); + } + + @action + componentDidMount = () => { + document.addEventListener("pointerdown", this.onPointerDown); + document.addEventListener("pointerup", this.onPointerUp); + + this._reactionDisposer = reaction( + () => this._shouldDisplay, + () => this._shouldDisplay && !this._mouseDown && runInAction(() => this._display = true) + ); + } + + @observable private _pageX: number = 0; + @observable private _pageY: number = 0; + @observable private _display: boolean = false; + @observable private _yRelativeToTop: boolean = true; + + + @observable private _width: number = 0; + @observable private _height: number = 0; + + + getItems() { + return this._items; + } + + findByDescription = (target: string, toLowerCase = false) => { + return this._items.find(menuItem => { + let reference = menuItem.description; + toLowerCase && (reference = reference.toLowerCase()); + return reference === target; + }); + } + + + @action + addItem(item: RadialMenuProps) { + if (this._items.indexOf(item) === -1) { + this._items.push(item); + } + } + + @observable + private _items: Array = []; + + @action + displayMenu = (x: number, y: number) => { + //maxX and maxY will change if the UI/font size changes, but will work for any amount + //of items added to the menu + + this._pageX = x; + this._pageY = y; + this._shouldDisplay = true; + } + + get pageX() { + const x = this._pageX; + if (x < 0) { + return 0; + } + const width = this._width; + if (x + width > window.innerWidth - RadialMenu.buffer) { + return window.innerWidth - RadialMenu.buffer - width; + } + return x; + } + + get pageY() { + const y = this._pageY; + if (y < 0) { + return 0; + } + const height = this._height; + if (y + height > window.innerHeight - RadialMenu.buffer) { + return window.innerHeight - RadialMenu.buffer - height; + } + return y; + } + + @computed get menuItems() { + return this._items.map((item,index) => ); + } + + @action + closeMenu = () => { + this.clearItems(); + this._display = false; + this._shouldDisplay = false; + } + + @action + openMenu = () => { + this._shouldDisplay; + this._display = true; + } + + @action + clearItems() { + this._items = []; + } + + render() { + if (!this._display) { + return null; + } + const style = this._yRelativeToTop ? { left: this._mouseX-150, top: this._mouseY-150 } : + { left: this._mouseX-150, bottom: this._mouseY+150 }; + + const contents = ( + <> + {this.menuItems} + + ); + // return ( + // { this._width = r.offset.width; this._height = r.offset.height; })}> + // {({ measureRef }) => ( + //
+ // {contents} + //
+ // ) + // } + //
+ // ); + return ( + +
+ {contents} +
+ + ); + } + + +} \ No newline at end of file diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx new file mode 100644 index 000000000..727d1c3be --- /dev/null +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -0,0 +1,96 @@ +import React = require("react"); +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; +import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { UndoManager } from "../../util/UndoManager"; + +library.add(faAngleRight); + +export interface OriginalMenuProps { + description: string; + event: (stuff?: any) => void; + undoable?: boolean; + icon: IconProp; //maybe should be optional (icon?) + closeMenu?: () => void; + min?: number; + max?:number; + selected:number; +} + + +export type RadialMenuProps = OriginalMenuProps; + +@observer +export class RadialMenuItem extends React.Component { + + componentDidMount = () =>{ + this.setcircle(); + } + + componentDidUpdate = () =>{ + this.setcircle(); + } + + handleEvent = async (e: React.MouseEvent) => { + if ("event" in this.props) { + this.props.closeMenu && this.props.closeMenu(); + let batch: UndoManager.Batch | undefined; + if (this.props.undoable !== false) { + batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); + } + await this.props.event({ x: e.clientX, y: e.clientY }); + batch && batch.end(); + } + } + + + setcircle(){ + let circlemin=0; + let circlemax=1 + this.props.min? circlemin=this.props.min:null; + this.props.max? circlemax=this.props.max:null; + if (document.getElementById("myCanvas")!==null){ + var c : any= document.getElementById("myCanvas"); + let color = "white" + switch(circlemin%3){ + case 1: + color = "#c2c2c5"; + break; + case 0: + color = "white"; + break; + case 2: + color = "lightgray"; + break; + } + if (circlemax%3===1 && circlemin===circlemax-1){ + color="#c2c2c5"; + } + console.log(this.props.selected,this.props.min) + + if (this.props.selected === this.props.min){ + console.log(this.props.selected,this.props.min) + color="#808080"; + + } + if (c.getContext){ + var ctx = c.getContext("2d"); + ctx.beginPath(); + ctx.arc(150, 150, 150, (circlemin/circlemax)*2*Math.PI, ((circlemin+1)/circlemax) * 2 * Math.PI); + ctx.arc(150, 150, 50, ((circlemin+1)/circlemax)*2*Math.PI, (circlemin/circlemax) * 2 * Math.PI,true); + ctx.fillStyle=color; + ctx.fill() + } + } + } + + render() { + return ( +
+ Your browser does not support the HTML5 canvas tag. +
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2379bdba987e524806eecd62cf439cd2bdad3db8 Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Thu, 9 Jan 2020 23:54:49 -0800 Subject: added icons --- src/client/views/nodes/DocumentView.tsx | 13 ++++++------- src/client/views/nodes/RadialMenu.tsx | 2 +- src/client/views/nodes/RadialMenuItem.tsx | 31 ++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c9a56b46f..9bc0244ba 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -146,7 +146,6 @@ export class DocumentView extends DocComponent(Docu @action onRadialMenu = async (event: React.TouchEvent): Promise => { - console.log("YUH") // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 // if (e.button === 0) { // e.preventDefault(); @@ -166,18 +165,18 @@ export class DocumentView extends DocComponent(Docu const two = RadialMenu.Instance.findByDescription("two..."); const three = RadialMenu.Instance.findByDescription("three..."); const four= RadialMenu.Instance.findByDescription("four..."); - const five= RadialMenu.Instance.findByDescription("five..."); - const six= RadialMenu.Instance.findByDescription("six..."); - const seven= RadialMenu.Instance.findByDescription("seven..."); + // const five= RadialMenu.Instance.findByDescription("five..."); + // const six= RadialMenu.Instance.findByDescription("six..."); + // const seven= RadialMenu.Instance.findByDescription("seven..."); !one?rm.addItem({ description: "one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; - !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; + // !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + // !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; + // !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; runInAction(() => { // cm.addItem({ diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx index 4fbb40c5f..b0cf3bb98 100644 --- a/src/client/views/nodes/RadialMenu.tsx +++ b/src/client/views/nodes/RadialMenu.tsx @@ -188,7 +188,7 @@ export class RadialMenu extends React.Component { return null; } const style = this._yRelativeToTop ? { left: this._mouseX-150, top: this._mouseY-150 } : - { left: this._mouseX-150, bottom: this._mouseY+150 }; + { left: this._mouseX-150, top: this._mouseY-150 }; const contents = ( <> diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx index 727d1c3be..f077dd447 100644 --- a/src/client/views/nodes/RadialMenuItem.tsx +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -12,7 +12,7 @@ export interface OriginalMenuProps { description: string; event: (stuff?: any) => void; undoable?: boolean; - icon: IconProp; //maybe should be optional (icon?) + icon: IconProp; closeMenu?: () => void; min?: number; max?:number; @@ -68,10 +68,8 @@ export class RadialMenuItem extends React.Component Your browser does not support the HTML5 canvas tag. +
); } -- cgit v1.2.3-70-g09d2 From 32ff2b6e39dfd943c784c3cb6afa8806ad3d11b9 Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Fri, 10 Jan 2020 19:03:08 -0800 Subject: added text in the middle of the menu for description --- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/RadialMenu.tsx | 34 ++++++++++++++++++++++++++++++- src/client/views/nodes/RadialMenuItem.tsx | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9bc0244ba..49d35637e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -170,7 +170,7 @@ export class DocumentView extends DocComponent(Docu // const seven= RadialMenu.Instance.findByDescription("seven..."); - !one?rm.addItem({ description: "one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + !one?rm.addItem({ description: "one one one one one one one one one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx index b0cf3bb98..d8750a192 100644 --- a/src/client/views/nodes/RadialMenu.tsx +++ b/src/client/views/nodes/RadialMenu.tsx @@ -87,13 +87,17 @@ export class RadialMenu extends React.Component { componentDidMount = () => { document.addEventListener("pointerdown", this.onPointerDown); document.addEventListener("pointerup", this.onPointerUp); - + this.previewcircle(); this._reactionDisposer = reaction( () => this._shouldDisplay, () => this._shouldDisplay && !this._mouseDown && runInAction(() => this._display = true) ); } + componentDidUpdate = () =>{ + this.previewcircle(); + } + @observable private _pageX: number = 0; @observable private _pageY: number = 0; @observable private _display: boolean = false; @@ -183,6 +187,33 @@ export class RadialMenu extends React.Component { this._items = []; } + + previewcircle(){ + if (document.getElementById("newCanvas")!==null){ + var c : any= document.getElementById("newCanvas"); + if (c.getContext){ + var ctx = c.getContext("2d"); + ctx.beginPath(); + ctx.arc(150, 150, 50, 0, 2 * Math.PI); + ctx.fillStyle="white"; + ctx.fill() + ctx.font = "12px Arial"; + ctx.fillStyle = "black"; + ctx.textAlign = "center"; + let description =""; + if (this._closest!==-1){ + description = this._items[this._closest].description; + } + if (description.length>15){ + description= description.slice(0,12); + description += "..."; + } + ctx.fillText(description, 150, 150, 90); + } + } + } + + render() { if (!this._display) { return null; @@ -208,6 +239,7 @@ export class RadialMenu extends React.Component { return (
+ Your browser does not support the HTML5 canvas tag. {contents}
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx index f077dd447..4ca257df5 100644 --- a/src/client/views/nodes/RadialMenuItem.tsx +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -59,7 +59,7 @@ export class RadialMenuItem extends React.Component Date: Mon, 13 Jan 2020 15:25:07 -0500 Subject: palette events are now on gestureOverlay, but cffv and dv is still capturing one and two finger events, which makes it impossible for the gestureoverlay to capture 5 fingers spread across different targets --- src/client/documents/Documents.ts | 3 + src/client/util/InteractionUtils.ts | 10 ++- src/client/views/GestureOverlay.tsx | 64 ++++++++++++++- src/client/views/InkingControl.tsx | 5 +- src/client/views/Palette.scss | 10 ++- src/client/views/Palette.tsx | 44 +++++++++- src/client/views/Touchable.tsx | 21 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 94 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 24 +++++- src/new_fields/documentSchemas.ts | 2 + .../authentication/models/current_user_utils.ts | 23 ++++++ 11 files changed, 224 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1853be529..64abd4f57 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -103,6 +103,8 @@ export interface DocumentOptions { ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + onPointerDown?: ScriptField; + onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; @@ -116,6 +118,7 @@ export interface DocumentOptions { color?: string; limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; + pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown } class EmptyBox { diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index 2e4e8c7ca..76b43da3c 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -8,12 +8,14 @@ export namespace InteractionUtils { const REACT_POINTER_PEN_BUTTON = 0; const ERASER_BUTTON = 5; - export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map): React.Touch[] { + export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); for (let i = 0; i < e.targetTouches.length; i++) { - const pt = e.targetTouches.item(i); + const pt: any = e.targetTouches.item(i); if (pt && prevPoints.has(pt.identifier)) { - myTouches.push(pt); + if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + myTouches.push(pt); + } } } return myTouches; @@ -23,7 +25,7 @@ export namespace InteractionUtils { 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 === (e instanceof PointerEvent ? POINTER_PEN_BUTTON : REACT_POINTER_PEN_BUTTON); + 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); default: diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 848927912..830c06f1f 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; import "./GestureOverlay.scss" -import { computed, observable, action } from "mobx"; +import { computed, observable, action, runInAction } from "mobx"; import { CreatePolyline } from "./InkingStroke"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; @@ -12,14 +12,22 @@ import { Doc } from "../../new_fields/Doc"; import { LinkManager } from "../util/LinkManager"; import { DocUtils } from "../documents/Documents"; import { undoBatch } from "../util/UndoManager"; +import { Scripting } from "../util/Scripting"; +import { FieldValue, Cast } from "../../new_fields/Types"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import Palette from "./Palette"; @observer export default class GestureOverlay extends Touchable { static Instance: GestureOverlay; @observable private _points: { X: number, Y: number }[] = []; + @observable private _palette?: JSX.Element; + @observable public Color: string = "rgb(244, 67, 54)"; + @observable public Width: number = 5; private _d1: Doc | undefined; + private thumbIdentifier?: number; constructor(props: Readonly<{}>) { super(props); @@ -27,6 +35,47 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } + @action + handleHandDown = (e: React.TouchEvent) => { + const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + this.thumbIdentifier = thumb?.identifier; + const others = fingers.filter(f => f !== thumb); + const minX = Math.min(...others.map(f => f.clientX)); + const minY = Math.min(...others.map(f => f.clientY)); + // const t = this.getTransform().transformPoint(minX, minY); + // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); + + const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + if (thumbDoc) { + this._palette = ; + } + + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchmove", this.handleHandMove); + document.addEventListener("touchmove", this.handleHandMove); + document.removeEventListener("touchend", this.handleHandUp); + document.addEventListener("touchend", this.handleHandUp); + } + + @action + handleHandMove = (e: TouchEvent) => { + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.changedTouches.item(i); + if (pt?.identifier === this.thumbIdentifier) { + } + } + } + + @action + handleHandUp = (e: TouchEvent) => { + this.onTouchEnd(e); + if (this.prevPoints.size < 3) { + this._palette = undefined; + document.removeEventListener("touchend", this.handleHandUp); + } + } + @action onPointerDown = (e: React.PointerEvent) => { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { @@ -82,6 +131,8 @@ export default class GestureOverlay extends Touchable { return actionPerformed; } + + @action onPointerUp = (e: PointerEvent) => { if (this._points.length > 1) { @@ -157,16 +208,21 @@ export default class GestureOverlay extends Touchable { return ( - {CreatePolyline(this._points, B.left, B.top)} + {CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} ); } render() { return ( -
+
{this.props.children} + {this._palette} {this.currentStroke}
); } -} \ No newline at end of file +} + +Scripting.addGlobal("GestureOverlay", GestureOverlay); +Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); +Scripting.addGlobal(function resetPen() { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); \ No newline at end of file diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 243123352..be07a9024 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -10,12 +10,13 @@ import { Scripting } from "../util/Scripting"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import GestureOverlay from "./GestureOverlay"; export class InkingControl { @observable static Instance: InkingControl; @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; } - @computed private get _selectedColor(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } - @computed private get _selectedWidth(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } + @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } + @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } @observable public _open: boolean = false; constructor() { diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss index 60004c81f..2626774cb 100644 --- a/src/client/views/Palette.scss +++ b/src/client/views/Palette.scss @@ -4,15 +4,17 @@ height: 300px; touch-action: pan-x; overflow: scroll; + position: absolute; .palette-thumbContent { width: 100%; height: 100%; + } - .palette-button { - width: 100px; - height: 100px; - } + .palette-button { + width: 100px; + height: 100px; + background: blue; } } } \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 390b7e2c2..3649cccfe 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -1,22 +1,60 @@ import * as React from "react"; import "./Palette.scss"; import { PointData } from "../../new_fields/InkField"; +import { Doc } from "../../new_fields/Doc"; +import { Docs } from "../documents/Documents"; +import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; +import { List } from "../../new_fields/List"; +import { DocumentView } from "./nodes/DocumentView"; +import { emptyPath, returnFalse, emptyFunction, returnOne, returnEmptyString, returnTrue } from "../../Utils"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { Transform } from "../util/Transform"; +import { computed, action } from "mobx"; +import { FieldValue, Cast } from "../../new_fields/Types"; +import { observer } from "mobx-react"; +import { DocumentContentsView } from "./nodes/DocumentContentsView"; +import { CollectionStackingView } from "./collections/CollectionStackingView"; +import { CollectionView } from "./collections/CollectionView"; export interface PaletteProps { x: number; y: number; thumb: number[]; + thumbDoc: Doc; } +@observer export default class Palette extends React.Component { render() { return (
-
console.log("hi")}>1
-
2
-
3
+ window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> +
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 3eb66ff72..24ea801a0 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -21,15 +21,26 @@ export abstract class Touchable extends React.Component { */ @action protected onTouchStart = (e: React.TouchEvent): void => { + const actualPts: React.Touch[] = []; for (let i = 0; i < e.targetTouches.length; i++) { const pt: any = e.targetTouches.item(i); + actualPts.push(pt); // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) // and this seems to be the only way of differentiating pen and touch on touch events - if (pt.radiusX > 0.5 && pt.radiusY > 0.5) { + if (pt.radiusX > 1 && pt.radiusY > 1) { this.prevPoints.set(pt.identifier, pt); } } + const ptsToDelete: number[] = []; + this.prevPoints.forEach(pt => { + if (!actualPts.includes(pt)) { + ptsToDelete.push(pt.identifier); + } + }); + + ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + if (this.prevPoints.size) { switch (this.prevPoints.size) { case 1: @@ -57,13 +68,12 @@ export abstract class Touchable extends React.Component { */ @action protected onTouch = (e: TouchEvent): void => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); // if we're not actually moving a lot, don't consider it as dragging yet if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { - console.log("clear"); clearTimeout(this.holdTimer); this.holdTimer = undefined; } @@ -99,7 +109,6 @@ export abstract class Touchable extends React.Component { } } if (this.holdTimer) { - console.log("clear"); clearTimeout(this.holdTimer); this.holdTimer = undefined; } @@ -152,7 +161,7 @@ export abstract class Touchable extends React.Component { } handleHandDown = (e: React.TouchEvent) => { - e.stopPropagation(); - e.preventDefault(); + // e.stopPropagation(); + // e.preventDefault(); } } \ 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 d7cccc036..84945c6e6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../../new_fields/FieldSymbols"; import { InkTool, InkField, InkData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; import { ScriptField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; @@ -468,7 +468,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { handle1PointerMove = (e: TouchEvent) => { // panning a workspace if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt = myTouches[0]; if (pt) { if (InkingControl.Instance.selectedTool === InkTool.None) { @@ -490,7 +490,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { handle2PointersMove = (e: TouchEvent) => { // pinch zooming if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; @@ -849,51 +849,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } private thumbIdentifier?: number; - private hand?: React.Touch[]; - @action - handleHandDown = (e: React.TouchEvent) => { - const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); - this.hand = fingers; - const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); - this.thumbIdentifier = thumb?.identifier; - const others = fingers.filter(f => f !== thumb); - const minX = Math.min(...others.map(f => f.clientX)); - const minY = Math.min(...others.map(f => f.clientY)); - const t = this.getTransform().transformPoint(minX, minY); - const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); - this._palette = ; - - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchmove", this.handleHandMove); - document.addEventListener("touchmove", this.handleHandMove); - document.removeEventListener("touchend", this.handleHandUp); - document.addEventListener("touchend", this.handleHandUp); - } - - @action - handleHandMove = (e: TouchEvent) => { - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - if (pt?.identifier === this.thumbIdentifier) { - } - } - } - - @action - handleHandUp = (e: TouchEvent) => { - console.log(e.changedTouches.length); - this.onTouchEnd(e); - if (this.prevPoints.size < 3) { - if (this.hand) { - for (const h of this.hand) { - this.prevPoints.has(h.identifier) && this.prevPoints.delete(h.identifier); - } - } - this._palette = undefined; - document.removeEventListener("touchend", this.handleHandUp); - } - } + // @action + // handleHandDown = (e: React.TouchEvent) => { + // const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + // const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + // this.thumbIdentifier = thumb?.identifier; + // const others = fingers.filter(f => f !== thumb); + // const minX = Math.min(...others.map(f => f.clientX)); + // const minY = Math.min(...others.map(f => f.clientY)); + // const t = this.getTransform().transformPoint(minX, minY); + // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); + + // const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + // if (thumbDoc) { + // this._palette = ; + // } + + // document.removeEventListener("touchmove", this.onTouch); + // document.removeEventListener("touchmove", this.handleHandMove); + // document.addEventListener("touchmove", this.handleHandMove); + // document.removeEventListener("touchend", this.handleHandUp); + // document.addEventListener("touchend", this.handleHandUp); + // } + + // @action + // handleHandMove = (e: TouchEvent) => { + // for (let i = 0; i < e.changedTouches.length; i++) { + // const pt = e.changedTouches.item(i); + // if (pt?.identifier === this.thumbIdentifier) { + // } + // } + // } + + // @action + // handleHandUp = (e: TouchEvent) => { + // this.onTouchEnd(e); + // if (this.prevPoints.size < 3) { + // this._palette = undefined; + // document.removeEventListener("touchend", this.handleHandUp); + // } + // } onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; @@ -958,12 +954,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } - @observable private _palette?: JSX.Element; + // @observable private _palette?: JSX.Element; children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); - this._palette && (eles.push(this._palette)); + // this._palette && (eles.push(this._palette)); // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); return eles; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 415d0e2ef..bdec94eb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -59,6 +59,8 @@ export interface DocumentViewProps { LibraryPath: Doc[]; fitToBox?: boolean; onClick?: ScriptField; + onPointerDown?: ScriptField; + onPointerUp?: ScriptField; dragDivName?: string; addDocument?: (doc: Doc) => boolean; removeDocument?: (doc: Doc) => boolean; @@ -105,6 +107,8 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return this.layoutDoc.nativeWidth || 0; } @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } + @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; } + @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; } @action componentDidMount() { @@ -180,7 +184,7 @@ export class DocumentView extends DocComponent(Docu } } - onClick = async (e: React.MouseEvent) => { + onClick = async (e: React.MouseEvent | React.PointerEvent) => { if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { e.stopPropagation(); @@ -240,8 +244,9 @@ export class DocumentView extends DocComponent(Docu } handle1PointerDown = (e: React.TouchEvent) => { + if (this.Document.onPointerDown) return; if (!e.nativeEvent.cancelBubble) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; this._downX = touch.clientX; this._downY = touch.clientY; this._hitTemplateDrag = false; @@ -265,7 +270,7 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("touchmove", this.onTouch); } else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { document.removeEventListener("touchmove", this.onTouch); @@ -293,7 +298,7 @@ export class DocumentView extends DocComponent(Docu @action handle2PointersMove = (e: TouchEvent) => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; const oldPoint1 = this.prevPoints.get(pt1.identifier); @@ -363,6 +368,12 @@ export class DocumentView extends DocComponent(Docu } onPointerDown = (e: React.PointerEvent): void => { + if (this.onPointerDownHandler && this.onPointerDownHandler.script) { + this.onPointerDownHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + return; + } // console.log(e.button) // console.log(e.nativeEvent) // continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document) @@ -412,6 +423,11 @@ export class DocumentView extends DocComponent(Docu } onPointerUp = (e: PointerEvent): void => { + if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + this.onPointerUpHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + document.removeEventListener("pointerup", this.onPointerUp); + return; + } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 21e69fbed..1cba3cba9 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -20,6 +20,8 @@ export const documentSchema = createSchema({ dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 71dd34e68..2ade6f102 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -110,6 +110,29 @@ export class CurrentUserUtils { })); } + static setupThumbButtons(doc: Doc) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + ]; + return docProtoData.map(data => Docs.Create.FontIconDocument({ + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, + ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, + })); + } + + static setupThumbDoc(userDoc: Doc) { + if (!userDoc.thumbDoc) { + return Docs.Create.MasonryDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + }); + } + return userDoc.thumbDoc; + } + static setupMobileDoc(userDoc: Doc) { return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), { columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 -- cgit v1.2.3-70-g09d2 From 4daad3765bd3c5693b9aff2ce53ca8b81804d000 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 13 Jan 2020 17:14:50 -0500 Subject: some fixes --- src/client/views/Touchable.tsx | 2 - src/client/views/nodes/DocumentView.tsx | 91 +++++++------------ src/client/views/nodes/RadialMenu.tsx | 132 +++++++++++---------------- src/client/views/nodes/RadialMenuItem.tsx | 146 +++++++++++++++--------------- 4 files changed, 156 insertions(+), 215 deletions(-) (limited to 'src') diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 49b2116f1..7d9184bea 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -137,11 +137,9 @@ export abstract class Touchable extends React.Component { } handle1PointerHoldStart = (e: React.TouchEvent): any => { - console.log("Hold"); e.stopPropagation(); e.preventDefault(); document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 49d35637e..e6c6aaa08 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -107,33 +107,38 @@ export class DocumentView extends DocComponent(Docu @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } - private _firstX:number=0; - private _firstY:number=0; + private _firstX: number = 0; + private _firstY: number = 0; - - handle1PointerHoldStart= (e: React.TouchEvent): any =>{ + + handle1PointerHoldStart = (e: React.TouchEvent): any => { this.onRadialMenu(e); - let page =e.touches[0]; - this._firstX=page.pageX; - this._firstY=page.pageY; + const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + this._firstX = pt.pageX; + this._firstY = pt.pageY; + e.stopPropagation(); + e.preventDefault(); document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); + document.removeEventListener("touchmove", this.handle1PointerHoldMove); document.addEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); document.addEventListener("touchend", this.handle1PointerHoldEnd); } - handle1PointerHoldMove = (event: TouchEvent): void => { - let e=event.touches[0]; - Math.abs(e.pageX-this._firstX)>150 ||Math.abs(e.pageY-this._firstY)>150? this.handleRelease():null; + handle1PointerHoldMove = (e: TouchEvent): void => { + const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) { + this.handleRelease(); + } document.removeEventListener("touchmove", this.handle1PointerHoldMove); document.addEventListener("touchmove", this.handle1PointerHoldMove); document.removeEventListener("touchend", this.handle1PointerHoldEnd); document.addEventListener("touchend", this.handle1PointerHoldEnd); } - handleRelease(){ - RadialMenu.Instance.closeMenu() + handleRelease() { + RadialMenu.Instance.closeMenu(); document.removeEventListener("touchmove", this.handle1PointerHoldMove); document.removeEventListener("touchend", this.handle1PointerHoldEnd); } @@ -145,56 +150,24 @@ export class DocumentView extends DocComponent(Docu } @action - onRadialMenu = async (event: React.TouchEvent): Promise => { - // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 - // if (e.button === 0) { - // e.preventDefault(); - // return; - // } - let e = event.touches[0]; - // if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 || - // // event.isDefaultPrevented()) { - // // event.preventDefault(); - // return; - // } - // event.preventDefault(); - - let rm = RadialMenu.Instance; - rm.openMenu(); - const one = RadialMenu.Instance.findByDescription("one..."); - const two = RadialMenu.Instance.findByDescription("two..."); - const three = RadialMenu.Instance.findByDescription("three..."); - const four= RadialMenu.Instance.findByDescription("four..."); - // const five= RadialMenu.Instance.findByDescription("five..."); - // const six= RadialMenu.Instance.findByDescription("six..."); - // const seven= RadialMenu.Instance.findByDescription("seven..."); - - - !one?rm.addItem({ description: "one one one one one one one one one", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - !two?rm.addItem({ description: "two", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - !three?rm.addItem({ description: "three", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - !four?rm.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; - // !five?rm.addItem({ description: "five", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group",selected:-1 }):null; + onRadialMenu = (e: React.TouchEvent): void => { + const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + + RadialMenu.Instance.openMenu(); + + RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); + RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 }); + RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "tab", selected: -1 }); + RadialMenu.Instance.addItem({ description: "four", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); // !six?rm.addItem({ description: "six", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; // !seven?rm.addItem({ description: "seven", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" ,selected:-1}):null; - runInAction(() => { - // cm.addItem({ - // description: "Share", - // event: () => SharingManager.Instance.open(this), - // icon: "external-link-alt" - // }); - - if (!this.topMost) { - // DocumentViews should stop propagation of this event - } - RadialMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - if (!SelectionManager.IsSelected(this, true)) { - SelectionManager.SelectDoc(this, false); - } - }); + RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15); + if (!SelectionManager.IsSelected(this, true)) { + SelectionManager.SelectDoc(this, false); + } } - + @action componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx index d8750a192..74c5f53bd 100644 --- a/src/client/views/nodes/RadialMenu.tsx +++ b/src/client/views/nodes/RadialMenu.tsx @@ -1,13 +1,11 @@ import React = require("react"); import { observer } from "mobx-react"; import { action, observable, computed, IReactionDisposer, reaction, runInAction } from "mobx"; -import { RadialMenuItem, RadialMenuProps, OriginalMenuProps } from "./RadialMenuItem"; +import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Measure from "react-measure"; import "./RadialMenu.scss"; - - @observer export class RadialMenu extends React.Component { static Instance: RadialMenu; @@ -30,36 +28,36 @@ export class RadialMenu extends React.Component { onPointerDown = (e: PointerEvent) => { this._mouseDown = true; this._mouseX = e.clientX; - this._mouseY = e.clientY; + this._mouseY = e.clientY; document.addEventListener("pointermove", this.onPointerMove); } - @observable - private _closest:number=-1; + @observable + private _closest: number = -1; @action onPointerMove = (e: PointerEvent) => { const curX = e.clientX; const curY = e.clientY; - const deltX = this._mouseX-curX - const deltY = this._mouseY-curY - const scale = Math.hypot(deltY,deltX) + const deltX = this._mouseX - curX; + const deltY = this._mouseY - curY; + const scale = Math.hypot(deltY, deltX); - if (scale <150 && scale > 50){ - const rad = Math.atan2(deltY,deltX)+Math.PI; - let closest =0; + if (scale < 150 && scale > 50) { + const rad = Math.atan2(deltY, deltX) + Math.PI; + let closest = 0; let closestval = 999999999; - for (let x =0; x0){ - closestval=rad-curmin - closest=x; + for (let x = 0; x < this._items.length; x++) { + const curmin = (x / this._items.length) * 2 * Math.PI; + if (rad - curmin < closestval && rad - curmin > 0) { + closestval = rad - curmin; + closest = x; } } - this._closest=closest; + this._closest = closest; } - else{ - this._closest=-1; + else { + this._closest = -1; } } @action @@ -72,8 +70,8 @@ export class RadialMenu extends React.Component { } this._shouldDisplay && (this._display = true); document.removeEventListener("pointermove", this.onPointerMove); - if (this._closest!==-1){ - this._items[this._closest].event(); + if (this._closest !== -1) { + this._items[this._closest]?.event(); } } componentWillUnmount() { @@ -94,7 +92,7 @@ export class RadialMenu extends React.Component { ); } - componentDidUpdate = () =>{ + componentDidUpdate = () => { this.previewcircle(); } @@ -112,23 +110,14 @@ export class RadialMenu extends React.Component { return this._items; } - findByDescription = (target: string, toLowerCase = false) => { - return this._items.find(menuItem => { - let reference = menuItem.description; - toLowerCase && (reference = reference.toLowerCase()); - return reference === target; - }); - } - - @action addItem(item: RadialMenuProps) { if (this._items.indexOf(item) === -1) { this._items.push(item); } } - - @observable + + @observable private _items: Array = []; @action @@ -166,7 +155,7 @@ export class RadialMenu extends React.Component { } @computed get menuItems() { - return this._items.map((item,index) => ); + return this._items.map((item, index) => ); } @action @@ -188,60 +177,45 @@ export class RadialMenu extends React.Component { } - previewcircle(){ - if (document.getElementById("newCanvas")!==null){ - var c : any= document.getElementById("newCanvas"); - if (c.getContext){ - var ctx = c.getContext("2d"); - ctx.beginPath(); - ctx.arc(150, 150, 50, 0, 2 * Math.PI); - ctx.fillStyle="white"; - ctx.fill() - ctx.font = "12px Arial"; - ctx.fillStyle = "black"; - ctx.textAlign = "center"; - let description =""; - if (this._closest!==-1){ - description = this._items[this._closest].description; - } - if (description.length>15){ - description= description.slice(0,12); - description += "..."; - } - ctx.fillText(description, 150, 150, 90); + previewcircle() { + if (document.getElementById("newCanvas") !== null) { + const c: any = document.getElementById("newCanvas"); + if (c.getContext) { + const ctx = c.getContext("2d"); + ctx.beginPath(); + ctx.arc(150, 150, 50, 0, 2 * Math.PI); + ctx.fillStyle = "white"; + ctx.fill(); + ctx.font = "12px Arial"; + ctx.fillStyle = "black"; + ctx.textAlign = "center"; + let description = ""; + if (this._closest !== -1) { + description = this._items[this._closest].description; + } + if (description.length > 15) { + description = description.slice(0, 12); + description += "..."; + } + ctx.fillText(description, 150, 150, 90); + } } } - } - + render() { if (!this._display) { return null; } - const style = this._yRelativeToTop ? { left: this._mouseX-150, top: this._mouseY-150 } : - { left: this._mouseX-150, top: this._mouseY-150 }; + const style = this._yRelativeToTop ? { left: this._mouseX - 150, top: this._mouseY - 150 } : + { left: this._mouseX - 150, top: this._mouseY - 150 }; - const contents = ( - <> - {this.menuItems} - - ); - // return ( - // { this._width = r.offset.width; this._height = r.offset.height; })}> - // {({ measureRef }) => ( - //
- // {contents} - //
- // ) - // } - //
- // ); return ( -
- Your browser does not support the HTML5 canvas tag. - {contents} -
+
+ Your browser does not support the HTML5 canvas tag. + {this.menuItems} +
); } diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx index 4ca257df5..fdc732d3f 100644 --- a/src/client/views/nodes/RadialMenuItem.tsx +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -8,114 +8,110 @@ import { UndoManager } from "../../util/UndoManager"; library.add(faAngleRight); -export interface OriginalMenuProps { +export interface RadialMenuProps { description: string; event: (stuff?: any) => void; undoable?: boolean; icon: IconProp; closeMenu?: () => void; min?: number; - max?:number; - selected:number; + max?: number; + selected: number; } -export type RadialMenuProps = OriginalMenuProps; - @observer -export class RadialMenuItem extends React.Component { +export class RadialMenuItem extends React.Component { - componentDidMount = () =>{ + componentDidMount = () => { this.setcircle(); } - componentDidUpdate = () =>{ + componentDidUpdate = () => { this.setcircle(); } - handleEvent = async (e: React.MouseEvent) => { - if ("event" in this.props) { - this.props.closeMenu && this.props.closeMenu(); - let batch: UndoManager.Batch | undefined; - if (this.props.undoable !== false) { - batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); - } - await this.props.event({ x: e.clientX, y: e.clientY }); - batch && batch.end(); + handleEvent = async (e: React.PointerEvent) => { + this.props.closeMenu && this.props.closeMenu(); + let batch: UndoManager.Batch | undefined; + if (this.props.undoable !== false) { + batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); } + await this.props.event({ x: e.clientX, y: e.clientY }); + batch && batch.end(); } - setcircle(){ - let circlemin=0; - let circlemax=1 - this.props.min? circlemin=this.props.min:null; - this.props.max? circlemax=this.props.max:null; - if (document.getElementById("myCanvas")!==null){ - var c : any= document.getElementById("myCanvas"); - let color = "white" - switch(circlemin%3){ - case 1: + setcircle() { + let circlemin = 0; + let circlemax = 1 + this.props.min ? circlemin = this.props.min : null; + this.props.max ? circlemax = this.props.max : null; + if (document.getElementById("myCanvas") !== null) { + var c: any = document.getElementById("myCanvas"); + let color = "white" + switch (circlemin % 3) { + case 1: + color = "#c2c2c5"; + break; + case 0: + color = "#f1efeb"; + break; + case 2: + color = "lightgray"; + break; + } + if (circlemax % 3 === 1 && circlemin === circlemax - 1) { color = "#c2c2c5"; - break; - case 0: - color = "#f1efeb"; - break; - case 2: - color = "lightgray"; - break; - } - if (circlemax%3===1 && circlemin===circlemax-1){ - color="#c2c2c5"; - } + } - if (this.props.selected === this.props.min){ - color="#808080"; - - } - if (c.getContext){ - var ctx = c.getContext("2d"); - ctx.beginPath(); - ctx.arc(150, 150, 150, (circlemin/circlemax)*2*Math.PI, ((circlemin+1)/circlemax) * 2 * Math.PI); - ctx.arc(150, 150, 50, ((circlemin+1)/circlemax)*2*Math.PI, (circlemin/circlemax) * 2 * Math.PI,true); - ctx.fillStyle=color; - ctx.fill() + if (this.props.selected === this.props.min) { + color = "#808080"; + + } + if (c.getContext) { + var ctx = c.getContext("2d"); + ctx.beginPath(); + ctx.arc(150, 150, 150, (circlemin / circlemax) * 2 * Math.PI, ((circlemin + 1) / circlemax) * 2 * Math.PI); + ctx.arc(150, 150, 50, ((circlemin + 1) / circlemax) * 2 * Math.PI, (circlemin / circlemax) * 2 * Math.PI, true); + ctx.fillStyle = color; + ctx.fill() + } } } - } - calculatorx(){ - let circlemin=0; - let circlemax=1 - this.props.min? circlemin=this.props.min:null; - this.props.max? circlemax=this.props.max:null; - let avg = ((circlemin/circlemax)+((circlemin+1)/circlemax))/2; - let degrees = 360*avg; - let x= 100*Math.cos(degrees*Math.PI/180); - let y =-125*Math.sin(degrees*Math.PI/180); + calculatorx() { + let circlemin = 0; + let circlemax = 1 + this.props.min ? circlemin = this.props.min : null; + this.props.max ? circlemax = this.props.max : null; + let avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; + let degrees = 360 * avg; + let x = 100 * Math.cos(degrees * Math.PI / 180); + let y = -125 * Math.sin(degrees * Math.PI / 180); return x; } - calculatory(){ - - let circlemin=0; - let circlemax=1 - this.props.min? circlemin=this.props.min:null; - this.props.max? circlemax=this.props.max:null; - let avg = ((circlemin/circlemax)+((circlemin+1)/circlemax))/2; - let degrees = 360*avg; - let x= 125*Math.cos(degrees*Math.PI/180); - let y =-100*Math.sin(degrees*Math.PI/180); + calculatory() { + + let circlemin = 0; + let circlemax = 1 + this.props.min ? circlemin = this.props.min : null; + this.props.max ? circlemax = this.props.max : null; + let avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; + let degrees = 360 * avg; + let x = 125 * Math.cos(degrees * Math.PI / 180); + let y = -100 * Math.sin(degrees * Math.PI / 180); return y; } render() { - return ( -
- Your browser does not support the HTML5 canvas tag. - -
- ); + return ( +
+ Your browser does not support the HTML5 canvas tag. + +
+ ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From de0c00ff0bb1f58a0736da24acc984f5a090d009 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Tue, 14 Jan 2020 18:35:28 -0500 Subject: ok we now use custom events for touch. im struggling with figuring out how to ignore a hand lol --- src/client/util/InteractionUtils.ts | 50 ++++++- src/client/views/CollectionLinearView.tsx | 4 +- src/client/views/GestureOverlay.tsx | 160 +++++++++++++++++++-- src/client/views/Touchable.tsx | 114 ++++++++++----- .../views/collections/CollectionStackingView.tsx | 6 +- src/client/views/collections/CollectionSubView.tsx | 8 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 80 ++++++----- src/client/views/nodes/DocumentView.tsx | 32 +++-- src/pen-gestures/GestureUtils.ts | 2 +- 9 files changed, 344 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index 76b43da3c..63ee2fb92 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -8,13 +8,53 @@ export namespace InteractionUtils { const REACT_POINTER_PEN_BUTTON = 0; const ERASER_BUTTON = 5; + export class MultiTouchEvent { + constructor( + readonly fingers: number, + readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + readonly touchEvent: T extends React.TouchEvent ? React.TouchEvent : TouchEvent + ) { } + } + + export interface MultiTouchEventDisposer { (): void; } + + 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); + // if (onMultiTouchMoveHandler) { + // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler); + // } + // if (onMultiTouchEndHandler) { + // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler); + // } + return () => { + element.removeEventListener("dashOnTouchStart", onMultiTouchStartHandler); + // if (onMultiTouchMoveHandler) { + // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler); + // } + // if (onMultiTouchEndHandler) { + // element.removeEventListener("dashOnTouchend", onMultiTouchEndHandler); + // } + }; + } + export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); - for (let i = 0; i < e.targetTouches.length; i++) { - const pt: any = e.targetTouches.item(i); - if (pt && prevPoints.has(pt.identifier)) { - if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { - myTouches.push(pt); + for (let i = 0; i < e.touches.length; i++) { + const pt: any = e.touches.item(i); + if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + for (let j = 0; j < e.targetTouches.length; j++) { + const tPt = e.targetTouches.item(j); + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && prevPoints.has(pt.identifier)) { + myTouches.push(pt); + } + } } } } diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 5d6a58656..38f2decdd 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -36,7 +36,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { { fireImmediately: true } ); } - protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); @@ -55,7 +55,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { render() { const guid = Utils.GenerateGuid(); return
-
+
this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} /> diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 830c06f1f..a5fba362c 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; -import "./GestureOverlay.scss" +import "./GestureOverlay.scss"; import { computed, observable, action, runInAction } from "mobx"; import { CreatePolyline } from "./InkingStroke"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; @@ -16,6 +16,7 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; +import { Utils } from "../../Utils"; @observer export default class GestureOverlay extends Touchable { @@ -28,6 +29,9 @@ export default class GestureOverlay extends Touchable { private _d1: Doc | undefined; private thumbIdentifier?: number; + private _hands: (React.Touch[])[] = []; + + protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; constructor(props: Readonly<{}>) { super(props); @@ -35,11 +39,143 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } - @action + getNewTouches(e: React.TouchEvent | TouchEvent) { + const ntt: (React.Touch | Touch)[] = []; + const nct: (React.Touch | Touch)[] = []; + const nt: (React.Touch | Touch)[] = []; + this._hands.forEach((hand) => { + for (let i = 0; i < e.targetTouches.length; i++) { + const pt = e.targetTouches.item(i); + if (pt && !hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + ntt.push(pt); + } + } + + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.changedTouches.item(i); + if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nct.push(pt); + } + } + + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.touches.item(i); + if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nt.push(pt); + } + } + }); + return { ntt, nct, nt }; + } + + onReactTouchStart = (te: React.TouchEvent) => { + const actualPts: React.Touch[] = []; + for (let i = 0; i < te.touches.length; i++) { + const pt: any = te.touches.item(i); + actualPts.push(pt); + // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) + // and this seems to be the only way of differentiating pen and touch on touch events + if (pt.radiusX > 1 && pt.radiusY > 1) { + // if (typeof pt.identifier !== "string") { + // pt.identifier = Utils.GenerateGuid(); + // } + this.prevPoints.set(pt.identifier, pt); + } + } + + const ptsToDelete: number[] = []; + this.prevPoints.forEach(pt => { + if (!actualPts.includes(pt)) { + ptsToDelete.push(pt.identifier); + } + }); + + ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + + if (this.prevPoints.size && this.prevPoints.size < 5) { + const nts: any = this.getNewTouches(te); + const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); + target?.dispatchEvent( + new CustomEvent>("dashOnTouchStart", + { + bubbles: true, + detail: { + fingers: this.prevPoints.size, + points: te.targetTouches, + touchEvent: new React.TouchEvent("start", { + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct + }); + } + } + ) + ); + document.removeEventListener("touchmove", this.onReactTouchMove); + document.removeEventListener("touchend", this.onReactTouchEnd); + document.addEventListener("touchmove", this.onReactTouchMove); + document.addEventListener("touchend", this.onReactTouchEnd); + } + else { + this.handleHandDown(te); + document.removeEventListener("touchmove", this.onReactTouchMove); + document.removeEventListener("touchend", this.onReactTouchEnd); + } + } + + onReactTouchMove = (e: TouchEvent) => { + const nts: any = this.getNewTouches(e); + document.dispatchEvent( + new CustomEvent>("dashOnTouchMove", + { + bubbles: true, + detail: { + fingers: this.prevPoints.size, + points: e.touches, + touchEvent: new TouchEvent("move", { + targetTouches: nts.ntt, + changedTouches: nts.nct, + touches: nts.nt, + }) + } + }) + ); + } + + onReactTouchEnd = (e: TouchEvent) => { + document.dispatchEvent( + new CustomEvent>("dashOnTouchEnd", + { + bubbles: true, + detail: { + fingers: this.prevPoints.size, + points: e.touches, + touchEvent: e + } + }) + ); + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.changedTouches.item(i); + if (pt) { + if (this.prevPoints.has(pt.identifier)) { + this.prevPoints.delete(pt.identifier); + } + } + } + + if (this.prevPoints.size === 0) { + document.removeEventListener("touchmove", this.onReactTouchMove); + document.removeEventListener("touchend", this.onReactTouchEnd); + } + e.stopPropagation(); + } + handleHandDown = (e: React.TouchEvent) => { const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); this.thumbIdentifier = thumb?.identifier; + fingers.forEach((f) => this.prevPoints.delete(f.identifier)); + this._hands.push(fingers); const others = fingers.filter(f => f !== thumb); const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); @@ -48,10 +184,12 @@ export default class GestureOverlay extends Touchable { const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); if (thumbDoc) { - this._palette = ; + runInAction(() => { + this._palette = ; + }); } - document.removeEventListener("touchmove", this.onTouch); + this.removeMoveListeners(); document.removeEventListener("touchmove", this.handleHandMove); document.addEventListener("touchmove", this.handleHandMove); document.removeEventListener("touchend", this.handleHandUp); @@ -69,11 +207,9 @@ export default class GestureOverlay extends Touchable { @action handleHandUp = (e: TouchEvent) => { - this.onTouchEnd(e); - if (this.prevPoints.size < 3) { - this._palette = undefined; - document.removeEventListener("touchend", this.handleHandUp); - } + // this.onTouchEnd(e); + this._palette = undefined; + document.removeEventListener("touchend", this.handleHandUp); } @action @@ -115,7 +251,7 @@ export default class GestureOverlay extends Touchable { DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); actionPerformed = true; } - } + }; const ge = new CustomEvent("dashOnGesture", { bubbles: true, @@ -181,7 +317,7 @@ export default class GestureOverlay extends Touchable { } } ) - ) + ); this._points = []; } } @@ -215,7 +351,7 @@ export default class GestureOverlay extends Touchable { render() { return ( -
+
{this.props.children} {this._palette} {this.currentStroke} diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 2a7599fbf..a71015b05 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -8,8 +8,11 @@ const HOLD_DURATION = 1000; export abstract class Touchable extends React.Component { //private holdTimer: NodeJS.Timeout | undefined; - holdTimer: NodeJS.Timeout | undefined; + private holdTimer: NodeJS.Timeout | undefined; + private moveDisposer?: InteractionUtils.MultiTouchEventDisposer; + private endDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected abstract multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _touchDrag: boolean = false; protected prevPoints: Map = new Map(); @@ -22,16 +25,27 @@ export abstract class Touchable extends React.Component { * When a touch even starts, we keep track of each touch that is associated with that event */ @action - protected onTouchStart = (e: React.TouchEvent): void => { + protected onTouchStart = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { const actualPts: React.Touch[] = []; - for (let i = 0; i < e.targetTouches.length; i++) { - const pt: any = e.targetTouches.item(i); + const te = me.touchEvent; + // loop through all touches on screen + for (let i = 0; i < te.touches.length; i++) { + const pt: any = te.touches.item(i); actualPts.push(pt); - // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) - // and this seems to be the only way of differentiating pen and touch on touch events - if (pt.radiusX > 1 && pt.radiusY > 1) { + if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); } + // only add the ones that are targeted on "this" element, but with the identifier that the screen touch gives + for (let j = 0; j < te.changedTouches.length; j++) { + const tPt = te.changedTouches.item(j); + if (pt.clientX === tPt.clientX && pt.clientY === tPt.clientY) { + // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) + // and this seems to be the only way of differentiating pen and touch on touch events + if (pt.radiusX > 1 && pt.radiusY > 1) { + this.prevPoints.set(pt.identifier, pt); + } + } + } } const ptsToDelete: number[] = []; @@ -41,26 +55,30 @@ export abstract class Touchable extends React.Component { } }); + // console.log(ptsToDelete.length); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + // console.log(this.prevPoints.size); if (this.prevPoints.size) { switch (this.prevPoints.size) { case 1: - this.handle1PointerDown(e); - e.persist(); + this.handle1PointerDown(te); + te.persist(); // if (this.holdTimer) { // clearTimeout(this.holdTimer) // this.holdTimer = undefined; // } - this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(e), HOLD_DURATION); + this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te), HOLD_DURATION); + // e.stopPropagation(); // console.log(this.holdTimer); break; case 2: - this.handle2PointersDown(e); - break; - case 5: - this.handleHandDown(e); + this.handle2PointersDown(te); + // e.stopPropagation(); break; + // case 5: + // this.handleHandDown(te); + // break; } } } @@ -69,28 +87,30 @@ export abstract class Touchable extends React.Component { * Handle touch move event */ @action - protected onTouch = (e: TouchEvent): void => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { + const te = me.touchEvent; + const myTouches = InteractionUtils.GetMyTargetTouches(te, this.prevPoints, true); // if we're not actually moving a lot, don't consider it as dragging yet if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { - console.log("CLEAR") + console.log("CLEAR"); clearTimeout(this.holdTimer); // this.holdTimer = undefined; } + // console.log(myTouches.length); switch (myTouches.length) { case 1: - this.handle1PointerMove(e); + this.handle1PointerMove(te); break; case 2: - this.handle2PointersMove(e); + this.handle2PointersMove(te); break; } - for (let i = 0; i < e.targetTouches.length; i++) { - const pt = e.targetTouches.item(i); + for (let i = 0; i < te.touches.length; i++) { + const pt = te.touches.item(i); if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); @@ -100,11 +120,12 @@ export abstract class Touchable extends React.Component { } @action - protected onTouchEnd = (e: TouchEvent): void => { + protected onTouchEnd = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { // console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up"); // remove all the touches associated with the event - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); + const te = me.touchEvent; + for (let i = 0; i < te.changedTouches.length; i++) { + const pt = te.changedTouches.item(i); if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.delete(pt.identifier); @@ -116,7 +137,7 @@ export abstract class Touchable extends React.Component { console.log("clear"); } this._touchDrag = false; - e.stopPropagation(); + te.stopPropagation(); // if (e.targetTouches.length === 0) { @@ -126,11 +147,12 @@ export abstract class Touchable extends React.Component { if (this.prevPoints.size === 0) { this.cleanUpInteractions(); } + e.stopPropagation(); } cleanUpInteractions = (): void => { - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.removeEndListeners(); } handle1PointerMove = (e: TouchEvent): any => { @@ -144,23 +166,43 @@ export abstract class Touchable extends React.Component { } handle1PointerDown = (e: React.TouchEvent): any => { - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); } handle2PointersDown = (e: React.TouchEvent): any => { - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); } handle1PointerHoldStart = (e: React.TouchEvent): any => { e.stopPropagation(); e.preventDefault(); - document.removeEventListener("touchmove", this.onTouch); + this.removeMoveListeners(); + } + + addMoveListeners = () => { + const handler = (e: Event) => this.onTouch(e, (e as CustomEvent>).detail); + document.addEventListener("dashOnTouchMove", handler); + this.moveDisposer = () => document.removeEventListener("dashOnTouchMove", handler); + } + + removeMoveListeners = () => { + this.moveDisposer && this.moveDisposer(); + } + + addEndListeners = () => { + const handler = (e: Event) => this.onTouchEnd(e, (e as CustomEvent>).detail); + document.addEventListener("dashOnTouchEnd", handler); + this.endDisposer = () => document.removeEventListener("dashOnTouchEnd", handler); + } + + removeEndListeners = () => { + this.endDisposer && this.endDisposer(); } handleHandDown = (e: React.TouchEvent) => { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 992820fc7..b8423af20 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -153,7 +153,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; - this.createDropAndGestureTarget(ele!); //so the whole grid is the drop target? + this.createDashEventsTarget(ele!); //so the whole grid is the drop target? } overlays = (doc: Doc) => { @@ -309,7 +309,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropAndGestureTarget} + createDropTarget={this.createDashEventsTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} />; } @@ -342,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropAndGestureTarget} + createDropTarget={this.createDashEventsTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} setDocHeight={this.setDocHeight} />; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 73dc7edc6..4133956fc 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -24,6 +24,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { Networking } from "../../Network"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; +import { InteractionUtils } from "../../util/InteractionUtils"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc) => boolean; @@ -49,17 +50,20 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; + protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; private _childLayoutDisposer?: IReactionDisposer; - protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer && this.dropDisposer(); this.gestureDisposer && this.gestureDisposer(); + this.multiTouchDisposer && this.multiTouchDisposer(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); + this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } } protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view - this.createDropAndGestureTarget(ele); + this.createDashEventsTarget(ele); } componentDidMount() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 84945c6e6..3894e9d63 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -326,29 +326,30 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action handle1PointerDown = (e: React.TouchEvent) => { - const pt = e.targetTouches.item(0); - if (pt) { - this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; - if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); - // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { - // e.stopPropagation(); - // e.preventDefault(); - // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); - // this._points.push({ X: point[0], Y: point[1] }); - // } - if (InkingControl.Instance.selectedTool === InkTool.None) { - this._lastX = pt.pageX; - this._lastY = pt.pageY; - e.stopPropagation(); - e.preventDefault(); - } - else { - e.stopPropagation(); - e.preventDefault(); + if (!e.nativeEvent.cancelBubble) { + // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const pt = e.changedTouches.item(0); + if (pt) { + this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; + if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); + // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { + // e.stopPropagation(); + // e.preventDefault(); + // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); + // this._points.push({ X: point[0], Y: point[1] }); + // } + if (InkingControl.Instance.selectedTool === InkTool.None) { + this._lastX = pt.pageX; + this._lastY = pt.pageY; + e.preventDefault(); + } + else { + e.preventDefault(); + } } } } @@ -395,8 +396,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.removeEndListeners(); } @action @@ -482,7 +483,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.pan(pt); } } - e.stopPropagation(); + // e.stopPropagation(); e.preventDefault(); } } @@ -527,7 +528,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } } - e.stopPropagation(); + // e.stopPropagation(); e.preventDefault(); } } @@ -535,18 +536,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action handle2PointersDown = (e: React.TouchEvent) => { if (!e.nativeEvent.cancelBubble && this.props.active(true)) { - const pt1: React.Touch | null = e.targetTouches.item(0); - const pt2: React.Touch | null = e.targetTouches.item(1); - if (!pt1 || !pt2) return; + // const pt1: React.Touch | null = e.targetTouches.item(0); + // const pt2: React.Touch | null = e.targetTouches.item(1); + // // if (!pt1 || !pt2) return; + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const pt1 = myTouches[0]; + const pt2 = myTouches[1]; const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2; const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; this._lastX = centerX; this._lastY = centerY; - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); e.stopPropagation(); } } @@ -554,8 +558,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { cleanUpInteractions = () => { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.removeEndListeners(); } @action @@ -989,9 +993,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document if (!this.extensionDoc) return (null); // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale; - return
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}> {!this.Document.LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? this.placeholder : this.marqueeView} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2e0ae09ba..b33bebe7d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -103,6 +103,8 @@ export class DocumentView extends DocComponent(Docu private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; private _titleRef = React.createRef(); + protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } @computed get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } @@ -177,6 +179,7 @@ export class DocumentView extends DocComponent(Docu componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); + this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this); } @@ -184,7 +187,11 @@ export class DocumentView extends DocComponent(Docu @action componentDidUpdate() { this._dropDisposer && this._dropDisposer(); + this._gestureEventDisposer && this._gestureEventDisposer(); + this.multiTouchDisposer && this.multiTouchDisposer(); this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); + this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); + this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); } @action @@ -319,25 +326,24 @@ export class DocumentView extends DocComponent(Docu } } if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); - if ((e.nativeEvent as any).formattedHandled) e.stopPropagation(); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); + e.stopPropagation(); } } handle1PointerMove = (e: TouchEvent) => { if ((e as any).formattedHandled) { e.stopPropagation; return; } if (e.cancelBubble && this.active) { - document.removeEventListener("touchmove", this.onTouch); + this.removeMoveListeners(); } else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); + this.cleanUpInteractions(); this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); } } @@ -352,10 +358,10 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); e.preventDefault(); - document.removeEventListener("touchmove", this.onTouch); - document.addEventListener("touchmove", this.onTouch); - document.removeEventListener("touchend", this.onTouchEnd); - document.addEventListener("touchend", this.onTouchEnd); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); } } @@ -970,7 +976,7 @@ export class DocumentView extends DocComponent(Docu width: animwidth, height: animheight, opacity: this.Document.opacity - }} onTouchStart={this.onTouchStart}> + }}> {this.innards}
; } diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 062604458..4b5ad6684 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -31,7 +31,7 @@ export namespace GestureUtils { element.addEventListener("dashOnGesture", handler); return () => { element.removeEventListener("dashOnGesture", handler); - } + }; } export enum Gestures { -- cgit v1.2.3-70-g09d2 From 19be14b5807b117f477b57b9ecc7b96247bf4fef Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Wed, 15 Jan 2020 13:30:08 -0500 Subject: the quick brown fox jumped over the lazy dog --- src/client/util/InteractionUtils.ts | 13 ++-- src/client/views/CollectionLinearView.tsx | 14 +++- src/client/views/GestureOverlay.tsx | 50 ++++++------- src/client/views/Palette.tsx | 4 ++ src/client/views/Touchable.tsx | 35 ++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 41 +++++------ src/client/views/nodes/DocumentView.tsx | 82 +++++++++++----------- .../authentication/models/current_user_utils.ts | 2 +- 8 files changed, 126 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index 63ee2fb92..da42bdc93 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -11,7 +11,10 @@ export namespace InteractionUtils { export class MultiTouchEvent { constructor( readonly fingers: number, - readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + // readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + readonly targetTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], + 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 ) { } } @@ -43,13 +46,11 @@ export namespace InteractionUtils { }; } - export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { + export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); - for (let i = 0; i < e.touches.length; i++) { - const pt: any = e.touches.item(i); + for (const pt of mte.touches) { if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { - for (let j = 0; j < e.targetTouches.length; j++) { - const tPt = e.targetTouches.item(j); + for (const tPt of mte.targetTouches) { if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { if (pt && prevPoints.has(pt.identifier)) { myTouches.push(pt); diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 38f2decdd..2262a3c0c 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -1,4 +1,4 @@ -import { action, IReactionDisposer, observable, reaction } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; @@ -21,12 +21,15 @@ const LinearDocument = makeInterface(documentSchema); @observer export class CollectionLinearView extends CollectionSubView(LinearDocument) { @observable public addMenuToggle = React.createRef(); + @observable private _selectedIndex = -1; private _dropDisposer?: DragManager.DragDropDisposer; private _widthDisposer?: IReactionDisposer; + private _selectedDisposer?: IReactionDisposer; componentWillUnmount() { this._dropDisposer && this._dropDisposer(); this._widthDisposer && this._widthDisposer(); + this._selectedDisposer && this._selectedDisposer(); } componentDidMount() { @@ -35,6 +38,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { () => this.props.Document.width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), { fireImmediately: true } ); + + this._selectedDisposer = reaction( + () => NumCast(this.props.Document.selectedIndex), + (i) => runInAction(() => this._selectedIndex = i), + { fireImmediately: true } + ); } protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); @@ -61,11 +70,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
- {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => { + {this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { const nested = pair.layout.viewType === CollectionViewType.Linear; const dref = React.createRef(); const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; + const isSelected = this._selectedIndex === ind; return
{ for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); - if (pt && !hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - ntt.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + ntt.splice(ntt.indexOf(pt)); } } for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nct.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nct.splice(nct.indexOf(pt)); } } - for (let i = 0; i < e.changedTouches.length; i++) { + for (let i = 0; i < e.touches.length; i++) { const pt = e.touches.item(i); - if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nt.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nt.splice(ntt.indexOf(pt)); } } }); @@ -93,7 +93,7 @@ export default class GestureOverlay extends Touchable { ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); if (this.prevPoints.size && this.prevPoints.size < 5) { - const nts: any = this.getNewTouches(te); + const nts = this.getNewTouches(te); const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); target?.dispatchEvent( new CustomEvent>("dashOnTouchStart", @@ -101,12 +101,10 @@ export default class GestureOverlay extends Touchable { bubbles: true, detail: { fingers: this.prevPoints.size, - points: te.targetTouches, - touchEvent: new React.TouchEvent("start", { - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct - }); + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, + touchEvent: te } } ) @@ -131,25 +129,26 @@ export default class GestureOverlay extends Touchable { bubbles: true, detail: { fingers: this.prevPoints.size, - points: e.touches, - touchEvent: new TouchEvent("move", { - targetTouches: nts.ntt, - changedTouches: nts.nct, - touches: nts.nt, - }) + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, + touchEvent: e } }) ); } onReactTouchEnd = (e: TouchEvent) => { + const nts: any = this.getNewTouches(e); document.dispatchEvent( new CustomEvent>("dashOnTouchEnd", { bubbles: true, detail: { fingers: this.prevPoints.size, - points: e.touches, + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, touchEvent: e } }) @@ -171,7 +170,7 @@ export default class GestureOverlay extends Touchable { } handleHandDown = (e: React.TouchEvent) => { - const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const fingers = Array.from(e.touches); const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); this.thumbIdentifier = thumb?.identifier; fingers.forEach((f) => this.prevPoints.delete(f.identifier)); @@ -201,6 +200,7 @@ export default class GestureOverlay extends Touchable { for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); if (pt?.identifier === this.thumbIdentifier) { + console.log("thumby movey") } } } diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 3649cccfe..aca1811a4 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -15,6 +15,9 @@ import { observer } from "mobx-react"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { CollectionStackingView } from "./collections/CollectionStackingView"; import { CollectionView } from "./collections/CollectionView"; +import { CollectionSubView, SubCollectionViewProps } from "./collections/CollectionSubView"; +import { makeInterface } from "../../new_fields/Schema"; +import { documentSchema } from "../../new_fields/documentSchemas"; export interface PaletteProps { x: number; @@ -25,6 +28,7 @@ export interface PaletteProps { @observer export default class Palette extends React.Component { + render() { return (
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index a71015b05..7800c4019 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -29,15 +29,13 @@ export abstract class Touchable extends React.Component { const actualPts: React.Touch[] = []; const te = me.touchEvent; // loop through all touches on screen - for (let i = 0; i < te.touches.length; i++) { - const pt: any = te.touches.item(i); + for (const pt of me.touches) { actualPts.push(pt); if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); } // only add the ones that are targeted on "this" element, but with the identifier that the screen touch gives - for (let j = 0; j < te.changedTouches.length; j++) { - const tPt = te.changedTouches.item(j); + for (const tPt of me.changedTouches) { if (pt.clientX === tPt.clientX && pt.clientY === tPt.clientY) { // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) // and this seems to be the only way of differentiating pen and touch on touch events @@ -58,22 +56,21 @@ export abstract class Touchable extends React.Component { // console.log(ptsToDelete.length); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); - // console.log(this.prevPoints.size); if (this.prevPoints.size) { switch (this.prevPoints.size) { case 1: - this.handle1PointerDown(te); + this.handle1PointerDown(te, me); te.persist(); // if (this.holdTimer) { // clearTimeout(this.holdTimer) // this.holdTimer = undefined; // } - this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te), HOLD_DURATION); + this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te, me), HOLD_DURATION); // e.stopPropagation(); // console.log(this.holdTimer); break; case 2: - this.handle2PointersDown(te); + this.handle2PointersDown(te, me); // e.stopPropagation(); break; // case 5: @@ -89,7 +86,7 @@ export abstract class Touchable extends React.Component { @action protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { const te = me.touchEvent; - const myTouches = InteractionUtils.GetMyTargetTouches(te, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); // if we're not actually moving a lot, don't consider it as dragging yet if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; @@ -102,15 +99,14 @@ export abstract class Touchable extends React.Component { // console.log(myTouches.length); switch (myTouches.length) { case 1: - this.handle1PointerMove(te); + this.handle1PointerMove(te, me); break; case 2: - this.handle2PointersMove(te); + this.handle2PointersMove(te, me); break; } - for (let i = 0; i < te.touches.length; i++) { - const pt = te.touches.item(i); + for (const pt of me.touches) { if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); @@ -124,8 +120,7 @@ export abstract class Touchable extends React.Component { // console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up"); // remove all the touches associated with the event const te = me.touchEvent; - for (let i = 0; i < te.changedTouches.length; i++) { - const pt = te.changedTouches.item(i); + for (const pt of me.changedTouches) { if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.delete(pt.identifier); @@ -155,31 +150,31 @@ export abstract class Touchable extends React.Component { this.removeEndListeners(); } - handle1PointerMove = (e: TouchEvent): any => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); } - handle2PointersMove = (e: TouchEvent): any => { + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); } - handle1PointerDown = (e: React.TouchEvent): any => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); } - handle2PointersDown = (e: React.TouchEvent): any => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); } - handle1PointerHoldStart = (e: React.TouchEvent): any => { + handle1PointerHoldStart = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); this.removeMoveListeners(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3894e9d63..aaa585b55 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,7 +44,7 @@ import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { LinkManager } from "../../../util/LinkManager"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; -import Palette from "../../Palette"; +import CollectionPaletteVIew from "../../Palette"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -276,7 +276,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { return; } this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; @@ -325,10 +325,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @action - handle1PointerDown = (e: React.TouchEvent) => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble) { // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); - const pt = e.changedTouches.item(0); + const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { @@ -466,10 +466,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - handle1PointerMove = (e: TouchEvent) => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { // panning a workspace if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = myTouches[0]; if (pt) { if (InkingControl.Instance.selectedTool === InkTool.None) { @@ -488,10 +488,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - handle2PointersMove = (e: TouchEvent) => { + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { // pinch zooming if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; @@ -534,24 +534,25 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @action - handle2PointersDown = (e: React.TouchEvent) => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble && this.props.active(true)) { // const pt1: React.Touch | null = e.targetTouches.item(0); // const pt2: React.Touch | null = e.targetTouches.item(1); // // if (!pt1 || !pt2) return; - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; - - const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2; - const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; - this._lastX = centerX; - this._lastY = centerY; - this.removeMoveListeners(); - this.addMoveListeners(); - this.removeEndListeners(); - this.addEndListeners(); - e.stopPropagation(); + if (pt1 && pt2) { + const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2; + const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; + this._lastX = centerX; + this._lastY = centerY; + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); + e.stopPropagation(); + } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b33bebe7d..0b6a284d6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -134,46 +134,46 @@ export class DocumentView extends DocComponent(Docu // document.addEventListener("touchend", this.handle1PointerHoldEnd); // } - handle1PointerHoldMove = (e: TouchEvent): void => { - const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; - if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) { - this.handleRelease(); - } - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.addEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - document.addEventListener("touchend", this.handle1PointerHoldEnd); - } + // handle1PointerHoldMove = (e: TouchEvent): void => { + // const pt = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; + // if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) { + // this.handleRelease(); + // } + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.addEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // document.addEventListener("touchend", this.handle1PointerHoldEnd); + // } - handleRelease() { - RadialMenu.Instance.closeMenu(); - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - } + // handleRelease() { + // RadialMenu.Instance.closeMenu(); + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // } - handle1PointerHoldEnd = (e: TouchEvent): void => { - RadialMenu.Instance.closeMenu(); - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - } + // handle1PointerHoldEnd = (e: TouchEvent): void => { + // RadialMenu.Instance.closeMenu(); + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // } - @action - onRadialMenu = (e: React.TouchEvent): void => { - const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + // @action + // onRadialMenu = (e: React.TouchEvent): void => { + // const pt = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; - RadialMenu.Instance.openMenu(); + // RadialMenu.Instance.openMenu(); - RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "folder", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "folder", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); - RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15); - if (!SelectionManager.IsSelected(this, true)) { - SelectionManager.SelectDoc(this, false); - } - e.stopPropagation(); - } + // RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15); + // if (!SelectionManager.IsSelected(this, true)) { + // SelectionManager.SelectDoc(this, false); + // } + // e.stopPropagation(); + // } @action componentDidMount() { @@ -313,10 +313,10 @@ export class DocumentView extends DocComponent(Docu } } - handle1PointerDown = (e: React.TouchEvent) => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (this.Document.onPointerDown) return; if (!e.nativeEvent.cancelBubble) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; this._downX = touch.clientX; this._downY = touch.clientY; this._hitTemplateDrag = false; @@ -334,13 +334,13 @@ export class DocumentView extends DocComponent(Docu } } - handle1PointerMove = (e: TouchEvent) => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if ((e as any).formattedHandled) { e.stopPropagation; return; } if (e.cancelBubble && this.active) { this.removeMoveListeners(); } else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { this.cleanUpInteractions(); @@ -353,7 +353,7 @@ export class DocumentView extends DocComponent(Docu } } - handle2PointersDown = (e: React.TouchEvent) => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble && !this.isSelected()) { e.stopPropagation(); e.preventDefault(); @@ -366,8 +366,8 @@ export class DocumentView extends DocComponent(Docu } @action - handle2PointersMove = (e: TouchEvent) => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; const oldPoint1 = this.prevPoints.get(pt1.identifier); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 2ade6f102..a850b0f9f 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -126,7 +126,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { - return Docs.Create.MasonryDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 }); } -- cgit v1.2.3-70-g09d2 From 4bf88786cc8a7e617bca4eb57f7639d69be06dcd Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 15 Jan 2020 17:42:41 -0500 Subject: wow this almost drove me crazy but swipey swipes on five fingers --- package-lock.json | 6 +-- src/client/views/CollectionLinearView.tsx | 6 ++- src/client/views/GestureOverlay.tsx | 45 ++++++++++++++++++---- src/client/views/Palette.scss | 19 ++++----- src/client/views/Palette.tsx | 20 ++++++++-- .../authentication/models/current_user_utils.ts | 4 +- 6 files changed, 75 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index da2648d9a..4a5de1c66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2645,7 +2645,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -15527,7 +15527,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -17768,7 +17768,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 2262a3c0c..0a3096833 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; import { makeInterface } from '../../new_fields/Schema'; -import { BoolCast, NumCast, StrCast } from '../../new_fields/Types'; +import { BoolCast, NumCast, StrCast, Cast } from '../../new_fields/Types'; import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; @@ -13,6 +13,7 @@ import { CollectionSubView } from './collections/CollectionSubView'; import { DocumentView } from './nodes/DocumentView'; import { documentSchema } from '../../new_fields/documentSchemas'; import { Id } from '../../new_fields/FieldSymbols'; +import { ScriptField } from '../../new_fields/ScriptField'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -76,6 +77,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; const isSelected = this._selectedIndex === ind; + if (isSelected) { + Cast(pair.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + } return
{ - const fingers = Array.from(e.touches); + const fingers = new Array(); + for (let i = 0; i < e.touches.length; i++) { + const pt: any = e.touches.item(i); + if (pt.radiusX > 1 && pt.radiusY > 1) { + for (let j = 0; j < e.targetTouches.length; j++) { + const tPt = e.targetTouches.item(j); + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && this.prevPoints.has(pt.identifier)) { + fingers.push(pt); + } + } + } + } + } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + if (thumb.identifier === this.thumbIdentifier) { + this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; + return; + } this.thumbIdentifier = thumb?.identifier; fingers.forEach((f) => this.prevPoints.delete(f.identifier)); this._hands.push(fingers); @@ -184,6 +205,9 @@ export default class GestureOverlay extends Touchable { const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); if (thumbDoc) { runInAction(() => { + this._thumbDoc = thumbDoc; + this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._palette = ; }); } @@ -199,17 +223,24 @@ export default class GestureOverlay extends Touchable { handleHandMove = (e: TouchEvent) => { for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt?.identifier === this.thumbIdentifier) { - console.log("thumby movey") + if (pt && pt.identifier === this.thumbIdentifier && this._thumbX && this._thumbDoc) { + if (Math.abs(pt.clientX - this._thumbX) > 20) { + this._thumbDoc.selectedIndex = Math.max(0, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX)); + this._thumbX = pt.clientX; + } } } } @action handleHandUp = (e: TouchEvent) => { - // this.onTouchEnd(e); - this._palette = undefined; - document.removeEventListener("touchend", this.handleHandUp); + if (e.touches.length < 3) { + // this.onTouchEnd(e); + this._palette = undefined; + this.thumbIdentifier = undefined; + this._thumbDoc = undefined; + document.removeEventListener("touchend", this.handleHandUp); + } } @action diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss index 2626774cb..4513de2b0 100644 --- a/src/client/views/Palette.scss +++ b/src/client/views/Palette.scss @@ -1,20 +1,21 @@ .palette-container { .palette-thumb { - width: 300px; - height: 300px; touch-action: pan-x; overflow: scroll; position: absolute; + width: 90px; + height: 70px; .palette-thumbContent { - width: 100%; - height: 100%; - } + transition: transform .3s; + + .collectionView { + overflow: visible; - .palette-button { - width: 100px; - height: 100px; - background: blue; + .collectionLinearView-outer { + overflow: visible; + } + } } } } \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index aca1811a4..811c24f53 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -9,8 +9,8 @@ import { DocumentView } from "./nodes/DocumentView"; import { emptyPath, returnFalse, emptyFunction, returnOne, returnEmptyString, returnTrue } from "../../Utils"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Transform } from "../util/Transform"; -import { computed, action } from "mobx"; -import { FieldValue, Cast } from "../../new_fields/Types"; +import { computed, action, IReactionDisposer, reaction, observable } from "mobx"; +import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; import { observer } from "mobx-react"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { CollectionStackingView } from "./collections/CollectionStackingView"; @@ -28,12 +28,26 @@ export interface PaletteProps { @observer export default class Palette extends React.Component { + private _selectedDisposer?: IReactionDisposer; + @observable private _selectedIndex: number = 0; + + componentDidMount = () => { + this._selectedDisposer = reaction( + () => NumCast(this.props.thumbDoc.selectedIndex), + (i) => this._selectedIndex = i, + { fireImmediately: true } + ); + } + + componentWillUnmount = () => { + this._selectedDisposer && this._selectedDisposer(); + } render() { return (
-
+
Docs.Create.FontIconDocument({ - nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, @@ -127,7 +127,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { - width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true }); } return userDoc.thumbDoc; -- cgit v1.2.3-70-g09d2 From acf72eb45bdcdda5a87a7017f698793d2feea1c4 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 16 Jan 2020 11:46:12 -0500 Subject: extra fixes after pull from master --- src/client/views/MainView.tsx | 3 --- src/server/ApiManagers/UserManager.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 05bfee95b..b300b0471 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,12 +39,9 @@ import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsM import InkSelectDecorations from './InkSelectDecorations'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; -<<<<<<< HEAD import SettingsManager from '../util/SettingsManager'; -======= import { TraceMobx } from '../../new_fields/util'; import RichTextMenu from '../util/RichTextMenu'; ->>>>>>> e410cde0e430553002d4e1a2f64364b57b65fdbc @observer export class MainView extends React.Component { diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 36d48e366..b0d868918 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -41,7 +41,7 @@ export default class UserManager extends ApiManager { register({ method: Method.POST, subscription: '/internalResetPassword', - onValidation: async ({ user, req, res }) => { + secureHandler: async ({ user, req, res }) => { const result: any = {}; const { curr_pass, new_pass, new_confirm } = req.body; // perhaps should assert whether curr password is entered correctly -- cgit v1.2.3-70-g09d2 From 42b325a94f66e6da0a9fdb0ca0740c01ac7b52f1 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 16 Jan 2020 14:59:37 -0500 Subject: additional qol changes --- src/client/util/SettingsManager.scss | 38 ++++++++++++++++++++++++------------ src/client/util/SettingsManager.tsx | 6 +++++- src/client/views/MainView.scss | 12 +++++++----- src/server/Websocket/Websocket.ts | 7 ++++++- 4 files changed, 44 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 5839fa748..9b6a2ccae 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -3,6 +3,8 @@ .dialogue-box { background-color: whitesmoke !important; color: grey; + width: 400px; + height: 300px; button { background: $lighter-alt-accent; @@ -14,6 +16,7 @@ letter-spacing: 2px; font-size: 75%; padding: 10px; + margin: 10px; transition: transform 0.2s; margin: 2px; } @@ -23,6 +26,23 @@ display: flex; flex-direction: column; + button { + width: 100px; + align-self: center; + background: $darker-alt-accent; + } + + .delete-button { + background: rgb(227, 86, 86); + } + + .close-button { + position: absolute; + right: 1em; + top: 1em; + } + + input { border-radius: 5px; border: none; @@ -32,8 +52,7 @@ .settings-body { display: flex; - flex-direction: row; - + justify-content: space-between; .settings-type { display: flex; @@ -44,13 +63,14 @@ .settings-content { padding-left: 1em; + padding-right: 1em; display: flex; flex-direction: column; - justify-content: space-between; - text-align: left; + justify-content: space-around; + // text-align: left; - button { - background: $darker-alt-accent; + ::placeholder { + color: $intermediate-color; } input { @@ -84,12 +104,6 @@ font-size: 120%; } - .close-button { - position: absolute; - right: 1em; - top: 1em; - } - .container { display: block; position: relative; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 652af438b..6f852a3e6 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -101,7 +101,11 @@ export default class SettingsManager extends React.Component<{}> {
: undefined} {this.settingsContent === "data" ? -
hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+
+ WARNING:
+ THIS WILL ERASE ALL YOUR CURRENT DOCUMENTS STORED ON DASH. IF YOU WISH TO PROCEED, CLICK THE BUTTON BELOW. + +
: undefined}
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index ab0a8e49b..0bc07fa43 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -5,6 +5,7 @@ .mainView-tabButtons { position: relative; width: 100%; + .documentView-node-topmost { height: 200% !important; } @@ -12,8 +13,8 @@ .mainContent-div { position: relative; - width:100%; - height:100%; + width: 100%; + height: 100%; } .mainView-contentArea { @@ -21,6 +22,7 @@ height: 200% !important; } } + // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; @@ -79,8 +81,8 @@ .mainView-logout { position: absolute; - right: 5; - bottom: 5; + right: 0; + bottom: 0; font-size: 8px; } @@ -90,7 +92,7 @@ .mainView-libraryFlyout { height: 100%; - width:100%; + width: 100%; position: absolute; display: flex; flex-direction: column; diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 6dda6956e..9e6ad1c72 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -28,7 +28,7 @@ export namespace WebSocket { function initialize(isRelease: boolean) { const endpoint = io(); - endpoint.on("connection", function(socket: Socket) { + endpoint.on("connection", function (socket: Socket) { _socket = socket; socket.use((_packet, next) => { @@ -89,6 +89,11 @@ export namespace WebSocket { await Database.Instance.deleteAll('newDocuments'); } + // export async function deleteUserDocuments() { + // await Database.Instance.deleteAll(); + // await Database.Instance.deleteAll('newDocuments'); + // } + export async function deleteAll() { await Database.Instance.deleteAll(); await Database.Instance.deleteAll('newDocuments'); -- cgit v1.2.3-70-g09d2 From b71d2cd765ea8b81fa60bc82ae962feba832baaf Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 17 Jan 2020 13:27:23 -0500 Subject: css updates --- package-lock.json | 386 +++++++++++++++++------------------ src/client/util/SettingsManager.scss | 32 +-- src/client/util/SettingsManager.tsx | 9 +- 3 files changed, 215 insertions(+), 212 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index 0ec256f02..2cfdfbbc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "chalk": { @@ -43,9 +43,9 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "supports-color": { @@ -53,7 +53,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -1504,7 +1504,7 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", "requires": { - "readable-stream": "3.4.0" + "readable-stream": "^3.0.1" } }, "readable-stream": { @@ -1512,9 +1512,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { - "inherits": "2.0.3", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "tar-stream": { @@ -1522,11 +1522,11 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", "requires": { - "bl": "3.0.0", - "end-of-stream": "1.4.1", - "fs-constants": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "3.4.0" + "bl": "^3.0.0", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" } } } @@ -5143,7 +5143,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -5207,7 +5207,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "chalk": { @@ -5216,9 +5216,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "supports-color": { @@ -5227,7 +5227,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -5358,8 +5358,8 @@ "bundled": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -5372,7 +5372,7 @@ "bundled": true, "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -5406,7 +5406,7 @@ "bundled": true, "optional": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "deep-extend": { @@ -5442,14 +5442,14 @@ "bundled": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -5457,12 +5457,12 @@ "bundled": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.4", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -5475,7 +5475,7 @@ "bundled": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { @@ -5483,7 +5483,7 @@ "bundled": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -5491,8 +5491,8 @@ "bundled": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -5510,7 +5510,7 @@ "bundled": true, "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -5523,7 +5523,7 @@ "bundled": true, "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -5566,9 +5566,9 @@ "bundled": true, "optional": true, "requires": { - "debug": "3.2.6", - "iconv-lite": "0.4.24", - "sax": "1.2.4" + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -5593,8 +5593,8 @@ "bundled": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -5602,7 +5602,7 @@ "bundled": true, "optional": true, "requires": { - "npm-normalize-package-bin": "1.0.1" + "npm-normalize-package-bin": "^1.0.1" } }, "npm-normalize-package-bin": { @@ -5615,8 +5615,8 @@ "bundled": true, "optional": true, "requires": { - "ignore-walk": "3.0.3", - "npm-bundled": "1.1.1" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -5624,10 +5624,10 @@ "bundled": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -5645,7 +5645,7 @@ "bundled": true, "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -5663,8 +5663,8 @@ "bundled": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -5682,10 +5682,10 @@ "bundled": true, "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -5700,13 +5700,13 @@ "bundled": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.4", - "isarray": "1.0.0", - "process-nextick-args": "2.0.1", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -5714,7 +5714,7 @@ "bundled": true, "optional": true, "requires": { - "glob": "7.1.6" + "glob": "^7.1.3" } }, "safe-buffer": { @@ -5752,9 +5752,9 @@ "bundled": true, "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -5762,7 +5762,7 @@ "bundled": true, "optional": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -5770,7 +5770,7 @@ "bundled": true, "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -5802,7 +5802,7 @@ "bundled": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -7324,9 +7324,9 @@ "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { - "ip-regex": "2.1.0", - "psl": "1.2.0", - "punycode": "2.1.1" + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" } } } @@ -8704,7 +8704,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -8722,7 +8722,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -12535,7 +12535,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "chalk": { @@ -12544,9 +12544,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { "supports-color": { @@ -12555,7 +12555,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -12572,7 +12572,7 @@ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -13406,7 +13406,7 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { - "loose-envify": "1.4.0" + "loose-envify": "^1.0.0" } } } @@ -13599,7 +13599,7 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { - "loose-envify": "1.4.0" + "loose-envify": "^1.0.0" } } } @@ -15054,7 +15054,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -15205,7 +15205,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "faye-websocket": { @@ -15344,7 +15344,7 @@ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -15838,9 +15838,9 @@ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, "requires": { - "commondir": "1.0.1", - "make-dir": "2.1.0", - "pkg-dir": "3.0.0" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" } }, "find-up": { @@ -15849,7 +15849,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "3.0.0" + "locate-path": "^3.0.0" } }, "locate-path": { @@ -15858,8 +15858,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "3.0.0", - "path-exists": "3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "lru-cache": { @@ -15868,7 +15868,7 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "3.0.3" + "yallist": "^3.0.2" } }, "make-dir": { @@ -15877,8 +15877,8 @@ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "requires": { - "pify": "4.0.1", - "semver": "5.7.0" + "pify": "^4.0.1", + "semver": "^5.6.0" } }, "mississippi": { @@ -15887,16 +15887,16 @@ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "concat-stream": "1.6.2", - "duplexify": "3.7.1", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.1.1", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "3.0.0", - "pumpify": "1.5.1", - "stream-each": "1.2.3", - "through2": "2.0.5" + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" } }, "p-locate": { @@ -15905,7 +15905,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "2.2.2" + "p-limit": "^2.0.0" } }, "pify": { @@ -15920,7 +15920,7 @@ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "find-up": "3.0.0" + "find-up": "^3.0.0" } }, "pump": { @@ -15929,8 +15929,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "rimraf": { @@ -15971,7 +15971,7 @@ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "figgy-pudding": "3.5.1" + "figgy-pudding": "^3.5.1" } }, "y18n": { @@ -16268,7 +16268,7 @@ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "glob": "7.1.4" + "glob": "^7.1.3" } } } @@ -16326,7 +16326,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "chalk": { @@ -16335,9 +16335,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "diff": { @@ -16352,7 +16352,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -16989,7 +16989,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.3" + "color-convert": "^1.9.0" } }, "camelcase": { @@ -17004,9 +17004,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.5.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { "supports-color": { @@ -17037,13 +17037,13 @@ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "6.0.5", - "get-stream": "4.1.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "find-up": { @@ -17052,7 +17052,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "3.0.0" + "locate-path": "^3.0.0" } }, "get-caller-file": { @@ -17067,7 +17067,7 @@ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pump": "3.0.0" + "pump": "^3.0.0" } }, "invert-kv": { @@ -17088,7 +17088,7 @@ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "2.0.0" + "invert-kv": "^2.0.0" } }, "locate-path": { @@ -17097,8 +17097,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "3.0.0", - "path-exists": "3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "os-locale": { @@ -17107,9 +17107,9 @@ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "1.0.0", - "lcid": "2.0.0", - "mem": "4.3.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, "p-locate": { @@ -17118,7 +17118,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "2.2.2" + "p-limit": "^2.0.0" } }, "pump": { @@ -17127,8 +17127,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "require-main-filename": { @@ -17245,8 +17245,8 @@ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "dev": true, "requires": { - "ansi-colors": "3.2.4", - "uuid": "3.3.3" + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" } } } @@ -17310,9 +17310,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" }, "dependencies": { "strip-ansi": { @@ -17321,7 +17321,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -17332,7 +17332,7 @@ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "execa": { @@ -17341,13 +17341,13 @@ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "6.0.5", - "get-stream": "4.1.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "find-up": { @@ -17356,7 +17356,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "3.0.0" + "locate-path": "^3.0.0" } }, "get-stream": { @@ -17365,7 +17365,7 @@ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pump": "3.0.0" + "pump": "^3.0.0" } }, "invert-kv": { @@ -17386,7 +17386,7 @@ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "2.0.0" + "invert-kv": "^2.0.0" } }, "locate-path": { @@ -17395,8 +17395,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "3.0.0", - "path-exists": "3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "loglevel": { @@ -17417,9 +17417,9 @@ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "1.0.0", - "lcid": "2.0.0", - "mem": "4.3.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, "p-locate": { @@ -17428,7 +17428,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "2.2.2" + "p-limit": "^2.0.0" } }, "pump": { @@ -17437,8 +17437,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "schema-utils": { @@ -17464,8 +17464,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "strip-ansi": { @@ -17474,7 +17474,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -17485,7 +17485,7 @@ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "webpack-log": { @@ -17494,8 +17494,8 @@ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "dev": true, "requires": { - "ansi-colors": "3.2.4", - "uuid": "3.3.3" + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" } }, "which-module": { @@ -17519,18 +17519,18 @@ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "3.0.0", - "get-caller-file": "1.0.3", - "os-locale": "3.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "11.1.1" + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" } }, "yargs-parser": { @@ -17539,8 +17539,8 @@ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { - "camelcase": "5.3.1", - "decamelize": "1.2.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -18156,9 +18156,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { - "inherits": "2.0.3", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } } } diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 9b6a2ccae..0b5cbe74f 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -4,7 +4,7 @@ background-color: whitesmoke !important; color: grey; width: 400px; - height: 300px; + height: 250px; button { background: $lighter-alt-accent; @@ -27,9 +27,10 @@ flex-direction: column; button { - width: 100px; + width: 100%; align-self: center; background: $darker-alt-accent; + margin-top: 4px; } .delete-button { @@ -42,14 +43,11 @@ top: 1em; } - - input { - border-radius: 5px; - border: none; - padding: 4px 4px 4px 10px; - margin: 2px; + .settings-heading { + letter-spacing: .5em; } + .settings-body { display: flex; justify-content: space-between; @@ -66,15 +64,20 @@ padding-right: 1em; display: flex; flex-direction: column; + flex-basis: 65%; justify-content: space-around; - // text-align: left; + text-align: left; ::placeholder { color: $intermediate-color; } input { + border-radius: 5px; + border: none; + padding: 4px; min-width: 100%; + margin: 2px 0; } .error-text { @@ -84,6 +87,11 @@ .success-text { color: #009F6B; } + + p { + padding: 0 0 .1em .2em; + } + } } @@ -91,12 +99,6 @@ text-decoration: underline; } - p { - text-align: left; - padding: 0; - margin: 0 0 20px 0; - } - h1 { color: $dark-color; text-transform: uppercase; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 6f852a3e6..e1fbeb138 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -91,19 +91,20 @@ export default class SettingsManager extends React.Component<{}> {
{this.settingsContent === "password" ?
- change password here: + + forgot password? {this.errorText ?
{this.errorText}
: undefined} {this.successText ?
{this.successText}
: undefined} - forgot password? +
: undefined} {this.settingsContent === "data" ?
- WARNING:
- THIS WILL ERASE ALL YOUR CURRENT DOCUMENTS STORED ON DASH. IF YOU WISH TO PROCEED, CLICK THE BUTTON BELOW. +

WARNING:
+ THIS WILL ERASE ALL YOUR CURRENT DOCUMENTS STORED ON DASH. IF YOU WISH TO PROCEED, CLICK THE BUTTON BELOW.

: undefined} -- cgit v1.2.3-70-g09d2 From 899efe656c8e16fd5845e437af375b1ecb02fb7f Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 17 Jan 2020 14:51:51 -0500 Subject: added temporary golden layout functions --- .../views/collections/CollectionDockingView.tsx | 79 +++++++++++++++++++++- src/client/views/nodes/DocumentView.tsx | 2 + 2 files changed, 80 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 022eccc13..9fa6edf8a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -174,7 +174,7 @@ export class CollectionDockingView extends React.Component { return MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath); + } else if (location === "onTop") { + return CollectionDockingView.AddTopSplit(doc, dataDoc, libraryPath); + } else if (location === "onBottom") { + return CollectionDockingView.AddBottomSplit(doc, dataDoc, libraryPath); } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); } else { @@ -754,3 +829,5 @@ export class DockedFrameRenderer extends React.Component { } Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); }); Scripting.addGlobal(function useRightSplit(doc: any) { CollectionDockingView.UseRightSplit(doc, undefined); }); +Scripting.addGlobal(function openOnTop(doc: any) { CollectionDockingView.AddTopSplit(doc, undefined); }); // TEMPORARY didnt really know what this did but it seems important so +Scripting.addGlobal(function openOnBottom(doc: any) { CollectionDockingView.AddBottomSplit(doc, undefined); }); // TEMPORARY didnt really know what this did but it seems important so diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index dabe5a7aa..845a2d3ab 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -601,6 +601,8 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" }); subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" }); subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" }); + subitems.push({ description: "Open Top ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onTop", this.props.LibraryPath), icon: "caret-square-right" }); // TEMPORARY for gestures + subitems.push({ description: "Open Bottom ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onBottom", this.props.LibraryPath), icon: "caret-square-right" }); // TEMPORARY for gestures subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); -- cgit v1.2.3-70-g09d2 From 19f525d244d1619f8456b8a455bc2043cf9dbe21 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 17 Jan 2020 15:10:31 -0500 Subject: minor visual changes --- src/client/util/SettingsManager.scss | 6 +++--- src/client/util/SettingsManager.tsx | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 0b5cbe74f..7a0fb0741 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -3,8 +3,8 @@ .dialogue-box { background-color: whitesmoke !important; color: grey; - width: 400px; - height: 250px; + width: 450px; + height: 300px; button { background: $lighter-alt-accent; @@ -64,7 +64,7 @@ padding-right: 1em; display: flex; flex-direction: column; - flex-basis: 65%; + flex-basis: 70%; justify-content: space-around; text-align: left; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index e1fbeb138..ff0b22381 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -73,6 +73,8 @@ export default class SettingsManager extends React.Component<{}> { @action onClick = (event: any) => { this.settingsContent = event.currentTarget.value; + this.errorText = ""; + this.successText = ""; } private get settingsInterface() { @@ -94,10 +96,10 @@ export default class SettingsManager extends React.Component<{}> { - - forgot password? {this.errorText ?
{this.errorText}
: undefined} {this.successText ?
{this.successText}
: undefined} + + forgot password?
: undefined} -- cgit v1.2.3-70-g09d2 From 4b0a056f1afaf20dea4be64c7b238748d99ad12e Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Fri, 17 Jan 2020 15:36:22 -0500 Subject: palette now doesn't interfere with touch --- src/client/documents/Documents.ts | 1 + src/client/util/InteractionUtils.ts | 2 +- src/client/views/GestureOverlay.tsx | 82 +++++++++++++++++----- src/client/views/MainView.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../authentication/models/current_user_utils.ts | 1 + 6 files changed, 70 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 64abd4f57..c49642de0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -119,6 +119,7 @@ export interface DocumentOptions { limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown + isExpanded?: boolean; // is linear view expanded } class EmptyBox { diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index da42bdc93..3e6d27242 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -49,7 +49,7 @@ export namespace InteractionUtils { export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); for (const pt of mte.touches) { - if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { for (const tPt of mte.targetTouches) { if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { if (pt && prevPoints.has(pt.identifier)) { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 96c1513ff..854dff0e2 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -16,7 +16,8 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils } from "../../Utils"; +import { Utils, emptyPath, emptyFunction } from "../../Utils"; +import { DocumentView } from "./nodes/DocumentView"; @observer export default class GestureOverlay extends Touchable { @@ -24,15 +25,15 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; + @observable private _elements: JSX.Element[]; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; private _thumbX?: number; - private _thumbY?: number; private thumbIdentifier?: number; - private _hands: (React.Touch[])[] = []; + private _hands: Map = new Map(); protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @@ -49,22 +50,22 @@ export default class GestureOverlay extends Touchable { this._hands.forEach((hand) => { for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - ntt.splice(ntt.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + ntt.splice(ntt.indexOf(pt), 1); } } for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nct.splice(nct.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + nct.splice(nct.indexOf(pt), 1); } } for (let i = 0; i < e.touches.length; i++) { const pt = e.touches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nt.splice(ntt.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + nt.splice(nt.indexOf(pt), 1); } } }); @@ -94,9 +95,10 @@ export default class GestureOverlay extends Touchable { }); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + const nts = this.getNewTouches(te); + console.log(nts.nt.length); - if (this.prevPoints.size && this.prevPoints.size < 5) { - const nts = this.getNewTouches(te); + if (nts.nt.length < 5) { const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); target?.dispatchEvent( new CustomEvent>("dashOnTouchStart", @@ -190,12 +192,12 @@ export default class GestureOverlay extends Touchable { const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; + this._hands.set(thumb.identifier, fingers); return; } this.thumbIdentifier = thumb?.identifier; - fingers.forEach((f) => this.prevPoints.delete(f.identifier)); - this._hands.push(fingers); + // fingers.forEach((f) => this.prevPoints.delete(f.identifier)); + this._hands.set(thumb.identifier, fingers); const others = fingers.filter(f => f !== thumb); const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); @@ -207,7 +209,6 @@ export default class GestureOverlay extends Touchable { runInAction(() => { this._thumbDoc = thumbDoc; this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; this._palette = ; }); } @@ -221,6 +222,29 @@ export default class GestureOverlay extends Touchable { @action handleHandMove = (e: TouchEvent) => { + const fingers = new Array(); + for (let i = 0; i < e.touches.length; i++) { + const pt: any = e.touches.item(i); + if (pt.radiusX > 1 && pt.radiusY > 1) { + for (let j = 0; j < e.targetTouches.length; j++) { + const tPt = e.targetTouches.item(j); + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && this.prevPoints.has(pt.identifier)) { + this._hands.forEach(hand => hand.some(f => { + if (f.identifier === pt.identifier) { + fingers.push(pt); + } + })); + } + } + } + } + } + const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + if (thumb?.identifier === this.thumbIdentifier) { + this._hands.set(thumb.identifier, fingers); + } + for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); if (pt && pt.identifier === this.thumbIdentifier && this._thumbX && this._thumbDoc) { @@ -236,6 +260,7 @@ export default class GestureOverlay extends Touchable { handleHandUp = (e: TouchEvent) => { if (e.touches.length < 3) { // this.onTouchEnd(e); + if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier); this._palette = undefined; this.thumbIdentifier = undefined; this._thumbDoc = undefined; @@ -380,16 +405,35 @@ export default class GestureOverlay extends Touchable { ); } + @computed get elements() { + return [ + this.props.children, + this._elements, + this._palette, + this.currentStroke + ] + } + + @action + openFloatingDoc = (doc: Doc) => { + // this._elements.push( + // + // ) + } + render() { return (
- {this.props.children} - {this._palette} - {this.currentStroke} + {this.elements}
); } } Scripting.addGlobal("GestureOverlay", GestureOverlay); Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); -Scripting.addGlobal(function resetPen() { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); \ No newline at end of file +Scripting.addGlobal(function resetPen() { runInAction(() => { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; })); }); \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 93fb0a07c..b8b956270 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, - faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp + faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp, faClipboard } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -137,6 +137,7 @@ export class MainView extends React.Component { library.add(faEllipsisV); library.add(faMusic); library.add(faPhone); + library.add(faClipboard); library.add(faStamp); this.initEventListeners(); this.initAuthenticationRouters(); @@ -518,7 +519,7 @@ export class MainView extends React.Component { - + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aaa585b55..b302bb1dc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -327,7 +327,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble) { - // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 8b8b1e029..eb48b28f2 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -114,6 +114,7 @@ export class CurrentUserUtils { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + // { title: "notepad", icon: "clipboard", pointerUp: "closeFloatingDoc(this.clipboard)", pointerDown: 'openFloatingDoc(this.clipboard)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2 From bd1282ff8609c46c4ce813cdea7b3dcdc04f59d1 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 17 Jan 2020 15:36:23 -0500 Subject: removed golden layout stuff --- .../views/collections/CollectionDockingView.tsx | 76 ---------------------- src/client/views/nodes/DocumentView.tsx | 2 - 2 files changed, 78 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9fa6edf8a..5f9b4e0c7 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -208,76 +208,6 @@ export class CollectionDockingView extends React.Component { return MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath); - } else if (location === "onTop") { - return CollectionDockingView.AddTopSplit(doc, dataDoc, libraryPath); - } else if (location === "onBottom") { - return CollectionDockingView.AddBottomSplit(doc, dataDoc, libraryPath); } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); } else { @@ -829,5 +755,3 @@ export class DockedFrameRenderer extends React.Component { } Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); }); Scripting.addGlobal(function useRightSplit(doc: any) { CollectionDockingView.UseRightSplit(doc, undefined); }); -Scripting.addGlobal(function openOnTop(doc: any) { CollectionDockingView.AddTopSplit(doc, undefined); }); // TEMPORARY didnt really know what this did but it seems important so -Scripting.addGlobal(function openOnBottom(doc: any) { CollectionDockingView.AddBottomSplit(doc, undefined); }); // TEMPORARY didnt really know what this did but it seems important so diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 845a2d3ab..dabe5a7aa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -601,8 +601,6 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" }); subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" }); subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" }); - subitems.push({ description: "Open Top ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onTop", this.props.LibraryPath), icon: "caret-square-right" }); // TEMPORARY for gestures - subitems.push({ description: "Open Bottom ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onBottom", this.props.LibraryPath), icon: "caret-square-right" }); // TEMPORARY for gestures subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); -- cgit v1.2.3-70-g09d2 From 3bb4d8324b6101a82122ecb31f025c2e0420df89 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Fri, 17 Jan 2020 16:22:38 -0500 Subject: moving around the createPolyline function --- src/client/util/InteractionUtils.ts | 206 ------------------- src/client/util/InteractionUtils.tsx | 220 +++++++++++++++++++++ src/client/views/GestureOverlay.tsx | 3 +- src/client/views/InkingStroke.tsx | 17 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - 5 files changed, 223 insertions(+), 224 deletions(-) delete mode 100644 src/client/util/InteractionUtils.ts create mode 100644 src/client/util/InteractionUtils.tsx (limited to 'src') diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts deleted file mode 100644 index 3e6d27242..000000000 --- a/src/client/util/InteractionUtils.ts +++ /dev/null @@ -1,206 +0,0 @@ -export namespace InteractionUtils { - 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; - const ERASER_BUTTON = 5; - - export class MultiTouchEvent { - constructor( - readonly fingers: number, - // readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, - readonly targetTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], - 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 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); - // if (onMultiTouchMoveHandler) { - // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler); - // } - // if (onMultiTouchEndHandler) { - // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler); - // } - return () => { - element.removeEventListener("dashOnTouchStart", onMultiTouchStartHandler); - // if (onMultiTouchMoveHandler) { - // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler); - // } - // if (onMultiTouchEndHandler) { - // element.removeEventListener("dashOnTouchend", onMultiTouchEndHandler); - // } - }; - } - - export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { - const myTouches = new Array(); - for (const pt of mte.touches) { - if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { - for (const tPt of mte.targetTouches) { - if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { - if (pt && prevPoints.has(pt.identifier)) { - myTouches.push(pt); - } - } - } - } - } - return myTouches; - } - - 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); - default: - return e.pointerType === type; - } - } - - export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number { - return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2)); - } - - /** - * 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 } { - 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 }; - } - - /** - * Returns -1 if pinching out, 0 if not pinching, and 1 if pinching in - * @param pt1 - new point that corresponds to oldPoint1 - * @param pt2 - new point that corresponds to oldPoint2 - * @param oldPoint1 - previous point 1 - * @param oldPoint2 - previous point 2 - */ - export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { - const threshold = 4; - const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2); - 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 - */ - if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) { - return Math.sign(oldDist - newDist); - } - return 0; - } - - /** - * Returns -1 if pinning and pinching out, 0 if not pinning, and 1 if pinching in - * @param pt1 - new point that corresponds to oldPoint1 - * @param pt2 - new point that corresponds to oldPoint2 - * @param oldPoint1 - previous point 1 - * @param oldPoint2 - previous point 2 - */ - export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { - const threshold = 4; - - const pt1Dist = TwoPointEuclidist(oldPoint1, pt1); - const pt2Dist = TwoPointEuclidist(oldPoint2, pt2); - - const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2); - - if (pinching !== 0) { - if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) { - return pinching; - } - } - return 0; - } - - export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { - for (const touch of newTouches) { - if (touch) { - const oldTouch = oldTouches.get(touch.identifier); - if (oldTouch) { - if (TwoPointEuclidist(touch, oldTouch) >= leniency) { - return true; - } - } - } - } - return false; - } - - // 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 - * @param pts - List of points - */ - // export function InterpretPointers(pts: React.Touch[]): { type: Opt, data?: any } { - // const leniency = 200; - // switch (pts.length) { - // case 1: - // return { type: OneFinger }; - // case 2: - // return { type: TwoSeperateFingers }; - // case 3: - // let pt1 = pts[0]; - // let pt2 = pts[1]; - // let pt3 = pts[2]; - // if (pt1 && pt2 && pt3) { - // let dist12 = TwoPointEuclidist(pt1, pt2); - // let dist23 = TwoPointEuclidist(pt2, pt3); - // let dist13 = TwoPointEuclidist(pt1, pt3); - // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`); - // let dist12close = dist12 < leniency; - // let dist23close = dist23 < leniency; - // let dist13close = dist13 < leniency; - // let xor2313 = dist23close ? !dist13close : dist13close; - // let xor = dist12close ? !xor2313 : xor2313; - // // three input xor because javascript doesn't have logical xor's - // if (xor) { - // let points: number[] = []; - // let min = Math.min(dist12, dist23, dist13); - // switch (min) { - // case dist12: - // points = [0, 1, 2]; - // break; - // case dist23: - // points = [1, 2, 0]; - // break; - // case dist13: - // points = [0, 2, 1]; - // break; - // } - // return { type: TwoToOneFingers, data: points }; - // } - // else { - // return { type: ThreeSeperateFingers, data: null }; - // } - // } - // default: - // return { type: undefined }; - // } - // } - } -} \ No newline at end of file diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx new file mode 100644 index 000000000..1fe95474c --- /dev/null +++ b/src/client/util/InteractionUtils.tsx @@ -0,0 +1,220 @@ +export namespace InteractionUtils { + 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; + const ERASER_BUTTON = 5; + + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number) { + const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + return ( + + ); + } + + export class MultiTouchEvent { + constructor( + readonly fingers: number, + // readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + readonly targetTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], + 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 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); + // if (onMultiTouchMoveHandler) { + // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler); + // } + // if (onMultiTouchEndHandler) { + // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler); + // } + return () => { + element.removeEventListener("dashOnTouchStart", onMultiTouchStartHandler); + // if (onMultiTouchMoveHandler) { + // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler); + // } + // if (onMultiTouchEndHandler) { + // element.removeEventListener("dashOnTouchend", onMultiTouchEndHandler); + // } + }; + } + + export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { + const myTouches = new Array(); + for (const pt of mte.touches) { + if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + for (const tPt of mte.targetTouches) { + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && prevPoints.has(pt.identifier)) { + myTouches.push(pt); + } + } + } + } + } + return myTouches; + } + + 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); + default: + return e.pointerType === type; + } + } + + export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number { + return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2)); + } + + /** + * 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 } { + 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 }; + } + + /** + * Returns -1 if pinching out, 0 if not pinching, and 1 if pinching in + * @param pt1 - new point that corresponds to oldPoint1 + * @param pt2 - new point that corresponds to oldPoint2 + * @param oldPoint1 - previous point 1 + * @param oldPoint2 - previous point 2 + */ + export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { + const threshold = 4; + const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2); + 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 + */ + if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) { + return Math.sign(oldDist - newDist); + } + return 0; + } + + /** + * Returns -1 if pinning and pinching out, 0 if not pinning, and 1 if pinching in + * @param pt1 - new point that corresponds to oldPoint1 + * @param pt2 - new point that corresponds to oldPoint2 + * @param oldPoint1 - previous point 1 + * @param oldPoint2 - previous point 2 + */ + export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { + const threshold = 4; + + const pt1Dist = TwoPointEuclidist(oldPoint1, pt1); + const pt2Dist = TwoPointEuclidist(oldPoint2, pt2); + + const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2); + + if (pinching !== 0) { + if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) { + return pinching; + } + } + return 0; + } + + export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { + for (const touch of newTouches) { + if (touch) { + const oldTouch = oldTouches.get(touch.identifier); + if (oldTouch) { + if (TwoPointEuclidist(touch, oldTouch) >= leniency) { + return true; + } + } + } + } + return false; + } + + // 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 + * @param pts - List of points + */ + // export function InterpretPointers(pts: React.Touch[]): { type: Opt, data?: any } { + // const leniency = 200; + // switch (pts.length) { + // case 1: + // return { type: OneFinger }; + // case 2: + // return { type: TwoSeperateFingers }; + // case 3: + // let pt1 = pts[0]; + // let pt2 = pts[1]; + // let pt3 = pts[2]; + // if (pt1 && pt2 && pt3) { + // let dist12 = TwoPointEuclidist(pt1, pt2); + // let dist23 = TwoPointEuclidist(pt2, pt3); + // let dist13 = TwoPointEuclidist(pt1, pt3); + // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`); + // let dist12close = dist12 < leniency; + // let dist23close = dist23 < leniency; + // let dist13close = dist13 < leniency; + // let xor2313 = dist23close ? !dist13close : dist13close; + // let xor = dist12close ? !xor2313 : xor2313; + // // three input xor because javascript doesn't have logical xor's + // if (xor) { + // let points: number[] = []; + // let min = Math.min(dist12, dist23, dist13); + // switch (min) { + // case dist12: + // points = [0, 1, 2]; + // break; + // case dist23: + // points = [1, 2, 0]; + // break; + // case dist13: + // points = [0, 2, 1]; + // break; + // } + // return { type: TwoToOneFingers, data: points }; + // } + // else { + // return { type: ThreeSeperateFingers, data: null }; + // } + // } + // default: + // return { type: undefined }; + // } + // } + } +} \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 3de901ff5..ab26687d7 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -3,7 +3,6 @@ import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; import "./GestureOverlay.scss"; import { computed, observable, action, runInAction } from "mobx"; -import { CreatePolyline } from "./InkingStroke"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { InkingControl } from "./InkingControl"; @@ -400,7 +399,7 @@ export default class GestureOverlay extends Touchable { return ( - {CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} + {InteractionUtils.CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} ); } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 8b346d5d9..aca507147 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -10,24 +10,11 @@ import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); import { TraceMobx } from "../../new_fields/util"; +import { InteractionUtils } from "../util/InteractionUtils"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); -export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color?: string, width?: number) { - const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); - return ( - - ); -} - @observer export class InkingStroke extends DocExtendableComponent(InkDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } @@ -44,7 +31,7 @@ export class InkingStroke extends DocExtendableComponent Date: Mon, 20 Jan 2020 13:40:57 -0500 Subject: working palette --- src/client/documents/Documents.ts | 1 + src/client/views/GestureOverlay.scss | 6 ++ src/client/views/GestureOverlay.tsx | 87 +++++++++++++++++----- .../views/collections/CollectionLinearView.tsx | 24 ++++-- .../authentication/models/current_user_utils.ts | 7 +- 5 files changed, 99 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3617630f3..2aa848f5c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -109,6 +109,7 @@ export interface DocumentOptions { onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop + clipboard?: Doc; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; gridGap?: number; // gap between items in masonry view xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index 31601efd4..f9a52d976 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -5,4 +5,10 @@ top: 0; left: 0; touch-action: none; +} + +.clipboardDoc-cont { + position: absolute; + width: 300px; + height: 300px; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index ab26687d7..7fb8e7797 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; import "./GestureOverlay.scss"; -import { computed, observable, action, runInAction } from "mobx"; +import { computed, observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { InkingControl } from "./InkingControl"; @@ -12,11 +12,13 @@ import { LinkManager } from "../util/LinkManager"; import { DocUtils } from "../documents/Documents"; import { undoBatch } from "../util/UndoManager"; import { Scripting } from "../util/Scripting"; -import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; +import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils, emptyPath, emptyFunction } from "../../Utils"; +import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from "../../Utils"; import { DocumentView } from "./nodes/DocumentView"; +import { Transform } from "../util/Transform"; +import { DocumentContentsView } from "./nodes/DocumentContentsView"; @observer export default class GestureOverlay extends Touchable { @@ -24,13 +26,16 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; - @observable private _elements: JSX.Element[]; + @observable private _clipboardDoc?: JSX.Element; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; + @observable public SavedColor?: string; + @observable public SavedWidth?: number; private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; - private _thumbX?: number; + @observable private _thumbX?: number; + @observable private _thumbY?: number; private thumbIdentifier?: number; private _hands: Map = new Map(); @@ -191,6 +196,7 @@ export default class GestureOverlay extends Touchable { const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._hands.set(thumb.identifier, fingers); return; } @@ -208,6 +214,7 @@ export default class GestureOverlay extends Touchable { runInAction(() => { this._thumbDoc = thumbDoc; this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._palette = ; }); } @@ -407,32 +414,76 @@ export default class GestureOverlay extends Touchable { @computed get elements() { return [ this.props.children, - this._elements, + // this._clipboardDoc, this._palette, this.currentStroke - ] + ]; } @action - openFloatingDoc = (doc: Doc) => { - // this._elements.push( - // - // ) + public openFloatingDoc = (doc: Doc) => { + // const t = new Transform(-(this._clipboardDoc ? (this._thumbX ?? 0) : -350), -(this._thumbY ?? 0) + 350, 1); + // let t = + this._clipboardDoc = + new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + 350, 1)} + ContentScaling={returnOne} + PanelWidth={() => 300} + PanelHeight={() => 300} + renderDepth={0} + backgroundColor={returnEmptyString} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} + />; + } + + @action + public closeFloatingDoc = () => { + this._clipboardDoc = undefined; } render() { return (
{this.elements} +
+ {this._clipboardDoc} +
); } } Scripting.addGlobal("GestureOverlay", GestureOverlay); -Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); -Scripting.addGlobal(function resetPen() { runInAction(() => { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); }); \ No newline at end of file +Scripting.addGlobal(function setPen(width: any, color: any) { + runInAction(() => { + GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color; + GestureOverlay.Instance.Color = color; + GestureOverlay.Instance.SavedWidth = GestureOverlay.Instance.Width; + GestureOverlay.Instance.Width = width; + }); +}); +Scripting.addGlobal(function resetPen() { + runInAction(() => { + GestureOverlay.Instance.Color = GestureOverlay.Instance.SavedColor ?? "rgb(244, 67, 54)"; + GestureOverlay.Instance.Width = GestureOverlay.Instance.SavedWidth ?? 5; + }); +}); \ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index e91c2a01d..8b0638aa1 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -31,6 +31,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { this._dropDisposer && this._dropDisposer(); this._widthDisposer && this._widthDisposer(); this._selectedDisposer && this._selectedDisposer(); + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + Cast(pair.layout.proto?.onPointerUp, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + }); } componentDidMount() { @@ -42,7 +45,22 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { this._selectedDisposer = reaction( () => NumCast(this.props.Document.selectedIndex), - (i) => runInAction(() => this._selectedIndex = i), + (i) => runInAction(() => { + this._selectedIndex = i; + let selected: any = undefined; + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + const isSelected = this._selectedIndex === ind; + if (isSelected) { + selected = pair; + } + else { + Cast(pair.layout.proto?.onPointerUp, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + } + }); + if (selected && selected.layout) { + Cast(selected.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: selected.layout.proto }, console.log); + } + }), { fireImmediately: true } ); } @@ -76,10 +94,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const dref = React.createRef(); const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; - const isSelected = this._selectedIndex === ind; - if (isSelected) { - Cast(pair.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); - } return
Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + clipboard: data.clipboard, onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, @@ -127,7 +128,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { - return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + userDoc.thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true }); } -- cgit v1.2.3-70-g09d2 From dabb4a9c66083b88eba7bfb07a2614634e124b10 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 20 Jan 2020 16:33:49 -0500 Subject: resizable palette --- src/client/documents/Documents.ts | 4 +- src/client/views/GestureOverlay.scss | 6 ++ src/client/views/GestureOverlay.tsx | 68 +++++++++++++++------- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionLinearView.tsx | 2 +- src/client/views/nodes/ButtonBox.tsx | 5 +- src/new_fields/documentSchemas.ts | 2 + .../authentication/models/current_user_utils.ts | 16 +++-- 8 files changed, 75 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7e7b10ae8..821185518 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -80,7 +80,7 @@ export interface DocumentOptions { isTemplateDoc?: boolean; templates?: List; viewType?: number; - backgroundColor?: string; + backgroundColor?: string | ScriptField; ignoreClick?: boolean; lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed @@ -127,6 +127,8 @@ export interface DocumentOptions { // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown isExpanded?: boolean; // is linear view expanded + textTransform?: string; // is linear view expanded + letterSpacing?: string; // is linear view expanded } class EmptyBox { diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index f9a52d976..d980b0a91 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -11,4 +11,10 @@ position: absolute; width: 300px; height: 300px; +} + +.filter-cont { + position: absolute; + background-color: transparent; + border: 1px solid black; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 7fb8e7797..e44db4463 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -24,19 +24,25 @@ import { DocumentContentsView } from "./nodes/DocumentContentsView"; export default class GestureOverlay extends Touchable { static Instance: GestureOverlay; - @observable private _points: { X: number, Y: number }[] = []; - @observable private _palette?: JSX.Element; - @observable private _clipboardDoc?: JSX.Element; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; @observable public SavedColor?: string; @observable public SavedWidth?: number; - private _d1: Doc | undefined; - private _thumbDoc: Doc | undefined; @observable private _thumbX?: number; @observable private _thumbY?: number; + @observable private _pointerY?: number; + @observable private _points: { X: number, Y: number }[] = []; + @observable private _palette?: JSX.Element; + @observable private _clipboardDoc?: JSX.Element; + @observable private _showBounds: boolean = false; + + @computed private get height(): number { return Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); } + + private _d1: Doc | undefined; + private _thumbDoc: Doc | undefined; private thumbIdentifier?: number; + private pointerIdentifier?: number; private _hands: Map = new Map(); protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @@ -178,7 +184,7 @@ export default class GestureOverlay extends Touchable { e.stopPropagation(); } - handleHandDown = (e: React.TouchEvent) => { + handleHandDown = async (e: React.TouchEvent) => { const fingers = new Array(); for (let i = 0; i < e.touches.length; i++) { const pt: any = e.touches.item(i); @@ -194,6 +200,22 @@ export default class GestureOverlay extends Touchable { } } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + const rightMost = Math.max(...fingers.map(f => f.clientX)); + const leftMost = Math.min(...fingers.map(f => f.clientX)); + let pointer: React.Touch | undefined; + // left hand + if (thumb.clientX === rightMost) { + pointer = fingers.reduce((a, v) => a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v); + } + // right hand + else if (thumb.clientX === leftMost) { + pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v); + } + else { + console.log("not hand"); + } + this.pointerIdentifier = pointer?.identifier; + runInAction(() => this._pointerY = pointer?.clientY); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; this._thumbY = thumb.clientY; @@ -201,15 +223,12 @@ export default class GestureOverlay extends Touchable { return; } this.thumbIdentifier = thumb?.identifier; - // fingers.forEach((f) => this.prevPoints.delete(f.identifier)); this._hands.set(thumb.identifier, fingers); const others = fingers.filter(f => f !== thumb); const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); - // const t = this.getTransform().transformPoint(minX, minY); - // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); - const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc); if (thumbDoc) { runInAction(() => { this._thumbDoc = thumbDoc; @@ -247,7 +266,7 @@ export default class GestureOverlay extends Touchable { } } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); - if (thumb?.identifier === this.thumbIdentifier) { + if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) { this._hands.set(thumb.identifier, fingers); } @@ -259,6 +278,9 @@ export default class GestureOverlay extends Touchable { this._thumbX = pt.clientX; } } + if (pt && pt.identifier === this.pointerIdentifier) { + this._pointerY = pt.clientY; + } } } @@ -329,8 +351,6 @@ export default class GestureOverlay extends Touchable { return actionPerformed; } - - @action onPointerUp = (e: PointerEvent) => { if (this._points.length > 1) { @@ -414,7 +434,6 @@ export default class GestureOverlay extends Touchable { @computed get elements() { return [ this.props.children, - // this._clipboardDoc, this._palette, this.currentStroke ]; @@ -422,8 +441,6 @@ export default class GestureOverlay extends Touchable { @action public openFloatingDoc = (doc: Doc) => { - // const t = new Transform(-(this._clipboardDoc ? (this._thumbX ?? 0) : -350), -(this._thumbY ?? 0) + 350, 1); - // let t = this._clipboardDoc = new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + 350, 1)} + ScreenToLocalTransform={() => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1)} ContentScaling={returnOne} PanelWidth={() => 300} PanelHeight={() => 300} @@ -462,13 +479,24 @@ export default class GestureOverlay extends Touchable {
{this.elements}
{this._clipboardDoc}
-
); +
+
+
); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 168d2ea18..14ee0dc53 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -46,7 +46,7 @@ import RichTextMenu from '../util/RichTextMenu'; @observer export class MainView extends React.Component { public static Instance: MainView; - private _buttonBarHeight = 75; + private _buttonBarHeight = 35; private _flyoutSizeOnDown = 0; private _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 8b0638aa1..77af2dc0e 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -48,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { (i) => runInAction(() => { this._selectedIndex = i; let selected: any = undefined; - this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map(async (pair, ind) => { const isSelected = this._selectedIndex === ind; if (isSelected) { selected = pair; diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index d1272c266..d29fe1711 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -80,7 +80,10 @@ export class ButtonBox extends DocComponent(Butt return (
-
+
{(this.Document.text || this.Document.title)}
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 24f6224eb..d5c34e128 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -57,6 +57,8 @@ export const documentSchema = createSchema({ yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set LODarea: "number", // area (width*height) where CollectionFreeFormViews switch from a label to rendering contents LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews + letterSpacing: "string", + textTransform: "string" }); export const positionSchema = createSchema({ diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index adeee452c..3e6399d82 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -129,7 +129,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { userDoc.thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { - width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true + width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true, backgroundColor: "white" }); } return userDoc.thumbDoc; @@ -154,7 +154,8 @@ export class CurrentUserUtils { }); return Docs.Create.ButtonDocument({ - width: 35, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, + width: 35, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" }), @@ -179,9 +180,10 @@ export class CurrentUserUtils { }); return Docs.Create.ButtonDocument({ - width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Library", fontSize: 10, + width: 50, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Library", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc], { - title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true + title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true, boxShadow: "0 0", }), targetContainer: sidebarContainer, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") @@ -191,7 +193,8 @@ export class CurrentUserUtils { // setup the Search button which will display the search panel. static setupSearchPanel(sidebarContainer: Doc) { return Docs.Create.ButtonDocument({ - width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Search", fontSize: 10, + width: 50, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Search", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.QueryDocument({ title: "search stack", ignoreClick: true }), @@ -214,7 +217,8 @@ export class CurrentUserUtils { // Finally, setup the list of buttons to display in the sidebar doc.sidebarButtons = Docs.Create.StackingDocument([doc.SearchBtn as Doc, doc.LibraryBtn as Doc, doc.ToolsBtn as Doc], { width: 500, height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true, - backgroundColor: "lightgrey", chromeStatus: "disabled", title: "library stack" + backgroundColor: "rgb(100, 100, 100)", chromeStatus: "disabled", title: "library stack", + yMargin: 10, }); } -- cgit v1.2.3-70-g09d2 From 42d82210f3bbfce5d79c9fe93889ddfe8944d5be Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 20 Jan 2020 17:28:01 -0500 Subject: stufs --- src/client/views/GestureOverlay.tsx | 102 ++++++++++++--------- .../authentication/models/current_user_utils.ts | 3 +- 2 files changed, 63 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index e44db4463..84d089b47 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -15,7 +15,7 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from "../../Utils"; +import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange } from "../../Utils"; import { DocumentView } from "./nodes/DocumentView"; import { Transform } from "../util/Transform"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; @@ -28,6 +28,7 @@ export default class GestureOverlay extends Touchable { @observable public Width: number = 5; @observable public SavedColor?: string; @observable public SavedWidth?: number; + @observable public Tool: ToolglassTools = ToolglassTools.None; @observable private _thumbX?: number; @observable private _thumbY?: number; @@ -35,9 +36,9 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; @observable private _clipboardDoc?: JSX.Element; - @observable private _showBounds: boolean = false; @computed private get height(): number { return Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); } + @computed private get showBounds() { return this.Tool !== ToolglassTools.None; } private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; @@ -357,51 +358,62 @@ export default class GestureOverlay extends Touchable { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); - let actionPerformed = false; - if (result && result.Score > 0.7) { - switch (result.Name) { - case GestureUtils.Gestures.Box: - const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); - target?.dispatchEvent(new CustomEvent("dashOnGesture", + const xInGlass = points[0].X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && points[0].X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height; + const yInGlass = points[0].Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && points[0].Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER); + + if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) { + switch (this.Tool) { + case ToolglassTools.InkToText: + break; + } + } + else { + const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); + let actionPerformed = false; + if (result && result.Score > 0.7) { + switch (result.Name) { + case GestureUtils.Gestures.Box: + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent(new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Box, + bounds: B + } + })); + actionPerformed = true; + break; + case GestureUtils.Gestures.Line: + actionPerformed = this.handleLineGesture(); + break; + case GestureUtils.Gestures.Scribble: + console.log("scribble"); + break; + } + if (actionPerformed) { + this._points = []; + } + } + + if (!actionPerformed) { + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent( + new CustomEvent("dashOnGesture", { bubbles: true, detail: { points: this._points, - gesture: GestureUtils.Gestures.Box, + gesture: GestureUtils.Gestures.Stroke, bounds: B } - })); - actionPerformed = true; - break; - case GestureUtils.Gestures.Line: - actionPerformed = this.handleLineGesture(); - break; - case GestureUtils.Gestures.Scribble: - console.log("scribble"); - break; - } - if (actionPerformed) { + } + ) + ); this._points = []; } } - - if (!actionPerformed) { - const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); - target?.dispatchEvent( - new CustomEvent("dashOnGesture", - { - bubbles: true, - detail: { - points: this._points, - gesture: GestureUtils.Gestures.Stroke, - bounds: B - } - } - ) - ); - this._points = []; - } } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -491,16 +503,24 @@ export default class GestureOverlay extends Touchable { transform: `translate(${this._thumbX}px, ${(this._thumbY ?? 0) - this.height}px)`, height: this.height, width: this.height, - pointerEvents: this._showBounds ? "unset" : "none", - touchAction: this._showBounds ? "unset" : "none", - display: this._showBounds ? "unset" : "none", + pointerEvents: "none", + touchAction: "none", + display: this.showBounds ? "unset" : "none", }}>
); } } +export enum ToolglassTools { + InkToText = "inktotext", + None = "none", +} + Scripting.addGlobal("GestureOverlay", GestureOverlay); +Scripting.addGlobal(function setToolglass(tool: any) { + runInAction(() => GestureOverlay.Instance.Tool = tool); +}); Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 3e6399d82..5375c8ac8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -114,7 +114,8 @@ export class CurrentUserUtils { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2