
* fix: fonts not rendered on init if `loadingdone` not fired (#5923) * fix: fonts not rendered on init if `loadingdone` not fired * remove unnecessary check * fix: Always bind to container selected by user (#5880) * fix: Always bind to container selected by user * Don't bind to container when using text tool * adjust z-index for bound text * fix * Add spec * Add test * Allow double click on transparent container and add spec * fix spec * adjust z-index only when binding * update index * fix * add index check * Update src/scene/Scene.ts Co-authored-by: dwelle <luzar.david@gmail.com> * feat: changed text copy/paste behaviour (#5786) Co-authored-by: dwelle <luzar.david@gmail.com> Co-authored-by: Antonio Della Fortuna <a.dellafortuna00@gmail.com> * feat: Don't add midpoint until dragged beyond a threshold (#5927) * Don't add midpoint until dragged beyond a threshold * remove unnecessary code * fix tests * fix * add spec * remove isMidpoint * cleanup * fix threshold for zoom * split into shouldAddMidpoint and addMidpoint * wrap in flushSync for synchronous updates * remove threshold for line editor and add spec * [unrelated] fix stack overflow state update * fix tests * don't drag arrow when dragging to add mid point * add specs Co-authored-by: dwelle <luzar.david@gmail.com> * refactor: remove unnecessary code (#5933) * fix: scale font correctly when using shift (#5935) * fix: scale font correctly when using shift * fix * Empty-Commit * Add spec * fix * fix: Dedupe boundElement ids when container duplicated with alt+drag (#5938) * Dedupe boundElement ids when container duplicated with alt+drag and add spec * set to null by default * fix: bindings do not survive history serialization (#5942) * fix: don't allow whitespaces for bound text (#5939) * fix: don't allow whitespaces for bound text * fix * remove * remove empty else * fix * fix * fix * feat: Support labels for arrow 🔥 (#5723) * feat: support arrow with text * render arrow -> clear rect-> render text * move bound text when linear elements move * fix centering cursor when linear element rotated * fix y coord when new line added and container has 3 points * update text position when 2nd point moved * support adding label on top of 2nd point when 3 points are present * change linear element editor shortcut to cmd+enter and fix tests * scale bound text points when resizing via bounding box * ohh yeah rotation works :) * fix coords when updating text properties * calculate new position after rotation always from original position * rotate the bound text by same angle as parent * don't rotate text and make sure dimensions and coords are always calculated from original point * hardcoding the text width for now * Move the linear element when bound text hit * Rotation working yaay * consider text element angle when editing * refactor * update x2 coords if needed when text updated * simplify * consider bound text to be part of bounding box when hit * show bounding box correctly when multiple element selected * fix typo * support rotating multiple elements * support multiple element resizing * shift bound text to mid point when odd points * Always render linear element handles inside editor after element rendered so point is visible for bound text * Delete bound text when point attached to it deleted * move bound to mid segement mid point when points are even * shift bound text when points nearby deleted and handle segment deletion * Resize working :) * more resize fixes * don't update cache-its breaking delete points, look for better soln * update mid point cache for bound elements when updated * introduce wrapping when resizing * wrap when resize for 2 pointer linear elements * support adding text for linear elements with more than 3 points * export to svg working :) * clip from nearest enclosing element with non transparent color if present when exporting and fill with correct color in canvas * fix snap * use visible elements * Make export to svg work with Mask :) * remove id * mask canvas linear element area where label is added * decide the position of bound text during render * fix coords when editing * fix multiple resize * update cache when bound text version changes * fix masking when rotated * render text in correct position in preview * remove unnecessary code * fix masking when rotating linear element * fix masking with zoom * fix mask in preview for export * fix offsets in export view * fix coords on svg export * fix mask when element rotated in svg * enable double-click to enter text * fix hint * Position cursor correctly and text dimensiosn when height of element is negative * don't allow 2 pointer linear element with bound text width to go beyond min width * code cleanup * fix freedraw * Add padding * don't show vertical align action for linear element containers * Add specs for getBoundTextElementPosition * more specs * move some utils to linearElementEditor.ts * remove only :p * check absoulte coods in test * Add test to hide vertical align for linear eleemnt with bound text * improve export preview * support labels only for arrows * spec * fix large texts * fix tests * fix zooming * enter line editor with cmd+double click * Allow points to move beyond min width/height for 2 pointer arrow with bound text * fix hint for line editing * attempt to fix arrow getting deselected * fix hint and shortcut * Add padding of 5px when creating bound text and add spec * Wrap bound text when arrow binding containers moved * Add spec * remove * set boundTextElementVersion to null if not present * dont use cache when version mismatch * Add a padding of 5px vertically when creating text * Add box sizing content box * Set bound elements when text element created to fix the padding * fix zooming in editor * fix zoom in export * remove globalCompositeOperation and use clearRect instead of fillRect * fix: repair element bindings on restore (#5956) * fix: repair element bindings on restore * fix dropping non-text bound elements * be more conservative * build: move release scripts to use release branch (#5958) * fix: renderFooter styling (#5962) * fix: `ExcalidrawArrowElement` rather than `ExcalidrawArrowEleement` (#5955) * fix: Galego and Kurdî missing in languages plus two locale typos (#5954) * fix: remove blank space (#5950) * fix: remove editor onpaste handler (#5971) * feat: better default radius sizes for rectangles (#5553) Co-authored-by: Ryan <diweihao@bytedance.com> Co-authored-by: dwelle <luzar.david@gmail.com> * chore: add display name to context providers (#5974) * chore: add display name to context providers * fix typo * fix: apply the right type of roundness when pasting styles (#5979) * fix: only paste roundness when target and source elements are of the same type * apply roundness when pasting across different types * simplify Co-authored-by: dwelle <luzar.david@gmail.com> * feat: allow readonly actions to be used in viewMode (#5982) * fix: chart pasting not working due to removing tab characters (#5987) * fix: Avatar outline on safari & center (#5997) * fix: not properly restoring element stroke and bg colors (#6002) * fix: PWA not working after CRA@5 update (#6012) * fix: PWA not working after CRA@5 update * fix: fallback to default locale when fetch fails * fix: resize sometimes throwing on missing null-checks (#6013) * fix: showing `grabbing` cursor when holding `spacebar` (#6015) * fix: don't push whitespace to next line when exceeding max width during wrapping and make sure to use same width of text editor on DOM when measuring dimensions (#5996) * fix: don't push whitespace to next line when exceeding max width during wrapping * add a helper function and never push empty line * use width same as in text area so dimensions are same * add tests * make sure dom element has exact same width as text editor * feat: render footer as a component instead of render prop (#5970) * feat: render footer as a component instead of render prop * Export FooterCenter as footer * remove useDevice export * revert some changes * remove * add spec * update specs * parse children into a dictionary * factor app footer components into a single file * Add docs * split app footer components Co-authored-by: dwelle <luzar.david@gmail.com> * feat: move contextMenu into the component tree and control via appState (#6021) * fix: ColorPicker getColor (#5949) Co-authored-by: dwelle <luzar.david@gmail.com> * chore: bump typescript @ 4.9.4 (#6024) * feat: support shrinking text containers to original height when text removed (#6025) * fix:cache bind text containers height so that it could autoshrink to original height when text deleted * revert * rename * reset cache when resized * safe check * restore original containr height when text is unbind * update cache when redrawing bounding box * reset cache when unbind * make type-safe * add specs * skip one test * remoe mock * fix Co-authored-by: dwelle <luzar.david@gmail.com> * fix: restoring deleted bindings (#6029) * fix: restoring deleted bindings * add tests * add one more test * merge restore tests files * fix: use canvas measureText to calculate width in measureText (#6030) * fix: use canvas measureText to calculate width in measureText * calculate multiline width correctly using canvas measure text and rename functions * set correct width when pasting in bound container * take existing value + new pasted * remove debugger :p * fix snaps * fix: remove background from wysiwyg when editing arrow label (#6033) Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com> * fix: use displayName since name gets stripped off when uglifying/minifiyng in production (#6036) fix: use displayName since name gets stripped off when uglifying/minifiy in production * feat: Scroll using PageUp and PageDown (#6038) * feat: Scroll using PageUp and PageDown * support x-axis via `shift` & enable in viewMode * tweak test Co-authored-by: dwelle <luzar.david@gmail.com> * chore: Update translations from Crowdin (#5807) Co-authored-by: David Luzar <luzar.david@gmail.com> * fix: remove ga from docker build (#6059) * fix: remove ga from docker build * lint * fix debug * fix: show error message on collab save failure (#6063) * fix: show error message on collab save failure * comment * feat: new Menu Component API (#6034) * feat: new Menu Component API * allow valid children types * introduce menu group to group items * Add lang footer * use display name * displayName * define types inside * fix default menu * add json export to menu * fix * simplify expression * put open menu into own compo to optimize perf So that we don't rerun `useOutsideClickHook` (and rebind event listeners all the time) * naming tweaks * rename MenuComponents->MenuDefaultItems and export default items from Menu.Items * import Menu.scss in Menu.tsx * move menu scss to excal app * Don't filter children inside menu group * move E+ out of socials * support style prop for MenuItem and MenuGroup * Support header in menu group and add Excalidraw links header for default items in social section * rename header to title * fix padding for lang * render menu in mobile * review fixes * tweaks * Export collaborators and show in mobile menu * revert .env * lint :p * again lint * show correct actions in view mode for mobile * Whitelist Collaborators Comp * mobile styling * padding * don't show nerds when menu open in mobile * lint :( * hide shortcuts * refactor userlist to support mobile and keep a wrapper comp for excal app * use only UserList * render only on mobile for default items * remove unused hooks * Show collab button in menu when onCollabButtonClick present and hide export when UIOptions.canvasActions.export is false * fix tests * lint * inject userlist inside menu on mobile * revert userlist * move menu socials to default menu * fix collab * use meny in library * Make Menu generic and create hamburgemenu for public excal menu and use menu in library as well * use appState.openMenu for mobile * fix tests * styling fixes and support style and class name in menu content * fix test * rename MenuDefaultItems->DefaultItems * move footer css to its own comp * rename HamburgerMenu -> MainMenu * rename menu -> dropdownMenu and update classes, onClick->onToggle * close main menu when dialog closes * by bye filtering * update docs * fix lint * update example, docs for useDevice and footer in mobile, rename menu ->DropDownMenu everywhere * spec * remove isMenuOpenAtom and set openMenu as canvas for main menu, render decreases in specs :) * [temp] remove cyclic depenedency to fix build * hack- update appstate to sync lang change * Add more specs * wip: rewrite MainMenu footer * fix margin * fix snaps * not needed as lang list no more imported * simplify custom footer rendering * Add DropdownMenuItemLink and DropdownMenuItemCustom and update API, docs * fix `MainMenu.ItemCustom` * naming * use onSelect and base class for custom items * fix lint * fix snap * use custom item for lang * update docs * fix * properly use `MainMenu.ItemCustom` for `LanguageList` * add margin top to custom items * flex Co-authored-by: dwelle <luzar.david@gmail.com> * fix: HelpDialog (#6072) * chore: Update translations from Crowdin (#6052) * New translations en.json (German) * Auto commit: Calculate translation coverage * New translations en.json (Hindi) * New translations en.json (Marathi) * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Galician) * Auto commit: Calculate translation coverage * 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 (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Basque) * 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 (Korean) * New translations en.json (Kurdish) * New translations en.json (Lithuanian) * New translations en.json (Dutch) * New translations en.json (Punjabi) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Slovak) * New translations en.json (Slovenian) * New translations en.json (Swedish) * New translations en.json (Turkish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (Vietnamese) * New translations en.json (Galician) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Indonesian) * New translations en.json (Persian) * New translations en.json (Tamil) * New translations en.json (Bengali) * New translations en.json (Marathi) * 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 (Chinese Traditional, Hong Kong) * New translations en.json (Sinhala) * New translations en.json (Norwegian Bokmal) * New translations en.json (Occitan) * New translations en.json (Kabyle) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Simplified) * Auto commit: Calculate translation coverage * New translations en.json (Chinese Traditional) * New translations en.json (Chinese Traditional) * New translations en.json (Norwegian Bokmal) * Auto commit: Calculate translation coverage * New translations en.json (Latvian) * Auto commit: Calculate translation coverage * New translations en.json (Romanian) * Auto commit: Calculate translation coverage * New translations en.json (Slovenian) * Auto commit: Calculate translation coverage * New translations en.json (Spanish) * New translations en.json (Russian) * Auto commit: Calculate translation coverage * New translations en.json (German) * Auto commit: Calculate translation coverage * New translations en.json (Vietnamese) * Auto commit: Calculate translation coverage * New translations en.json (Hindi) * Auto commit: Calculate translation coverage * New translations en.json (Dutch) * Auto commit: Calculate translation coverage * New translations en.json (Marathi) * Auto commit: Calculate translation coverage * New translations en.json (Latvian) * New translations en.json (French) * Auto commit: Calculate translation coverage * New translations en.json (French) * Auto commit: Calculate translation coverage * New translations en.json (Portuguese, Brazilian) * Auto commit: Calculate translation coverage * New translations en.json (Japanese) * Auto commit: Calculate translation coverage * build(deps): bump json5 from 2.2.1 to 2.2.3 in /src/packages/excalidraw (#6062) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump json5 from 2.2.1 to 2.2.3 in /src/packages/utils (#6061) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump json5 from 2.2.1 to 2.2.3 in /dev-docs (#6060) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#5963) Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump json5 from 1.0.1 to 1.0.2 (#6076) Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump loader-utils from 2.0.3 to 2.0.4 (#5905) Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump loader-utils from 2.0.3 to 2.0.4 in /src/packages/excalidraw (#5892) build(deps): bump loader-utils in /src/packages/excalidraw Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: stale appState of MainMenu defaultItems rendered from Actions (#6074) * fix: png-exporting does not preserve angles correctly for flipped images (#6085) * fix: png-exporting does not preserve angles correctly for flipped images * refactor related code * simplify further and comment * fix: image horizontal flip fix + improved tests (#5799) Co-authored-by: Antonio Della Fortuna <a.dellafortuna00@gmail.com> Co-authored-by: dwelle <luzar.david@gmail.com> fixes https://github.com/excalidraw/excalidraw/issues/5784 * fix: React.memo resolvers not accounting for all props (#6042) * fix: use position absolute for mobile misc tools (#6099) * feat: generic button export (#6092) Co-authored-by: dwelle <luzar.david@gmail.com> * feat: render unknown supplied children to UI (#6096) * feat: support WelcomeScreen customization API (#6048) * fix: renamed folder MainMenu->main-menu and support rest props (#6103) * renamed folder MainMenu -> main-menu * rename ariaLabel -> aria-label and dataTestId -> data-testid * allow rest props * fix * lint * add ts check * ts for div * fix * fix * fix * feat: new Live Collaboration Component API (#6104) * feat: new Live Collaboration Component API * namespace export icons into `icons` dictionary and lowercase * update readme and changelog * review fixes * fix * fix * update docs * remove * allow button rest props * update docs * docs * add `WelcomeScreen.Center.MenuItemLiveCollaborationTrigger` * fix lint * update changelog Co-authored-by: dwelle <luzar.david@gmail.com> * fix: mobile tools positioning (#6107) * fix: mobile tools positioning * add var for padding * use css var * new line * stupid mistake * lint * fix: remove overflow hidden from button (#6110) remove overflow hidden from button * docs: release @excalidraw/excalidraw@0.14.0 🎉 (#6109) * docs: release @excalidraw/excalidraw@0.14.1 🎉 (#6112) * build: temporarily disable pre-commit (#6132) * chore: Update translations from Crowdin (#6077) * feat: show copy-as-png export button on firefox and show steps how to enable it (#6125) * feat: hide copy-as-png shortcut from help dialog if not supported * fix: support firefox if clipboard.write supported * show shrotcut in firefox and instead show error message how to enable the flag support * widen to TypeError because minification * show copy-as-png on firefox even if it will throw * style: change in ExportButton style (#6147) (#6148) Co-authored-by: David Luzar <luzar.david@gmail.com> * fix: button background and svg sizes (#6155) * fix: button background color fallback * fix svg width/height * feat: add hand/panning tool (#6141) * feat: add hand/panning tool * move hand tool right of tool lock separator * tweak i18n * rename `panning` -> `hand` * toggle between last tool and hand on `H` shortcut * hide properties sidebar when `hand` active * revert to rendering HandButton manually due to mobile toolbar * feat: close MainMenu and Library dropdown on item select (#6152) * fix: declare css variable for font in excalidraw so its available in host (#6160) declar css variable for font in excalidraw so its available in host * fix: 🐛 broken emojis when wrap text (#6153) * fix: 🐛 broken emojis when wrap text * refactor: Delete unnecessary "else" (reduce indentation) * fix: remove code block that causes the emojis to disappear * Apply suggestions from code review Co-authored-by: David Luzar <luzar.david@gmail.com> * fix: 🚑 possibly undefined value * Add spec Co-authored-by: David Luzar <luzar.david@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com> * fix: set the width correctly using measureText in editor (#6162) * fix: quick typo fix (#6167) * fix: add 1px width to the container to calculate more accurately (#6174) * fix: add 1px width to the container to calculate accurately * fix tests * feat: rewrite public UI component rendering using tunnels (#6117) * feat: rewrite public UI component rendering using tunnels * factor out into components * comments * fix variable naming * fix not hiding welcomeScreen * factor out AppFooter and memoize components * remove `UIOptions.welcomeScreen` and render only from host app * factor out tunnels into own file * update changelog. Keep `UIOptions.welcomeScreen` as deprecated * update changelog * lint --------- Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com> * fix: make tunnels work in multi-instance scenarios (#6178) * fix: make tunnels work in multi-instance scenarios * factor tunnels out * use tunnel-rat fork until upsteam updated * fix: horizontal padding when aligning bound text containers (#6180) * fix: horizontal padding when aligning bound text containers * Add specs * fix * docs: release @excalidraw/excalidraw@0.14.2 🎉 (#6181) * docs: migrating dev docs to docusaurus :) (#6073) * docs: migrating existing docs to docosaraus :) * log broken links * lint :p * fix * divide the doc into diff categories * fix * order sidebars and more * fix lint * point to installation * making docs better :) * fix * renaming git * renaming git * fix links * fix * update readme * fix * resolve duplicate url and make /docs as base url * fix * move main docs as well * making docs better * support mdx * update og * fix title * upgrade docusarus to stable version * use draculla theme * fix * make entire sidebar collapsable * live editor for footer wohoo * render excalidraw only on client to fix the prod build * migrate MainMenu to live editor too :) * lint :p * cleanup integration and use live editor and tabs * fix * Add welcome screen doc * Live Collaboration comp docs * Add collaborator example * Add example * add more * remove isCollaborating * Rewrite ref and move to sidebar * change color of links inside pre * add initial data * fix lint * Add styling * fix lint * Add example for customizing styles * fix lint * fix * fix lint * Add link to livecollabtrigger * fix * rewrite UIOptions to sidebar * move initialdata to sidebar * move render props to sidebar and rewrite renderTopRightUI and renderCustomStats * rewrite renderSidebar * update og * update url for testing * fix url * update readme * fix style * tweaks * Add highlight comp to highlight text * Add bash syntax highlight * fix * tweaks * fix * rewrite export utilities * fix restore * rewrite utils * move constants to sidebar * update readme * add copyright * fix links style * Add linkedin * tweaks * rename package to @excalidraw/excalidraw * enable algolia with dummy creds * tweaks to integration doc * tweak WelcomeScreen docs to reflect upcoming API changes * tweak components intro * tweak nomenclature * fix admonition * rename `components` sidebar item and change order of components list * uncollapse package section in sidebar * show level 4 haeadings in TOC * remove algolia * remove unused assets * capitalize C * tweak * rename components to App * rename components -> children-components in the routes * move notable used tools to intro * update MainMenu docs with `onSelect` preventDefault behavior * change sidebar label for children components * use code * tweak README & docs intro * tweak package development doc * make scrollbar gutter stable * tweak api intro * add admonition for export utils * use next * wip * wip * make excalidraw examples use current color theme & prefer system * fix welcomescreen docs * use latest temp release * fix component order * revert wip changes * use next * tweak * increase height to fix welcome screen hint * tweak editor height * update excal version * wrap Excal with forwardRef to fix refs * migrate contributing.md * fix broken links --------- Co-authored-by: dwelle <luzar.david@gmail.com> * fix: edit link in docs (#6182) * docs: show last updated time and author (#6183) docs:show last updated time and author * fix: hide welcome screen on mobile once user interacts (#6185) * fix: hide welcome screen on mobile once started drawing * Add specs * fix: sort bound text elements to fix text duplication z-index error (#5130) * fix: sort bound text elements to fix text duplication z-index error * improve & sort groups & add tests * fix backtracking and discontiguous groups --------- Co-authored-by: dwelle <luzar.david@gmail.com> * feat: disable canvas smoothing (antialiasing) for right-angled elements (#6186)Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * feat: disable canvas smoothing for text and other types * disable smoothing for all right-angled elements * Update src/renderer/renderElement.ts Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * Update src/renderer/renderElement.ts Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * fix lint * always enable smoothing while zooming --------- Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> * chore: Update translations from Crowdin (#6150) * feat: shortcut for clearCanvas confirmDialog (#6114) Co-authored-by: dwelle <luzar.david@gmail.com> resolve https://github.com/excalidraw/excalidraw/issues/5818 * feat: show error message when not connected to internet while collabo… (#6165) Co-authored-by: dwelle <luzar.david@gmail.com> Resolves https://github.com/excalidraw/excalidraw/issues/5994 * fix: docker build architecture:linux/amd64 error occur on linux/arm64 instance (#6197) fix docker build when in linux/arm64 use docker buildx plugin to build linux/amd64 image, a build error will occur causing the build to break * refactor: Make the example React app reusable without duplication (#6188) * fix: don't allow blank space in collab name (#6211) * don't allow blank space in collab name * add spec * prevent blank * docs: enable Algolia for search (#6230) * feat: Make repair and refreshDimensions configurable in restoreElements (#6238) * fix: don't repair during reconcilation * Add opts to restoreElement and enable refreshDimensions and repair via config * remove * update changelog * fix tests * rename to repairBindings * docs: Fixed broken codesandbox link in the dev-docs (#6229) fixed broken link * docs: new readme (#6240) Co-authored-by: David Luzar <luzar.david@gmail.com> * docs: fix next.js example (#6241) * docs: fix typo (#6252) * feat: Bind text to container if double clicked on filled shape or stroke (#6250) * feat: bind text to container when clicked on filled shape or element stroke * Bind if double clicked on stroke as well * remove * specs * remove * shuffle * fix * back to normal * docs: Fix outdated link in README.md (#6263) * fix: improve text wrapping in ellipse and alignment (#6172) * fix: improve text wrapping in ellipse * compute height when font properties updated * fix alignment * fix alignment when resizing * fix * ad padding * always compute height when redrawing bounding box and refactor * lint * fix specs * fix * redraw text bounding box when pasted or refreshed * fix * Add specs * fix * restore on font load * add comments * fix: improve text wrapping inside rhombus and more fixes (#6265) * fix: improve text wrapping inside rhombus * Add comments * specs * fix: shift resize and multiple element regression for ellipse and rhombus * use container width for scaling font size * fix * fix multiple resize * lint * redraw on submit * redraw only newly pasted elements * no padding when center * fix tests * fix * dont add padding in rhombus when aligning * refactor * fix * move getMaxContainerHeight and getMaxContainerWidth to textElement.ts * Add specs * fix: indenting via `tab` clashing with IME compositor (#6258) * chore: Update translations from Crowdin (#6191) * fix: rerender i18n in host components on lang change (#6224) * fix: fit mobile toolbar and make scrollable (#6270) Co-authored-by: dwelle <luzar.david@gmail.com> * feat: improve text measurements in bound containers (#6187) * feat: move to canvas measureText * calcualte height with better heuristic * improve heuristic more * remove vertical offset as its not needed * lint * calculate width of individual char and ceil to calculate width and remove adjustment factor * push the word if equal to max width * update height when text overflows for vertical alignment top/bottom * remove the hack of updating height when line mismatch as its not needed * remove scroll height and calculate the height instead * remove unused code * fix * remove * use math.ceil for whole width instead of individual chars * fix tests * fix * fix * redraw text bounding box instead when font loaded to fix alignment as well * fix * fix * fix * Add a 0.05px extra only for firefox * Add spec * stop taking ceil and increase firefox editor width by 0.05px * Ad 0.05px in safari too * lint * lint * remove baseline from measureFontSizeFromWH * don't redraw on font load * lint * refactor name and signature * fix: compute container height from bound text correctly (#6273) * fix: compute container height from bound text correctly * fix specs * Add tests * fix: svg text baseline (#6285 * fix: svg text baseline * fix for multiline --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: David Luzar <luzar.david@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com> Co-authored-by: Antonio Della Fortuna <50418432+adarkforce@users.noreply.github.com> Co-authored-by: Antonio Della Fortuna <a.dellafortuna00@gmail.com> Co-authored-by: DanielJGeiger <1852529+DanielJGeiger@users.noreply.github.com> Co-authored-by: Fer <63980689+1fbr@users.noreply.github.com> Co-authored-by: fennghuang <89014758+fennghuang@users.noreply.github.com> Co-authored-by: Ryan Di <ryan.weihao.di@gmail.com> Co-authored-by: Ryan <diweihao@bytedance.com> Co-authored-by: Excalidraw Bot <77840495+excalibot@users.noreply.github.com> Co-authored-by: EternalWill43 <70084418+EternalWill43@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Barnabás Molnár <38168628+barnabasmolnar@users.noreply.github.com> Co-authored-by: Nishant-l <61119157+Nishant-l@users.noreply.github.com> Co-authored-by: Ignacio Cuadra <67276174+ignacio-cuadra@users.noreply.github.com> Co-authored-by: JUNYI OU <49964599+Irvingouj@users.noreply.github.com> Co-authored-by: Jang Min <jangmin.dev@gmail.com> Co-authored-by: Matthieu Rossignon <51274353+Mattross45@users.noreply.github.com> Co-authored-by: Dejavu Moe <jialong.vip@gmail.com> Co-authored-by: Luka Hietala <95122845+LukaHietala@users.noreply.github.com> Co-authored-by: Milos Vetesnik <maielo.mv@gmail.com> Co-authored-by: Jan Klass <kissaki@posteo.de> Co-authored-by: Hikaru Yoshino <57059705+osushicrusher@users.noreply.github.com> Co-authored-by: Tengku Farhan <109069184+tfarhan00@users.noreply.github.com>
431 lines
12 KiB
TypeScript
431 lines
12 KiB
TypeScript
import React from "react";
|
|
import { Popover } from "./Popover";
|
|
import { isTransparent } from "../utils";
|
|
|
|
import "./ColorPicker.scss";
|
|
import { isArrowKey, KEYS } from "../keys";
|
|
import { t, getLanguage } from "../i18n";
|
|
import { isWritableElement } from "../utils";
|
|
import colors from "../colors";
|
|
import { ExcalidrawElement } from "../element/types";
|
|
import { AppState } from "../types";
|
|
|
|
const MAX_CUSTOM_COLORS = 5;
|
|
const MAX_DEFAULT_COLORS = 15;
|
|
|
|
export const getCustomColors = (
|
|
elements: readonly ExcalidrawElement[],
|
|
type: "elementBackground" | "elementStroke",
|
|
) => {
|
|
const customColors: string[] = [];
|
|
const updatedElements = elements
|
|
.filter((element) => !element.isDeleted)
|
|
.sort((ele1, ele2) => ele2.updated - ele1.updated);
|
|
|
|
let index = 0;
|
|
const elementColorTypeMap = {
|
|
elementBackground: "backgroundColor",
|
|
elementStroke: "strokeColor",
|
|
};
|
|
const colorType = elementColorTypeMap[type] as
|
|
| "backgroundColor"
|
|
| "strokeColor";
|
|
while (
|
|
index < updatedElements.length &&
|
|
customColors.length < MAX_CUSTOM_COLORS
|
|
) {
|
|
const element = updatedElements[index];
|
|
|
|
if (
|
|
customColors.length < MAX_CUSTOM_COLORS &&
|
|
isCustomColor(element[colorType], type) &&
|
|
!customColors.includes(element[colorType])
|
|
) {
|
|
customColors.push(element[colorType]);
|
|
}
|
|
index++;
|
|
}
|
|
return customColors;
|
|
};
|
|
|
|
const isCustomColor = (
|
|
color: string,
|
|
type: "elementBackground" | "elementStroke",
|
|
) => {
|
|
return !colors[type].includes(color);
|
|
};
|
|
|
|
const isValidColor = (color: string) => {
|
|
const style = new Option().style;
|
|
style.color = color;
|
|
return !!style.color;
|
|
};
|
|
|
|
const getColor = (color: string): string | null => {
|
|
if (isTransparent(color)) {
|
|
return color;
|
|
}
|
|
|
|
// testing for `#` first fixes a bug on Electron (more specfically, an
|
|
// Obsidian popout window), where a hex color without `#` is (incorrectly)
|
|
// considered valid
|
|
return isValidColor(`#${color}`)
|
|
? `#${color}`
|
|
: isValidColor(color)
|
|
? color
|
|
: null;
|
|
};
|
|
|
|
// This is a narrow reimplementation of the awesome react-color Twitter component
|
|
// https://github.com/casesandberg/react-color/blob/master/src/components/twitter/Twitter.js
|
|
|
|
// Unfortunately, we can't detect keyboard layout in the browser. So this will
|
|
// only work well for QWERTY but not AZERTY or others...
|
|
const keyBindings = [
|
|
["1", "2", "3", "4", "5"],
|
|
["q", "w", "e", "r", "t"],
|
|
["a", "s", "d", "f", "g"],
|
|
["z", "x", "c", "v", "b"],
|
|
].flat();
|
|
|
|
const Picker = ({
|
|
colors,
|
|
color,
|
|
onChange,
|
|
onClose,
|
|
label,
|
|
showInput = true,
|
|
type,
|
|
elements,
|
|
}: {
|
|
colors: string[];
|
|
color: string | null;
|
|
onChange: (color: string) => void;
|
|
onClose: () => void;
|
|
label: string;
|
|
showInput: boolean;
|
|
type: "canvasBackground" | "elementBackground" | "elementStroke";
|
|
elements: readonly ExcalidrawElement[];
|
|
}) => {
|
|
const firstItem = React.useRef<HTMLButtonElement>();
|
|
const activeItem = React.useRef<HTMLButtonElement>();
|
|
const gallery = React.useRef<HTMLDivElement>();
|
|
const colorInput = React.useRef<HTMLInputElement>();
|
|
|
|
const [customColors] = React.useState(() => {
|
|
if (type === "canvasBackground") {
|
|
return [];
|
|
}
|
|
return getCustomColors(elements, type);
|
|
});
|
|
|
|
React.useEffect(() => {
|
|
// After the component is first mounted focus on first input
|
|
if (activeItem.current) {
|
|
activeItem.current.focus();
|
|
} else if (colorInput.current) {
|
|
colorInput.current.focus();
|
|
} else if (gallery.current) {
|
|
gallery.current.focus();
|
|
}
|
|
}, []);
|
|
|
|
const handleKeyDown = (event: React.KeyboardEvent) => {
|
|
let handled = false;
|
|
if (isArrowKey(event.key)) {
|
|
handled = true;
|
|
const { activeElement } = document;
|
|
const isRTL = getLanguage().rtl;
|
|
let isCustom = false;
|
|
let index = Array.prototype.indexOf.call(
|
|
gallery.current!.querySelector(".color-picker-content--default")
|
|
?.children,
|
|
activeElement,
|
|
);
|
|
if (index === -1) {
|
|
index = Array.prototype.indexOf.call(
|
|
gallery.current!.querySelector(".color-picker-content--canvas-colors")
|
|
?.children,
|
|
activeElement,
|
|
);
|
|
if (index !== -1) {
|
|
isCustom = true;
|
|
}
|
|
}
|
|
const parentElement = isCustom
|
|
? gallery.current?.querySelector(".color-picker-content--canvas-colors")
|
|
: gallery.current?.querySelector(".color-picker-content--default");
|
|
|
|
if (parentElement && index !== -1) {
|
|
const length = parentElement.children.length - (showInput ? 1 : 0);
|
|
const nextIndex =
|
|
event.key === (isRTL ? KEYS.ARROW_LEFT : KEYS.ARROW_RIGHT)
|
|
? (index + 1) % length
|
|
: event.key === (isRTL ? KEYS.ARROW_RIGHT : KEYS.ARROW_LEFT)
|
|
? (length + index - 1) % length
|
|
: !isCustom && event.key === KEYS.ARROW_DOWN
|
|
? (index + 5) % length
|
|
: !isCustom && event.key === KEYS.ARROW_UP
|
|
? (length + index - 5) % length
|
|
: index;
|
|
(parentElement.children[nextIndex] as HTMLElement | undefined)?.focus();
|
|
}
|
|
event.preventDefault();
|
|
} else if (
|
|
keyBindings.includes(event.key.toLowerCase()) &&
|
|
!event[KEYS.CTRL_OR_CMD] &&
|
|
!event.altKey &&
|
|
!isWritableElement(event.target)
|
|
) {
|
|
handled = true;
|
|
const index = keyBindings.indexOf(event.key.toLowerCase());
|
|
const isCustom = index >= MAX_DEFAULT_COLORS;
|
|
const parentElement = isCustom
|
|
? gallery?.current?.querySelector(
|
|
".color-picker-content--canvas-colors",
|
|
)
|
|
: gallery?.current?.querySelector(".color-picker-content--default");
|
|
const actualIndex = isCustom ? index - MAX_DEFAULT_COLORS : index;
|
|
(
|
|
parentElement?.children[actualIndex] as HTMLElement | undefined
|
|
)?.focus();
|
|
|
|
event.preventDefault();
|
|
} else if (event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) {
|
|
handled = true;
|
|
event.preventDefault();
|
|
onClose();
|
|
}
|
|
if (handled) {
|
|
event.nativeEvent.stopImmediatePropagation();
|
|
event.stopPropagation();
|
|
}
|
|
};
|
|
|
|
const renderColors = (colors: Array<string>, custom: boolean = false) => {
|
|
return colors.map((_color, i) => {
|
|
const _colorWithoutHash = _color.replace("#", "");
|
|
const keyBinding = custom
|
|
? keyBindings[i + MAX_DEFAULT_COLORS]
|
|
: keyBindings[i];
|
|
const label = custom
|
|
? _colorWithoutHash
|
|
: t(`colors.${_colorWithoutHash}`);
|
|
return (
|
|
<button
|
|
className="color-picker-swatch"
|
|
onClick={(event) => {
|
|
(event.currentTarget as HTMLButtonElement).focus();
|
|
onChange(_color);
|
|
}}
|
|
title={`${label}${
|
|
!isTransparent(_color) ? ` (${_color})` : ""
|
|
} — ${keyBinding.toUpperCase()}`}
|
|
aria-label={label}
|
|
aria-keyshortcuts={keyBindings[i]}
|
|
style={{ color: _color }}
|
|
key={_color}
|
|
ref={(el) => {
|
|
if (!custom && 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">{keyBinding}</span>
|
|
</button>
|
|
);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`color-picker color-picker-type-${type}`}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={t("labels.colorPicker")}
|
|
onKeyDown={handleKeyDown}
|
|
>
|
|
<div className="color-picker-triangle color-picker-triangle-shadow"></div>
|
|
<div className="color-picker-triangle"></div>
|
|
<div
|
|
className="color-picker-content"
|
|
ref={(el) => {
|
|
if (el) {
|
|
gallery.current = el;
|
|
}
|
|
}}
|
|
// to allow focusing by clicking but not by tabbing
|
|
tabIndex={-1}
|
|
>
|
|
<div className="color-picker-content--default">
|
|
{renderColors(colors)}
|
|
</div>
|
|
{!!customColors.length && (
|
|
<div className="color-picker-content--canvas">
|
|
<span className="color-picker-content--canvas-title">
|
|
{t("labels.canvasColors")}
|
|
</span>
|
|
<div className="color-picker-content--canvas-colors">
|
|
{renderColors(customColors, true)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{showInput && (
|
|
<ColorInput
|
|
color={color}
|
|
label={label}
|
|
onChange={(color) => {
|
|
onChange(color);
|
|
}}
|
|
ref={colorInput}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ColorInput = React.forwardRef(
|
|
(
|
|
{
|
|
color,
|
|
onChange,
|
|
label,
|
|
}: {
|
|
color: string | null;
|
|
onChange: (color: string) => void;
|
|
label: string;
|
|
},
|
|
ref,
|
|
) => {
|
|
const [innerValue, setInnerValue] = React.useState(color);
|
|
const inputRef = React.useRef(null);
|
|
|
|
React.useEffect(() => {
|
|
setInnerValue(color);
|
|
}, [color]);
|
|
|
|
React.useImperativeHandle(ref, () => inputRef.current);
|
|
|
|
const changeColor = React.useCallback(
|
|
(inputValue: string) => {
|
|
const value = inputValue.toLowerCase();
|
|
const color = getColor(value);
|
|
if (color) {
|
|
onChange(color);
|
|
}
|
|
setInnerValue(value);
|
|
},
|
|
[onChange],
|
|
);
|
|
|
|
return (
|
|
<label className="color-input-container">
|
|
<div className="color-picker-hash">#</div>
|
|
<input
|
|
spellCheck={false}
|
|
className="color-picker-input"
|
|
aria-label={label}
|
|
onChange={(event) => changeColor(event.target.value)}
|
|
value={(innerValue || "").replace(/^#/, "")}
|
|
onBlur={() => setInnerValue(color)}
|
|
ref={inputRef}
|
|
/>
|
|
</label>
|
|
);
|
|
},
|
|
);
|
|
|
|
ColorInput.displayName = "ColorInput";
|
|
|
|
export const ColorPicker = ({
|
|
type,
|
|
color,
|
|
onChange,
|
|
label,
|
|
isActive,
|
|
setActive,
|
|
elements,
|
|
appState,
|
|
}: {
|
|
type: "canvasBackground" | "elementBackground" | "elementStroke";
|
|
color: string | null;
|
|
onChange: (color: string) => void;
|
|
label: string;
|
|
isActive: boolean;
|
|
setActive: (active: boolean) => void;
|
|
elements: readonly ExcalidrawElement[];
|
|
appState: AppState;
|
|
}) => {
|
|
const pickerButton = React.useRef<HTMLButtonElement>(null);
|
|
const coords = pickerButton.current?.getBoundingClientRect();
|
|
|
|
return (
|
|
<div>
|
|
<div className="color-picker-control-container">
|
|
<div className="color-picker-label-swatch-container">
|
|
<button
|
|
className="color-picker-label-swatch"
|
|
aria-label={label}
|
|
style={color ? { "--swatch-color": color } : undefined}
|
|
onClick={() => setActive(!isActive)}
|
|
ref={pickerButton}
|
|
/>
|
|
</div>
|
|
<ColorInput
|
|
color={color}
|
|
label={label}
|
|
onChange={(color) => {
|
|
onChange(color);
|
|
}}
|
|
/>
|
|
</div>
|
|
<React.Suspense fallback="">
|
|
{isActive ? (
|
|
<div
|
|
className="color-picker-popover-container"
|
|
style={{
|
|
position: "fixed",
|
|
top: coords?.top,
|
|
left: coords?.right,
|
|
zIndex: 1,
|
|
}}
|
|
>
|
|
<Popover
|
|
onCloseRequest={(event) =>
|
|
event.target !== pickerButton.current && setActive(false)
|
|
}
|
|
>
|
|
<Picker
|
|
colors={colors[type]}
|
|
color={color || null}
|
|
onChange={(changedColor) => {
|
|
onChange(changedColor);
|
|
}}
|
|
onClose={() => {
|
|
setActive(false);
|
|
pickerButton.current?.focus();
|
|
}}
|
|
label={label}
|
|
showInput={false}
|
|
type={type}
|
|
elements={elements}
|
|
/>
|
|
</Popover>
|
|
</div>
|
|
) : null}
|
|
</React.Suspense>
|
|
</div>
|
|
);
|
|
};
|