Some PR made the settings UI show up even though nothing is selected. However, you couldn't actually change any of those settings except for the colors. This PR pipes through the rest of the properties so everything works now!
When we are scrolling, resizing, or moving elements, we already disable the history. Since those actions do not change the state of the UI, we can also avoid re-drawing it and save ~10ms per frame.
I had to change all the forceUpdate() to setState({}), otherwise it would bypass shouldComponentUpdate.
* Add a gap between shapes and lock
The lock is a different type as the rest of the shapes, so we should visually separate it.
* redesign lock icon
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Only show correct settings
The logic to display which settings when nothing was selected was incorrect. This PR ensures that they are in sync.
I also removed all the <hr /> which after the redesigned just looked like weird empty spaces
* fix handling editing/text elements
Co-authored-by: David Luzar <luzar.david@gmail.com>
A common workflow I have is to enable the lock, draw a bunch of things, unlock to be able to select stuff. However, after I unlock, the last shape is still enabled, so I end up drawing yet another of the same shape :(
This PR resets to selection instead!
If you have two elements selected that have a different value (eg: a green line and a red line), the value of the color picker should be undefined, not the default value.
If you scroll and draw to the left of the origin, when you export the scene, there's a weird whitespace on the right. This is because we do the min() computation starting at 0 and not -Infinity
This also fixes pasted elements and scrollbars.
This updates the way fonts are handled to not have to download the font from the internet.
If you do `./public/font.ttf` in the .scss file, unfortunately the bundle packages them somewhere that's different from the public folder that is used by the index.html to preload them.
The fix I used is to use absolute path `/font.ttf` so that both work. Unfortunately, this means that the website will only work if at the root of the domain. That's the case so far so it's okay but still annoying if we want to embed it somewhere.
Problem:
- Select arrow
- Mouse down somewhere
- Mouse move somewhere to create an arrow
- Press escape
- Now you're in a weird situation where the shape is now "selection" but you're still dragging the arrow. If you mouse up, the arrow disappears
In order to solve this problem, we can avoid making escape do anything if you're currently dragging an element
Sorry my OCD is kicking in... It's super weird that the base of the lock moves when we check / unckeck it. Instead, just the semi-circle shape should move (what this PR implements).
It turns out the only thing we need to save in the appState is the background color. All the rest is transient data.
I added `"type": "excalidraw"` at the beginning to explain where it was.
I removed `"source": "http://localhost:3000/"`. I don't think we want to leak on which webpage it was saved from.
I removed `isSelected` from the json
I added indentation so it's easier to read the content. I'm not 100% sure on this one, but I figure filesize doesn't matter too much those days. And if we want to shrink it, there are more effective ways than json.
Improve the accessibility of our modals (the color picker and the export dialog)
Implement a focus trap so that tapping through the controls inside them don't escape to outer elements, it also allows to close the modals with the "Escape" key.
Right now we're running useEffect block which runs getExportCanvasPreview on every state update, even if the modal is not visible (eg: when moving the mouse around!). This puts the modal code in its own component so that it doesn't trigger useEffect when not visible.
The code isn't very elegant as we're forwarding all the props, there's likely a better way to handle it (if anyone is interested, PR would be appreciated), but at least now it no longer double renders the scene.
Fixes#559
* Do not store cursor position in state
Storing it in state causes a full re-render. The only time we use the cursor position is for pasting. This halves the number of renders on drag.
* remove passive change
* Rename ToolIcon to ToolButton
It makes more semantic sense
* Label and keyboard shortcuts announcement
* Refactor common props for ToolButton
* Better doc outline and form controls
* Adjust color picker
* Styling fixes
Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
* Add german translation
* Add german language option
* Fix missing german translation
* Alphabetical order for languages
* Sort
Co-authored-by: Lipis <lipiridis@gmail.com>
* add translations in data.ts
* add language list
add spanish version
* fixes pr review
* add more translations
* remove unused label
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Render only visible elements
* Fixed exporting as PNG
* Moved isVisibleElement to module scope
* rerun-ci
Co-authored-by: David Luzar <luzar.david@gmail.com>
* update events for GH actions to include PRs
* fix: cursor on keyboard tool toggle
* fix: change cursor type to constant
* fix: swap condition
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Introduce shape lock
* Format code with prettier
* Do not reset elementLocked on selection change
* Don't set isSelected to true if element is locked
* Don't reset the cursor
* Move reset cursor call to better spot
* Run prettier + lint
* ensure panel props are sync to editing elem
* ensure we don't create empty-text elements (fixes#468)
* remove dead code
Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
* Render text actions panel on double click
* cleanup wysiwyg on click
* use `state.editingElement` instead of global to determine whether t ext panel is shown
* clarify comment
Co-authored-by: David Luzar <luzar.david@gmail.com>
Previously the type used for the data URI when saving was text/plain.
On iPad, this caused the file to automatically have a .txt extension
added (so files ended up with names like "drawing-xyz.json.txt"). This
meant that the files couldn't be loaded by the tool, which expects only
files with a .json extension.
Now, the type used is application/json, which means that the files get
saved with the correct extension and can be successfully loaded on iPad.
* Add Native File System API saving/exporting
* Add Native File System API opening
* Add origin trial token placeholder
* Reuse an opened file handle for better saving experience
* Fix file handle reuse to only kick in for Excalidraw files
* Remove reference
* feat: add line shape
* fix: align line tool
* fix: hitbox bug sw to ne
* fix: add stroke width n sloppiness for line
* fix: center line inside a panel box
* fix: use color as a unique key
* Redisign idea
* Code cleanup
* Fixed to right container
* Reoredered layout
* Reordering panels
* Export dialog
* Removed redunant code
* Fixed not removing temp canvas
* Fixed preview not using only selected elements
* Returned file name on export
* Toggle export selected/all elements
* Hide copy to clipboard button if no support of clipboard
* Added border to swatches
* Fixed modal flickering
Fixes#252
Test plan:
- Click on text icon
- Click anywhere to start entering text
- Add a letter
- Make sure the cursor is selection and not text
- Click anywhere else, make sure it completes the text and not create a new one
I profiled dragging and it looks like it takes ~3ms to save to localStorage a smallish scene and we're doing it twice per mousemove. Let's debounce so we don't pay that cost on every mouse move.
Stole the implementation from #220 which got reverted.
Unfortunately, react-color has a bug where transparent color doesn't trigger onChange. I've been annoyed by the huge dependency anyway so decided to take the generated html (which is awesome) and reimplement a specific component for it.
I also made sure that we don't actually render anything when the background is transparent on rough (I looked at the generated path and made sure it didn't have the commands for the background)
* Regenerate roughjs shape only when the item is updated
* Remove shape object during export and history undo/redo
* Remove shape element during copying
* Fix shape generation during creation
- Move the context menu right next to the mouse so it's not so far away. But 1px out so that nothing is selected until you move your mouse
- Change the colors to be closer to the macos one. Unfortunately, macos has a 0.5px border that I'm not able to reproduce without some annoying hacks, 1px it'll be.
* Add Action System
- Add keyboard test
- Add context menu label
- Add PanelComponent
* Show context menu items based on actions
* Add render action feature
- Replace bringForward etc buttons with action manager render functions
* Move all property changes and canvas into actions
* Remove unnecessary functions and add forgotten force update when elements array change
* Extract export operations into actions
* Add elements and app state as arguments to `keyTest` function
* Add key priorities
- Sort actions by key priority when handling key presses
* Extract copy/paste styles
* Add Context Menu Item order
- Sort context menu items based on menu item order parameter
* Remove unnecessary functions from App component
* Command clicking should "xor" selection
* Only shift key should play a role
* Get rid of `isDraggingElements`
* Renamed someElementIsDragged to draggingOccured
* Make scene functions return array instead of mutate array
- Not all functions were changes; so the given argument was a new array to some
* Make data restoration functions immutable
- Make mutations in App component
* Make history actions immutable
* Fix an issue in change property that was causing elements to be removed
* mark elements params as readonly & remove unnecessary copying
* Make `clearSelection` return a new array
* Perform Id comparisons instead of reference comparisons in onDoubleClick
* Allow deselecting items with SHIFT key
- Refactor hit detection code
* Fix a bug in element selection and revert drag functionality
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Extract app component from entrypoint (index)
- Use refs to refer to canvas and rough context
- Remove ReactDOM double rendering
* Extract keys and key related utils into their own module
* Move everything back to entrypoint
* Make panels collapsible
- Add Panel component with collapse logic
- Use the component in all the necessary panel groups
* Remove unnecessary container from PanelCanvas
* Add "hide property" to Pane component to hide Panel contents using a prop
- Instead of doing conditional rendering, pass the condition to Panel as props
* Change collapse icon rotation for closed
- Use one icon and use CSS transforms to rotate it
* Remove unnecessary imports from PanelSelection
* Snap to element center
* Fixed typo
* Added comment
* Reduced threshold to 30
* Skip snapping if alt key is pressed
* Fixed creating text with shape tool