cleanipp
Panayiotis Lipiridis 4 years ago
commit fde1579884

249
package-lock.json generated

@ -1368,9 +1368,9 @@
} }
}, },
"@firebase/database": { "@firebase/database": {
"version": "0.8.2", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.8.2.tgz", "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.8.3.tgz",
"integrity": "sha512-E86yrom0Ii+61UScG44y1q3H3NuozzGGTGbYmiyTe1qK8Qvzuiu7yyfdDnqFW2fkeKvTRLoDeCpgZy27FgEndQ==", "integrity": "sha512-i29rr3kcPltIkA8La9M1lgsSxx9bfu5lCQ0T+tbJptZ3UpqpcL1NzCcZa24cJjiLgq3HQNPyLvUvCtcPSFDlRg==",
"requires": { "requires": {
"@firebase/auth-interop-types": "0.1.5", "@firebase/auth-interop-types": "0.1.5",
"@firebase/component": "0.1.21", "@firebase/component": "0.1.21",
@ -1649,17 +1649,17 @@
"dev": true "dev": true
}, },
"@google-cloud/pubsub": { "@google-cloud/pubsub": {
"version": "2.7.0", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-2.7.0.tgz", "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-2.8.0.tgz",
"integrity": "sha512-wc/XOo5Ibo3GWmuaLu80EBIhXSdu2vf99HUqBbdsSSkmRNIka2HqoIhLlOFnnncQn0lZnGL7wtKGIDLoH9LiBg==", "integrity": "sha512-AoSKAbpHCoLq6jO9vMX+K6hJhkayafan24Rs2RKHU8Y0qF6IGSm1+ly0OG12TgziHWg818/6dljWWKgwDcp8KA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@google-cloud/paginator": "^3.0.0", "@google-cloud/paginator": "^3.0.0",
"@google-cloud/precise-date": "^2.0.0", "@google-cloud/precise-date": "^2.0.0",
"@google-cloud/projectify": "^2.0.0", "@google-cloud/projectify": "^2.0.0",
"@google-cloud/promisify": "^2.0.0", "@google-cloud/promisify": "^2.0.0",
"@opentelemetry/api": "^0.11.0", "@opentelemetry/api": "^0.12.0",
"@opentelemetry/tracing": "^0.11.0", "@opentelemetry/tracing": "^0.12.0",
"@types/duplexify": "^3.6.0", "@types/duplexify": "^3.6.0",
"@types/long": "^4.0.0", "@types/long": "^4.0.0",
"arrify": "^2.0.0", "arrify": "^2.0.0",
@ -2460,28 +2460,28 @@
} }
}, },
"@opentelemetry/api": { "@opentelemetry/api": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.12.0.tgz",
"integrity": "sha512-K+1ADLMxduhsXoZ0GRfi9Pw162FvzBQLDQlHru1lg86rpIU+4XqdJkSGo6y3Kg+GmOWq1HNHOA/ydw/rzHQkRg==", "integrity": "sha512-Dn4vU5GlaBrIWzLpsM6xbJwKHdlpwBQ4Bd+cL9ofJP3hKT8jBXpBpribmyaqAzrajzzl2Yt8uTa9rFVLfjDAvw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@opentelemetry/context-base": "^0.11.0" "@opentelemetry/context-base": "^0.12.0"
} }
}, },
"@opentelemetry/context-base": { "@opentelemetry/context-base": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.12.0.tgz",
"integrity": "sha512-ESRk+572bftles7CVlugAj5Azrz61VO0MO0TS2pE9MLVL/zGmWuUBQryART6/nsrFqo+v9HPt37GPNcECTZR1w==", "integrity": "sha512-UXwSsXo3F3yZ1dIBOG9ID8v2r9e+bqLWoizCtTb8rXtwF+N5TM7hzzvQz72o3nBU+zrI/D5e+OqAYK8ZgDd3DA==",
"dev": true "dev": true
}, },
"@opentelemetry/core": { "@opentelemetry/core": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.12.0.tgz",
"integrity": "sha512-ZEKjBXeDGBqzouz0uJmrbEKNExEsQOhsZ3tJDCLcz5dUNoVw642oIn2LYWdQK2YdIfZbEmltiF65/csGsaBtFA==", "integrity": "sha512-oLZIkmTNWTJXzo1eA4dGu/S7wOVtylsgnEsCmhSJGhrJVDXm1eW/aGuNs3DVBeuxp0ZvQLAul3/PThsC3YrnzA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@opentelemetry/api": "^0.11.0", "@opentelemetry/api": "^0.12.0",
"@opentelemetry/context-base": "^0.11.0", "@opentelemetry/context-base": "^0.12.0",
"semver": "^7.1.3" "semver": "^7.1.3"
}, },
"dependencies": { "dependencies": {
@ -2506,32 +2506,32 @@
} }
}, },
"@opentelemetry/resources": { "@opentelemetry/resources": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.12.0.tgz",
"integrity": "sha512-o7DwV1TcezqBtS5YW2AWBcn01nVpPptIbTr966PLlVBcS//w8LkjeOShiSZxQ0lmV4b2en0FiSouSDoXk/5qIQ==", "integrity": "sha512-8cYvIKB68cyupc7D6SWzkLtt13mbjgxMahL4JKCM6hWPyiGSJlPFEAey4XFXI5LLpPZRYTPHLVoLqI/xwCFZZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@opentelemetry/api": "^0.11.0", "@opentelemetry/api": "^0.12.0",
"@opentelemetry/core": "^0.11.0" "@opentelemetry/core": "^0.12.0"
} }
}, },
"@opentelemetry/semantic-conventions": { "@opentelemetry/semantic-conventions": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.12.0.tgz",
"integrity": "sha512-xsthnI/J+Cx0YVDGgUzvrH0ZTtfNtl866M454NarYwDrc0JvC24sYw+XS5PJyk2KDzAHtb0vlrumUc1OAut/Fw==", "integrity": "sha512-BuCcDW0uLNYYTns0/LwXkJ8lp8aDm7kpS+WunEmPAPRSCe6ciOYRvzn5reqJfX93rf+6A3U2SgrBnCTH+0qoQQ==",
"dev": true "dev": true
}, },
"@opentelemetry/tracing": { "@opentelemetry/tracing": {
"version": "0.11.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/tracing/-/tracing-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/tracing/-/tracing-0.12.0.tgz",
"integrity": "sha512-QweFmxzl32BcyzwdWCNjVXZT1WeENNS/RWETq/ohqu+fAsTcMyGcr6cOq/yDdFmtBy+bm5WVVdeByEjNS+c4/w==", "integrity": "sha512-2TUGhTGkhgnxTciHCNAILPSeyXageJewRqfP9wOrx65sKd/jgvNYoY8nYf4EVWVMirDOxKDsmYgUkjdQrwb2dg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@opentelemetry/api": "^0.11.0", "@opentelemetry/api": "^0.12.0",
"@opentelemetry/context-base": "^0.11.0", "@opentelemetry/context-base": "^0.12.0",
"@opentelemetry/core": "^0.11.0", "@opentelemetry/core": "^0.12.0",
"@opentelemetry/resources": "^0.11.0", "@opentelemetry/resources": "^0.12.0",
"@opentelemetry/semantic-conventions": "^0.11.0" "@opentelemetry/semantic-conventions": "^0.12.0"
} }
}, },
"@pmmmwh/react-refresh-webpack-plugin": { "@pmmmwh/react-refresh-webpack-plugin": {
@ -2663,70 +2663,125 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.30.0.tgz",
"integrity": "sha512-uxZ7y7rp85tJll+RZtXRhXPbnFnOaxZqJEv05vJlXBtBNLQtlczV5iCtU9mZRLVHDtmZ5VVKUV8IKXntEqqDpQ==", "integrity": "sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==",
"requires": { "requires": {
"@sentry/core": "5.29.2", "@sentry/core": "5.30.0",
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"@sentry/utils": "5.29.2", "@sentry/utils": "5.30.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
},
"dependencies": {
"@sentry/types": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
"integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw=="
},
"@sentry/utils": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
"integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
"requires": {
"@sentry/types": "5.30.0",
"tslib": "^1.9.3"
}
}
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz",
"integrity": "sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw==", "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==",
"requires": { "requires": {
"@sentry/hub": "5.29.2", "@sentry/hub": "5.30.0",
"@sentry/minimal": "5.29.2", "@sentry/minimal": "5.30.0",
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"@sentry/utils": "5.29.2", "@sentry/utils": "5.30.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
},
"dependencies": {
"@sentry/types": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
"integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw=="
},
"@sentry/utils": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
"integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
"requires": {
"@sentry/types": "5.30.0",
"tslib": "^1.9.3"
}
}
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz",
"integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==", "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==",
"requires": { "requires": {
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"@sentry/utils": "5.29.2", "@sentry/utils": "5.30.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
},
"dependencies": {
"@sentry/types": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
"integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw=="
},
"@sentry/utils": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
"integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
"requires": {
"@sentry/types": "5.30.0",
"tslib": "^1.9.3"
}
}
} }
}, },
"@sentry/integrations": { "@sentry/integrations": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-5.30.0.tgz",
"integrity": "sha512-bH50B0xubbHrJFq8xZRxOc5BgXe1PXKfC0OqQkhhSd+Bu2WDLCHcn0CEzV+8thZTYkipAoFAFJNdEWcsM2Wcew==", "integrity": "sha512-Fqh4ALLoQWdd+1ih0iBduANWFyNmFWMxwvBu3V/wLDRi8OcquI0lEzWai1InzTJTiNhRHPnhuU++l/vkO0OCww==",
"requires": { "requires": {
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"@sentry/utils": "5.29.2", "@sentry/utils": "5.30.0",
"localforage": "1.8.1", "localforage": "1.8.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz",
"integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==", "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==",
"requires": { "requires": {
"@sentry/hub": "5.29.2", "@sentry/hub": "5.30.0",
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
},
"dependencies": {
"@sentry/types": {
"version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
"integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw=="
}
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
"integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA==" "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "5.29.2", "version": "5.30.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
"integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==", "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
"requires": { "requires": {
"@sentry/types": "5.29.2", "@sentry/types": "5.30.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
@ -2992,9 +3047,9 @@
} }
}, },
"@testing-library/jest-dom": { "@testing-library/jest-dom": {
"version": "5.11.8", "version": "5.11.9",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.8.tgz", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz",
"integrity": "sha512-ScyKrWQM5xNcr79PkSewnA79CLaoxVskE+f7knTOhDD9ftZSA1Jw8mj+pneqhEu3x37ncNfW84NUr7lqK+mXjA==", "integrity": "sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==",
"requires": { "requires": {
"@babel/runtime": "^7.9.2", "@babel/runtime": "^7.9.2",
"@types/testing-library__jest-dom": "^5.9.1", "@types/testing-library__jest-dom": "^5.9.1",
@ -3360,9 +3415,9 @@
} }
}, },
"@types/socket.io-client": { "@types/socket.io-client": {
"version": "1.4.34", "version": "1.4.35",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz", "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.35.tgz",
"integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==" "integrity": "sha512-MI8YmxFS+jMkIziycT5ickBWK1sZwDwy16mgH/j99Mcom6zRG/NimNGQ3vJV0uX5G6g/hEw0FG3w3b3sT5OUGw=="
}, },
"@types/source-list-map": { "@types/source-list-map": {
"version": "0.1.2", "version": "0.1.2",
@ -8497,9 +8552,9 @@
} }
}, },
"qs": { "qs": {
"version": "6.9.4", "version": "6.9.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==",
"dev": true "dev": true
}, },
"semver": { "semver": {
@ -9012,15 +9067,15 @@
} }
}, },
"firebase": { "firebase": {
"version": "8.2.2", "version": "8.2.3",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-8.2.2.tgz", "resolved": "https://registry.npmjs.org/firebase/-/firebase-8.2.3.tgz",
"integrity": "sha512-a07aW2TTAA9S7p4mx5pu8hvtVokJEjAQlAocHKOWwmRJRIduE9Vvr/3i50FtujT5gGNr0Qm+EyWyB+/7TJiwnw==", "integrity": "sha512-WdbcGSiLxiW/kGZT+EyqD9z3Md7kR35+k9qMjDn/twiIrm6Hh7Qi/Z69cqxhKW6+4uK5ghXIF28CjK67OyD9Qw==",
"requires": { "requires": {
"@firebase/analytics": "0.6.2", "@firebase/analytics": "0.6.2",
"@firebase/app": "0.6.13", "@firebase/app": "0.6.13",
"@firebase/app-types": "0.6.1", "@firebase/app-types": "0.6.1",
"@firebase/auth": "0.16.1", "@firebase/auth": "0.16.1",
"@firebase/database": "0.8.2", "@firebase/database": "0.8.3",
"@firebase/firestore": "2.1.2", "@firebase/firestore": "2.1.2",
"@firebase/functions": "0.6.1", "@firebase/functions": "0.6.1",
"@firebase/installations": "0.4.19", "@firebase/installations": "0.4.19",
@ -9033,9 +9088,9 @@
} }
}, },
"firebase-tools": { "firebase-tools": {
"version": "9.1.2", "version": "9.2.1",
"resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.1.2.tgz", "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.2.1.tgz",
"integrity": "sha512-YUiqMuQ+nbdCNpahSO0eyKxxVfT0nDdijkUEUplTGArkDwqdOKPIxVqHj1edq7GEPXTRWlk7zibnbOnCCHaedw==", "integrity": "sha512-sD4wfB5hs/8IKXV6AJOmkpvXf/St7gVc9QeW4Qz21PG7CkirgRf6FqcYkPKtBcro4wfj48dihnYx/IO1+XPTGg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@google-cloud/pubsub": "^2.7.0", "@google-cloud/pubsub": "^2.7.0",
@ -10138,9 +10193,9 @@
}, },
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "13.13.39", "version": "13.13.40",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.39.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.40.tgz",
"integrity": "sha512-wct+WgRTTkBm2R3vbrFOqyZM5w0g+D8KnhstG9463CJBVC3UVZHMToge7iMBR1vDl/I+NWFHUeK9X+JcF0rWKw==", "integrity": "sha512-eKaRo87lu1yAXrzEJl0zcJxfUMDT5/mZalFyOkT44rnQps41eS2pfWzbaulSPpQLFNy29bFqn+Y5lOTL8ATlEQ==",
"dev": true "dev": true
}, },
"duplexify": { "duplexify": {
@ -10776,9 +10831,9 @@
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
}, },
"husky": { "husky": {
"version": "4.3.7", "version": "4.3.8",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.3.7.tgz", "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz",
"integrity": "sha512-0fQlcCDq/xypoyYSJvEuzbDPHFf8ZF9IXKJxlrnvxABTSzK1VPT2RKYQKrcgJ+YD39swgoB6sbzywUqFxUiqjw==", "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^4.0.0", "chalk": "^4.0.0",
@ -17645,9 +17700,9 @@
} }
}, },
"proxy-agent": { "proxy-agent": {
"version": "4.0.0", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-4.0.0.tgz", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-4.0.1.tgz",
"integrity": "sha512-8P0Y2SkwvKjiGU1IkEfYuTteioMIDFxPL4/j49zzt5Mz3pG1KO+mIrDG1qH0PQUHTTczjwGcYl+EzfXiFj5vUQ==", "integrity": "sha512-ODnQnW2jc/FUVwHHuaZEfN5otg/fMbvMxz9nMSUQfJ9JU7q2SZvSULSsjLloVgJOiv9yhc8GlNMKc4GkFmcVEA==",
"dev": true, "dev": true,
"requires": { "requires": {
"agent-base": "^6.0.0", "agent-base": "^6.0.0",

@ -19,17 +19,17 @@
] ]
}, },
"dependencies": { "dependencies": {
"@sentry/browser": "5.29.2", "@sentry/browser": "5.30.0",
"@sentry/integrations": "5.29.2", "@sentry/integrations": "5.30.0",
"@testing-library/jest-dom": "5.11.8", "@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.3", "@testing-library/react": "11.2.3",
"@types/jest": "26.0.20", "@types/jest": "26.0.20",
"@types/react": "17.0.0", "@types/react": "17.0.0",
"@types/react-dom": "17.0.0", "@types/react-dom": "17.0.0",
"@types/socket.io-client": "1.4.34", "@types/socket.io-client": "1.4.35",
"browser-nativefs": "0.12.0", "browser-nativefs": "0.12.0",
"clsx": "1.1.1", "clsx": "1.1.1",
"firebase": "8.2.2", "firebase": "8.2.3",
"i18next-browser-languagedetector": "6.0.1", "i18next-browser-languagedetector": "6.0.1",
"lodash.throttle": "4.1.1", "lodash.throttle": "4.1.1",
"nanoid": "3.1.20", "nanoid": "3.1.20",
@ -53,8 +53,8 @@
"@types/pako": "1.0.1", "@types/pako": "1.0.1",
"eslint-config-prettier": "7.1.0", "eslint-config-prettier": "7.1.0",
"eslint-plugin-prettier": "3.3.1", "eslint-plugin-prettier": "3.3.1",
"firebase-tools": "9.1.2", "firebase-tools": "9.2.1",
"husky": "4.3.7", "husky": "4.3.8",
"jest-canvas-mock": "2.3.0", "jest-canvas-mock": "2.3.0",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"pepjs": "0.5.3", "pepjs": "0.5.3",

@ -74,13 +74,13 @@ export const actionShortcuts = register({
return { return {
appState: { appState: {
...appState, ...appState,
showShortcutsDialog: true, showHelpDialog: true,
}, },
commitToHistory: false, commitToHistory: false,
}; };
}, },
PanelComponent: ({ updateData }) => ( PanelComponent: ({ updateData }) => (
<HelpIcon title={t("shortcutsDialog.title")} onClick={updateData} /> <HelpIcon title={t("helpDialog.title")} onClick={updateData} />
), ),
keyTest: (event) => event.key === KEYS.QUESTION_MARK, keyTest: (event) => event.key === KEYS.QUESTION_MARK,
}); });

@ -34,7 +34,7 @@ const shortcutMap: Record<ShortcutName, string[]> = {
delete: [getShortcutKey("Del")], delete: [getShortcutKey("Del")],
duplicateSelection: [ duplicateSelection: [
getShortcutKey("CtrlOrCmd+D"), getShortcutKey("CtrlOrCmd+D"),
getShortcutKey(`Alt+${t("shortcutsDialog.drag")}`), getShortcutKey(`Alt+${t("helpDialog.drag")}`),
], ],
sendBackward: [getShortcutKey("CtrlOrCmd+[")], sendBackward: [getShortcutKey("CtrlOrCmd+[")],
bringForward: [getShortcutKey("CtrlOrCmd+]")], bringForward: [getShortcutKey("CtrlOrCmd+]")],

@ -63,7 +63,7 @@ export const getDefaultAppState = (): Omit<
selectionElement: null, selectionElement: null,
shouldAddWatermark: false, shouldAddWatermark: false,
shouldCacheIgnoreZoom: false, shouldCacheIgnoreZoom: false,
showShortcutsDialog: false, showHelpDialog: false,
showStats: false, showStats: false,
startBoundElement: null, startBoundElement: null,
suggestedBindings: [], suggestedBindings: [],
@ -142,7 +142,7 @@ const APP_STATE_STORAGE_CONF = (<
selectionElement: { browser: false, export: false }, selectionElement: { browser: false, export: false },
shouldAddWatermark: { browser: true, export: false }, shouldAddWatermark: { browser: true, export: false },
shouldCacheIgnoreZoom: { browser: true, export: false }, shouldCacheIgnoreZoom: { browser: true, export: false },
showShortcutsDialog: { browser: false, export: false }, showHelpDialog: { browser: false, export: false },
showStats: { browser: true, export: false }, showStats: { browser: true, export: false },
startBoundElement: { browser: false, export: false }, startBoundElement: { browser: false, export: false },
suggestedBindings: { browser: false, export: false }, suggestedBindings: { browser: false, export: false },

@ -163,9 +163,9 @@ export const ShapesSwitcher = ({
{SHAPES.map(({ value, icon, key }, index) => { {SHAPES.map(({ value, icon, key }, index) => {
const label = t(`toolBar.${value}`); const label = t(`toolBar.${value}`);
const letter = typeof key === "string" ? key : key[0]; const letter = typeof key === "string" ? key : key[0];
const shortcut = `${capitalizeString(letter)} ${t( const shortcut = `${capitalizeString(letter)} ${t("helpDialog.or")} ${
"shortcutsDialog.or", index + 1
)} ${index + 1}`; }`;
return ( return (
<ToolButton <ToolButton
className="Shape" className="Shape"

@ -1249,7 +1249,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (event.key === KEYS.QUESTION_MARK) { if (event.key === KEYS.QUESTION_MARK) {
this.setState({ this.setState({
showShortcutsDialog: true, showHelpDialog: true,
}); });
} }
@ -3587,9 +3587,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
transformElements( transformElements(
pointerDownState, pointerDownState,
transformHandleType, transformHandleType,
(newTransformHandle) => {
pointerDownState.resize.handleType = newTransformHandle;
},
selectedElements, selectedElements,
pointerDownState.resize.arrowDirection, pointerDownState.resize.arrowDirection,
getRotateWithDiscreteAngleKey(event), getRotateWithDiscreteAngleKey(event),

@ -15,6 +15,7 @@
padding: calc(var(--space-factor) * 2); padding: calc(var(--space-factor) * 2);
text-align: center; text-align: center;
font-variant: small-caps; font-variant: small-caps;
font-size: 1.2em;
} }
.Dialog__titleContent { .Dialog__titleContent {

@ -80,7 +80,7 @@ export const Dialog = (props: {
onCloseRequest={props.onCloseRequest} onCloseRequest={props.onCloseRequest}
> >
<Island ref={setIslandNode}> <Island ref={setIslandNode}>
<h3 id="dialog-title" className="Dialog__title"> <h2 id="dialog-title" className="Dialog__title">
<span className="Dialog__titleContent">{props.title}</span> <span className="Dialog__titleContent">{props.title}</span>
<button <button
className="Modal__close" className="Modal__close"
@ -89,7 +89,7 @@ export const Dialog = (props: {
> >
{useIsMobile() ? back : close} {useIsMobile() ? back : close}
</button> </button>
</h3> </h2>
<div className="Dialog__content">{props.children}</div> <div className="Dialog__content">{props.children}</div>
</Island> </Island>
</Modal> </Modal>

@ -1,23 +1,28 @@
@import "../css/_variables"; @import "../css/_variables";
.excalidraw { .excalidraw {
.ShortcutsDialog-island { .HelpDialog h3 {
border-bottom: 1px solid var(--button-gray-2);
padding-bottom: 4px;
}
.HelpDialog--island {
border: 1px solid var(--button-gray-2); border: 1px solid var(--button-gray-2);
margin-bottom: 16px; margin-bottom: 16px;
} }
.ShortcutsDialog-island-title { .HelpDialog--island-title {
margin: 0; margin: 0;
padding: 4px; padding: 4px;
background-color: var(--button-gray-1); background-color: var(--button-gray-1);
text-align: center; text-align: center;
} }
.ShorcutsDialog-shortcut { .HelpDialog--shortcut {
border-top: 1px solid var(--button-gray-2); border-top: 1px solid var(--button-gray-2);
} }
.ShorcutsDialog-key { .HelpDialog--key {
word-break: keep-all; word-break: keep-all;
border: 1px solid var(--button-gray-2); border: 1px solid var(--button-gray-2);
padding: 2px 8px; padding: 2px 8px;
@ -32,12 +37,20 @@
font-family: inherit; font-family: inherit;
} }
.ShortcutsDialog-footer { .HelpDialog--header {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-evenly; justify-content: space-evenly;
border-top: 1px solid var(--button-gray-2); margin-bottom: 32px;
margin-top: 8px; padding-bottom: 16px;
padding-top: 16px; }
.HelpDialog--btn {
border: 1px solid var(--link-color);
padding: 8px 32px;
border-radius: 4px;
}
.HelpDialog--btn:hover {
text-decoration: none;
} }
} }

@ -0,0 +1,348 @@
import React from "react";
import { t } from "../i18n";
import { isDarwin } from "../keys";
import { Dialog } from "./Dialog";
import { getShortcutKey } from "../utils";
import "./HelpDialog.scss";
const Header = () => (
<div className="HelpDialog--header">
<a
className="HelpDialog--btn"
href="https://github.com/excalidraw/excalidraw#documentation"
target="_blank"
rel="noopener noreferrer"
>
{t("helpDialog.documentation")}
</a>
<a
className="HelpDialog--btn"
href="https://blog.excalidraw.com"
target="_blank"
rel="noopener noreferrer"
>
{t("helpDialog.blog")}
</a>
<a
className="HelpDialog--btn"
href="https://github.com/excalidraw/excalidraw/issues"
target="_blank"
rel="noopener noreferrer"
>
{t("helpDialog.github")}
</a>
</div>
);
const Section = (props: { title: string; children: React.ReactNode }) => (
<>
<h3>{props.title}</h3>
{props.children}
</>
);
const Columns = (props: { children: React.ReactNode }) => (
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-between",
}}
>
{props.children}
</div>
);
const Column = (props: { children: React.ReactNode }) => (
<div style={{ width: "49%" }}>{props.children}</div>
);
const ShortcutIsland = (props: {
caption: string;
children: React.ReactNode;
}) => (
<div className="HelpDialog--island">
<h3 className="HelpDialog--island-title">{props.caption}</h3>
{props.children}
</div>
);
const Shortcut = (props: {
label: string;
shortcuts: string[];
isOr: boolean;
}) => {
return (
<div className="HelpDialog--shortcut">
<div
style={{
display: "flex",
margin: "0",
padding: "4px 8px",
alignItems: "center",
}}
>
<div
style={{
lineHeight: 1.4,
}}
>
{props.label}
</div>
<div
style={{
display: "flex",
flex: "0 0 auto",
justifyContent: "flex-end",
marginInlineStart: "auto",
minWidth: "30%",
}}
>
{props.shortcuts.map((shortcut, index) => (
<React.Fragment key={index}>
<ShortcutKey>{shortcut}</ShortcutKey>
{props.isOr &&
index !== props.shortcuts.length - 1 &&
t("helpDialog.or")}
</React.Fragment>
))}
</div>
</div>
</div>
);
};
Shortcut.defaultProps = {
isOr: true,
};
const ShortcutKey = (props: { children: React.ReactNode }) => (
<kbd className="HelpDialog--key" {...props} />
);
export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
const handleClose = React.useCallback(() => {
if (onClose) {
onClose();
}
}, [onClose]);
return (
<>
<Dialog
onCloseRequest={handleClose}
title={t("helpDialog.title")}
className={"HelpDialog"}
>
<Header />
<Section title={t("helpDialog.shortcuts")}>
<Columns>
<Column>
<ShortcutIsland caption={t("helpDialog.shapes")}>
<Shortcut
label={t("toolBar.selection")}
shortcuts={["V", "1"]}
/>
<Shortcut
label={t("toolBar.rectangle")}
shortcuts={["R", "2"]}
/>
<Shortcut label={t("toolBar.diamond")} shortcuts={["D", "3"]} />
<Shortcut label={t("toolBar.ellipse")} shortcuts={["E", "4"]} />
<Shortcut label={t("toolBar.arrow")} shortcuts={["A", "5"]} />
<Shortcut label={t("toolBar.line")} shortcuts={["P", "6"]} />
<Shortcut
label={t("toolBar.draw")}
shortcuts={["Shift+P", "7"]}
/>
<Shortcut label={t("toolBar.text")} shortcuts={["T", "8"]} />
<Shortcut
label={t("helpDialog.textNewLine")}
shortcuts={[
getShortcutKey("Enter"),
getShortcutKey("Shift+Enter"),
]}
/>
<Shortcut
label={t("helpDialog.textFinish")}
shortcuts={[
getShortcutKey("Esc"),
getShortcutKey("CtrlOrCmd+Enter"),
]}
/>
<Shortcut
label={t("helpDialog.curvedArrow")}
shortcuts={[
"A",
t("helpDialog.click"),
t("helpDialog.click"),
t("helpDialog.click"),
]}
isOr={false}
/>
<Shortcut
label={t("helpDialog.curvedLine")}
shortcuts={[
"L",
t("helpDialog.click"),
t("helpDialog.click"),
t("helpDialog.click"),
]}
isOr={false}
/>
<Shortcut label={t("toolBar.lock")} shortcuts={["Q"]} />
<Shortcut
label={t("helpDialog.preventBinding")}
shortcuts={[getShortcutKey("CtrlOrCmd")]}
/>
</ShortcutIsland>
<ShortcutIsland caption={t("helpDialog.view")}>
<Shortcut
label={t("buttons.zoomIn")}
shortcuts={[getShortcutKey("CtrlOrCmd++")]}
/>
<Shortcut
label={t("buttons.zoomOut")}
shortcuts={[getShortcutKey("CtrlOrCmd+-")]}
/>
<Shortcut
label={t("buttons.resetZoom")}
shortcuts={[getShortcutKey("CtrlOrCmd+0")]}
/>
<Shortcut
label={t("helpDialog.zoomToFit")}
shortcuts={["Shift+1"]}
/>
<Shortcut
label={t("helpDialog.zoomToSelection")}
shortcuts={["Shift+2"]}
/>
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
<Shortcut
label={t("buttons.zenMode")}
shortcuts={[getShortcutKey("Alt+Z")]}
/>
<Shortcut
label={t("labels.gridMode")}
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
/>
</ShortcutIsland>
</Column>
<Column>
<ShortcutIsland caption={t("helpDialog.editor")}>
<Shortcut
label={t("labels.selectAll")}
shortcuts={[getShortcutKey("CtrlOrCmd+A")]}
/>
<Shortcut
label={t("labels.multiSelect")}
shortcuts={[getShortcutKey(`Shift+${t("helpDialog.click")}`)]}
/>
<Shortcut
label={t("labels.moveCanvas")}
shortcuts={[
getShortcutKey(`Space+${t("helpDialog.drag")}`),
getShortcutKey(`Wheel+${t("helpDialog.drag")}`),
]}
isOr={true}
/>
<Shortcut
label={t("labels.cut")}
shortcuts={[getShortcutKey("CtrlOrCmd+X")]}
/>
<Shortcut
label={t("labels.copy")}
shortcuts={[getShortcutKey("CtrlOrCmd+C")]}
/>
<Shortcut
label={t("labels.paste")}
shortcuts={[getShortcutKey("CtrlOrCmd+V")]}
/>
<Shortcut
label={t("labels.copyAsPng")}
shortcuts={[getShortcutKey("Shift+Alt+C")]}
/>
<Shortcut
label={t("labels.copyStyles")}
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+C")]}
/>
<Shortcut
label={t("labels.pasteStyles")}
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+V")]}
/>
<Shortcut
label={t("labels.delete")}
shortcuts={[getShortcutKey("Del")]}
/>
<Shortcut
label={t("labels.sendToBack")}
shortcuts={[
isDarwin
? getShortcutKey("CtrlOrCmd+Alt+[")
: getShortcutKey("CtrlOrCmd+Shift+["),
]}
/>
<Shortcut
label={t("labels.bringToFront")}
shortcuts={[
isDarwin
? getShortcutKey("CtrlOrCmd+Alt+]")
: getShortcutKey("CtrlOrCmd+Shift+]"),
]}
/>
<Shortcut
label={t("labels.sendBackward")}
shortcuts={[getShortcutKey("CtrlOrCmd+[")]}
/>
<Shortcut
label={t("labels.bringForward")}
shortcuts={[getShortcutKey("CtrlOrCmd+]")]}
/>
<Shortcut
label={t("labels.alignTop")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Up")]}
/>
<Shortcut
label={t("labels.alignBottom")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Down")]}
/>
<Shortcut
label={t("labels.alignLeft")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Left")]}
/>
<Shortcut
label={t("labels.alignRight")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Right")]}
/>
<Shortcut
label={t("labels.duplicateSelection")}
shortcuts={[
getShortcutKey("CtrlOrCmd+D"),
getShortcutKey(`Alt+${t("helpDialog.drag")}`),
]}
/>
<Shortcut
label={t("buttons.undo")}
shortcuts={[getShortcutKey("CtrlOrCmd+Z")]}
/>
<Shortcut
label={t("buttons.redo")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Z")]}
/>
<Shortcut
label={t("labels.group")}
shortcuts={[getShortcutKey("CtrlOrCmd+G")]}
/>
<Shortcut
label={t("labels.ungroup")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+G")]}
/>
</ShortcutIsland>
</Column>
</Columns>
</Section>
</Dialog>
</>
);
};

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { questionCircle } from "../components/icons";
type HelpIconProps = { type HelpIconProps = {
title?: string; title?: string;
@ -7,19 +8,8 @@ type HelpIconProps = {
onClick?(): void; onClick?(): void;
}; };
const ICON = (
<svg
width="30"
height="22"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M528 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h480c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM128 180v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm288 0v-40c0-6.627-5.373-12-12-12H172c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" />
</svg>
);
export const HelpIcon = (props: HelpIconProps) => ( export const HelpIcon = (props: HelpIconProps) => (
<label title={`${props.title} — ?`} className="help-icon"> <label title={`${props.title} — ?`} className="help-icon">
<div onClick={props.onClick}>{ICON}</div> <div onClick={props.onClick}>{questionCircle}</div>
</label> </label>
); );

@ -36,7 +36,7 @@ import { LockIcon } from "./LockIcon";
import { MobileMenu } from "./MobileMenu"; import { MobileMenu } from "./MobileMenu";
import { PasteChartDialog } from "./PasteChartDialog"; import { PasteChartDialog } from "./PasteChartDialog";
import { Section } from "./Section"; import { Section } from "./Section";
import { ShortcutsDialog } from "./ShortcutsDialog"; import { HelpDialog } from "./HelpDialog";
import Stack from "./Stack"; import Stack from "./Stack";
import { ToolButton } from "./ToolButton"; import { ToolButton } from "./ToolButton";
import { Tooltip } from "./Tooltip"; import { Tooltip } from "./Tooltip";
@ -566,10 +566,8 @@ const LayerUI = ({
onClose={() => setAppState({ errorMessage: null })} onClose={() => setAppState({ errorMessage: null })}
/> />
)} )}
{appState.showShortcutsDialog && ( {appState.showHelpDialog && (
<ShortcutsDialog <HelpDialog onClose={() => setAppState({ showHelpDialog: false })} />
onClose={() => setAppState({ showShortcutsDialog: false })}
/>
)} )}
{appState.pasteDialog.shown && ( {appState.pasteDialog.shown && (
<PasteChartDialog <PasteChartDialog

@ -1,321 +0,0 @@
import React from "react";
import { t } from "../i18n";
import { isDarwin } from "../keys";
import { Dialog } from "./Dialog";
import { getShortcutKey } from "../utils";
import "./ShortcutsDialog.scss";
const Columns = (props: { children: React.ReactNode }) => (
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-between",
}}
>
{props.children}
</div>
);
const Column = (props: { children: React.ReactNode }) => (
<div style={{ width: "49%" }}>{props.children}</div>
);
const ShortcutIsland = (props: {
caption: string;
children: React.ReactNode;
}) => (
<div className="ShortcutsDialog-island">
<h3 className="ShortcutsDialog-island-title">{props.caption}</h3>
{props.children}
</div>
);
const Shortcut = (props: {
label: string;
shortcuts: string[];
isOr: boolean;
}) => {
return (
<div className="ShorcutsDialog-shortcut">
<div
style={{
display: "flex",
margin: "0",
padding: "4px 8px",
alignItems: "center",
}}
>
<div
style={{
lineHeight: 1.4,
}}
>
{props.label}
</div>
<div
style={{
display: "flex",
flex: "0 0 auto",
justifyContent: "flex-end",
marginInlineStart: "auto",
minWidth: "30%",
}}
>
{props.shortcuts.map((shortcut, index) => (
<React.Fragment key={index}>
<ShortcutKey>{shortcut}</ShortcutKey>
{props.isOr &&
index !== props.shortcuts.length - 1 &&
t("shortcutsDialog.or")}
</React.Fragment>
))}
</div>
</div>
</div>
);
};
Shortcut.defaultProps = {
isOr: true,
};
const ShortcutKey = (props: { children: React.ReactNode }) => (
<kbd className="ShorcutsDialog-key" {...props} />
);
const Footer = () => (
<div className="ShortcutsDialog-footer">
<a
href="https://blog.excalidraw.com"
target="_blank"
rel="noopener noreferrer"
>
{t("shortcutsDialog.blog")}
</a>
<a
href="https://github.com/excalidraw/excalidraw/issues"
target="_blank"
rel="noopener noreferrer"
>
{t("shortcutsDialog.github")}
</a>
</div>
);
export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
const handleClose = React.useCallback(() => {
if (onClose) {
onClose();
}
}, [onClose]);
return (
<>
<Dialog onCloseRequest={handleClose} title={t("shortcutsDialog.title")}>
<Columns>
<Column>
<ShortcutIsland caption={t("shortcutsDialog.shapes")}>
<Shortcut label={t("toolBar.selection")} shortcuts={["V", "1"]} />
<Shortcut label={t("toolBar.rectangle")} shortcuts={["R", "2"]} />
<Shortcut label={t("toolBar.diamond")} shortcuts={["D", "3"]} />
<Shortcut label={t("toolBar.ellipse")} shortcuts={["E", "4"]} />
<Shortcut label={t("toolBar.arrow")} shortcuts={["A", "5"]} />
<Shortcut label={t("toolBar.line")} shortcuts={["P", "6"]} />
<Shortcut
label={t("toolBar.draw")}
shortcuts={["Shift+P", "7"]}
/>
<Shortcut label={t("toolBar.text")} shortcuts={["T", "8"]} />
<Shortcut
label={t("shortcutsDialog.textNewLine")}
shortcuts={[
getShortcutKey("Enter"),
getShortcutKey("Shift+Enter"),
]}
/>
<Shortcut
label={t("shortcutsDialog.textFinish")}
shortcuts={[
getShortcutKey("Esc"),
getShortcutKey("CtrlOrCmd+Enter"),
]}
/>
<Shortcut
label={t("shortcutsDialog.curvedArrow")}
shortcuts={[
"A",
t("shortcutsDialog.click"),
t("shortcutsDialog.click"),
t("shortcutsDialog.click"),
]}
isOr={false}
/>
<Shortcut
label={t("shortcutsDialog.curvedLine")}
shortcuts={[
"L",
t("shortcutsDialog.click"),
t("shortcutsDialog.click"),
t("shortcutsDialog.click"),
]}
isOr={false}
/>
<Shortcut label={t("toolBar.lock")} shortcuts={["Q"]} />
<Shortcut
label={t("shortcutsDialog.preventBinding")}
shortcuts={[getShortcutKey("CtrlOrCmd")]}
/>
</ShortcutIsland>
<ShortcutIsland caption={t("shortcutsDialog.view")}>
<Shortcut
label={t("buttons.zoomIn")}
shortcuts={[getShortcutKey("CtrlOrCmd++")]}
/>
<Shortcut
label={t("buttons.zoomOut")}
shortcuts={[getShortcutKey("CtrlOrCmd+-")]}
/>
<Shortcut
label={t("buttons.resetZoom")}
shortcuts={[getShortcutKey("CtrlOrCmd+0")]}
/>
<Shortcut
label={t("shortcutsDialog.zoomToFit")}
shortcuts={["Shift+1"]}
/>
<Shortcut
label={t("shortcutsDialog.zoomToSelection")}
shortcuts={["Shift+2"]}
/>
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
<Shortcut
label={t("buttons.zenMode")}
shortcuts={[getShortcutKey("Alt+Z")]}
/>
<Shortcut
label={t("labels.gridMode")}
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
/>
</ShortcutIsland>
</Column>
<Column>
<ShortcutIsland caption={t("shortcutsDialog.editor")}>
<Shortcut
label={t("labels.selectAll")}
shortcuts={[getShortcutKey("CtrlOrCmd+A")]}
/>
<Shortcut
label={t("labels.multiSelect")}
shortcuts={[
getShortcutKey(`Shift+${t("shortcutsDialog.click")}`),
]}
/>
<Shortcut
label={t("labels.moveCanvas")}
shortcuts={[
getShortcutKey(`Space+${t("shortcutsDialog.drag")}`),
getShortcutKey(`Wheel+${t("shortcutsDialog.drag")}`),
]}
isOr={true}
/>
<Shortcut
label={t("labels.cut")}
shortcuts={[getShortcutKey("CtrlOrCmd+X")]}
/>
<Shortcut
label={t("labels.copy")}
shortcuts={[getShortcutKey("CtrlOrCmd+C")]}
/>
<Shortcut
label={t("labels.paste")}
shortcuts={[getShortcutKey("CtrlOrCmd+V")]}
/>
<Shortcut
label={t("labels.copyAsPng")}
shortcuts={[getShortcutKey("Shift+Alt+C")]}
/>
<Shortcut
label={t("labels.copyStyles")}
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+C")]}
/>
<Shortcut
label={t("labels.pasteStyles")}
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+V")]}
/>
<Shortcut
label={t("labels.delete")}
shortcuts={[getShortcutKey("Del")]}
/>
<Shortcut
label={t("labels.sendToBack")}
shortcuts={[
isDarwin
? getShortcutKey("CtrlOrCmd+Alt+[")
: getShortcutKey("CtrlOrCmd+Shift+["),
]}
/>
<Shortcut
label={t("labels.bringToFront")}
shortcuts={[
isDarwin
? getShortcutKey("CtrlOrCmd+Alt+]")
: getShortcutKey("CtrlOrCmd+Shift+]"),
]}
/>
<Shortcut
label={t("labels.sendBackward")}
shortcuts={[getShortcutKey("CtrlOrCmd+[")]}
/>
<Shortcut
label={t("labels.bringForward")}
shortcuts={[getShortcutKey("CtrlOrCmd+]")]}
/>
<Shortcut
label={t("labels.alignTop")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Up")]}
/>
<Shortcut
label={t("labels.alignBottom")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Down")]}
/>
<Shortcut
label={t("labels.alignLeft")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Left")]}
/>
<Shortcut
label={t("labels.alignRight")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Right")]}
/>
<Shortcut
label={t("labels.duplicateSelection")}
shortcuts={[
getShortcutKey("CtrlOrCmd+D"),
getShortcutKey(`Alt+${t("shortcutsDialog.drag")}`),
]}
/>
<Shortcut
label={t("buttons.undo")}
shortcuts={[getShortcutKey("CtrlOrCmd+Z")]}
/>
<Shortcut
label={t("buttons.redo")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+Z")]}
/>
<Shortcut
label={t("labels.group")}
shortcuts={[getShortcutKey("CtrlOrCmd+G")]}
/>
<Shortcut
label={t("labels.ungroup")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+G")]}
/>
</ShortcutIsland>
</Column>
</Columns>
<Footer />
</Dialog>
</>
);
};

@ -90,3 +90,4 @@ export const TAP_TWICE_TIMEOUT = 300;
export const TITLE_TIMEOUT = 10000; export const TITLE_TIMEOUT = 10000;
export const TOAST_TIMEOUT = 5000; export const TOAST_TIMEOUT = 5000;
export const TOUCH_CTX_MENU_TIMEOUT = 500; export const TOUCH_CTX_MENU_TIMEOUT = 500;
export const VERSION_TIMEOUT = 15000;

@ -13,7 +13,7 @@
a { a {
font-weight: 500; font-weight: 500;
text-decoration: none; text-decoration: none;
color: $oc-blue-7; /* OC Blue 7 */ color: var(--link-color);
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
@ -431,6 +431,7 @@
cursor: pointer; cursor: pointer;
fill: $oc-gray-6; fill: $oc-gray-6;
bottom: 14px; bottom: 14px;
width: 1.5rem;
:root[dir="ltr"] & { :root[dir="ltr"] & {
right: 14px; right: 14px;

@ -19,6 +19,7 @@
--input-label-color: #{$oc-gray-7}; --input-label-color: #{$oc-gray-7};
--island-bg-color: #{transparentize($oc-white, 0.12)}; --island-bg-color: #{transparentize($oc-white, 0.12)};
--keybinding-color: #{$oc-gray-5}; --keybinding-color: #{$oc-gray-5};
--link-color: #{$oc-blue-7};
--overlay-bg-color: #{transparentize($oc-white, 0.12)}; --overlay-bg-color: #{transparentize($oc-white, 0.12)};
--popup-bg-color: #{$oc-white}; --popup-bg-color: #{$oc-white};
--popup-secondary-bg-color: #{$oc-gray-1}; --popup-secondary-bg-color: #{$oc-gray-1};

@ -34,7 +34,6 @@ export {
export { export {
resizeTest, resizeTest,
getCursorForResizingElement, getCursorForResizingElement,
normalizeTransformHandleType,
getElementWithTransformHandleType, getElementWithTransformHandleType,
getTransformHandleTypeFromCoords, getTransformHandleTypeFromCoords,
} from "./resizeTest"; } from "./resizeTest";

@ -4,7 +4,6 @@ import { rescalePoints } from "../points";
import { import {
rotate, rotate,
adjustXYWithRotation, adjustXYWithRotation,
getFlipAdjustment,
centerPoint, centerPoint,
rotatePoint, rotatePoint,
} from "../math"; } from "../math";
@ -13,21 +12,16 @@ import {
ExcalidrawTextElement, ExcalidrawTextElement,
NonDeletedExcalidrawElement, NonDeletedExcalidrawElement,
NonDeleted, NonDeleted,
ExcalidrawGenericElement,
ExcalidrawElement,
} from "./types"; } from "./types";
import { import {
getElementAbsoluteCoords, getElementAbsoluteCoords,
getCommonBounds, getCommonBounds,
getResizedElementAbsoluteCoords, getResizedElementAbsoluteCoords,
} from "./bounds"; } from "./bounds";
import { isGenericElement, isLinearElement, isTextElement } from "./typeChecks"; import { isLinearElement, isTextElement } from "./typeChecks";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import { getPerfectElementSize } from "./sizeHelpers"; import { getPerfectElementSize } from "./sizeHelpers";
import { import { getCursorForResizingElement } from "./resizeTest";
getCursorForResizingElement,
normalizeTransformHandleType,
} from "./resizeTest";
import { measureText, getFontString } from "../utils"; import { measureText, getFontString } from "../utils";
import { updateBoundElements } from "./binding"; import { updateBoundElements } from "./binding";
import { import {
@ -49,7 +43,6 @@ const normalizeAngle = (angle: number): number => {
export const transformElements = ( export const transformElements = (
pointerDownState: PointerDownState, pointerDownState: PointerDownState,
transformHandleType: MaybeTransformHandleType, transformHandleType: MaybeTransformHandleType,
setTransformHandle: (nextTransformHandle: MaybeTransformHandleType) => void,
selectedElements: readonly NonDeletedExcalidrawElement[], selectedElements: readonly NonDeletedExcalidrawElement[],
resizeArrowDirection: "origin" | "end", resizeArrowDirection: "origin" | "end",
isRotateWithDiscreteAngle: boolean, isRotateWithDiscreteAngle: boolean,
@ -101,36 +94,15 @@ export const transformElements = (
); );
updateBoundElements(element); updateBoundElements(element);
} else if (transformHandleType) { } else if (transformHandleType) {
if (isGenericElement(element)) { resizeSingleElement(
resizeSingleGenericElement( pointerDownState.originalElements.get(element.id) as typeof element,
pointerDownState.originalElements.get(element.id) as typeof element, shouldKeepSidesRatio,
shouldKeepSidesRatio, element,
element, transformHandleType,
transformHandleType, isResizeCenterPoint,
isResizeCenterPoint, pointerX,
pointerX, pointerY,
pointerY, );
);
} else {
const keepSquareAspectRatio = shouldKeepSidesRatio;
resizeSingleNonGenericElement(
element,
transformHandleType,
isResizeCenterPoint,
keepSquareAspectRatio,
pointerX,
pointerY,
);
setTransformHandle(
normalizeTransformHandleType(element, transformHandleType),
);
if (element.width < 0) {
mutateElement(element, { width: -element.width });
}
if (element.height < 0) {
mutateElement(element, { height: -element.height });
}
}
} }
// update cursor // update cursor
@ -414,8 +386,8 @@ const resizeSingleTextElement = (
} }
}; };
const resizeSingleGenericElement = ( const resizeSingleElement = (
stateAtResizeStart: NonDeleted<ExcalidrawGenericElement>, stateAtResizeStart: NonDeletedExcalidrawElement,
shouldKeepSidesRatio: boolean, shouldKeepSidesRatio: boolean,
element: NonDeletedExcalidrawElement, element: NonDeletedExcalidrawElement,
transformHandleDirection: TransformHandleDirection, transformHandleDirection: TransformHandleDirection,
@ -423,251 +395,184 @@ const resizeSingleGenericElement = (
pointerX: number, pointerX: number,
pointerY: number, pointerY: number,
) => { ) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(stateAtResizeStart); // Gets bounds corners
const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(
stateAtResizeStart,
stateAtResizeStart.width,
stateAtResizeStart.height,
);
const startTopLeft: Point = [x1, y1]; const startTopLeft: Point = [x1, y1];
const startBottomRight: Point = [x2, y2]; const startBottomRight: Point = [x2, y2];
const startCenter: Point = centerPoint(startTopLeft, startBottomRight); const startCenter: Point = centerPoint(startTopLeft, startBottomRight);
// Calculate new dimensions based on cursor position // Calculate new dimensions based on cursor position
let newWidth = stateAtResizeStart.width;
let newHeight = stateAtResizeStart.height;
const rotatedPointer = rotatePoint( const rotatedPointer = rotatePoint(
[pointerX, pointerY], [pointerX, pointerY],
startCenter, startCenter,
-stateAtResizeStart.angle, -stateAtResizeStart.angle,
); );
//Get bounds corners rendered on screen
const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords(
element,
element.width,
element.height,
);
const boundsCurrentWidth = esx2 - esx1;
const boundsCurrentHeight = esy2 - esy1;
// It's important we set the initial scale value based on the width and height at resize start,
// otherwise previous dimensions affected by modifiers will be taken into account.
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1];
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
let scaleY = atStartBoundsHeight / boundsCurrentHeight;
if (transformHandleDirection.includes("e")) { if (transformHandleDirection.includes("e")) {
newWidth = rotatedPointer[0] - startTopLeft[0]; scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
} }
if (transformHandleDirection.includes("s")) { if (transformHandleDirection.includes("s")) {
newHeight = rotatedPointer[1] - startTopLeft[1]; scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight;
} }
if (transformHandleDirection.includes("w")) { if (transformHandleDirection.includes("w")) {
newWidth = startBottomRight[0] - rotatedPointer[0]; scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
} }
if (transformHandleDirection.includes("n")) { if (transformHandleDirection.includes("n")) {
newHeight = startBottomRight[1] - rotatedPointer[1]; scaleY = (startBottomRight[1] - rotatedPointer[1]) / boundsCurrentHeight;
} }
// Linear elements dimensions differ from bounds dimensions
const eleInitialWidth = stateAtResizeStart.width;
const eleInitialHeight = stateAtResizeStart.height;
// We have to use dimensions of element on screen, otherwise the scaling of the
// dimensions won't match the cursor for linear elements.
let eleNewWidth = element.width * scaleX;
let eleNewHeight = element.height * scaleY;
// adjust dimensions for resizing from center // adjust dimensions for resizing from center
if (isResizeFromCenter) { if (isResizeFromCenter) {
newWidth = 2 * newWidth - stateAtResizeStart.width; eleNewWidth = 2 * eleNewWidth - eleInitialWidth;
newHeight = 2 * newHeight - stateAtResizeStart.height; eleNewHeight = 2 * eleNewHeight - eleInitialHeight;
} }
// adjust dimensions to keep sides ratio // adjust dimensions to keep sides ratio
if (shouldKeepSidesRatio) { if (shouldKeepSidesRatio) {
const widthRatio = Math.abs(newWidth) / stateAtResizeStart.width; const widthRatio = Math.abs(eleNewWidth) / eleInitialWidth;
const heightRatio = Math.abs(newHeight) / stateAtResizeStart.height; const heightRatio = Math.abs(eleNewHeight) / eleInitialHeight;
if (transformHandleDirection.length === 1) { if (transformHandleDirection.length === 1) {
newHeight *= widthRatio; eleNewHeight *= widthRatio;
newWidth *= heightRatio; eleNewWidth *= heightRatio;
} }
if (transformHandleDirection.length === 2) { if (transformHandleDirection.length === 2) {
const ratio = Math.max(widthRatio, heightRatio); const ratio = Math.max(widthRatio, heightRatio);
newWidth = stateAtResizeStart.width * ratio * Math.sign(newWidth); eleNewWidth = eleInitialWidth * ratio * Math.sign(eleNewWidth);
newHeight = stateAtResizeStart.height * ratio * Math.sign(newHeight); eleNewHeight = eleInitialHeight * ratio * Math.sign(eleNewHeight);
} }
} }
const [
newBoundsX1,
newBoundsY1,
newBoundsX2,
newBoundsY2,
] = getResizedElementAbsoluteCoords(
stateAtResizeStart,
eleNewWidth,
eleNewHeight,
);
const newBoundsWidth = newBoundsX2 - newBoundsX1;
const newBoundsHeight = newBoundsY2 - newBoundsY1;
// Calculate new topLeft based on fixed corner during resize // Calculate new topLeft based on fixed corner during resize
let newTopLeft = startTopLeft as [number, number]; let newTopLeft = [...startTopLeft] as [number, number];
if (["n", "w", "nw"].includes(transformHandleDirection)) { if (["n", "w", "nw"].includes(transformHandleDirection)) {
newTopLeft = [ newTopLeft = [
startBottomRight[0] - Math.abs(newWidth), startBottomRight[0] - Math.abs(newBoundsWidth),
startBottomRight[1] - Math.abs(newHeight), startBottomRight[1] - Math.abs(newBoundsHeight),
]; ];
} }
if (transformHandleDirection === "ne") { if (transformHandleDirection === "ne") {
const bottomLeft = [ const bottomLeft = [startTopLeft[0], startBottomRight[1]];
stateAtResizeStart.x, newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newBoundsHeight)];
stateAtResizeStart.y + stateAtResizeStart.height,
];
newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newHeight)];
} }
if (transformHandleDirection === "sw") { if (transformHandleDirection === "sw") {
const topRight = [ const topRight = [startBottomRight[0], startTopLeft[1]];
stateAtResizeStart.x + stateAtResizeStart.width, newTopLeft = [topRight[0] - Math.abs(newBoundsWidth), topRight[1]];
stateAtResizeStart.y,
];
newTopLeft = [topRight[0] - Math.abs(newWidth), topRight[1]];
} }
// Keeps opposite handle fixed during resize // Keeps opposite handle fixed during resize
if (shouldKeepSidesRatio) { if (shouldKeepSidesRatio) {
if (["s", "n"].includes(transformHandleDirection)) { if (["s", "n"].includes(transformHandleDirection)) {
newTopLeft[0] = startCenter[0] - newWidth / 2; newTopLeft[0] = startCenter[0] - newBoundsWidth / 2;
} }
if (["e", "w"].includes(transformHandleDirection)) { if (["e", "w"].includes(transformHandleDirection)) {
newTopLeft[1] = startCenter[1] - newHeight / 2; newTopLeft[1] = startCenter[1] - newBoundsHeight / 2;
} }
} }
// Flip horizontally // Flip horizontally
if (newWidth < 0) { if (eleNewWidth < 0) {
if (transformHandleDirection.includes("e")) { if (transformHandleDirection.includes("e")) {
newTopLeft[0] -= Math.abs(newWidth); newTopLeft[0] -= Math.abs(newBoundsWidth);
} }
if (transformHandleDirection.includes("w")) { if (transformHandleDirection.includes("w")) {
newTopLeft[0] += Math.abs(newWidth); newTopLeft[0] += Math.abs(newBoundsWidth);
} }
} }
// Flip vertically // Flip vertically
if (newHeight < 0) { if (eleNewHeight < 0) {
if (transformHandleDirection.includes("s")) { if (transformHandleDirection.includes("s")) {
newTopLeft[1] -= Math.abs(newHeight); newTopLeft[1] -= Math.abs(newBoundsHeight);
} }
if (transformHandleDirection.includes("n")) { if (transformHandleDirection.includes("n")) {
newTopLeft[1] += Math.abs(newHeight); newTopLeft[1] += Math.abs(newBoundsHeight);
} }
} }
if (isResizeFromCenter) { if (isResizeFromCenter) {
newTopLeft[0] = startCenter[0] - Math.abs(newWidth) / 2; newTopLeft[0] = startCenter[0] - Math.abs(newBoundsWidth) / 2;
newTopLeft[1] = startCenter[1] - Math.abs(newHeight) / 2; newTopLeft[1] = startCenter[1] - Math.abs(newBoundsHeight) / 2;
} }
// adjust topLeft to new rotation point // adjust topLeft to new rotation point
const angle = stateAtResizeStart.angle; const angle = stateAtResizeStart.angle;
const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle); const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
const newCenter: Point = [ const newCenter: Point = [
newTopLeft[0] + Math.abs(newWidth) / 2, newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
newTopLeft[1] + Math.abs(newHeight) / 2, newTopLeft[1] + Math.abs(newBoundsHeight) / 2,
]; ];
const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle); const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle); newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
const resizedElement = { // Readjust points for linear elements
width: Math.abs(newWidth), const rescaledPoints = rescalePointsInElement(
height: Math.abs(newHeight), stateAtResizeStart,
x: newTopLeft[0], eleNewWidth,
y: newTopLeft[1], eleNewHeight,
};
updateBoundElements(element, {
newSize: { width: resizedElement.width, height: resizedElement.height },
});
mutateElement(element, resizedElement);
};
const resizeSingleNonGenericElement = (
element: NonDeleted<Exclude<ExcalidrawElement, ExcalidrawGenericElement>>,
transformHandleType: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
isResizeFromCenter: boolean,
keepSquareAspectRatio: boolean,
pointerX: number,
pointerY: number,
) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
// rotation pointer with reverse angle
const [rotatedX, rotatedY] = rotate(
pointerX,
pointerY,
cx,
cy,
-element.angle,
); );
// For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner
// So we need to readjust (x,y) to be where the first point should be
const newOrigin = [...newTopLeft];
newOrigin[0] += stateAtResizeStart.x - newBoundsX1;
newOrigin[1] += stateAtResizeStart.y - newBoundsY1;
let scaleX = 1; const resizedElement = {
let scaleY = 1; width: Math.abs(eleNewWidth),
if ( height: Math.abs(eleNewHeight),
transformHandleType === "e" || x: newOrigin[0],
transformHandleType === "ne" || y: newOrigin[1],
transformHandleType === "se" ...rescaledPoints,
) { };
scaleX = (rotatedX - x1) / (x2 - x1);
}
if (
transformHandleType === "s" ||
transformHandleType === "sw" ||
transformHandleType === "se"
) {
scaleY = (rotatedY - y1) / (y2 - y1);
}
if (
transformHandleType === "w" ||
transformHandleType === "nw" ||
transformHandleType === "sw"
) {
scaleX = (x2 - rotatedX) / (x2 - x1);
}
if (
transformHandleType === "n" ||
transformHandleType === "nw" ||
transformHandleType === "ne"
) {
scaleY = (y2 - rotatedY) / (y2 - y1);
}
let nextWidth = element.width * scaleX;
let nextHeight = element.height * scaleY;
if (keepSquareAspectRatio) {
nextWidth = nextHeight = Math.max(nextWidth, nextHeight);
}
const [nextX1, nextY1, nextX2, nextY2] = getResizedElementAbsoluteCoords(
element,
nextWidth,
nextHeight,
);
const deltaX1 = (x1 - nextX1) / 2;
const deltaY1 = (y1 - nextY1) / 2;
const deltaX2 = (x2 - nextX2) / 2;
const deltaY2 = (y2 - nextY2) / 2;
const rescaledPoints = rescalePointsInElement(element, nextWidth, nextHeight);
updateBoundElements(element, {
newSize: { width: nextWidth, height: nextHeight },
});
const [finalX1, finalY1, finalX2, finalY2] = getResizedElementAbsoluteCoords(
{
...element,
...rescaledPoints,
},
Math.abs(nextWidth),
Math.abs(nextHeight),
);
const [flipDiffX, flipDiffY] = getFlipAdjustment(
transformHandleType,
nextWidth,
nextHeight,
nextX1,
nextY1,
nextX2,
nextY2,
finalX1,
finalY1,
finalX2,
finalY2,
isLinearElement(element),
element.angle,
);
const [nextElementX, nextElementY] = adjustXYWithRotation(
getSidesForTransformHandle(transformHandleType, isResizeFromCenter),
element.x - flipDiffX,
element.y - flipDiffY,
element.angle,
deltaX1,
deltaY1,
deltaX2,
deltaY2,
);
if ( if (
nextWidth !== 0 && resizedElement.width !== 0 &&
nextHeight !== 0 && resizedElement.height !== 0 &&
Number.isFinite(nextElementX) && Number.isFinite(resizedElement.x) &&
Number.isFinite(nextElementY) Number.isFinite(resizedElement.y)
) { ) {
mutateElement(element, { updateBoundElements(element, {
width: nextWidth, newSize: { width: resizedElement.width, height: resizedElement.height },
height: nextHeight,
x: nextElementX,
y: nextElementY,
...rescaledPoints,
}); });
mutateElement(element, resizedElement);
} }
}; };

@ -173,57 +173,3 @@ export const getCursorForResizingElement = (resizingElement: {
return cursor ? `${cursor}-resize` : ""; return cursor ? `${cursor}-resize` : "";
}; };
export const normalizeTransformHandleType = (
element: ExcalidrawElement,
transformHandleType: TransformHandleType,
): TransformHandleType => {
if (element.width >= 0 && element.height >= 0) {
return transformHandleType;
}
if (element.width < 0 && element.height < 0) {
switch (transformHandleType) {
case "nw":
return "se";
case "ne":
return "sw";
case "se":
return "nw";
case "sw":
return "ne";
}
} else if (element.width < 0) {
switch (transformHandleType) {
case "nw":
return "ne";
case "ne":
return "nw";
case "se":
return "sw";
case "sw":
return "se";
case "e":
return "w";
case "w":
return "e";
}
} else {
switch (transformHandleType) {
case "nw":
return "sw";
case "ne":
return "se";
case "se":
return "ne";
case "sw":
return "nw";
case "n":
return "s";
case "s":
return "n";
}
}
return transformHandleType;
};

@ -11,7 +11,7 @@ import { getDefaultAppState } from "../appState";
import { ExcalidrawImperativeAPI } from "../components/App"; import { ExcalidrawImperativeAPI } from "../components/App";
import { ErrorDialog } from "../components/ErrorDialog"; import { ErrorDialog } from "../components/ErrorDialog";
import { TopErrorBoundary } from "../components/TopErrorBoundary"; import { TopErrorBoundary } from "../components/TopErrorBoundary";
import { APP_NAME, EVENT, TITLE_TIMEOUT } from "../constants"; import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
import { ImportedDataState } from "../data/types"; import { ImportedDataState } from "../data/types";
import { import {
ExcalidrawElement, ExcalidrawElement,
@ -229,18 +229,10 @@ const ExcalidrawWrapper = (props: { collab: CollabAPI }) => {
const { collab } = props; const { collab } = props;
useEffect(() => { useEffect(() => {
// delayed by 15 sec so that the app has a time to load the latest SW // Delayed so that the app has a time to load the latest SW
setTimeout(() => { setTimeout(() => {
const version = getVersion(); trackEvent("load", "version", getVersion());
const loggedVersion = window.localStorage.getItem( }, VERSION_TIMEOUT);
"excalidraw-lastLoggedVersion",
);
// prevent logging on multiple visits
if (version && version !== loggedVersion) {
window.localStorage.setItem("excalidraw-lastLoggedVersion", version);
trackEvent("load", "version", version);
}
}, 15000);
excalidrawRef.current!.readyPromise.then((excalidrawApi) => { excalidrawRef.current!.readyPromise.then((excalidrawApi) => {
initializeScene({ initializeScene({

@ -199,24 +199,26 @@
"errorDialog": { "errorDialog": {
"title": "Error" "title": "Error"
}, },
"shortcutsDialog": { "helpDialog": {
"title": "Keyboard shortcuts", "blog": "Read our blog",
"shapes": "Shapes",
"or": "or",
"click": "click", "click": "click",
"drag": "drag",
"curvedArrow": "Curved arrow", "curvedArrow": "Curved arrow",
"curvedLine": "Curved line", "curvedLine": "Curved line",
"documentation": "Documentation",
"drag": "drag",
"editor": "Editor", "editor": "Editor",
"view": "View",
"blog": "Read our blog",
"howto": "Follow our guides",
"github": "Found an issue? Submit", "github": "Found an issue? Submit",
"textNewLine": "Add new line (text)", "howto": "Follow our guides",
"or": "or",
"preventBinding": "Prevent arrow binding",
"shapes": "Shapes",
"shortcuts": "Keyboard shortcuts",
"textFinish": "Finish editing (text)", "textFinish": "Finish editing (text)",
"textNewLine": "Add new line (text)",
"title": "Help",
"view": "View",
"zoomToFit": "Zoom to fit all elements", "zoomToFit": "Zoom to fit all elements",
"zoomToSelection": "Zoom to selection", "zoomToSelection": "Zoom to selection"
"preventBinding": "Prevent arrow binding"
}, },
"encrypted": { "encrypted": {
"tooltip": "Your drawings are end-to-end encrypted so Excalidraw's servers will never see them." "tooltip": "Your drawings are end-to-end encrypted so Excalidraw's servers will never see them."

@ -70,64 +70,6 @@ export const adjustXYWithRotation = (
return [x, y]; return [x, y];
}; };
export const getFlipAdjustment = (
side: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
nextWidth: number,
nextHeight: number,
nextX1: number,
nextY1: number,
nextX2: number,
nextY2: number,
finalX1: number,
finalY1: number,
finalX2: number,
finalY2: number,
needsRotation: boolean,
angle: number,
): [number, number] => {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
let flipDiffX = 0;
let flipDiffY = 0;
if (nextWidth < 0) {
if (side === "e" || side === "ne" || side === "se") {
if (needsRotation) {
flipDiffX += (finalX2 - nextX1) * cos;
flipDiffY += (finalX2 - nextX1) * sin;
} else {
flipDiffX += finalX2 - nextX1;
}
}
if (side === "w" || side === "nw" || side === "sw") {
if (needsRotation) {
flipDiffX += (finalX1 - nextX2) * cos;
flipDiffY += (finalX1 - nextX2) * sin;
} else {
flipDiffX += finalX1 - nextX2;
}
}
}
if (nextHeight < 0) {
if (side === "s" || side === "se" || side === "sw") {
if (needsRotation) {
flipDiffY += (finalY2 - nextY1) * cos;
flipDiffX += (finalY2 - nextY1) * -sin;
} else {
flipDiffY += finalY2 - nextY1;
}
}
if (side === "n" || side === "ne" || side === "nw") {
if (needsRotation) {
flipDiffY += (finalY1 - nextY2) * cos;
flipDiffX += (finalY1 - nextY2) * -sin;
} else {
flipDiffY += finalY1 - nextY2;
}
}
}
return [flipDiffX, flipDiffY];
};
export const getPointOnAPath = (point: Point, path: Point[]) => { export const getPointOnAPath = (point: Point, path: Point[]) => {
const [px, py] = point; const [px, py] = point;
const [start, ...other] = path; const [start, ...other] = path;

@ -12,15 +12,34 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section. Please add the latest change on the top under the correct section.
--> -->
## [Unreleased] ## 0.2.0
## Excalidraw API
### Features ### Features
- Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732) - Exported few [Extra API's](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#extra-apis) which can be used by the host to communicate with Excalidraw.
- Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644): - Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644):
- BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component. - BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component.
- Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language. - Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language.
- Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files) - Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
### Fixes
- Hide collaboration button when the prop `onCollabButtonClick` is not provided [#2598](https://github.com/excalidraw/excalidraw/pull/2598)
## Excalidraw Library
### Features
- Add toast [#2772](https://github.com/excalidraw/excalidraw/pull/2772)
- Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732)
- Require use of a preset dialog size; adjust dialog sizing [#2684](https://github.com/excalidraw/excalidraw/pull/2684)
- Add line chart and paste dialog selection [#2670](https://github.com/excalidraw/excalidraw/pull/2670)
- Tweak editing behavior [#2668](https://github.com/excalidraw/excalidraw/pull/2668)
- Change title to Excalidraw after a timeout
- Checkmark to toggle context-menu-items [#2645](https://github.com/excalidraw/excalidraw/pull/2645)
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522) - Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527) - Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
- Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501) - Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
@ -31,13 +50,16 @@ Please add the latest change on the top under the correct section.
### Fixes ### Fixes
- Fix compile error [#2685](https://github.com/excalidraw/excalidraw/pull/2685)
- Center zoom on iPhone and iPad [#2642](https://github.com/excalidraw/excalidraw/pull/2642)
- Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783) - Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783)
- Don't render due to zoom after unmount [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
- Track the chart type correctly [#2773](https://github.com/excalidraw/excalidraw/pull/2773)
- Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779) - Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
- Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677) - Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677)
- Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622) - Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
- Remove unnecessary console.error as it was polluting Sentry [#2637](https://github.com/excalidraw/excalidraw/pull/2637) - Remove unnecessary console.error as it was polluting Sentry [#2637](https://github.com/excalidraw/excalidraw/pull/2637)
- Fix scroll-to-center on init for non-zero canvas offsets [#2445](https://github.com/excalidraw/excalidraw/pull/2445) - Fix scroll-to-center on init for non-zero canvas offsets [#2445](https://github.com/excalidraw/excalidraw/pull/2445)
- Hide collab button when onCollabButtonClick not supplied [#2598](https://github.com/excalidraw/excalidraw/pull/2598)
- Fix resizing the pasted charts [#2586](https://github.com/excalidraw/excalidraw/pull/2586) - Fix resizing the pasted charts [#2586](https://github.com/excalidraw/excalidraw/pull/2586)
- Fix element visibility and zoom on cursor when canvas offset isn't 0. [#2534](https://github.com/excalidraw/excalidraw/pull/2534) - Fix element visibility and zoom on cursor when canvas offset isn't 0. [#2534](https://github.com/excalidraw/excalidraw/pull/2534)
- Fix Library Menu Layout [#2502](https://github.com/excalidraw/excalidraw/pull/2502) - Fix Library Menu Layout [#2502](https://github.com/excalidraw/excalidraw/pull/2502)
@ -47,6 +69,11 @@ Please add the latest change on the top under the correct section.
### Improvements ### Improvements
- Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734) - Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734)
- Do not reset to selection for draw tool [#2721]((https://github.com/excalidraw/excalidraw/pull/2721)
- Make dialogs look more like dialogs [#2686](https://github.com/excalidraw/excalidraw/pull/2686)
- Browse libraries styles fixed [#2694](https://github.com/excalidraw/excalidraw/pull/2694)
- Change hint for 2-point lines on resize [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
- Align items in context menu [#2640](https://github.com/excalidraw/excalidraw/pull/2640)
- Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721) - Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721)
- Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655) - Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
- Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619) - Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619)
@ -56,9 +83,13 @@ Please add the latest change on the top under the correct section.
- Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508) - Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508)
- Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509) - Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509)
### Chore ### Refactor
- Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500) - refactor: Converting span to kbd tag [#2774](https://github.com/excalidraw/excalidraw/pull/2774)
- Media queries [#2680](https://github.com/excalidraw/excalidraw/pull/2680)
- Remove duplicate entry from en.json[#2654](https://github.com/excalidraw/excalidraw/pull/2654)
- Remove the word toggle from labels [#2648](https://github.com/excalidraw/excalidraw/pull/2648)
-
### Docs ### Docs

@ -142,6 +142,51 @@ export default function App() {
| [`langCode`](#langCode) | string | `en` | Language code string | | [`langCode`](#langCode) | string | `en` | Language code string |
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer | | [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
### `Extra API's`
#### `getSceneVersion`
**How to use**
<pre>
import { getSceneVersion } from "@excalidraw/excalidraw";
getSceneVersion(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>)
</pre>
This function returns the current scene version.
#### `getSyncableElements`
**_Signature_**
<pre>
getSyncableElements(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>):<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>
</pre>
**How to use**
```js
import { getSyncableElements } from "@excalidraw/excalidraw";
```
This function returns all the deleted elements of the scene.
### `getElementMap`
**_Signature_**
<pre>
getElementsMap(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>): {[id: string]: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a>}
</pre>
**How to use**
```js
import { getElementsMap } from "@excalidraw/excalidraw";
```
This function returns an object where each element is mapped to its id.
#### `width` #### `width`
This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed. This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed.

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.1.1", "version": "0.2.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -1933,6 +1933,12 @@
"prr": "~1.0.1" "prr": "~1.0.1"
} }
}, },
"es-module-lexer": {
"version": "0.3.26",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz",
"integrity": "sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==",
"dev": true
},
"escalade": { "escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -2456,9 +2462,9 @@
"dev": true "dev": true
}, },
"mini-css-extract-plugin": { "mini-css-extract-plugin": {
"version": "1.3.3", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.4.tgz",
"integrity": "sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw==", "integrity": "sha512-dNjqyeogUd8ucUgw5sxm1ahvSfSUgef7smbmATRSbDm4EmNx5kQA6VdUEhEeCKSjX6CTYjb5vxgMUvRjqP3uHg==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "^2.0.0", "loader-utils": "^2.0.0",
@ -2841,9 +2847,9 @@
"dev": true "dev": true
}, },
"sass-loader": { "sass-loader": {
"version": "10.1.0", "version": "10.1.1",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.0.tgz", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.1.tgz",
"integrity": "sha512-ZCKAlczLBbFd3aGAhowpYEy69Te3Z68cg8bnHHl6WnSCvnKpbM6pQrz957HWMa8LKVuhnD9uMplmMAHwGQtHeg==", "integrity": "sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==",
"dev": true, "dev": true,
"requires": { "requires": {
"klona": "^2.0.4", "klona": "^2.0.4",
@ -3251,9 +3257,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "5.12.3", "version": "5.15.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.12.3.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.15.0.tgz",
"integrity": "sha512-7tiQmcTnKhZwbf7X7sEfXe0pgkGjUZjT6JfYkZHvvIb4/ZsXl1rJu5PxsJoN7W3v5sNSP/8TgBoiOdDqVdvK5w==", "integrity": "sha512-y/xG+ONDz78yn3VvP6gAvGr1/gkxOgitvHSXBmquyN8KDtrGEyE3K9WkXOPB7QmfcOBCpO4ELXwNcCYQnEmexA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-scope": "^3.7.0", "@types/eslint-scope": "^3.7.0",
@ -3264,7 +3270,8 @@
"acorn": "^8.0.4", "acorn": "^8.0.4",
"browserslist": "^4.14.5", "browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.3.1", "enhanced-resolve": "^5.7.0",
"es-module-lexer": "^0.3.26",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
@ -3282,9 +3289,9 @@
}, },
"dependencies": { "dependencies": {
"enhanced-resolve": { "enhanced-resolve": {
"version": "5.5.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.5.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz",
"integrity": "sha512-b4a6BasBCoLzri4MdaeOlDMpls2oioI28CF17csMiav9dq46yvQaKPFNUrCHB6VqQokBDG2VIEEL81jMiQ6Wtw==", "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@ -3360,20 +3367,6 @@
"integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
"dev": true "dev": true
}, },
"terser-webpack-plugin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz",
"integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==",
"dev": true,
"requires": {
"jest-worker": "^26.6.2",
"p-limit": "^3.1.0",
"schema-utils": "^3.0.0",
"serialize-javascript": "^5.0.1",
"source-map": "^0.6.1",
"terser": "^5.5.1"
}
},
"webpack-sources": { "webpack-sources": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz",

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.1.1", "version": "0.2.0",
"main": "dist/excalidraw.min.js", "main": "dist/excalidraw.min.js",
"files": [ "files": [
"dist/*" "dist/*"
@ -54,11 +54,11 @@
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "5.0.1", "css-loader": "5.0.1",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"mini-css-extract-plugin": "1.3.3", "mini-css-extract-plugin": "1.3.4",
"sass-loader": "10.1.0", "sass-loader": "10.1.1",
"terser-webpack-plugin": "5.1.1", "terser-webpack-plugin": "5.1.1",
"ts-loader": "8.0.14", "ts-loader": "8.0.14",
"webpack": "5.12.3", "webpack": "5.15.0",
"webpack-bundle-analyzer": "4.3.0", "webpack-bundle-analyzer": "4.3.0",
"webpack-cli": "4.3.1" "webpack-cli": "4.3.1"
}, },

@ -1109,9 +1109,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "14.14.20", "version": "14.14.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz",
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==",
"dev": true "dev": true
}, },
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
@ -1783,6 +1783,12 @@
"prr": "~1.0.1" "prr": "~1.0.1"
} }
}, },
"es-module-lexer": {
"version": "0.3.26",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz",
"integrity": "sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==",
"dev": true
},
"escalade": { "escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -2918,9 +2924,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "5.12.3", "version": "5.15.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.12.3.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.15.0.tgz",
"integrity": "sha512-7tiQmcTnKhZwbf7X7sEfXe0pgkGjUZjT6JfYkZHvvIb4/ZsXl1rJu5PxsJoN7W3v5sNSP/8TgBoiOdDqVdvK5w==", "integrity": "sha512-y/xG+ONDz78yn3VvP6gAvGr1/gkxOgitvHSXBmquyN8KDtrGEyE3K9WkXOPB7QmfcOBCpO4ELXwNcCYQnEmexA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-scope": "^3.7.0", "@types/eslint-scope": "^3.7.0",
@ -2931,7 +2937,8 @@
"acorn": "^8.0.4", "acorn": "^8.0.4",
"browserslist": "^4.14.5", "browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.3.1", "enhanced-resolve": "^5.7.0",
"es-module-lexer": "^0.3.26",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
@ -2949,9 +2956,9 @@
}, },
"dependencies": { "dependencies": {
"enhanced-resolve": { "enhanced-resolve": {
"version": "5.5.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.5.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz",
"integrity": "sha512-b4a6BasBCoLzri4MdaeOlDMpls2oioI28CF17csMiav9dq46yvQaKPFNUrCHB6VqQokBDG2VIEEL81jMiQ6Wtw==", "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",

@ -46,7 +46,7 @@
"cross-env": "7.0.3", "cross-env": "7.0.3",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"ts-loader": "8.0.14", "ts-loader": "8.0.14",
"webpack": "5.12.3", "webpack": "5.15.0",
"webpack-bundle-analyzer": "4.3.0", "webpack-bundle-analyzer": "4.3.0",
"webpack-cli": "4.3.1" "webpack-cli": "4.3.1"
}, },

@ -70,7 +70,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -536,7 +536,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -984,7 +984,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -1760,7 +1760,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -1967,7 +1967,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -2418,7 +2418,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -2666,7 +2666,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -2831,7 +2831,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -3303,7 +3303,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -3612,7 +3612,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -3816,7 +3816,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -4056,7 +4056,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -4307,7 +4307,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -4708,7 +4708,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -4979,7 +4979,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -5304,7 +5304,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -5488,7 +5488,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -5650,7 +5650,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -6108,7 +6108,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -6417,7 +6417,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -8454,7 +8454,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -8815,7 +8815,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -9066,7 +9066,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -9318,7 +9318,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -9626,7 +9626,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -9788,7 +9788,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -9950,7 +9950,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -10112,7 +10112,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -10304,7 +10304,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -10496,7 +10496,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -10688,7 +10688,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -10880,7 +10880,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -11042,7 +11042,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -11204,7 +11204,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -11396,7 +11396,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -11558,7 +11558,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -11761,7 +11761,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -12468,7 +12468,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -12715,7 +12715,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": true, "shouldCacheIgnoreZoom": true,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -12813,7 +12813,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -12913,7 +12913,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -13075,7 +13075,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -13381,7 +13381,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -13687,7 +13687,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -13847,7 +13847,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -14043,7 +14043,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -14296,7 +14296,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -14612,7 +14612,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -15449,7 +15449,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -15755,7 +15755,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -16065,7 +16065,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -16441,7 +16441,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -16612,7 +16612,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -16925,7 +16925,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -17165,7 +17165,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -17420,7 +17420,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -17735,7 +17735,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -17835,7 +17835,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -18008,7 +18008,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -18814,7 +18814,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -18916,7 +18916,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -19692,7 +19692,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -20093,7 +20093,7 @@ Object {
}, },
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -20340,7 +20340,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": true, "shouldCacheIgnoreZoom": true,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -20440,7 +20440,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -20934,7 +20934,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],
@ -21032,7 +21032,7 @@ Object {
"selectionElement": null, "selectionElement": null,
"shouldAddWatermark": false, "shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false, "shouldCacheIgnoreZoom": false,
"showShortcutsDialog": false, "showHelpDialog": false,
"showStats": false, "showStats": false,
"startBoundElement": null, "startBoundElement": null,
"suggestedBindings": Array [], "suggestedBindings": Array [],

@ -41,7 +41,7 @@ describe("resize rectangle ellipses and diamond elements", () => {
${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]} ${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]}
${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]} ${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]}
${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]} ${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]}
${"ne"} | ${[10, 55]} | ${[110, 45]} | ${[elemData.x, 55]} ${"ne"} | ${[5, 55]} | ${[105, 45]} | ${[elemData.x, 55]}
${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]} ${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]}
${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]} ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]}
${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]} ${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]}

@ -81,7 +81,7 @@ export type AppState = {
selectedElementIds: { [id: string]: boolean }; selectedElementIds: { [id: string]: boolean };
previousSelectedElementIds: { [id: string]: boolean }; previousSelectedElementIds: { [id: string]: boolean };
shouldCacheIgnoreZoom: boolean; shouldCacheIgnoreZoom: boolean;
showShortcutsDialog: boolean; showHelpDialog: boolean;
toastMessage: string | null; toastMessage: string | null;
zenModeEnabled: boolean; zenModeEnabled: boolean;
appearance: "light" | "dark"; appearance: "light" | "dark";

Loading…
Cancel
Save