Compare commits

...

582 Commits

Author SHA1 Message Date
52a129c4dd rename 2022-01-05 17:41:45 +05:30
f41515b8bc fix: show text properties button states correctly for bounded text 2022-01-05 15:03:58 +05:30
a2e8806f57 fix: rotate bounded text when container is rotated before typing (#4535) 2022-01-04 18:03:24 +05:30
b71e702991 fix: undo should work when selecting bounded textr (#4537) 2022-01-04 18:02:16 +05:30
5c67329be6 fix: Reduce padding to 5px for bounded text (#4530)
* fix: Reduce padding to 5px

* reduce width by 50 to fix tests

* Push the word if appending space exceeds max width when breaking words

* fix spec
2022-01-03 17:59:26 +05:30
28546fbb55 fix: bound text doesn't inherit container (#4521) 2021-12-31 14:55:02 +01:00
b0cccbb9e8 chore(deps): bump @tldraw/vec from 1.2.9 to 1.4.0 (#4517)
Bumps [@tldraw/vec](https://github.com/tldraw/tldraw) from 1.2.9 to 1.4.0.
- [Release notes](https://github.com/tldraw/tldraw/releases)
- [Commits](https://github.com/tldraw/tldraw/compare/v1.2.9...v1.4.0)

---
updated-dependencies:
- dependency-name: "@tldraw/vec"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 14:44:44 +02:00
b621d065de feat: hints and shortcuts help around deep selection (#4502) 2021-12-31 11:00:20 +01:00
96580c92a5 chore(deps-dev): bump @babel/plugin-transform-arrow-functions (#4515)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.16.5 to 7.16.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.7/packages/babel-plugin-transform-arrow-functions)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-arrow-functions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 12:42:29 +05:30
975441549b chore(deps): bump sass from 1.43.5 to 1.45.2 (#4518)
Bumps [sass](https://github.com/sass/dart-sass) from 1.43.5 to 1.45.2.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.43.5...1.45.2)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 12:42:04 +05:30
4be701416a chore(deps-dev): bump @babel/preset-react in /src/packages/excalidraw (#4519)
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.16.0 to 7.16.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.7/packages/babel-preset-react)

---
updated-dependencies:
- dependency-name: "@babel/preset-react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 12:41:31 +05:30
1acb1e33f1 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#4516)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.16.4 to 7.16.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.7/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 01:11:22 +00:00
986e1e40d3 chore(deps-dev): bump @babel/preset-typescript (#4514)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.16.0 to 7.16.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.7/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 01:00:57 +00:00
fab4a0e060 chore(deps-dev): bump @babel/plugin-transform-runtime (#4508)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.16.4 to 7.16.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.7/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:59:42 +02:00
b265ebf88f chore(deps): bump @tldraw/vec from 1.1.5 to 1.2.9 (#4479)
Bumps [@tldraw/vec](https://github.com/tldraw/tldraw) from 1.1.5 to 1.2.9.
- [Release notes](https://github.com/tldraw/tldraw/releases)
- [Commits](https://github.com/tldraw/tldraw/compare/v1.1.5...v1.2.9)

---
updated-dependencies:
- dependency-name: "@tldraw/vec"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:52:36 +02:00
351845019e chore(deps-dev): bump @types/pako from 1.0.2 to 1.0.3 (#4481)
Bumps [@types/pako](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pako) from 1.0.2 to 1.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pako)

---
updated-dependencies:
- dependency-name: "@types/pako"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:51:20 +02:00
c0fcce6f27 chore(deps): bump @testing-library/jest-dom from 5.15.1 to 5.16.1 (#4395)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.15.1 to 5.16.1.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.15.1...v5.16.1)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:50:30 +02:00
b093d2d2b6 chore(deps-dev): bump prettier from 2.5.0 to 2.5.1 (#4360)
Bumps [prettier](https://github.com/prettier/prettier) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.5.0...2.5.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:49:43 +02:00
69548c5502 chore(deps): bump @types/react from 17.0.37 to 17.0.38 (#4483)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.37 to 17.0.38.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:49:15 +02:00
6ca0afa6e5 chore(deps-dev): bump @types/chai from 4.2.22 to 4.3.0 (#4394)
Bumps [@types/chai](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chai) from 4.2.22 to 4.3.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chai)

---
updated-dependencies:
- dependency-name: "@types/chai"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:48:59 +02:00
c50f81b829 chore(deps-dev): bump @babel/plugin-transform-arrow-functions (#4426)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.16.0 to 7.16.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.5/packages/babel-plugin-transform-arrow-functions)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-arrow-functions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:47:42 +02:00
b122c8c4eb chore(deps-dev): bump @babel/plugin-transform-async-to-generator (#4437)
Bumps [@babel/plugin-transform-async-to-generator](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-async-to-generator) from 7.16.0 to 7.16.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.5/packages/babel-plugin-transform-async-to-generator)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-async-to-generator"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:47:16 +02:00
9a7216fe94 chore(deps-dev): bump terser-webpack-plugin in /src/packages/excalidraw (#4423)
Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.2.5 to 5.3.0.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.2.5...v5.3.0)

---
updated-dependencies:
- dependency-name: terser-webpack-plugin
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:46:52 +02:00
8eee749076 chore(deps-dev): bump @babel/preset-typescript in /src/packages/utils (#4430)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.16.0 to 7.16.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.5/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:46:36 +02:00
2158ad0656 chore(deps-dev): bump @babel/core in /src/packages/utils (#4435)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.16.0 to 7.16.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.5/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:45:49 +02:00
74c3fea7f5 chore(deps): bump typescript from 4.5.2 to 4.5.4 (#4442)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.5.2 to 4.5.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.5.2...v4.5.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:45:31 +02:00
5e456e6d05 chore(deps-dev): bump lint-staged from 12.1.2 to 12.1.4 (#4478)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 12.1.2 to 12.1.4.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v12.1.2...v12.1.4)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-31 02:45:09 +02:00
477cce2ed6 fix: Text wrapping with grid (#4505) (#4506) 2021-12-30 19:10:31 +01:00
dd8e465304 feat: Support updating text properties by clicking on container (#4499) 2021-12-29 16:49:52 +05:30
11396a21de fix: check if process is defined before using so it works in browser (#4497)
* refactor: use isTestEnv() utils where applicable

* check if process is defined
2021-12-28 17:17:41 +05:30
38236bc5e0 tests: Add tests for wrapText util (#4495) 2021-12-28 16:52:57 +05:30
63ce5b82d7 fix: pending review fixes for sticky notes (#4493) 2021-12-28 16:24:44 +05:30
bae0e985b2 fix: prevent browser from scrolling when panning (#4489) 2021-12-27 14:18:11 +01:00
04f852a40a build: Added example folder for testing @excalidraw/excalidraw in local (#4488)
* build: Added example folder for testing @excalidraw/excalidraw in local

* remove unnecessary files

* use scss

* update docs

* newline

* remove index

* remove yarn

* use the bundled excalidraw.development.js for better testing and font will also be available

* remove src folder from example
2021-12-27 18:01:33 +05:30
f463c047c0 fix: pasted elements except binded text once paste action is complete (#4472) 2021-12-23 22:07:16 +05:30
1fd347cade fix: don't select binded text when ungrouping (#4470) 2021-12-23 21:36:29 +05:30
ef62390841 fix: set height correctly when text properties updated while editing in container until first submit (#4469)
* fix: set height correctly when text properties updated while editing in container

* rename PADDING to BOUND_TEXT_PADDING
2021-12-23 17:02:35 +05:30
bf2bca221e fix: align and distribute binded text in container and cleanup (#4468) 2021-12-23 17:02:13 +05:30
d0733b1960 fix: move binded text when moving container using keyboard (#4466) 2021-12-23 01:47:14 +05:30
64c2d76cfa fix: support dragging binded text in container selected in a group (#4462)
* fix: support moving binded text when container selected via group

* update coords of bounded text only when element doesn't belong to any group or element in group is selected

* dnt drag binded text when nested group selected

* Update src/element/dragElements.ts

Co-authored-by: David Luzar <luzar.david@gmail.com>

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-12-22 19:16:49 +05:30
c76784b774 fix: Scope drag and drop events to Excalidraw container to prevent overriding the host drag and drop events (#4445)
* cross-env

* reverting lib

https://github.com/excalidraw/excalidraw/issues/4282

* Revert "reverting lib"

This reverts commit 840726806a.

* Update package.json

* Update App.tsx

* Update App.tsx

* lint

* updated changelog

* Update src/packages/excalidraw/CHANGELOG.md

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

* Update src/packages/excalidraw/CHANGELOG.md

* Move fixes above build header

* Update src/packages/excalidraw/CHANGELOG.md

* lint

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-12-22 18:55:34 +05:30
25e54e5999 fix: vertically align single line when deleting text in bounded container (#4460) 2021-12-22 15:32:21 +05:30
55b7a7d554 fix: update height correctly when updating text properties in binded text (#4459)
* fix: update height correctly when updating text properties in binded text

* read height from editor style so its accurate

* fix
2021-12-22 12:08:51 +05:30
c1c37a6ee7 fix: align library item previews to center (#4447) 2021-12-21 19:59:36 +01:00
25b529f519 fix: vertically center align text when text deleted (#4457) 2021-12-22 00:07:55 +05:30
8e6a747873 fix: vertically center the first line as user starts typing in container (#4454)
* fix: vertically center the first line as user starts typing in container

* fix
2021-12-21 23:08:36 +05:30
089b05db1b fix: switch cursor to center of container when adding text when dimensions are too small (#4452) 2021-12-21 19:00:01 +05:30
081e097cef fix: vertically center align the bounded text correctly when zoomed (#4444)
* fix: vertically center align the bounded text correctly when zoomed

* dnt add offsets since its calculated correctly

* set editor max width better when offsets present

* Update src/element/textWysiwyg.tsx

* const

* revert
2021-12-21 17:13:11 +05:30
8b5657e1ce fix: support updating stroke color for text by typing in color picker input (#4415)
* fix: support updating stroke color for text by typing in color picker input

* restore focus when clicked on same property unless its color picker input and submit text on color picker blur

* focus editor on color picker blur

* don't focus text editor when color picker is active
2021-12-17 20:15:22 +05:30
8b2b03347c fix: bound text not atomic with container when changing z-index (#4414)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-12-17 13:10:37 +00:00
c2a8712593 fix: update viewport coords correctly when editing text (#4416) 2021-12-17 11:38:18 +01:00
ff1d7728a0 fix: use word-break break-word only and update text editor height only when binded to container (#4410) 2021-12-17 15:24:23 +05:30
98b5c37e45 feat: bind text to shapes when pressing enter and support sticky notes 🎉 (#4343)
* feat: Word wrap inside rect and increase height when size exceeded

* fixes for auto increase in height

* fix height

* respect newlines when wrapping text

* shift text area when height increases beyond mid rect height until it reaches to the top

* select bound text if present when rect selected

* mutate y coord after text submit

* Add padding of 30px and update dimensions acordingly

* Don't allow selecting bound text element directly

* support deletion of bound text element when rect deleted

* trim text

* Support autoshrink and improve algo

* calculate approx line height instead of hardcoding

* use textContainerId instead of storing textContainer element itself

* rename boundTextElement -> boundTextElementId

* fix text properties not getting reflected after edit inside rect

* Support resizing

* remove ts ignore

* increase height of container when text height increases while resizing

* use original text when editing/resizing so it adjusts based on original text

* fix tests

* add util isRectangleElement

* use isTextElement util everywhere

* disable selecting text inside rect when selectAll

* Bind text to circle and diamond as well

* fix tests

* vertically center align the text always

* better vertical align

* Disable binding arrows for text inside shapes

* set min width for text container when text is binded to container

* update dimensions of container if its less than min width/ min height

* Allow selecting of text container for transparent containers when clicked inside

* fix test

* preserve whitespaces between long word exceeding width and next word
Use word break instead of whitespace no wrap for better readability and support safari

* Perf improvements for measuring text width and resizing
* Use canvas measureText instead of our algo. This has reduced the perf ~ 10 times
* Rewrite wrapText algo to break in words appropriately and for longer words
calculate the char width in order unless max width reached. This makes the
the number of runs linear (max text length times) which was earlier
textLength * textLength-1/2 as I was slicing the chars from end until max width reached for each run
* Add a util to calculate getApproxCharsToFitInWidth to calculate min chars to fit in a line

* use console.info so eslint doesnt warn :p

* cache char width and don't call resize unless min width exceeded

* update line height and height correctly when text properties inside container updated

* improve vertical centering when text properties updated, not yet perfect though

* when double clicked inside a conatiner  take the cursor to end of text same as what happens when enter is pressed

* Add hint when container selected

* Select container when escape key is pressed after submitting text

* fix copy/paste when using copy/paste action

* fix copy when dragged with alt pressed

* fix export to svg/png

* fix add to library

* Fix copy as png/svg

* Don't allow selecting text when using selection tool and support resizing when multiple elements include ones with binded text selectec

* fix rotation jump

* moove all text utils to textElement.ts

* resize text element only after container resized so that width doesnt change when editing

* insert the remaining chars for long words once it goes beyond line

* fix typo, use string for character type

* renaming

* fix bugs in word wrap algo

* make grouping work

* set boundTextElementId only when text present else unset it

* rename textContainerId to containerId

* fix

* fix snap

* use originalText in redrawTextBoundingBox so height is calculated properly and center align works after props updated

* use boundElementIds and also support binding text in images 🎉

* fix the sw/se ends when resizing from ne/nw

* fix y coord when resizing from north

* bind when enter is pressed, double click/text tool willl edit the binded text if present else create a new text

* bind when clicked on center of container

* use pre-wrap instead of normal so it works in ff

* use container boundTextElement when container present and trying to edit text

* review fixes

* make getBoundTextElementId type safe and check for existence when using this function

* fix

* don't duplicate boundElementIds when text submitted

* only remove last trailing space if present which we have added when joining words

* set width correctly when resizing to fix alignment issues

* make duplication work using cmd/ctrl+d

* set X coord correctly during resize

* don't allow resize to negative dimensions when text is bounded to container

* fix, check last char is space

* remove logs

* make sure text editor doesn't go beyond viewport and set container dimensions in case it overflows

* add a util isTextBindableContainer to check if the container could bind text
2021-12-16 21:14:03 +05:30
7db63bd397 feat: redesign toolbar & tweaks (#4387) 2021-12-15 15:31:44 +01:00
390da3fd0f feat: change boundElementIdsboundElements (#4404) 2021-12-14 16:07:01 +01:00
104664cb9e feat: support selecting multiple points when editing line (#4373) 2021-12-13 13:35:07 +01:00
c822055ec8 chore(deps-dev): bump sass-loader in /src/packages/utils (#4390)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.3.0 to 12.4.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v12.3.0...v12.4.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 11:42:29 +05:30
e15d73d94c chore(deps-dev): bump typescript in /src/packages/excalidraw (#4392)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.5.2 to 4.5.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.5.2...v4.5.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 11:42:18 +05:30
80ee097b85 chore(deps-dev): bump webpack in /src/packages/utils (#4391)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.4 to 5.65.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.4...v5.65.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 11:42:03 +05:30
10048b877b chore(deps-dev): bump webpack in /src/packages/excalidraw (#4389)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.4 to 5.65.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.4...v5.65.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 10:41:00 +05:30
5dd5862bb9 chore(deps-dev): bump sass-loader in /src/packages/excalidraw (#4393)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.3.0 to 12.4.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v12.3.0...v12.4.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-13 10:40:41 +05:30
79989fedda chore: bump roughjs version (#4386)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-12-11 22:46:44 +01:00
cecabc2196 fix: husky not able to execute pre-commit on windows (#4370) 2021-12-09 15:15:54 +01:00
ed8fb40b63 fix: make firebase config parsing not fail on undefined env (#4381) 2021-12-09 12:24:41 +00:00
6e391728fe build: remove file loader and migrate to asset modules webpack for font assets (#4380)
* build: use type:javascript/auto so font file assets aren't duplicated

* update changelog

* remove file loader and use asset modules

* fix
2021-12-08 15:56:25 +05:30
dfbfbc3f11 feat: set package build target to es2017 (#4341) 2021-12-07 16:38:46 +01:00
9b8ee3cacf feat: horizontally center toolbar menu 2021-12-05 17:47:19 +01:00
4ea73d5d5b feat: Add support for rounded corners in diamond (#4369) 2021-12-05 16:56:19 +01:00
618f204ddd feat: allow zooming up to 3000% (#4358) 2021-12-04 13:51:28 +00:00
720588130c feat: stop discarding precision when rendering (#4357) 2021-12-04 13:49:57 +00:00
f354788cd0 fix: adding to library via contextmenu when no image is selected (#4356) 2021-12-04 11:59:37 +01:00
1c7ee09010 feat: support Image binding (#4347) 2021-12-03 11:42:28 +01:00
ca15b0a008 chore(deps-dev): bump postcss-loader in /src/packages/excalidraw (#4329)
Bumps [postcss-loader](https://github.com/webpack-contrib/postcss-loader) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/webpack-contrib/postcss-loader/releases)
- [Changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/postcss-loader/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: postcss-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:10:06 +02:00
650930c5ce chore(deps-dev): bump webpack in /src/packages/utils (#4328)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.3 to 5.64.4.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.3...v5.64.4)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:09:53 +02:00
79c0d59244 chore(deps-dev): bump webpack in /src/packages/excalidraw (#4327)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.3 to 5.64.4.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.3...v5.64.4)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:09:44 +02:00
cd50b5f7e9 chore(deps): bump @types/react from 17.0.35 to 17.0.37 (#4332)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.35 to 17.0.37.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:09:20 +02:00
c0434957ff chore(deps): bump @testing-library/jest-dom from 5.15.0 to 5.15.1 (#4333)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.15.0 to 5.15.1.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.15.0...v5.15.1)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:08:59 +02:00
66aeaeb38d chore(deps): bump @tldraw/vec from 0.1.3 to 1.1.5 (#4334)
Bumps [@tldraw/vec](https://github.com/tldraw/tldraw) from 0.1.3 to 1.1.5.
- [Release notes](https://github.com/tldraw/tldraw/releases)
- [Commits](https://github.com/tldraw/tldraw/compare/v0.1.3...v1.1.5)

---
updated-dependencies:
- dependency-name: "@tldraw/vec"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 16:08:30 +02:00
7f545e74ab chore(deps): bump sass from 1.43.4 to 1.43.5 (#4330)
Bumps [sass](https://github.com/sass/dart-sass) from 1.43.4 to 1.43.5.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.43.4...1.43.5)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 15:12:01 +02:00
a776955579 chore(deps-dev): bump prettier from 2.4.1 to 2.5.0 (#4331)
Bumps [prettier](https://github.com/prettier/prettier) from 2.4.1 to 2.5.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.4.1...2.5.0)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 15:11:17 +02:00
afa7932c9b feat: set appState.exportBackground to true when exporting to jpg (#4342) 2021-11-30 22:08:55 +01:00
1ee8d7d082 chore(deps): Bump browser-fs-access to latest version (#4338)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-11-29 22:23:12 +01:00
06db702b5d feat: support selecting multiple library items via shift (#4306) 2021-11-26 12:46:23 +01:00
b53d1f6f3e feat: improve library preview image generation on publish (#4321)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-11-26 11:46:13 +01:00
ca1f3aa094 chore: Update translations from Crowdin (#4258) 2021-11-26 11:27:28 +01:00
8ff159e76e fix: export scale quality regression (#4316) 2021-11-25 14:05:22 +01:00
f9d2d537a2 feat: add element.updated (#4070) 2021-11-24 18:38:33 +01:00
dac970c640 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#4291)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.16.0 to 7.16.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.4/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 16:55:14 +00:00
78bb3b3d84 chore(deps-dev): bump @babel/plugin-transform-runtime (#4292)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.16.0 to 7.16.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.4/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 18:54:33 +02:00
7d9d7ad297 chore(deps-dev): bump @babel/plugin-transform-runtime (#4288)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.16.0 to 7.16.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.4/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 18:54:13 +02:00
de20a5e3ba chore(deps-dev): bump webpack in /src/packages/excalidraw (#4314)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.0 to 5.64.3.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.0...v5.64.3)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 18:52:51 +02:00
289f72e45d chore(deps-dev): bump webpack in /src/packages/utils (#4315)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.0 to 5.64.3.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.0...v5.64.3)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 18:52:36 +02:00
6dd0e6a4c5 fix: remove 100% height from tooltip container to fix layout issues (#3980) 2021-11-24 17:16:18 +01:00
96b31ecbce fix: inline ENV variables when building excalidraw package (#4311) 2021-11-24 16:25:19 +01:00
a132f154cb chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#4286)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.16.0 to 7.16.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.4/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 17:22:57 +02:00
23acd8f6d1 chore(deps-dev): bump mini-css-extract-plugin (#4289)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.4.4 to 2.4.5.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.4.4...v2.4.5)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 17:22:34 +02:00
a60709f5ea chore(deps-dev): bump lint-staged from 12.0.1 to 12.1.2 (#4312)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 12.0.1 to 12.1.2.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v12.0.1...v12.1.2)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 17:21:06 +02:00
896c476716 feat: compress shareLink data when uploading to json server (#4225) 2021-11-24 14:45:13 +01:00
133ba19919 chore(deps): bump @types/jest from 27.0.2 to 27.0.3 (#4295)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 27.0.2 to 27.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 15:40:49 +02:00
a2136bfe9d chore(deps): bump @types/react from 17.0.34 to 17.0.35 (#4297)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.34 to 17.0.35.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 15:39:32 +02:00
6fbd64fdaa chore(deps-dev): bump firebase-tools from 9.22.0 to 9.23.0 (#4293)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.22.0 to 9.23.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.22.0...v9.23.0)

---
updated-dependencies:
- dependency-name: firebase-tools
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-24 15:39:16 +02:00
cc4b0c2932 feat: supply version param when installing libraries (#4305) 2021-11-23 17:59:26 +01:00
b6ef953dc9 fix: SVG export in dark mode with embedded bitmap image (#4285)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-11-20 15:31:51 +01:00
620b662085 chore: bump typescript@4.5.2 (#4283)
* chore: bump typescript@4.5.2

* bump typescript and fix tsconfig for npm package
2021-11-19 19:51:28 +01:00
1c11df011a fix: new FS API not working on Linux (#4280) 2021-11-19 14:20:42 +01:00
59e9651547 feat: log FS abortError to console (#4279) 2021-11-19 10:54:23 +01:00
1c48d122e0 fix: url -> URL for consistency (#4277) 2021-11-19 11:21:23 +05:30
e4d02fb275 feat: Add validation for website and remove validation for library item name (#4269)
* Github->GitHub

* allow numbers

* remove validation for lib/item name
2021-11-18 10:24:26 +00:00
34a382ace9 fix: prevent adding images to library via contextMenu (#4264) 2021-11-17 20:06:26 +00:00
e60e48e67d fix: account for libraries v2 when prompting (#4263) 2021-11-17 19:54:40 +00:00
84d1d9993c feat: Allow publishing libraries from UI (#4115)
* feat: Allow publishing libraries from UI

* Add status for each library item and show publish only for unpublished libs

* Add publish library dialog

* Pass the data to publish the library

* pass lib blob

* Handle old and new libraries when importing

* Better error handling

* Show publish success when library submitted for review

* don't close library when publish success dialog open

* Support multiple libs deletion and publish

* Set status to published once library submitted for review

* Save  to LS after library published

* unique key for publish and delete

* fix layout shift when hover and also highlight selected library items

* design improvements

* migrate old library to the new one

* fix

* fix tests

* use i18n

* Support submit type in toolbutton

* Use html5 form validation, add asteriks for required fields, add twitter handle, mark github handle optional

* Add twitter handle in form state

* revert html5 validation as fetch is giving some issues :/

* clarify types around LibraryItems

* Add website optional field

* event.preventDefault to make htm5 form validationw work

* improve png generation by drawing a bounding box rect and aligining pngs to support multiple libs png

* remove ts-ignore

* add placeholders for fields

* decrease clickable area for checkbox by 0.5em

* add checkbox background color

* rename `items` to `elements`

* improve checkbox hit area

* show selected library items in publish dialog

* decrease dimensions by 3px to improve jerky experience when opening/closing library menu

* Don't close publish dialog when clicked outside

* Show selected library actions only when any library item selected and use icons instead of button

* rename library to libraryItems in excalidrawLib and added migration

* change icon and swap bg/color

* use blue brand color for hover/selected states

* prompt for confirmation when deleting library items

* separate unpublished items from published

* factor `LibraryMenu` into own file

* i18n and minor fixes for unpublished items

* fix not rendering empty cells when library empty

* don't render published section if empty and unpublished is not

* Add edit name functionality for library items

* fix

* edit lib name with onchange/blur

* bump library version

* prefer response error message

* add library urls to ENV vars

* mark lib item name as required

* Use input only for lib item name

* better error validation for lib items

* fix label styling for lib items

* design and i18n fixes

* Save publish dialog data to local storage and clear once published

* Add a note about MIT License

* Add note for guidelines

* Add tooltip for publish button

* Show spinner in submit button when submission is in progress

* assign id for older lib items when installed and set status as published for all lib when installed

* update export icon and support export library for selected items

* move LibraryMenuItems into its own component as its best to keep one comp per file

* fix spec

* Refactoring the library actions for reusablility

* show only load when items not present

* close on click outside in publish dialog

* ad dialog description and tweak copy

* vertically center input labels

* align input styles

* move author name input to other usernames

* rename param

* inline to simplify

* fix to not inline `undefined` class names

* fix version & include only latest lib schema in library export type

* await response callback

* refactor types

* refactor

* i18n

* align casing & tweaks

* move ls logic to publishLibrary

* support removal of item inside publish dialog

* fix labels for trash icon when items selected

* replace window.confirm for removal libs with confirm dialog

* fix input/textarea styling

* move library item menu scss to its own file

* use blue for load and cyan for publish

* reduce margin for submit and make submit => Submit

* Make library items header sticky

* move publish icon to left so there is no jerkiness when unpublish items selected

* update url

* fix grid gap between lib items

* Mark older items imported from initial data as unpublished

* add text to publish button on non-mobile

* add items counter

* fix test

* show personal and excal libs sections and personal goes first

* show toast on adding to library via contextMenu

* Animate plus icon and not the pending item

* fix snap

* use i18n when no item in publish dialog

* tweak style of new lib item

* show empty cells for both sections and set status as published for installed libs

* fix

* push selected item first in unpublished section

* set status as published for imported from webiste but unpublished for json

* Add items to the begining of library

* add `created` library item attr

* fix test

* use `defaultValue` instead of `value`

* fix dark theme styles

* fix toggle button not closing library

* close library menu on Escape

* tweak publish dialog item remove style

* fix remove icon in publish dialog

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-11-17 23:53:43 +05:30
3ff9744b39 feat: create confirm dialog to use instead of window.confirm (#4256)
* feat: create confirm dialog to use instead of window.confirm

* move confirm to right

* add types

* less margin
2021-11-16 18:55:56 +05:30
b9abcc825a chore(deps-dev): bump webpack in /src/packages/utils (#4245)
Bumps [webpack](https://github.com/webpack/webpack) from 5.62.1 to 5.64.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.62.1...v5.64.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:47:31 +01:00
9679eaf74c chore(deps-dev): bump @types/web from 0.0.46 to 0.0.47 (#4249)
Bumps [@types/web](https://github.com/microsoft/TypeScript-DOM-Lib-Generator) from 0.0.46 to 0.0.47.
- [Release notes](https://github.com/microsoft/TypeScript-DOM-Lib-Generator/releases)
- [Commits](https://github.com/microsoft/TypeScript-DOM-Lib-Generator/compare/@types/web@0.0.46...@types/web@0.0.47)

---
updated-dependencies:
- dependency-name: "@types/web"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:46:42 +01:00
284747d742 chore(deps-dev): bump terser-webpack-plugin in /src/packages/excalidraw (#4243)
Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.2.4 to 5.2.5.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.2.4...v5.2.5)

---
updated-dependencies:
- dependency-name: terser-webpack-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:46:18 +01:00
876f85fd7a chore(deps-dev): bump webpack in /src/packages/excalidraw (#4244)
Bumps [webpack](https://github.com/webpack/webpack) from 5.62.1 to 5.64.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.62.1...v5.64.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:45:55 +01:00
efc2bbed21 chore(deps): bump browser-fs-access from 0.21.0 to 0.21.1 (#4248)
Bumps [browser-fs-access](https://github.com/GoogleChromeLabs/browser-fs-access) from 0.21.0 to 0.21.1.
- [Release notes](https://github.com/GoogleChromeLabs/browser-fs-access/releases)
- [Commits](https://github.com/GoogleChromeLabs/browser-fs-access/compare/v0.21.0...v0.21.1)

---
updated-dependencies:
- dependency-name: browser-fs-access
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:45:29 +01:00
61d193b87b chore(deps-dev): bump lint-staged from 11.2.6 to 12.0.1 (#4250)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.2.6 to 12.0.1.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.2.6...v12.0.1)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:45:06 +01:00
3989d6a989 chore(deps): bump roughjs from 4.4.5 to 4.5.0 (#4246)
Bumps [roughjs](https://github.com/pshihn/rough) from 4.4.5 to 4.5.0.
- [Release notes](https://github.com/pshihn/rough/releases)
- [Changelog](https://github.com/rough-stuff/rough/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pshihn/rough/commits)

---
updated-dependencies:
- dependency-name: roughjs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-14 10:44:44 +01:00
f6559b65ef chore: Update translations from Crowdin (#4201) 2021-11-12 12:13:34 +01:00
bc6b066c07 Remove outdated OT info (#4232) 2021-11-09 12:16:32 +01:00
6370d517a2 chore(deps-dev): bump css-loader in /src/packages/excalidraw (#4209)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.5.0 to 6.5.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v6.5.0...v6.5.1)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 17:45:06 +00:00
b8a37c42e4 chore(deps): bump @tldraw/vec from 0.0.132 to 0.1.3 (#4215)
Bumps [@tldraw/vec](https://github.com/tldraw/vec) from 0.0.132 to 0.1.3.
- [Release notes](https://github.com/tldraw/vec/releases)
- [Commits](https://github.com/tldraw/vec/commits)

---
updated-dependencies:
- dependency-name: "@tldraw/vec"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:42:21 +01:00
76763b80a9 chore(deps-dev): bump @types/web from 0.0.45 to 0.0.46 (#4214)
Bumps [@types/web](https://github.com/microsoft/TypeScript-DOM-Lib-Generator) from 0.0.45 to 0.0.46.
- [Release notes](https://github.com/microsoft/TypeScript-DOM-Lib-Generator/releases)
- [Commits](https://github.com/microsoft/TypeScript-DOM-Lib-Generator/compare/@types/web@0.0.45...@types/web@0.0.46)

---
updated-dependencies:
- dependency-name: "@types/web"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:41:59 +01:00
d2a2c9d6b5 chore(deps-dev): bump webpack in /src/packages/utils (#4211)
Bumps [webpack](https://github.com/webpack/webpack) from 5.61.0 to 5.62.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.61.0...v5.62.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 17:41:50 +00:00
3a72f347d2 chore(deps-dev): bump webpack in /src/packages/excalidraw (#4208)
Bumps [webpack](https://github.com/webpack/webpack) from 5.61.0 to 5.62.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.61.0...v5.62.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:41:13 +01:00
c1d9456235 chore(deps-dev): bump mini-css-extract-plugin (#4207)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.4.3 to 2.4.4.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.4.3...v2.4.4)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:40:45 +01:00
c4f8b98208 chore(deps-dev): bump css-loader in /src/packages/utils (#4210)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.5.0 to 6.5.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v6.5.0...v6.5.1)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:40:18 +01:00
b6eb57d3f1 chore(deps): bump @testing-library/jest-dom from 5.14.1 to 5.15.0 (#4212)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.14.1 to 5.15.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.14.1...v5.15.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:39:52 +01:00
473b8ca0ca chore(deps): bump @types/react from 17.0.33 to 17.0.34 (#4216)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.33 to 17.0.34.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:39:24 +01:00
45206c4ef1 chore(deps-dev): bump firebase-tools from 9.21.0 to 9.22.0 (#4219)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.21.0 to 9.22.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.21.0...v9.22.0)

---
updated-dependencies:
- dependency-name: firebase-tools
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-08 18:38:55 +01:00
56b4a29aaa chore(deps): bump @types/react-dom from 17.0.10 to 17.0.11 (#4218)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-07 15:00:24 +01:00
bb4dda64b5 chore(deps): bump roughjs from 4.4.4 to 4.4.5 (#4221)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-07 14:57:09 +01:00
39e53b4ae7 feat: Allow letters in IDs for storing files in backend (#4224) 2021-11-07 14:37:13 +01:00
6143d5195a refactor: deduplicate encryption helpers (#4146) 2021-11-07 14:33:21 +01:00
f59e608f18 fix: Skia rendering issues (#4200) 2021-11-04 15:55:10 +01:00
6b24592e4a chore: Update translations from Crowdin (#4150) 2021-11-04 14:10:21 +02:00
7b442997dc chore: Update docker action to v2 (#4198) 2021-11-04 14:10:00 +02:00
4bfc5bbcaa chore: Update i18next-browser-languagedetector (#4196) 2021-11-03 21:07:13 +00:00
2b29b9a96d chore: Consistent case for clear canvas, change font of buttons and clean up unused strings (#4195)
* chore: Consistent case for clear canvas and font

* Remove unused

* remove
2021-11-03 21:31:27 +02:00
cc201a6d80 fix: ellipse roughness when 0 (#4194) 2021-11-03 12:50:54 +01:00
5be58b59e0 fix: Proper string for invalid SVG (#4191) 2021-11-03 10:10:58 +01:00
f1eb969565 feat: Remove support for V1 unencrypted backend (#4189) 2021-11-02 14:52:25 +02:00
8d4f455cd3 chore: Update Typescript to 4.4.4 (#4188) 2021-11-02 14:24:16 +02:00
60262cb4cc chore(deps): bump idb-keyval from 5.1.3 to 6.0.3 (#4181)
Bumps [idb-keyval](https://github.com/jakearchibald/idb-keyval) from 5.1.3 to 6.0.3.
- [Release notes](https://github.com/jakearchibald/idb-keyval/releases)
- [Changelog](https://github.com/jakearchibald/idb-keyval/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jakearchibald/idb-keyval/compare/v5.1.3...v6.0.3)

---
updated-dependencies:
- dependency-name: idb-keyval
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-02 09:44:05 +02:00
7501c24f22 feat: Use separate backend for local storage (#4187) 2021-11-02 09:33:27 +02:00
00d81aa982 chore(deps-dev): bump @babel/plugin-transform-typescript (#4160)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.15.8 to 7.16.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.1/packages/babel-plugin-transform-typescript)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:36:09 +00:00
67fe156d06 chore(deps-dev): bump @babel/plugin-transform-async-to-generator (#4162)
Bumps [@babel/plugin-transform-async-to-generator](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-async-to-generator) from 7.14.5 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-async-to-generator)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-async-to-generator"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:31:12 +00:00
ef433233d1 chore(deps-dev): bump @babel/preset-typescript in /src/packages/utils (#4161)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.15.0 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:24:26 +00:00
1c7056bdaa chore: bump Prettier to the latest (#4185) 2021-11-01 15:24:05 +02:00
277ffaacb9 chore(deps-dev): bump css-loader in /src/packages/excalidraw (#4122)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.6 to 6.5.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.6...v6.5.0)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:23:38 +00:00
2a3e242cfd chore(deps-dev): bump @babel/plugin-transform-runtime (#4164)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:20:22 +00:00
b1c6051d6b chore(deps-dev): bump webpack in /src/packages/utils (#4134)
Bumps [webpack](https://github.com/webpack/webpack) from 5.50.0 to 5.61.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.50.0...v5.61.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:19:04 +00:00
8df9742463 chore(deps-dev): bump webpack-cli in /src/packages/excalidraw (#4132)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.7.2 to 4.9.1.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.7.2...webpack-cli@4.9.1)

---
updated-dependencies:
- dependency-name: webpack-cli
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:18:24 +00:00
9fdc382d71 chore(deps-dev): bump @babel/plugin-transform-arrow-functions (#4165)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.14.5 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-arrow-functions)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-arrow-functions"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:15:16 +00:00
f70d11c2d1 chore(deps-dev): bump @babel/plugin-transform-typescript (#4167)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.15.8 to 7.16.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.1/packages/babel-plugin-transform-typescript)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 15:11:04 +02:00
05e54d6785 chore(deps-dev): bump mini-css-extract-plugin (#4094)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.6.1 to 2.4.3.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.6.1...v2.4.3)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:10:36 +00:00
795a6e4546 chore(deps-dev): bump webpack-cli in /src/packages/utils (#4118)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.7.2 to 4.9.1.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.7.2...webpack-cli@4.9.1)

---
updated-dependencies:
- dependency-name: webpack-cli
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:09:32 +00:00
a01a4ad739 chore(deps): bump @testing-library/react from 11.2.6 to 12.1.2 (#4177)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 11.2.6 to 12.1.2.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v11.2.6...v12.1.2)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 13:03:24 +00:00
e09b96ac6f chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#4166)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 12:56:22 +00:00
d48fb17718 chore(deps-dev): bump webpack in /src/packages/excalidraw (#4144)
Bumps [webpack](https://github.com/webpack/webpack) from 5.50.0 to 5.61.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.50.0...v5.61.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 18:24:40 +05:30
ede3c4af82 chore(deps-dev): bump postcss-loader in /src/packages/excalidraw (#4169)
Bumps [postcss-loader](https://github.com/webpack-contrib/postcss-loader) from 6.1.1 to 6.2.0.
- [Release notes](https://github.com/webpack-contrib/postcss-loader/releases)
- [Changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/postcss-loader/compare/v6.1.1...v6.2.0)

---
updated-dependencies:
- dependency-name: postcss-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:53:43 +02:00
8bcfd97fc5 chore(deps-dev): bump css-loader in /src/packages/utils (#4119)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.2.0 to 6.5.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v6.2.0...v6.5.0)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:50:49 +02:00
5721c6dfb5 chore(deps): bump nanoid from 3.1.22 to 3.1.30 (#4176)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.22 to 3.1.30.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.22...3.1.30)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 12:47:06 +00:00
9b1f77c3be chore(deps-dev): bump @babel/plugin-transform-async-to-generator (#4168)
Bumps [@babel/plugin-transform-async-to-generator](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-async-to-generator) from 7.14.5 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-async-to-generator)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-async-to-generator"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 12:43:37 +00:00
3369035f40 feat: add hint around canvas panning (#4159) 2021-11-01 13:36:06 +01:00
dbc7a8599b chore(deps): bump @tldraw/vec from 0.0.106 to 0.0.132 (#4175)
Bumps [@tldraw/vec](https://github.com/tldraw/tldraw/tree/HEAD/packages/vec) from 0.0.106 to 0.0.132.
- [Release notes](https://github.com/tldraw/tldraw/releases)
- [Commits](https://github.com/tldraw/tldraw/commits/HEAD/packages/vec)

---
updated-dependencies:
- dependency-name: "@tldraw/vec"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:35:17 +02:00
09f649daf7 chore(deps-dev): bump @types/pako from 1.0.1 to 1.0.2 (#4170)
Bumps [@types/pako](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pako) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pako)

---
updated-dependencies:
- dependency-name: "@types/pako"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:33:09 +02:00
d357664850 chore(deps): bump perfect-freehand from 1.0.15 to 1.0.16 (#4178)
Bumps [perfect-freehand](https://github.com/steveruizok/perfect-freehand) from 1.0.15 to 1.0.16.
- [Release notes](https://github.com/steveruizok/perfect-freehand/releases)
- [Commits](https://github.com/steveruizok/perfect-freehand/compare/v1.0.15...v1.0.16)

---
updated-dependencies:
- dependency-name: perfect-freehand
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:32:37 +02:00
f973fdfa89 chore(deps): bump fake-indexeddb from 3.1.3 to 3.1.7 (#4172)
Bumps [fake-indexeddb](https://github.com/dumbmatter/fakeIndexedDB) from 3.1.3 to 3.1.7.
- [Release notes](https://github.com/dumbmatter/fakeIndexedDB/releases)
- [Changelog](https://github.com/dumbmatter/fakeIndexedDB/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dumbmatter/fakeIndexedDB/compare/v3.1.3...v3.1.7)

---
updated-dependencies:
- dependency-name: fake-indexeddb
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:31:54 +02:00
c15bc50f17 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#4163)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:31:34 +02:00
c2d0107cc5 chore(deps): bump sass from 1.32.10 to 1.43.4 (#4174)
Bumps [sass](https://github.com/sass/dart-sass) from 1.32.10 to 1.43.4.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.32.10...1.43.4)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:31:13 +02:00
c43fac31a1 chore(deps): bump @types/react from 17.0.3 to 17.0.33 (#4179)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.3 to 17.0.33.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:30:43 +02:00
9dfaf1752b chore(deps): bump open-color from 1.8.0 to 1.9.1 (#4180)
Bumps [open-color](https://github.com/yeun/open-color) from 1.8.0 to 1.9.1.
- [Release notes](https://github.com/yeun/open-color/releases)
- [Changelog](https://github.com/yeun/open-color/blob/master/build_release)
- [Commits](https://github.com/yeun/open-color/compare/v1.8.0...v1.9.1)

---
updated-dependencies:
- dependency-name: open-color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:30:17 +02:00
d9a1eb2f01 chore(deps-dev): bump firebase-tools from 9.9.0 to 9.21.0 (#4184)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.9.0 to 9.21.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.9.0...v9.21.0)

---
updated-dependencies:
- dependency-name: firebase-tools
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 14:29:56 +02:00
f1e17a320f chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#4151)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 10:05:36 +00:00
75ecd818b3 chore(deps-dev): bump webpack-bundle-analyzer in /src/packages/utils (#4158)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.4.2 to 4.5.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.4.2...v4.5.0)

---
updated-dependencies:
- dependency-name: webpack-bundle-analyzer
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 10:05:03 +00:00
a7abc71f6a chore: Update dependabot limits (#4145) 2021-11-01 12:03:16 +02:00
6d0f0c8f21 chore(deps-dev): bump @babel/plugin-transform-runtime (#4152)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 12:00:41 +02:00
790e6da500 fix: images not initialized correctly (#4157)
* fix: image not initialized correctly due to not renewing `state.pendingImageElement`

* ensure we replace elements on update

* set file as errored on >= 400 status respones
2021-11-01 10:44:57 +01:00
8df1a11535 chore(deps-dev): bump @babel/plugin-transform-arrow-functions (#4148)
Bumps [@babel/plugin-transform-arrow-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-arrow-functions) from 7.14.5 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-plugin-transform-arrow-functions)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-arrow-functions"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-31 10:00:11 +00:00
b61ee56dc8 chore(deps-dev): bump @babel/core in /src/packages/utils (#4149)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.15.8 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-31 11:55:32 +02:00
c61f95a327 fix: image-related fixes (#4147)
* flush queues on portal close

* fix mouse broadcast race condition

* stop mutating image elements when updating status

to fix race condition when closing/opening collab room

* check `files` when resolving `LayerUI`

* fix displaying AbortError
2021-10-30 23:40:35 +02:00
d6d629f416 chore: Update translations from Crowdin (#4109) 2021-10-30 19:26:54 +03:00
65dec605f2 chore(deps-dev): bump @babel/preset-typescript (#4143)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.15.0 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-30 16:20:16 +00:00
cacec0b5c4 chore(deps-dev): bump @babel/preset-react in /src/packages/excalidraw (#4140)
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.14.5 to 7.16.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.16.0/packages/babel-preset-react)

---
updated-dependencies:
- dependency-name: "@babel/preset-react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-30 12:03:51 +03:00
87a302d7e9 chore(deps-dev): bump sass-loader in /src/packages/excalidraw (#4139)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.1.0 to 12.3.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v12.1.0...v12.3.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-30 11:34:17 +03:00
899b36c206 chore(deps-dev): bump babel-loader in /src/packages/excalidraw (#4138)
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.2.2 to 8.2.3.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.2.2...v8.2.3)

---
updated-dependencies:
- dependency-name: babel-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-30 00:41:13 +00:00
534cbef982 chore(deps-dev): bump autoprefixer in /src/packages/excalidraw (#4137)
Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.3.1 to 10.4.0.
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.3.1...10.4.0)

---
updated-dependencies:
- dependency-name: autoprefixer
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-30 00:01:45 +03:00
b7f118404e chore(deps-dev): bump webpack-bundle-analyzer (#4121)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.4.2 to 4.5.0.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.4.2...v4.5.0)

---
updated-dependencies:
- dependency-name: webpack-bundle-analyzer
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 18:37:49 +03:00
aab5067718 chore(deps-dev): bump @types/resize-observer-browser from 0.1.5 to 0.1.6 (#4135)
Bumps [@types/resize-observer-browser](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/resize-observer-browser) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/resize-observer-browser)

---
updated-dependencies:
- dependency-name: "@types/resize-observer-browser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 15:36:23 +00:00
b679da02ee chore(deps-dev): bump terser-webpack-plugin in /src/packages/excalidraw (#4128)
Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.1.4 to 5.2.4.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.1.4...v5.2.4)

---
updated-dependencies:
- dependency-name: terser-webpack-plugin
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 18:26:30 +03:00
ec652820ea chore(deps): bump @types/jest from 26.0.22 to 27.0.2 (#4131)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.22 to 27.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 18:26:15 +03:00
5d941ed107 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#4129)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.15.0 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 18:25:59 +03:00
adc478ca34 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#4130)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.9 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 18:25:47 +03:00
f1202adb15 feat: stop using production services for development (#4113) 2021-10-29 17:13:28 +02:00
fd439cf38a chore(deps-dev): bump babel-loader in /src/packages/utils (#4124)
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.2.2 to 8.2.3.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.2.2...v8.2.3)

---
updated-dependencies:
- dependency-name: babel-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:32:52 +00:00
83c63be846 chore(deps-dev): bump @babel/preset-typescript (#4127)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.14.5 to 7.15.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:26:28 +00:00
b59d49dd7f chore(deps-dev): bump sass-loader in /src/packages/utils (#4126)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.1.0 to 12.3.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v12.1.0...v12.3.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:25:02 +00:00
0116b70edf chore(deps): bump @testing-library/jest-dom from 5.11.10 to 5.14.1 (#4125)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.11.10 to 5.14.1.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.11.10...v5.14.1)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:24:54 +00:00
3f390d4858 chore(deps-dev): bump ts-loader in /src/packages/excalidraw (#4002)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 9.2.4 to 9.2.6.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.2.4...v9.2.6)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:24:16 +00:00
fdde73bff4 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#4069)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.8 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:21:07 +00:00
90a416e265 chore(deps): bump @types/react-dom from 17.0.3 to 17.0.10 (#4120)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 17.0.3 to 17.0.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 14:17:59 +03:00
a828b2e5de chore(deps-dev): bump @babel/plugin-transform-typescript (#4044)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.14.6 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-plugin-transform-typescript)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:16:44 +00:00
7c51d3c24c chore(deps-dev): bump ts-loader in /src/packages/utils (#4117)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 9.2.4 to 9.2.6.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.2.4...v9.2.6)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:13:03 +00:00
4d2d6f181a chore(deps): bump tar from 4.4.15 to 4.4.19 (#3953)
Bumps [tar](https://github.com/npm/node-tar) from 4.4.15 to 4.4.19.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v4.4.15...v4.4.19)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 11:10:33 +00:00
071416f6ef chore(deps-dev): bump @babel/plugin-transform-typescript (#4045)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.14.6 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-plugin-transform-typescript)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 14:10:19 +03:00
d675b07089 chore(deps-dev): bump @babel/core in /src/packages/utils (#4043)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.8 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 14:09:53 +03:00
3975fd592a chore(deps-dev): bump @babel/plugin-transform-runtime (#4042)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.14.5 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 14:09:37 +03:00
34a9a4dac6 chore(deps-dev): bump @babel/preset-typescript in /src/packages/utils (#3928)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.14.5 to 7.15.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 14:08:37 +03:00
78e419b790 chore(deps-dev): Upgrade commit hooks to Husky 7 (#4116)
* Upgrade to Husky 7

* Husky
2021-10-29 14:06:13 +03:00
8d8769ba4e feat: add triangle arrowhead (#4024)
Co-authored-by: ad1992 <aakansha1216@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-10-27 19:27:39 +02:00
d89fb3371b fix: rewrite collab element reconciliation to fix z-index issues (#4076) 2021-10-27 15:14:20 +02:00
8410972cff chore: Update translations from Crowdin (#4047)
* New translations en.json (Occitan)

* New translations en.json (Catalan)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Bengali)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Simplified)

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Slovak)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Traditional)

* New translations en.json (Indonesian)

* New translations en.json (Punjabi)

* New translations en.json (Persian)

* New translations en.json (Norwegian Nynorsk)

* New translations en.json (Kazakh)

* New translations en.json (Latvian)

* New translations en.json (Hindi)

* New translations en.json (Burmese)

* New translations en.json (Norwegian Bokmal)

* New translations en.json (Polish)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Bulgarian)

* New translations en.json (Tamil)

* New translations en.json (Bengali)

* New translations en.json (Chinese Simplified)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Arabic)

* New translations en.json (Czech)

* New translations en.json (Korean)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Japanese)

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Italian)

* New translations en.json (Dutch)

* New translations en.json (Chinese Traditional)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* New translations en.json (Ukrainian)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* Auto commit: Calculate translation coverage

* New translations en.json (German)

* Auto commit: Calculate translation coverage

* New translations en.json (German)

* New translations en.json (Occitan)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Slovak)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Traditional)

* New translations en.json (Indonesian)

* New translations en.json (Punjabi)

* New translations en.json (Persian)

* New translations en.json (Norwegian Nynorsk)

* New translations en.json (Kazakh)

* New translations en.json (Latvian)

* New translations en.json (Hindi)

* New translations en.json (Burmese)

* New translations en.json (Norwegian Bokmal)

* New translations en.json (Polish)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Bulgarian)

* New translations en.json (Tamil)

* New translations en.json (Bengali)

* New translations en.json (Chinese Simplified)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Arabic)

* New translations en.json (Czech)

* New translations en.json (Korean)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Japanese)

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* Auto commit: Calculate translation coverage

* New translations en.json (Indonesian)

* Auto commit: Calculate translation coverage

* New translations en.json (Indonesian)

* New translations en.json (Kazakh)

* Auto commit: Calculate translation coverage

* New translations en.json (Japanese)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* New translations en.json (Dutch)

* Auto commit: Calculate translation coverage

* New translations en.json (Italian)

* Auto commit: Calculate translation coverage

* New translations en.json (Romanian)

* New translations en.json (Italian)

* Auto commit: Calculate translation coverage

* New translations en.json (Portuguese)

* Auto commit: Calculate translation coverage

* New translations en.json (Finnish)

* Auto commit: Calculate translation coverage

* New translations en.json (Sinhala)

* Auto commit: Calculate translation coverage

* New translations en.json (German)

* Auto commit: Calculate translation coverage

* New translations en.json (Portuguese, Brazilian)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Tamil)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* New translations en.json (Romanian)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* Auto commit: Calculate translation coverage

* New translations en.json (Sinhala)

* Auto commit: Calculate translation coverage

* New translations en.json (Sinhala)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Chinese Simplified)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Simplified)

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage
2021-10-27 12:41:00 +05:30
2c8d041987 Migrate the implementation back to browser-fs-access (#4106) 2021-10-27 08:57:10 +02:00
94519c8250 fix: redirect excalidraw.com/about to for-webex.excalidraw.com (#4104) 2021-10-26 17:01:05 +02:00
add8a1b1a7 fix: redirect to webex LP instead of rewrite to fix SW (#4103) 2021-10-26 16:02:30 +02:00
516e7656f3 feat: Add rewrite to webex landing page (#4102)
* feat: Add rewrite to webex landing page

* blacklist webex url

* dont cache webex

* Unregister sw for webex

* fix

* fix

* reload in callback

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-10-26 18:19:41 +05:30
d7cdee37bf feat: switch collab server (#4092) 2021-10-24 11:47:45 +02:00
5c5b8c517f fix: clear image/shape cache of affected elements when adding files (#4089) 2021-10-23 14:17:04 +02:00
7dbd0c5e0a fix: clear LibraryUnit DOM on unmount (#4084) 2021-10-22 22:07:20 +02:00
ba35eb8f8c fix: pasting images on firefox (#4085) 2021-10-22 21:04:04 +02:00
163ad1f4c4 feat: image support (#4011)
Co-authored-by: Emil Atanasov <heitara@gmail.com>
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-10-21 22:05:48 +02:00
0f0244224d feat: Use dialog component for clear canvas instead of window confirm (#4075)
* feat: Use dialog component for clear canvas instead of window confirm

* reduce font weight

* fix specs

* update button name and use action

* export clearCanvas from actions
2021-10-21 17:35:28 +05:30
6eecadce60 feat: export isLinearElement and getNonDeletedElements (#4072)
* feat: export isLinearElement and getNonDeletedElements

* fix
2021-10-19 14:40:48 +05:30
bc88cf5002 fix: Don't show save file to disk when UIOptions.canvasActions.export.saveFileToDisk is false (#4073) 2021-10-19 14:39:47 +05:30
571be9c0fe chore(deps-dev): bump @babel/plugin-transform-runtime (#4053)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.14.5 to 7.15.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.8/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-18 15:59:56 +05:30
5d925c7d3f build: Allow package.json changes when autoreleasing next (#4068) 2021-10-18 15:58:58 +05:30
45c520341f chore: bump @dwelle/browser-fs-access to 0.21.2 (#4067) 2021-10-18 11:08:12 +02:00
c6ffc06541 feat: support renderTopRightUI in mobile (#4065) 2021-10-17 21:44:46 +05:30
ff29780760 Refactor: convert initializeApp to func component and use JSX transform in the codebase (#4056) 2021-10-14 22:56:51 +05:30
463857ad9a feat: Export THEME from the package (#4055)
* Use Theme type everywhere
* Rename Appearance type to Theme for consistency
* Reorder headers in readme
The host don't need to pass hardcoded strings any more and instead can use the exported constant
2021-10-14 14:15:57 +05:30
be2da9539e chore(deps): bump path-parse in /src/packages/excalidraw (#3912)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-13 20:18:29 +05:30
bb7829ef90 chore(deps): bump url-parse from 1.5.1 to 1.5.3 (#3927)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-13 20:18:09 +05:30
1104f6891e chore(deps): bump semver-regex from 3.1.2 to 3.1.3 (#3988)
Bumps [semver-regex](https://github.com/sindresorhus/semver-regex) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/sindresorhus/semver-regex/releases)
- [Commits](https://github.com/sindresorhus/semver-regex/commits)

---
updated-dependencies:
- dependency-name: semver-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-13 20:17:43 +05:30
a97e172070 chore(deps): bump tmpl from 1.0.4 to 1.0.5 (#3992)
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-13 20:17:24 +05:30
39d45afc06 build: Enable jsx transform in webpack and release @excalidraw/excalidraw@0.10.0 🎉 (#4049)
* fix: Enable jsx transform in webpack

* update changelog

* fix

* typo fix in script

* docs: release @excalidraw/excalidraw@0.9.1  🎉

* fix changelog

* release 0.10.0

* Update src/packages/excalidraw/CHANGELOG.md
2021-10-13 17:03:50 +05:30
00c6940851 fix: freehand points (#4031)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-10-11 20:11:26 +02:00
982cba2035 chore: Update translations from Crowdin (#3996) 2021-10-09 20:32:35 +02:00
54739cd2df fix: abstract and fix legacy fs (#4032) 2021-10-07 13:19:40 +02:00
75aeaa6c38 fix: context menu positioning (#4025) 2021-10-04 12:13:17 +02:00
bea4a1e066 chore: bump browser-fs-acccess to 0.20.5 (#4018)
* bump browser-fs-acccess to 0.20.5

* add null check
2021-10-02 18:27:26 +02:00
e8b462cc31 fix: Added alert for bad encryption key (#3998) 2021-09-24 22:34:56 +02:00
c86c176e10 docs: Added note on encryption key length (#3995) 2021-09-23 21:30:01 +05:30
b09c11bb14 chore: Update translations from Crowdin (#3858) 2021-09-22 22:58:12 +02:00
7199d13f48 feat: improve freedraw shape (#3984) 2021-09-18 16:56:55 +02:00
7d1fddc144 fix: onPaste should return false to prevent paste action (#3974)
* Update App.tsx

* Update README_NEXT.md

* Update CHANGELOG.md

* Update App.tsx

* Update App.tsx

* Update src/packages/excalidraw/CHANGELOG.md

* fix lint

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-09-13 20:58:53 +05:30
5da3207633 docs: correct exportToBackend to onExportToBackend in README (#3952)
* docs: correct exportToBackend to onExportToBackend in README

* Update changelog with link to PR

* Add Excalidraw API title to changelog section

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-08-30 14:16:44 +05:30
8c9786e026 fix: help-icon now visible on Safari (#3939) 2021-08-25 13:57:42 +02:00
f0f13ed694 fix: permanent zoom mode (#3931) 2021-08-23 22:47:29 +02:00
850d8eb47e chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3899)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.9 to 7.15.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-21 00:03:05 +05:30
f287f9c002 chore(deps): bump path-parse from 1.0.6 to 1.0.7 in /src/packages/utils (#3913)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-21 00:02:39 +05:30
78df5bc852 chore(deps): bump path-parse from 1.0.6 to 1.0.7 (#3914)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-21 00:02:29 +05:30
f0073c7e26 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3918)
Bumps [webpack](https://github.com/webpack/webpack) from 5.47.1 to 5.50.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.47.1...v5.50.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-21 00:02:11 +05:30
fa7a313412 chore(deps-dev): bump webpack in /src/packages/utils (#3919)
Bumps [webpack](https://github.com/webpack/webpack) from 5.40.0 to 5.50.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.40.0...v5.50.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-21 00:01:46 +05:30
8b3f236cd8 chore(deps): bump tar from 4.4.13 to 4.4.15 (#3888)
Bumps [tar](https://github.com/npm/node-tar) from 4.4.13 to 4.4.15.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v4.4.13...v4.4.15)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 02:20:23 +05:30
621812d0eb feat: Make color ARIA labels better (#3871)
* Make color aria labels better

* Use isTransparent helper

* Fix import

* Try to fix test

* More test fixes

* Reuse variable
2021-08-02 20:18:55 +02:00
d607249205 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#3884)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.7 to 7.14.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.9/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:45:17 +00:00
df28c3299f chore(deps-dev): bump ts-loader in /src/packages/excalidraw (#3883)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 9.2.3 to 9.2.4.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.2.3...v9.2.4)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:45:02 +00:00
b00a57b4be chore(deps-dev): bump ts-loader in /src/packages/utils (#3863)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 9.2.3 to 9.2.4.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.2.3...v9.2.4)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:39:53 +00:00
9277e839db chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3885)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.7 to 7.14.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.9/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:39:29 +00:00
0d5d60944f chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3886)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.6 to 7.14.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:39:15 +00:00
489a652d73 chore(deps-dev): bump typescript in /src/packages/excalidraw (#3787)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.3.4 to 4.3.5.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.3.4...v4.3.5)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 13:37:33 +00:00
2b85d96121 chore(deps-dev): bump postcss-loader in /src/packages/excalidraw (#3788)
Bumps [postcss-loader](https://github.com/webpack-contrib/postcss-loader) from 6.1.0 to 6.1.1.
- [Release notes](https://github.com/webpack-contrib/postcss-loader/releases)
- [Changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/postcss-loader/compare/v6.1.0...v6.1.1)

---
updated-dependencies:
- dependency-name: postcss-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 19:03:04 +05:30
6ce535d3a4 chore(deps-dev): bump @babel/core in /src/packages/utils (#3860)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.6 to 7.14.8.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.8/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 19:02:43 +05:30
da43cf5635 chore(deps-dev): bump css-loader in /src/packages/utils (#3862)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.6 to 6.2.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.6...v6.2.0)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 19:02:22 +05:30
603ecfba34 chore(deps-dev): bump autoprefixer in /src/packages/excalidraw (#3839)
Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.2.6 to 10.3.1.
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.2.6...10.3.1)

---
updated-dependencies:
- dependency-name: autoprefixer
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 19:00:23 +05:30
a589708737 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3881)
Bumps [webpack](https://github.com/webpack/webpack) from 5.40.0 to 5.47.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.40.0...v5.47.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 18:59:48 +05:30
4df401d012 feat: Add origin trial tokens (#3853) 2021-07-23 10:43:27 +02:00
b2c4552416 feat: re-order zoom buttons (#3837)
* feat: re-order zoom buttons

* undo last commit & change zoomOut/zoomIn order
2021-07-16 23:47:43 +02:00
5cae218f1b fix: undo/redo buttons gap in Safari (#3836) 2021-07-15 22:10:48 +02:00
4be726d405 chore: Update translations from Crowdin (#3812) 2021-07-15 19:10:38 +02:00
99623334d1 feat: add undo/redo buttons & tweak footer (#3832)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-07-15 18:48:03 +02:00
685abac81a feat: resave to png/svg with metadata if you loaded your scene from a png/svg file (#3645)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-07-15 15:54:26 +02:00
9581c45522 fix: Prevent gradual canvas misalignment (#3833)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-07-14 13:29:22 +02:00
0749d2c1f3 fix: color picker shortcuts not working when elements selected (#3817) 2021-07-10 21:05:00 +00:00
8787f3dc60 docs: release @excalidraw/excalidraw@0.9.0 🎉 (#3807)
* docs: release @excalidraw/excalidraw@0.9.0  🎉

* remove

* update changelog
2021-07-10 18:52:19 +05:30
5fabc57277 fix: view mode cursor adjustments (#3809) 2021-07-10 00:00:13 +02:00
e7cbb859f0 chore: Bump nginx version to newest (#3811)
Bump nginx version to newest.
2021-07-09 17:07:34 +02:00
aa860251c7 chore: Update translations from Crowdin (#3718)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-07-08 13:09:13 +02:00
380aaa30e6 fix: pass next release to updatePackageVersion & replace ## unreleased with new version (#3806)
* fix: pass next version to updatePackageVersion

* replace unreleased with next version in changelog

* fix

* fix
2021-07-06 00:13:56 +05:30
2e61fec7a6 build: Add release script to update relevant files and commit for next release (#3805)
* build: Add script to update package.json and commit for next release

* fix
2021-07-05 22:29:35 +05:30
3c295559c7 docs: specify to use yarn v1 not v2 (#3799)
Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-07-05 18:01:52 +02:00
55d3287abf fix: include deleted elements when passing to restore (#3802) 2021-07-05 13:43:53 +02:00
e3e967421e fix: import React before using jsx (#3804) 2021-07-05 15:59:09 +05:30
77aae63006 docs: tweak changelog and readme (#3796)
* docs: tweak changelog and readme

* moving to discussions :)

* Apply suggestions from code review

Co-authored-by: David Luzar <luzar.david@gmail.com>

* Add about  attributes passed to updateScene

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-07-05 14:21:01 +05:30
ee64a7e264 fix: ensure s and g shortcuts work on no selection (#3800)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-07-04 22:27:33 +02:00
097362662d feat: pass localElements to restore and restoreElement API's and bump versions of duplicate elements on import (#3797) 2021-07-04 22:23:35 +02:00
038e9c13dd build: Add script to update changelog before a stable release (#3784)
* build: Add script to update changelog before a stable release

* fix

* fix

* fix

* Add note for lib updates

* Update scripts/updateChangelog.js

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-07-04 18:40:25 +05:30
bc8ba08ad0 build: Add script to update readme before stable release (#3781)
* build: Add script to update readme before stable release

* fix

* fix
2021-07-03 18:50:22 +05:30
f861a9fdd0 feat: support appState.exportEmbedScene to embed scene data in exportToSvg util (#3777)
* feat: add embedScene attribute to exportToSvg util

* fix

* return promise

* add docs and remove

* fix

* fix tests

* use

* fix

* fix

* remove metadata and use exportEmbedScene

* fix

* fix

* fix

* fix

* IIFE
2021-07-03 02:07:01 +05:30
62303b8a22 chore: bump browser-fs-access (#3780) 2021-07-02 14:55:16 +00:00
9cc741ab3a chore(deps): bump ssri from 6.0.1 to 6.0.2 (#3711)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: ssri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-30 01:29:46 +05:30
2d279cbb02 chore(deps-dev): bump mini-css-extract-plugin (#3767)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-29 02:13:49 +05:30
57ea4fdf9a chore(deps-dev): bump terser-webpack-plugin in /src/packages/excalidraw (#3768)
Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.1.3 to 5.1.4.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.1.3...v5.1.4)

---
updated-dependencies:
- dependency-name: terser-webpack-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-29 02:13:27 +05:30
44402f42bf feat: switch to selection tool on library item insert (#3773)
* switch to selection tool on library item insert

* add test

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-06-28 12:00:33 +02:00
bdead4d164 feat: expose getFreeDrawSvg, loadFromBlob and loadLibraryFromBlob from excalidraw package (#3764)
* feat: expose getFreeDrawSvg, loadFromBlob and loadLibraryFromBlob from excalidraw package

* Add docs

* fix
2021-06-26 02:12:58 +05:30
bfc0656475 chore(deps): bump color-string from 1.5.4 to 1.5.5 (#3761)
Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/Qix-/color-string/releases)
- [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.5.5)

---
updated-dependencies:
- dependency-name: color-string
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-26 01:37:59 +05:30
a33a3334f7 chore: upgrade deps in packages (#3760)
* chore: upgrade deps

* upgrade deps in utils
2021-06-24 01:26:44 +05:30
969d3c694a fix: keep binding for attached arrows after changing text (#3754)
Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-06-21 14:31:49 +02:00
5cd921549a fix: deselect elements on viewMode toggle (#3741) 2021-06-16 19:01:16 +02:00
437afcbea4 fix: allow pointer events for disable zen mode button (#3743) 2021-06-16 22:25:22 +05:30
6dee02e320 feat: expose fontfamily and refactor FONT_FAMILY (#3710)
* feat: expose fontfamily and refactor FONT_FAMILY for better readability

* fix

* fix

* fix

* docs

* fix
2021-06-13 21:26:55 +05:30
74a2f16501 feat: Show active file name when saving to current file (#3733)
* feat: Show active file name when saving to current file

* Make requested changes

* More changes
2021-06-13 21:11:07 +05:30
fd4460be37 feat: add hint around text editing (#3708) 2021-06-12 22:58:34 +02:00
e82d0493cf chore(deps-dev): bump ts-loader in /src/packages/excalidraw (#3716)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.1.0 to 9.2.3.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.1.0...v9.2.3)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 20:03:02 +00:00
083cb4c656 chore(deps-dev): bump ts-loader in /src/packages/utils (#3712)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.1.0 to 9.2.3.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.1.0...v9.2.3)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 01:27:54 +05:30
d067365c1d chore(deps-dev): bump typescript in /src/packages/excalidraw (#3671)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.2.4 to 4.3.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.2.4...v4.3.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 01:26:27 +05:30
273cac6b60 chore(deps-dev): bump @babel/plugin-transform-typescript (#3713)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.14.3 to 7.14.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.5/packages/babel-plugin-transform-typescript)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 21:48:03 +05:30
b9337b8a36 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3714)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.4 to 7.14.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.5/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 15:41:26 +00:00
0e0025921b chore(deps-dev): bump @babel/plugin-transform-async-to-generator (#3715)
Bumps [@babel/plugin-transform-async-to-generator](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-async-to-generator) from 7.13.0 to 7.14.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.5/packages/babel-plugin-transform-async-to-generator)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-async-to-generator"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 15:36:46 +00:00
efc01ddab1 chore(deps): bump ws from 7.4.3 to 7.4.6 in /src/packages/excalidraw (#3665)
Bumps [ws](https://github.com/websockets/ws) from 7.4.3 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.3...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 15:34:00 +00:00
7bce22b114 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3670)
Bumps [webpack](https://github.com/webpack/webpack) from 5.37.1 to 5.38.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.37.1...v5.38.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 15:31:49 +00:00
aab4965bbb chore(deps): bump ws from 7.4.3 to 7.4.6 in /src/packages/utils (#3664)
Bumps [ws](https://github.com/websockets/ws) from 7.4.3 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.3...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 15:30:49 +00:00
486a9a3da8 chore(deps-dev): bump autoprefixer in /src/packages/excalidraw (#3672)
Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.2.5 to 10.2.6.
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.2.5...10.2.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 20:56:01 +05:30
2425c06082 chore(deps-dev): bump webpack in /src/packages/utils (#3673)
Bumps [webpack](https://github.com/webpack/webpack) from 5.37.1 to 5.38.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.37.1...v5.38.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 20:55:40 +05:30
79ea844901 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3675)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.1 to 7.14.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.4/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-12 20:54:40 +05:30
6690215cd1 feat: change library icon to be more clear (#3583) 2021-06-11 23:13:05 +02:00
7f5e783fe8 chore: Update translations from Crowdin (#3659) 2021-06-11 19:15:44 +02:00
9325109836 fix: use excal id so every element has unique id (#3696)
* fix: use excal id so every element has unique id

* fix

* fix

* fix

* add docs

* fix
2021-06-10 02:46:56 +05:30
69b6fbb3f4 feat: pass current theme when installing libraries (#3701)
* pass current `theme` when installing libraries

* pass `theme` instead of `appState`
2021-06-08 23:14:02 +02:00
6b6002baae refactor: Delete React SyntheticEvent persist (#3700) 2021-06-07 10:32:30 +02:00
54dcb73701 docs: improve grammar and example (#3699)
* Improve grammar, render only once

* Update README_NEXT.md
2021-06-07 01:38:05 +05:30
b595d3fcba test: add unit tests for restore.ts file (#3679)
* Add unit tests for restore.ts file

* Improving describe blocks in restore tests

* Move restore.tests.ts folder and remove depedency of UI in the tests

* Adding snapshots to restore.ts tests

* Using snapshot in freedraw test

* Updating description of test for line and draw elements

* Updating description of test for arrow element

* Improving restoreAppState tests

* specs cleanup

* fix

* fix
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-06-06 21:53:12 +05:30
d0867d1c3b refatcor: make ProjectName a functional component (#3695) 2021-06-04 21:22:09 +05:30
0d19e9210c feat: update virgil font (#3692)
Co-authored-by: tjangy <youri.tjang@rabobank.nl>
2021-06-02 21:41:14 +02:00
4249de41d4 feat: Add prop autoFocus to set focus on the Excalidraw component (#3691)
* feat: Add prop autofocus to set focus on Excalidraw component

* Update PR number

* Make requested changes

* Add note

* Update src/packages/excalidraw/CHANGELOG.md

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

* Update src/tests/excalidrawPackage.test.tsx

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

* Remove duplicate sentence

* Indent note

* autofocus -> autoFocus

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-06-02 19:54:21 +05:30
15f02ba191 refactor: code clean up (#3681)
* refactor: code clean up
Move types from App.tsx to types.ts
Move excalidrawPackage.test.tsx inside src/tests/package

* import type
2021-06-01 23:52:13 +05:30
a2e1199907 feat: support exporting json to excalidraw plus (#3678)
* feat: support exporting json to excalidraw plus

* add Firebase Storage rules to codebase

* factor the onClick handler out

* move excal icon to icons.tsx

* handle export error
2021-06-01 14:05:09 +02:00
c08e9c4172 fix: use rgba instead of shorthand alpha (#3688) 2021-05-31 14:29:40 +05:30
abfc58eb24 feat: save exportScale in AppState (#3580)
Co-authored-by: David Luzar <luzar.david@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-30 16:31:12 +02:00
035c7affff fix: color pickers not opening on mobile (#3676) 2021-05-30 12:05:07 +02:00
c819b653bf fix: on contextMenu, use selected element regardless of z-index (#3668) 2021-05-29 22:33:53 +02:00
60cea7a0c2 fix: selectedGroupIds not being stored in history (#3630)
thanks!
2021-05-29 21:35:03 +02:00
d63b6a3469 feat: support custom UI rendering inside export dialog (#3666)
* feat: support custom UI rendering inside export dialog

* docs

* add

* remove assertion

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-30 00:37:38 +05:30
0912fe1c93 fix: overscroll on touch devices (#3663) 2021-05-29 11:54:36 -04:00
360310de31 feat: Add prop UIOptions.canvasActions.saveAsImage to show/hide save image button (#3662)
* feat: Add prop UIOptions.canvasActions.saveAsImage which implies whether the save as image dialog should be shown

* Add docs

* fix specs
2021-05-29 19:41:50 +05:30
716c78e930 chore(deps): bump dns-packet from 1.3.1 to 1.3.4 (#3652)
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-29 16:06:20 +02:00
ba48974351 feat: customise export dialog with UIOptions.canvasActions.export prop (#3658)
* refactor: update UIOptions.canvasActions.export to be a an object

* fix

* fix

* dnt show export icon when false

* fix

* inline

* memoize UIOptions

* update docs

* fix

* tweak readme

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-05-29 02:56:25 +05:30
6c3e4417e1 feat: Add shortcuts for stroke and background color picker (#3318)
* feat: Add shortcuts for opening stroke and background color picker

* Use App.tsx keydown handler

* only get selectedElements if applicable (perf)

* fix tests and snaps

* reuse `appState.openMenu`

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-28 13:52:42 +02:00
bc0b6e1888 refactor: rename UIOptions.canvasActions.saveScene to UIOptions.canvasActions.saveToActiveFile (#3657)
* refactor rename action saveScene to saveFileToDisk

* docs

* fix

* fix
2021-05-28 02:10:33 +05:30
99a22e8445 chore: Update translations from Crowdin (#3542)
* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Bulgarian)

* New translations en.json (Italian)

* New translations en.json (Catalan)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage

* New translations en.json (Romanian)

* New translations en.json (Dutch)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* New translations en.json (Japanese)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Portuguese)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* New translations en.json (Catalan)

* Auto commit: Calculate translation coverage

* New translations en.json (Finnish)

* New translations en.json (Indonesian)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Simplified)

* New translations en.json (German)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* New translations en.json (Chinese Simplified)

* New translations en.json (Punjabi)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Traditional)

* New translations en.json (Korean)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Persian)

* New translations en.json (Hindi)

* New translations en.json (Burmese)

* New translations en.json (Norwegian Bokmal)

* New translations en.json (Occitan)

* New translations en.json (Dutch)

* New translations en.json (Japanese)

* New translations en.json (Turkish)

* New translations en.json (Arabic)

* New translations en.json (Indonesian)

* New translations en.json (Norwegian Nynorsk)

* New translations en.json (Latvian)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Bulgarian)

* New translations en.json (Italian)

* New translations en.json (Catalan)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Kabyle)

* Auto commit: Calculate translation coverage

* New translations en.json (Spanish)

* New translations en.json (German)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (Indonesian)

* New translations en.json (Romanian)

* New translations en.json (Finnish)

* New translations en.json (Italian)

* New translations en.json (Portuguese)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* Auto commit: Calculate translation coverage

* New translations en.json (Turkish)

* New translations en.json (Turkish)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* New translations en.json (Slovak)

* New translations en.json (Chinese Simplified)

* New translations en.json (Punjabi)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Traditional)

* New translations en.json (Korean)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Persian)

* New translations en.json (Hindi)

* New translations en.json (Burmese)

* New translations en.json (Norwegian Bokmal)

* New translations en.json (Occitan)

* New translations en.json (Dutch)

* New translations en.json (Japanese)

* New translations en.json (Turkish)

* New translations en.json (Arabic)

* New translations en.json (Indonesian)

* New translations en.json (Norwegian Nynorsk)

* New translations en.json (Latvian)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Bulgarian)

* New translations en.json (Italian)

* New translations en.json (Catalan)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Kabyle)

* New translations en.json (Dutch)

* New translations en.json (Swedish)

* New translations en.json (Dutch)

* New translations en.json (Norwegian Bokmal)

* New translations en.json (Romanian)

* New translations en.json (Finnish)

* New translations en.json (Occitan)

* New translations en.json (Slovak)

* New translations en.json (German)

* New translations en.json (Italian)

* New translations en.json (Slovak)

* New translations en.json (French)

* New translations en.json (Portuguese)

* New translations en.json (Indonesian)

* New translations en.json (Indonesian)

* New translations en.json (French)

* New translations en.json (Chinese Traditional)

* New translations en.json (Kabyle)

* New translations en.json (Ukrainian)

* New translations en.json (Slovak)

* New translations en.json (Slovak)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Japanese)

* New translations en.json (Occitan)

* New translations en.json (Latvian)

* New translations en.json (Latvian)

* New translations en.json (Latvian)

* New translations en.json (Turkish)

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* New translations en.json (Czech)

* Auto commit: Calculate translation coverage

* update language picker & coverage descriptions

* New translations en.json (Punjabi)

* Auto commit: Calculate translation coverage

* New translations en.json (Punjabi)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* New translations en.json (Russian)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Dutch)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Swedish)

* New translations en.json (Finnish)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Persian)

* New translations en.json (Norwegian Nynorsk)

* New translations en.json (Hindi)

* New translations en.json (Burmese)

* New translations en.json (Hebrew)

* New translations en.json (Greek)

* New translations en.json (Turkish)

* New translations en.json (Occitan)

* New translations en.json (Latvian)

* New translations en.json (Japanese)

* New translations en.json (Punjabi)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Traditional)

* New translations en.json (Kabyle)

* New translations en.json (German)

* New translations en.json (Czech)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Arabic)

* New translations en.json (Bulgarian)

* New translations en.json (Catalan)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (Kabyle)

* New translations en.json (Dutch)

* New translations en.json (Norwegian Bokmal)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Indonesian)

* Auto commit: Calculate translation coverage

* New translations en.json (German)

* Auto commit: Calculate translation coverage

* New translations en.json (Romanian)

* New translations en.json (German)

* Auto commit: Calculate translation coverage

* New translations en.json (Romanian)

* Auto commit: Calculate translation coverage

* New translations en.json (French)

* Auto commit: Calculate translation coverage

* New translations en.json (Ukrainian)

* New translations en.json (French)

* Auto commit: Calculate translation coverage

* New translations en.json (Japanese)

* Auto commit: Calculate translation coverage

* New translations en.json (Japanese)

* Auto commit: Calculate translation coverage

* New translations en.json (Italian)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Traditional)

* Auto commit: Calculate translation coverage

* New translations en.json (Russian)

* Auto commit: Calculate translation coverage

* New translations en.json (Arabic)

* Auto commit: Calculate translation coverage

* New translations en.json (Arabic)

* New translations en.json (Swedish)

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-27 23:39:19 +05:30
e6d9797167 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3620)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.2 to 7.14.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.3/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:22:44 +05:30
a1e8fdfb1b chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3653)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.4 to 5.2.6.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.4...v5.2.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:17:33 +05:30
1cce63b07b chore(deps-dev): bump css-loader in /src/packages/utils (#3654)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.4 to 5.2.6.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.4...v5.2.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:17:15 +05:30
e9c2a09c21 chore(deps-dev): bump @babel/core in /src/packages/utils (#3619)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.2 to 7.14.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.3/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:15:10 +05:30
55e0812680 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3618)
Bumps [webpack](https://github.com/webpack/webpack) from 5.37.0 to 5.37.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.37.0...v5.37.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:14:46 +05:30
0f32278a7e chore(deps-dev): bump webpack-bundle-analyzer in /src/packages/utils (#3617)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.4.1...v4.4.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:14:30 +05:30
1bdb8da1c3 chore(deps-dev): bump @babel/plugin-transform-typescript (#3625)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.13.0 to 7.14.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.3/packages/babel-plugin-transform-typescript)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:13:37 +05:30
9c9787e0a0 chore(deps-dev): bump @babel/plugin-transform-typescript (#3624)
Bumps [@babel/plugin-transform-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-typescript) from 7.13.0 to 7.14.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.3/packages/babel-plugin-transform-typescript)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:13:12 +05:30
c2fe24c562 chore(deps): bump browserslist in /src/packages/utils (#3647)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:12:30 +05:30
52faa52091 chore(deps): bump browserslist in /src/packages/excalidraw (#3648)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 18:12:08 +05:30
dd12abc583 refactor: remove watermark code (#3639)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-05-26 21:44:54 +02:00
abebf9aff8 fix: small UI issues around image export dialog (#3642) 2021-05-26 14:44:03 +02:00
790c9fd02e feat: exporting redesign (#3613)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-05-25 21:37:14 +02:00
357266e9ab feat: auto-position tooltip and suport overflowing container (#3631) 2021-05-25 13:52:04 +02:00
0bbb4535cf fix: normalize linear element points on restore (#3633) 2021-05-24 20:35:53 +02:00
d201d0be1b fix: disable pointer-events on footer-center container (#3629) 2021-05-23 17:09:39 +02:00
5662c5141d feat: Auto release @excalidraw/excalidraw-next on every change (#3614)
* feat: Auto release @excalidraw/excalidraw-next on every change

* fix

* fix name

* fix

* add logs

* use commithash

* yarn installå

* fix

* catch

* log

* fix

* uncomment

* remove console

* add logs

* list files changed between prev and current commit

* fetch last two commits

* remove logs

* fix

* update readme_next

* update readme before release

* temp commit to trigger release

* update package name to excalidraw-next

* bold

* remove temp branch

* add note about next

* fix

* fix

* fix
2021-05-22 19:43:28 +05:30
044614dcf3 perf: Improve arrow head sizing (#3480)
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2021-05-22 12:30:02 +02:00
9ec15989ab chore(deps-dev): bump sass-loader in /src/packages/utils (#3589)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 11.0.1 to 11.1.1.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v11.0.1...v11.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 09:19:54 +00:00
08aafcd248 chore(deps-dev): bump sass-loader in /src/packages/excalidraw (#3593)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 11.0.1 to 11.1.1.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v11.0.1...v11.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 09:18:51 +00:00
ea5602457f chore(deps-dev): bump terser-webpack-plugin in /src/packages/excalidraw (#3594)
Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.1.1...v5.1.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 14:45:16 +05:30
3c58d19d45 chore(deps-dev): bump @babel/plugin-transform-runtime (#3609)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.15 to 7.14.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.3/packages/babel-plugin-transform-runtime)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 14:44:54 +05:30
fcfcdebc99 chore(deps-dev): bump webpack in /src/packages/utils (#3610)
Bumps [webpack](https://github.com/webpack/webpack) from 5.36.2 to 5.37.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.36.2...v5.37.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 14:44:24 +05:30
aa97c074a7 chore(deps-dev): bump webpack-cli in /src/packages/excalidraw (#3586)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.6.0...webpack-cli@4.7.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 12:39:45 +05:30
d65d2c5279 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#3595)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.1 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-21 12:38:34 +05:30
6d40039f08 feat: allow inner-drag-selecting with cmd/ctrl (#3603)
* feat: allow inner-drag-selecting with cmd/ctrl

* don't use  cursor when pressing cmd/ctrl

* ensure we reset deselected groups

* add tests

* add docs

* couple fixes around group selection
2021-05-20 22:28:34 +02:00
f4e10c93e1 chore(deps-dev): bump eslint-config-prettier from 8.2.0 to 8.3.0 (#3494)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.2.0 to 8.3.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.2.0...v8.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-20 14:53:07 +05:30
82c6df0e1f chore: Make deploy source and logs public (#3596) 2021-05-17 23:03:01 +02:00
c37bd59ddd chore(deps-dev): bump @babel/core in /src/packages/utils (#3588)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.0 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-16 18:40:49 +05:30
198a5e3b53 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3587)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.0 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-16 18:40:21 +05:30
a78e1fa99b chore(deps-dev): bump postcss-loader in /src/packages/excalidraw (#3585)
Bumps [postcss-loader](https://github.com/webpack-contrib/postcss-loader) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/webpack-contrib/postcss-loader/releases)
- [Changelog](https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/postcss-loader/compare/v5.2.0...v5.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-16 18:39:45 +05:30
fc5db9248c chore(deps-dev): bump webpack in /src/packages/excalidraw (#3584)
Bumps [webpack](https://github.com/webpack/webpack) from 5.36.2 to 5.37.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.36.2...v5.37.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-16 18:39:03 +05:30
ebf64036fd docs: release @excalidraw/excalidraw@0.8.0 🎉 (#3581)
* docs: release @excalidraw/excalidraw@0.8.0

* remove

* remove

* add info for each section

* add .

* update
2021-05-15 18:17:44 +05:30
6271a031a3 fix: move encrypted icon to excalidraw-app add separate animation for renderFooter prop (#3577)
* fix: move encrypted icon to excalidraw-app

* use grid & separate animation for custom footer

* update docs

* fix
2021-05-15 14:49:58 +05:30
78da4c075e feat: support updating appState in updateScene API (#3576)
* feat: support updating appState in updateScene API

* make `updateScene.data.appState` more type-safe

* restore `appState` when passing to `updateScene`

* fix

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-14 17:52:56 +05:30
f1cf28a84e refactor: reduce passing-around of canvas in export code (#3571) 2021-05-13 19:21:15 +02:00
3b9290831a refactor: rename renderTopRight prop to renderTopRightUI (#3572)
* refactor: rename renderTopRight prop to renderTopRightUI

* update

* fix

* update
2021-05-13 21:02:59 +05:30
bec34f2d57 feat: Shortcut key for nerd stats (#3552)
* added alt+/ as the shortcut key for nerd stats

Signed-off-by: gurkiran_singh <gurkiransinghk@gmail.com>

* added shortcut info in HelpDialog.ts

Signed-off-by: gurkiran_singh <gurkiransinghk@gmail.com>

* resolved conflicts

Signed-off-by: gurkiran_singh <gurkiransinghk@gmail.com>

* added shortcut info in HelpDialog.ts

Signed-off-by: gurkiran_singh <gurkiransinghk@gmail.com>
2021-05-12 14:27:35 +05:30
07839f8d20 perf: Reduce SVG export size by more than half by reducing precision to 2 decimal places (#3567)
* render svg with a specified precision

* moved precision to a constant

* fix test case  to use rounded values
2021-05-11 19:35:35 -07:00
8068d1f853 feat: export serializeAsJSON from package (#3538)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-05-12 00:24:41 +02:00
92c7d3257f fix: Exporting freedraw with color to svg (#3565) 2021-05-11 10:44:26 +02:00
a8a5e7b6ff fix: no migrating draw lines correctly 2021-05-10 16:19:31 +02:00
45a4a00b69 chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#3555)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:12:10 +05:30
436e539d3a chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3549)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.15 to 7.14.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.1/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:08:35 +05:30
ff19167063 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#3547)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.15 to 7.14.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.1/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:03:50 +05:30
3fc531ed6e chore(deps-dev): bump webpack in /src/packages/utils (#3528)
Bumps [webpack](https://github.com/webpack/webpack) from 5.35.1 to 5.36.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.35.1...v5.36.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:02:06 +05:30
6f55c00814 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3523)
Bumps [webpack](https://github.com/webpack/webpack) from 5.35.1 to 5.36.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.35.1...v5.36.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:01:29 +05:30
a7eb6e1168 chore(deps): bump lodash from 4.17.20 to 4.17.21 in /src/packages/utils (#3556)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 18:00:54 +05:30
641bbdd2da chore(deps): bump lodash in /src/packages/excalidraw (#3554)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 17:53:27 +05:30
42b0f7a614 chore(deps-dev): bump @babel/core in /src/packages/utils (#3526)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.16 to 7.14.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.0/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 17:33:06 +05:30
c11e3818ac chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3525)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.16 to 7.14.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.0/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 17:32:46 +05:30
4b6aa5c53b chore(deps-dev): bump mini-css-extract-plugin (#3522)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.5.0...v1.6.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 17:32:01 +05:30
ebd0408d7d chore(deps-dev): bump webpack-cli in /src/packages/utils (#3550)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.6.0...webpack-cli@4.7.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 17:31:30 +05:30
f4fefbcee8 feat: Better rendering of curved rectangles (#3562) 2021-05-10 12:41:17 +02:00
11b8cc2caa fix: remove draw element from codebase (#3559) 2021-05-10 11:01:10 +02:00
6bebfe63be fix: handle render errors (#3557) 2021-05-09 21:43:36 +02:00
91ab7f36e2 fix: restore on paste or lib import (#3558) 2021-05-09 21:42:12 +02:00
5ee8e8249c log instead of throw on unimplemented render type 2021-05-09 17:47:52 +02:00
49c6bdd520 feat: improved freedraw (#3512)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-09 17:42:10 +02:00
198800136e feat: Add shortcut for dark mode (#3543)
* Create and move toggle into an action

* Add shortcut on help dialog
2021-05-08 11:47:30 +02:00
178ee04d82 feat: Adds rounded icons, joins and caps. (#3521)
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2021-05-07 18:03:23 +02:00
18cdafbcbe chore: Update translations from Crowdin (#3472)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-05-06 22:13:35 +02:00
286e9a1524 feat: add temporary Excalidraw+ promo (#3540)
* feat: add temporary Excalidraw+ promo

* add seemingly required query params
2021-05-06 21:29:05 +02:00
bac76778ce feat: add renderTopRight prop & remove GH corner from core (#3539)
* feat: add `renderTopRight` prop & remove GH corner from core

* reuse `--space-factor` var

* update readme & changelog
2021-05-06 21:00:17 +02:00
f28f7ffb6e fix: improve mobile user experience (#3508) 2021-04-27 12:46:30 +02:00
12e8cc853f feat: remove backdrop-filter to improve perf (#3506)
* feat: remove `backdrop-filter` to improve perf

* remove `backdrop-filter` from Modal
2021-04-27 10:55:59 +02:00
81108bf580 fix: prevent selecting .visually-hidden elements (#3501) 2021-04-26 00:03:53 +02:00
23030a15f2 docs: release @excalidraw/excalidraw 0.7.0 🎉 (#3497)
* docs: release version 0.7.0

* update

* fix

* fix

* fix

* fix

* fix

* fix

* version bump

* fix readme

* tweaks

Co-authored-by: David Luzar <luzar.david@gmail.com>

* update link

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-25 19:02:06 +05:30
4ef7cb7365 feat: bump element version on z-index change (#3483)
* feat: bump element version on z-index change

* update snaps

* update changelog
2021-04-25 14:09:38 +02:00
5cc3f7db80 feat: support scroll to center to single element and rename setScrollToContent (#3482)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-04-25 12:28:41 +02:00
5c42cb5be4 fix: only handle cut/paste events inside excalidraw (#3484)
* fix: only hand cut/paste events inside excalidraw

* changelog

* check if excalidraw is active for copy event

* check if active element is part of excalidraw
2021-04-25 15:13:42 +05:30
004d3180b5 chore(deps-dev): bump webpack in /src/packages/utils (#3491)
Bumps [webpack](https://github.com/webpack/webpack) from 5.33.2 to 5.35.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.33.2...v5.35.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:59:13 +05:30
c12119278a chore(deps-dev): bump webpack in /src/packages/excalidraw (#3492)
Bumps [webpack](https://github.com/webpack/webpack) from 5.34.0 to 5.35.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.34.0...v5.35.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:58:51 +05:30
4d628844de chore(deps-dev): bump css-loader in /src/packages/utils (#3489)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.2 to 5.2.4.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.2...v5.2.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:58:02 +05:30
946a209927 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3487)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.15 to 7.13.16.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.16/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:57:11 +05:30
811437724b chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3486)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.3 to 5.2.4.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.3...v5.2.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:56:40 +05:30
9dcde502aa chore(deps-dev): bump @babel/core in /src/packages/utils (#3493)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.15 to 7.13.16.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.16/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 12:56:18 +05:30
d3106495b2 fix: make history local to a given excalidraw instance (#3481)
* fix: make history local to a given excalidraw instance

* changelog

* Update src/packages/excalidraw/CHANGELOG.md
2021-04-24 18:21:02 +05:30
891ac82447 fix: use active Excalidraw component when editing text (#3478)
* fix: use active excalidraw component when editing text

* changelog

* tweak
2021-04-23 21:11:18 +05:30
354976e08e build: Add vendor prefixes to css rules (#3476)
* build: Add vendor prefixes to css files

* changelog

* fix
2021-04-23 11:31:38 +05:30
5c73c5813c chore: fix CHANGELOG links 2021-04-21 23:40:51 +02:00
3a0b6fb41b refactor: move getSyncableElements to CollabWrapper & expose isInvisiblySmallElement helper (#3471)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-04-21 23:37:44 +02:00
37d513ad59 feat: Make library local to given excalidraw instance and allow consumer to control it (#3451)
* feat: dnt share library & attach to the excalidraw instance

* fix

* Add addToLibrary, resetLibrary and libraryItems in initialData

* remove comment

* handle errors & improve types

* remove resetLibrary and addToLibrary and add onLibraryChange prop

* set library cache to empty arrary on reset

* Add i18n for remove/add library

* Update src/locales/en.json

Co-authored-by: David Luzar <luzar.david@gmail.com>

* update docs

* Assign unique ID to
 each excalidraw component and remove csrfToken from library as its not needed

* tweaks

Co-authored-by: David Luzar <luzar.david@gmail.com>

* update

* tweak

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-04-21 23:38:24 +05:30
46624cc953 docs: update local installation instructions in readme (#3452)
* docs: tweak readme

* remove editors

* Update README.md

Co-authored-by: David Luzar <luzar.david@gmail.com>

* Update README.md

Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-21 14:52:38 +05:30
0d23c8dd76 chore(deps): bump sass from 1.32.8 to 1.32.10 (#3460)
Bumps [sass](https://github.com/sass/dart-sass) from 1.32.8 to 1.32.10.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.32.8...1.32.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-21 00:32:37 +03:00
51ef4cd97b chore: Update translations from Crowdin (#3377) 2021-04-20 23:48:57 +03:00
b558d19d37 chore(deps-dev): bump eslint-config-prettier from 8.1.0 to 8.2.0 (#3461)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.1.0 to 8.2.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.1.0...v8.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-20 23:48:30 +03:00
b8fb6580ef chore(deps-dev): bump @babel/plugin-transform-runtime (#3434)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.10 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-plugin-transform-runtime)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-20 15:55:13 +05:30
6730eb41c2 fix: scrollToContent only on visible elements (#3466) 2021-04-19 17:29:13 +02:00
87c42cb327 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3469)
Bumps [webpack](https://github.com/webpack/webpack) from 5.31.2 to 5.34.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.31.2...v5.34.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 15:15:46 +00:00
8cfd05aa95 chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3470)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.1 to 5.2.3.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.1...v5.2.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 15:14:24 +00:00
3ed8271344 chore(deps-dev): bump webpack-bundle-analyzer in /src/packages/utils (#3458)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.4.0...v4.4.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 20:41:14 +05:30
73515b5a83 chore(deps-dev): bump webpack-bundle-analyzer (#3455)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.4.0...v4.4.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 20:40:27 +05:30
63d3da9a54 chore(deps-dev): bump mini-css-extract-plugin (#3456)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.4.1...v1.5.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 20:39:53 +05:30
215fb5e357 chore(deps-dev): bump css-loader in /src/packages/utils (#3457)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.1...v5.2.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 20:39:18 +05:30
886177816b chore(deps-dev): bump webpack in /src/packages/utils (#3459)
Bumps [webpack](https://github.com/webpack/webpack) from 5.31.2 to 5.33.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.31.2...v5.33.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 20:35:56 +05:30
7d29351d66 fix: library onClick paste off-center (#3462) 2021-04-18 13:33:05 +02:00
c0047269c1 fix: focus on last active element when dialog closes (#3447)
* fix: focus on last active element when dialog closes

* useState instead of ref
2021-04-15 20:29:00 +05:30
793b69e592 fix: Apply theme to only to active excalidraw component (#3446)
* feat: Apply theme to only current instance of excalidraw

* fix

* fix

* fix

* fix

* fix

* update changelog

* fix
2021-04-13 23:02:57 +05:30
e0a449aa40 feat: support tab in text Wyswig (#3411)
* fix: support tab in text Wyswig

* Refactor tab handling

Tab now indent the whole line, instead of inserting at the cursor
position.

Shift+Tab now deindent the whole line.

* Add multi-line tabulation support

* rename

* simplify algo for selected lines start indices & naming tweaks

* add cmd-bracket shortcuts as alias to indent/outdent

* support outdenting partial tabs

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-04-13 16:23:46 +02:00
d5a270f643 fix: tweak readme for syncable elements (#3444)
* fix: tweak readme for syncable elements

* fix

* tweak

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-13 15:04:02 +05:30
d126d04d17 feat: Bind keyboard events to the current excalidraw container and add handleKeyboardGlobally prop to allow host to bind to document (#3430)
* fix: Bind keyboard events to excalidraw container

* fix cases around blurring

* fix modal rendering so keyboard shortcuts work on modal as well

* Revert "fix modal rendering so keyboard shortcuts work on modal as well"

This reverts commit 2c8ec6be8e.

* Attach keyboard event in react way so we need not handle portals separately (modals)

* dnt propagate esc event when modal shown

* focus the container when help dialog closed with shift+?

* focus the help icon when help dialog on close triggered

* move focusNearestTabbableParent to util

* rename util to focusNearestParent and remove outline from excal and modal

* Add prop bindKeyGlobally to decide if keyboard events should be binded to document and allow it in excal app, revert tests

* fix

* focus container after installing library, reset library and closing error dialog

* fix tests and create util to focus container

* Add excalidraw-container class to focus on the container

* pass focus container to library to focus current instance of excal

* update docs

* remove util as it wont be used anywhere

* fix propagation not being stopped for React keyboard handling

* tweak reamde

Co-authored-by: David Luzar <luzar.david@gmail.com>

* tweak changelog

* rename prop to handleKeyboardGlobally

Co-authored-by: dwelle <luzar.david@gmail.com>
2021-04-13 01:29:25 +05:30
153ca6a7c6 chore(deps-dev): bump typescript in /src/packages/excalidraw (#3438)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.2.3...v4.2.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 19:06:44 +00:00
2618ac9f6e chore(deps-dev): bump @babel/core in /src/packages/utils (#3432)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.14 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 19:05:54 +00:00
f64fd80493 chore(deps-dev): bump @babel/plugin-transform-runtime (#3435)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.10 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-plugin-transform-runtime)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 19:00:35 +00:00
a884351137 chore(deps-dev): bump mini-css-extract-plugin (#3443)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.4.0...v1.4.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 18:58:53 +00:00
e546a85a8d chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#3431)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.12 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 18:57:23 +00:00
29e630086c chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3442)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.14 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:24:47 +05:30
a82165cb50 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3433)
Bumps [webpack](https://github.com/webpack/webpack) from 5.30.0 to 5.31.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.30.0...v5.31.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:22:34 +05:30
4dc0159a05 chore(deps-dev): bump webpack in /src/packages/utils (#3436)
Bumps [webpack](https://github.com/webpack/webpack) from 5.30.0 to 5.31.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.30.0...v5.31.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:22:07 +05:30
458787d1d7 chore(deps-dev): bump css-loader in /src/packages/utils (#3437)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.0 to 5.2.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.0...v5.2.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:20:50 +05:30
815977296e chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3439)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.0 to 5.2.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.0...v5.2.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:20:23 +05:30
58f840aa93 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3440)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.12 to 7.13.15.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.15/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 00:19:49 +05:30
422149c249 chore(deps): bump firebase from 8.3.2 to 8.3.3 (#3441)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 10:48:31 +02:00
a7cbe68ae8 refactor: improve types around dataState and libraryData (#3427) 2021-04-10 19:17:49 +02:00
c19c8ecd27 feat: Add scroll listener to the nearest scrollable container and allow consumer to disable it (#3408)
* fix: Add scroll listener to the nearest scrollable container

* fix

* use loop instead of recursion

* fix

* return document

* calculate nearest scrollable container in settimeout to unblock main thread

* Add prop detectNearestScroll and clear timeout on unmount

* disable scroll listener on excal app

* update prop name to detectScroll

* update docs

* remove settimeout

* tweak docs

Co-authored-by: David Luzar <luzar.david@gmail.com>

* tweak changelog

Co-authored-by: David Luzar <luzar.david@gmail.com>

* lint

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-09 20:44:54 +05:30
d91950bd03 feat: Add onPaste prop to customise clipboard paste event (#3420)
* Add Awaited type util

* Expose onPasteFromClipboard props

* Add `event` as second param for advanced usages

* Add support for async flows

* Extract ClipboardData type

* Rename `onPasteFromClipboard` to `onPaste`

* Remove unused type helper

* Add `onPaste` documentation

* tweak docs

* fix

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-04-09 20:19:58 +05:30
89472c14ed chore(deps): bump typescript from 4.2.3 to 4.2.4 (#3422)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 19:55:25 +02:00
09dfd16b17 feat: use component dimensions to break to mobile (#3414)
Co-authored-by: Jed Fox <git@jedfox.com>
2021-04-08 19:54:50 +02:00
016e69b9f2 chore(deps): bump @sentry/integrations from 6.2.1 to 6.2.5 (#3423)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 17:46:04 +02:00
bb1f979718 chore(deps): bump @types/react-dom from 17.0.2 to 17.0.3 (#3419)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 17:19:17 +02:00
5fda8400f3 chore(deps): bump @testing-library/react from 11.2.5 to 11.2.6 (#3421)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 11.2.5 to 11.2.6.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v11.2.5...v11.2.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 17:03:31 +02:00
96beaa4354 chore(deps): bump browser-fs-access from 0.16.2 to 0.16.4 (#3418)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 16:51:50 +02:00
7183f1c83e chore(deps): bump i18next-browser-languagedetector from 6.0.1 to 6.1.0 (#3417)
Bumps [i18next-browser-languagedetector](https://github.com/i18next/i18next-browser-languageDetector) from 6.0.1 to 6.1.0.
- [Release notes](https://github.com/i18next/i18next-browser-languageDetector/releases)
- [Changelog](https://github.com/i18next/i18next-browser-languageDetector/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next-browser-languageDetector/compare/v6.0.1...v6.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 16:32:37 +02:00
24ae9dca2e chore(deps-dev): bump firebase-tools from 9.6.1 to 9.9.0 (#3416)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.6.1 to 9.9.0.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.6.1...v9.9.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 16:14:16 +02:00
f6ac3ea7c6 chore(deps): bump firebase from 8.2.10 to 8.3.2 (#3391)
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 8.2.10 to 8.3.2.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/CHANGELOG.md)
- [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@8.2.10...firebase@8.3.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 16:00:29 +02:00
b88e0253cc chore(deps): bump @sentry/browser from 6.2.2 to 6.2.5 (#3403)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.2.2 to 6.2.5.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.2.2...6.2.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 15:57:46 +02:00
1e48aafb9c fix: incorrectly caching png file handle (#3407) 2021-04-06 21:27:15 +02:00
34761200bf feat: Add screenshots to manifest.json (#3369)
* feat: Add screenshots to manifest.json

* rename screenshots
2021-04-06 23:02:58 +05:30
a0899966ff feat: enable drop event on the whole component (#3406)
Co-authored-by: Thang Vu <thang.huu.vu@mgm-tp.com>
2021-04-06 17:17:00 +02:00
c2b40dff92 docs: changelog tweaks and add Library updates for 0.6.0 (#3404)
* docs: changelog tweaks and Library updates for 0.6.0
* update readme_next to be same as readme
2021-04-06 00:38:14 +05:30
9733ecb3df fix: popover positioning (#3399) 2021-04-05 17:26:37 +02:00
189b721eed chore(deps): bump @testing-library/jest-dom from 5.11.9 to 5.11.10 (#3393)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.11.9 to 5.11.10.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.11.9...v5.11.10)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-05 20:47:02 +05:30
90fd4a95df refactor: rename setCanvasOffsets to refresh and release @excalidraw/excalidraw v0.6.0 🎉 (#3398)
* docs: Release @excalidraw/excalidraw v0.6.0

* update

* fix

* Update src/packages/excalidraw/README.md

Co-authored-by: David Luzar <luzar.david@gmail.com>

* rename setCanvasOffsets to refresh

* fix

* fix

* typo fix

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-04 22:05:02 +05:30
5d3e98fa04 chore: bump browser-fs-access to 0.16.2 (#3396) 2021-04-04 14:45:02 +02:00
422c25449f fix: export dialog canvas positioning (#3397) 2021-04-04 14:41:22 +02:00
67289ef4ce feat: reopen library menu on import from file (#3383)
Co-authored-by: Thang Vu <thang.huu.vu@mgm-tp.com>
2021-04-04 14:06:10 +02:00
233576628c feat: Support customising canvas actions 🎉 (#3364)
* feat: Support hiding save, save as, clear & export

* Remove canvasActions from state & minor changes

* Rename prop to UIOptions & pass default value

* Make requested changes

* better type checking so that optional check not needed at every point

* remove optional checks

* Add few tests

* Add describe block for canvasActions & use snapshot tests

* Add support for hiding canvas background picker

* Take snapshot of canvasActions instead of the whole app

* Add support for hiding dark mode toggle

* Update README.md

* Rename table heading

* Update changelog

* Make requested changes

* Update test name

* tweaks

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-04-04 15:57:14 +05:30
c54a099010 feat: Calculate width/height of canvas based on container dimensions (".excalidraw" selector) & remove props width & height (#3379)
* Remove width/height from the ".excalidraw" container so it will sized automatically.
* updated all ref calculation to ".excalidraw" instead of parent since now ".excalidraw" will get resized
* Remove props width/height as its not needed anymore.
* Resize handler is also not needed anymore.
* Position absolute canvas due to #3379 (comment)

* move css to style and remove one extra rerendering

* factor out mock logic for test

* set height, width so as to avoid unnecessary updates of regression snap

* better mock

* better type checking and omit width,height from getDefaultAppState and also restore

* revert

* default to window dimensions in constructor

* update docs

* update

* update

* tweaks
2021-04-04 15:05:16 +05:30
3b976613ba chore(deps-dev): bump ts-loader in /src/packages/utils (#3388)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.18 to 8.1.0.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.18...v8.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 09:12:34 +00:00
bee59747d1 chore(deps-dev): bump ts-loader in /src/packages/excalidraw (#3385)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.18 to 8.1.0.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.18...v8.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 09:11:09 +00:00
2e1352f3fa chore(deps-dev): bump @babel/core in /src/packages/utils (#3386)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.13 to 7.13.14.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.14/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 14:39:06 +05:30
6b65db7b68 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3387)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.13 to 7.13.14.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.14/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 14:38:14 +05:30
e4c5ebf867 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3389)
Bumps [webpack](https://github.com/webpack/webpack) from 5.28.0 to 5.30.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.28.0...v5.30.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 14:37:16 +05:30
0602f3cfe4 chore(deps-dev): bump webpack in /src/packages/utils (#3390)
Bumps [webpack](https://github.com/webpack/webpack) from 5.28.0 to 5.30.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.28.0...v5.30.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-04 14:36:59 +05:30
eade72b744 chore(deps): bump @types/jest from 26.0.21 to 26.0.22 (#3349)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.21 to 26.0.22.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-02 01:24:24 +03:00
ef5c9002ad chore(deps): bump react and react-dom (#3348)
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). These dependencies needed to be updated together.

Updates `react` from 17.0.1 to 17.0.2
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v17.0.2/packages/react)

Updates `react-dom` from 17.0.1 to 17.0.2
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v17.0.2/packages/react-dom)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-02 01:24:04 +03:00
aa9e1c4566 chore: Update translations from Crowdin (#3371) 2021-04-01 18:47:48 +03:00
edc7f7bf47 feat: calculate offsets when excalidraw container resizes (#3374)
* feat: calculate offsets when excalidraw container resizes

* fix

* rename

* update docs

* Update src/packages/excalidraw/README_NEXT.md

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-04-01 21:10:11 +05:30
1310256dcc fix: remove JSON.stringify when calculating storage as its not needed (#3373) 2021-04-01 11:19:31 +02:00
4ac1841d92 test: Add unit tests for package/utils (#3357)
* Update return type to reflect actual signature

* add tests

* Set getDimensions as optional

* add newlines between specs

* remove redundant assertion

* move fixtures to separate files

* Add spacing

* Move tests, add cases

* Add unit tests for package/utils exportToSvg

* extract default object in test

* Move test suite to new file
2021-03-31 17:58:25 +05:30
bdf6e53289 fix: Add aria-label to end-to-end encryption blog link (#3367) 2021-03-31 17:02:54 +05:30
a6706cff20 feat: export types for package @excalidraw/excalidraw 🎉 (#3337)
* feat: export types for package @excalidraw/excalidraw

* update

* remove

* Add lib in tsconfig-types and Add global.d.ts, and errors down to 39 :)

* Add declaration for scss so typescript allows scss imports, errors down to 37 :)

* Add css.d.ts, errors down to 32 yay

* set target to es6, all errors resolved yay

* move types outside dist

* update docs

* fix
2021-03-30 23:51:55 +05:30
c739ac5c61 chore: Update translations from Crowdin (#3335)
* New translations en.json (Greek)

* Auto commit: Calculate translation coverage

* New translations en.json (Turkish)

* Auto commit: Calculate translation coverage

* New translations en.json (Swedish)

* Auto commit: Calculate translation coverage

* New translations en.json (Occitan)

* Auto commit: Calculate translation coverage

* New translations en.json (Finnish)

* Auto commit: Calculate translation coverage

* New translations en.json (Spanish)

* Auto commit: Calculate translation coverage

* New translations en.json (Slovak)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Simplified)

* Auto commit: Calculate translation coverage

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* Auto commit: Calculate translation coverage
2021-03-30 12:22:15 +03:00
0d818f3810 feat: Add renderCustomStats prop and expose setToastMessage API via refs to update toast (#3360)
* feat: Add renderCustomStats prop to render extra stats & move storage and version to renderCustomStats

* expose Api to update toast message so single instance of toast is used

* rename to setToastMessage

* update docs
2021-03-29 20:06:34 +05:30
58a7568c9f chore(deps): bump nanoid from 3.1.21 to 3.1.22 (#3350)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.21 to 3.1.22.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.21...3.1.22)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-29 17:09:44 +03:00
722e5ca845 refactor: Use arrow function where possible (#3315) 2021-03-29 17:09:20 +03:00
bb568a9670 chore: Remove duplicate Twitter og:image (#3359)
* removed-duplicate-twitter-ogtags

* put favicon back

* fix lint
2021-03-29 13:18:21 +05:30
0f5b0d1d1d docs: revert README to last version and add README_NEXT with changes for next version (#3355) 2021-03-29 01:12:27 +05:30
25fd275158 fix: Don't share collab types with core (#3353)
* fix: Don't share collab types with core

* fix

* remove

* fix
2021-03-28 19:26:03 +05:30
3d047d57a7 build: Add separate dev and prod builds and add source-maps to dev build 🎉 (#3330)
* build: Add separate dev and prod builds and add sourcemaps to dev build

* update

* add

* update changelog
2021-03-28 13:48:26 +05:30
26a6f9e76d chore(deps-dev): bump webpack-cli in /src/packages/utils (#3342)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.5.0...webpack-cli@4.6.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 08:13:04 +00:00
1c11bb5b41 chore(deps-dev): bump @babel/preset-env in /src/packages/excalidraw (#3351)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.10 to 7.13.12.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.12/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:42:20 +05:30
aced1cc6f5 chore(deps-dev): bump css-loader in /src/packages/utils (#3341)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.3 to 5.2.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.3...v5.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 08:11:35 +00:00
f3f85b4c90 chore(deps-dev): bump @babel/preset-react in /src/packages/excalidraw (#3343)
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.12.13 to 7.13.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.13/packages/babel-preset-react)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:36:35 +05:30
86781f09dd chore(deps-dev): bump webpack in /src/packages/utils (#3344)
Bumps [webpack](https://github.com/webpack/webpack) from 5.27.1 to 5.28.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.27.1...v5.28.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:35:59 +05:30
a94b44440e chore(deps-dev): bump webpack in /src/packages/excalidraw (#3339)
Bumps [webpack](https://github.com/webpack/webpack) from 5.27.1 to 5.28.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.27.1...v5.28.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:35:28 +05:30
77bf553ed8 chore(deps-dev): bump @babel/preset-env in /src/packages/utils (#3346)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.10 to 7.13.12.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.12/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:34:56 +05:30
fce7047199 chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3352)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.3 to 5.2.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.3...v5.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:34:22 +05:30
9905deb4b4 chore(deps-dev): bump webpack-cli in /src/packages/excalidraw (#3338)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.5.0...webpack-cli@4.6.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:08:57 +05:30
fee84f3807 chore(deps-dev): bump mini-css-extract-plugin (#3340)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.3.9 to 1.4.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.3.9...v1.4.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:08:14 +05:30
9020ab3761 chore(deps-dev): bump @babel/core in /src/packages/excalidraw (#3345)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.10 to 7.13.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.13/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:07:31 +05:30
136f8b2279 chore(deps-dev): bump @babel/core in /src/packages/utils (#3347)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.10 to 7.13.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.13/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-28 13:06:56 +05:30
8670b2d587 fix: support d&d of files without extension (#3168) 2021-03-26 22:12:02 +01:00
b081a09962 fix: positions stats for linear elements (#3331) 2021-03-26 22:56:27 +02:00
10a23a10a5 chore: Update translations from Crowdin (#3313) 2021-03-26 22:54:57 +02:00
30ae4b8bf2 feat: don't unnecessarily prompt when installing libraries (#3329) 2021-03-26 18:32:38 +01:00
cf9e29834d feat: prefer hash when importing libraries & expose importLibrary (#3320) 2021-03-26 18:10:43 +01:00
5d26c15daf fix: debounce.flush invokes func even if never queued before (#3326)
* fix: `debounce.flush` invokes func even if never queued before

* reset after debounced invocation

* account for fn throwing
2021-03-26 17:12:32 +01:00
b0d7ff290f feat: Add option to flip single element on the context menu (#2520)
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-03-26 16:45:08 +01:00
458e6d6c24 fix: state selection state on opening contextMenu (#3333) 2021-03-26 16:41:57 +01:00
a21db08cae Update to browser-fs-access v0.16.0 (#3323) 2021-03-26 11:37:27 +02:00
1b626175de feat: use origin + pathname as libraryReturnUrl default (#3325) 2021-03-25 18:27:40 +01:00
5ffdd3f32d docs: Readme tweaks :) (#3319)
* docs: Readme tweaks :)

* update

* fix

* Add info about collab
2021-03-25 21:38:15 +05:30
77b873251a fix: Add unique key for library header to resolve dev warnings (#3316) 2021-03-23 22:25:54 +05:30
b50b8f7b0d fix: disallow create text in viewMode on mobile (#3219) 2021-03-23 19:36:16 +05:30
40656c70d1 fix: Make help toggle tabbable (#3310)
* fix: Make help toggle tabbable

* Update src/components/HelpIcon.tsx

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-03-23 16:48:10 +05:30
c5b4b04d6b chore: Update translations from Crowdin (#3270) 2021-03-22 22:51:06 +02:00
1ad212677b chore(deps): bump @types/jest from 26.0.20 to 26.0.21 (#3298)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.20 to 26.0.21.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-22 22:50:15 +02:00
32427c355c chore(deps): bump @types/react-dom from 17.0.1 to 17.0.2 (#3296)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 17.0.1 to 17.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-22 18:12:02 +00:00
402a812159 chore(deps): bump @types/react from 17.0.2 to 17.0.3 (#3297)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.2 to 17.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-22 20:00:23 +02:00
0480753581 fix: Show Windows share icon for Windows users (#3306)
* fix: Show Windows share icon for Windows users

* move function outside t he component
2021-03-22 17:02:20 +01:00
f7e17a28fa fix: Update browser-fs-access to use new supported export (#3303)
* Use new exported supported

* Bump to v0.15.3
2021-03-22 14:58:26 +01:00
78f3a92dd1 feat: replaces fontSize and fontFamily text with icons (#2857)
Co-authored-by: Hitesh Goyal <hiteshlyearn@Hiteshs-MacBook-Pro.local>
Co-authored-by: dwelle <luzar.david@gmail.com>
2021-03-22 14:26:35 +01:00
c8743a8c02 fix: use random IV for link-sharing encryption (#2829) (#2833)
* fix: use random IV for link-sharing encryption (#2829)

* fix: add backward compatibility for link-sharing encryption (#2829)
2021-03-21 22:31:35 -07:00
127c1be6ad fix: Don't scroll to content on INIT websocket message (#3291)
If you load a shared scene with at least another person on the scene, you can start seeing the content via the firebase response. If you scroll and you receive the response from the websocket INIT, then it scrolls you back to the center which is jarring.

This PR removes the scroll to content for that use case.
2021-03-21 17:25:19 +01:00
86bf2d697d docs: Release @excalidraw/excalidraw@0.5.0 🎉 (#3289)
* docs: Release @excalidraw/excalidraw@0.5.0

* update changelog

* update readme

* remove styles since github strips the styles in markdown
2021-03-21 19:19:09 +05:30
7ee8de0a46 feat: set window.name in excalidraw app & also support target for excalidraw libraries (#3299)
* feat: set window.name in excalidraw app so library installation always opens on same tab & also support target for excalidraw libraries

* update changelog and readme

* Update public/index.html

Co-authored-by: David Luzar <luzar.david@gmail.com>

* use level 4 heading

* Update src/packages/excalidraw/README.md

Co-authored-by: David Luzar <luzar.david@gmail.com>

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-03-21 18:13:52 +05:30
981f327b48 chore(deps-dev): bump css-loader in /src/packages/utils (#3292)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.2 to 5.1.3.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.2...v5.1.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-21 12:55:37 +05:30
eeea8406c9 chore(deps-dev): bump css-loader in /src/packages/excalidraw (#3293)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.2 to 5.1.3.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.2...v5.1.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-21 12:31:15 +05:30
0f249e3b26 chore(deps-dev): bump webpack in /src/packages/utils (#3294)
Bumps [webpack](https://github.com/webpack/webpack) from 5.24.3 to 5.27.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.24.3...v5.27.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-21 12:29:44 +05:30
2c7c80bd75 chore(deps-dev): bump webpack in /src/packages/excalidraw (#3295)
Bumps [webpack](https://github.com/webpack/webpack) from 5.24.3 to 5.27.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.24.3...v5.27.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-21 12:21:09 +05:30
94ad8eaa19 feat: support pasting file contents & always prefer system clip (#3257) 2021-03-20 20:20:47 +01:00
13d9374cde fix: Don't show export and delete when library is empty (#3288) 2021-03-20 21:58:37 +05:30
efb6d0825b feat: Add label for name field and use input when editable in export dialog (#3286)
* feat: Add label for name field and use input when editable in export dialog

* fix

* review fix

* dnt allow to edit file name when view mode

* Update src/components/ProjectName.tsx

Co-authored-by: David Luzar <luzar.david@gmail.com>

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-03-20 21:57:58 +05:30
80a61db72f fix: overflow in textinput in export dialog (#3284)
* fix: overflow in textinput in export dialog

* use width
2021-03-20 18:21:48 +05:30
9a13dd8836 fix: bail on noop updates for newElementWith (#3279) 2021-03-20 13:29:53 +01:00
cf6a5ff16b fix: state continuously updated when holding ctrl/cmd (#3283) 2021-03-20 13:28:28 +01:00
fa8c7abf50 fix: debounce.flush not invoked if lastArgs not defined (#3281) 2021-03-20 13:15:28 +01:00
c3ecbcb3ab feat: Allow host app to update title of drawing (#3273)
* Allow updating name on updateScene

* Revert "Allow updating name on updateScene"

This reverts commit 4e07a608d3.

* Make requested changes

* Make requested changes

* Remove customName from state

* Remove redundant if statement

* Add tests, update changelog and minor fixes

* remove eempty lines

* minor fixes

* no border and on hover no background change

* Give preference to name prop when initialData.appState.name is present and update specs

* minor fix

* Fix name input style in dark mode

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-03-20 16:08:03 +05:30
de99484a1f feat: Expose the API to calculate offsets and remove offsetTop and offsetLeft props (#3265)
* feat: Expose the API to calculate offsets and remove offsetTop and offsetLeft props

* update

* fix tests

* fix

* update readme and changelog

* fix

* better
2021-03-20 13:00:49 +05:30
add1785ace fix: allow copying text outside the component (#3275) 2021-03-19 18:36:23 +01:00
0e3eb3cc63 chore: fix npm to yarn in contributing.md and package.json (#3274)
* chore: fix npm to yarn in contributing.md

* chore: fix npm pack to yarn pack
2021-03-18 22:57:00 +05:30
c1379c3c10 fix: Revert 906faaf0 #3206 (#3269) 2021-03-17 14:43:58 +01:00
70c36e196c chore(deps): bump @types/socket.io-client from 1.4.35 to 1.4.36 (#3256)
Bumps [@types/socket.io-client](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/socket.io-client) from 1.4.35 to 1.4.36.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/socket.io-client)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-17 10:17:18 +02:00
9757f8e2e5 chore: Update translations from Crowdin (#3221) 2021-03-17 10:16:22 +02:00
052b73d95b refactor: Rename prop scrollToCenter and setScrollToCenter to scrollToContent and setScrollToContent respectively (#3261)
* refactor: Rename prop scrollToCenter and setScrollToCenter to scrollToContent and setScrollToContent respectively

* fix

* update changelog/readme

* fix
2021-03-16 23:02:17 +05:30
e90e56452f fix: stop preventing canvas pointerdown/tapend events (#3207) 2021-03-16 18:04:53 +01:00
edc62c550a feat: Export API's to export the drawing to canvas, svg and blob (#3258)
* feat: Export utilities from package/utils

* update

* fix

* collapsible

* Update README.md

* update change

* update

* fix

* Apply suggestions from code review

Co-authored-by: David Luzar <luzar.david@gmail.com>

* Apply suggestions from code review

Co-authored-by: David Luzar <luzar.david@gmail.com>

* update

* Apply suggestions from code review

Co-authored-by: David Luzar <luzar.david@gmail.com>
2021-03-16 22:21:56 +05:30
84a1863233 feat: Add theme prop (#3228)
* support appearance when updating scene data

* works!

* whoops, missed a prop

* hide appearance button when prop is not set

* cleanup

* fix export + rename prop to theme

* rename to showThemeBtn, hide via react instead of css

* adapt to new state name

* add tests and css selector to target the dark mode toggle

* updated changelog and readme

* fix markdown rendering in readme

* pr feedback
2021-03-16 00:03:46 +05:30
1f295955d0 chore: Add vercel & sentry as sponsors (#3202)
* add vercel & sentry as sponsors

* fix lint

* move images to `.github/assets`

* fix lint

* add Crowdin & change copy
2021-03-14 15:02:36 +01:00
8f45aa2924 fix: double scrollbar on modals (#3226) 2021-03-14 14:30:12 +01:00
b4c0bc6ace chore(deps): bump nanoid from 3.1.20 to 3.1.21 (#3252)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.20 to 3.1.21.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.20...3.1.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 07:33:03 +00:00
8fdc25ce10 chore(deps-dev): bump firebase-tools from 9.5.0 to 9.6.1 (#3254)
Bumps [firebase-tools](https://github.com/firebase/firebase-tools) from 9.5.0 to 9.6.1.
- [Release notes](https://github.com/firebase/firebase-tools/releases)
- [Commits](https://github.com/firebase/firebase-tools/compare/v9.5.0...v9.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 07:30:40 +00:00
efc2db3af1 chore(deps): bump react-dev-utils from 11.0.3 to 11.0.4 (#3255)
Bumps [react-dev-utils](https://github.com/facebook/create-react-app/tree/HEAD/packages/react-dev-utils) from 11.0.3 to 11.0.4.
- [Release notes](https://github.com/facebook/create-react-app/releases)
- [Changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG-1.x.md)
- [Commits](https://github.com/facebook/create-react-app/commits/HEAD/packages/react-dev-utils)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:24:05 +02:00
fa186c69cb chore(deps-dev): bump @babel/core from 7.13.8 to 7.13.10 in /src/packages/utils (#3248)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.8 to 7.13.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:23:39 +02:00
f2adda46e5 chore(deps-dev): bump ts-loader from 8.0.17 to 8.0.18 in /src/packages/utils (#3246)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.17 to 8.0.18.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.17...v8.0.18)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:23:33 +02:00
0786b472cc chore(deps-dev): bump @babel/preset-env from 7.13.9 to 7.13.10 in /src/packages/utils (#3251)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.9 to 7.13.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:23:25 +02:00
15ae64eb7a chore(deps-dev): bump ts-loader from 8.0.17 to 8.0.18 in /src/packages/excalidraw (#3247)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 8.0.17 to 8.0.18.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v8.0.17...v8.0.18)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:23:18 +02:00
f3b9bd7c1d chore(deps-dev): bump @babel/plugin-transform-runtime from 7.13.9 to 7.13.10 in /src/packages/utils (#3250)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:23:05 +02:00
eae25329f0 chore(deps-dev): bump css-loader from 5.1.1 to 5.1.2 in /src/packages/utils (#3249)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.1...v5.1.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:22:51 +02:00
99de8e79e2 chore(deps-dev): bump @babel/plugin-transform-runtime from 7.13.9 to 7.13.10 in /src/packages/excalidraw (#3245)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.9 to 7.13.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-plugin-transform-runtime)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:22:35 +02:00
adcfaf34de chore(deps-dev): bump @babel/core from 7.13.8 to 7.13.10 in /src/packages/excalidraw (#3242)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.8 to 7.13.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:21:57 +02:00
04ae836874 chore(deps-dev): bump css-loader from 5.1.1 to 5.1.2 in /src/packages/excalidraw (#3243)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.1...v5.1.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:21:40 +02:00
f6ffa7ed45 chore(deps-dev): bump @babel/preset-env from 7.13.9 to 7.13.10 in /src/packages/excalidraw (#3244)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.9 to 7.13.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-preset-env)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:21:27 +02:00
2964d36d9a chore(deps): bump @sentry/browser from 6.2.1 to 6.2.2 (#3253)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.2.1...6.2.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-14 09:20:12 +02:00
b9e70ec666 feat: Implement the Web Share Target API (#3230)
* Use the web share target API

* Make requested changes

* Remove line

* Add application/json back

* Add application/vnd.excalidraw+json

* Add 'POST' check back

* Make requested changes

* Update src/appState.ts

Co-authored-by: Thomas Steiner <tomac@google.com>

* Update test

* Override initializeScene

* Use Excalidraw MIME type

* Minor fixes

* More MIME type tweaks

* More permissive file open

* Be overpermissive in file open

Co-authored-by: Thomas Steiner <tomac@google.com>
Co-authored-by: tomayac <steiner.thomas@gmail.com>
2021-03-13 22:42:54 +01:00
f1daff2437 refactor: Rename appearance to theme (#3237)
* refactor: Rename appearance to theme

* fix

* update changelog

* rename theme_dark to theme--dark

* add about migration
2021-03-13 18:58:06 +05:30
91c8b6ecbf feat: support libraryReturnUrl when installing libraries (#3227)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2021-03-13 12:35:35 +01:00
47c26cd4cf docs: release patch version 0.4.3 (#3236) 2021-03-13 01:29:54 +05:30
336 changed files with 46828 additions and 18353 deletions

5
.env
View File

@ -1,5 +0,0 @@
REACT_APP_BACKEND_V1_GET_URL=https://json.excalidraw.com/api/v1/
REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
REACT_APP_SOCKET_SERVER_URL=https://portal.excalidraw.com
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}'

8
.env.development Normal file
View File

@ -0,0 +1,8 @@
REACT_APP_BACKEND_V2_GET_URL=https://json-dev.excalidraw.com/api/v2/
REACT_APP_BACKEND_V2_POST_URL=https://json-dev.excalidraw.com/api/v2/post/
REACT_APP_LIBRARY_URL=https://libraries.excalidraw.com
REACT_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries
REACT_APP_SOCKET_SERVER_URL=http://localhost:3000
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","authDomain":"excalidraw-oss-dev.firebaseapp.com","projectId":"excalidraw-oss-dev","storageBucket":"excalidraw-oss-dev.appspot.com","messagingSenderId":"664559512677","appId":"1:664559512677:web:a385181f2928d328a7aa8c"}'

View File

@ -1 +1,11 @@
REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
REACT_APP_LIBRARY_URL=https://libraries.excalidraw.com
REACT_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries
REACT_APP_SOCKET_SERVER_URL=https://oss-collab-us1.excalidraw.com
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}'
# production-only vars
REACT_APP_GOOGLE_ANALYTICS_ID=UA-387204-13

View File

@ -5,3 +5,4 @@ package-lock.json
firebase/
dist/
public/workbox
src/packages/excalidraw/types

View File

@ -1,6 +1,7 @@
{
"extends": ["@excalidraw/eslint-config", "react-app"],
"rules": {
"import/no-anonymous-default-export": "off"
"import/no-anonymous-default-export": "off",
"no-restricted-globals": "off"
}
}

6
.github/assets/crowdin.svg vendored Normal file
View File

@ -0,0 +1,6 @@
<svg height="50" viewBox="0 0 257 50" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">
<path fill="#fff" d="M-7.977-9.253h288.95v78.13H-7.977z" />
<path d="M67.626 32.315c-1.34 0-2.207 0-2.207-1.025 0-.236.079-.551.236-.946l4.02-8.907h12.929c1.34 0 2.128-.08 2.128.946 0 .315-.078.63-.236.946l-.788 1.734h5.439l1.104-2.444c.157-.394.157-.71.157-1.025 0-2.207-2.365-3.31-4.257-3.31H65.655l-5.754 12.691c-.158.394-.158.71-.158 1.025 0 2.365 1.97 3.547 4.73 3.547h20.26l1.26-3.232H67.627zm42.727-14.11H95.059l-6.937 17.342h5.518l5.519-14.032h8.435c1.34 0 2.05-.157 2.05.868 0 .315-.08.63-.237.946l-.789 1.734h5.518l1.104-2.444c.158-.394.158-.71.158-1.025 0-1.025-.552-1.892-1.734-2.522-.946-.473-2.208-.868-3.311-.868zm30.35 0h-21.285l-5.754 12.691c-.158.316-.158.63-.158 1.025 0 1.97 1.419 3.547 3.232 3.547h21.52l5.834-13.007c.158-.394.158-.71.158-1.024 0-2.05-1.734-3.233-3.547-3.233zm-6.701 14.19h-12.85c-1.34 0-1.97-.159-1.97-1.183 0-.316.079-.631.236-.946l4.178-8.908h12.929c1.26 0 1.891-.08 1.891.946 0 .315-.078.63-.236 1.025l-4.178 9.065zm13.953 3.152h28.695l7.41-17.264h-5.676l-6.149 14.032h-9.223l6.149-14.11h-5.676l-6.386 14.031h-6.306c-1.34 0-2.05-.157-2.05-1.182 0-.315.08-.63.237-.946l5.282-11.982h-5.519l-5.518 12.455c-1.103 3.39 2.207 4.966 4.73 4.966zm67.874-23.649l-5.913 1.577-1.97 4.73h-14.584c-3.548 0-6.7 1.576-8.278 4.73l-3.941 9.46c-.788 1.576.63 3.152 3.31 3.152h21.128l10.248-23.649zm-27.591 20.496c-1.183 0-1.735-.788-1.577-1.577l3.469-7.567c.788-1.813 2.68-1.892 4.414-1.892h11.825l-4.73 11.036h-13.401zm26.802 3.153l7.49-17.737-6.307 1.183-7.095 16.554h5.912zm8.435-19.944l1.656-3.705-6.228 1.261-1.577 3.705 6.15-1.26zm22.23 2.601h-20.417l-7.094 17.343h5.518l5.518-14.19h13.48c1.34 0 2.05-.078 2.05 1.026 0 .315-.08.63-.237.946l-5.518 12.297h5.518l5.834-13.007c.157-.315.157-.63.157-1.025 0-1.025-.552-1.892-1.734-2.522-.867-.473-1.892-.868-3.074-.868zm-192.82.868c-8.672-1.025-16.476.71-17.58 6.148 0 .237-.157 1.262-.157 1.42l1.419.157v2.207l-1.34-.157c.551 5.597 3.626 7.252 6.858 7.331h.236c1.42.079 2.917-.237 4.178-.788.08 0 .08-.08.08-.08v-.157c0-.079-.08-.079-.08-.157-.078 0-.078-.08-.157-.08-2.996.395-5.755-2.049-5.755-7.015 0-6.228 4.888-8.514 12.298-8.514.236.158.315-.237 0-.315zM36.803 30.344c.788 0 1.498.158 2.207.237.237 1.655 1.025 3.232 2.208 4.336-1.183-.158-2.208-.71-3.075-1.498a6.051 6.051 0 01-1.34-3.075zm2.68-5.439c0 .237-.157.552-.236.946h-1.025c-.552 0-1.025-.079-1.576-.158v-.157c.63-3.39 4.02-4.73 7.252-5.36a7.997 7.997 0 00-2.76 1.812c-.787.868-1.34 1.813-1.655 2.917z" fill="#2e3340" fill-rule="nonzero" />
<path d="M56.274 14.105c-6.543-1.813-34.055-4.02-34.055 11.273.946.158 1.577.315 2.05.394-.079 1.183 0 2.444 0 3.626l-2.444-.394c0 8.83 6.464 11.667 11.588 11.667.868 0 1.656-.078 2.523-.157 2.128-.237 4.178-.867 5.991-1.892.079 0 .079-.08.079-.08v-.157c0-.079-.079-.079-.079-.157-.079 0-.079-.08-.157-.08-4.336.868-10.17-.315-10.17-10.563 0-13.637 19.156-12.77 24.753-13.007.08 0 .08-.079.08-.079v-.157c0-.08 0-.08-.08-.158 0-.079 0-.079-.079-.079zM33.414 39.41a9.362 9.362 0 01-6.78-2.286c-1.892-1.656-3.074-3.942-3.31-6.385 1.655.236 3.704.394 5.438.473a9.43 9.43 0 001.577 4.808c.946 1.42 2.207 2.602 3.705 3.39h-.63zM28.92 24.984l-2.601-.237-2.602-.315c0-7.962 12.77-11.036 18.683-10.484-5.912 1.34-13.086 4.099-13.48 11.036z" fill="#2e3340" fill-rule="nonzero" />
<path d="M59.664 9.533c-7.962-2.68-17.027-4.02-25.462-3.941-12.22 0-27.67 3.626-28.064 16.081l3.31.788c-.393 1.577-.393 4.81-.393 4.81s-1.892-.553-2.917-.79c0 14.821 8.671 18.526 17.027 18.526 3.39 0 6.701-.552 9.854-1.734.08 0 .08-.08.08-.08v-.157c0-.079-.08-.079-.08-.157h-.157c-2.602 0-4.651.867-8.75-2.05-7.963-5.597-7.017-20.102 2.128-26.408 9.46-6.701 29.798-4.573 33.267-4.415h.157s.079 0 .079-.079v-.236l-.079-.158zm-36.42 34.292c-9.932 0-14.978-5.36-15.45-15.609 2.68.71 5.202 1.34 7.961 1.734-.157 4.02 1.262 7.962 4.02 11.037a12.488 12.488 0 005.046 2.916l-1.577-.078zM45.632 7.956c-12.06 0-26.014 1.42-28.773 14.584 0 0-7.41-1.182-9.066-1.576C9.843 4.409 38.38 5.67 49.89 7.956h-4.257z" fill="#2e3340" fill-rule="nonzero" />
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

9
.github/assets/sentry.svg vendored Normal file
View File

@ -0,0 +1,9 @@
<svg class="__sntry__ css-15xgryy e10nushx5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 222 66" height="50" style="background-color: rgb(255, 255, 255);">
<defs>
<style type="text/css">
@media (prefers-color-scheme: dark) {svg.__sntry__ { background-color: #584674 !important; }path.__sntry__ { fill: #ffffff !important; }}
</style>
</defs>
<path d="M29,2.26a4.67,4.67,0,0,0-8,0L14.42,13.53A32.21,32.21,0,0,1,32.17,40.19H27.55A27.68,27.68,0,0,0,12.09,17.47L6,28a15.92,15.92,0,0,1,9.23,12.17H4.62A.76.76,0,0,1,4,39.06l2.94-5a10.74,10.74,0,0,0-3.36-1.9l-2.91,5a4.54,4.54,0,0,0,1.69,6.24A4.66,4.66,0,0,0,4.62,44H19.15a19.4,19.4,0,0,0-8-17.31l2.31-4A23.87,23.87,0,0,1,23.76,44H36.07a35.88,35.88,0,0,0-16.41-31.8l4.67-8a.77.77,0,0,1,1.05-.27c.53.29,20.29,34.77,20.66,35.17a.76.76,0,0,1-.68,1.13H40.6q.09,1.91,0,3.81h4.78A4.59,4.59,0,0,0,50,39.43a4.49,4.49,0,0,0-.62-2.28Z M124.32,28.28,109.56,9.22h-3.68V34.77h3.73V15.19l15.18,19.58h3.26V9.22h-3.73ZM87.15,23.54h13.23V20.22H87.14V12.53h14.93V9.21H83.34V34.77h18.92V31.45H87.14ZM71.59,20.3h0C66.44,19.06,65,18.08,65,15.7c0-2.14,1.89-3.59,4.71-3.59a12.06,12.06,0,0,1,7.07,2.55l2-2.83a14.1,14.1,0,0,0-9-3c-5.06,0-8.59,3-8.59,7.27,0,4.6,3,6.19,8.46,7.52C74.51,24.74,76,25.78,76,28.11s-2,3.77-5.09,3.77a12.34,12.34,0,0,1-8.3-3.26l-2.25,2.69a15.94,15.94,0,0,0,10.42,3.85c5.48,0,9-2.95,9-7.51C79.75,23.79,77.47,21.72,71.59,20.3ZM195.7,9.22l-7.69,12-7.64-12h-4.46L186,24.67V34.78h3.84V24.55L200,9.22Zm-64.63,3.46h8.37v22.1h3.84V12.68h8.37V9.22H131.08ZM169.41,24.8c3.86-1.07,6-3.77,6-7.63,0-4.91-3.59-8-9.38-8H154.67V34.76h3.8V25.58h6.45l6.48,9.2h4.44l-7-9.82Zm-10.95-2.5V12.6h7.17c3.74,0,5.88,1.77,5.88,4.84s-2.29,4.86-5.84,4.86Z" transform="translate(11, 11)" fill="#362d59" class="__sntry__">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

3
.github/assets/vercel.svg vendored Normal file
View File

@ -0,0 +1,3 @@
<svg height="50" viewBox="0 0 164 50" xmlns="http://www.w3.org/2000/svg" style="background:#fff" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">
<path d="M78.21 15.587c-5.672 0-9.762 3.864-9.762 9.661s4.604 9.66 10.276 9.66c3.427 0 6.448-1.416 8.319-3.805l-3.931-2.372c-1.038 1.186-2.615 1.879-4.388 1.879-2.461 0-4.552-1.342-5.328-3.489h14.397c.113-.601.18-1.223.18-1.879 0-5.79-4.09-9.655-9.763-9.655zm-4.86 7.783c.642-2.142 2.399-3.489 4.855-3.489 2.461 0 4.219 1.347 4.855 3.489h-9.71zm60.187-7.783c-5.673 0-9.763 3.864-9.763 9.661s4.604 9.66 10.276 9.66c3.427 0 6.449-1.416 8.319-3.805l-3.931-2.372c-1.038 1.186-2.615 1.879-4.388 1.879-2.461 0-4.552-1.342-5.328-3.489h14.397c.113-.601.18-1.223.18-1.879 0-5.79-4.09-9.655-9.762-9.655zm-4.856 7.783c.642-2.142 2.4-3.489 4.856-3.489 2.46 0 4.218 1.347 4.855 3.489h-9.711zm-20.054 1.878c0 3.22 2.015 5.367 5.139 5.367 2.116 0 3.704-1.003 4.52-2.64l3.947 2.378c-1.634 2.843-4.696 4.556-8.467 4.556-5.678 0-9.763-3.864-9.763-9.661s4.09-9.66 9.763-9.66c3.77 0 6.828 1.712 8.467 4.556l-3.946 2.377c-.817-1.637-2.405-2.64-4.521-2.64-3.12 0-5.139 2.147-5.139 5.367zm42.378-15.565v24.69h-4.624V9.682h4.624zM24.73 7l18.985 34.35H5.744L24.73 7zm47.465 2.683L57.956 35.446 43.72 9.683h5.338l8.9 16.102 8.898-16.102h5.339zm30.268 6.44v5.202a5.634 5.634 0 00-1.644-.263c-2.985 0-5.138 2.147-5.138 5.367v7.943h-4.624V16.124h4.624v4.938c0-2.727 3.036-4.938 6.782-4.938z" fill-rule="nonzero" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -10,6 +10,7 @@ updates:
- lipis
assignees:
- lipis
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: /src/packages/excalidraw/
@ -21,6 +22,7 @@ updates:
- ad1992
assignees:
- ad1992
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: /src/packages/utils/
@ -32,3 +34,4 @@ updates:
- ad1992
assignees:
- ad1992
open-pull-requests-limit: 20

View File

@ -0,0 +1,26 @@
name: Auto release @excalidraw/excalidraw-next
on:
push:
branches:
- master
jobs:
Auto-release-excalidraw-next:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Setup Node.js 14.x
uses: actions/setup-node@v2
with:
node-version: 14.x
- name: Set up publish access
run: |
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Auto release
run: |
yarn autorelease

View File

@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: docker/build-push-action@v1
- uses: docker/build-push-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

3
.gitignore vendored
View File

@ -5,9 +5,11 @@
.env.test.local
.envrc
.eslintcache
.history
.idea
.vercel
.vscode
.yarn
*.log
*.tgz
build
@ -20,3 +22,4 @@ package-lock.json
static
yarn-debug.log*
yarn-error.log*
src/packages/excalidraw/types

2
.husky/pre-commit Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
yarn lint-staged

View File

@ -5,7 +5,7 @@
### Option 1 - Manual
1. Fork and clone the repo
1. Run `npm install` to install dependencies
1. Run `yarn` to install dependencies
1. Create a branch for your PR with `git checkout -b your-branch-name`
> To keep `master` branch pointing to remote repository and make pull requests from branches on your fork. To do this, run:

View File

@ -10,7 +10,7 @@ ARG NODE_ENV=production
COPY . .
RUN yarn build:app:docker
FROM nginx:1.17-alpine
FROM nginx:1.21-alpine
COPY --from=build /opt/node_app/build /usr/share/nginx/html

View File

@ -28,6 +28,10 @@ If you like the project, you can become a sponsor at [Open Collective](https://o
<a href="https://opencollective.com/excalidraw#category-CONTRIBUTE" target="_blank"><img src="https://opencollective.com/excalidraw/tiers/backers.svg?avatarHeight=32"/></a>
Last but not least, we're thankful to these companies for offering their services for free:
[![Vercel](./.github/assets/vercel.svg)](https://vercel.com) [![Sentry](./.github/assets/sentry.svg)](https://sentry.io) [![Crowdin](./.github/assets/crowdin.svg)](https://crowdin.com)
## Documentation
### Shortcuts
@ -66,6 +70,8 @@ The first set of digits is the room. This is visible from the server thats go
The second set of digits is the encryption key. The Excalidraw server doesnt know about it. This is what all the participants use to encrypt/decrypt the messages.
> Note: Please ensure that the encryption key is 22 characters long.
## Shape libraries
Find a growing list of libraries containing assets for your drawings at [libraries.excalidraw.com](https://libraries.excalidraw.com).
@ -89,7 +95,7 @@ These instructions will get you a copy of the project up and running on your loc
#### Requirements
- [Node.js](https://nodejs.org/en/)
- [Yarn](https://yarnpkg.com/getting-started/install)
- [Yarn](https://yarnpkg.com/getting-started/install) (v1 or v2.4.2+)
- [Git](https://git-scm.com/downloads)
#### Clone the repo
@ -98,6 +104,20 @@ These instructions will get you a copy of the project up and running on your loc
git clone https://github.com/excalidraw/excalidraw.git
```
#### Install the dependencies
```bash
yarn
```
#### Start the server
```bash
yarn start
```
Now you can open [http://localhost:3000](http://localhost:3000) and start coding in your favorite code editor.
#### Commands
| Command | Description |

View File

@ -2,5 +2,8 @@
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}

View File

@ -0,0 +1,12 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{migrations} {
match /{scenes}/{scene} {
allow get, write: if true;
// redundant, but let's be explicit'
allow list: if false;
}
}
}
}

View File

@ -19,60 +19,67 @@
]
},
"dependencies": {
"@sentry/browser": "6.2.1",
"@sentry/integrations": "6.2.1",
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
"@types/jest": "26.0.20",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.1",
"@types/socket.io-client": "1.4.35",
"browser-fs-access": "0.14.1",
"@sentry/browser": "6.2.5",
"@sentry/integrations": "6.2.5",
"@testing-library/jest-dom": "5.16.1",
"@testing-library/react": "12.1.2",
"@tldraw/vec": "1.4.0",
"@types/jest": "27.0.3",
"@types/pica": "5.1.3",
"@types/react": "17.0.38",
"@types/react-dom": "17.0.11",
"@types/socket.io-client": "1.4.36",
"browser-fs-access": "0.23.0",
"clsx": "1.1.1",
"firebase": "8.2.10",
"i18next-browser-languagedetector": "6.0.1",
"fake-indexeddb": "3.1.7",
"firebase": "8.3.3",
"i18next-browser-languagedetector": "6.1.2",
"idb-keyval": "6.0.3",
"image-blob-reduce": "3.0.1",
"lodash.throttle": "4.1.1",
"nanoid": "3.1.20",
"open-color": "1.8.0",
"nanoid": "3.1.30",
"open-color": "1.9.1",
"pako": "1.0.11",
"perfect-freehand": "1.0.16",
"png-chunk-text": "1.0.0",
"png-chunks-encode": "1.0.0",
"png-chunks-extract": "1.0.0",
"points-on-curve": "0.2.0",
"pwacompat": "2.0.17",
"react": "17.0.1",
"react-dom": "17.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-scripts": "4.0.3",
"roughjs": "4.3.1",
"sass": "1.32.8",
"roughjs": "4.5.2",
"sass": "1.45.2",
"socket.io-client": "2.3.1",
"typescript": "4.2.3"
"typescript": "4.5.4"
},
"devDependencies": {
"@excalidraw/eslint-config": "1.0.0",
"@excalidraw/prettier-config": "1.0.2",
"@types/chai": "4.3.0",
"@types/lodash.throttle": "4.1.6",
"@types/pako": "1.0.1",
"@types/resize-observer-browser": "0.1.5",
"eslint-config-prettier": "8.1.0",
"@types/pako": "1.0.3",
"@types/resize-observer-browser": "0.1.6",
"chai": "4.3.4",
"dotenv": "10.0.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-prettier": "3.3.1",
"firebase-tools": "9.5.0",
"husky": "4.3.8",
"firebase-tools": "9.23.0",
"husky": "7.0.4",
"jest-canvas-mock": "2.3.1",
"lint-staged": "10.5.4",
"lint-staged": "12.1.4",
"pepjs": "0.5.3",
"prettier": "2.2.1",
"prettier": "2.5.1",
"rewire": "5.0.0"
},
"resolutions": {
"@typescript-eslint/typescript-estree": "5.3.0"
},
"engines": {
"node": ">=14.0.0"
},
"homepage": ".",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!(roughjs|points-on-curve|path-data-parser|points-on-path|browser-fs-access)/)"
@ -94,6 +101,7 @@
"fix": "yarn fix:other && yarn fix:code",
"locales-coverage": "node scripts/build-locales-coverage.js",
"locales-coverage:description": "node scripts/locales-coverage-description.js",
"prepare": "husky install",
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
"start": "react-scripts start",
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watchAll=false",
@ -103,6 +111,7 @@
"test:other": "yarn prettier --list-different",
"test:typecheck": "tsc",
"test:update": "yarn test:app --updateSnapshot --watchAll=false",
"test": "yarn test:app"
"test": "yarn test:app",
"autorelease": "node scripts/autorelease.js"
}
}

Binary file not shown.

View File

@ -51,8 +51,7 @@
name="twitter:description"
content="Excalidraw is a whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them."
/>
<!-- OG tags require absolute url for images -->
<meta name="twitter:image" content="https://excalidraw.com/og-image.png" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<!-- Excalidraw version -->
@ -88,6 +87,8 @@
<link rel="stylesheet" href="fonts.css" type="text/css" />
<script>
window.EXCALIDRAW_ASSET_PATH = "/";
// setting this so that libraries installation reuses this window tab.
window.name = "_excalidraw";
</script>
<% if (process.env.REACT_APP_GOOGLE_ANALYTICS_ID) { %>
<script
@ -106,16 +107,17 @@
<!-- FIXME: remove this when we update CRA (fix SW caching) -->
<style>
body {
body,
html {
margin: 0;
--ui-font: system-ui, BlinkMacSystemFont, -apple-system, Segoe UI,
Roboto, Helvetica, Arial, sans-serif;
font-family: var(--ui-font);
-webkit-text-size-adjust: 100%;
-webkit-user-select: none;
user-select: none;
width: 100vw;
height: 100vh;
width: 100%;
height: 100%;
overflow: hidden;
}
.visually-hidden {
@ -125,6 +127,7 @@
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
user-select: none;
}
.LoadingMessage {
@ -147,6 +150,24 @@
color: var(--popup-text-color);
font-size: 1.3em;
}
#root {
height: 100%;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
@media screen and (min-width: 1200px) {
-webkit-touch-callout: default;
-webkit-user-select: auto;
-khtml-user-select: auto;
-moz-user-select: auto;
-ms-user-select: auto;
user-select: auto;
}
}
</style>
</head>

View File

@ -26,5 +26,49 @@
}
}
],
"capture_links": "new_client"
"share_target": {
"action": "/web-share-target",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"files": [
{
"name": "file",
"accept": ["application/vnd.excalidraw+json", "application/json", ".excalidraw"]
}
]
}
},
"screenshots": [
{
"src": "/screenshots/virtual-whiteboard.png",
"type": "image/png",
"sizes": "462x945"
},
{
"src": "/screenshots/wireframe.png",
"type": "image/png",
"sizes": "462x945"
},
{
"src": "/screenshots/illustration.png",
"type": "image/png",
"sizes": "462x945"
},
{
"src": "/screenshots/shapes.png",
"type": "image/png",
"sizes": "462x945"
},
{
"src": "/screenshots/collaboration.png",
"type": "image/png",
"sizes": "462x945"
},
{
"src": "/screenshots/export.png",
"type": "image/png",
"sizes": "462x945"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

52
scripts/autorelease.js Normal file
View File

@ -0,0 +1,52 @@
const fs = require("fs");
const { exec, execSync } = require("child_process");
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
const excalidrawPackage = `${excalidrawDir}/package.json`;
const pkg = require(excalidrawPackage);
const getShortCommitHash = () => {
return execSync("git rev-parse --short HEAD").toString().trim();
};
const publish = () => {
try {
execSync(`yarn --frozen-lockfile`);
execSync(`yarn --frozen-lockfile`, { cwd: excalidrawDir });
execSync(`yarn run build:umd`, { cwd: excalidrawDir });
execSync(`yarn --cwd ${excalidrawDir} publish`);
} catch (error) {
console.error(error);
}
};
// get files changed between prev and head commit
exec(`git diff --name-only HEAD^ HEAD`, async (error, stdout, stderr) => {
if (error || stderr) {
console.error(error);
process.exit(1);
}
const changedFiles = stdout.trim().split("\n");
const filesToIgnoreRegex = /src\/excalidraw-app|packages\/utils/;
const excalidrawPackageFiles = changedFiles.filter((file) => {
return (
(file.indexOf("src") >= 0 || file.indexOf("package.json")) >= 0 &&
!filesToIgnoreRegex.test(file)
);
});
if (!excalidrawPackageFiles.length) {
process.exit(0);
}
// update package.json
pkg.version = `${pkg.version}-${getShortCommitHash()}`;
pkg.name = "@excalidraw/excalidraw-next";
fs.writeFileSync(excalidrawPackage, JSON.stringify(pkg, null, 2), "utf8");
// update readme
const data = fs.readFileSync(`${excalidrawDir}/README_NEXT.md`, "utf8");
fs.writeFileSync(`${excalidrawDir}/README.md`, data, "utf8");
publish();
});

View File

@ -1,11 +1,16 @@
const { readdirSync, writeFileSync } = require("fs");
const files = readdirSync(`${__dirname}/../src/locales`);
const flatten = (object) =>
Object.keys(object).reduce(
(initial, current) => ({ ...initial, ...object[current] }),
{},
);
const flatten = (object = {}, result = {}, extraKey = "") => {
for (const key in object) {
if (typeof object[key] !== "object") {
result[extraKey + key] = object[key];
} else {
flatten(object[key], result, `${extraKey}${key}.`);
}
}
return result;
};
const locales = files.filter(
(file) => file !== "README.md" && file !== "percentages.json",
@ -19,10 +24,8 @@ for (let index = 0; index < locales.length; index++) {
const allKeys = Object.keys(data);
const translatedKeys = allKeys.filter((item) => data[item] !== "");
const percentage = (100 * translatedKeys.length) / allKeys.length;
percentages[currentLocale.replace(".json", "")] = parseInt(percentage);
const percentage = Math.floor((100 * translatedKeys.length) / allKeys.length);
percentages[currentLocale.replace(".json", "")] = percentage;
}
writeFileSync(

View File

@ -5,7 +5,9 @@ const THRESSHOLD = 85;
const crowdinMap = {
"ar-SA": "en-ar",
"bg-BG": "en-bg",
"bn-BD": "en-bn",
"ca-ES": "en-ca",
"da-DK": "en-da",
"de-DE": "en-de",
"el-GR": "en-el",
"es-ES": "en-es",
@ -31,18 +33,27 @@ const crowdinMap = {
"pt-PT": "en-pt",
"ro-RO": "en-ro",
"ru-RU": "en-ru",
"si-LK": "en-silk",
"sk-SK": "en-sk",
"sv-SE": "en-sv",
"ta-IN": "en-ta",
"tr-TR": "en-tr",
"uk-UA": "en-uk",
"zh-CN": "en-zhcn",
"zh-HK": "en-zhhk",
"zh-TW": "en-zhtw",
"lv-LV": "en-lv",
"cs-CZ": "en-cs",
"kk-KZ": "en-kk",
};
const flags = {
"ar-SA": "🇸🇦",
"bg-BG": "🇧🇬",
"bn-BD": "🇧🇩",
"ca-ES": "🏳",
"cs-CZ": "🇨🇿",
"da-DK": "🇩🇰",
"de-DE": "🇩🇪",
"el-GR": "🇬🇷",
"es-ES": "🇪🇸",
@ -56,7 +67,9 @@ const flags = {
"it-IT": "🇮🇹",
"ja-JP": "🇯🇵",
"kab-KAB": "🏳",
"kk-KZ": "🇰🇿",
"ko-KR": "🇰🇷",
"lv-LV": "🇱🇻",
"my-MM": "🇲🇲",
"nb-NO": "🇳🇴",
"nl-NL": "🇳🇱",
@ -68,18 +81,24 @@ const flags = {
"pt-PT": "🇵🇹",
"ro-RO": "🇷🇴",
"ru-RU": "🇷🇺",
"si-LK": "🇱🇰",
"sk-SK": "🇸🇰",
"sv-SE": "🇸🇪",
"ta-IN": "🇮🇳",
"tr-TR": "🇹🇷",
"uk-UA": "🇺🇦",
"zh-CN": "🇨🇳",
"zh-HK": "🇭🇰",
"zh-TW": "🇹🇼",
};
const languages = {
"ar-SA": "العربية",
"bg-BG": "Български",
"bn-BD": "Bengali",
"ca-ES": "Català",
"cs-CZ": "Česky",
"da-DK": "Dansk",
"de-DE": "Deutsch",
"el-GR": "Ελληνικά",
"es-ES": "Español",
@ -93,7 +112,9 @@ const languages = {
"it-IT": "Italiano",
"ja-JP": "日本語",
"kab-KAB": "Taqbaylit",
"kk-KZ": "Қазақ тілі",
"ko-KR": "한국어",
"lv-LV": "Latviešu",
"my-MM": "Burmese",
"nb-NO": "Norsk bokmål",
"nl-NL": "Nederlands",
@ -105,11 +126,14 @@ const languages = {
"pt-PT": "Português",
"ro-RO": "Română",
"ru-RU": "Русский",
"si-LK": "සිංහල",
"sk-SK": "Slovenčina",
"sv-SE": "Svenska",
"ta-IN": "Tamil",
"tr-TR": "Türkçe",
"uk-UA": "Українська",
"zh-CN": "简体中文",
"zh-HK": "繁體中文 (香港)",
"zh-TW": "繁體中文",
};

39
scripts/release.js Normal file
View File

@ -0,0 +1,39 @@
const fs = require("fs");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const updateReadme = require("./updateReadme");
const updateChangelog = require("./updateChangelog");
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
const excalidrawPackage = `${excalidrawDir}/package.json`;
const updatePackageVersion = (nextVersion) => {
const pkg = require(excalidrawPackage);
pkg.version = nextVersion;
const content = `${JSON.stringify(pkg, null, 2)}\n`;
fs.writeFileSync(excalidrawPackage, content, "utf-8");
};
const release = async (nextVersion) => {
try {
updateReadme();
await updateChangelog(nextVersion);
updatePackageVersion(nextVersion);
await exec(`git add -u`);
await exec(
`git commit -m "docs: release @excalidraw/excalidraw@${nextVersion} 🎉"`,
);
/* eslint-disable no-console */
console.log("Done!");
} catch (error) {
console.error(error);
process.exit(1);
}
};
const nextVersion = process.argv.slice(2)[0];
if (!nextVersion) {
console.error("Pass the next version to release!");
process.exit(1);
}
release(nextVersion);

View File

@ -0,0 +1,97 @@
const fs = require("fs");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
const excalidrawPackage = `${excalidrawDir}/package.json`;
const pkg = require(excalidrawPackage);
const lastVersion = pkg.version;
const existingChangeLog = fs.readFileSync(
`${excalidrawDir}/CHANGELOG.md`,
"utf8",
);
const supportedTypes = ["feat", "fix", "style", "refactor", "perf", "build"];
const headerForType = {
feat: "Features",
fix: "Fixes",
style: "Styles",
refactor: " Refactor",
perf: "Performance",
build: "Build",
};
const getCommitHashForLastVersion = async () => {
try {
const commitMessage = `"release @excalidraw/excalidraw@${lastVersion}"`;
const { stdout } = await exec(
`git log --format=format:"%H" --grep=${commitMessage}`,
);
return stdout;
} catch (error) {
console.error(error);
}
};
const getLibraryCommitsSinceLastRelease = async () => {
const commitHash = await getCommitHashForLastVersion();
const { stdout } = await exec(
`git log --pretty=format:%s ${commitHash}...master`,
);
const commitsSinceLastRelease = stdout.split("\n");
const commitList = {};
supportedTypes.forEach((type) => {
commitList[type] = [];
});
commitsSinceLastRelease.forEach((commit) => {
const indexOfColon = commit.indexOf(":");
const type = commit.slice(0, indexOfColon);
if (!supportedTypes.includes(type)) {
return;
}
const messageWithoutType = commit.slice(indexOfColon + 1).trim();
const messageWithCapitalizeFirst =
messageWithoutType.charAt(0).toUpperCase() + messageWithoutType.slice(1);
const prNumber = commit.match(/\(#([0-9]*)\)/)[1];
// return if the changelog already contains the pr number which would happen for package updates
if (existingChangeLog.includes(prNumber)) {
return;
}
const prMarkdown = `[#${prNumber}](https://github.com/excalidraw/excalidraw/pull/${prNumber})`;
const messageWithPRLink = messageWithCapitalizeFirst.replace(
/\(#[0-9]*\)/,
prMarkdown,
);
commitList[type].push(messageWithPRLink);
});
return commitList;
};
const updateChangelog = async (nextVersion) => {
const commitList = await getLibraryCommitsSinceLastRelease();
let changelogForLibrary =
"## Excalidraw Library\n\n**_This section lists the updates made to the excalidraw library and will not affect the integration._**\n\n";
supportedTypes.forEach((type) => {
if (commitList[type].length) {
changelogForLibrary += `### ${headerForType[type]}\n\n`;
const commits = commitList[type];
commits.forEach((commit) => {
changelogForLibrary += `- ${commit}\n\n`;
});
}
});
changelogForLibrary += "---\n";
const lastVersionIndex = existingChangeLog.indexOf(`## ${lastVersion}`);
let updatedContent =
existingChangeLog.slice(0, lastVersionIndex) +
changelogForLibrary +
existingChangeLog.slice(lastVersionIndex);
const currentDate = new Date().toISOString().slice(0, 10);
const newVersion = `## ${nextVersion} (${currentDate})`;
updatedContent = updatedContent.replace(`## Unreleased`, newVersion);
fs.writeFileSync(`${excalidrawDir}/CHANGELOG.md`, updatedContent, "utf8");
};
module.exports = updateChangelog;

27
scripts/updateReadme.js Normal file
View File

@ -0,0 +1,27 @@
const fs = require("fs");
const updateReadme = () => {
const excalidrawDir = `${__dirname}/../src/packages/excalidraw`;
let data = fs.readFileSync(`${excalidrawDir}/README_NEXT.md`, "utf8");
// remove note for unstable release
data = data.replace(
/<!-- unstable-readme-start-->[\s\S]*?<!-- unstable-readme-end-->/,
"",
);
// replace "excalidraw-next" with "excalidraw"
data = data.replace(/excalidraw-next/g, "excalidraw");
data = data.trim();
const demoIndex = data.indexOf("### Demo");
const excalidrawNextNote =
"#### Note\n\n**If you don't want to wait for the next stable release and try out the unreleased changes you can use [@excalidraw/excalidraw-next](https://www.npmjs.com/package/@excalidraw/excalidraw-next).**\n\n";
// Add excalidraw next note to try out for unreleased changes
data = data.slice(0, demoIndex) + excalidrawNextNote + data.slice(demoIndex);
// update readme
fs.writeFileSync(`${excalidrawDir}/README.md`, data, "utf8");
};
module.exports = updateReadme;

View File

@ -2,20 +2,58 @@ import { register } from "./register";
import { getSelectedElements } from "../scene";
import { getNonDeletedElements } from "../element";
import { deepCopyElement } from "../element/newElement";
import { Library } from "../data/library";
import { randomId } from "../random";
import { t } from "../i18n";
export const actionAddToLibrary = register({
name: "addToLibrary",
perform: (elements, appState) => {
perform: (elements, appState, _, app) => {
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
true,
);
if (selectedElements.some((element) => element.type === "image")) {
return {
commitToHistory: false,
appState: {
...appState,
errorMessage: "Support for adding images to the library coming soon!",
},
};
}
Library.loadLibrary().then((items) => {
Library.saveLibrary([...items, selectedElements.map(deepCopyElement)]);
});
return false;
return app.library
.loadLibrary()
.then((items) => {
return app.library.saveLibrary([
{
id: randomId(),
status: "unpublished",
elements: selectedElements.map(deepCopyElement),
created: Date.now(),
},
...items,
]);
})
.then(() => {
return {
commitToHistory: false,
appState: {
...appState,
toastMessage: t("toast.addedToLibrary"),
},
};
})
.catch((error) => {
return {
commitToHistory: false,
appState: {
...appState,
errorMessage: error.message,
},
};
});
},
contextItemLabel: "labels.addToLibrary",
});

View File

@ -1,4 +1,3 @@
import React from "react";
import { alignElements, Alignment } from "../align";
import {
AlignBottomIcon,
@ -9,13 +8,13 @@ import {
CenterVerticallyIcon,
} from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { getElementMap, getNonDeletedElements } from "../element";
import { getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { KEYS } from "../keys";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { AppState } from "../types";
import { getShortcutKey } from "../utils";
import { arrayToMap, getShortcutKey } from "../utils";
import { register } from "./register";
const enableActionGroup = (
@ -35,9 +34,11 @@ const alignSelectedElements = (
const updatedElements = alignElements(selectedElements, alignment);
const updatedElementsMap = getElementMap(updatedElements);
const updatedElementsMap = arrayToMap(updatedElements);
return elements.map((element) => updatedElementsMap[element.id] || element);
return elements.map(
(element) => updatedElementsMap.get(element.id) || element,
);
};
export const actionAlignTop = register({
@ -58,7 +59,7 @@ export const actionAlignTop = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<AlignTopIcon appearance={appState.appearance} />}
icon={<AlignTopIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.alignTop")}${getShortcutKey(
"CtrlOrCmd+Shift+Up",
@ -87,7 +88,7 @@ export const actionAlignBottom = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<AlignBottomIcon appearance={appState.appearance} />}
icon={<AlignBottomIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.alignBottom")}${getShortcutKey(
"CtrlOrCmd+Shift+Down",
@ -116,7 +117,7 @@ export const actionAlignLeft = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<AlignLeftIcon appearance={appState.appearance} />}
icon={<AlignLeftIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.alignLeft")}${getShortcutKey(
"CtrlOrCmd+Shift+Left",
@ -145,7 +146,7 @@ export const actionAlignRight = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<AlignRightIcon appearance={appState.appearance} />}
icon={<AlignRightIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.alignRight")}${getShortcutKey(
"CtrlOrCmd+Shift+Right",
@ -172,7 +173,7 @@ export const actionAlignVerticallyCentered = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<CenterVerticallyIcon appearance={appState.appearance} />}
icon={<CenterVerticallyIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={t("labels.centerVertically")}
aria-label={t("labels.centerVertically")}
@ -197,7 +198,7 @@ export const actionAlignHorizontallyCentered = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<CenterHorizontallyIcon appearance={appState.appearance} />}
icon={<CenterHorizontallyIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={t("labels.centerHorizontally")}
aria-label={t("labels.centerHorizontally")}

View File

@ -1,14 +1,11 @@
import React from "react";
import { getDefaultAppState } from "../appState";
import { ColorPicker } from "../components/ColorPicker";
import { resetZoom, trash, zoomIn, zoomOut } from "../components/icons";
import { zoomIn, zoomOut } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { ZOOM_STEP } from "../constants";
import { DarkModeToggle } from "../components/DarkModeToggle";
import { THEME, ZOOM_STEP } from "../constants";
import { getCommonBounds, getNonDeletedElements } from "../element";
import { newElementWith } from "../element/mutateElement";
import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { CODES, KEYS } from "../keys";
import { getNormalizedZoom, getSelectedElements } from "../scene";
import { centerScrollOn } from "../scene/scroll";
@ -16,13 +13,17 @@ import { getNewZoom } from "../scene/zoom";
import { AppState, NormalizedZoomValue } from "../types";
import { getShortcutKey } from "../utils";
import { register } from "./register";
import { Tooltip } from "../components/Tooltip";
import { newElementWith } from "../element/mutateElement";
import { getDefaultAppState } from "../appState";
import ClearCanvas from "../components/ClearCanvas";
export const actionChangeViewBackgroundColor = register({
name: "changeViewBackgroundColor",
perform: (_, appState, value) => {
return {
appState: { ...appState, viewBackgroundColor: value },
commitToHistory: true,
appState: { ...appState, ...value },
commitToHistory: !!value.viewBackgroundColor,
};
},
PanelComponent: ({ appState, updateData }) => {
@ -32,7 +33,12 @@ export const actionChangeViewBackgroundColor = register({
label={t("labels.canvasBackground")}
type="canvasBackground"
color={appState.viewBackgroundColor}
onChange={(color) => updateData(color)}
onChange={(color) => updateData({ viewBackgroundColor: color })}
isActive={appState.openPopup === "canvasColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "canvasColorPicker" : null })
}
data-testid="canvas-background-picker"
/>
</div>
);
@ -41,39 +47,28 @@ export const actionChangeViewBackgroundColor = register({
export const actionClearCanvas = register({
name: "clearCanvas",
perform: (elements, appState: AppState) => {
perform: (elements, appState, _, app) => {
app.imageCache.clear();
return {
elements: elements.map((element) =>
newElementWith(element, { isDeleted: true }),
),
appState: {
...getDefaultAppState(),
appearance: appState.appearance,
files: {},
theme: appState.theme,
elementLocked: appState.elementLocked,
exportBackground: appState.exportBackground,
exportEmbedScene: appState.exportEmbedScene,
gridSize: appState.gridSize,
shouldAddWatermark: appState.shouldAddWatermark,
showStats: appState.showStats,
pasteDialog: appState.pasteDialog,
},
commitToHistory: true,
};
},
PanelComponent: ({ updateData }) => (
<ToolButton
type="button"
icon={trash}
title={t("buttons.clearReset")}
aria-label={t("buttons.clearReset")}
showAriaLabel={useIsMobile()}
onClick={() => {
if (window.confirm(t("alerts.clearReset"))) {
updateData(null);
}
}}
/>
),
PanelComponent: ({ updateData }) => <ClearCanvas onConfirm={updateData} />,
});
export const actionZoomIn = register({
@ -102,6 +97,7 @@ export const actionZoomIn = register({
onClick={() => {
updateData(null);
}}
size="small"
/>
),
keyTest: (event) =>
@ -136,6 +132,7 @@ export const actionZoomOut = register({
onClick={() => {
updateData(null);
}}
size="small"
/>
),
keyTest: (event) =>
@ -162,16 +159,21 @@ export const actionResetZoom = register({
commitToHistory: false,
};
},
PanelComponent: ({ updateData }) => (
<ToolButton
type="button"
icon={resetZoom}
title={t("buttons.resetZoom")}
aria-label={t("buttons.resetZoom")}
onClick={() => {
updateData(null);
}}
/>
PanelComponent: ({ updateData, appState }) => (
<Tooltip label={t("buttons.resetZoom")} style={{ height: "100%" }}>
<ToolButton
type="button"
className="reset-zoom-button"
title={t("buttons.resetZoom")}
aria-label={t("buttons.resetZoom")}
onClick={() => {
updateData(null);
}}
size="small"
>
{(appState.zoom.value * 100).toFixed(0)}%
</ToolButton>
</Tooltip>
),
keyTest: (event) =>
(event.code === CODES.ZERO || event.code === CODES.NUM_ZERO) &&
@ -258,3 +260,28 @@ export const actionZoomToFit = register({
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionToggleTheme = register({
name: "toggleTheme",
perform: (_, appState, value) => {
return {
appState: {
...appState,
theme:
value || (appState.theme === THEME.LIGHT ? THEME.DARK : THEME.LIGHT),
},
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData }) => (
<div style={{ marginInlineStart: "0.25rem" }}>
<DarkModeToggle
value={appState.theme}
onChange={(theme) => {
updateData(theme);
}}
/>
</div>
),
keyTest: (event) => event.altKey && event.shiftKey && event.code === CODES.D,
});

View File

@ -9,15 +9,16 @@ import { t } from "../i18n";
export const actionCopy = register({
name: "copy",
perform: (elements, appState) => {
copyToClipboard(getNonDeletedElements(elements), appState);
perform: (elements, appState, _, app) => {
copyToClipboard(getNonDeletedElements(elements), appState, app.files);
return {
commitToHistory: false,
};
},
contextItemLabel: "labels.copy",
// Don't assign keyTest since its handled via copy event
// don't supply a shortcut since we handle this conditionally via onCopy event
keyTest: undefined,
});
export const actionCut = register({
@ -41,6 +42,7 @@ export const actionCopyAsSvg = register({
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
true,
);
try {
await exportCanvas(
@ -49,13 +51,13 @@ export const actionCopyAsSvg = register({
? selectedElements
: getNonDeletedElements(elements),
appState,
app.canvas,
app.files,
appState,
);
return {
commitToHistory: false,
};
} catch (error) {
} catch (error: any) {
console.error(error);
return {
appState: {
@ -80,6 +82,7 @@ export const actionCopyAsPng = register({
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
true,
);
try {
await exportCanvas(
@ -88,7 +91,7 @@ export const actionCopyAsPng = register({
? selectedElements
: getNonDeletedElements(elements),
appState,
app.canvas,
app.files,
appState,
);
return {
@ -105,7 +108,7 @@ export const actionCopyAsPng = register({
},
commitToHistory: false,
};
} catch (error) {
} catch (error: any) {
console.error(error);
return {
appState: {

View File

@ -1,7 +1,6 @@
import { isSomeElementSelected } from "../scene";
import { KEYS } from "../keys";
import { ToolButton } from "../components/ToolButton";
import React from "react";
import { trash } from "../components/icons";
import { t } from "../i18n";
import { register } from "./register";
@ -12,6 +11,7 @@ import { newElementWith } from "../element/mutateElement";
import { getElementsInGroup } from "../groups";
import { LinearElementEditor } from "../element/linearElementEditor";
import { fixBindingsAfterDeletion } from "../element/binding";
import { isBoundToContainer } from "../element/typeChecks";
const deleteSelectedElements = (
elements: readonly ExcalidrawElement[],
@ -22,6 +22,12 @@ const deleteSelectedElements = (
if (appState.selectedElementIds[el.id]) {
return newElementWith(el, { isDeleted: true });
}
if (
isBoundToContainer(el) &&
appState.selectedElementIds[el.containerId]
) {
return newElementWith(el, { isDeleted: true });
}
return el;
}),
appState: {
@ -56,7 +62,7 @@ export const actionDeleteSelected = register({
if (appState.editingLinearElement) {
const {
elementId,
activePointIndex,
selectedPointsIndices,
startBindingElement,
endBindingElement,
} = appState.editingLinearElement;
@ -66,8 +72,7 @@ export const actionDeleteSelected = register({
}
if (
// case: no point selected → delete whole element
activePointIndex == null ||
activePointIndex === -1 ||
selectedPointsIndices == null ||
// case: deleting last remaining point
element.points.length < 2
) {
@ -87,15 +92,17 @@ export const actionDeleteSelected = register({
// We cannot do this inside `movePoint` because it is also called
// when deleting the uncommitted point (which hasn't caused any binding)
const binding = {
startBindingElement:
activePointIndex === 0 ? null : startBindingElement,
endBindingElement:
activePointIndex === element.points.length - 1
? null
: endBindingElement,
startBindingElement: selectedPointsIndices?.includes(0)
? null
: startBindingElement,
endBindingElement: selectedPointsIndices?.includes(
element.points.length - 1,
)
? null
: endBindingElement,
};
LinearElementEditor.movePoint(element, activePointIndex, "delete");
LinearElementEditor.deletePoints(element, selectedPointsIndices);
return {
elements,
@ -104,17 +111,17 @@ export const actionDeleteSelected = register({
editingLinearElement: {
...appState.editingLinearElement,
...binding,
activePointIndex: activePointIndex > 0 ? activePointIndex - 1 : 0,
selectedPointsIndices:
selectedPointsIndices?.[0] > 0
? [selectedPointsIndices[0] - 1]
: [0],
},
},
commitToHistory: true,
};
}
let {
elements: nextElements,
appState: nextAppState,
} = deleteSelectedElements(elements, appState);
let { elements: nextElements, appState: nextAppState } =
deleteSelectedElements(elements, appState);
fixBindingsAfterDeletion(
nextElements,
elements.filter(({ id }) => appState.selectedElementIds[id]),

View File

@ -1,17 +1,16 @@
import React from "react";
import {
DistributeHorizontallyIcon,
DistributeVerticallyIcon,
} from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { distributeElements, Distribution } from "../disitrubte";
import { getElementMap, getNonDeletedElements } from "../element";
import { getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { CODES } from "../keys";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { AppState } from "../types";
import { getShortcutKey } from "../utils";
import { arrayToMap, getShortcutKey } from "../utils";
import { register } from "./register";
const enableActionGroup = (
@ -31,9 +30,11 @@ const distributeSelectedElements = (
const updatedElements = distributeElements(selectedElements, distribution);
const updatedElementsMap = getElementMap(updatedElements);
const updatedElementsMap = arrayToMap(updatedElements);
return elements.map((element) => updatedElementsMap[element.id] || element);
return elements.map(
(element) => updatedElementsMap.get(element.id) || element,
);
};
export const distributeHorizontally = register({
@ -53,7 +54,7 @@ export const distributeHorizontally = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<DistributeHorizontallyIcon appearance={appState.appearance} />}
icon={<DistributeHorizontallyIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.distributeHorizontally")}${getShortcutKey(
"Alt+H",
@ -81,7 +82,7 @@ export const distributeVertically = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<DistributeVerticallyIcon appearance={appState.appearance} />}
icon={<DistributeVerticallyIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.distributeVertically")}${getShortcutKey("Alt+V")}`}
aria-label={t("labels.distributeVertically")}

View File

@ -1,15 +1,13 @@
import React from "react";
import { KEYS } from "../keys";
import { register } from "./register";
import { ExcalidrawElement } from "../element/types";
import { duplicateElement, getNonDeletedElements } from "../element";
import { isSomeElementSelected } from "../scene";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { ToolButton } from "../components/ToolButton";
import { clone } from "../components/icons";
import { t } from "../i18n";
import { getShortcutKey } from "../utils";
import { arrayToMap, getShortcutKey } from "../utils";
import { LinearElementEditor } from "../element/linearElementEditor";
import { mutateElement } from "../element/mutateElement";
import {
selectGroupsForSelectedElements,
getSelectedGroupForElement,
@ -19,41 +17,23 @@ import { AppState } from "../types";
import { fixBindingsAfterDuplication } from "../element/binding";
import { ActionResult } from "./types";
import { GRID_SIZE } from "../constants";
import { bindTextToShapeAfterDuplication } from "../element/textElement";
import { isBoundToContainer } from "../element/typeChecks";
export const actionDuplicateSelection = register({
name: "duplicateSelection",
perform: (elements, appState) => {
// duplicate point if selected while editing multi-point element
// duplicate selected point(s) if editing a line
if (appState.editingLinearElement) {
const { activePointIndex, elementId } = appState.editingLinearElement;
const element = LinearElementEditor.getElement(elementId);
if (!element || activePointIndex === null) {
const ret = LinearElementEditor.duplicateSelectedPoints(appState);
if (!ret) {
return false;
}
const { points } = element;
const selectedPoint = points[activePointIndex];
const nextPoint = points[activePointIndex + 1];
mutateElement(element, {
points: [
...points.slice(0, activePointIndex + 1),
nextPoint
? [
(selectedPoint[0] + nextPoint[0]) / 2,
(selectedPoint[1] + nextPoint[1]) / 2,
]
: [selectedPoint[0] + 30, selectedPoint[1] + 30],
...points.slice(activePointIndex + 1),
],
});
return {
appState: {
...appState,
editingLinearElement: {
...appState.editingLinearElement,
activePointIndex: activePointIndex + 1,
},
},
elements,
appState: ret.appState,
commitToHistory: true,
};
}
@ -107,9 +87,12 @@ const duplicateElements = (
const finalElements: ExcalidrawElement[] = [];
let index = 0;
const selectedElementIds = arrayToMap(
getSelectedElements(elements, appState, true),
);
while (index < elements.length) {
const element = elements[index];
if (appState.selectedElementIds[element.id]) {
if (selectedElementIds.get(element.id)) {
if (element.groupIds.length) {
const groupId = getSelectedGroupForElement(appState, element);
// if group selected, duplicate it atomically
@ -131,7 +114,11 @@ const duplicateElements = (
}
index++;
}
bindTextToShapeAfterDuplication(
finalElements,
oldElements,
oldIdToDuplicatedId,
);
fixBindingsAfterDuplication(finalElements, oldElements, oldIdToDuplicatedId);
return {
@ -141,7 +128,9 @@ const duplicateElements = (
...appState,
selectedGroupIds: {},
selectedElementIds: newElements.reduce((acc, element) => {
acc[element.id] = true;
if (!isBoundToContainer(element)) {
acc[element.id] = true;
}
return acc;
}, {} as any),
},

View File

@ -1,16 +1,25 @@
import React from "react";
import { trackEvent } from "../analytics";
import { load, questionCircle, save, saveAs } from "../components/icons";
import { load, questionCircle, saveAs } from "../components/icons";
import { ProjectName } from "../components/ProjectName";
import { ToolButton } from "../components/ToolButton";
import "../components/ToolIcon.scss";
import { Tooltip } from "../components/Tooltip";
import { DarkModeToggle, Appearence } from "../components/DarkModeToggle";
import { DarkModeToggle } from "../components/DarkModeToggle";
import { loadFromJSON, saveAsJSON } from "../data";
import { resaveAsImageWithScene } from "../data/resave";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { useIsMobile } from "../components/App";
import { KEYS } from "../keys";
import { register } from "./register";
import { CheckboxItem } from "../components/CheckboxItem";
import { getExportSize } from "../scene/export";
import { DEFAULT_EXPORT_PADDING, EXPORT_SCALES, THEME } from "../constants";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { getNonDeletedElements } from "../element";
import { ActiveFile } from "../components/ActiveFile";
import { isImageFileHandle } from "../data/blob";
import { nativeFileSystemSupported } from "../data/filesystem";
import { Theme } from "../element/types";
export const actionChangeProjectName = register({
name: "changeProjectName",
@ -18,15 +27,66 @@ export const actionChangeProjectName = register({
trackEvent("change", "title");
return { appState: { ...appState, name: value }, commitToHistory: false };
},
PanelComponent: ({ appState, updateData }) => (
PanelComponent: ({ appState, updateData, appProps }) => (
<ProjectName
label={t("labels.fileTitle")}
value={appState.name || "Unnamed"}
onChange={(name: string) => updateData(name)}
isNameEditable={
typeof appProps.name === "undefined" && !appState.viewModeEnabled
}
/>
),
});
export const actionChangeExportScale = register({
name: "changeExportScale",
perform: (_elements, appState, value) => {
return {
appState: { ...appState, exportScale: value },
commitToHistory: false,
};
},
PanelComponent: ({ elements: allElements, appState, updateData }) => {
const elements = getNonDeletedElements(allElements);
const exportSelected = isSomeElementSelected(elements, appState);
const exportedElements = exportSelected
? getSelectedElements(elements, appState)
: elements;
return (
<>
{EXPORT_SCALES.map((s) => {
const [width, height] = getExportSize(
exportedElements,
DEFAULT_EXPORT_PADDING,
s,
);
const scaleButtonTitle = `${t(
"buttons.scale",
)} ${s}x (${width}x${height})`;
return (
<ToolButton
key={s}
size="small"
type="radio"
icon={`${s}x`}
name="export-canvas-scale"
title={scaleButtonTitle}
aria-label={scaleButtonTitle}
id="export-canvas-scale"
checked={s === appState.exportScale}
onChange={() => updateData(s)}
/>
);
})}
</>
);
},
});
export const actionChangeExportBackground = register({
name: "changeExportBackground",
perform: (_elements, appState, value) => {
@ -36,14 +96,12 @@ export const actionChangeExportBackground = register({
};
},
PanelComponent: ({ appState, updateData }) => (
<label>
<input
type="checkbox"
checked={appState.exportBackground}
onChange={(event) => updateData(event.target.checked)}
/>{" "}
<CheckboxItem
checked={appState.exportBackground}
onChange={(checked) => updateData(checked)}
>
{t("labels.withBackground")}
</label>
</CheckboxItem>
),
});
@ -56,57 +114,35 @@ export const actionChangeExportEmbedScene = register({
};
},
PanelComponent: ({ appState, updateData }) => (
<label style={{ display: "flex" }}>
<input
type="checkbox"
checked={appState.exportEmbedScene}
onChange={(event) => updateData(event.target.checked)}
/>{" "}
<CheckboxItem
checked={appState.exportEmbedScene}
onChange={(checked) => updateData(checked)}
>
{t("labels.exportEmbedScene")}
<Tooltip
label={t("labels.exportEmbedScene_details")}
position="above"
long={true}
>
<div className="TooltipIcon">{questionCircle}</div>
<Tooltip label={t("labels.exportEmbedScene_details")} long={true}>
<div className="excalidraw-tooltip-icon">{questionCircle}</div>
</Tooltip>
</label>
</CheckboxItem>
),
});
export const actionChangeShouldAddWatermark = register({
name: "changeShouldAddWatermark",
perform: (_elements, appState, value) => {
return {
appState: { ...appState, shouldAddWatermark: value },
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData }) => (
<label>
<input
type="checkbox"
checked={appState.shouldAddWatermark}
onChange={(event) => updateData(event.target.checked)}
/>{" "}
{t("labels.addWatermark")}
</label>
),
});
export const actionSaveScene = register({
name: "saveScene",
perform: async (elements, appState, value) => {
export const actionSaveToActiveFile = register({
name: "saveToActiveFile",
perform: async (elements, appState, value, app) => {
const fileHandleExists = !!appState.fileHandle;
try {
const { fileHandle } = await saveAsJSON(elements, appState);
const { fileHandle } = isImageFileHandle(appState.fileHandle)
? await resaveAsImageWithScene(elements, appState, app.files)
: await saveAsJSON(elements, appState, app.files);
return {
commitToHistory: false,
appState: {
...appState,
fileHandle,
toastMessage: fileHandleExists
? fileHandle.name
? fileHandle?.name
? t("toast.fileSavedToFilename").replace(
"{filename}",
`"${fileHandle.name}"`,
@ -115,39 +151,43 @@ export const actionSaveScene = register({
: null,
},
};
} catch (error) {
} catch (error: any) {
if (error?.name !== "AbortError") {
console.error(error);
} else {
console.warn(error);
}
return { commitToHistory: false };
}
},
keyTest: (event) =>
event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
PanelComponent: ({ updateData }) => (
<ToolButton
type="button"
icon={save}
title={t("buttons.save")}
aria-label={t("buttons.save")}
showAriaLabel={useIsMobile()}
onClick={() => updateData(null)}
PanelComponent: ({ updateData, appState }) => (
<ActiveFile
onSave={() => updateData(null)}
fileName={appState.fileHandle?.name}
/>
),
});
export const actionSaveAsScene = register({
name: "saveAsScene",
perform: async (elements, appState, value) => {
export const actionSaveFileToDisk = register({
name: "saveFileToDisk",
perform: async (elements, appState, value, app) => {
try {
const { fileHandle } = await saveAsJSON(elements, {
...appState,
fileHandle: null,
});
const { fileHandle } = await saveAsJSON(
elements,
{
...appState,
fileHandle: null,
},
app.files,
);
return { commitToHistory: false, appState: { ...appState, fileHandle } };
} catch (error) {
} catch (error: any) {
if (error?.name !== "AbortError") {
console.error(error);
} else {
console.warn(error);
}
return { commitToHistory: false };
}
@ -161,34 +201,37 @@ export const actionSaveAsScene = register({
title={t("buttons.saveAs")}
aria-label={t("buttons.saveAs")}
showAriaLabel={useIsMobile()}
hidden={
!("chooseFileSystemEntries" in window || "showOpenFilePicker" in window)
}
hidden={!nativeFileSystemSupported}
onClick={() => updateData(null)}
data-testid="save-as-button"
/>
),
});
export const actionLoadScene = register({
name: "loadScene",
perform: async (elements, appState) => {
perform: async (elements, appState, _, app) => {
try {
const {
elements: loadedElements,
appState: loadedAppState,
} = await loadFromJSON(appState);
files,
} = await loadFromJSON(appState, elements);
return {
elements: loadedElements,
appState: loadedAppState,
files,
commitToHistory: true,
};
} catch (error) {
} catch (error: any) {
if (error?.name === "AbortError") {
console.warn(error);
return false;
}
return {
elements,
appState: { ...appState, errorMessage: error.message },
files: app.files,
commitToHistory: false,
};
}
@ -202,6 +245,7 @@ export const actionLoadScene = register({
aria-label={t("buttons.load")}
showAriaLabel={useIsMobile()}
onClick={updateData}
data-testid="load-button"
/>
),
});
@ -224,9 +268,9 @@ export const actionExportWithDarkMode = register({
}}
>
<DarkModeToggle
value={appState.exportWithDarkMode ? "dark" : "light"}
onChange={(appearance: Appearence) => {
updateData(appearance === "dark");
value={appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT}
onChange={(theme: Theme) => {
updateData(theme === THEME.DARK);
}}
title={t("labels.toggleExportColorScheme")}
/>

View File

@ -1,7 +1,6 @@
import { KEYS } from "../keys";
import { isInvisiblySmallElement } from "../element";
import { resetCursor } from "../utils";
import React from "react";
import { ToolButton } from "../components/ToolButton";
import { done } from "../components/icons";
import { t } from "../i18n";
@ -18,13 +17,10 @@ import { isBindingElement } from "../element/typeChecks";
export const actionFinalize = register({
name: "finalize",
perform: (elements, appState, _, { canvas }) => {
perform: (elements, appState, _, { canvas, focusContainer }) => {
if (appState.editingLinearElement) {
const {
elementId,
startBindingElement,
endBindingElement,
} = appState.editingLinearElement;
const { elementId, startBindingElement, endBindingElement } =
appState.editingLinearElement;
const element = LinearElementEditor.getElement(elementId);
if (element) {
@ -50,20 +46,25 @@ export const actionFinalize = register({
}
let newElements = elements;
if (appState.pendingImageElement) {
mutateElement(appState.pendingImageElement, { isDeleted: true }, false);
}
if (window.document.activeElement instanceof HTMLElement) {
window.document.activeElement.blur();
focusContainer();
}
const multiPointElement = appState.multiElement
? appState.multiElement
: appState.editingElement?.type === "draw"
: appState.editingElement?.type === "freedraw"
? appState.editingElement
: null;
if (multiPointElement) {
// pen and mouse have hover
if (
multiPointElement.type !== "draw" &&
multiPointElement.type !== "freedraw" &&
appState.lastPointerDownWith !== "touch"
) {
const { points, lastCommittedPoint } = multiPointElement;
@ -86,7 +87,7 @@ export const actionFinalize = register({
const isLoop = isPathALoop(multiPointElement.points, appState.zoom.value);
if (
multiPointElement.type === "line" ||
multiPointElement.type === "draw"
multiPointElement.type === "freedraw"
) {
if (isLoop) {
const linePoints = multiPointElement.points;
@ -118,22 +119,24 @@ export const actionFinalize = register({
);
}
if (!appState.elementLocked && appState.elementType !== "draw") {
if (!appState.elementLocked && appState.elementType !== "freedraw") {
appState.selectedElementIds[multiPointElement.id] = true;
}
}
if (
(!appState.elementLocked && appState.elementType !== "draw") ||
(!appState.elementLocked && appState.elementType !== "freedraw") ||
!multiPointElement
) {
resetCursor(canvas);
}
return {
elements: newElements,
appState: {
...appState,
elementType:
(appState.elementLocked || appState.elementType === "draw") &&
(appState.elementLocked || appState.elementType === "freedraw") &&
multiPointElement
? appState.elementType
: "selection",
@ -145,14 +148,15 @@ export const actionFinalize = register({
selectedElementIds:
multiPointElement &&
!appState.elementLocked &&
appState.elementType !== "draw"
appState.elementType !== "freedraw"
? {
...appState.selectedElementIds,
[multiPointElement.id]: true,
}
: appState.selectedElementIds,
pendingImageElement: null,
},
commitToHistory: appState.elementType === "draw",
commitToHistory: appState.elementType === "freedraw",
};
},
keyTest: (event, appState) =>

209
src/actions/actionFlip.ts Normal file
View File

@ -0,0 +1,209 @@
import { register } from "./register";
import { getSelectedElements } from "../scene";
import { getNonDeletedElements } from "../element";
import { mutateElement } from "../element/mutateElement";
import { ExcalidrawElement, NonDeleted } from "../element/types";
import { normalizeAngle, resizeSingleElement } from "../element/resizeElements";
import { AppState } from "../types";
import { getTransformHandles } from "../element/transformHandles";
import { isFreeDrawElement, isLinearElement } from "../element/typeChecks";
import { updateBoundElements } from "../element/binding";
import { LinearElementEditor } from "../element/linearElementEditor";
import { arrayToMap } from "../utils";
const enableActionFlipHorizontal = (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const eligibleElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
);
return eligibleElements.length === 1 && eligibleElements[0].type !== "text";
};
const enableActionFlipVertical = (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const eligibleElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
);
return eligibleElements.length === 1;
};
export const actionFlipHorizontal = register({
name: "flipHorizontal",
perform: (elements, appState) => {
return {
elements: flipSelectedElements(elements, appState, "horizontal"),
appState,
commitToHistory: true,
};
},
keyTest: (event) => event.shiftKey && event.code === "KeyH",
contextItemLabel: "labels.flipHorizontal",
contextItemPredicate: (elements, appState) =>
enableActionFlipHorizontal(elements, appState),
});
export const actionFlipVertical = register({
name: "flipVertical",
perform: (elements, appState) => {
return {
elements: flipSelectedElements(elements, appState, "vertical"),
appState,
commitToHistory: true,
};
},
keyTest: (event) => event.shiftKey && event.code === "KeyV",
contextItemLabel: "labels.flipVertical",
contextItemPredicate: (elements, appState) =>
enableActionFlipVertical(elements, appState),
});
const flipSelectedElements = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
flipDirection: "horizontal" | "vertical",
) => {
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
);
// remove once we allow for groups of elements to be flipped
if (selectedElements.length > 1) {
return elements;
}
const updatedElements = flipElements(
selectedElements,
appState,
flipDirection,
);
const updatedElementsMap = arrayToMap(updatedElements);
return elements.map(
(element) => updatedElementsMap.get(element.id) || element,
);
};
const flipElements = (
elements: NonDeleted<ExcalidrawElement>[],
appState: AppState,
flipDirection: "horizontal" | "vertical",
): ExcalidrawElement[] => {
elements.forEach((element) => {
flipElement(element, appState);
// If vertical flip, rotate an extra 180
if (flipDirection === "vertical") {
rotateElement(element, Math.PI);
}
});
return elements;
};
const flipElement = (
element: NonDeleted<ExcalidrawElement>,
appState: AppState,
) => {
const originalX = element.x;
const originalY = element.y;
const width = element.width;
const height = element.height;
const originalAngle = normalizeAngle(element.angle);
let finalOffsetX = 0;
if (isLinearElement(element) || isFreeDrawElement(element)) {
finalOffsetX =
element.points.reduce((max, point) => Math.max(max, point[0]), 0) * 2 -
element.width;
}
// Rotate back to zero, if necessary
mutateElement(element, {
angle: normalizeAngle(0),
});
// Flip unrotated by pulling TransformHandle to opposite side
const transformHandles = getTransformHandles(element, appState.zoom);
let usingNWHandle = true;
let newNCoordsX = 0;
let nHandle = transformHandles.nw;
if (!nHandle) {
// Use ne handle instead
usingNWHandle = false;
nHandle = transformHandles.ne;
if (!nHandle) {
mutateElement(element, {
angle: originalAngle,
});
return;
}
}
if (isLinearElement(element)) {
for (let index = 1; index < element.points.length; index++) {
LinearElementEditor.movePoints(element, [
{ index, point: [-element.points[index][0], element.points[index][1]] },
]);
}
LinearElementEditor.normalizePoints(element);
} else {
// calculate new x-coord for transformation
newNCoordsX = usingNWHandle ? element.x + 2 * width : element.x - 2 * width;
resizeSingleElement(
element,
true,
element,
usingNWHandle ? "nw" : "ne",
false,
newNCoordsX,
nHandle[1],
);
// fix the size to account for handle sizes
mutateElement(element, {
width,
height,
});
}
// Rotate by (360 degrees - original angle)
let angle = normalizeAngle(2 * Math.PI - originalAngle);
if (angle < 0) {
// check, probably unnecessary
angle = normalizeAngle(angle + 2 * Math.PI);
}
mutateElement(element, {
angle,
});
// Move back to original spot to appear "flipped in place"
mutateElement(element, {
x: originalX + finalOffsetX,
y: originalY,
});
updateBoundElements(element);
};
const rotateElement = (element: ExcalidrawElement, rotationAngle: number) => {
const originalX = element.x;
const originalY = element.y;
let angle = normalizeAngle(element.angle + rotationAngle);
if (angle < 0) {
// check, probably unnecessary
angle = normalizeAngle(2 * Math.PI + angle);
}
mutateElement(element, {
angle,
});
// Move back to original spot
mutateElement(element, {
x: originalX,
y: originalY,
});
};

View File

@ -1,7 +1,6 @@
import React from "react";
import { CODES, KEYS } from "../keys";
import { t } from "../i18n";
import { getShortcutKey } from "../utils";
import { arrayToMap, getShortcutKey } from "../utils";
import { register } from "./register";
import { UngroupIcon, GroupIcon } from "../components/icons";
import { newElementWith } from "../element/mutateElement";
@ -18,8 +17,9 @@ import {
import { getNonDeletedElements } from "../element";
import { randomId } from "../random";
import { ToolButton } from "../components/ToolButton";
import { ExcalidrawElement } from "../element/types";
import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
import { AppState } from "../types";
import { isBoundToContainer } from "../element/typeChecks";
const allElementsInSameGroup = (elements: readonly ExcalidrawElement[]) => {
if (elements.length >= 2) {
@ -45,6 +45,7 @@ const enableActionGroup = (
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
true,
);
return (
selectedElements.length >= 2 && !allElementsInSameGroup(selectedElements)
@ -57,6 +58,7 @@ export const actionGroup = register({
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
true,
);
if (selectedElements.length < 2) {
// nothing to group
@ -84,8 +86,9 @@ export const actionGroup = register({
}
}
const newGroupId = randomId();
const selectElementIds = arrayToMap(selectedElements);
const updatedElements = elements.map((element) => {
if (!appState.selectedElementIds[element.id]) {
if (!selectElementIds.get(element.id)) {
return element;
}
return newElementWith(element, {
@ -100,9 +103,8 @@ export const actionGroup = register({
// to the z order of the highest element in the layer stack
const elementsInGroup = getElementsInGroup(updatedElements, newGroupId);
const lastElementInGroup = elementsInGroup[elementsInGroup.length - 1];
const lastGroupElementIndex = updatedElements.lastIndexOf(
lastElementInGroup,
);
const lastGroupElementIndex =
updatedElements.lastIndexOf(lastElementInGroup);
const elementsAfterGroup = updatedElements.slice(lastGroupElementIndex + 1);
const elementsBeforeGroup = updatedElements
.slice(0, lastGroupElementIndex)
@ -134,7 +136,7 @@ export const actionGroup = register({
<ToolButton
hidden={!enableActionGroup(elements, appState)}
type="button"
icon={<GroupIcon appearance={appState.appearance} />}
icon={<GroupIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.group")}${getShortcutKey("CtrlOrCmd+G")}`}
aria-label={t("labels.group")}
@ -150,7 +152,12 @@ export const actionUngroup = register({
if (groupIds.length === 0) {
return { appState, elements, commitToHistory: false };
}
const boundTextElementIds: ExcalidrawTextElement["id"][] = [];
const nextElements = elements.map((element) => {
if (isBoundToContainer(element)) {
boundTextElementIds.push(element.id);
}
const nextGroupIds = removeFromSelectedGroups(
element.groupIds,
appState.selectedGroupIds,
@ -162,11 +169,19 @@ export const actionUngroup = register({
groupIds: nextGroupIds,
});
});
const updateAppState = selectGroupsForSelectedElements(
{ ...appState, selectedGroupIds: {} },
getNonDeletedElements(nextElements),
);
// remove binded text elements from selection
boundTextElementIds.forEach(
(id) => (updateAppState.selectedElementIds[id] = false),
);
return {
appState: selectGroupsForSelectedElements(
{ ...appState, selectedGroupIds: {} },
getNonDeletedElements(nextElements),
),
appState: updateAppState,
elements: nextElements,
commitToHistory: true,
};
@ -181,7 +196,7 @@ export const actionUngroup = register({
<ToolButton
type="button"
hidden={getSelectedGroupIds(appState).length === 0}
icon={<UngroupIcon appearance={appState.appearance} />}
icon={<UngroupIcon theme={appState.theme} />}
onClick={() => updateData(null)}
title={`${t("labels.ungroup")}${getShortcutKey("CtrlOrCmd+Shift+G")}`}
aria-label={t("labels.ungroup")}

View File

@ -1,15 +1,14 @@
import { Action, ActionResult } from "./types";
import React from "react";
import { undo, redo } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n";
import { SceneHistory, HistoryEntry } from "../history";
import History, { HistoryEntry } from "../history";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import { isWindows, KEYS } from "../keys";
import { getElementMap } from "../element";
import { newElementWith } from "../element/mutateElement";
import { fixBindingsAfterDeletion } from "../element/binding";
import { arrayToMap } from "../utils";
const writeData = (
prevElements: readonly ExcalidrawElement[],
@ -28,17 +27,17 @@ const writeData = (
return { commitToHistory };
}
const prevElementMap = getElementMap(prevElements);
const prevElementMap = arrayToMap(prevElements);
const nextElements = data.elements;
const nextElementMap = getElementMap(nextElements);
const nextElementMap = arrayToMap(nextElements);
const deletedElements = prevElements.filter(
(prevElement) => !nextElementMap.hasOwnProperty(prevElement.id),
(prevElement) => !nextElementMap.has(prevElement.id),
);
const elements = nextElements
.map((nextElement) =>
newElementWith(
prevElementMap[nextElement.id] || nextElement,
prevElementMap.get(nextElement.id) || nextElement,
nextElement,
),
)
@ -59,7 +58,7 @@ const writeData = (
return { commitToHistory };
};
type ActionCreator = (history: SceneHistory) => Action;
type ActionCreator = (history: History) => Action;
export const createUndoAction: ActionCreator = (history) => ({
name: "undo",
@ -69,12 +68,13 @@ export const createUndoAction: ActionCreator = (history) => ({
event[KEYS.CTRL_OR_CMD] &&
event.key.toLowerCase() === KEYS.Z &&
!event.shiftKey,
PanelComponent: ({ updateData }) => (
PanelComponent: ({ updateData, data }) => (
<ToolButton
type="button"
icon={undo}
aria-label={t("buttons.undo")}
onClick={updateData}
size={data?.size || "medium"}
/>
),
commitToHistory: () => false,
@ -89,12 +89,13 @@ export const createRedoAction: ActionCreator = (history) => ({
event.shiftKey &&
event.key.toLowerCase() === KEYS.Z) ||
(isWindows && event.ctrlKey && !event.shiftKey && event.key === KEYS.Y),
PanelComponent: ({ updateData }) => (
PanelComponent: ({ updateData, data }) => (
<ToolButton
type="button"
icon={redo}
aria-label={t("buttons.redo")}
onClick={updateData}
size={data?.size || "medium"}
/>
),
commitToHistory: () => false,

View File

@ -1,4 +1,3 @@
import React from "react";
import { menu, palette } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n";
@ -70,7 +69,10 @@ export const actionFullScreen = register({
export const actionShortcuts = register({
name: "toggleShortcuts",
perform: (_elements, appState) => {
perform: (_elements, appState, _, { focusContainer }) => {
if (appState.showHelpDialog) {
focusContainer();
}
return {
appState: {
...appState,

View File

@ -1,4 +1,3 @@
import React from "react";
import { getClientColors, getClientInitials } from "../clients";
import { Avatar } from "../components/Avatar";
import { centerScrollOn } from "../scene/scroll";
@ -30,8 +29,8 @@ export const actionGoToCollaborator = register({
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData, id }) => {
const clientId = id;
PanelComponent: ({ appState, updateData, data }) => {
const clientId: string | undefined = data?.id;
if (!clientId) {
return null;
}

View File

@ -1,19 +1,25 @@
import React from "react";
import { AppState } from "../../src/types";
import { ButtonIconSelect } from "../components/ButtonIconSelect";
import { ButtonSelect } from "../components/ButtonSelect";
import { ColorPicker } from "../components/ColorPicker";
import { IconPicker } from "../components/IconPicker";
import {
ArrowheadArrowIcon,
ArrowheadBarIcon,
ArrowheadDotIcon,
ArrowheadTriangleIcon,
ArrowheadNoneIcon,
EdgeRoundIcon,
EdgeSharpIcon,
FillCrossHatchIcon,
FillHachureIcon,
FillSolidIcon,
FontFamilyCodeIcon,
FontFamilyHandDrawnIcon,
FontFamilyNormalIcon,
FontSizeExtraLargeIcon,
FontSizeLargeIcon,
FontSizeMediumIcon,
FontSizeSmallIcon,
SloppinessArchitectIcon,
SloppinessArtistIcon,
SloppinessCartoonistIcon,
@ -21,21 +27,29 @@ import {
StrokeStyleDottedIcon,
StrokeStyleSolidIcon,
StrokeWidthIcon,
TextAlignCenterIcon,
TextAlignLeftIcon,
TextAlignRightIcon,
} from "../components/icons";
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
import {
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
FONT_FAMILY,
} from "../constants";
import {
getNonDeletedElements,
isTextElement,
redrawTextBoundingBox,
} from "../element";
import { newElementWith } from "../element/mutateElement";
import { getBoundTextElement } from "../element/textElement";
import { isLinearElement, isLinearElementType } from "../element/typeChecks";
import {
Arrowhead,
ExcalidrawElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
FontFamily,
FontFamilyValues,
TextAlign,
} from "../element/types";
import { getLanguage, t } from "../i18n";
@ -44,19 +58,27 @@ import {
canChangeSharpness,
canHaveArrowheads,
getCommonAttributeOfSelectedElements,
getSelectedElements,
getTargetElements,
isSomeElementSelected,
} from "../scene";
import { hasStrokeColor } from "../scene/comparisons";
import Scene from "../scene/Scene";
import { arrayToMap } from "../utils";
import { register } from "./register";
const changeProperty = (
elements: readonly ExcalidrawElement[],
appState: AppState,
callback: (element: ExcalidrawElement) => ExcalidrawElement,
includeBoundText = false,
) => {
const selectedElementIds = arrayToMap(
getSelectedElements(elements, appState, includeBoundText),
);
return elements.map((element) => {
if (
appState.selectedElementIds[element.id] ||
selectedElementIds.get(element.id) ||
element.id === appState.editingElement?.id
) {
return callback(element);
@ -90,13 +112,20 @@ export const actionChangeStrokeColor = register({
name: "changeStrokeColor",
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
strokeColor: value,
...(value.currentItemStrokeColor && {
elements: changeProperty(elements, appState, (el) => {
return hasStrokeColor(el.type)
? newElementWith(el, {
strokeColor: value.currentItemStrokeColor,
})
: el;
}),
),
appState: { ...appState, currentItemStrokeColor: value },
commitToHistory: true,
}),
appState: {
...appState,
...value,
},
commitToHistory: !!value.currentItemStrokeColor,
};
},
PanelComponent: ({ elements, appState, updateData }) => (
@ -111,7 +140,11 @@ export const actionChangeStrokeColor = register({
(element) => element.strokeColor,
appState.currentItemStrokeColor,
)}
onChange={updateData}
onChange={(color) => updateData({ currentItemStrokeColor: color })}
isActive={appState.openPopup === "strokeColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "strokeColorPicker" : null })
}
/>
</>
),
@ -121,13 +154,18 @@ export const actionChangeBackgroundColor = register({
name: "changeBackgroundColor",
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
backgroundColor: value,
}),
),
appState: { ...appState, currentItemBackgroundColor: value },
commitToHistory: true,
...(value.currentItemBackgroundColor && {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
backgroundColor: value.currentItemBackgroundColor,
}),
),
}),
appState: {
...appState,
...value,
},
commitToHistory: !!value.currentItemBackgroundColor,
};
},
PanelComponent: ({ elements, appState, updateData }) => (
@ -142,7 +180,11 @@ export const actionChangeBackgroundColor = register({
(element) => element.backgroundColor,
appState.currentItemBackgroundColor,
)}
onChange={updateData}
onChange={(color) => updateData({ currentItemBackgroundColor: color })}
isActive={appState.openPopup === "backgroundColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "backgroundColorPicker" : null })
}
/>
</>
),
@ -169,17 +211,17 @@ export const actionChangeFillStyle = register({
{
value: "hachure",
text: t("labels.hachure"),
icon: <FillHachureIcon appearance={appState.appearance} />,
icon: <FillHachureIcon theme={appState.theme} />,
},
{
value: "cross-hatch",
text: t("labels.crossHatch"),
icon: <FillCrossHatchIcon appearance={appState.appearance} />,
icon: <FillCrossHatchIcon theme={appState.theme} />,
},
{
value: "solid",
text: t("labels.solid"),
icon: <FillSolidIcon appearance={appState.appearance} />,
icon: <FillSolidIcon theme={appState.theme} />,
},
]}
group="fill"
@ -219,32 +261,17 @@ export const actionChangeStrokeWidth = register({
{
value: 1,
text: t("labels.thin"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={2}
/>
),
icon: <StrokeWidthIcon theme={appState.theme} strokeWidth={2} />,
},
{
value: 2,
text: t("labels.bold"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={6}
/>
),
icon: <StrokeWidthIcon theme={appState.theme} strokeWidth={6} />,
},
{
value: 4,
text: t("labels.extraBold"),
icon: (
<StrokeWidthIcon
appearance={appState.appearance}
strokeWidth={10}
/>
),
icon: <StrokeWidthIcon theme={appState.theme} strokeWidth={10} />,
},
]}
value={getFormValue(
@ -282,17 +309,17 @@ export const actionChangeSloppiness = register({
{
value: 0,
text: t("labels.architect"),
icon: <SloppinessArchitectIcon appearance={appState.appearance} />,
icon: <SloppinessArchitectIcon theme={appState.theme} />,
},
{
value: 1,
text: t("labels.artist"),
icon: <SloppinessArtistIcon appearance={appState.appearance} />,
icon: <SloppinessArtistIcon theme={appState.theme} />,
},
{
value: 2,
text: t("labels.cartoonist"),
icon: <SloppinessCartoonistIcon appearance={appState.appearance} />,
icon: <SloppinessCartoonistIcon theme={appState.theme} />,
},
]}
value={getFormValue(
@ -329,17 +356,17 @@ export const actionChangeStrokeStyle = register({
{
value: "solid",
text: t("labels.strokeStyle_solid"),
icon: <StrokeStyleSolidIcon appearance={appState.appearance} />,
icon: <StrokeStyleSolidIcon theme={appState.theme} />,
},
{
value: "dashed",
text: t("labels.strokeStyle_dashed"),
icon: <StrokeStyleDashedIcon appearance={appState.appearance} />,
icon: <StrokeStyleDashedIcon theme={appState.theme} />,
},
{
value: "dotted",
text: t("labels.strokeStyle_dotted"),
icon: <StrokeStyleDottedIcon appearance={appState.appearance} />,
icon: <StrokeStyleDottedIcon theme={appState.theme} />,
},
]}
value={getFormValue(
@ -407,17 +434,26 @@ export const actionChangeFontSize = register({
name: "changeFontSize",
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
fontSize: value,
});
redrawTextBoundingBox(element);
return element;
}
elements: changeProperty(
elements,
appState,
(el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
fontSize: value,
});
let container = null;
if (el.containerId) {
container = Scene.getScene(el)!.getElement(el.containerId);
}
redrawTextBoundingBox(element, container, appState);
return element;
}
return el;
}),
return el;
},
true,
),
appState: {
...appState,
currentItemFontSize: value,
@ -428,18 +464,43 @@ export const actionChangeFontSize = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.fontSize")}</legend>
<ButtonSelect
<ButtonIconSelect
group="font-size"
options={[
{ value: 16, text: t("labels.small") },
{ value: 20, text: t("labels.medium") },
{ value: 28, text: t("labels.large") },
{ value: 36, text: t("labels.veryLarge") },
{
value: 16,
text: t("labels.small"),
icon: <FontSizeSmallIcon theme={appState.theme} />,
},
{
value: 20,
text: t("labels.medium"),
icon: <FontSizeMediumIcon theme={appState.theme} />,
},
{
value: 28,
text: t("labels.large"),
icon: <FontSizeLargeIcon theme={appState.theme} />,
},
{
value: 36,
text: t("labels.veryLarge"),
icon: <FontSizeExtraLargeIcon theme={appState.theme} />,
},
]}
value={getFormValue(
elements,
appState,
(element) => isTextElement(element) && element.fontSize,
(element) => {
if (isTextElement(element)) {
return element.fontSize;
}
const boundTextElement = getBoundTextElement(element);
if (boundTextElement) {
return boundTextElement.fontSize;
}
return null;
},
appState.currentItemFontSize || DEFAULT_FONT_SIZE,
)}
onChange={(value) => updateData(value)}
@ -452,17 +513,26 @@ export const actionChangeFontFamily = register({
name: "changeFontFamily",
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
fontFamily: value,
});
redrawTextBoundingBox(element);
return element;
}
elements: changeProperty(
elements,
appState,
(el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
fontFamily: value,
});
let container = null;
if (el.containerId) {
container = Scene.getScene(el)!.getElement(el.containerId);
}
redrawTextBoundingBox(element, container, appState);
return element;
}
return el;
}),
return el;
},
true,
),
appState: {
...appState,
currentItemFontFamily: value,
@ -471,22 +541,47 @@ export const actionChangeFontFamily = register({
};
},
PanelComponent: ({ elements, appState, updateData }) => {
const options: { value: FontFamily; text: string }[] = [
{ value: 1, text: t("labels.handDrawn") },
{ value: 2, text: t("labels.normal") },
{ value: 3, text: t("labels.code") },
const options: {
value: FontFamilyValues;
text: string;
icon: JSX.Element;
}[] = [
{
value: FONT_FAMILY.Virgil,
text: t("labels.handDrawn"),
icon: <FontFamilyHandDrawnIcon theme={appState.theme} />,
},
{
value: FONT_FAMILY.Helvetica,
text: t("labels.normal"),
icon: <FontFamilyNormalIcon theme={appState.theme} />,
},
{
value: FONT_FAMILY.Cascadia,
text: t("labels.code"),
icon: <FontFamilyCodeIcon theme={appState.theme} />,
},
];
return (
<fieldset>
<legend>{t("labels.fontFamily")}</legend>
<ButtonSelect<FontFamily | false>
<ButtonIconSelect<FontFamilyValues | false>
group="font-family"
options={options}
value={getFormValue(
elements,
appState,
(element) => isTextElement(element) && element.fontFamily,
(element) => {
if (isTextElement(element)) {
return element.fontFamily;
}
const boundTextElement = getBoundTextElement(element);
if (boundTextElement) {
return boundTextElement.fontFamily;
}
return null;
},
appState.currentItemFontFamily || DEFAULT_FONT_FAMILY,
)}
onChange={(value) => updateData(value)}
@ -500,17 +595,26 @@ export const actionChangeTextAlign = register({
name: "changeTextAlign",
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
textAlign: value,
});
redrawTextBoundingBox(element);
return element;
}
elements: changeProperty(
elements,
appState,
(el) => {
if (isTextElement(el)) {
const element: ExcalidrawTextElement = newElementWith(el, {
textAlign: value,
});
let container = null;
if (el.containerId) {
container = Scene.getScene(el)!.getElement(el.containerId);
}
redrawTextBoundingBox(element, container, appState);
return element;
}
return el;
}),
return el;
},
true,
),
appState: {
...appState,
currentItemTextAlign: value,
@ -521,17 +625,38 @@ export const actionChangeTextAlign = register({
PanelComponent: ({ elements, appState, updateData }) => (
<fieldset>
<legend>{t("labels.textAlign")}</legend>
<ButtonSelect<TextAlign | false>
<ButtonIconSelect<TextAlign | false>
group="text-align"
options={[
{ value: "left", text: t("labels.left") },
{ value: "center", text: t("labels.center") },
{ value: "right", text: t("labels.right") },
{
value: "left",
text: t("labels.left"),
icon: <TextAlignLeftIcon theme={appState.theme} />,
},
{
value: "center",
text: t("labels.center"),
icon: <TextAlignCenterIcon theme={appState.theme} />,
},
{
value: "right",
text: t("labels.right"),
icon: <TextAlignRightIcon theme={appState.theme} />,
},
]}
value={getFormValue(
elements,
appState,
(element) => isTextElement(element) && element.textAlign,
(element) => {
if (isTextElement(element)) {
return element.textAlign;
}
const boundTextElement = getBoundTextElement(element);
if (boundTextElement) {
return boundTextElement.textAlign;
}
return null;
},
appState.currentItemTextAlign,
)}
onChange={(value) => updateData(value)}
@ -580,12 +705,12 @@ export const actionChangeSharpness = register({
{
value: "sharp",
text: t("labels.sharp"),
icon: <EdgeSharpIcon appearance={appState.appearance} />,
icon: <EdgeSharpIcon theme={appState.theme} />,
},
{
value: "round",
text: t("labels.round"),
icon: <EdgeRoundIcon appearance={appState.appearance} />,
icon: <EdgeRoundIcon theme={appState.theme} />,
},
]}
value={getFormValue(
@ -653,42 +778,37 @@ export const actionChangeArrowhead = register({
{
value: null,
text: t("labels.arrowhead_none"),
icon: <ArrowheadNoneIcon appearance={appState.appearance} />,
icon: <ArrowheadNoneIcon theme={appState.theme} />,
keyBinding: "q",
},
{
value: "arrow",
text: t("labels.arrowhead_arrow"),
icon: (
<ArrowheadArrowIcon
appearance={appState.appearance}
flip={!isRTL}
/>
<ArrowheadArrowIcon theme={appState.theme} flip={!isRTL} />
),
keyBinding: "w",
},
{
value: "bar",
text: t("labels.arrowhead_bar"),
icon: (
<ArrowheadBarIcon
appearance={appState.appearance}
flip={!isRTL}
/>
),
icon: <ArrowheadBarIcon theme={appState.theme} flip={!isRTL} />,
keyBinding: "e",
},
{
value: "dot",
text: t("labels.arrowhead_dot"),
icon: (
<ArrowheadDotIcon
appearance={appState.appearance}
flip={!isRTL}
/>
),
icon: <ArrowheadDotIcon theme={appState.theme} flip={!isRTL} />,
keyBinding: "r",
},
{
value: "triangle",
text: t("labels.arrowhead_triangle"),
icon: (
<ArrowheadTriangleIcon theme={appState.theme} flip={!isRTL} />
),
keyBinding: "t",
},
]}
value={getFormValue<Arrowhead | null>(
elements,
@ -709,40 +829,35 @@ export const actionChangeArrowhead = register({
value: null,
text: t("labels.arrowhead_none"),
keyBinding: "q",
icon: <ArrowheadNoneIcon appearance={appState.appearance} />,
icon: <ArrowheadNoneIcon theme={appState.theme} />,
},
{
value: "arrow",
text: t("labels.arrowhead_arrow"),
keyBinding: "w",
icon: (
<ArrowheadArrowIcon
appearance={appState.appearance}
flip={isRTL}
/>
<ArrowheadArrowIcon theme={appState.theme} flip={isRTL} />
),
},
{
value: "bar",
text: t("labels.arrowhead_bar"),
keyBinding: "e",
icon: (
<ArrowheadBarIcon
appearance={appState.appearance}
flip={isRTL}
/>
),
icon: <ArrowheadBarIcon theme={appState.theme} flip={isRTL} />,
},
{
value: "dot",
text: t("labels.arrowhead_dot"),
keyBinding: "r",
icon: <ArrowheadDotIcon theme={appState.theme} flip={isRTL} />,
},
{
value: "triangle",
text: t("labels.arrowhead_triangle"),
icon: (
<ArrowheadDotIcon
appearance={appState.appearance}
flip={isRTL}
/>
<ArrowheadTriangleIcon theme={appState.theme} flip={isRTL} />
),
keyBinding: "t",
},
]}
value={getFormValue<Arrowhead | null>(

View File

@ -1,7 +1,7 @@
import { KEYS } from "../keys";
import { register } from "./register";
import { selectGroupsForSelectedElements } from "../groups";
import { getNonDeletedElements } from "../element";
import { getNonDeletedElements, isTextElement } from "../element";
export const actionSelectAll = register({
name: "selectAll",
@ -15,7 +15,10 @@ export const actionSelectAll = register({
...appState,
editingGroupId: null,
selectedElementIds: elements.reduce((map, element) => {
if (!element.isDeleted) {
if (
!element.isDeleted &&
!(isTextElement(element) && element.containerId)
) {
map[element.id] = true;
}
return map;

View File

@ -12,6 +12,9 @@ import {
DEFAULT_FONT_FAMILY,
DEFAULT_TEXT_ALIGN,
} from "../constants";
import Scene from "../scene/Scene";
import { isBoundToContainer } from "../element/typeChecks";
import { ExcalidrawTextElement } from "../element/types";
// `copiedStyles` is exported only for tests.
export let copiedStyles: string = "{}";
@ -61,7 +64,18 @@ export const actionPasteStyles = register({
fontFamily: pastedElement?.fontFamily || DEFAULT_FONT_FAMILY,
textAlign: pastedElement?.textAlign || DEFAULT_TEXT_ALIGN,
});
redrawTextBoundingBox(newElement);
let container = null;
if (isBoundToContainer(element)) {
container = Scene.getScene(element)!.getElement(
element.containerId,
);
}
redrawTextBoundingBox(
element as ExcalidrawTextElement,
container,
appState,
);
}
return newElement;
}

View File

@ -1,4 +1,5 @@
import { register } from "./register";
import { CODES, KEYS } from "../keys";
export const actionToggleStats = register({
name: "stats",
@ -13,4 +14,6 @@ export const actionToggleStats = register({
},
checked: (appState) => appState.showStats,
contextItemLabel: "stats.title",
keyTest: (event) =>
!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.SLASH,
});

View File

@ -10,7 +10,6 @@ export const actionToggleViewMode = register({
appState: {
...appState,
viewModeEnabled: !this.checked!(appState),
selectedElementIds: {},
},
commitToHistory: false,
};

View File

@ -38,7 +38,7 @@ export const actionSendBackward = register({
onClick={() => updateData(null)}
title={`${t("labels.sendBackward")}${getShortcutKey("CtrlOrCmd+[")}`}
>
<SendBackwardIcon appearance={appState.appearance} />
<SendBackwardIcon theme={appState.theme} />
</button>
),
});
@ -65,7 +65,7 @@ export const actionBringForward = register({
onClick={() => updateData(null)}
title={`${t("labels.bringForward")}${getShortcutKey("CtrlOrCmd+]")}`}
>
<BringForwardIcon appearance={appState.appearance} />
<BringForwardIcon theme={appState.theme} />
</button>
),
});
@ -99,7 +99,7 @@ export const actionSendToBack = register({
: getShortcutKey("CtrlOrCmd+Shift+[")
}`}
>
<SendToBackIcon appearance={appState.appearance} />
<SendToBackIcon theme={appState.theme} />
</button>
),
});
@ -133,7 +133,7 @@ export const actionBringToFront = register({
: getShortcutKey("CtrlOrCmd+Shift+]")
}`}
>
<BringToFrontIcon appearance={appState.appearance} />
<BringToFrontIcon theme={appState.theme} />
</button>
),
});

View File

@ -26,6 +26,7 @@ export {
actionZoomOut,
actionResetZoom,
actionZoomToFit,
actionToggleTheme,
} from "./actionCanvas";
export { actionFinalize } from "./actionFinalize";
@ -33,8 +34,8 @@ export { actionFinalize } from "./actionFinalize";
export {
actionChangeProjectName,
actionChangeExportBackground,
actionSaveScene,
actionSaveAsScene,
actionSaveToActiveFile,
actionSaveFileToDisk,
actionLoadScene,
} from "./actionExport";
@ -66,6 +67,8 @@ export {
distributeVertically,
} from "./actionDistribute";
export { actionFlipHorizontal, actionFlipVertical } from "./actionFlip";
export {
actionCopy,
actionCut,

View File

@ -5,15 +5,12 @@ import {
UpdaterFn,
ActionName,
ActionResult,
PanelComponentProps,
} from "./types";
import { ExcalidrawElement } from "../element/types";
import { AppState, ExcalidrawProps } from "../types";
import { AppClassProperties, AppState } from "../types";
import { MODES } from "../constants";
// This is the <App> component, but for now we don't care about anything but its
// `canvas` state.
type App = { canvas: HTMLCanvasElement | null; props: ExcalidrawProps };
export class ActionManager implements ActionsManagerInterface {
actions = {} as ActionsManagerInterface["actions"];
@ -21,13 +18,13 @@ export class ActionManager implements ActionsManagerInterface {
getAppState: () => Readonly<AppState>;
getElementsIncludingDeleted: () => readonly ExcalidrawElement[];
app: App;
app: AppClassProperties;
constructor(
updater: UpdaterFn,
getAppState: () => AppState,
getElementsIncludingDeleted: () => readonly ExcalidrawElement[],
app: App,
app: AppClassProperties,
) {
this.updater = (actionResult) => {
if (actionResult && "then" in actionResult) {
@ -51,11 +48,15 @@ export class ActionManager implements ActionsManagerInterface {
actions.forEach((action) => this.registerAction(action));
}
handleKeyDown(event: KeyboardEvent) {
handleKeyDown(event: React.KeyboardEvent | KeyboardEvent) {
const canvasActions = this.app.props.UIOptions.canvasActions;
const data = Object.values(this.actions)
.sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0))
.filter(
(action) =>
(action.name in canvasActions
? canvasActions[action.name as keyof typeof canvasActions]
: true) &&
action.keyTest &&
action.keyTest(
event,
@ -97,12 +98,19 @@ export class ActionManager implements ActionsManagerInterface {
);
}
// Id is an attribute that we can use to pass in data like keys.
// This is needed for dynamically generated action components
// like the user list. We can use this key to extract more
// data from app state. This is an alternative to generic prop hell!
renderAction = (name: ActionName, id?: string) => {
if (this.actions[name] && "PanelComponent" in this.actions[name]) {
/**
* @param data additional data sent to the PanelComponent
*/
renderAction = (name: ActionName, data?: PanelComponentProps["data"]) => {
const canvasActions = this.app.props.UIOptions.canvasActions;
if (
this.actions[name] &&
"PanelComponent" in this.actions[name] &&
(name in canvasActions
? canvasActions[name as keyof typeof canvasActions]
: true)
) {
const action = this.actions[name];
const PanelComponent = action.PanelComponent!;
const updateData = (formState?: any) => {
@ -121,7 +129,8 @@ export class ActionManager implements ActionsManagerInterface {
elements={this.getElementsIncludingDeleted()}
appState={this.getAppState()}
updateData={updateData}
id={id}
appProps={this.app.props}
data={data}
/>
);
}

View File

@ -23,7 +23,9 @@ export type ShortcutName =
| "zenMode"
| "stats"
| "addToLibrary"
| "viewMode";
| "viewMode"
| "flipHorizontal"
| "flipVertical";
const shortcutMap: Record<ShortcutName, string[]> = {
cut: [getShortcutKey("CtrlOrCmd+X")],
@ -55,8 +57,10 @@ const shortcutMap: Record<ShortcutName, string[]> = {
ungroup: [getShortcutKey("CtrlOrCmd+Shift+G")],
gridMode: [getShortcutKey("CtrlOrCmd+'")],
zenMode: [getShortcutKey("Alt+Z")],
stats: [],
stats: [getShortcutKey("Alt+/")],
addToLibrary: [],
flipHorizontal: [getShortcutKey("Shift+H")],
flipVertical: [getShortcutKey("Shift+V")],
viewMode: [getShortcutKey("Alt+R")],
};

View File

@ -1,14 +1,25 @@
import React from "react";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import {
AppClassProperties,
AppState,
ExcalidrawProps,
BinaryFiles,
} from "../types";
import { ToolButtonSize } from "../components/ToolButton";
/** if false, the action should be prevented */
export type ActionResult =
| {
elements?: readonly ExcalidrawElement[] | null;
appState?: MarkOptional<AppState, "offsetTop" | "offsetLeft"> | null;
appState?: MarkOptional<
AppState,
"offsetTop" | "offsetLeft" | "width" | "height"
> | null;
files?: BinaryFiles | null;
commitToHistory: boolean;
syncHistory?: boolean;
replaceFiles?: boolean;
}
| false;
@ -16,7 +27,7 @@ type ActionFn = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
formData: any,
app: { canvas: HTMLCanvasElement | null },
app: AppClassProperties,
) => ActionResult | Promise<ActionResult>;
export type UpdaterFn = (res: ActionResult) => void;
@ -42,6 +53,7 @@ export type ActionName =
| "changeBackgroundColor"
| "changeFillStyle"
| "changeStrokeWidth"
| "changeStrokeShape"
| "changeSloppiness"
| "changeStrokeStyle"
| "changeArrowhead"
@ -55,9 +67,9 @@ export type ActionName =
| "changeProjectName"
| "changeExportBackground"
| "changeExportEmbedScene"
| "changeShouldAddWatermark"
| "saveScene"
| "saveAsScene"
| "changeExportScale"
| "saveToActiveFile"
| "saveFileToDisk"
| "loadScene"
| "duplicateSelection"
| "deleteSelectedElements"
@ -85,21 +97,27 @@ export type ActionName =
| "alignHorizontallyCentered"
| "distributeHorizontally"
| "distributeVertically"
| "flipHorizontal"
| "flipVertical"
| "viewMode"
| "exportWithDarkMode";
| "exportWithDarkMode"
| "toggleTheme";
export type PanelComponentProps = {
elements: readonly ExcalidrawElement[];
appState: AppState;
updateData: (formData?: any) => void;
appProps: ExcalidrawProps;
data?: Partial<{ id: string; size: ToolButtonSize }>;
};
export interface Action {
name: ActionName;
PanelComponent?: React.FC<{
elements: readonly ExcalidrawElement[];
appState: AppState;
updateData: (formData?: any) => void;
id?: string;
}>;
PanelComponent?: React.FC<PanelComponentProps>;
perform: ActionFn;
keyPriority?: number;
keyTest?: (
event: KeyboardEvent,
event: React.KeyboardEvent | KeyboardEvent,
appState: AppState,
elements: readonly ExcalidrawElement[],
) => boolean;
@ -114,6 +132,7 @@ export interface Action {
export interface ActionsManagerInterface {
actions: Record<ActionName, Action>;
registerAction: (action: Action) => void;
handleKeyDown: (event: KeyboardEvent) => boolean;
handleKeyDown: (event: React.KeyboardEvent | KeyboardEvent) => boolean;
renderAction: (name: ActionName) => React.ReactElement | null;
executeAction: (action: Action) => void;
}

View File

@ -1,13 +1,7 @@
import { ExcalidrawElement } from "./element/types";
import { newElementWith } from "./element/mutateElement";
import { getCommonBounds } from "./element";
interface Box {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
import { Box, getCommonBoundingBox } from "./element/bounds";
import { getMaximumGroups } from "./groups";
export interface Alignment {
position: "start" | "center" | "end";
@ -37,28 +31,6 @@ export const alignElements = (
});
};
export const getMaximumGroups = (
elements: ExcalidrawElement[],
): ExcalidrawElement[][] => {
const groups: Map<String, ExcalidrawElement[]> = new Map<
String,
ExcalidrawElement[]
>();
elements.forEach((element: ExcalidrawElement) => {
const groupId =
element.groupIds.length === 0
? element.id
: element.groupIds[element.groupIds.length - 1];
const currentGroupMembers = groups.get(groupId) || [];
groups.set(groupId, [...currentGroupMembers, element]);
});
return Array.from(groups.values());
};
const calculateTranslation = (
group: ExcalidrawElement[],
selectionBoundingBox: Box,
@ -88,8 +60,3 @@ const calculateTranslation = (
(groupBoundingBox[min] + groupBoundingBox[max]) / 2,
};
};
const getCommonBoundingBox = (elements: ExcalidrawElement[]): Box => {
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
return { minX, minY, maxX, maxY };
};

View File

@ -3,17 +3,23 @@ import {
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
DEFAULT_TEXT_ALIGN,
EXPORT_SCALES,
THEME,
} from "./constants";
import { t } from "./i18n";
import { AppState, NormalizedZoomValue } from "./types";
import { getDateTime } from "./utils";
const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
? devicePixelRatio
: 1;
export const getDefaultAppState = (): Omit<
AppState,
"offsetTop" | "offsetLeft"
"offsetTop" | "offsetLeft" | "width" | "height"
> => {
return {
appearance: "light",
theme: THEME.LIGHT,
collaborators: new Map(),
currentChartType: "bar",
currentItemBackgroundColor: "transparent",
@ -39,11 +45,11 @@ export const getDefaultAppState = (): Omit<
elementType: "selection",
errorMessage: null,
exportBackground: true,
exportScale: defaultExportScale,
exportEmbedScene: false,
exportWithDarkMode: false,
fileHandle: null,
gridSize: null,
height: window.innerHeight,
isBindingEnabled: true,
isLibraryOpen: false,
isLoading: false,
@ -53,6 +59,7 @@ export const getDefaultAppState = (): Omit<
multiElement: null,
name: `${t("labels.untitled")}-${getDateTime()}`,
openMenu: null,
openPopup: null,
pasteDialog: { shown: false, data: null },
previousSelectedElementIds: {},
resizingElement: null,
@ -62,7 +69,6 @@ export const getDefaultAppState = (): Omit<
selectedElementIds: {},
selectedGroupIds: {},
selectionElement: null,
shouldAddWatermark: false,
shouldCacheIgnoreZoom: false,
showHelpDialog: false,
showStats: false,
@ -70,10 +76,10 @@ export const getDefaultAppState = (): Omit<
suggestedBindings: [],
toastMessage: null,
viewBackgroundColor: oc.white,
width: window.innerWidth,
zenModeEnabled: false,
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
viewModeEnabled: false,
pendingImageElement: null,
};
};
@ -87,77 +93,86 @@ const APP_STATE_STORAGE_CONF = (<
browser: boolean;
/** whether to keep when exporting to file/database */
export: boolean;
/** server (shareLink/collab/...) */
server: boolean;
},
T extends Record<keyof AppState, Values>
>(
config: { [K in keyof T]: K extends keyof AppState ? T[K] : never },
) => config)({
appearance: { browser: true, export: false },
collaborators: { browser: false, export: false },
currentChartType: { browser: true, export: false },
currentItemBackgroundColor: { browser: true, export: false },
currentItemEndArrowhead: { browser: true, export: false },
currentItemFillStyle: { browser: true, export: false },
currentItemFontFamily: { browser: true, export: false },
currentItemFontSize: { browser: true, export: false },
currentItemLinearStrokeSharpness: { browser: true, export: false },
currentItemOpacity: { browser: true, export: false },
currentItemRoughness: { browser: true, export: false },
currentItemStartArrowhead: { browser: true, export: false },
currentItemStrokeColor: { browser: true, export: false },
currentItemStrokeSharpness: { browser: true, export: false },
currentItemStrokeStyle: { browser: true, export: false },
currentItemStrokeWidth: { browser: true, export: false },
currentItemTextAlign: { browser: true, export: false },
cursorButton: { browser: true, export: false },
draggingElement: { browser: false, export: false },
editingElement: { browser: false, export: false },
editingGroupId: { browser: true, export: false },
editingLinearElement: { browser: false, export: false },
elementLocked: { browser: true, export: false },
elementType: { browser: true, export: false },
errorMessage: { browser: false, export: false },
exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false },
exportWithDarkMode: { browser: true, export: false },
fileHandle: { browser: false, export: false },
gridSize: { browser: true, export: true },
height: { browser: false, export: false },
isBindingEnabled: { browser: false, export: false },
isLibraryOpen: { browser: false, export: false },
isLoading: { browser: false, export: false },
isResizing: { browser: false, export: false },
isRotating: { browser: false, export: false },
lastPointerDownWith: { browser: true, export: false },
multiElement: { browser: false, export: false },
name: { browser: true, export: false },
offsetLeft: { browser: false, export: false },
offsetTop: { browser: false, export: false },
openMenu: { browser: true, export: false },
pasteDialog: { browser: false, export: false },
previousSelectedElementIds: { browser: true, export: false },
resizingElement: { browser: false, export: false },
scrolledOutside: { browser: true, export: false },
scrollX: { browser: true, export: false },
scrollY: { browser: true, export: false },
selectedElementIds: { browser: true, export: false },
selectedGroupIds: { browser: true, export: false },
selectionElement: { browser: false, export: false },
shouldAddWatermark: { browser: true, export: false },
shouldCacheIgnoreZoom: { browser: true, export: false },
showHelpDialog: { browser: false, export: false },
showStats: { browser: true, export: false },
startBoundElement: { browser: false, export: false },
suggestedBindings: { browser: false, export: false },
toastMessage: { browser: false, export: false },
viewBackgroundColor: { browser: true, export: true },
width: { browser: false, export: false },
zenModeEnabled: { browser: true, export: false },
zoom: { browser: true, export: false },
viewModeEnabled: { browser: false, export: false },
T extends Record<keyof AppState, Values>,
>(config: { [K in keyof T]: K extends keyof AppState ? T[K] : never }) =>
config)({
theme: { browser: true, export: false, server: false },
collaborators: { browser: false, export: false, server: false },
currentChartType: { browser: true, export: false, server: false },
currentItemBackgroundColor: { browser: true, export: false, server: false },
currentItemEndArrowhead: { browser: true, export: false, server: false },
currentItemFillStyle: { browser: true, export: false, server: false },
currentItemFontFamily: { browser: true, export: false, server: false },
currentItemFontSize: { browser: true, export: false, server: false },
currentItemLinearStrokeSharpness: {
browser: true,
export: false,
server: false,
},
currentItemOpacity: { browser: true, export: false, server: false },
currentItemRoughness: { browser: true, export: false, server: false },
currentItemStartArrowhead: { browser: true, export: false, server: false },
currentItemStrokeColor: { browser: true, export: false, server: false },
currentItemStrokeSharpness: { browser: true, export: false, server: false },
currentItemStrokeStyle: { browser: true, export: false, server: false },
currentItemStrokeWidth: { browser: true, export: false, server: false },
currentItemTextAlign: { browser: true, export: false, server: false },
cursorButton: { browser: true, export: false, server: false },
draggingElement: { browser: false, export: false, server: false },
editingElement: { browser: false, export: false, server: false },
editingGroupId: { browser: true, export: false, server: false },
editingLinearElement: { browser: false, export: false, server: false },
elementLocked: { browser: true, export: false, server: false },
elementType: { browser: true, export: false, server: false },
errorMessage: { browser: false, export: false, server: false },
exportBackground: { browser: true, export: false, server: false },
exportEmbedScene: { browser: true, export: false, server: false },
exportScale: { browser: true, export: false, server: false },
exportWithDarkMode: { browser: true, export: false, server: false },
fileHandle: { browser: false, export: false, server: false },
gridSize: { browser: true, export: true, server: true },
height: { browser: false, export: false, server: false },
isBindingEnabled: { browser: false, export: false, server: false },
isLibraryOpen: { browser: false, export: false, server: false },
isLoading: { browser: false, export: false, server: false },
isResizing: { browser: false, export: false, server: false },
isRotating: { browser: false, export: false, server: false },
lastPointerDownWith: { browser: true, export: false, server: false },
multiElement: { browser: false, export: false, server: false },
name: { browser: true, export: false, server: false },
offsetLeft: { browser: false, export: false, server: false },
offsetTop: { browser: false, export: false, server: false },
openMenu: { browser: true, export: false, server: false },
openPopup: { browser: false, export: false, server: false },
pasteDialog: { browser: false, export: false, server: false },
previousSelectedElementIds: { browser: true, export: false, server: false },
resizingElement: { browser: false, export: false, server: false },
scrolledOutside: { browser: true, export: false, server: false },
scrollX: { browser: true, export: false, server: false },
scrollY: { browser: true, export: false, server: false },
selectedElementIds: { browser: true, export: false, server: false },
selectedGroupIds: { browser: true, export: false, server: false },
selectionElement: { browser: false, export: false, server: false },
shouldCacheIgnoreZoom: { browser: true, export: false, server: false },
showHelpDialog: { browser: false, export: false, server: false },
showStats: { browser: true, export: false, server: false },
startBoundElement: { browser: false, export: false, server: false },
suggestedBindings: { browser: false, export: false, server: false },
toastMessage: { browser: false, export: false, server: false },
viewBackgroundColor: { browser: true, export: true, server: true },
width: { browser: false, export: false, server: false },
zenModeEnabled: { browser: true, export: false, server: false },
zoom: { browser: true, export: false, server: false },
viewModeEnabled: { browser: false, export: false, server: false },
pendingImageElement: { browser: false, export: false, server: false },
});
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
const _clearAppStateForStorage = <
ExportType extends "export" | "browser" | "server",
>(
appState: Partial<AppState>,
exportType: ExportType,
) => {
@ -170,8 +185,10 @@ const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
for (const key of Object.keys(appState) as (keyof typeof appState)[]) {
const propConfig = APP_STATE_STORAGE_CONF[key];
if (propConfig?.[exportType]) {
// @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445
stateForExport[key] = appState[key];
const nextValue = appState[key];
// https://github.com/microsoft/TypeScript/issues/31445
(stateForExport as any)[key] = nextValue;
}
}
return stateForExport;
@ -184,3 +201,7 @@ export const clearAppStateForLocalStorage = (appState: Partial<AppState>) => {
export const cleanAppStateForExport = (appState: Partial<AppState>) => {
return _clearAppStateForStorage(appState, "export");
};
export const clearAppStateForDatabase = (appState: Partial<AppState>) => {
return _clearAppStateForStorage(appState, "server");
};

View File

@ -3,19 +3,26 @@ import {
NonDeletedExcalidrawElement,
} from "./element/types";
import { getSelectedElements } from "./scene";
import { AppState } from "./types";
import { AppState, BinaryFiles } from "./types";
import { SVG_EXPORT_TAG } from "./scene/export";
import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts";
import { canvasToBlob } from "./data/blob";
const TYPE_ELEMENTS = "excalidraw/elements";
import { EXPORT_DATA_TYPES, MIME_TYPES } from "./constants";
import { isInitializedImageElement } from "./element/typeChecks";
type ElementsClipboard = {
type: typeof TYPE_ELEMENTS;
created: number;
type: typeof EXPORT_DATA_TYPES.excalidrawClipboard;
elements: ExcalidrawElement[];
files: BinaryFiles | undefined;
};
export interface ClipboardData {
spreadsheet?: Spreadsheet;
elements?: readonly ExcalidrawElement[];
files?: BinaryFiles;
text?: string;
errorMessage?: string;
}
let CLIPBOARD = "";
let PREFER_APP_CLIPBOARD = false;
@ -31,8 +38,16 @@ export const probablySupportsClipboardBlob =
"ClipboardItem" in window &&
"toBlob" in HTMLCanvasElement.prototype;
const isElementsClipboard = (contents: any): contents is ElementsClipboard => {
if (contents?.type === TYPE_ELEMENTS) {
const clipboardContainsElements = (
contents: any,
): contents is { elements: ExcalidrawElement[]; files?: BinaryFiles } => {
if (
[
EXPORT_DATA_TYPES.excalidraw,
EXPORT_DATA_TYPES.excalidrawClipboard,
].includes(contents?.type) &&
Array.isArray(contents.elements)
) {
return true;
}
return false;
@ -41,18 +56,26 @@ const isElementsClipboard = (contents: any): contents is ElementsClipboard => {
export const copyToClipboard = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
files: BinaryFiles,
) => {
// select binded text elements when copying
const selectedElements = getSelectedElements(elements, appState, true);
const contents: ElementsClipboard = {
type: TYPE_ELEMENTS,
created: Date.now(),
elements: getSelectedElements(elements, appState),
type: EXPORT_DATA_TYPES.excalidrawClipboard,
elements: selectedElements,
files: selectedElements.reduce((acc, element) => {
if (isInitializedImageElement(element) && files[element.fileId]) {
acc[element.fileId] = files[element.fileId];
}
return acc;
}, {} as BinaryFiles),
};
const json = JSON.stringify(contents);
CLIPBOARD = json;
try {
PREFER_APP_CLIPBOARD = false;
await copyTextToSystemClipboard(json);
} catch (error) {
} catch (error: any) {
PREFER_APP_CLIPBOARD = true;
console.error(error);
}
@ -65,7 +88,7 @@ const getAppClipboard = (): Partial<ElementsClipboard> => {
try {
return JSON.parse(CLIPBOARD);
} catch (error) {
} catch (error: any) {
console.error(error);
return {};
}
@ -105,12 +128,7 @@ const getSystemClipboard = async (
*/
export const parseClipboard = async (
event: ClipboardEvent | null,
): Promise<{
spreadsheet?: Spreadsheet;
elements?: readonly ExcalidrawElement[];
text?: string;
errorMessage?: string;
}> => {
): Promise<ClipboardData> => {
const systemClipboard = await getSystemClipboard(event);
// if system clipboard empty, couldn't be resolved, or contains previously
@ -131,15 +149,12 @@ export const parseClipboard = async (
try {
const systemClipboardData = JSON.parse(systemClipboard);
// system clipboard elements are newer than in-app clipboard
if (
isElementsClipboard(systemClipboardData) &&
(!appClipboardData?.created ||
appClipboardData.created < systemClipboardData.created)
) {
return { elements: systemClipboardData.elements };
if (clipboardContainsElements(systemClipboardData)) {
return {
elements: systemClipboardData.elements,
files: systemClipboardData.files,
};
}
// in-app clipboard is newer than system clipboard
return appClipboardData;
} catch {
// system clipboard doesn't contain excalidraw elements → return plaintext
@ -151,10 +166,9 @@ export const parseClipboard = async (
}
};
export const copyCanvasToClipboardAsPng = async (canvas: HTMLCanvasElement) => {
const blob = await canvasToBlob(canvas);
export const copyBlobToClipboardAsPng = async (blob: Blob) => {
await navigator.clipboard.write([
new window.ClipboardItem({ "image/png": blob }),
new window.ClipboardItem({ [MIME_TYPES.png]: blob }),
]);
};
@ -166,7 +180,7 @@ export const copyTextToSystemClipboard = async (text: string | null) => {
// not focused
await navigator.clipboard.writeText(text || "");
copied = true;
} catch (error) {
} catch (error: any) {
console.error(error);
}
}
@ -206,7 +220,7 @@ const copyTextViaExecCommand = (text: string) => {
textarea.setSelectionRange(0, textarea.value.length);
success = document.execCommand("copy");
} catch (error) {
} catch (error: any) {
console.error(error);
}

View File

@ -1,15 +1,16 @@
import React from "react";
import { ActionManager } from "../actions/manager";
import { getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { ExcalidrawElement, PointerType } from "../element/types";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { useIsMobile } from "../components/App";
import {
canChangeSharpness,
canHaveArrowheads,
getTargetElements,
hasBackground,
hasStroke,
hasStrokeStyle,
hasStrokeWidth,
hasText,
} from "../scene";
import { SHAPES } from "../shapes";
@ -17,6 +18,7 @@ import { AppState, Zoom } from "../types";
import { capitalizeString, isTransparent, setCursorForShape } from "../utils";
import Stack from "./Stack";
import { ToolButton } from "./ToolButton";
import { hasStrokeColor } from "../scene/comparisons";
export const SelectedShapeActions = ({
appState,
@ -47,16 +49,36 @@ export const SelectedShapeActions = ({
hasBackground(elementType) ||
targetElements.some((element) => hasBackground(element.type));
let commonSelectedType: string | null = targetElements[0]?.type || null;
for (const element of targetElements) {
if (element.type !== commonSelectedType) {
commonSelectedType = null;
break;
}
}
return (
<div className="panelColumn">
{renderAction("changeStrokeColor")}
{((hasStrokeColor(elementType) &&
elementType !== "image" &&
commonSelectedType !== "image") ||
targetElements.some((element) => hasStrokeColor(element.type))) &&
renderAction("changeStrokeColor")}
{showChangeBackgroundIcons && renderAction("changeBackgroundColor")}
{showFillIcons && renderAction("changeFillStyle")}
{(hasStroke(elementType) ||
targetElements.some((element) => hasStroke(element.type))) && (
{(hasStrokeWidth(elementType) ||
targetElements.some((element) => hasStrokeWidth(element.type))) &&
renderAction("changeStrokeWidth")}
{(elementType === "freedraw" ||
targetElements.some((element) => element.type === "freedraw")) &&
renderAction("changeStrokeShape")}
{(hasStrokeStyle(elementType) ||
targetElements.some((element) => hasStrokeStyle(element.type))) && (
<>
{renderAction("changeStrokeWidth")}
{renderAction("changeStrokeStyle")}
{renderAction("changeSloppiness")}
</>
@ -143,31 +165,24 @@ export const SelectedShapeActions = ({
);
};
const LIBRARY_ICON = (
// fa-th-large
<svg viewBox="0 0 512 512">
<path d="M296 32h192c13.255 0 24 10.745 24 24v160c0 13.255-10.745 24-24 24H296c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24zm-80 0H24C10.745 32 0 42.745 0 56v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24zM0 296v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm296 184h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H296c-13.255 0-24 10.745-24 24v160c0 13.255 10.745 24 24 24z" />
</svg>
);
export const ShapesSwitcher = ({
canvas,
elementType,
setAppState,
isLibraryOpen,
onImageAction,
}: {
canvas: HTMLCanvasElement | null;
elementType: ExcalidrawElement["type"];
setAppState: React.Component<any, AppState>["setState"];
isLibraryOpen: boolean;
onImageAction: (data: { pointerType: PointerType | null }) => void;
}) => (
<>
{SHAPES.map(({ value, icon, key }, index) => {
const label = t(`toolBar.${value}`);
const letter = typeof key === "string" ? key : key[0];
const shortcut = `${capitalizeString(letter)} ${t("helpDialog.or")} ${
index + 1
}`;
const letter = key && (typeof key === "string" ? key : key[0]);
const shortcut = letter
? `${capitalizeString(letter)} ${t("helpDialog.or")} ${index + 1}`
: `${index + 1}`;
return (
<ToolButton
className="Shape"
@ -181,31 +196,20 @@ export const ShapesSwitcher = ({
aria-label={capitalizeString(label)}
aria-keyshortcuts={shortcut}
data-testid={value}
onChange={() => {
onChange={({ pointerType }) => {
setAppState({
elementType: value,
multiElement: null,
selectedElementIds: {},
});
setCursorForShape(canvas, value);
setAppState({});
if (value === "image") {
onImageAction({ pointerType });
}
}}
/>
);
})}
<ToolButton
className="Shape ToolIcon_type_button__library"
type="button"
icon={LIBRARY_ICON}
name="editor-library"
keyBindingLabel="9"
aria-keyshortcuts="9"
title={`${capitalizeString(t("toolBar.library"))} — 9`}
aria-label={capitalizeString(t("toolBar.library"))}
onClick={() => {
setAppState({ isLibraryOpen: !isLibraryOpen });
}}
/>
</>
);
@ -218,12 +222,9 @@ export const ZoomActions = ({
}) => (
<Stack.Col gap={1}>
<Stack.Row gap={1} align="center">
{renderAction("zoomIn")}
{renderAction("zoomOut")}
{renderAction("zoomIn")}
{renderAction("resetZoom")}
<div style={{ marginInlineStart: 4 }}>
{(zoom.value * 100).toFixed(0)}%
</div>
</Stack.Row>
</Stack.Col>
);

View File

@ -0,0 +1,21 @@
.excalidraw {
.ActiveFile {
.ActiveFile__fileName {
display: flex;
align-items: center;
span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 9.3em;
}
svg {
width: 1.15em;
margin-inline-end: 0.3em;
transform: scaleY(0.9);
}
}
}
}

View File

@ -0,0 +1,28 @@
import Stack from "../components/Stack";
import { ToolButton } from "../components/ToolButton";
import { save, file } from "../components/icons";
import { t } from "../i18n";
import "./ActiveFile.scss";
type ActiveFileProps = {
fileName?: string;
onSave: () => void;
};
export const ActiveFile = ({ fileName, onSave }: ActiveFileProps) => (
<Stack.Row className="ActiveFile" gap={1} align="center">
<span className="ActiveFile__fileName">
{file}
<span>{fileName}</span>
</span>
<ToolButton
type="icon"
icon={save}
title={t("buttons.save")}
aria-label={t("buttons.save")}
onClick={onSave}
data-testid="save-button"
/>
</Stack.Row>
);

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,20 @@
import React from "react";
import { ActionManager } from "../actions/manager";
import { AppState } from "../types";
import { DarkModeToggle } from "./DarkModeToggle";
export const BackgroundPickerAndDarkModeToggle = ({
appState,
setAppState,
actionManager,
showThemeBtn,
}: {
actionManager: ActionManager;
appState: AppState;
setAppState: React.Component<any, AppState>["setState"];
showThemeBtn: boolean;
}) => (
<div style={{ display: "flex" }}>
{actionManager.renderAction("changeViewBackgroundColor")}
<div style={{ marginInlineStart: "0.25rem" }}>
<DarkModeToggle
value={appState.appearance}
onChange={(appearance) => {
setAppState({ appearance });
}}
/>
</div>
{showThemeBtn && actionManager.renderAction("toggleTheme")}
</div>
);

View File

@ -1,4 +1,3 @@
import React from "react";
import clsx from "clsx";
export const ButtonIconCycle = <T extends any>({
@ -14,11 +13,11 @@ export const ButtonIconCycle = <T extends any>({
}) => {
const current = options.find((op) => op.value === value);
function cycle() {
const cycle = () => {
const index = options.indexOf(current!);
const next = (index + 1) % options.length;
onChange(options[next].value);
}
};
return (
<label key={group} className={clsx({ active: current!.value !== null })}>

View File

@ -1,4 +1,3 @@
import React from "react";
import clsx from "clsx";
// TODO: It might be "clever" to add option.icon to the existing component <ButtonSelect />

View File

@ -1,4 +1,3 @@
import React from "react";
import clsx from "clsx";
export const ButtonSelect = <T extends Object>({

57
src/components/Card.scss Normal file
View File

@ -0,0 +1,57 @@
@import "../css/variables.module";
.excalidraw {
.Card {
display: flex;
flex-direction: column;
align-items: center;
max-width: 290px;
margin: 1em;
text-align: center;
.Card-icon {
font-size: 2.6em;
display: flex;
flex: 0 0 auto;
padding: 1.4rem;
border-radius: 50%;
background: var(--card-color);
color: $oc-white;
svg {
width: 2.8rem;
height: 2.8rem;
}
}
.Card-details {
font-size: 0.96em;
min-height: 90px;
padding: 0 1em;
margin-bottom: auto;
}
& .Card-button.ToolIcon_type_button {
height: 2.5rem;
margin-top: 1em;
margin-bottom: 0.3em;
background-color: var(--card-color);
&:hover {
background-color: var(--card-color-darker);
}
&:active {
background-color: var(--card-color-darkest);
}
.ToolIcon__label {
color: $oc-white;
}
.Spinner {
--spinner-color: #fff;
}
}
}
}

27
src/components/Card.tsx Normal file
View File

@ -0,0 +1,27 @@
import OpenColor from "open-color";
import "./Card.scss";
export const Card: React.FC<{
color: keyof OpenColor | "primary";
}> = ({ children, color }) => {
return (
<div
className="Card"
style={{
["--card-color" as any]:
color === "primary" ? "var(--color-primary)" : OpenColor[color][7],
["--card-color-darker" as any]:
color === "primary"
? "var(--color-primary-darker)"
: OpenColor[color][8],
["--card-color-darkest" as any]:
color === "primary"
? "var(--color-primary-darkest)"
: OpenColor[color][9],
}}
>
{children}
</div>
);
};

View File

@ -0,0 +1,89 @@
@import "../css/variables.module";
.excalidraw {
.Checkbox {
margin: 4px 0.3em;
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
&:hover:not(.is-checked) .Checkbox-box:not(:focus) {
box-shadow: 0 0 0 2px #{$oc-blue-4};
}
&:hover:not(.is-checked) .Checkbox-box:not(:focus) {
svg {
display: block;
opacity: 0.3;
}
}
&:active {
.Checkbox-box {
box-shadow: 0 0 2px 1px inset #{$oc-blue-7} !important;
}
}
&:hover {
.Checkbox-box {
background-color: fade-out($oc-blue-1, 0.8);
}
}
&.is-checked {
.Checkbox-box {
background-color: #{$oc-blue-1};
svg {
display: block;
}
}
&:hover .Checkbox-box {
background-color: #{$oc-blue-2};
}
}
.Checkbox-box {
width: 22px;
height: 22px;
padding: 0;
flex: 0 0 auto;
margin: 0 1em;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 0 2px #{$oc-blue-7};
background-color: transparent;
border-radius: 4px;
color: #{$oc-blue-7};
&:focus {
box-shadow: 0 0 0 3px #{$oc-blue-7};
}
svg {
display: none;
width: 16px;
height: 16px;
stroke-width: 3px;
}
}
.Checkbox-label {
display: flex;
align-items: center;
}
.excalidraw-tooltip-icon {
width: 1em;
height: 1em;
}
}
}

View File

@ -0,0 +1,30 @@
import React from "react";
import clsx from "clsx";
import { checkIcon } from "./icons";
import "./CheckboxItem.scss";
export const CheckboxItem: React.FC<{
checked: boolean;
onChange: (checked: boolean, event: React.MouseEvent) => void;
className?: string;
}> = ({ children, checked, onChange, className }) => {
return (
<div
className={clsx("Checkbox", className, { "is-checked": checked })}
onClick={(event) => {
onChange(!checked, event);
(
(event.currentTarget as HTMLDivElement).querySelector(
".Checkbox-box",
) as HTMLButtonElement
).focus();
}}
>
<button className="Checkbox-box" role="checkbox" aria-checked={checked}>
{checkIcon}
</button>
<div className="Checkbox-label">{children}</div>
</div>
);
};

View File

@ -0,0 +1,43 @@
import { useState } from "react";
import { t } from "../i18n";
import { useIsMobile } from "./App";
import { trash } from "./icons";
import { ToolButton } from "./ToolButton";
import ConfirmDialog from "./ConfirmDialog";
const ClearCanvas = ({ onConfirm }: { onConfirm: () => void }) => {
const [showDialog, setShowDialog] = useState(false);
const toggleDialog = () => {
setShowDialog(!showDialog);
};
return (
<>
<ToolButton
type="button"
icon={trash}
title={t("buttons.clearReset")}
aria-label={t("buttons.clearReset")}
showAriaLabel={useIsMobile()}
onClick={toggleDialog}
data-testid="clear-canvas-button"
/>
{showDialog && (
<ConfirmDialog
onConfirm={() => {
onConfirm();
toggleDialog();
}}
onCancel={toggleDialog}
title={t("clearCanvasDialog.title")}
>
<p className="clear-canvas__content"> {t("alerts.clearReset")}</p>
</ConfirmDialog>
)}
</>
);
};
export default ClearCanvas;

View File

@ -1,8 +1,7 @@
import React from "react";
import clsx from "clsx";
import { ToolButton } from "./ToolButton";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { useIsMobile } from "../components/App";
import { users } from "./icons";
import "./CollabButton.scss";

View File

@ -73,7 +73,7 @@
box-sizing: border-box;
border: 1px solid #ddd;
background-color: currentColor !important;
filter: var(--appearance-filter);
filter: var(--theme-filter);
&:focus {
/* TODO: only show the border when the color is too light to see as a shadow */
@ -160,7 +160,7 @@
}
.color-picker-input {
width: 12ch; /* length of `transparent` + 1 */
width: 11ch; /* length of `transparent` */
margin: 0;
font-size: 1rem;
background-color: var(--input-bg-color);
@ -192,7 +192,7 @@
position: relative;
overflow: hidden;
background-color: transparent !important;
filter: var(--appearance-filter);
filter: var(--theme-filter);
&:after {
content: "";
@ -218,7 +218,7 @@
left: 2px;
}
@media #{$is-mobile-query} {
@include isMobile {
display: none;
}
}
@ -239,7 +239,7 @@
color: #d4d4d4;
}
&.Appearance_dark {
&.theme--dark {
.color-picker-type-elementBackground .color-picker-keybinding {
color: $oc-black;
}

View File

@ -1,5 +1,6 @@
import React from "react";
import { Popover } from "./Popover";
import { isTransparent } from "../utils";
import "./ColorPicker.scss";
import { isArrowKey, KEYS } from "../keys";
@ -14,7 +15,7 @@ const isValidColor = (color: string) => {
};
const getColor = (color: string): string | null => {
if (color === "transparent") {
if (isTransparent(color)) {
return color;
}
@ -115,6 +116,7 @@ const Picker = ({
onClose();
}
event.nativeEvent.stopImmediatePropagation();
event.stopPropagation();
};
return (
@ -136,36 +138,41 @@ const Picker = ({
}}
tabIndex={0}
>
{colors.map((_color, i) => (
<button
className="color-picker-swatch"
onClick={(event) => {
(event.currentTarget as HTMLButtonElement).focus();
onChange(_color);
}}
title={`${_color}${keyBindings[i].toUpperCase()}`}
aria-label={_color}
aria-keyshortcuts={keyBindings[i]}
style={{ color: _color }}
key={_color}
ref={(el) => {
if (el && i === 0) {
firstItem.current = el;
}
if (el && _color === color) {
activeItem.current = el;
}
}}
onFocus={() => {
onChange(_color);
}}
>
{_color === "transparent" ? (
<div className="color-picker-transparent"></div>
) : undefined}
<span className="color-picker-keybinding">{keyBindings[i]}</span>
</button>
))}
{colors.map((_color, i) => {
const _colorWithoutHash = _color.replace("#", "");
return (
<button
className="color-picker-swatch"
onClick={(event) => {
(event.currentTarget as HTMLButtonElement).focus();
onChange(_color);
}}
title={`${t(`colors.${_colorWithoutHash}`)}${
!isTransparent(_color) ? ` (${_color})` : ""
}${keyBindings[i].toUpperCase()}`}
aria-label={t(`colors.${_colorWithoutHash}`)}
aria-keyshortcuts={keyBindings[i]}
style={{ color: _color }}
key={_color}
ref={(el) => {
if (el && i === 0) {
firstItem.current = el;
}
if (el && _color === color) {
activeItem.current = el;
}
}}
onFocus={() => {
onChange(_color);
}}
>
{isTransparent(_color) ? (
<div className="color-picker-transparent"></div>
) : undefined}
<span className="color-picker-keybinding">{keyBindings[i]}</span>
</button>
);
})}
{showInput && (
<ColorInput
color={color}
@ -237,13 +244,16 @@ export const ColorPicker = ({
color,
onChange,
label,
isActive,
setActive,
}: {
type: "canvasBackground" | "elementBackground" | "elementStroke";
color: string | null;
onChange: (color: string) => void;
label: string;
isActive: boolean;
setActive: (active: boolean) => void;
}) => {
const [isActive, setActive] = React.useState(false);
const pickerButton = React.useRef<HTMLButtonElement>(null);
return (

View File

@ -0,0 +1,37 @@
@import "../css/variables.module";
.excalidraw {
.confirm-dialog {
&-buttons {
display: flex;
padding: 0.2rem 0;
justify-content: flex-end;
}
.ToolIcon__icon {
min-width: 2.5rem;
width: auto;
font-size: 1rem;
}
.ToolIcon_type_button {
margin-left: 0.8rem;
padding: 0 0.5rem;
}
&__content {
font-size: 1rem;
}
&--confirm.ToolIcon_type_button {
background-color: $oc-red-6;
&:hover {
background-color: $oc-red-8;
}
.ToolIcon__icon {
color: $oc-white;
}
}
}
}

View File

@ -0,0 +1,52 @@
import { t } from "../i18n";
import { Dialog, DialogProps } from "./Dialog";
import { ToolButton } from "./ToolButton";
import "./ConfirmDialog.scss";
interface Props extends Omit<DialogProps, "onCloseRequest"> {
onConfirm: () => void;
onCancel: () => void;
confirmText?: string;
cancelText?: string;
}
const ConfirmDialog = (props: Props) => {
const {
onConfirm,
onCancel,
children,
confirmText = t("buttons.confirm"),
cancelText = t("buttons.cancel"),
className = "",
...rest
} = props;
return (
<Dialog
onCloseRequest={onCancel}
small={true}
{...rest}
className={`confirm-dialog ${className}`}
>
{children}
<div className="confirm-dialog-buttons">
<ToolButton
type="button"
title={cancelText}
aria-label={cancelText}
label={cancelText}
onClick={onCancel}
className="confirm-dialog--cancel"
/>
<ToolButton
type="button"
title={confirmText}
aria-label={confirmText}
label={confirmText}
onClick={onConfirm}
className="confirm-dialog--confirm"
/>
</div>
</Dialog>
);
};
export default ConfirmDialog;

View File

@ -76,7 +76,7 @@
z-index: 1;
}
@media #{$is-mobile-query} {
@include isMobile {
.context-menu-option {
display: block;

View File

@ -1,4 +1,3 @@
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import clsx from "clsx";
import { Popover } from "./Popover";
@ -32,67 +31,63 @@ const ContextMenu = ({
actionManager,
appState,
}: ContextMenuProps) => {
const isDarkTheme = !!document
.querySelector(".excalidraw")
?.classList.contains("Appearance_dark");
return (
<div
className={clsx("excalidraw", {
"Appearance_dark Appearance_dark-background-none": isDarkTheme,
})}
<Popover
onCloseRequest={onCloseRequest}
top={top}
left={left}
fitInViewport={true}
>
<Popover
onCloseRequest={onCloseRequest}
top={top}
left={left}
fitInViewport={true}
<ul
className="context-menu"
onContextMenu={(event) => event.preventDefault()}
>
<ul
className="context-menu"
onContextMenu={(event) => event.preventDefault()}
>
{options.map((option, idx) => {
if (option === "separator") {
return <hr key={idx} className="context-menu-option-separator" />;
}
{options.map((option, idx) => {
if (option === "separator") {
return <hr key={idx} className="context-menu-option-separator" />;
}
const actionName = option.name;
const label = option.contextItemLabel
? t(option.contextItemLabel)
: "";
return (
<li key={idx} data-testid={actionName} onClick={onCloseRequest}>
<button
className={clsx("context-menu-option", {
dangerous: actionName === "deleteSelectedElements",
checkmark: option.checked?.(appState),
})}
onClick={() => actionManager.executeAction(option)}
>
<div className="context-menu-option__label">{label}</div>
<kbd className="context-menu-option__shortcut">
{actionName
? getShortcutFromShortcutName(actionName as ShortcutName)
: ""}
</kbd>
</button>
</li>
);
})}
</ul>
</Popover>
</div>
const actionName = option.name;
const label = option.contextItemLabel
? t(option.contextItemLabel)
: "";
return (
<li key={idx} data-testid={actionName} onClick={onCloseRequest}>
<button
className={clsx("context-menu-option", {
dangerous: actionName === "deleteSelectedElements",
checkmark: option.checked?.(appState),
})}
onClick={() => actionManager.executeAction(option)}
>
<div className="context-menu-option__label">{label}</div>
<kbd className="context-menu-option__shortcut">
{actionName
? getShortcutFromShortcutName(actionName as ShortcutName)
: ""}
</kbd>
</button>
</li>
);
})}
</ul>
</Popover>
);
};
let contextMenuNode: HTMLDivElement;
const getContextMenuNode = (): HTMLDivElement => {
const contextMenuNodeByContainer = new WeakMap<HTMLElement, HTMLDivElement>();
const getContextMenuNode = (container: HTMLElement): HTMLDivElement => {
let contextMenuNode = contextMenuNodeByContainer.get(container);
if (contextMenuNode) {
return contextMenuNode;
}
const div = document.createElement("div");
document.body.appendChild(div);
return (contextMenuNode = div);
contextMenuNode = document.createElement("div");
container
.querySelector(".excalidraw-contextMenuContainer")!
.appendChild(contextMenuNode);
contextMenuNodeByContainer.set(container, contextMenuNode);
return contextMenuNode;
};
type ContextMenuParams = {
@ -101,10 +96,16 @@ type ContextMenuParams = {
left: ContextMenuProps["left"];
actionManager: ContextMenuProps["actionManager"];
appState: Readonly<AppState>;
container: HTMLElement;
};
const handleClose = () => {
unmountComponentAtNode(getContextMenuNode());
const handleClose = (container: HTMLElement) => {
const contextMenuNode = contextMenuNodeByContainer.get(container);
if (contextMenuNode) {
unmountComponentAtNode(contextMenuNode);
contextMenuNode.remove();
contextMenuNodeByContainer.delete(container);
}
};
export default {
@ -121,11 +122,11 @@ export default {
top={params.top}
left={params.left}
options={options}
onCloseRequest={handleClose}
onCloseRequest={() => handleClose(params.container)}
actionManager={params.actionManager}
appState={params.appState}
/>,
getContextMenuNode(),
getContextMenuNode(params.container),
);
}
},

View File

@ -1,41 +1,32 @@
import "./ToolIcon.scss";
import React from "react";
import { t } from "../i18n";
export type Appearence = "light" | "dark";
import { ToolButton } from "./ToolButton";
import { THEME } from "../constants";
import { Theme } from "../element/types";
// We chose to use only explicit toggle and not a third option for system value,
// but this could be added in the future.
export const DarkModeToggle = (props: {
value: Appearence;
onChange: (value: Appearence) => void;
value: Theme;
onChange: (value: Theme) => void;
title?: string;
}) => {
const title = props.title
? props.title
: props.value === "dark"
? t("buttons.lightMode")
: t("buttons.darkMode");
const title =
props.title ||
(props.value === "dark" ? t("buttons.lightMode") : t("buttons.darkMode"));
return (
<label
className={`ToolIcon ToolIcon_type_floating ToolIcon_size_M`}
<ToolButton
type="icon"
icon={props.value === THEME.LIGHT ? ICONS.MOON : ICONS.SUN}
title={title}
>
<input
className="ToolIcon_type_checkbox ToolIcon_toggle_opaque"
type="checkbox"
onChange={(event) =>
props.onChange(event.target.checked ? "dark" : "light")
}
checked={props.value === "dark"}
aria-label={title}
/>
<div className="ToolIcon__icon">
{props.value === "light" ? ICONS.MOON : ICONS.SUN}
</div>
</label>
aria-label={title}
onClick={() =>
props.onChange(props.value === THEME.DARK ? THEME.LIGHT : THEME.DARK)
}
data-testid="toggle-dark-mode"
/>
);
};

View File

@ -31,7 +31,7 @@
padding: 0 16px 16px;
}
@media #{$is-mobile-query} {
@include isMobile {
.Dialog {
--metric: calc(var(--space-factor) * 4);
--inset-left: #{"max(var(--metric), var(--sal))"};

View File

@ -1,23 +1,30 @@
import clsx from "clsx";
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useCallbackRefState } from "../hooks/useCallbackRefState";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { useExcalidrawContainer, useIsMobile } from "../components/App";
import { KEYS } from "../keys";
import "./Dialog.scss";
import { back, close } from "./icons";
import { Island } from "./Island";
import { Modal } from "./Modal";
import { AppState } from "../types";
export const Dialog = (props: {
export interface DialogProps {
children: React.ReactNode;
className?: string;
small?: boolean;
onCloseRequest(): void;
title: React.ReactNode;
autofocus?: boolean;
}) => {
theme?: AppState["theme"];
closeOnClickOutside?: boolean;
}
export const Dialog = (props: DialogProps) => {
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
const [lastActiveElement] = useState(document.activeElement);
const { id } = useExcalidrawContainer();
useEffect(() => {
if (!islandNode) {
@ -65,19 +72,26 @@ export const Dialog = (props: {
return focusableElements ? Array.from(focusableElements) : [];
};
const onClose = () => {
(lastActiveElement as HTMLElement).focus();
props.onCloseRequest();
};
return (
<Modal
className={clsx("Dialog", props.className)}
labelledBy="dialog-title"
maxWidth={props.small ? 550 : 800}
onCloseRequest={props.onCloseRequest}
onCloseRequest={onClose}
theme={props.theme}
closeOnClickOutside={props.closeOnClickOutside}
>
<Island ref={setIslandNode}>
<h2 id="dialog-title" className="Dialog__title">
<h2 id={`${id}-dialog-title`} className="Dialog__title">
<span className="Dialog__titleContent">{props.title}</span>
<button
className="Modal__close"
onClick={props.onCloseRequest}
onClick={onClose}
aria-label={t("buttons.close")}
>
{useIsMobile() ? back : close}

View File

@ -2,6 +2,7 @@ import React, { useState } from "react";
import { t } from "../i18n";
import { Dialog } from "./Dialog";
import { useExcalidrawContainer } from "./App";
export const ErrorDialog = ({
message,
@ -11,6 +12,7 @@ export const ErrorDialog = ({
onClose?: () => void;
}) => {
const [modalIsShown, setModalIsShown] = useState(!!message);
const { container: excalidrawContainer } = useExcalidrawContainer();
const handleClose = React.useCallback(() => {
setModalIsShown(false);
@ -18,7 +20,9 @@ export const ErrorDialog = ({
if (onClose) {
onClose();
}
}, [onClose]);
// TODO: Fix the A11y issues so this is never needed since we should always focus on last active element
excalidrawContainer?.focus();
}, [onClose, excalidrawContainer]);
return (
<>
@ -28,14 +32,7 @@ export const ErrorDialog = ({
onCloseRequest={handleClose}
title={t("errorDialog.title")}
>
<div>
{message.split("\n").map((line) => (
<>
{line}
<br />
</>
))}
</div>
<div style={{ whiteSpace: "pre-wrap" }}>{message}</div>
</Dialog>
)}
</>

View File

@ -16,7 +16,7 @@
max-height: 25rem;
}
&.Appearance_dark .ExportDialog__preview canvas {
&.theme--dark .ExportDialog__preview canvas {
filter: none;
}
@ -28,16 +28,7 @@
justify-content: space-between;
}
.ExportDialog__name {
grid-column: project-name;
margin: auto;
.TextInput {
height: calc(1rem - 3px);
}
}
@media #{$is-mobile-query} {
@include isMobile {
.ExportDialog {
display: flex;
flex-direction: column;
@ -66,4 +57,63 @@
overflow-y: auto;
}
}
.ExportDialog--json {
.ExportDialog-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
justify-items: center;
row-gap: 2em;
@media (max-width: 460px) {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
.Card-details {
min-height: 40px;
}
}
.ProjectName {
width: fit-content;
margin: 1em auto;
align-items: flex-start;
flex-direction: column;
.TextInput {
width: auto;
}
}
.ProjectName-label {
margin: 0.625em 0;
font-weight: bold;
}
}
}
button.ExportDialog-imageExportButton {
width: 5rem;
height: 5rem;
margin: 0 0.2em;
border-radius: 1rem;
background-color: var(--button-color);
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.28),
0 6px 10px 0 rgba(0, 0, 0, 0.14);
font-family: Cascadia;
font-size: 1.8em;
color: $oc-white;
&:hover {
background-color: var(--button-color-darker);
}
&:active {
background-color: var(--button-color-darkest);
box-shadow: none;
}
svg {
width: 0.9em;
}
}
}

View File

@ -1,284 +0,0 @@
import React, { useEffect, useRef, useState } from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { ActionsManagerInterface } from "../actions/types";
import { probablySupportsClipboardBlob } from "../clipboard";
import { canvasToBlob } from "../data/blob";
import { NonDeletedExcalidrawElement } from "../element/types";
import { CanvasError } from "../errors";
import { t } from "../i18n";
import useIsMobile from "../is-mobile";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { exportToCanvas, getExportSize } from "../scene/export";
import { AppState } from "../types";
import { Dialog } from "./Dialog";
import "./ExportDialog.scss";
import { clipboard, exportFile, link } from "./icons";
import Stack from "./Stack";
import { ToolButton } from "./ToolButton";
const scales = [1, 2, 3];
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
const supportsContextFilters =
"filter" in document.createElement("canvas").getContext("2d")!;
export const ErrorCanvasPreview = () => {
return (
<div>
<h3>{t("canvasError.cannotShowPreview")}</h3>
<p>
<span>{t("canvasError.canvasTooBig")}</span>
</p>
<em>({t("canvasError.canvasTooBigTip")})</em>
</div>
);
};
const renderPreview = (
content: HTMLCanvasElement | Error,
previewNode: HTMLDivElement,
) => {
unmountComponentAtNode(previewNode);
previewNode.innerHTML = "";
if (content instanceof HTMLCanvasElement) {
previewNode.appendChild(content);
} else {
render(<ErrorCanvasPreview />, previewNode);
}
};
export type ExportCB = (
elements: readonly NonDeletedExcalidrawElement[],
scale?: number,
) => void;
const ExportModal = ({
elements,
appState,
exportPadding = 10,
actionManager,
onExportToPng,
onExportToSvg,
onExportToClipboard,
onExportToBackend,
}: {
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
exportPadding?: number;
actionManager: ActionsManagerInterface;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
onExportToClipboard: ExportCB;
onExportToBackend?: ExportCB;
onCloseRequest: () => void;
}) => {
const someElementIsSelected = isSomeElementSelected(elements, appState);
const [scale, setScale] = useState(defaultScale);
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const previewRef = useRef<HTMLDivElement>(null);
const {
exportBackground,
viewBackgroundColor,
shouldAddWatermark,
} = appState;
const exportedElements = exportSelected
? getSelectedElements(elements, appState)
: elements;
useEffect(() => {
setExportSelected(someElementIsSelected);
}, [someElementIsSelected]);
useEffect(() => {
const previewNode = previewRef.current;
if (!previewNode) {
return;
}
try {
const canvas = exportToCanvas(exportedElements, appState, {
exportBackground,
viewBackgroundColor,
exportPadding,
scale,
shouldAddWatermark,
});
// if converting to blob fails, there's some problem that will
// likely prevent preview and export (e.g. canvas too big)
canvasToBlob(canvas)
.then(() => {
renderPreview(canvas, previewNode);
})
.catch((error) => {
console.error(error);
renderPreview(new CanvasError(), previewNode);
});
} catch (error) {
console.error(error);
renderPreview(new CanvasError(), previewNode);
}
}, [
appState,
exportedElements,
exportBackground,
exportPadding,
viewBackgroundColor,
scale,
shouldAddWatermark,
]);
return (
<div className="ExportDialog">
<div className="ExportDialog__preview" ref={previewRef} />
{supportsContextFilters &&
actionManager.renderAction("exportWithDarkMode")}
<Stack.Col gap={2} align="center">
<div className="ExportDialog__actions">
<Stack.Row gap={2}>
<ToolButton
type="button"
label="PNG"
title={t("buttons.exportToPng")}
aria-label={t("buttons.exportToPng")}
onClick={() => onExportToPng(exportedElements, scale)}
/>
<ToolButton
type="button"
label="SVG"
title={t("buttons.exportToSvg")}
aria-label={t("buttons.exportToSvg")}
onClick={() => onExportToSvg(exportedElements, scale)}
/>
{probablySupportsClipboardBlob && (
<ToolButton
type="button"
icon={clipboard}
title={t("buttons.copyPngToClipboard")}
aria-label={t("buttons.copyPngToClipboard")}
onClick={() => onExportToClipboard(exportedElements, scale)}
/>
)}
{onExportToBackend && (
<ToolButton
type="button"
icon={link}
title={t("buttons.getShareableLink")}
aria-label={t("buttons.getShareableLink")}
onClick={() => onExportToBackend(exportedElements)}
/>
)}
</Stack.Row>
<div className="ExportDialog__name">
{actionManager.renderAction("changeProjectName")}
</div>
<Stack.Row gap={2}>
{scales.map((s) => {
const [width, height] = getExportSize(
exportedElements,
exportPadding,
shouldAddWatermark,
s,
);
const scaleButtonTitle = `${t(
"buttons.scale",
)} ${s}x (${width}x${height})`;
return (
<ToolButton
key={s}
size="s"
type="radio"
icon={`${s}x`}
name="export-canvas-scale"
title={scaleButtonTitle}
aria-label={scaleButtonTitle}
id="export-canvas-scale"
checked={s === scale}
onChange={() => setScale(s)}
/>
);
})}
</Stack.Row>
</div>
{actionManager.renderAction("changeExportBackground")}
{someElementIsSelected && (
<div>
<label>
<input
type="checkbox"
checked={exportSelected}
onChange={(event) =>
setExportSelected(event.currentTarget.checked)
}
/>{" "}
{t("labels.onlySelected")}
</label>
</div>
)}
{actionManager.renderAction("changeExportEmbedScene")}
{actionManager.renderAction("changeShouldAddWatermark")}
</Stack.Col>
</div>
);
};
export const ExportDialog = ({
elements,
appState,
exportPadding = 10,
actionManager,
onExportToPng,
onExportToSvg,
onExportToClipboard,
onExportToBackend,
}: {
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
exportPadding?: number;
actionManager: ActionsManagerInterface;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
onExportToClipboard: ExportCB;
onExportToBackend?: ExportCB;
}) => {
const [modalIsShown, setModalIsShown] = useState(false);
const triggerButton = useRef<HTMLButtonElement>(null);
const handleClose = React.useCallback(() => {
setModalIsShown(false);
triggerButton.current?.focus();
}, []);
return (
<>
<ToolButton
onClick={() => {
setModalIsShown(true);
}}
icon={exportFile}
type="button"
aria-label={t("buttons.export")}
showAriaLabel={useIsMobile()}
title={t("buttons.export")}
ref={triggerButton}
/>
{modalIsShown && (
<Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
<ExportModal
elements={elements}
appState={appState}
exportPadding={exportPadding}
actionManager={actionManager}
onExportToPng={onExportToPng}
onExportToSvg={onExportToSvg}
onExportToClipboard={onExportToClipboard}
onExportToBackend={onExportToBackend}
onCloseRequest={handleClose}
/>
</Dialog>
)}
</>
);
};

View File

@ -1,6 +1,5 @@
.excalidraw {
.FixedSideContainer {
--margin: 0.25rem;
position: absolute;
pointer-events: none;
}
@ -10,9 +9,9 @@
}
.FixedSideContainer_side_top {
left: var(--margin);
top: var(--margin);
right: var(--margin);
left: var(--space-factor);
top: var(--space-factor);
right: var(--space-factor);
z-index: 2;
}
@ -23,16 +22,16 @@
/* TODO: if these are used, make sure to implement RTL support
.FixedSideContainer_side_left {
left: var(--margin);
top: var(--margin);
bottom: var(--margin);
left: var(--space-factor);
top: var(--space-factor);
bottom: var(--space-factor);
z-index: 1;
}
.FixedSideContainer_side_right {
right: var(--margin);
top: var(--margin);
bottom: var(--margin);
right: var(--space-factor);
top: var(--space-factor);
bottom: var(--space-factor);
z-index: 3;
}
*/

View File

@ -153,10 +153,19 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
<Shortcut label={t("toolBar.arrow")} shortcuts={["A", "5"]} />
<Shortcut label={t("toolBar.line")} shortcuts={["P", "6"]} />
<Shortcut
label={t("toolBar.draw")}
label={t("toolBar.freedraw")}
shortcuts={["Shift+P", "7"]}
/>
<Shortcut label={t("toolBar.text")} shortcuts={["T", "8"]} />
<Shortcut label={t("toolBar.image")} shortcuts={["9"]} />
<Shortcut label={t("toolBar.library")} shortcuts={["0"]} />
<Shortcut
label={t("helpDialog.editSelectedShape")}
shortcuts={[
getShortcutKey("Enter"),
t("helpDialog.doubleClick"),
]}
/>
<Shortcut
label={t("helpDialog.textNewLine")}
shortcuts={[
@ -231,6 +240,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
label={t("labels.viewMode")}
shortcuts={[getShortcutKey("Alt+R")]}
/>
<Shortcut
label={t("labels.toggleTheme")}
shortcuts={[getShortcutKey("Alt+Shift+D")]}
/>
<Shortcut
label={t("stats.title")}
shortcuts={[getShortcutKey("Alt+/")]}
/>
</ShortcutIsland>
</Column>
<Column>
@ -243,6 +260,18 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
label={t("labels.multiSelect")}
shortcuts={[getShortcutKey(`Shift+${t("helpDialog.click")}`)]}
/>
<Shortcut
label={t("helpDialog.deepSelect")}
shortcuts={[
getShortcutKey(`CtrlOrCmd+${t("helpDialog.click")}`),
]}
/>
<Shortcut
label={t("helpDialog.deepBoxSelect")}
shortcuts={[
getShortcutKey(`CtrlOrCmd+${t("helpDialog.drag")}`),
]}
/>
<Shortcut
label={t("labels.moveCanvas")}
shortcuts={[
@ -349,6 +378,22 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
label={t("labels.ungroup")}
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+G")]}
/>
<Shortcut
label={t("labels.flipHorizontal")}
shortcuts={[getShortcutKey("Shift+H")]}
/>
<Shortcut
label={t("labels.flipVertical")}
shortcuts={[getShortcutKey("Shift+V")]}
/>
<Shortcut
label={t("labels.showStroke")}
shortcuts={[getShortcutKey("S")]}
/>
<Shortcut
label={t("labels.showBackground")}
shortcuts={[getShortcutKey("G")]}
/>
</ShortcutIsland>
</Column>
</Columns>

View File

@ -1,4 +1,3 @@
import React from "react";
import { questionCircle } from "../components/icons";
type HelpIconProps = {
@ -9,7 +8,13 @@ type HelpIconProps = {
};
export const HelpIcon = (props: HelpIconProps) => (
<label title={`${props.title} — ?`} className="help-icon">
<div onClick={props.onClick}>{questionCircle}</div>
</label>
<button
className="help-icon"
onClick={props.onClick}
type="button"
title={`${props.title} — ?`}
aria-label={props.title}
>
{questionCircle}
</button>
);

View File

@ -19,7 +19,7 @@ $wide-viewport-width: 1000px;
color: $oc-gray-6;
font-size: 0.8rem;
@media #{$is-mobile-query} {
@include isMobile {
position: static;
padding-right: 2em;
}

View File

@ -1,21 +1,27 @@
import React from "react";
import { t } from "../i18n";
import { NonDeletedExcalidrawElement } from "../element/types";
import { getSelectedElements } from "../scene";
import "./HintViewer.scss";
import { AppState } from "../types";
import { isLinearElement } from "../element/typeChecks";
import {
isImageElement,
isLinearElement,
isTextBindableContainer,
isTextElement,
} from "../element/typeChecks";
import { getShortcutKey } from "../utils";
interface Hint {
interface HintViewerProps {
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
isMobile: boolean;
}
const getHints = ({ appState, elements }: Hint) => {
const getHints = ({ appState, elements, isMobile }: HintViewerProps) => {
const { elementType, isResizing, isRotating, lastPointerDownWith } = appState;
const multiMode = appState.multiElement !== null;
if (elementType === "arrow" || elementType === "line") {
if (!multiMode) {
return t("hints.linearElement");
@ -23,7 +29,7 @@ const getHints = ({ appState, elements }: Hint) => {
return t("hints.linearElementMulti");
}
if (elementType === "draw") {
if (elementType === "freedraw") {
return t("hints.freeDraw");
}
@ -31,7 +37,12 @@ const getHints = ({ appState, elements }: Hint) => {
return t("hints.text");
}
if (appState.elementType === "image" && appState.pendingImageElement) {
return t("hints.placeImage");
}
const selectedElements = getSelectedElements(elements, appState);
if (
isResizing &&
lastPointerDownWith === "mouse" &&
@ -41,29 +52,62 @@ const getHints = ({ appState, elements }: Hint) => {
if (isLinearElement(targetElement) && targetElement.points.length === 2) {
return t("hints.lockAngle");
}
return t("hints.resize");
return isImageElement(targetElement)
? t("hints.resizeImage")
: t("hints.resize");
}
if (isRotating && lastPointerDownWith === "mouse") {
return t("hints.rotate");
}
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
if (appState.editingLinearElement) {
return appState.editingLinearElement.activePointIndex
? t("hints.lineEditor_pointSelected")
: t("hints.lineEditor_nothingSelected");
if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
return t("hints.text_selected");
}
if (appState.editingElement && isTextElement(appState.editingElement)) {
return t("hints.text_editing");
}
if (elementType === "selection") {
if (
appState.draggingElement?.type === "selection" &&
!appState.editingElement &&
!appState.editingLinearElement
) {
return t("hints.deepBoxSelect");
}
if (!selectedElements.length && !isMobile) {
return t("hints.canvasPanning");
}
}
if (selectedElements.length === 1) {
if (isLinearElement(selectedElements[0])) {
if (appState.editingLinearElement) {
return appState.editingLinearElement.selectedPointsIndices
? t("hints.lineEditor_pointSelected")
: t("hints.lineEditor_nothingSelected");
}
return t("hints.lineEditor_info");
}
if (isTextBindableContainer(selectedElements[0])) {
return t("hints.bindTextToElement");
}
return t("hints.lineEditor_info");
}
return null;
};
export const HintViewer = ({ appState, elements }: Hint) => {
export const HintViewer = ({
appState,
elements,
isMobile,
}: HintViewerProps) => {
let hint = getHints({
appState,
elements,
isMobile,
});
if (!hint) {
return null;

View File

@ -22,7 +22,7 @@
align-items: center;
justify-content: center;
&:focus {
&:focus-visible {
outline: transparent;
background-color: var(--button-gray-2);
& svg {
@ -90,7 +90,7 @@
.picker-content {
padding: 0.5rem;
display: grid;
grid-auto-flow: column;
grid-template-columns: repeat(3, auto);
grid-gap: 0.5rem;
border-radius: 4px;
:root[dir="rtl"] & {
@ -111,7 +111,7 @@
:root[dir="rtl"] & {
left: 2px;
}
@media #{$is-mobile-query} {
@include isMobile {
display: none;
}
}
@ -132,7 +132,7 @@
color: #d4d4d4;
}
&.Appearance_dark {
&.theme--dark {
.picker-type-elementBackground .picker-keybinding {
color: $oc-black;
}

View File

@ -88,6 +88,7 @@ function Picker<T>({
onClose();
}
event.nativeEvent.stopImmediatePropagation();
event.stopPropagation();
};
return (

View File

@ -0,0 +1,273 @@
import React, { useEffect, useRef, useState } from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { ActionsManagerInterface } from "../actions/types";
import { probablySupportsClipboardBlob } from "../clipboard";
import { canvasToBlob } from "../data/blob";
import { NonDeletedExcalidrawElement } from "../element/types";
import { CanvasError } from "../errors";
import { t } from "../i18n";
import { useIsMobile } from "./App";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { exportToCanvas } from "../scene/export";
import { AppState, BinaryFiles } from "../types";
import { Dialog } from "./Dialog";
import { clipboard, exportImage } from "./icons";
import Stack from "./Stack";
import { ToolButton } from "./ToolButton";
import "./ExportDialog.scss";
import OpenColor from "open-color";
import { CheckboxItem } from "./CheckboxItem";
import { DEFAULT_EXPORT_PADDING } from "../constants";
import { nativeFileSystemSupported } from "../data/filesystem";
const supportsContextFilters =
"filter" in document.createElement("canvas").getContext("2d")!;
export const ErrorCanvasPreview = () => {
return (
<div>
<h3>{t("canvasError.cannotShowPreview")}</h3>
<p>
<span>{t("canvasError.canvasTooBig")}</span>
</p>
<em>({t("canvasError.canvasTooBigTip")})</em>
</div>
);
};
const renderPreview = (
content: HTMLCanvasElement | Error,
previewNode: HTMLDivElement,
) => {
unmountComponentAtNode(previewNode);
previewNode.innerHTML = "";
if (content instanceof HTMLCanvasElement) {
previewNode.appendChild(content);
} else {
render(<ErrorCanvasPreview />, previewNode);
}
};
export type ExportCB = (
elements: readonly NonDeletedExcalidrawElement[],
scale?: number,
) => void;
const ExportButton: React.FC<{
color: keyof OpenColor;
onClick: () => void;
title: string;
shade?: number;
}> = ({ children, title, onClick, color, shade = 6 }) => {
return (
<button
className="ExportDialog-imageExportButton"
style={{
["--button-color" as any]: OpenColor[color][shade],
["--button-color-darker" as any]: OpenColor[color][shade + 1],
["--button-color-darkest" as any]: OpenColor[color][shade + 2],
}}
title={title}
aria-label={title}
onClick={onClick}
>
{children}
</button>
);
};
const ImageExportModal = ({
elements,
appState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
onExportToClipboard,
}: {
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionsManagerInterface;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
onExportToClipboard: ExportCB;
onCloseRequest: () => void;
}) => {
const someElementIsSelected = isSomeElementSelected(elements, appState);
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const previewRef = useRef<HTMLDivElement>(null);
const { exportBackground, viewBackgroundColor } = appState;
const exportedElements = exportSelected
? getSelectedElements(elements, appState, true)
: elements;
useEffect(() => {
setExportSelected(someElementIsSelected);
}, [someElementIsSelected]);
useEffect(() => {
const previewNode = previewRef.current;
if (!previewNode) {
return;
}
exportToCanvas(exportedElements, appState, files, {
exportBackground,
viewBackgroundColor,
exportPadding,
})
.then((canvas) => {
// if converting to blob fails, there's some problem that will
// likely prevent preview and export (e.g. canvas too big)
return canvasToBlob(canvas).then(() => {
renderPreview(canvas, previewNode);
});
})
.catch((error) => {
console.error(error);
renderPreview(new CanvasError(), previewNode);
});
}, [
appState,
files,
exportedElements,
exportBackground,
exportPadding,
viewBackgroundColor,
]);
return (
<div className="ExportDialog">
<div className="ExportDialog__preview" ref={previewRef} />
{supportsContextFilters &&
actionManager.renderAction("exportWithDarkMode")}
<div style={{ display: "grid", gridTemplateColumns: "1fr" }}>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))",
// dunno why this is needed, but when the items wrap it creates
// an overflow
overflow: "hidden",
}}
>
{actionManager.renderAction("changeExportBackground")}
{someElementIsSelected && (
<CheckboxItem
checked={exportSelected}
onChange={(checked) => setExportSelected(checked)}
>
{t("labels.onlySelected")}
</CheckboxItem>
)}
{actionManager.renderAction("changeExportEmbedScene")}
</div>
</div>
<div style={{ display: "flex", alignItems: "center", marginTop: ".6em" }}>
<Stack.Row gap={2}>
{actionManager.renderAction("changeExportScale")}
</Stack.Row>
<p style={{ marginLeft: "1em", userSelect: "none" }}>Scale</p>
</div>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
margin: ".6em 0",
}}
>
{!nativeFileSystemSupported &&
actionManager.renderAction("changeProjectName")}
</div>
<Stack.Row gap={2} justifyContent="center" style={{ margin: "2em 0" }}>
<ExportButton
color="indigo"
title={t("buttons.exportToPng")}
aria-label={t("buttons.exportToPng")}
onClick={() => onExportToPng(exportedElements)}
>
PNG
</ExportButton>
<ExportButton
color="red"
title={t("buttons.exportToSvg")}
aria-label={t("buttons.exportToSvg")}
onClick={() => onExportToSvg(exportedElements)}
>
SVG
</ExportButton>
{probablySupportsClipboardBlob && (
<ExportButton
title={t("buttons.copyPngToClipboard")}
onClick={() => onExportToClipboard(exportedElements)}
color="gray"
shade={7}
>
{clipboard}
</ExportButton>
)}
</Stack.Row>
</div>
);
};
export const ImageExportDialog = ({
elements,
appState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
onExportToClipboard,
}: {
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionsManagerInterface;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
onExportToClipboard: ExportCB;
}) => {
const [modalIsShown, setModalIsShown] = useState(false);
const handleClose = React.useCallback(() => {
setModalIsShown(false);
}, []);
return (
<>
<ToolButton
onClick={() => {
setModalIsShown(true);
}}
data-testid="image-export-button"
icon={exportImage}
type="button"
aria-label={t("buttons.exportImage")}
showAriaLabel={useIsMobile()}
title={t("buttons.exportImage")}
/>
{modalIsShown && (
<Dialog onCloseRequest={handleClose} title={t("buttons.exportImage")}>
<ImageExportModal
elements={elements}
appState={appState}
files={files}
exportPadding={exportPadding}
actionManager={actionManager}
onExportToPng={onExportToPng}
onExportToSvg={onExportToSvg}
onExportToClipboard={onExportToClipboard}
onCloseRequest={handleClose}
/>
</Dialog>
)}
</>
);
};

View File

@ -1,30 +1,25 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { LoadingMessage } from "./LoadingMessage";
import { defaultLang, Language, languages, setLanguage } from "../i18n";
interface Props {
langCode: Language["code"];
children: React.ReactElement;
}
interface State {
isLoading: boolean;
}
export class InitializeApp extends React.Component<Props, State> {
public state: { isLoading: boolean } = {
isLoading: true,
};
async componentDidMount() {
export const InitializeApp = (props: Props) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
const updateLang = async () => {
await setLanguage(currentLang);
};
const currentLang =
languages.find((lang) => lang.code === this.props.langCode) ||
defaultLang;
await setLanguage(currentLang);
this.setState({
isLoading: false,
});
}
languages.find((lang) => lang.code === props.langCode) || defaultLang;
updateLang();
setLoading(false);
}, [props.langCode]);
public render() {
return this.state.isLoading ? <LoadingMessage /> : this.props.children;
}
}
return loading ? <LoadingMessage /> : props.children;
};

View File

@ -2,9 +2,8 @@
.Island {
--padding: 0;
background-color: var(--island-bg-color);
backdrop-filter: saturate(100%) blur(10px);
box-shadow: var(--shadow-island);
border-radius: 4px;
border-radius: var(--border-radius-lg);
padding: calc(var(--padding) * var(--space-factor));
position: relative;
transition: box-shadow 0.5s ease-in-out;

View File

@ -0,0 +1,135 @@
import React, { useState } from "react";
import { ActionsManagerInterface } from "../actions/types";
import { NonDeletedExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { useIsMobile } from "./App";
import { AppState, ExportOpts, BinaryFiles } from "../types";
import { Dialog } from "./Dialog";
import { exportFile, exportToFileIcon, link } from "./icons";
import { ToolButton } from "./ToolButton";
import { actionSaveFileToDisk } from "../actions/actionExport";
import { Card } from "./Card";
import "./ExportDialog.scss";
import { nativeFileSystemSupported } from "../data/filesystem";
export type ExportCB = (
elements: readonly NonDeletedExcalidrawElement[],
scale?: number,
) => void;
const JSONExportModal = ({
elements,
appState,
files,
actionManager,
exportOpts,
canvas,
}: {
appState: AppState;
files: BinaryFiles;
elements: readonly NonDeletedExcalidrawElement[];
actionManager: ActionsManagerInterface;
onCloseRequest: () => void;
exportOpts: ExportOpts;
canvas: HTMLCanvasElement | null;
}) => {
const { onExportToBackend } = exportOpts;
return (
<div className="ExportDialog ExportDialog--json">
<div className="ExportDialog-cards">
{exportOpts.saveFileToDisk && (
<Card color="lime">
<div className="Card-icon">{exportToFileIcon}</div>
<h2>{t("exportDialog.disk_title")}</h2>
<div className="Card-details">
{t("exportDialog.disk_details")}
{!nativeFileSystemSupported &&
actionManager.renderAction("changeProjectName")}
</div>
<ToolButton
className="Card-button"
type="button"
title={t("exportDialog.disk_button")}
aria-label={t("exportDialog.disk_button")}
showAriaLabel={true}
onClick={() => {
actionManager.executeAction(actionSaveFileToDisk);
}}
/>
</Card>
)}
{onExportToBackend && (
<Card color="pink">
<div className="Card-icon">{link}</div>
<h2>{t("exportDialog.link_title")}</h2>
<div className="Card-details">{t("exportDialog.link_details")}</div>
<ToolButton
className="Card-button"
type="button"
title={t("exportDialog.link_button")}
aria-label={t("exportDialog.link_button")}
showAriaLabel={true}
onClick={() =>
onExportToBackend(elements, appState, files, canvas)
}
/>
</Card>
)}
{exportOpts.renderCustomUI &&
exportOpts.renderCustomUI(elements, appState, files, canvas)}
</div>
</div>
);
};
export const JSONExportDialog = ({
elements,
appState,
files,
actionManager,
exportOpts,
canvas,
}: {
elements: readonly NonDeletedExcalidrawElement[];
appState: AppState;
files: BinaryFiles;
actionManager: ActionsManagerInterface;
exportOpts: ExportOpts;
canvas: HTMLCanvasElement | null;
}) => {
const [modalIsShown, setModalIsShown] = useState(false);
const handleClose = React.useCallback(() => {
setModalIsShown(false);
}, []);
return (
<>
<ToolButton
onClick={() => {
setModalIsShown(true);
}}
data-testid="json-export-button"
icon={exportFile}
type="button"
aria-label={t("buttons.export")}
showAriaLabel={useIsMobile()}
title={t("buttons.export")}
/>
{modalIsShown && (
<Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
<JSONExportModal
elements={elements}
appState={appState}
files={files}
actionManager={actionManager}
onCloseRequest={handleClose}
exportOpts={exportOpts}
canvas={canvas}
/>
</Dialog>
)}
</>
);
};

View File

@ -1,89 +1,20 @@
@import "open-color/open-color";
.excalidraw {
.layer-ui__library {
margin: auto;
display: flex;
align-items: center;
justify-content: center;
.layer-ui__library-header {
display: flex;
align-items: center;
width: 100%;
margin: 2px 0;
button {
// 2px from the left to account for focus border of left-most button
margin: 0 2px;
}
a {
margin-inline-start: auto;
// 17px for scrollbar (needed for overlay scrollbars on Big Sur?) + 1px extra
padding-inline-end: 18px;
white-space: nowrap;
}
}
}
.layer-ui__library-message {
padding: 10px 20px;
max-width: 200px;
}
.layer-ui__library-items {
max-height: 50vh;
overflow: auto;
}
.layer-ui__wrapper {
z-index: var(--zIndex-layerUI);
.encrypted-icon {
position: relative;
margin-inline-start: 15px;
&__top-right {
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--space-factor);
color: $oc-green-9;
svg {
width: 1.2rem;
height: 1.2rem;
}
}
&__github-corner {
top: 0;
:root[dir="ltr"] & {
right: 0;
}
:root[dir="rtl"] & {
left: 0;
}
position: absolute;
width: 40px;
}
&__footer {
position: absolute;
z-index: 100;
bottom: 0;
width: 100%;
:root[dir="ltr"] & {
right: 0;
&-right {
z-index: 100;
display: flex;
}
:root[dir="rtl"] & {
left: 0;
}
width: 190px;
}
.zen-mode-transition {
@ -105,11 +36,15 @@
transform: translate(-999px, 0);
}
:root[dir="ltr"] &.App-menu_bottom--transition-left {
transform: translate(-92px, 0);
:root[dir="ltr"] &.layer-ui__wrapper__footer-left--transition-left {
transform: translate(-76px, 0);
}
:root[dir="rtl"] &.App-menu_bottom--transition-left {
transform: translate(92px, 0);
:root[dir="rtl"] &.layer-ui__wrapper__footer-left--transition-left {
transform: translate(76px, 0);
}
&.layer-ui__wrapper__footer-left--transition-bottom {
transform: translate(0, 92px);
}
}
@ -137,5 +72,27 @@
transition-delay: 0.8s;
}
}
.layer-ui__wrapper__footer-center {
pointer-events: none;
& > * {
pointer-events: all;
}
}
.layer-ui__wrapper__footer-left,
.layer-ui__wrapper__footer-right,
.disable-zen-mode--visible {
pointer-events: all;
}
.layer-ui__wrapper__footer-left {
margin-bottom: 0.2em;
}
.layer-ui__wrapper__footer-right {
margin-top: auto;
margin-bottom: auto;
margin-inline-end: 1em;
}
}
}

View File

@ -1,50 +1,46 @@
import clsx from "clsx";
import React, {
RefObject,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import React, { useCallback } from "react";
import { ActionManager } from "../actions/manager";
import { CLASSES } from "../constants";
import { exportCanvas } from "../data";
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
import { Library } from "../data/library";
import { isTextElement, showSelectedShapeActions } from "../element";
import { NonDeletedExcalidrawElement } from "../element/types";
import { Language, t } from "../i18n";
import useIsMobile from "../is-mobile";
import { useIsMobile } from "../components/App";
import { calculateScrollCenter, getSelectedElements } from "../scene";
import { ExportType } from "../scene/types";
import { AppState, LibraryItem, LibraryItems } from "../types";
import { AppProps, AppState, ExcalidrawProps, BinaryFiles } from "../types";
import { muteFSAbortError } from "../utils";
import { SelectedShapeActions, ShapesSwitcher, ZoomActions } from "./Actions";
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
import CollabButton from "./CollabButton";
import { ErrorDialog } from "./ErrorDialog";
import { ExportCB, ExportDialog } from "./ExportDialog";
import { ExportCB, ImageExportDialog } from "./ImageExportDialog";
import { FixedSideContainer } from "./FixedSideContainer";
import { GitHubCorner } from "./GitHubCorner";
import { HintViewer } from "./HintViewer";
import { exportFile, load, shield, trash } from "./icons";
import { Island } from "./Island";
import "./LayerUI.scss";
import { LibraryUnit } from "./LibraryUnit";
import { LoadingMessage } from "./LoadingMessage";
import { LockIcon } from "./LockIcon";
import { LockButton } from "./LockButton";
import { MobileMenu } from "./MobileMenu";
import { PasteChartDialog } from "./PasteChartDialog";
import { Section } from "./Section";
import { HelpDialog } from "./HelpDialog";
import Stack from "./Stack";
import { ToolButton } from "./ToolButton";
import { Tooltip } from "./Tooltip";
import { UserList } from "./UserList";
import Library from "../data/library";
import { JSONExportDialog } from "./JSONExportDialog";
import { LibraryButton } from "./LibraryButton";
import { isImageFileHandle } from "../data/blob";
import { LibraryMenu } from "./LibraryMenu";
import "./LayerUI.scss";
import "./Toolbar.scss";
interface LayerUIProps {
actionManager: ActionManager;
appState: AppState;
files: BinaryFiles;
canvas: HTMLCanvasElement | null;
setAppState: React.Component<any, AppState>["setState"];
elements: readonly NonDeletedExcalidrawElement[];
@ -53,259 +49,28 @@ interface LayerUIProps {
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
zenModeEnabled: boolean;
showExitZenModeBtn: boolean;
showThemeBtn: boolean;
toggleZenMode: () => void;
langCode: Language["code"];
isCollaborating: boolean;
onExportToBackend?: (
exportedElements: readonly NonDeletedExcalidrawElement[],
renderTopRightUI?: (
isMobile: boolean,
appState: AppState,
canvas: HTMLCanvasElement | null,
) => void;
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
) => JSX.Element | null;
renderCustomFooter?: (isMobile: boolean, appState: AppState) => JSX.Element;
viewModeEnabled: boolean;
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
UIOptions: AppProps["UIOptions"];
focusContainer: () => void;
library: Library;
id: string;
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
}
const useOnClickOutside = (
ref: RefObject<HTMLElement>,
cb: (event: MouseEvent) => void,
) => {
useEffect(() => {
const listener = (event: MouseEvent) => {
if (!ref.current) {
return;
}
if (
event.target instanceof Element &&
(ref.current.contains(event.target) ||
!document.body.contains(event.target))
) {
return;
}
cb(event);
};
document.addEventListener("pointerdown", listener, false);
return () => {
document.removeEventListener("pointerdown", listener);
};
}, [ref, cb]);
};
const LibraryMenuItems = ({
library,
onRemoveFromLibrary,
onAddToLibrary,
onInsertShape,
pendingElements,
setAppState,
setLibraryItems,
}: {
library: LibraryItems;
pendingElements: LibraryItem;
onRemoveFromLibrary: (index: number) => void;
onInsertShape: (elements: LibraryItem) => void;
onAddToLibrary: (elements: LibraryItem) => void;
setAppState: React.Component<any, AppState>["setState"];
setLibraryItems: (library: LibraryItems) => void;
}) => {
const isMobile = useIsMobile();
const numCells = library.length + (pendingElements.length > 0 ? 1 : 0);
const CELLS_PER_ROW = isMobile ? 4 : 6;
const numRows = Math.max(1, Math.ceil(numCells / CELLS_PER_ROW));
const rows = [];
let addedPendingElements = false;
rows.push(
<div className="layer-ui__library-header">
<ToolButton
key="import"
type="button"
title={t("buttons.load")}
aria-label={t("buttons.load")}
icon={load}
onClick={() => {
importLibraryFromJSON()
.then(() => {
// Maybe we should close and open the menu so that the items get updated.
// But for now we just close the menu.
setAppState({ isLibraryOpen: false });
})
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
<ToolButton
key="export"
type="button"
title={t("buttons.export")}
aria-label={t("buttons.export")}
icon={exportFile}
onClick={() => {
saveLibraryAsJSON()
.catch(muteFSAbortError)
.catch((error) => {
setAppState({ errorMessage: error.message });
});
}}
/>
<ToolButton
key="reset"
type="button"
title={t("buttons.resetLibrary")}
aria-label={t("buttons.resetLibrary")}
icon={trash}
onClick={() => {
if (window.confirm(t("alerts.resetLibrary"))) {
Library.resetLibrary();
setLibraryItems([]);
}
}}
/>
<a href="https://libraries.excalidraw.com" target="_excalidraw_libraries">
{t("labels.libraries")}
</a>
</div>,
);
for (let row = 0; row < numRows; row++) {
const y = CELLS_PER_ROW * row;
const children = [];
for (let x = 0; x < CELLS_PER_ROW; x++) {
const shouldAddPendingElements: boolean =
pendingElements.length > 0 &&
!addedPendingElements &&
y + x >= library.length;
addedPendingElements = addedPendingElements || shouldAddPendingElements;
children.push(
<Stack.Col key={x}>
<LibraryUnit
elements={library[y + x]}
pendingElements={
shouldAddPendingElements ? pendingElements : undefined
}
onRemoveFromLibrary={onRemoveFromLibrary.bind(null, y + x)}
onClick={
shouldAddPendingElements
? onAddToLibrary.bind(null, pendingElements)
: onInsertShape.bind(null, library[y + x])
}
/>
</Stack.Col>,
);
}
rows.push(
<Stack.Row align="center" gap={1} key={row}>
{children}
</Stack.Row>,
);
}
return (
<Stack.Col align="start" gap={1} className="layer-ui__library-items">
{rows}
</Stack.Col>
);
};
const LibraryMenu = ({
onClickOutside,
onInsertShape,
pendingElements,
onAddToLibrary,
setAppState,
}: {
pendingElements: LibraryItem;
onClickOutside: (event: MouseEvent) => void;
onInsertShape: (elements: LibraryItem) => void;
onAddToLibrary: () => void;
setAppState: React.Component<any, AppState>["setState"];
}) => {
const ref = useRef<HTMLDivElement | null>(null);
useOnClickOutside(ref, (event) => {
// If click on the library icon, do nothing.
if ((event.target as Element).closest(".ToolIcon_type_button__library")) {
return;
}
onClickOutside(event);
});
const [libraryItems, setLibraryItems] = useState<LibraryItems>([]);
const [loadingState, setIsLoading] = useState<
"preloading" | "loading" | "ready"
>("preloading");
const loadingTimerRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
Promise.race([
new Promise((resolve) => {
loadingTimerRef.current = setTimeout(() => {
resolve("loading");
}, 100);
}),
Library.loadLibrary().then((items) => {
setLibraryItems(items);
setIsLoading("ready");
}),
]).then((data) => {
if (data === "loading") {
setIsLoading("loading");
}
});
return () => {
clearTimeout(loadingTimerRef.current!);
};
}, []);
const removeFromLibrary = useCallback(async (indexToRemove) => {
const items = await Library.loadLibrary();
const nextItems = items.filter((_, index) => index !== indexToRemove);
Library.saveLibrary(nextItems);
setLibraryItems(nextItems);
}, []);
const addToLibrary = useCallback(
async (elements: LibraryItem) => {
const items = await Library.loadLibrary();
const nextItems = [...items, elements];
onAddToLibrary();
Library.saveLibrary(nextItems);
setLibraryItems(nextItems);
},
[onAddToLibrary],
);
return loadingState === "preloading" ? null : (
<Island padding={1} ref={ref} className="layer-ui__library">
{loadingState === "loading" ? (
<div className="layer-ui__library-message">
{t("labels.libraryLoadingMessage")}
</div>
) : (
<LibraryMenuItems
library={libraryItems}
onRemoveFromLibrary={removeFromLibrary}
onAddToLibrary={addToLibrary}
onInsertShape={onInsertShape}
pendingElements={pendingElements}
setAppState={setAppState}
setLibraryItems={setLibraryItems}
/>
)}
</Island>
);
};
const LayerUI = ({
actionManager,
appState,
files,
setAppState,
canvas,
elements,
@ -314,70 +79,89 @@ const LayerUI = ({
onInsertElements,
zenModeEnabled,
showExitZenModeBtn,
showThemeBtn,
toggleZenMode,
isCollaborating,
onExportToBackend,
renderTopRightUI,
renderCustomFooter,
viewModeEnabled,
libraryReturnUrl,
UIOptions,
focusContainer,
library,
id,
onImageAction,
}: LayerUIProps) => {
const isMobile = useIsMobile();
const renderEncryptedIcon = () => (
<a
className={clsx("encrypted-icon tooltip zen-mode-visibility", {
"zen-mode-visibility--hidden": zenModeEnabled,
})}
href="https://blog.excalidraw.com/end-to-end-encryption/"
target="_blank"
rel="noopener noreferrer"
>
<Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
{shield}
</Tooltip>
</a>
);
const renderJSONExportDialog = () => {
if (!UIOptions.canvasActions.export) {
return null;
}
const renderExportDialog = () => {
const createExporter = (type: ExportType): ExportCB => async (
exportedElements,
scale,
) => {
if (canvas) {
await exportCanvas(type, exportedElements, appState, canvas, {
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
scale,
shouldAddWatermark: appState.shouldAddWatermark,
})
return (
<JSONExportDialog
elements={elements}
appState={appState}
files={files}
actionManager={actionManager}
exportOpts={UIOptions.canvasActions.export}
canvas={canvas}
/>
);
};
const renderImageExportDialog = () => {
if (!UIOptions.canvasActions.saveAsImage) {
return null;
}
const createExporter =
(type: ExportType): ExportCB =>
async (exportedElements) => {
const fileHandle = await exportCanvas(
type,
exportedElements,
appState,
files,
{
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
},
)
.catch(muteFSAbortError)
.catch((error) => {
console.error(error);
setAppState({ errorMessage: error.message });
});
}
};
if (
appState.exportEmbedScene &&
fileHandle &&
isImageFileHandle(fileHandle)
) {
setAppState({ fileHandle });
}
};
return (
<ExportDialog
<ImageExportDialog
elements={elements}
appState={appState}
files={files}
actionManager={actionManager}
onExportToPng={createExporter("png")}
onExportToSvg={createExporter("svg")}
onExportToClipboard={createExporter("clipboard")}
onExportToBackend={
onExportToBackend
? (elements) => {
onExportToBackend &&
onExportToBackend(elements, appState, canvas);
}
: undefined
}
/>
);
};
const Separator = () => {
return <div style={{ width: ".625em" }} />;
};
const renderViewModeCanvasActions = () => {
return (
<Section
@ -391,15 +175,15 @@ const LayerUI = ({
<Island padding={2} style={{ zIndex: 1 }}>
<Stack.Col gap={4}>
<Stack.Row gap={1} justifyContent="space-between">
{actionManager.renderAction("saveScene")}
{actionManager.renderAction("saveAsScene")}
{renderExportDialog()}
{renderJSONExportDialog()}
{renderImageExportDialog()}
</Stack.Row>
</Stack.Col>
</Island>
</Section>
);
};
const renderCanvasActions = () => (
<Section
heading="canvasActions"
@ -412,11 +196,12 @@ const LayerUI = ({
<Island padding={2} style={{ zIndex: 1 }}>
<Stack.Col gap={4}>
<Stack.Row gap={1} justifyContent="space-between">
{actionManager.renderAction("loadScene")}
{actionManager.renderAction("saveScene")}
{actionManager.renderAction("saveAsScene")}
{renderExportDialog()}
{actionManager.renderAction("clearCanvas")}
<Separator />
{actionManager.renderAction("loadScene")}
{renderJSONExportDialog()}
{renderImageExportDialog()}
<Separator />
{onCollabButtonClick && (
<CollabButton
isCollaborating={isCollaborating}
@ -429,7 +214,11 @@ const LayerUI = ({
actionManager={actionManager}
appState={appState}
setAppState={setAppState}
showThemeBtn={showThemeBtn}
/>
{appState.fileHandle && (
<>{actionManager.renderAction("saveToActiveFile")}</>
)}
</Stack.Col>
</Island>
</Section>
@ -448,7 +237,8 @@ const LayerUI = ({
style={{
// we want to make sure this doesn't overflow so substracting 200
// which is approximately height of zoom footer and top left menu items with some buffer
maxHeight: `${appState.height - 200}px`,
// if active file name is displayed, subtracting 248 to account for its height
maxHeight: `${appState.height - (appState.fileHandle ? 248 : 200)}px`,
}}
>
<SelectedShapeActions
@ -461,12 +251,15 @@ const LayerUI = ({
</Section>
);
const closeLibrary = useCallback(
(event) => {
setAppState({ isLibraryOpen: false });
},
[setAppState],
);
const closeLibrary = useCallback(() => {
const isDialogOpen = !!document.querySelector(".Dialog");
// Prevent closing if any dialog is open
if (isDialogOpen) {
return;
}
setAppState({ isLibraryOpen: false });
}, [setAppState]);
const deselectItems = useCallback(() => {
setAppState({
@ -477,11 +270,18 @@ const LayerUI = ({
const libraryMenu = appState.isLibraryOpen ? (
<LibraryMenu
pendingElements={getSelectedElements(elements, appState)}
onClickOutside={closeLibrary}
pendingElements={getSelectedElements(elements, appState, true)}
onClose={closeLibrary}
onInsertShape={onInsertElements}
onAddToLibrary={deselectItems}
setAppState={setAppState}
libraryReturnUrl={libraryReturnUrl}
focusContainer={focusContainer}
library={library}
theme={appState.theme}
files={files}
id={id}
appState={appState}
/>
) : null;
@ -507,27 +307,46 @@ const LayerUI = ({
<Section heading="shapes">
{(heading) => (
<Stack.Col gap={4} align="start">
<Stack.Row gap={1}>
<Stack.Row
gap={1}
className={clsx("App-toolbar-container", {
"zen-mode": zenModeEnabled,
})}
>
<LockButton
zenModeEnabled={zenModeEnabled}
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
/>
<Island
padding={1}
className={clsx({ "zen-mode": zenModeEnabled })}
className={clsx("App-toolbar", {
"zen-mode": zenModeEnabled,
})}
>
<HintViewer appState={appState} elements={elements} />
<HintViewer
appState={appState}
elements={elements}
isMobile={isMobile}
/>
{heading}
<Stack.Row gap={1}>
<ShapesSwitcher
canvas={canvas}
elementType={appState.elementType}
setAppState={setAppState}
isLibraryOpen={appState.isLibraryOpen}
onImageAction={({ pointerType }) => {
onImageAction({
insertOnCanvasDirectly: pointerType !== "mouse",
});
}}
/>
</Stack.Row>
</Island>
<LockIcon
zenModeEnabled={zenModeEnabled}
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
<LibraryButton
appState={appState}
setAppState={setAppState}
/>
</Stack.Row>
{libraryMenu}
@ -535,24 +354,32 @@ const LayerUI = ({
)}
</Section>
)}
<UserList
className={clsx("zen-mode-transition", {
"transition-right": zenModeEnabled,
})}
<div
className={clsx(
"layer-ui__wrapper__top-right zen-mode-transition",
{
"transition-right": zenModeEnabled,
},
)}
>
{appState.collaborators.size > 0 &&
Array.from(appState.collaborators)
// Collaborator is either not initialized or is actually the current user.
.filter(([_, client]) => Object.keys(client).length !== 0)
.map(([clientId, client]) => (
<Tooltip
label={client.username || "Unknown user"}
key={clientId}
>
{actionManager.renderAction("goToCollaborator", clientId)}
</Tooltip>
))}
</UserList>
<UserList>
{appState.collaborators.size > 0 &&
Array.from(appState.collaborators)
// Collaborator is either not initialized or is actually the current user.
.filter(([_, client]) => Object.keys(client).length !== 0)
.map(([clientId, client]) => (
<Tooltip
label={client.username || "Unknown user"}
key={clientId}
>
{actionManager.renderAction("goToCollaborator", {
id: clientId,
})}
</Tooltip>
))}
</UserList>
{renderTopRightUI?.(isMobile, appState)}
</div>
</div>
</FixedSideContainer>
);
@ -560,61 +387,73 @@ const LayerUI = ({
const renderBottomAppMenu = () => {
return (
<div
className={clsx("App-menu App-menu_bottom zen-mode-transition", {
"App-menu_bottom--transition-left": zenModeEnabled,
})}
<footer
role="contentinfo"
className="layer-ui__wrapper__footer App-menu App-menu_bottom"
>
<Stack.Col gap={2}>
<Section heading="canvasActions">
<Island padding={1}>
<ZoomActions
renderAction={actionManager.renderAction}
zoom={appState.zoom}
/>
</Island>
{renderEncryptedIcon()}
</Section>
</Stack.Col>
</div>
<div
className={clsx(
"layer-ui__wrapper__footer-left zen-mode-transition",
{
"layer-ui__wrapper__footer-left--transition-left": zenModeEnabled,
},
)}
>
<Stack.Col gap={2}>
<Section heading="canvasActions">
<Island padding={1}>
<ZoomActions
renderAction={actionManager.renderAction}
zoom={appState.zoom}
/>
</Island>
{!viewModeEnabled && (
<div
className={clsx("undo-redo-buttons zen-mode-transition", {
"layer-ui__wrapper__footer-left--transition-bottom":
zenModeEnabled,
})}
>
{actionManager.renderAction("undo", { size: "small" })}
{actionManager.renderAction("redo", { size: "small" })}
</div>
)}
</Section>
</Stack.Col>
</div>
<div
className={clsx(
"layer-ui__wrapper__footer-center zen-mode-transition",
{
"layer-ui__wrapper__footer-left--transition-bottom":
zenModeEnabled,
},
)}
>
{renderCustomFooter?.(false, appState)}
</div>
<div
className={clsx(
"layer-ui__wrapper__footer-right zen-mode-transition",
{
"transition-right disable-pointerEvents": zenModeEnabled,
},
)}
>
{actionManager.renderAction("toggleShortcuts")}
</div>
<button
className={clsx("disable-zen-mode", {
"disable-zen-mode--visible": showExitZenModeBtn,
})}
onClick={toggleZenMode}
>
{t("buttons.exitZenMode")}
</button>
</footer>
);
};
const renderGitHubCorner = () => {
return (
<aside
className={clsx(
"layer-ui__wrapper__github-corner zen-mode-transition",
{
"transition-right": zenModeEnabled,
},
)}
>
<GitHubCorner appearance={appState.appearance} />
</aside>
);
};
const renderFooter = () => (
<footer role="contentinfo" className="layer-ui__wrapper__footer">
<div
className={clsx("zen-mode-transition", {
"transition-right disable-pointerEvents": zenModeEnabled,
})}
>
{renderCustomFooter?.(false)}
{actionManager.renderAction("toggleShortcuts")}
</div>
<button
className={clsx("disable-zen-mode", {
"disable-zen-mode--visible": showExitZenModeBtn,
})}
onClick={toggleZenMode}
>
{t("buttons.exitZenMode")}
</button>
</footer>
);
const dialogs = (
<>
{appState.isLoading && <LoadingMessage />}
@ -625,7 +464,11 @@ const LayerUI = ({
/>
)}
{appState.showHelpDialog && (
<HelpDialog onClose={() => setAppState({ showHelpDialog: false })} />
<HelpDialog
onClose={() => {
setAppState({ showHelpDialog: false });
}}
/>
)}
{appState.pasteDialog.shown && (
<PasteChartDialog
@ -650,7 +493,8 @@ const LayerUI = ({
elements={elements}
actionManager={actionManager}
libraryMenu={libraryMenu}
exportButton={renderExportDialog()}
renderJSONExportDialog={renderJSONExportDialog}
renderImageExportDialog={renderImageExportDialog}
setAppState={setAppState}
onCollabButtonClick={onCollabButtonClick}
onLockToggle={onLockToggle}
@ -658,6 +502,9 @@ const LayerUI = ({
isCollaborating={isCollaborating}
renderCustomFooter={renderCustomFooter}
viewModeEnabled={viewModeEnabled}
showThemeBtn={showThemeBtn}
onImageAction={onImageAction}
renderTopRightUI={renderTopRightUI}
/>
</>
) : (
@ -672,8 +519,6 @@ const LayerUI = ({
{dialogs}
{renderFixedSideContainer()}
{renderBottomAppMenu()}
{renderGitHubCorner()}
{renderFooter()}
{appState.scrolledOutside && (
<button
className="scroll-back-to-content"
@ -707,6 +552,7 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
prev.renderCustomFooter === next.renderCustomFooter &&
prev.langCode === next.langCode &&
prev.elements === next.elements &&
prev.files === next.files &&
keys.every((key) => prevAppState[key] === nextAppState[key])
);
};

Some files were not shown because too many files have changed in this diff Show More