* Enable multi points in lines
* Stop retrieving arrow points for lines
* Migrate lines to new spec during load
* Clean up and refactor some code
- Normalize shape dimensions during load
- Rename getArrowAbsoluteBounds
* Fix linter issues
* Add Hint viewer
- Add hints for arrows and lines
- Add hints for resizing elements
* Swap priority of multi mode and resize mode in Hint Viewer
* Remove dangling locales from public
* Add shortcut to hide hints
* Change hint texts and show resize hint ONLY during resizing
* Remove hints toggling
* Add keybindings for shapes
I'm not 100% sure about this one. I feel like it's going to help people be a lot more productive to display the key bindings at all time. But it also clutters the UI...
* increase font-size
* fix shape keybindings for non-qwerty keyboards
* tweak position and color
Co-authored-by: David Luzar <luzar.david@gmail.com>
With the infinite scroll behavior, it's easy to scroll super far away from where the content is and have a hard time getting back. This PR adds a button to refocus on the center of the scene when no elements are visible anymore.
* support undo/redo for azerty keyboards
* migrate to event.key
* remove unnecessary shiftKey check
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Add keybindings for color picker
This adds the ability to navigate using left/right/bottom/up keys and shows key bindings for all the different colors. This is only optimized for the qwerty keyboard layout, but unfortunately it's not possible to detect other keyboard layouts :(
* add aria-keyshortcuts and keybinding in title
* make focus select color, confirm on enter
Co-authored-by: David Luzar <luzar.david@gmail.com>
* Revert "Revert "Feature: Multi Point Arrows (#338)" (#634)"
This reverts commit 3d2e59bfed4fa41a0cae49ee567a6f95ca26e7bf.
* Convert old arrow spec to new one
* Remove unnecessary failchecks and fix context transform issue in retina displays
* Remove old points failcheck from getArrowAbsoluteBounds
* Remove all failchecks for old arrow
* remove the rest of unnecessary checks
* Set default values for the arrow during import
* Add translations
* fix restore using unmigrated elements for state computation
* don't use width/height when migrating from new arrow spec
Co-authored-by: David Luzar <luzar.david@gmail.com>
Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
Turns out the root cause was the outline. For some reason, doing "transparent" doesn't work but doing "1px solid transparent" does. Don't know why but I'll take it!
There's a bug where the carret doesn't show up when the text is first focused on Chrome with the previous combination of CSS. I tweaked it and now it seems to work (don't ask me why!).
Unfortunately on Safari, ever since we moved to contentEditable on #274, the carret disappeared the first time. I unsuccessfully tried to repro in a smaller codebase ( https://jsfiddle.net/u2mjs90y/1/ ) but it does work in Safari...
I'm not exactly sure what's going on, there are bunch of issues opened against this bug when googling against all the browsers...
We did some hackery to prevent copy pasting when we didn't support multilines. But we do now so we can remove it.
Interestingly, newlines are actually <br /> elements. So we need to tweak the isInputLike logic a bit
Thanks to this stack overflow answer ( https://stackoverflow.com/a/41678350/232122 ) I was able to fix the font preloading!
If we put fonts.css in the public/folder and include it with normal html, we can avoid going through the build pipeline!
By using position: fixed like the rest of the UI components, it will no longer make the body change size and have bad side effects like scrolling.
Fixes#365
This is the only way to navigate using the keyboard and was prevented by #596. Now it works fine.
Test Plan:
- Click on selection icon
- Use left/right arrow keys to move between tool icons, that works.
- Click on a shape, cmd+c
- Click on the selection icon
- Cmd+v, it pastes correctly
The test that was added is not a good test. If we want to test things, we should be testing logic that is error prone such as all the mouse handling logic and state management. Adding a test for something trivial as displaying a list of data is just going to be annoying when we eventually change the UI and the test breaks.
Since this is the only test using enzyme, I also removed enzyme. We can add it back if we want to test a component using it.
There are two problems with the current localization strategy:
- We download the translations on-demand, which means that it does a serial roundtrip for nothing.
- withTranslation helper actually renders the app 3 times on startup, instead of once (I haven't tried to debug it)
Instead of finding all the places where we want to resume recording, we should do it after every componentDidUpdate(). The idea is that we just want to disable the history for certain setState, for which we call directly before skipHistory.
* Add points to arrow on double click
* Use line generator instead of path to generate line segments
* Switch color of the circle when it is on an existing point in the segment
* Check point against both ends of the line segment to find collinearity
* Keep drawing the arrow based on mouse position until shape is changed
* Always select the arrow when in multi element mode
* Use curves instead of lines when drawing arrow points
* Add basic collision detection with some debug points
* Use roughjs shape when performing hit testing
* Draw proper handler rectangles for arrows
* Add argument to renderScene in export
* Globally resize all points on the arrow when bounds are resized
* Hide handler rectangles if an arrow has no size
- Allow continuing adding arrows when selected element is deleted
* Add dragging functionality to arrows
* Add SHIFT functionality to two point arrows
- Fix arrow positions when scrolling
- Revert the element back to selection when not in multi select mode
* Clean app state for export (JSON)
* Set curve options manually instead of using global options
- For some reason, this fixed the flickering issue in all shapes when arrows are rendered
* Set proper options for the arrow
* Increaase accuracy of hit testing arrows
- Additionally, skip testing if point is outside the domain of arrow and each curve
* Calculate bounding box of arrow based on roughjs curves
- Remove domain check per curve
* Change bounding box threshold to 10 and remove unnecessary code
* Fix handler rectangles for 2 and multi point arrows
- Fix margins of handler rectangles when using arrows
- Show handler rectangles in endpoints of 2-point arrows
* Remove unnecessary values from app state for export
* Use `resetTransform` instead of "retranslating" canvas space after each element rendering
* Allow resizing 2-point arrows
- Fix position of one of the handler rectangles
* refactor variable initialization
* Refactored to extract out mult-point generation to the abstracted function
* prevent dragging on arrow creation if under threshold
* Finalize selection during multi element mode when ENTER or ESC is clicked
* Set dragging element to null when finalizing
* Remove pathSegmentCircle from code
* Check if element is any "non-value" instead of NULL
* Show two points on any two point arrow and fix visibility of arrows during scroll
* Resume recording when done with drawing
- When deleting a multi select element, revert back to selection element type
* Resize arrow starting points perfectly
* Fix direction of arrow resize based for NW
* Resume recording history when there is more than one arrow
* Set dragging element to NULL when element is not locked
* Blur active element when finalizing
* Disable undo/redo for multielement, editingelement, and resizing element
- Allow undoing parts of the arrow
* Disable element visibility for arrow
* Use points array for arrow bounds when bezier curve shape is not available
Co-authored-by: David Luzar <luzar.david@gmail.com>
Co-authored-by: Preet <833927+pshihn@users.noreply.github.com>
* Panning with space key
* prevent panning when selecting/dragging & add more checks
* Fix changing current tool via shortcut while panning
* Fix order of statements
* teardown on blur event
* Refactor cursor setting
Co-authored-by: David Luzar <luzar.david@gmail.com>
* first draft of export to SVG. WIP
* enabled text rendeing - which is not quite right atm
* placeholder svg icon
* size the canvas based on the bounding box of elements
* Do not add opacity attributes if default
* render background rect
* Ensure arrows are in the same SVG group
* parse font-size from font
* export web fonts
* use fixed locations for fonts
* Rename export functions
* renamed export file
* oops broke the icon.
Use HTML semantic elements to set the landmarks of the page.
This is helpful for assistive technologies to determine the different regions of content. In our case it's useful for jumping between the different islands that we use to group the form controls.
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
* Add IDs to elements
- Move round rect function within the renderer
* Generate IDs using nanoid
* If element ID does not exist, add the ID during restoration
* Remove `generatedraw` from element object
- Create a function that renders a single element
- Refactor rendering selected elements
* Replace getElementAbsoluteXY with getElementAbsoluteCoords
* Extract History into its own module
* Encapsulate undo and redo actions within history
* Encapsulate clearing redo stack within History
* Add private access modifiers to scene history class member variables
* Remove duplicate files
* Move math and random files into their respective modules
- Move distanceBetweenPointAndSegment to math module
- Move LCG, randomSeed, and withCustomMathRandom to random module
* Add everything else back
* Allow user to set file name
* Add EditableText component
Added editable text component and use component for project name edit.
* rebased branch
* Updated EditableText component
* Set default project name
* Move project name field away from the top section.
* Set the cursor to 'crosshair' when expected to draw
And reset it back to default on mouse up.
Fixes#102
* Also reset cursor on text selection
* Use 'text' cursor for text
* Add yarn.lock to .gitignore
* Extract available shapes to one place
* Add event listeners for shapes shortcuts
* fixup! Add event listeners for shapes shortcuts
* Underline first letter of shapes
to indicate interactivity
* fixup! Extract available shapes to one place
* fixup! Add event listeners for shapes shortcuts
* add package-lock.json to gitignore as project appears to use yarn
* add support for deleting elements with "delete" key + assign magic numbers and keycode values to named properties