Compare commits
18 Commits
v0.14.2
...
dwelle/hit
Author | SHA1 | Date | |
---|---|---|---|
c1b903395a | |||
96b172ebfa | |||
16db74cedd | |||
4d1b31a171 | |||
0d7ee891e0 | |||
71fb60394a | |||
c9d18ecab6 | |||
8c1168ef33 | |||
c3c45a8c37 | |||
a8e6028c33 | |||
11e2f90ca1 | |||
4db87a0b6a | |||
4414069617 | |||
a9c5bdb878 | |||
5a0334f37f | |||
d8a4ca6911 | |||
eb9eeefc63 | |||
f23cdc47ee |
@ -1,63 +1,3 @@
|
||||
# Contributing
|
||||
|
||||
## Setup
|
||||
|
||||
### Option 1 - Manual
|
||||
|
||||
1. Fork and clone the repo
|
||||
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:
|
||||
>
|
||||
> ```sh
|
||||
> git remote add upstream https://github.com/excalidraw/excalidraw.git
|
||||
> git fetch upstream
|
||||
> git branch --set-upstream-to=upstream/master master
|
||||
> ```
|
||||
|
||||
### Option 2 - CodeSandbox
|
||||
|
||||
1. Go to https://codesandbox.io/s/github/excalidraw/excalidraw
|
||||
1. Connect your GitHub account
|
||||
1. Go to Git tab on left side
|
||||
1. Tap on `Fork Sandbox`
|
||||
1. Write your code
|
||||
1. Commit and PR automatically
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
Don't worry if you get any of the below wrong, or if you don't know how. We'll gladly help out.
|
||||
|
||||
### Title
|
||||
|
||||
Make sure the title starts with a semantic prefix:
|
||||
|
||||
- **feat**: A new feature
|
||||
- **fix**: A bug fix
|
||||
- **docs**: Documentation only changes
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
- **perf**: A code change that improves performance
|
||||
- **test**: Adding missing tests or correcting existing tests
|
||||
- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
- **chore**: Other changes that don't modify src or test files
|
||||
- **revert**: Reverts a previous commit
|
||||
|
||||
### Changelog
|
||||
|
||||
Add a brief description of your pull request to the changelog located here: [`src/packages/excalidraw/CHANGELOG.md`](src/packages/excalidraw/CHANGELOG.md)
|
||||
|
||||
Notes:
|
||||
|
||||
- Make sure to prepend to the section corresponding with the semantic prefix you selected in the title
|
||||
- Link to your pull request - this will require updating the CHANGELOG _after_ creating the pull request
|
||||
|
||||
### Testing
|
||||
|
||||
Once you submit your pull request it will automatically be tested. Be sure to check the results of the test and fix any issues that arise.
|
||||
|
||||
It's also a good idea to consider if your change should include additional tests. This is highly recommended for new features or bug-fixes. For example, it's good practice to create a test for each bug you fix which ensures that we don't regress the code in the future.
|
||||
|
||||
Finally - always manually test your changes using the convenient staging environment deployed for each pull request. As much as local development attempts to replicate production, there can still be subtle differences in behavior. For larger features consider testing your change in multiple browsers as well.
|
||||
Head over to the [docs](https://docs.excalidraw.com/docs/introduction/contributing)
|
||||
|
@ -3,7 +3,7 @@ FROM node:14-alpine AS build
|
||||
WORKDIR /opt/node_app
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn --ignore-optional
|
||||
RUN yarn --ignore-optional --network-timeout 600000
|
||||
|
||||
ARG NODE_ENV=production
|
||||
|
||||
|
197
README.md
@ -1,30 +1,31 @@
|
||||
<div align="center" style="display:flex;flex-direction:column;">
|
||||
<div align="center" style="display:flex;flex-direction:column;"}>
|
||||
<a href="https://excalidraw.com">
|
||||
<img width="540" src="./public/og-image-sm.png" alt="Excalidraw logo: Sketch handrawn like diagrams." />
|
||||
<img width="540" src="./public/og-image-sm.png" alt="Excalidraw logo: Sketch handrawn like diagrams."/>
|
||||
</a>
|
||||
<h3>Virtual whiteboard for sketching hand-drawn like diagrams.<br>Collaborative and end-to-end encrypted.</h3>
|
||||
<h3>Virtual whiteboard for sketching hand-drawn like diagrams.<br/>Collaborative and end-to-end encrypted.</h3>
|
||||
<p>
|
||||
<a href="https://twitter.com/Excalidraw">
|
||||
<img alt="Follow Excalidraw on Twitter" src="https://img.shields.io/twitter/follow/excalidraw.svg?label=follow+excalidraw&style=social&logo=twitter">
|
||||
<a href="https://twitter.com/excalidraw">
|
||||
<img alt="Follow Excalidraw on Twitter" src="https://img.shields.io/twitter/follow/excalidraw.svg?label=follow+@excalidraw&style=social&logo=twitter"/>
|
||||
</a>
|
||||
<a target="_blank" href="https://crowdin.com/project/excalidraw">
|
||||
<img src="https://badges.crowdin.net/excalidraw/localized.svg">
|
||||
<a href="https://discord.gg/UexuTaE">
|
||||
<img alt="Chat with us on Discord" src="https://img.shields.io/discord/723672430744174682?color=738ad6&label=Chat%20on%20Discord&logo=discord&logoColor=ffffff&widge=false"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>Ask questions or hang out on our <a target="_blank" href="https://discord.gg/UexuTaE">discord.gg/UexuTaE</a>.</p>
|
||||
</div>
|
||||
|
||||
## Try it now
|
||||
## Try now
|
||||
|
||||
Go to [excalidraw.com](https://excalidraw.com) to start sketching.
|
||||
Visit [excalidraw.com](https://excalidraw.com) to start sketching.
|
||||
|
||||
Read the latest news and updates on our [blog](https://blog.excalidraw.com). A good start is to see all the updates of [One Year of Excalidraw](https://blog.excalidraw.com/one-year-of-excalidraw/).
|
||||
## Community
|
||||
|
||||
For latest updates, follow us on [twitter](https://twitter.com/excalidraw). If you need help or want to chat, join us on [Discord](https://discord.gg/UexuTaE). For releases and deep dives, check out our [blog](https://blog.excalidraw.com). Report bugs on [GitHub](https://github.com/excalidraw/excalidraw/issues).
|
||||
|
||||
## Supporting Excalidraw
|
||||
|
||||
If you like the project, you can become a sponsor at [Open Collective](https://opencollective.com/excalidraw).
|
||||
|
||||
[<img src="https://opencollective.com/excalidraw/tiers/sponsors/0/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/0/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/1/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/1/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/2/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/2/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/3/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/3/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/4/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/4/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/5/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/5/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/6/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/6/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/7/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/7/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/8/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/8/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/9/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/9/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/10/avatar.svg?avatarHeight=120">](https://opencollective.com/excalidraw/tiers/sponsors/10/website)
|
||||
[<img src="https://opencollective.com/excalidraw/tiers/sponsors/0/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/0/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/1/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/1/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/2/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/2/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/3/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/3/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/4/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/4/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/5/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/5/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/6/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/6/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/7/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/7/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/8/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/8/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/9/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/9/website) [<img src="https://opencollective.com/excalidraw/tiers/sponsors/10/avatar.svg?avatarHeight=120"/>](https://opencollective.com/excalidraw/tiers/sponsors/10/website)
|
||||
|
||||
<a href="https://opencollective.com/excalidraw#category-CONTRIBUTE" target="_blank"><img src="https://opencollective.com/excalidraw/tiers/backers.svg?avatarHeight=32"/></a>
|
||||
|
||||
@ -32,170 +33,12 @@ Last but not least, we're thankful to these companies for offering their service
|
||||
|
||||
[](https://vercel.com) [](https://sentry.io) [](https://crowdin.com)
|
||||
|
||||
## Developers
|
||||
|
||||
You can integrate Excalidraw into your app by installing our [npm component](https://npmjs.com/package/@excalidraw/excalidraw).
|
||||
|
||||
Visit our documentation on [https://docs.excalidraw.com](https://docs.excalidraw.com).
|
||||
|
||||
## Who's integrating Excalidraw
|
||||
|
||||
[Google Cloud](https://googlecloudcheatsheet.withgoogle.com/architecture) • [Meta](https://meta.com/) • [CodeSandbox](https://codesandbox.io/) • [Obsidian Excalidraw](https://github.com/zsviczian/obsidian-excalidraw-plugin) • [Replit](https://replit.com/) • [Slite](https://slite.com/) • [Notion](https://notion.so/) • [HackerRank](https://www.hackerrank.com/) •
|
||||
|
||||
## Documentation
|
||||
|
||||
### Shortcuts
|
||||
|
||||
You can almost do anything with shortcuts. Click on the help icon on the bottom right corner to see them all.
|
||||
|
||||
### Curved lines and arrows
|
||||
|
||||
Choose line or arrow and click click click instead of drag.
|
||||
|
||||
### Charts
|
||||
|
||||
You can easily create charts by copy pasting data from Excel or just plain comma separated text.
|
||||
|
||||
### Translating
|
||||
|
||||
To translate Excalidraw into other languages, please visit [our Crowdin page](https://crowdin.com/project/excalidraw). To add a new language, [open an issue](https://github.com/excalidraw/excalidraw/issues/new) so we can get things set up on our end first.
|
||||
|
||||
Translations will be available on the app if they exceed a certain threshold of completion (currently 85%).
|
||||
|
||||
### Create a collaboration session manually
|
||||
|
||||
In order to create a session manually, you just need to generate a link of this form:
|
||||
|
||||
```
|
||||
https://excalidraw.com/#room=[0-9a-f]{20},[a-zA-Z0-9_-]{22}
|
||||
```
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
https://excalidraw.com/#room=91bd46ae3aa84dff9d20,pfLqgEoY1c2ioq8LmGwsFA
|
||||
```
|
||||
|
||||
The first set of digits is the room. This is visible from the server that’s going to dispatch messages to everyone that knows this number.
|
||||
|
||||
The second set of digits is the encryption key. The Excalidraw server doesn’t 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).
|
||||
|
||||
## Embedding Excalidraw in your App?
|
||||
|
||||
Try out [`@excalidraw/excalidraw`](https://www.npmjs.com/package/@excalidraw/excalidraw). This package allows you to easily embed Excalidraw as a React component into your apps.
|
||||
|
||||
## Development
|
||||
|
||||
### Code Sandbox
|
||||
|
||||
- Go to https://codesandbox.io/p/github/excalidraw/excalidraw
|
||||
- You may need to sign in with GitHub and reload the page
|
||||
- You can start coding instantly, and even send PRs from there!
|
||||
|
||||
### Local Installation
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
|
||||
|
||||
#### Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org/en/)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install) (v1 or v2.4.2+)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
#### Clone the repo
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
#### Collaboration
|
||||
|
||||
For collaboration, you will need to set up [collab server](https://github.com/excalidraw/excalidraw-room) in local.
|
||||
|
||||
#### Commands
|
||||
|
||||
##### Install the dependencies
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
##### Run the project
|
||||
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
|
||||
##### Reformat all files with Prettier
|
||||
|
||||
```
|
||||
yarn fix
|
||||
```
|
||||
|
||||
##### Run tests
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
##### Update test snapshots
|
||||
|
||||
```
|
||||
yarn test:update
|
||||
```
|
||||
|
||||
##### Test for formatting with Prettier
|
||||
|
||||
```
|
||||
yarn test:code
|
||||
```
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
You can use docker-compose to work on Excalidraw locally if you don't want to setup a Node.js env.
|
||||
|
||||
```sh
|
||||
docker-compose up --build -d
|
||||
```
|
||||
|
||||
### Self-hosting
|
||||
|
||||
We publish a Docker image with the Excalidraw client at [excalidraw/excalidraw](https://hub.docker.com/r/excalidraw/excalidraw). You can use it to self-host your own client under your own domain, on Kubernetes, AWS ECS, etc.
|
||||
|
||||
```sh
|
||||
docker build -t excalidraw/excalidraw .
|
||||
docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latest
|
||||
```
|
||||
|
||||
The Docker image is free of analytics and other tracking libraries.
|
||||
|
||||
**At the moment, self-hosting your own instance doesn't support sharing or collaboration features.**
|
||||
|
||||
We are working towards providing a full-fledged solution for self-hosting your own Excalidraw.
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests are welcome. For major changes, please [open an issue](https://github.com/excalidraw/excalidraw/issues/new) first to discuss what you would like to change.
|
||||
|
||||
## Notable used tools
|
||||
|
||||
- [Create React App](https://github.com/facebook/create-react-app)
|
||||
- [Rough.js](https://roughjs.com)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [Vercel](https://vercel.com)
|
||||
|
||||
And the main source of inspiration for starting the project is the awesome [Zwibbler](https://zwibbler.com/demo/) app.
|
||||
[Google Cloud](https://googlecloudcheatsheet.withgoogle.com/architecture) • [Meta](https://meta.com/) • [CodeSandbox](https://codesandbox.io/) • [Obsidian Excalidraw](https://github.com/zsviczian/obsidian-excalidraw-plugin) • [Replit](https://replit.com/) • [Slite](https://slite.com/) • [Notion](https://notion.so/) • [HackerRank](https://www.hackerrank.com/)
|
||||
|
11
dev-docs/docs/@excalidraw/excalidraw/api/api-intro.mdx
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
slug: /@excalidraw/excalidraw/api
|
||||
---
|
||||
|
||||
# API
|
||||
|
||||
Currently the **API** is divided into 3 broad categories 👇
|
||||
|
||||
- [Props](/docs/@excalidraw/excalidraw/api/props) - The `props` you can pass to the `Excalidraw` component.
|
||||
- [Children components](/docs/@excalidraw/excalidraw/api/children-components) - Official components you can use to customize the UI.
|
||||
- [Utils](/docs/@excalidraw/excalidraw/api/utils) - Utilities and helpers you can use to export, restore and more.
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
sidebar_label: Children Components
|
||||
slug: /@excalidraw/excalidraw/api/children-components
|
||||
---
|
||||
|
||||
# `<Excalidraw/>` children
|
||||
|
||||
We expose several components you can render as children of the `<Excalidraw/>` component to customize the UI.
|
||||
|
||||
:::info
|
||||
|
||||
We have only recently started migrating to this type of component API. Some UI components are still using render props, and some UI customization isn't supported yet (such as the toolbar or the element properties panel). Stay tuned for more updates!
|
||||
|
||||
:::
|
||||
|
||||
Below are the currently supported components:
|
||||
|
||||
- [MainMenu](/docs/@excalidraw/excalidraw/api/children-components/main-menu)
|
||||
- [WelcomeScreen](/docs/@excalidraw/excalidraw/api/children-components/welcome-screen)
|
||||
- [Footer](/docs/@excalidraw/excalidraw/api/children-components/footer)
|
||||
- [LiveCollaborationTrigger](/docs/@excalidraw/excalidraw/api/children-components/live-collaboration-trigger)
|
@ -0,0 +1,69 @@
|
||||
# Footer
|
||||
|
||||
Earlier we were using `renderFooter` prop to render custom footer which was removed in [#5970](https://github.com/excalidraw/excalidraw/pull/5970). Now you can pass a `Footer` component instead to render the custom UI for footer.
|
||||
|
||||
You will need to import the `Footer` component from the package and wrap your component with the Footer component. The `Footer` should a valid React Node.
|
||||
|
||||
**Usage**
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px"}}>
|
||||
<Excalidraw>
|
||||
<Footer>
|
||||
<button
|
||||
className="custom-footer"
|
||||
onClick={() => alert("This is dummy footer")}
|
||||
>
|
||||
{" "}
|
||||
custom footer
|
||||
</button>
|
||||
</Footer>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
This will only for `Desktop` devices.
|
||||
|
||||
For `mobile` you will need to render it inside the [MainMenu](#mainmenu). You can use the [`useDevice`](#useDevice) hook to check the type of device, this will be available only inside the `children` of `Excalidraw` component.
|
||||
|
||||
Open the `Menu` in the below playground and you will see the `custom footer` rendered.
|
||||
|
||||
```jsx live noInline
|
||||
const MobileFooter = ({}) => {
|
||||
const device = useDevice();
|
||||
if (device.isMobile) {
|
||||
return (
|
||||
<Footer>
|
||||
<button
|
||||
className="custom-footer"
|
||||
style= {{ marginLeft: '20px', height: '2rem'}}
|
||||
onClick={() => alert("This is custom footer in mobile menu")}
|
||||
>
|
||||
custom footer
|
||||
</button>
|
||||
</Footer>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<div style={{ height: "400px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.Item> Item1 </MainMenu.Item>
|
||||
<MainMenu.Item> Item 2 </MainMenu.Item>
|
||||
<MobileFooter />
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Need to render when code is span across multiple components
|
||||
// in Live Code blocks editor
|
||||
render(<App />);
|
||||
```
|
@ -0,0 +1,62 @@
|
||||
# LiveCollaborationTrigger
|
||||
|
||||
If you implement live collaboration support and want to expose the same UI button as on [excalidraw.com](https://excalidraw.com), you can render the `<LiveCollaborationTrigger/>` component using the [renderTopRightUI](/docs/@excalidraw/excalidraw/api/props#rendertoprightui) prop.
|
||||
|
||||
You'll need to supply `onSelect()` to handle opening of your collaboration dialog, but the button will display `appState.collaborators` count provided you have supplied it.
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `onSelect` | `function` | Yes | | Handler called when the user clicks on the button |
|
||||
| `isCollaborating` | `boolean` | Yes | false | Whether live collaboration session is in effect. Modifies button style. |
|
||||
|
||||
```tsx live
|
||||
function App() {
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||
const [isCollaborating, setIsCollaborating] = useState(false);
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<p style={{ fontSize: "16px" }}>
|
||||
Selecting the checkbox to see the collaborator count
|
||||
</p>
|
||||
<label style={{ fontSize: "16px", fontWeight: "bold" }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isCollaborating}
|
||||
onChange={() => {
|
||||
if (!isCollaborating) {
|
||||
const collaborators = new Map();
|
||||
collaborators.set("id1", {
|
||||
username: "Doremon",
|
||||
avatarUrl: "../../../../img/doremon.png",
|
||||
});
|
||||
collaborators.set("id3", {
|
||||
username: "Pika",
|
||||
avatarUrl: "../../../../img/pika.jpeg",
|
||||
});
|
||||
excalidrawAPI.updateScene({ collaborators });
|
||||
} else {
|
||||
excalidrawAPI.updateScene({
|
||||
collaborators: new Map(),
|
||||
});
|
||||
}
|
||||
setIsCollaborating(!isCollaborating);
|
||||
}}
|
||||
/>
|
||||
Show Collaborators
|
||||
</label>
|
||||
<Excalidraw
|
||||
ref={(api) => setExcalidrawAPI(api)}
|
||||
renderTopRightUI={() => (
|
||||
<LiveCollaborationTrigger
|
||||
isCollaborating={isCollaborating}
|
||||
onSelect={() => {
|
||||
window.alert("You clicked on collab button");
|
||||
setIsCollaborating(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
></Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
@ -0,0 +1,169 @@
|
||||
# MainMenu
|
||||
|
||||
By default Excalidraw will render the `MainMenu` with default options. If you want to customise the `MainMenu`, you can pass the `MainMenu` component with the list options.
|
||||
|
||||
**Usage**
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "300px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.Item onSelect={() => window.alert("Item1")}>
|
||||
Item1
|
||||
</MainMenu.Item>
|
||||
<MainMenu.Item onSelect={() => window.alert("Item2")}>
|
||||
{" "}
|
||||
Item 2{" "}
|
||||
</MainMenu.Item>
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### `<MainMenu>`
|
||||
|
||||
This is the `MainMenu` component. If you render it, you will need to populate the menu with your own items as we will not render any ourselves at that point.
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | :-: | :-: | --- |
|
||||
| `onSelect` | `function` | No | - | Triggered when any item is selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
||||
|
||||
### MainMenu.Item
|
||||
|
||||
To render an item, its recommended to use `MainMenu.Item`.
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | :-: | :-: | --- |
|
||||
| `onSelect` | `function` | Yes | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
||||
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
||||
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
||||
| `shortcut` | `string` | No | - | The shortcut to be shown for the menu item |
|
||||
|
||||
### MainMenu.ItemLink
|
||||
|
||||
To render an item as a link, its recommended to use `MainMenu.ItemLink`.
|
||||
|
||||
**Usage**
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.ItemLink href="https://google.com">
|
||||
Google
|
||||
</MainMenu.ItemLink>
|
||||
<MainMenu.ItemLink href="https://excalidraw.com">
|
||||
Excalidraw
|
||||
</MainMenu.ItemLink>
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | :-: | :-: | --- |
|
||||
| `onSelect` | `function` | No | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
||||
| `href` | `string` | Yes | - | The `href` attribute to be added to the `anchor` element. |
|
||||
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
||||
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
||||
| `shortcut` | `string` | No | - | The shortcut to be shown for the menu item |
|
||||
|
||||
### MainMenu.ItemCustom
|
||||
|
||||
To render a custom item, you can use `MainMenu.ItemCustom`.
|
||||
|
||||
**Usage**
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.ItemCustom>
|
||||
<button
|
||||
style={{ height: "2rem" }}
|
||||
onClick={() => window.alert("custom menu item")}
|
||||
>
|
||||
{" "}
|
||||
custom item
|
||||
</button>
|
||||
</MainMenu.ItemCustom>
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | :-: | :-: | --- |
|
||||
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
||||
|
||||
### MainMenu.DefaultItems
|
||||
|
||||
For the items which are shown in the menu in [excalidraw.com](https://excalidraw.com), you can use `MainMenu.DefaultItems`
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.DefaultItems.Socials />
|
||||
<MainMenu.DefaultItems.Export />
|
||||
<MainMenu.Item onSelect={() => window.alert("Item1")}>
|
||||
Item1
|
||||
</MainMenu.Item>
|
||||
<MainMenu.Item onSelect={() => window.alert("Item2")}>
|
||||
Item 2
|
||||
</MainMenu.Item>
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Here is a [complete list](https://github.com/excalidraw/excalidraw/blob/master/src/components/mainMenu/DefaultItems.tsx) of the default items.
|
||||
|
||||
### MainMenu.Group
|
||||
|
||||
To Group item in the main menu, you can use `MainMenu.Group`
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.Group title="Excalidraw items">
|
||||
<MainMenu.DefaultItems.Socials />
|
||||
<MainMenu.DefaultItems.Export />
|
||||
</MainMenu.Group>
|
||||
<MainMenu.Group title="custom items">
|
||||
<MainMenu.Item onSelect={() => window.alert("Item1")}>
|
||||
Item1
|
||||
</MainMenu.Item>
|
||||
<MainMenu.Item onSelect={() => window.alert("Item2")}>
|
||||
Item 2
|
||||
</MainMenu.Item>
|
||||
</MainMenu.Group>
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | :-: | :-: | --- |
|
||||
| `children ` | `React.ReactNode` | Yes | - | The content of the `Menu Group` |
|
@ -0,0 +1,140 @@
|
||||
# WelcomeScreen
|
||||
|
||||
When the canvas is empty, Excalidraw can show a welcome _splash_ screen with a logo, a few quick action items, and hints explaining what some of the UI buttons do. Once the user picks a tool, or has created an element on the canvas, the welcome screen will disappear.
|
||||
|
||||
You can enable this behavior by rendering a `WelcomeScreen` component like this:
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "46rem" }}>
|
||||
<Excalidraw>
|
||||
<WelcomeScreen />
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
You can also customize the welcome screen by passing children to the `WelcomeScreen` component. See below.
|
||||
|
||||
## <WelcomeScreen/>
|
||||
|
||||
This is the main component. If you render it without any children, we will render the default welcome screen.
|
||||
|
||||
You can customize which welcome screen subcomponents are rendered by passing them as children.
|
||||
|
||||
The welcome screen consists of two main groups of subcomponents:
|
||||
|
||||
1. [WelcomeScreen.Center](#welcomescreencenterlogo).
|
||||
2. [WeelcomeScreen.Hints](#welcomescreenhints).
|
||||
|
||||
<img
|
||||
src={require("@site/static/img/welcome-screen-overview.png").default}
|
||||
alt="Excalidraw logo: Sketch handrawn like diagrams."
|
||||
/>
|
||||
|
||||
### Center
|
||||
|
||||
`<WelcomeScreen.Center/>` subcomponent is the center piece of the welcome screen, containing the `logo`, `heading`, and `menu`. All three subcomponents are optional, and you can render whatever you wish into the center component.
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<WelcomeScreen>
|
||||
<WelcomeScreen.Center>
|
||||
<WelcomeScreen.Center.Logo />
|
||||
<WelcomeScreen.Center.Heading>
|
||||
Welcome Screen Heading!
|
||||
</WelcomeScreen.Center.Heading>
|
||||
<WelcomeScreen.Center.Menu>
|
||||
<WelcomeScreen.Center.MenuItemLink href="https://github.com/excalidraw/excalidraw">
|
||||
Excalidraw GitHub
|
||||
</WelcomeScreen.Center.MenuItemLink>
|
||||
<WelcomeScreen.Center.MenuItemHelp />
|
||||
</WelcomeScreen.Center.Menu>
|
||||
</WelcomeScreen.Center>
|
||||
</WelcomeScreen>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Logo
|
||||
|
||||
Use the `<WelcomeScreen.Center.Logo/>` to render a logo. By default it renders the Excalidraw logo and name. Supply `children` to customize.
|
||||
|
||||
#### Heading
|
||||
|
||||
Use the `<WelcomeScreen.Center.Heading/>` to render a heading below the logo. Supply `children` to change the default message.
|
||||
|
||||
#### Menu
|
||||
|
||||
`<WelcomeScreen.Center.Menu/>` is a wrapper component for the menu items. You can build your menu using the `<WelcomeScreen.Center.MenuItem>` and `<WelcomeScreen.Center.MenuItemLink>` components, render your own, or render one of the default menu items.
|
||||
|
||||
The default menu items are:
|
||||
|
||||
- `<WelcomeScreen.Center.MenuItemHelp/>` - opens the help dialog.
|
||||
|
||||
- `<WelcomeScreen.Center.MenuItemLoadScene/>` - open the load file dialog.
|
||||
|
||||
- `<WelcomeScreen.Center.MenuItemLiveCollaborationTrigger/>` - intended to open the live collaboration dialog. Works similarly to [`<LiveCollaborationTrigger>`](/docs/@excalidraw/excalidraw/api/children-components/live-collaboration-trigger) and you must supply `onSelect()` handler to integrate with your collaboration implementation.
|
||||
|
||||
#### MenuItem
|
||||
|
||||
The `<WelcomeScreen.Center.MenuItem/>` component can be used to render a menu item.
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `onSelect` | `function` | Yes | | The handler is triggered when the item is selected. |
|
||||
| `children` | `React.ReactNode` | Yes | | The content of the menu item |
|
||||
| `icon` | `JSX.Element` | No | | The icon used in the menu item |
|
||||
| `shortcut` | `string` | No | | The keyboard shortcut (label-only, does not affect behavior) |
|
||||
|
||||
**WelcomeScreen.Center.MenuItemLink**
|
||||
|
||||
To render an external link in a menu item, you can use this component.
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `href` | `string` | Yes | | The `href` attribute to be added to the `anchor` element. |
|
||||
| `children` | `React.ReactNode` | Yes | | The content of the menu item |
|
||||
| `icon` | `JSX.Element` | No | | The icon used in the menu item |
|
||||
| `shortcut` | `string` | No | | The keyboard shortcut (label-only, does not affect behavior) |
|
||||
|
||||
### Hints
|
||||
|
||||
These `<WelcomeScreen.Hints.*>` subcomponents render the UI hints. Text of each hint can be customized by supplying `children`.
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw>
|
||||
<WelcomeScreen>
|
||||
<WelcomeScreen.Hints.ToolbarHint>
|
||||
<p> ToolBar Hints </p>
|
||||
</WelcomeScreen.Hints.ToolbarHint>
|
||||
<WelcomeScreen.Hints.MenuHint />
|
||||
<WelcomeScreen.Hints.HelpHint />
|
||||
</WelcomeScreen>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### MenuHint
|
||||
|
||||
`<WelcomeScreen.Hints.MenuHint>` hint subcomponent for the main menu. Supply `children` to customize the hint text.
|
||||
|
||||
#### ToolbarHint
|
||||
|
||||
`<WelcomeScreen.Hints.ToolbarHint>` hint subcomponent for the toolbar. Supply `children` to customize the hint text.
|
||||
|
||||
#### Help
|
||||
|
||||
`<WelcomeScreen.Hints.Help>` hint subcomponent for the help dialog. Supply `children` to customize the hint text.
|
46
dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx
Normal file
@ -0,0 +1,46 @@
|
||||
# Constants
|
||||
|
||||
### FONT_FAMILY
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { FONT_FAMILY } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
`FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below
|
||||
|
||||
| Font Family | Description |
|
||||
| ----------- | ---------------------- |
|
||||
| `Virgil` | The `handwritten` font |
|
||||
| `Helvetica` | The `Normal` Font |
|
||||
| `Cascadia` | The `Code` Font |
|
||||
|
||||
Defaults to `FONT_FAMILY.Virgil` unless passed in `initialData.appState.currentItemFontFamily`.
|
||||
|
||||
### THEME
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { THEME } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
`THEME` contains all the themes supported by `Excalidraw` as explained below
|
||||
|
||||
| Theme | Description |
|
||||
| ------- | ----------------- |
|
||||
| `LIGHT` | The `light` theme |
|
||||
| `DARK` | The `Dark` theme |
|
||||
|
||||
Defaults to `THEME.LIGHT` unless passed in `initialData.appState.theme`
|
||||
|
||||
### MIME_TYPES
|
||||
|
||||
[`MIME_TYPES`](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L101) contains all the mime types supported by `Excalidraw`.
|
||||
|
||||
**How to use **
|
||||
|
||||
```js
|
||||
import { MIME_TYPES } from "@excalidraw/excalidraw";
|
||||
```
|
@ -0,0 +1,55 @@
|
||||
# initialData
|
||||
|
||||
<pre>
|
||||
{ elements?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>, appState?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a> }
|
||||
</pre>
|
||||
|
||||
This helps to load Excalidraw with `initialData`. It must be an object or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to an object containing the below optional fields.
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `elements` | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | The `elements` with which `Excalidraw` should be mounted. |
|
||||
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) | The `AppState` with which `Excalidraw` should be mounted. |
|
||||
| `scrollToContent` | `boolean` | This attribute indicates whether to `scroll` to the nearest element to center once `Excalidraw` is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass `initialData.appState.scrollX` and `initialData.appState.scrollY` when `scrollToContent` is false so that scroll positions are retained |
|
||||
| `libraryItems` | [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L247) | Promise<[LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)> | This library items with which `Excalidraw` should be mounted. |
|
||||
| `files` | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L82) | The `files` added to the scene. |
|
||||
|
||||
You might want to use this when you want to load excalidraw with some initial elements and app state.
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw
|
||||
initialData={{
|
||||
elements: [
|
||||
{
|
||||
type: "rectangle",
|
||||
version: 141,
|
||||
versionNonce: 361174001,
|
||||
isDeleted: false,
|
||||
id: "oDVXy8D6rom3H1-LLH2-f",
|
||||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
angle: 0,
|
||||
x: 100.50390625,
|
||||
y: 93.67578125,
|
||||
strokeColor: "#000000",
|
||||
backgroundColor: "transparent",
|
||||
width: 186.47265625,
|
||||
height: 141.9765625,
|
||||
seed: 1968410350,
|
||||
groupIds: [],
|
||||
},
|
||||
],
|
||||
appState: { zenModeEnabled: true, viewBackgroundColor: "#a5d8ff" },
|
||||
scrollToContent: true
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
230
dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx
Normal file
@ -0,0 +1,230 @@
|
||||
# Props
|
||||
|
||||
All `props` are *optional*.
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata) | `object` | `null` | <code>Promise<object | null></code> | `null` | The initial data with which app loads. |
|
||||
| [`ref`](/docs/@excalidraw/excalidraw/api/props/ref) | `object` | _ | `Ref` to be passed to Excalidraw |
|
||||
| [`isCollaborating`](#iscollaborating) | `boolean` | _ | This indicates if the app is in `collaboration` mode |
|
||||
| [`onChange`](#onchange) | `function` | _ | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw `elements` and the current `app state`. |
|
||||
| [`onPointerUpdate`](#onpointerupdate) | `function` | _ | Callback triggered when mouse pointer is updated. |
|
||||
| [`onPointerDown`](#onpointerdown) | `function` | _ | This prop if passed gets triggered on pointer down evenets |
|
||||
| [`onScrollChange`](#onscrollchange) | `function` | _ | This prop if passed gets triggered when scrolling the canvas. |
|
||||
| [`onPaste`](#onpaste) | `function` | _ | Callback to be triggered if passed when the something is pasted in to the scene |
|
||||
| [`onLibraryChange`](#onlibrarychange) | `function` | _ | The callback if supplied is triggered when the library is updated and receives the library items. |
|
||||
| [`onLinkOpen`](#onlinkopen) | `function` | _ | The callback if supplied is triggered when any link is opened. |
|
||||
| [`langCode`](#langcode) | `string` | `en` | Language code string to be used in Excalidraw |
|
||||
| [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | _ | Render function that renders custom UI in top right corner |
|
||||
| [`renderCustomStats`](/docs/@excalidraw/excalidraw/api/props/render-props#rendercustomstats) | `function` | _ | Render function that can be used to render custom stats on the stats dialog. |
|
||||
| [`renderSidebar`](/docs/@excalidraw/excalidraw/api/props/render-props#rendersidebar) | `function` | _ | Render function that renders custom sidebar. |
|
||||
| [`viewModeEnabled`](#viewmodeenabled) | `boolean` | _ | This indicates if the app is in `view` mode. |
|
||||
| [`zenModeEnabled`](#zenmodeenabled) | `boolean` | _ | This indicates if the `zen` mode is enabled |
|
||||
| [`gridModeEnabled`](#gridmodeenabled) | `boolean` | _ | This indicates if the `grid` mode is enabled |
|
||||
| [`libraryReturnUrl`](#libraryreturnurl) | `string` | _ | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
|
||||
| [`theme`](#theme) | `"light"` | `"dark"` | `"light"` | The theme of the Excalidraw component |
|
||||
| [`name`](#name) | `string` | | Name of the drawing |
|
||||
| [`UIOptions`](/docs/@excalidraw/excalidraw/api/props/ui-options) | `object` | [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L151) | To customise UI options. Currently we support customising [`canvas actions`](#canvasactions) |
|
||||
| [`detectScroll`](#detectscroll) | `boolean` | `true` | Indicates whether to update the offsets when nearest ancestor is scrolled. |
|
||||
| [`handleKeyboardGlobally`](#handlekeyboardglobally) | `boolean` | `false` | Indicates whether to bind the keyboard events to document. |
|
||||
| [`autoFocus`](#autofocus) | `boolean` | `false` | indicates whether to focus the Excalidraw component on page load |
|
||||
| [`generateIdForFile`](#generateidforfile) | `function` | _ | Allows you to override `id` generation for files added on canvas |
|
||||
|
||||
### Storing custom data on Excalidraw elements
|
||||
|
||||
Beyond attributes that Excalidraw elements already support, you can store `custom` data on each `element` in a `customData` object. The type of the attribute is [`Record<string, any>`](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L66) and is optional.
|
||||
|
||||
You can use this to add any extra information you need to keep track of.
|
||||
|
||||
You can add `customData` to elements when passing them as [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata), or using [`updateScene`](/docs/@excalidraw/excalidraw/api/props/ref#updatescene) / [`updateLibrary`](/docs/@excalidraw/excalidraw/api/props/ref#updatelibrary) afterwards.
|
||||
|
||||
```js showLineNumbers
|
||||
{
|
||||
type: "rectangle",
|
||||
id: "oDVXy8D6rom3H1-LLH2-f",
|
||||
customData: {customId: '162'},
|
||||
}
|
||||
```
|
||||
|
||||
### isCollaborating
|
||||
|
||||
This prop indicates if the app is in `collaboration` mode.
|
||||
|
||||
### onChange
|
||||
|
||||
Every time component updates, this callback if passed will get triggered and has the below signature.
|
||||
|
||||
```js
|
||||
(excalidrawElements, appState, files) => void;
|
||||
```
|
||||
|
||||
1. `excalidrawElements`: Array of [excalidrawElements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) in the scene.
|
||||
|
||||
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) of the scene.
|
||||
|
||||
3. `files`: The [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L64) which are added to the scene.
|
||||
|
||||
Here you can try saving the data to your backend or local storage for example.
|
||||
|
||||
### onPointerUpdate
|
||||
|
||||
This callback is triggered when mouse pointer is updated.
|
||||
|
||||
```js
|
||||
({ x, y }, button, pointersMap}) => void;
|
||||
```
|
||||
|
||||
1.`{x, y}`: Pointer coordinates
|
||||
|
||||
2.`button`: The position of the button. This will be one of `["down", "up"]`
|
||||
|
||||
3.`pointersMap`: [`pointers`](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L131) map of the scene
|
||||
|
||||
```js
|
||||
(exportedElements, appState, canvas) => void
|
||||
```
|
||||
|
||||
1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L87) which needs to be exported.
|
||||
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) of the scene.
|
||||
3. `canvas`: The `HTMLCanvasElement` of the scene.
|
||||
|
||||
### onPointerDown
|
||||
|
||||
This prop if passed will be triggered on pointer down events and has the below signature.
|
||||
|
||||
<pre>
|
||||
(activeTool: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L115"> AppState["activeTool"]</a>, pointerDownState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L424">PointerDownState</a>) => void
|
||||
</pre>
|
||||
|
||||
### onScrollChange
|
||||
|
||||
This prop if passed will be triggered when canvas is scrolled and has the below signature.
|
||||
|
||||
```ts
|
||||
(scrollX: number, scrollY: number) => void
|
||||
```
|
||||
|
||||
### onPaste
|
||||
|
||||
This callback is triggered if passed when something is pasted into the scene. You can use this callback in case you want to do something additional when the paste event occurs.
|
||||
|
||||
<pre>
|
||||
(data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts#L18">ClipboardData</a>, event: ClipboardEvent | null) => boolean
|
||||
</pre>
|
||||
|
||||
This callback must return a `boolean` value or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to a boolean value.
|
||||
|
||||
In case you want to prevent the excalidraw paste action you must return `false`, it will stop the native excalidraw clipboard management flow (nothing will be pasted into the scene).
|
||||
|
||||
### onLibraryChange
|
||||
|
||||
This callback if supplied will get triggered when the library is updated and has the below signature.
|
||||
|
||||
<pre>
|
||||
(items:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200">
|
||||
LibraryItems
|
||||
</a>
|
||||
) => void | Promise<any>
|
||||
</pre>
|
||||
|
||||
It is invoked with empty items when user clears the library. You can use this callback when you want to do something additional when library is updated for example persisting it to local storage.
|
||||
|
||||
### onLinkOpen
|
||||
|
||||
This prop if passed will be triggered when clicked on `link`. To handle the redirect yourself (such as when using your own router for internal links), you must call `event.preventDefault()`.
|
||||
|
||||
<pre>
|
||||
(element: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement</a>,
|
||||
event: CustomEvent<{ nativeEvent: MouseEvent }>) => void
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
```js showLineNumbers
|
||||
const history = useHistory();
|
||||
|
||||
// open internal links using the app's router, but opens external links in
|
||||
// a new tab/window
|
||||
const onLinkOpen: ExcalidrawProps["onLinkOpen"] = useCallback(
|
||||
(element, event) => {
|
||||
const link = element.link;
|
||||
const { nativeEvent } = event.detail;
|
||||
const isNewTab = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
||||
const isNewWindow = nativeEvent.shiftKey;
|
||||
const isInternalLink =
|
||||
link.startsWith("/") || link.includes(window.location.origin);
|
||||
if (isInternalLink && !isNewTab && !isNewWindow) {
|
||||
history.push(link.replace(window.location.origin, ""));
|
||||
// signal that we're handling the redirect ourselves
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
[history],
|
||||
);
|
||||
```
|
||||
|
||||
### langCode
|
||||
|
||||
Determines the `language` of the UI. It should be one of the [available language codes](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L14). Defaults to `en` (English). We also export default language and supported languages which you can import as shown below.
|
||||
|
||||
```js
|
||||
import { defaultLang, languages } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
| name | type |
|
||||
| --- | --- |
|
||||
| `defaultLang` | `string` |
|
||||
| `languages` | [`Language[]`](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L15) |
|
||||
|
||||
### viewModeEnabled
|
||||
|
||||
This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over *intialData.appState.viewModeEnabled*, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
|
||||
### zenModeEnabled
|
||||
|
||||
This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over *intialData.appState.zenModeEnabled*, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
|
||||
### gridModeEnabled
|
||||
|
||||
This prop indicates whether the shows the grid. When supplied, the value takes precedence over *intialData.appState.gridModeEnabled*, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
|
||||
### libraryReturnUrl
|
||||
|
||||
If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com).
|
||||
Defaults to *window.location.origin + window.location.pathname*. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
|
||||
|
||||
### theme
|
||||
|
||||
This prop controls Excalidraw's theme. When supplied, the value takes precedence over *intialData.appState.theme*, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app unless *UIOptions.canvasActions.toggleTheme* is set to `true`, in which case the `theme` prop will control Excalidraw's default theme with ability to allow theme switching (you must take care of updating the `theme` prop when you detect a change to `appState.theme` from the [onChange](#onchange) callback).
|
||||
|
||||
You can use [`THEME`](/docs/@excalidraw/excalidraw/api/utils#theme) to specify the theme.
|
||||
|
||||
### name
|
||||
|
||||
This prop sets the `name` of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over *intialData.appState.name*, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
|
||||
|
||||
|
||||
### detectScroll
|
||||
|
||||
Indicates whether Excalidraw should listen for `scroll` event on the nearest scrollable container in the DOM tree and recompute the coordinates (e.g. to correctly handle the cursor) when the component's position changes. You can disable this when you either know this doesn't affect your app or you want to take care of it yourself (calling the [`refresh()`](#ref) method).
|
||||
|
||||
### handleKeyboardGlobally
|
||||
|
||||
Indicates whether to bind keyboard events to `document`. Disabled by default, meaning the keyboard events are bound to the Excalidraw component. This allows for multiple Excalidraw components to live on the same page, and ensures that Excalidraw keyboard handling doesn't collide with your app's (or the browser) when the component isn't focused.
|
||||
|
||||
Enable this if you want Excalidraw to handle keyboard even if the component isn't focused (e.g. a user is interacting with the navbar, sidebar, or similar).
|
||||
|
||||
|
||||
### autoFocus
|
||||
|
||||
This prop indicates whether to `focus` the Excalidraw component on page load. Defaults to false.
|
||||
|
||||
### generateIdForFile
|
||||
|
||||
Allows you to override `id` generation for files added on canvas (images). By default, an SHA-1 digest of the file is used.
|
||||
|
||||
```tsx
|
||||
(file: File) => string | Promise<string>
|
||||
```
|
||||
|
391
dev-docs/docs/@excalidraw/excalidraw/api/props/ref.mdx
Normal file
@ -0,0 +1,391 @@
|
||||
# ref
|
||||
<pre>
|
||||
<a href="https://reactjs.org/docs/refs-and-the-dom.html#creating-refs">createRef</a> | <a href="https://reactjs.org/docs/hooks-reference.html#useref">useRef</a> | <a href="https://reactjs.org/docs/refs-and-the-dom.html#callback-refs">callbackRef</a> | <br/>{ current: { readyPromise: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L460">resolvablePromise</a> } }
|
||||
</pre>
|
||||
|
||||
You can pass a `ref` when you want to access some excalidraw APIs. We expose the below APIs:
|
||||
|
||||
| API | Signature | Usage |
|
||||
| --- | --- | --- |
|
||||
| ready | `boolean` | This is set to true once Excalidraw is rendered |
|
||||
| [readyPromise](#readypromise) | `function` | This promise will be resolved with the api once excalidraw has rendered. This will be helpful when you want do some action on the host app once this promise resolves. For this to work you will have to pass ref as shown [here](#readypromise) |
|
||||
| [updateScene](#updatescene) | `function` | updates the scene with the sceneData |
|
||||
| [updateLibrary](#updatelibrary) | `function` | updates the scene with the sceneData |
|
||||
| [addFiles](#addfiles) | `function` | add files data to the appState |
|
||||
| [resetScene](#resetscene) | `function` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. |
|
||||
| [getSceneElementsIncludingDeleted](#getsceneelementsincludingdeleted) | `function` | Returns all the elements including the deleted in the scene |
|
||||
| [getSceneElements](#getsceneelements) | `function` | Returns all the elements excluding the deleted in the scene |
|
||||
| [getAppState](#getappstate) | `function` | Returns current appState |
|
||||
| [history](#history) | `object` | This is the history API. `history.clear()` will clear the history |
|
||||
| [scrollToContent](#scrolltocontent) | `function` | Scroll the nearest element out of the elements supplied to the center. Defaults to the elements on the scene. |
|
||||
| [refresh](#refresh) | `function` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). |
|
||||
| [setToast](#settoast) | `function` | This API can be used to show the toast with custom message. |
|
||||
| [id](#id) | `string` | Unique ID for the excalidraw component. |
|
||||
| [getFiles](#getfiles) | `function` | This API can be used to get the files present in the scene. |
|
||||
| [setActiveTool](#setactivetool) | `function` | This API can be used to set the active tool |
|
||||
| [setCursor](#setcursor) | `function` | This API can be used to set customise the mouse cursor on the canvas |
|
||||
| [resetCursor](#resetcursor) | `function` | This API can be used to reset to default mouse cursor on the canvas |
|
||||
| [toggleMenu](#togglemenu) | `function` | Toggles specific menus on/off |
|
||||
|
||||
## readyPromise
|
||||
|
||||
<pre>
|
||||
const excalidrawRef = { current:{ readyPromise:
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L460">
|
||||
resolvablePromise
|
||||
</a>
|
||||
} }
|
||||
</pre>
|
||||
|
||||
Since plain object is passed as a `ref`, the `readyPromise` is resolved as soon as the component is mounted. Most of the time you will not need this unless you have a specific use case where you can't pass the `ref` in the react way and want to do some action on the host when this promise resolves.
|
||||
|
||||
```jsx showLineNumbers
|
||||
const resolvablePromise = () => {
|
||||
let resolve;
|
||||
let reject;
|
||||
const promise = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
promise.resolve = resolve;
|
||||
promise.reject = reject;
|
||||
return promise;
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
const excalidrawRef = useMemo(
|
||||
() => ({
|
||||
current: {
|
||||
readyPromise: resolvablePromise(),
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
excalidrawRef.current.readyPromise.then((api) => {
|
||||
console.log("loaded", api);
|
||||
});
|
||||
}, [excalidrawRef]);
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw ref={excalidrawRef} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## updateScene
|
||||
|
||||
<pre>
|
||||
(scene:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L339">
|
||||
sceneData
|
||||
</a>
|
||||
) => void
|
||||
</pre>
|
||||
|
||||
You can use this function to update the scene with the sceneData. It accepts the below attributes.
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `elements` | [`ImportedDataState["elements"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L38) | The `elements` to be updated in the scene |
|
||||
| `appState` | [`ImportedDataState["appState"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L39) | The `appState` to be updated in the scene. |
|
||||
| `collaborators` | <code>Map<string, <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37">Collaborator></a></code> | The list of collaborators to be updated in the scene. |
|
||||
| `commitToHistory` | `boolean` | Implies if the `history (undo/redo)` should be recorded. Defaults to `false`. |
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
const updateScene = () => {
|
||||
const sceneData = {
|
||||
elements: [
|
||||
{
|
||||
type: "rectangle",
|
||||
version: 141,
|
||||
versionNonce: 361174001,
|
||||
isDeleted: false,
|
||||
id: "oDVXy8D6rom3H1-LLH2-f",
|
||||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
angle: 0,
|
||||
x: 100.50390625,
|
||||
y: 93.67578125,
|
||||
strokeColor: "#c92a2a",
|
||||
backgroundColor: "transparent",
|
||||
width: 186.47265625,
|
||||
height: 141.9765625,
|
||||
seed: 1968410350,
|
||||
groupIds: [],
|
||||
boundElements: null,
|
||||
locked: false,
|
||||
link: null,
|
||||
updated: 1,
|
||||
roundness: {
|
||||
type: 3,
|
||||
value: 32,
|
||||
},
|
||||
},
|
||||
],
|
||||
appState: {
|
||||
viewBackgroundColor: "#edf2ff",
|
||||
},
|
||||
};
|
||||
excalidrawAPI.updateScene(sceneData);
|
||||
};
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<p style={{ fontSize: "16px" }}> Click to update the scene</p>
|
||||
<button className="custom-button" onClick={updateScene}>Update Scene</button>
|
||||
<Excalidraw ref={(api) => setExcalidrawAPI(api)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### updateLibrary
|
||||
|
||||
<pre>
|
||||
(opts: { <br /> libraryItems:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L249">
|
||||
LibraryItemsSource
|
||||
</a>
|
||||
;<br /> merge?: boolean; <br /> prompt?: boolean;
|
||||
<br /> openLibraryMenu?: boolean;
|
||||
<br /> defaultStatus?: "unpublished" | "published"; <br /> }) => Promise<
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L246">
|
||||
LibraryItems
|
||||
</a>
|
||||
>
|
||||
</pre>
|
||||
|
||||
You can use this function to update the library. It accepts the below attributes.
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `libraryItems` | [LibraryItemsSource](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L249) | \_ | The `libraryItems` to be replaced/merged with current library |
|
||||
| `merge` | boolean | `false` | Whether to merge with existing library items. |
|
||||
| `prompt` | boolean | `false` | Whether to prompt user for confirmation. |
|
||||
| `openLibraryMenu` | boolean | `false` | Keep the library menu open after library is updated. |
|
||||
| `defaultStatus` | <code>"unpublished" | "published"</code> | `"unpublished"` | Default library item's `status` if not present. |
|
||||
|
||||
```tsx live
|
||||
function App() {
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!excalidrawAPI) {
|
||||
return;
|
||||
}
|
||||
// to open the library sidebar
|
||||
excalidrawAPI.updateScene({ appState: { openSidebar: "library" } });
|
||||
}, [excalidrawAPI]);
|
||||
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<p style={{ fontSize: "16px" }}> Click to update the library items</p>
|
||||
<button className="custom-button"
|
||||
onClick={() => {
|
||||
const libraryItems = [
|
||||
{
|
||||
status: "published",
|
||||
id: "1",
|
||||
created: 1,
|
||||
elements: initialData.libraryItems[1],
|
||||
},
|
||||
{
|
||||
status: "unpublished",
|
||||
id: "2",
|
||||
created: 2,
|
||||
elements: initialData.libraryItems[1],
|
||||
},
|
||||
];
|
||||
excalidrawAPI.updateLibrary({
|
||||
libraryItems,
|
||||
openLibraryMenu: true
|
||||
|
||||
});
|
||||
|
||||
}}
|
||||
>
|
||||
Update Library
|
||||
</button>
|
||||
<Excalidraw
|
||||
ref={(api) => setExcalidrawAPI(api)}
|
||||
// initial data retrieved from https://github.com/excalidraw/excalidraw/blob/master/dev-docs/src/initialData.js
|
||||
initialData={{
|
||||
libraryItems: initialData.libraryItems,
|
||||
appState: { openSidebar: "library" },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### addFiles
|
||||
|
||||
<pre>
|
||||
(files:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59">
|
||||
BinaryFileData
|
||||
</a>
|
||||
) => void
|
||||
</pre>
|
||||
|
||||
Adds supplied files data to the `appState.files` cache on top of existing files present in the cache.
|
||||
|
||||
## resetScene
|
||||
|
||||
```tsx
|
||||
(opts?: { resetLoadingState: boolean }) => void
|
||||
```
|
||||
|
||||
Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false.
|
||||
|
||||
## getSceneElementsIncludingDeleted
|
||||
|
||||
<pre>
|
||||
() =>{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||
ExcalidrawElement[]
|
||||
</a>
|
||||
</pre>
|
||||
|
||||
Returns all the elements including the deleted in the scene.
|
||||
|
||||
## getSceneElements
|
||||
|
||||
<pre>
|
||||
() => NonDeleted<
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||
ExcalidrawElement
|
||||
</a>
|
||||
[]>
|
||||
</pre>
|
||||
|
||||
Returns all the elements excluding the deleted in the scene
|
||||
|
||||
## getAppState
|
||||
|
||||
<pre>
|
||||
() =>{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||
AppState
|
||||
</a>
|
||||
</pre>
|
||||
|
||||
Returns current appState.
|
||||
|
||||
## history
|
||||
|
||||
```tsx
|
||||
{
|
||||
clear: () => void
|
||||
}
|
||||
```
|
||||
|
||||
This is the history API. history.clear() will clear the history.
|
||||
|
||||
## scrollToContent
|
||||
|
||||
<pre>
|
||||
(target?:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||
ExcalidrawElement
|
||||
</a>{" "}
|
||||
|{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||
ExcalidrawElement
|
||||
</a>
|
||||
[]) => void
|
||||
</pre>
|
||||
|
||||
Scroll the nearest element out of the elements supplied to the center. Defaults to the elements on the scene.
|
||||
|
||||
## refresh
|
||||
|
||||
```tsx
|
||||
() => void
|
||||
```
|
||||
|
||||
Updates the `offsets` for the `Excalidraw` component so that the coordinates are computed correctly (for example the cursor position).
|
||||
|
||||
You don't have to call this when the position is changed on page scroll or when the excalidraw container resizes (we handle that ourselves).
|
||||
|
||||
For any other cases if the position of excalidraw is updated (example due to scroll on parent container and not page scroll) you should call this API.
|
||||
|
||||
## setToast
|
||||
|
||||
This API can be used to show the toast with custom message.
|
||||
|
||||
```tsx
|
||||
({ message: string, closable?:boolean,duration?:number
|
||||
} | null) => void
|
||||
```
|
||||
|
||||
| Attribute | type | Description |
|
||||
| --- | --- | --- |
|
||||
| message | string | The message to be shown on the toast. |
|
||||
| closable | boolean | Indicates whether to show the closable button on toast to dismiss the toast. |
|
||||
| duration | number | Determines the duration after which the toast should auto dismiss. To prevent autodimiss you can pass `Infinity`. |
|
||||
|
||||
To dismiss an existing toast you can simple pass `null`
|
||||
|
||||
```js
|
||||
setToast(null);
|
||||
```
|
||||
|
||||
## id
|
||||
|
||||
The unique id of the excalidraw component. This can be used to identify the excalidraw component, for example importing the library items to the excalidraw component from where it was initiated when you have multiple excalidraw components rendered on the same page as shown in [multiple excalidraw demo](https://codesandbox.io/s/multiple-excalidraw-k1xx5).
|
||||
|
||||
## getFiles
|
||||
|
||||
<pre>
|
||||
() =>{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L82">
|
||||
files
|
||||
</a>
|
||||
</pre>
|
||||
|
||||
This API can be used to get the files present in the scene. It may contain files that aren't referenced by any element, so if you're persisting the files to a storage, you should compare them against stored elements.
|
||||
|
||||
## setActiveTool
|
||||
|
||||
This API has the below signature. It sets the `tool` passed in param as the active tool.
|
||||
|
||||
|
||||
<pre>
|
||||
(tool: <br/> { type: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/shapes.tsx#L15">SHAPES</a>[number]["value"]| "eraser" } |<br/> { type: "custom"; customType: string }) => void
|
||||
</pre>
|
||||
|
||||
## setCursor
|
||||
|
||||
This API can be used to customise the mouse cursor on the canvas and has the below signature.
|
||||
It sets the mouse cursor to the cursor passed in param.
|
||||
|
||||
```tsx
|
||||
(cursor: string) => void
|
||||
```
|
||||
|
||||
## toggleMenu
|
||||
|
||||
```tsx
|
||||
(type: "library" | "customSidebar", force?: boolean) => boolean;
|
||||
```
|
||||
|
||||
This API can be used to toggle a specific menu (currently only the sidebars), and returns whether the menu was toggled on or off. If the `force` flag passed, it will force the menu to be toggled either on/off based on the `boolean` passed.
|
||||
|
||||
This API is especially useful when you render a custom sidebar using [`renderSidebar`](#rendersidebar) prop, and you want to toggle it from your app based on a user action.
|
||||
|
||||
## resetCursor
|
||||
|
||||
```tsx
|
||||
() => void
|
||||
```
|
||||
|
||||
This API can be used to reset to default mouse cursor.
|
126
dev-docs/docs/@excalidraw/excalidraw/api/props/render-props.mdx
Normal file
@ -0,0 +1,126 @@
|
||||
# Render Props
|
||||
|
||||
## renderTopRightUI
|
||||
|
||||
<pre>
|
||||
(isMobile: boolean, appState:{" "}
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||
AppState
|
||||
</a>
|
||||
) => JSX | null
|
||||
</pre>
|
||||
|
||||
A function returning `JSX` to render `custom` UI in the top right corner of the app.
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw
|
||||
renderTopRightUI={() => {
|
||||
return (
|
||||
<button
|
||||
style={{
|
||||
background: "#70b1ec",
|
||||
border: "none",
|
||||
color: "#fff",
|
||||
width: "max-content",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
onClick={() => window.alert("This is dummy top right UI")}
|
||||
>
|
||||
{" "}
|
||||
Click me{" "}
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## renderCustomStats
|
||||
|
||||
A function that can be used to render custom stats (returns JSX) in the `nerd stats` dialog.
|
||||
|
||||

|
||||
|
||||
For example you can use this prop to render the size of the elements in the storage as do in [excalidraw.com](https://excalidraw.com).
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw
|
||||
renderCustomStats={() => (
|
||||
<p style={{ color: "#70b1ec", fontWeight: "bold" }}>
|
||||
{" "}
|
||||
Dummy stats will be shown here{" "}
|
||||
</p>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## renderSidebar
|
||||
|
||||
```tsx
|
||||
() => JSX | null;
|
||||
```
|
||||
|
||||
You can render `custom sidebar` using this prop. This sidebar is the same that the library menu sidebar is using, and can be used for any purposes your app needs.
|
||||
|
||||
You need to import the `Sidebar` component from `excalidraw` package and pass your content as its `children`. The function `renderSidebar` should return the `Sidebar` instance.
|
||||
|
||||
### Sidebar
|
||||
The `<Sidebar>` component takes these props (all are optional except `children`):
|
||||
|
||||
| Prop | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `children` | `React.ReactNode` | Content you want to render inside the `sidebar`. |
|
||||
| `onClose` | `function` | Invoked when the component is closed (by user, or the editor). No need to act on this event, as the editor manages the sidebar open state on its own. |
|
||||
| `onDock` | `function` | Invoked when the user toggles the `dock` button. The callback recieves a `boolean` parameter `isDocked` which indicates whether the sidebar is `docked` |
|
||||
| `docked` | `boolean` | Indicates whether the sidebar is`docked`. By default, the sidebar is `undocked`. If passed, the docking becomes controlled, and you are responsible for updating the `docked` state by listening on `onDock` callback. To decide the breakpoint for docking you can use [UIOptions.dockedSidebarBreakpoint](/docs/@excalidraw/excalidraw/api/props/ui-options#dockedsidebarbreakpoint) for more info on docking. |
|
||||
| `dockable` | `boolean` | Indicates whether to show the `dock` button so that user can `dock` the sidebar. If `false`, you can still dock programmatically by passing `docked` as `true`. |
|
||||
|
||||
The sidebar will always include a header with `close / dock` buttons (when applicable).
|
||||
You can also add custom content to the header, by rendering `<Sidebar.Header>` as a child of the `<Sidebar>` component. Note that the custom header will still include the default buttons.
|
||||
|
||||
|
||||
### Sidebar.Header
|
||||
|
||||
| name | type | description |
|
||||
| --- | --- | --- |
|
||||
| children | `React.ReactNode` | Content you want to render inside the sidebar header as a sibling of `close` / `dock` buttons. |
|
||||
|
||||
To control the visibility of the sidebar you can use [`toggleMenu("customSidebar")`](/docs/@excalidraw/excalidraw/api/props/ref#togglemenu) api available via `ref`.
|
||||
|
||||
```tsx live
|
||||
function App() {
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<button className="custom-button" onClick={() => excalidrawAPI.toggleMenu("customSidebar")}>
|
||||
{" "}
|
||||
Toggle Custom Sidebar{" "}
|
||||
</button>
|
||||
<Excalidraw
|
||||
UIOptions={{ dockedSidebarBreakpoint: 100 }}
|
||||
ref={(api) => setExcalidrawAPI(api)}
|
||||
renderSidebar={() => {
|
||||
return (
|
||||
<Sidebar dockable={true}>
|
||||
<Sidebar.Header>Custom Sidebar Header </Sidebar.Header>
|
||||
<p style={{ padding: "1rem" }}> custom Sidebar Content </p>
|
||||
</Sidebar>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
@ -0,0 +1,72 @@
|
||||
# UIOptions
|
||||
|
||||
This prop can be used to customise UI of Excalidraw. Currently we support customising [`canvasActions`](#canvasactions), [`dockedSidebarBreakpoint`](#dockedsidebarbreakpoint) and [`welcomeScreen`](#welcmescreen).
|
||||
|
||||
<pre>
|
||||
{
|
||||
<br /> canvasActions?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L372">
|
||||
CanvasActions
|
||||
</a>, <br /> dockedSidebarBreakpoint?: number, <br /> welcomeScreen?: boolean <br />
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
## canvasActions
|
||||
|
||||
This `prop` controls the visibility of the canvas actions inside the `menu`.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `changeViewBackgroundColor` | `boolean` | `true` | Indicates whether to show `Background color picker`. |
|
||||
| `clearCanvas` | `boolean` | `true` | Indicates whether to show `Clear canvas` button. |
|
||||
| `export` | `false` | [`exportOpts`](#exportopts) | `object` | This prop allows to customize the UI inside the export dialog. By default it shows the `save file to disk`. For more details visit [`exportOpts`](#exportopts). |
|
||||
| `loadScene` | `boolean` | `true` | Indicates whether to show `Load` button. |
|
||||
| `saveToActiveFile` | `boolean` | `true` | Indicates whether to show `Save` button to save to current file. |
|
||||
| `toggleTheme` | `boolean` | `null` | `null` | Indicates whether to show `Theme toggle`. When defined as `boolean`, takes precedence over [`props.theme`](/docs/@excalidraw/excalidraw/api/props#theme) to show `Theme toggle`. |
|
||||
| `saveAsImage` | `boolean` | `true` | Indicates whether to show `Save as image` button. |
|
||||
|
||||
```tsx live
|
||||
function App() {
|
||||
const UIOptions = {
|
||||
canvasActions: {
|
||||
changeViewBackgroundColor: false,
|
||||
clearCanvas: false,
|
||||
loadScene: false,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw UIOptions={UIOptions} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### exportOpts
|
||||
|
||||
The below attributes can be set in `UIOptions.canvasActions.export` to customize the export dialog.
|
||||
If `UIOptions.canvasActions.export` is `false` the export button will not be rendered.
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `saveFileToDisk` | `boolean` | `true` | Indicates whether `save file to disk` button should be shown |
|
||||
| `onExportToBackend` | `object` | \_ | This callback is triggered when the shareable-link button is clicked in the export dialog. The link button will only be shown if this callback is passed. |
|
||||
| `renderCustomUI` | `object` | \_ | This callback should be supplied if you want to render custom UI in the export dialog. |
|
||||
|
||||
## dockedSidebarBreakpoint
|
||||
|
||||
This prop indicates at what point should we break to a docked, permanent sidebar. If not passed it defaults to [`MQ_RIGHT_SIDEBAR_MAX_WIDTH_PORTRAIT`](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L161).
|
||||
If the _width_ of the _excalidraw_ container exceeds _dockedSidebarBreakpoint_, the sidebar will be `dockable` and the button to `dock` the sidebar will be shown
|
||||
If user choses to `dock` the sidebar, it will push the right part of the UI towards the left, making space for the sidebar as shown below.
|
||||
|
||||

|
||||
|
||||
```tsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw UIOptions={{dockedSidebarBreakpoint: 200}}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
196
dev-docs/docs/@excalidraw/excalidraw/api/utils/export.mdx
Normal file
@ -0,0 +1,196 @@
|
||||
---
|
||||
title: Export Utilities
|
||||
id: "export"
|
||||
---
|
||||
|
||||
:::info
|
||||
|
||||
We're working on much improved export utilities. Stay tuned!
|
||||
|
||||
:::
|
||||
|
||||
### exportToCanvas
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
exportToCanvas({<br/>
|
||||
elements,<br/>
|
||||
appState<br/>
|
||||
getDimensions,<br/>
|
||||
files,<br/>
|
||||
exportPadding?: number;<br/>
|
||||
}: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">ExportOpts</a>
|
||||
</pre>
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `elements` | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to be exported to canvas. |
|
||||
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L23) | [Default App State](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L17) | The app state of the scene. |
|
||||
| [`getDimensions`](#getdimensions) | `function` | _ | A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported. |
|
||||
| `maxWidthOrHeight` | `number` | _ | The maximum `width` or `height` of the exported image. If provided, `getDimensions` is ignored. |
|
||||
| `files` | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59) | _ | The files added to the scene. |
|
||||
| `exportPadding` | `number` | `10` | The `padding` to be added on canvas. |
|
||||
|
||||
|
||||
#### getDimensions
|
||||
|
||||
```tsx
|
||||
(width: number, height: number) => {
|
||||
width: number,
|
||||
height: number,
|
||||
scale?: number
|
||||
}
|
||||
```
|
||||
A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { exportToCanvas } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
This function returns the canvas with the exported elements, appState and dimensions.
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
const [canvasUrl, setCanvasUrl] = useState("");
|
||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="custom-button"
|
||||
onClick={async () => {
|
||||
if (!excalidrawAPI) {
|
||||
return
|
||||
}
|
||||
const elements = excalidrawAPI.getSceneElements();
|
||||
if (!elements || !elements.length) {
|
||||
return
|
||||
}
|
||||
const canvas = await exportToCanvas({
|
||||
elements,
|
||||
appState: {
|
||||
...initialData.appState,
|
||||
exportWithDarkMode: false,
|
||||
},
|
||||
files: excalidrawAPI.getFiles(),
|
||||
getDimensions: () => { return {width: 350, height: 350}}
|
||||
});
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.font = "30px Virgil";
|
||||
ctx.strokeText("My custom text", 50, 60);
|
||||
setCanvasUrl(canvas.toDataURL());
|
||||
}}
|
||||
>
|
||||
Export to Canvas
|
||||
</button>
|
||||
<div className="export export-canvas">
|
||||
<img src={canvasUrl} alt="" />
|
||||
</div>
|
||||
<div style={{ height: "400px" }}>
|
||||
<Excalidraw ref={(api) => setExcalidrawAPI(api)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### exportToBlob
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
exportToBlob(<br/>
|
||||
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {<br/>
|
||||
mimeType?: string,<br/>
|
||||
quality?: number,<br/>
|
||||
exportPadding?: number;<br/>
|
||||
})
|
||||
</pre>
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `opts` | `object` | _ | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exporttocanvas) |
|
||||
| `mimeType` | `string` | `image/png` | Indicates the image format. |
|
||||
| `quality` | `number` | `0.92` | A value between `0` and `1` indicating the [image quality](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#parameters). Applies only to `image/jpeg`/`image/webp` MIME types. |
|
||||
| `exportPadding` | `number` | `10` | The padding to be added on canvas. |
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { exportToBlob } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
Returns a promise which resolves with a [blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob). It internally uses [canvas.ToBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob).
|
||||
|
||||
### exportToSvg
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
exportToSvg({<br/>
|
||||
elements:
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||
ExcalidrawElement[]
|
||||
</a>,<br/>
|
||||
appState:
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95"> AppState
|
||||
</a>,<br/>
|
||||
exportPadding: number,<br/>
|
||||
metadata: string,<br/>
|
||||
files:
|
||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59">
|
||||
BinaryFiles
|
||||
</a>,<br/>
|
||||
});
|
||||
</pre>
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| elements | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to exported as `svg `|
|
||||
| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) | [defaultAppState](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L11) | The `appState` of the scene |
|
||||
| exportPadding | number | 10 | The `padding` to be added on canvas |
|
||||
| files | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L64) | undefined | The `files` added to the scene. |
|
||||
|
||||
This function returns a promise which resolves to `svg` of the exported drawing.
|
||||
|
||||
### exportToClipboard
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
exportToClipboard(<br/>
|
||||
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">ExportOpts</a> & {<br/>
|
||||
mimeType?: string,<br/>
|
||||
quality?: number;<br/>
|
||||
type: 'png' | 'svg' |'json'<br/>
|
||||
})
|
||||
</pre>
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `opts` | | | This param is same as the params passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exporttocanvas). |
|
||||
| `mimeType` | `string` | `image/png` | Indicates the image format, this will be used when exporting as `png`. |
|
||||
| `quality` | `number` | `0.92` | A value between `0` and `1` indicating the [image quality](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#parameters). Applies only to `image/jpeg` / `image/webp` MIME types. This will be used when exporting as `png`. |
|
||||
| `type` | 'png' | 'svg' | 'json' | _ | This determines the format to which the scene data should be `exported`. |
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { exportToClipboard } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
Copies the scene data in the specified format (determined by `type`) to clipboard.
|
||||
|
||||
### Additional attributes of appState for export\* APIs
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `exportBackground` | `boolean` | `true` | Indicates whether `background` should be exported |
|
||||
| `viewBackgroundColor` | `string` | `#fff` | The default background color |
|
||||
| `exportWithDarkMode` | `boolean` | `false` | Indicates whether to export with `dark` mode |
|
||||
| `exportEmbedScene` | `boolean` | `false` | Indicates whether scene data should be embedded in `svg/png`. This will increase the image size. |
|
90
dev-docs/docs/@excalidraw/excalidraw/api/utils/restore.mdx
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Restore Utilities
|
||||
id: "restore"
|
||||
---
|
||||
|
||||
### restoreAppState
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
restoreAppState(appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState["appState"]</a>,<br/> localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>> | null): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>
|
||||
</pre>
|
||||
|
||||
**_How to use_**
|
||||
|
||||
```js
|
||||
import { restoreAppState } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
This function will make sure all the `keys` have appropriate `values` in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) and if any key is missing, it will be set to its `default` value.
|
||||
|
||||
When `localAppState` is supplied, it's used in place of values that are missing (`undefined`) in `appState` instead of the defaults.
|
||||
Use this as a way to not override user's defaults if you persist them.
|
||||
You can pass `null` / `undefined` if not applicable.
|
||||
|
||||
### restoreElements
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
restoreElements(
|
||||
elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ImportedDataState["elements"]</a>,<br/>
|
||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>,<br/>
|
||||
refreshDimensions?: boolean<br/>
|
||||
)
|
||||
</pre>
|
||||
|
||||
**_How to use_**
|
||||
|
||||
```js
|
||||
import { restoreElements } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
This function will make sure all properties of element is correctly set and if any attribute is missing, it will be set to its default value.
|
||||
|
||||
When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`.
|
||||
Use this when you import elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the updates.
|
||||
|
||||
Parameter `refreshDimensions` indicates whether we should also `recalculate` text element dimensions. Defaults to `false`. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration.
|
||||
|
||||
### restore
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
restoreElements(
|
||||
data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState</a>,<br/>
|
||||
localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>> | null | undefined,<br/>
|
||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined<br/>): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L4">DataState</a>
|
||||
)
|
||||
</pre>
|
||||
|
||||
See [`restoreAppState()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreAppState) about `localAppState`, and [`restoreElements()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreElements) about `localElements`.
|
||||
|
||||
**_How to use_**
|
||||
|
||||
```js
|
||||
import { restore } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
This function makes sure elements and state is set to appropriate values and set to default value if not present. It is a combination of [restoreElements](#restoreelements) and [restoreAppState](#restoreappstate).
|
||||
|
||||
### restoreLibraryItems
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
restoreLibraryItems(libraryItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState["libraryItems"]</a>,<br/>
|
||||
defaultStatus: "published" | "unpublished")
|
||||
</pre>
|
||||
|
||||
**_How to use_**
|
||||
|
||||
```js
|
||||
import { restoreLibraryItems } from "@excalidraw/excalidraw";
|
||||
|
||||
restoreLibraryItems(libraryItems, "unpublished");
|
||||
```
|
||||
|
||||
This function normalizes library items elements, adding missing values when needed.
|
341
dev-docs/docs/@excalidraw/excalidraw/api/utils/utils-intro.md
Normal file
@ -0,0 +1,341 @@
|
||||
---
|
||||
slug: /@excalidraw/excalidraw/api/utils
|
||||
---
|
||||
|
||||
# Utils
|
||||
|
||||
These are pure Javascript functions exported from the @excalidraw/excalidraw [`@excalidraw/excalidraw`](https://npmjs.com/@excalidraw/excalidraw). If you want to export your drawings in different formats eg `png`, `svg` and more you can check out [Export Utilities](/docs/@excalidraw/excalidraw/API/utils/export). If you want to restore your drawings you can check out [Restore Utilities](/docs/@excalidraw/excalidraw/API/utils/restore).
|
||||
|
||||
### serializeAsJSON
|
||||
|
||||
Takes the scene elements and state and returns a JSON string. `Deleted` elements as well as most properties from `AppState` are removed from the resulting JSON. (see [`serializeAsJSON()`](https://github.com/excalidraw/excalidraw/blob/master/src/data/json.ts#L42) source for details).
|
||||
|
||||
If you want to overwrite the `source` field in the `JSON` string, you can set `window.EXCALIDRAW_EXPORT_SOURCE` to the desired value.
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
serializeAsJSON({<br/>
|
||||
elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>,<br/>
|
||||
appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>,<br/>
|
||||
}): string
|
||||
</pre>
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { serializeAsJSON } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
### serializeLibraryAsJSON
|
||||
|
||||
Takes the `library` items and returns a `JSON` string.
|
||||
|
||||
If you want to overwrite the source field in the JSON string, you can set `window.EXCALIDRAW_EXPORT_SOURCE` to the desired value.
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
serializeLibraryAsJSON(
|
||||
libraryItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200">LibraryItems[]</a>)
|
||||
</pre>
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { serializeLibraryAsJSON } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
#### isInvisiblySmallElement
|
||||
|
||||
Returns `true` if element is invisibly small (e.g. width & height are zero).
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
isInvisiblySmallElement(element: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement</a>): boolean
|
||||
</pre>
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { isInvisiblySmallElement } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
### loadFromBlob
|
||||
|
||||
This function loads the scene data from the blob (or file). If you pass `localAppState`, `localAppState` value will be preferred over the `appState` derived from `blob`. Throws if blob doesn't contain valid scene data.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { loadFromBlob } from "@excalidraw/excalidraw";
|
||||
|
||||
const scene = await loadFromBlob(file, null, null);
|
||||
excalidrawAPI.updateScene(scene);
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
<pre>
|
||||
loadFromBlob(<br/>
|
||||
blob: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a>,<br/>
|
||||
localAppState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a> | null,<br/>
|
||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null,<br/>
|
||||
fileHandle?: FileSystemHandle | null <br/>
|
||||
) => Promise<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L61">RestoredDataState</a>>
|
||||
</pre>
|
||||
|
||||
### loadLibraryFromBlob
|
||||
|
||||
This function loads the library from the blob. Additonally takes `defaultStatus` param which sets the default status for library item if not present, defaults to `unpublished`.
|
||||
|
||||
**How to use **
|
||||
|
||||
```js
|
||||
import { loadLibraryFromBlob } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
loadLibraryFromBlob(blob: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a>, defaultStatus: "published" | "unpublished")
|
||||
</pre>
|
||||
|
||||
### loadSceneOrLibraryFromBlob
|
||||
|
||||
This function loads either scene or library data from the supplied blob. If the blob contains scene data, and you pass `localAppState`, `localAppState` value will be preferred over the `appState` derived from `blob`.
|
||||
|
||||
:::caution
|
||||
|
||||
Throws if blob doesn't contain valid `scene` data or `library` data.
|
||||
|
||||
:::
|
||||
|
||||
**How to use**
|
||||
|
||||
```js showLineNumbers
|
||||
import { loadSceneOrLibraryFromBlob, MIME_TYPES } from "@excalidraw/excalidraw";
|
||||
|
||||
const contents = await loadSceneOrLibraryFromBlob(file, null, null);
|
||||
if (contents.type === MIME_TYPES.excalidraw) {
|
||||
excalidrawAPI.updateScene(contents.data);
|
||||
} else if (contents.type === MIME_TYPES.excalidrawlib) {
|
||||
excalidrawAPI.updateLibrary(contents.data);
|
||||
}
|
||||
```
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
loadSceneOrLibraryFromBlob(<br/>
|
||||
blob: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a>,
|
||||
localAppState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a> | null,<br/>
|
||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null,<br/>
|
||||
fileHandle?: FileSystemHandle | null<br/>
|
||||
) => Promise<{ type: string, data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/restore.ts#L53">RestoredDataState</a> | <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L33">ImportedLibraryState</a>}>
|
||||
</pre>
|
||||
|
||||
### getFreeDrawSvgPath
|
||||
|
||||
This function returns the `free draw` svg path for the element.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { getFreeDrawSvgPath } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
<pre>
|
||||
getFreeDrawSvgPath(element: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L182">ExcalidrawFreeDrawElement</a>)
|
||||
</pre>
|
||||
|
||||
### isLinearElement
|
||||
|
||||
This function returns true if the element is `linear` type (`arrow` |`line`) else returns `false`.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { isLinearElement } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
```tsx
|
||||
isLinearElement(elementType?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L80">ExcalidrawElement</a>): boolean
|
||||
```
|
||||
|
||||
### getNonDeletedElements
|
||||
|
||||
This function returns an array of `deleted` elements.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { getNonDeletedElements } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
<pre>
|
||||
getNonDeletedElements(elements:<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114"> readonly ExcalidrawElement[]</a>): as readonly <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L125">NonDeletedExcalidrawElement[]</a>
|
||||
</pre>
|
||||
|
||||
### mergeLibraryItems
|
||||
|
||||
This function merges two `LibraryItems` arrays, where unique items from `otherItems` are sorted first in the returned array.
|
||||
|
||||
```js
|
||||
import { mergeLibraryItems } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
mergeLibraryItems(localItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L250">LibraryItems</a>,<br/>
|
||||
otherItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200">LibraryItems</a>) => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L250">LibraryItems</a>
|
||||
</pre>
|
||||
|
||||
### parseLibraryTokensFromUrl
|
||||
|
||||
Parses library parameters from URL if present (expects the `#addLibrary` hash key), and returns an object with the `libraryUrl` and `idToken`. Returns `null` if `#addLibrary` hash key not found.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { parseLibraryTokensFromUrl } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
```tsx
|
||||
parseLibraryTokensFromUrl(): {
|
||||
libraryUrl: string;
|
||||
idToken: string | null;
|
||||
} | null
|
||||
```
|
||||
|
||||
### useHandleLibrary
|
||||
|
||||
A hook that automatically imports library from url if `#addLibrary` hash key exists on initial load, or when it changes during the editing session (e.g. when a user installs a new library), and handles initial library load if `getInitialLibraryItems` getter is supplied.
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { useHandleLibrary } from "@excalidraw/excalidraw";
|
||||
|
||||
export const App = () => {
|
||||
// ...
|
||||
useHandleLibrary({ excalidrawAPI });
|
||||
};
|
||||
```
|
||||
|
||||
**Signature**
|
||||
|
||||
<pre>
|
||||
useHandleLibrary(opts: {<br/>
|
||||
excalidrawAPI: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L494">ExcalidrawAPI</a>,<br/>
|
||||
getInitialLibraryItems?: () => <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L253">LibraryItemsSource</a><br/>
|
||||
});
|
||||
</pre>
|
||||
|
||||
In the future, we will be adding support for handling `library` persistence to `browser storage` (or elsewhere).
|
||||
|
||||
### getSceneVersion
|
||||
|
||||
This function returns the current `scene` version.
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
getSceneVersion(elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>)
|
||||
</pre>
|
||||
|
||||
**How to use**
|
||||
|
||||
```js
|
||||
import { getSceneVersion } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
### sceneCoordsToViewportCoords
|
||||
|
||||
This function returns equivalent `viewport` coords for the provided `scene` coords in params.
|
||||
|
||||
```js
|
||||
import { sceneCoordsToViewportCoords } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
sceneCoordsToViewportCoords({ sceneX: number, sceneY: number },<br/>
|
||||
appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a><br/>): { x: number, y: number }
|
||||
</pre>
|
||||
|
||||
### viewportCoordsToSceneCoords
|
||||
|
||||
This function returns equivalent `scene` coords for the provided `viewport` coords in params.
|
||||
|
||||
```js
|
||||
import { viewportCoordsToSceneCoords } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
**_Signature_**
|
||||
|
||||
<pre>
|
||||
viewportCoordsToSceneCoords({ clientX: number, clientY: number },<br/>
|
||||
appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a><br/>): {x: number, y: number}
|
||||
</pre>
|
||||
|
||||
### useDevice
|
||||
|
||||
This hook can be used to check the type of device which is being used. It can only be used inside the `children` of `Excalidraw` component.
|
||||
|
||||
Open the `main menu` in the below example to view the footer.
|
||||
|
||||
```jsx live noInline
|
||||
const MobileFooter = ({}) => {
|
||||
const device = useDevice();
|
||||
if (device.isMobile) {
|
||||
return (
|
||||
<Footer>
|
||||
<button
|
||||
className="custom-footer"
|
||||
style={{ marginLeft: "20px", height: "2rem" }}
|
||||
onClick={() => alert("This is custom footer in mobile menu")}
|
||||
>
|
||||
custom footer
|
||||
</button>
|
||||
</Footer>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const App = () => (
|
||||
<div style={{ height: "400px" }}>
|
||||
<Excalidraw>
|
||||
<MainMenu>
|
||||
<MainMenu.Item> Item1 </MainMenu.Item>
|
||||
<MainMenu.Item> Item 2 </MainMenu.Item>
|
||||
<MobileFooter />
|
||||
</MainMenu>
|
||||
</Excalidraw>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Need to render when code is span across multiple components
|
||||
// in Live Code blocks editor
|
||||
render(<App />);
|
||||
```
|
||||
|
||||
The `device` has the following `attributes`
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `isSmScreen` | `boolean` | Set to `true` when the device small screen is small (Width < `640px` ) |
|
||||
| `isMobile` | `boolean` | Set to `true` when the device is `mobile` |
|
||||
| `isTouchScreen` | `boolean` | Set to `true` for `touch` devices |
|
||||
| `canDeviceFitSidebar` | `boolean` | Implies whether there is enough space to fit the `sidebar` |
|
49
dev-docs/docs/@excalidraw/excalidraw/customizing-styles.mdx
Normal file
@ -0,0 +1,49 @@
|
||||
# Customizing Styles
|
||||
|
||||
Excalidraw is using CSS variables to style certain components. To override them, you should set your own on the `.excalidraw` and `.excalidraw.theme--dark` (for dark mode variables) selectors.
|
||||
|
||||
Make sure the selector has higher specificity, e.g. by prefixing it with your app's selector:
|
||||
|
||||
```css
|
||||
.your-app .excalidraw {
|
||||
--color-primary: red;
|
||||
}
|
||||
.your-app .excalidraw.theme--dark {
|
||||
--color-primary: pink;
|
||||
}
|
||||
```
|
||||
|
||||
Most notably, you can customize the primary colors, by overriding these variables:
|
||||
|
||||
- `--color-primary`
|
||||
- `--color-primary-darker`
|
||||
- `--color-primary-darkest`
|
||||
- `--color-primary-light`
|
||||
- `--color-primary-contrast-offset` — a slightly darker (in light mode), or lighter (in dark mode) `--color-primary` color to fix contrast issues (see [Chubb illusion](https://en.wikipedia.org/wiki/Chubb_illusion)). It will fall back to `--color-primary` if not present.
|
||||
|
||||
For a complete list of variables, check [theme.scss](https://github.com/excalidraw/excalidraw/blob/master/src/css/theme.scss), though most of them will not make sense to override.
|
||||
|
||||
```css showLineNumbers
|
||||
.custom-styles .excalidraw {
|
||||
--color-primary: #fcc6d9;
|
||||
--color-primary-darker: #f783ac;
|
||||
--color-primary-darkest: #e64980;
|
||||
--color-primary-light: #f2a9c4;
|
||||
}
|
||||
|
||||
.custom-styles .excalidraw.theme--dark {
|
||||
--color-primary: #d494aa;
|
||||
--color-primary-darker: #d64c7e;
|
||||
--color-primary-darkest: #e86e99;
|
||||
--color-primary-light: #dcbec9;
|
||||
}
|
||||
```
|
||||
```tsx live
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ height: "500px" }} className="custom-styles">
|
||||
<Excalidraw />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
59
dev-docs/docs/@excalidraw/excalidraw/development.mdx
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
pagination_prev: "@excalidraw/excalidraw/installation"
|
||||
---
|
||||
|
||||
# Development
|
||||
|
||||
This page relates to developing the `@excalidraw/excalidraw` package itself.
|
||||
|
||||
## Example app
|
||||
|
||||
To start the example app using the `@excalidraw/excalidraw` package, follow the below steps:
|
||||
|
||||
1. Install the dependencies
|
||||
|
||||
```bash
|
||||
cd src/packages/excalidraw && yarn
|
||||
```
|
||||
|
||||
2. Start the example app
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
[http://localhost:3001](http://localhost:3001) will open in your default browser.
|
||||
|
||||
The example is same as the [codesandbox example](https://ehlz3.csb.app/)
|
||||
|
||||
## Releasing
|
||||
|
||||
### Create a test release
|
||||
|
||||
You can create a test release by posting the below comment in your pull request:
|
||||
|
||||
```bash
|
||||
@excalibot trigger release
|
||||
```
|
||||
|
||||
Once the version is released `@excalibot` will post a comment with the release version.
|
||||
|
||||
### Creating a production release
|
||||
|
||||
To release the next stable version follow the below steps:
|
||||
|
||||
```bash
|
||||
yarn prerelease version
|
||||
```
|
||||
|
||||
You need to pass the `version` for which you want to create the release. This will make the changes needed before making the release like updating `package.json`, `changelog` and more.
|
||||
|
||||
The next step is to run the `release` script:
|
||||
|
||||
```bash
|
||||
yarn release
|
||||
```
|
||||
|
||||
This will publish the package.
|
||||
|
||||
Right now there are two steps to create a production release but once this works fine these scripts will be combined and more automation will be done.
|
9
dev-docs/docs/@excalidraw/excalidraw/faq.mdx
Normal file
@ -0,0 +1,9 @@
|
||||
# FAQ
|
||||
|
||||
### Does this package support collaboration ?
|
||||
|
||||
No, Excalidraw package doesn't come with collaboration built in, since the implementation is specific to each host app. We expose APIs which you can use to communicate with Excalidraw which you can use to implement it. You can check our own implementation [here](https://github.com/excalidraw/excalidraw/blob/master/src/excalidraw-app/index.tsx). Here is a [detailed answer](https://github.com/excalidraw/excalidraw/discussions/3879#discussioncomment-1110524) on how you can achieve the same.
|
||||
|
||||
## Need help?
|
||||
|
||||
Check out the existing [Q&A](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw). If you have any queries or need help, ask us [here](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw).
|
43
dev-docs/docs/@excalidraw/excalidraw/installation.mdx
Normal file
@ -0,0 +1,43 @@
|
||||
# Installation
|
||||
|
||||
**Excalidraw** is published to npm as a component you can directly embed in your projects.
|
||||
|
||||
Using `npm`:
|
||||
|
||||
```bash
|
||||
npm install react react-dom @excalidraw/excalidraw
|
||||
```
|
||||
|
||||
or `yarn`:
|
||||
|
||||
```bash
|
||||
yarn add react react-dom @excalidraw/excalidraw
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
**If you don't want to wait for the next stable release and try out the unreleased changes you can use `@excalidraw/excalidraw@next`.**
|
||||
|
||||
:::
|
||||
|
||||
### Static assets
|
||||
|
||||
Excalidraw depends on assets such as localization files (if you opt to use them), fonts, and others.
|
||||
|
||||
By default these assets are loaded from a public CDN [`https://unpkg.com/@excalidraw/excalidraw/dist/`](https://unpkg.com/@excalidraw/excalidraw/dist), so you don't need to do anything on your end.
|
||||
|
||||
However, if you want to host these files yourself, you can find them in your `node_modules/@excalidraw/excalidraw/dist` directory, in folders `excalidraw-assets` (for production) and `excalidraw-assets-dev` (for development).
|
||||
|
||||
Copy these folders to your static assets directory, and add a `window.EXCALIDRAW_ASSET_PATH` variable in your `index.html` or `index.js` entry file pointing to your public assets path (relative). For example, if you serve your assets from the root of your hostname, you would do:
|
||||
|
||||
```js
|
||||
window.EXCALIDRAW_ASSET_PATH = "/";
|
||||
```
|
||||
|
||||
### Dimensions of Excalidraw
|
||||
|
||||
Excalidraw takes _100%_ of `width` and `height` of the containing block so make sure the container in which you render Excalidraw has non zero dimensions.
|
||||
|
||||
### Demo
|
||||
|
||||
[Try here](https://codesandbox.io/s/excalidraw-ehlz3).
|
129
dev-docs/docs/@excalidraw/excalidraw/integration.mdx
Normal file
@ -0,0 +1,129 @@
|
||||
# Integration
|
||||
|
||||
## Module bundler
|
||||
|
||||
If you are using a module bundler (for instance, Webpack), you can import it as an ES6 module as shown below
|
||||
|
||||
```js
|
||||
import { Excalidraw } from "@excalidraw/excalidraw";
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Throughout the documentation we use live, editable Excalidraw examples like the one shown below.
|
||||
|
||||
While we aim for the examples to closely reflect what you'd get if you rendered it yourself, we actually initialize it with some props behind the scenes.
|
||||
For example, we're passing a `theme` prop to it based on the current color theme of the docs you're just reading.
|
||||
|
||||
:::
|
||||
|
||||
```jsx live
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<h1 style={{ textAlign: "center" }}>Excalidraw Example</h1>
|
||||
<div style={{ height: "500px" }}>
|
||||
<Excalidraw />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Rendering Excalidraw only on client
|
||||
|
||||
Since _Excalidraw_ doesn't support server side rendering, you should render the component once the host is `mounted`.
|
||||
|
||||
```jsx showLineNumbers
|
||||
import { useState, useEffect } from "react";
|
||||
export default function App() {
|
||||
const [Comp, setComp] = useState(null);
|
||||
useEffect(() => {
|
||||
import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
|
||||
}, []);
|
||||
return <>{Comp && <Comp />}</>;
|
||||
}
|
||||
```
|
||||
|
||||
The `types` are available at `@excalidraw/excalidraw/types`, you can view [example for typescript](https://codesandbox.io/s/excalidraw-types-9h2dm)
|
||||
|
||||
## Browser
|
||||
|
||||
To use it in a browser directly:
|
||||
|
||||
For development use :point_down:
|
||||
|
||||
```js
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.development.js"
|
||||
></script>
|
||||
```
|
||||
|
||||
For production use :point_down:
|
||||
|
||||
```js
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.production.min.js"
|
||||
></script>
|
||||
```
|
||||
|
||||
You will need to make sure `react`, `react-dom` is available as shown in the below example. For prod please use the production versions of `react`, `react-dom`.
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="html" label="html">
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Excalidraw in browser</title>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="https://unpkg.com/react@18.2.0/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.development.js"></script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.development.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Excalidraw Embed Example</h1>
|
||||
<div id="app"></div>
|
||||
</div>
|
||||
<script type="text/javascript" src="src/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="js" label="Javascript">
|
||||
|
||||
```js showLineNumbers
|
||||
const App = () => {
|
||||
return React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement(
|
||||
"div",
|
||||
{
|
||||
style: { height: "500px" },
|
||||
},
|
||||
React.createElement(ExcalidrawLib.Excalidraw),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const excalidrawWrapper = document.getElementById("app");
|
||||
const root = ReactDOM.createRoot(excalidrawWrapper);
|
||||
root.render(React.createElement(App));
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
BIN
dev-docs/docs/assets/nerd-stats.png
Normal file
After Width: | Height: | Size: 83 KiB |
@ -1,6 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: Overview
|
||||
---
|
||||
|
||||
In development. For now, refer to [excalidraw Readme](https://github.com/excalidraw/excalidraw/blob/master/README.md).
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
Want to integrate Excalidraw into your app? Head over to the [package docs](/docs/package/overview).
|
||||
|
||||
If you're looking into the Excalidraw codebase itself, start [here](/docs/codebase/overview).
|
72
dev-docs/docs/introduction/contributing.mdx
Normal file
@ -0,0 +1,72 @@
|
||||
# Contributing
|
||||
|
||||
Pull requests are welcome. For major changes, please [open an issue](https://github.com/excalidraw/excalidraw/issues/new) first to discuss what you would like to change.
|
||||
|
||||
## Setup
|
||||
|
||||
### Option 1 - Manual
|
||||
|
||||
1. Fork and clone the repo
|
||||
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:
|
||||
>
|
||||
> ```bash
|
||||
> git remote add upstream https://github.com/excalidraw/excalidraw.git
|
||||
> git fetch upstream
|
||||
> git branch --set-upstream-to=upstream/master master
|
||||
> ```
|
||||
|
||||
### Option 2 - CodeSandbox
|
||||
|
||||
1. Go to https://codesandbox.io/s/github/excalidraw/excalidraw
|
||||
1. Connect your GitHub account
|
||||
1. Go to Git tab on left side
|
||||
1. Tap on `Fork Sandbox`
|
||||
1. Write your code
|
||||
1. Commit and PR automatically
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
Don't worry if you get any of the below wrong, or if you don't know how. We'll gladly help out.
|
||||
|
||||
### Title
|
||||
|
||||
Make sure the title starts with a semantic prefix:
|
||||
|
||||
- **feat**: A new feature
|
||||
- **fix**: A bug fix
|
||||
- **docs**: Documentation only changes
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
- **perf**: A code change that improves performance
|
||||
- **test**: Adding missing tests or correcting existing tests
|
||||
- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
- **chore**: Other changes that don't modify src or test files
|
||||
- **revert**: Reverts a previous commit
|
||||
|
||||
### Changelog
|
||||
|
||||
Add a brief description of your pull request to the changelog located here: [changelog](https://github.com/excalidraw/excalidraw/blob/master/CHANGELOG.md)
|
||||
|
||||
Notes:
|
||||
|
||||
- Make sure to prepend to the section corresponding with the semantic prefix you selected in the title
|
||||
- Link to your pull request - this will require updating the CHANGELOG _after_ creating the pull request
|
||||
|
||||
### Testing
|
||||
|
||||
Once you submit your pull request it will automatically be tested. Be sure to check the results of the test and fix any issues that arise.
|
||||
|
||||
It's also a good idea to consider if your change should include additional tests. This is highly recommended for new features or bug-fixes. For example, it's good practice to create a test for each bug you fix which ensures that we don't regress the code in the future.
|
||||
|
||||
Finally - always manually test your changes using the convenient staging environment deployed for each pull request. As much as local development attempts to replicate production, there can still be subtle differences in behavior. For larger features consider testing your change in multiple browsers as well.
|
||||
|
||||
|
||||
## Translating
|
||||
|
||||
To translate Excalidraw into other languages, please visit [our Crowdin page](https://crowdin.com/project/excalidraw). To add a new language, [open an issue](https://github.com/excalidraw/excalidraw/issues/new) so we can get things set up on our end first.
|
||||
|
||||
Translations will be available on the app if they exceed a certain threshold of completion (currently **85%**).
|
102
dev-docs/docs/introduction/development.mdx
Normal file
@ -0,0 +1,102 @@
|
||||
# Development
|
||||
|
||||
## Code Sandbox
|
||||
|
||||
- Go to https://codesandbox.io/p/github/excalidraw/excalidraw
|
||||
- You may need to sign in with GitHub and reload the page
|
||||
- You can start coding instantly, and even send PRs from there!
|
||||
|
||||
## Local Installation
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org/en/)
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install) (v1 or v2.4.2+)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
### Clone the repo
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
## Collaboration
|
||||
|
||||
For collaboration, you will need to set up [collab server](https://github.com/excalidraw/excalidraw-room) in local.
|
||||
|
||||
## Commands
|
||||
|
||||
### Install the dependencies
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
### Run the project
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Reformat all files with Prettier
|
||||
|
||||
```bash
|
||||
yarn fix
|
||||
```
|
||||
|
||||
### Run tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
### Update test snapshots
|
||||
|
||||
```bash
|
||||
yarn test:update
|
||||
```
|
||||
|
||||
### Test for formatting with Prettier
|
||||
|
||||
```bash
|
||||
yarn test:code
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
You can use docker-compose to work on Excalidraw locally if you don't want to setup a Node.js env.
|
||||
|
||||
```bash
|
||||
docker-compose up --build -d
|
||||
```
|
||||
|
||||
## Self-hosting
|
||||
|
||||
We publish a Docker image with the Excalidraw client at [excalidraw/excalidraw](https://hub.docker.com/r/excalidraw/excalidraw). You can use it to self-host your own client under your own domain, on Kubernetes, AWS ECS, etc.
|
||||
|
||||
```bash
|
||||
docker build -t excalidraw/excalidraw .
|
||||
docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latest
|
||||
```
|
||||
|
||||
The Docker image is free of analytics and other tracking libraries.
|
||||
|
||||
**At the moment, self-hosting your own instance doesn't support sharing or collaboration features.**
|
||||
|
||||
We are working towards providing a full-fledged solution for self-hosting your own Excalidraw.
|
16
dev-docs/docs/introduction/get-started.mdx
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: Introduction
|
||||
slug: ../
|
||||
---
|
||||
|
||||
## Try now
|
||||
|
||||
Go to [excalidraw.com](https://excalidraw.com) to start sketching.
|
||||
|
||||
## How are these docs structured
|
||||
|
||||
These docs are focused on developers, and structured in the following way:
|
||||
|
||||
- [Introduction](/docs/) — development setup and introduction.
|
||||
- [@excalidraw/excalidraw](/docs/@excalidraw/excalidraw/installation) — docs for the npm package to help you integrate Excalidraw into your own app.
|
||||
- Editor — IN PROGRESS. Docs describing the internals of the Excalidraw editor to help in contributing to the codebase.
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: Overview
|
||||
---
|
||||
|
||||
In development. For now, refer to [excalidraw package readme](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md).
|
@ -1,15 +1,12 @@
|
||||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require("prism-react-renderer/themes/github");
|
||||
const darkCodeTheme = require("prism-react-renderer/themes/dracula");
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: "Excalidraw developer docs",
|
||||
tagline:
|
||||
"For Excalidraw contributors or those integrating the Excalidraw editor",
|
||||
url: "https://docs.excalidraw.com.com",
|
||||
url: "https://docs.excalidraw.com",
|
||||
baseUrl: "/",
|
||||
onBrokenLinks: "throw",
|
||||
onBrokenMarkdownLinks: "warn",
|
||||
@ -33,10 +30,16 @@ const config = {
|
||||
docs: {
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
// Please change this to your repo.
|
||||
editUrl: "https://github.com/excalidraw/docs/tree/master/",
|
||||
editUrl:
|
||||
"https://github.com/excalidraw/excalidraw/tree/master/dev-docs/",
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
customCss: [
|
||||
require.resolve("./src/css/custom.scss"),
|
||||
require.resolve("../src/packages/excalidraw/example/App.scss"),
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
@ -45,18 +48,20 @@ const config = {
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
colorMode: {
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
navbar: {
|
||||
title: "Excalidraw Docs",
|
||||
title: "Excalidraw",
|
||||
logo: {
|
||||
alt: "Excalidraw Logo",
|
||||
src: "img/logo.svg",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: "doc",
|
||||
docId: "get-started",
|
||||
to: "/docs",
|
||||
position: "left",
|
||||
label: "Get started",
|
||||
label: "Docs",
|
||||
},
|
||||
{
|
||||
to: "https://blog.excalidraw.com",
|
||||
@ -78,7 +83,7 @@ const config = {
|
||||
items: [
|
||||
{
|
||||
label: "Get Started",
|
||||
to: "/docs/get-started",
|
||||
to: "/docs",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -93,6 +98,10 @@ const config = {
|
||||
label: "Twitter",
|
||||
href: "https://twitter.com/excalidraw",
|
||||
},
|
||||
{
|
||||
label: "Linkedin",
|
||||
href: "https://www.linkedin.com/company/excalidraw",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -109,13 +118,28 @@ const config = {
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Made with ❤️ Built with Docusaurus`,
|
||||
copyright: `Copyright © 2023 Excalidraw community. Built with Docusaurus ❤️`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
theme: require("prism-react-renderer/themes/dracula"),
|
||||
},
|
||||
image: "img/og-image.png",
|
||||
docs: {
|
||||
sidebar: {
|
||||
hideable: true,
|
||||
},
|
||||
},
|
||||
tableOfContents: {
|
||||
maxHeadingLevel: 4,
|
||||
},
|
||||
algolia: {
|
||||
appId: "8FEAOD28DI",
|
||||
apiKey: "4b07cca33ff2d2919bc95ff98f148e9e",
|
||||
indexName: "excalidraw",
|
||||
},
|
||||
}),
|
||||
themes: ["@docusaurus/theme-live-codeblock"],
|
||||
plugins: ["docusaurus-plugin-sass"],
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
@ -15,13 +15,17 @@
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.0.0-rc.1",
|
||||
"@docusaurus/preset-classic": "2.0.0-rc.1",
|
||||
"@docusaurus/core": "2.2.0",
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@docusaurus/theme-live-codeblock": "2.2.0",
|
||||
"@excalidraw/excalidraw": "0.14.2",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"docusaurus-plugin-sass": "0.2.3",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react-dom": "^17.0.2",
|
||||
"sass": "1.57.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.0.0-rc.1",
|
||||
|
@ -13,19 +13,86 @@
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
docs: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['hello'],
|
||||
type: "category",
|
||||
label: "Introduction",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "introduction/get-started",
|
||||
},
|
||||
items: ["introduction/development", "introduction/contributing"],
|
||||
},
|
||||
|
||||
{
|
||||
type: "category",
|
||||
label: "@excalidraw/excalidraw",
|
||||
collapsed: false,
|
||||
items: [
|
||||
"@excalidraw/excalidraw/installation",
|
||||
"@excalidraw/excalidraw/integration",
|
||||
"@excalidraw/excalidraw/customizing-styles",
|
||||
{
|
||||
type: "category",
|
||||
label: "API",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "@excalidraw/excalidraw/api/api-intro",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: "category",
|
||||
label: "Props",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "@excalidraw/excalidraw/api/props/props",
|
||||
},
|
||||
items: [
|
||||
"@excalidraw/excalidraw/api/props/initialdata",
|
||||
"@excalidraw/excalidraw/api/props/ref",
|
||||
"@excalidraw/excalidraw/api/props/render-props",
|
||||
"@excalidraw/excalidraw/api/props/ui-options",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Children Components",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "@excalidraw/excalidraw/api/children-components/children-components-intro",
|
||||
},
|
||||
items: [
|
||||
"@excalidraw/excalidraw/api/children-components/main-menu",
|
||||
"@excalidraw/excalidraw/api/children-components/welcome-screen",
|
||||
"@excalidraw/excalidraw/api/children-components/footer",
|
||||
"@excalidraw/excalidraw/api/children-components/live-collaboration-trigger",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Utils",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "@excalidraw/excalidraw/api/utils/utils-intro",
|
||||
},
|
||||
items: [
|
||||
"@excalidraw/excalidraw/api/utils/export",
|
||||
"@excalidraw/excalidraw/api/utils/restore",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Constants",
|
||||
link: { type: "doc", id: "@excalidraw/excalidraw/api/constants" },
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
"@excalidraw/excalidraw/faq",
|
||||
"@excalidraw/excalidraw/development",
|
||||
],
|
||||
},
|
||||
],
|
||||
*/
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
||||
|
15
dev-docs/src/components/Highlight.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
export default function Highlight({ children }) {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: "#7874e8",
|
||||
borderRadius: "2px",
|
||||
color: "#fff",
|
||||
padding: "0.2rem",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
@ -14,11 +14,13 @@
|
||||
--ifm-color-primary-lighter: #5b57d1;
|
||||
--ifm-color-primary-lightest: #5b57d1;
|
||||
--ifm-code-font-size: 95%;
|
||||
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme="dark"] {
|
||||
--ifm-color-primary: #5650f0;
|
||||
--ifm-color-primary: #8784e3;
|
||||
--ifm-color-primary-dark: #4b46d8;
|
||||
--ifm-color-primary-darker: #4b46d8;
|
||||
--ifm-color-primary-darkest: #3e39be;
|
||||
@ -41,3 +43,59 @@
|
||||
[data-theme="dark"] .navbar__logo {
|
||||
filter: invert(93%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
pre a {
|
||||
color: #5dccff;
|
||||
|
||||
&:hover {
|
||||
color: #8fd3f3;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-button {
|
||||
height: 40px;
|
||||
max-width: 200px;
|
||||
margin: 10px 0;
|
||||
padding: 5px;
|
||||
background: #70b1ec;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.custom-styles .excalidraw {
|
||||
--color-primary: #fcc6d9;
|
||||
--color-primary-darker: #f783ac;
|
||||
--color-primary-darkest: #e64980;
|
||||
--color-primary-light: #f2a9c4;
|
||||
}
|
||||
|
||||
.custom-styles .excalidraw.theme--dark {
|
||||
--color-primary: #d494aa;
|
||||
--color-primary-darker: #d64c7e;
|
||||
--color-primary-darkest: #e86e99;
|
||||
--color-primary-light: #dcbec9;
|
||||
}
|
||||
|
||||
/* The global css conflicts with Excal css hence overriding */
|
||||
|
||||
.excalidraw .context-menu-item__shortcut {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
.excalidraw .Stats table td,
|
||||
.excalidraw .Stats table th,
|
||||
.excalidraw .Stats table tr {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
.excalidraw .Stats .close {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.excalidraw .Stats table {
|
||||
display: table;
|
||||
}
|
1230
dev-docs/src/initialData.js
Normal file
@ -1,42 +0,0 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import Layout from "@theme/Layout";
|
||||
import Link from "@docusaurus/Link";
|
||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
||||
import styles from "./index.module.css";
|
||||
import HomepageFeatures from "@site/src/components/Homepage";
|
||||
|
||||
function HomepageHeader() {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<header className={clsx("hero hero--primary", styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
to="/docs/get-started"
|
||||
>
|
||||
Get started
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />"
|
||||
>
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
@ -14,10 +14,7 @@ function HomepageHeader() {
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
to="/docs/get-started"
|
||||
>
|
||||
<Link className="button button--secondary button--lg" to="/docs">
|
||||
Get started
|
||||
</Link>
|
||||
</div>
|
||||
@ -27,12 +24,8 @@ function HomepageHeader() {
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />"
|
||||
>
|
||||
<Layout description="For Excalidraw contributors or those integrating the Excalidraw editor">
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
|
11
dev-docs/src/theme/MDXComponents.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Import the original mapper
|
||||
import MDXComponents from "@theme-original/MDXComponents";
|
||||
import Highlight from "@site/src/components/Highlight";
|
||||
|
||||
export default {
|
||||
// Re-use the default mapping
|
||||
...MDXComponents,
|
||||
// Map the "highlight" tag to our <Highlight /> component!
|
||||
// `Highlight` will receive all props that were passed to `highlight` in MDX
|
||||
highlight: Highlight,
|
||||
};
|
29
dev-docs/src/theme/ReactLiveScope/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
|
||||
import initialData from "@site/src/initialData";
|
||||
import { useColorMode } from "@docusaurus/theme-common";
|
||||
|
||||
let ExcalidrawComp = {};
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
ExcalidrawComp = require("@excalidraw/excalidraw");
|
||||
}
|
||||
const Excalidraw = React.forwardRef((props, ref) => {
|
||||
const { colorMode } = useColorMode();
|
||||
return <ExcalidrawComp.Excalidraw theme={colorMode} {...props} ref={ref} />;
|
||||
});
|
||||
// Add react-live imports you need here
|
||||
const ExcalidrawScope = {
|
||||
React,
|
||||
...React,
|
||||
Excalidraw,
|
||||
Footer: ExcalidrawComp.Footer,
|
||||
useDevice: ExcalidrawComp.useDevice,
|
||||
MainMenu: ExcalidrawComp.MainMenu,
|
||||
WelcomeScreen: ExcalidrawComp.WelcomeScreen,
|
||||
LiveCollaborationTrigger: ExcalidrawComp.LiveCollaborationTrigger,
|
||||
Sidebar: ExcalidrawComp.Sidebar,
|
||||
exportToCanvas: ExcalidrawComp.exportToCanvas,
|
||||
initialData,
|
||||
};
|
||||
|
||||
export default ExcalidrawScope;
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
BIN
dev-docs/static/img/og-image-sm.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
dev-docs/static/img/og-image.png
Normal file
After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
@ -1191,10 +1191,10 @@
|
||||
"@docsearch/css" "3.1.1"
|
||||
algoliasearch "^4.0.0"
|
||||
|
||||
"@docusaurus/core@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-rc.1.tgz#828d93d241171565d8947a9ab404091e04759141"
|
||||
integrity sha512-b9FX0Z+EddfQ6wAiNh+Wx4fysKfcvEcWJrZ5USROn3C+EVU5P4luaa8mwWK//O+hTwD9ur7/A44IZ/tWCTAoLQ==
|
||||
"@docusaurus/core@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.2.0.tgz#64c9ee31502c23b93c869f8188f73afaf5fd4867"
|
||||
integrity sha512-Vd6XOluKQqzG12fEs9prJgDtyn6DPok9vmUWDR2E6/nV5Fl9SVkhEQOBxwObjk3kQh7OY7vguFaLh0jqdApWsA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.18.6"
|
||||
"@babel/generator" "^7.18.7"
|
||||
@ -1206,13 +1206,13 @@
|
||||
"@babel/runtime" "^7.18.6"
|
||||
"@babel/runtime-corejs3" "^7.18.6"
|
||||
"@babel/traverse" "^7.18.8"
|
||||
"@docusaurus/cssnano-preset" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/cssnano-preset" "2.2.0"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/react-loadable" "5.5.2"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-common" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-common" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
"@slorber/static-site-generator-webpack-plugin" "^4.0.7"
|
||||
"@svgr/webpack" "^6.2.1"
|
||||
autoprefixer "^10.4.7"
|
||||
@ -1268,33 +1268,33 @@
|
||||
webpack-merge "^5.8.0"
|
||||
webpackbar "^5.0.2"
|
||||
|
||||
"@docusaurus/cssnano-preset@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-rc.1.tgz#76bbd7f6912779a0667f8f2fd8fc1a05618a6148"
|
||||
integrity sha512-9/KmQvF+eTlMqUqG6UcXbRgxbGed/8bQInXuKEs+95/jI6jO/3xSzuRwuHHHP0naUvSVWjnNI9jngPrQerXE5w==
|
||||
"@docusaurus/cssnano-preset@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.2.0.tgz#fc05044659051ae74ab4482afcf4a9936e81d523"
|
||||
integrity sha512-mAAwCo4n66TMWBH1kXnHVZsakW9VAXJzTO4yZukuL3ro4F+JtkMwKfh42EG75K/J/YIFQG5I/Bzy0UH/hFxaTg==
|
||||
dependencies:
|
||||
cssnano-preset-advanced "^5.3.8"
|
||||
postcss "^8.4.14"
|
||||
postcss-sort-media-queries "^4.2.1"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/logger@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-rc.1.tgz#db95e9b15bc243695830a5b791c0eff705ef1b54"
|
||||
integrity sha512-daa3g+SXuO9K60PVMiSUmDEK9Vro+Ed7i7uF8CH6QQJLcNZy/zJc0Xz62eH7ip1x77fmeb6Rg4Us1TqTFc9AbQ==
|
||||
"@docusaurus/logger@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.2.0.tgz#ea2f7feda7b8675485933b87f06d9c976d17423f"
|
||||
integrity sha512-DF3j1cA5y2nNsu/vk8AG7xwpZu6f5MKkPPMaaIbgXLnWGfm6+wkOeW7kNrxnM95YOhKUkJUophX69nGUnLsm0A==
|
||||
dependencies:
|
||||
chalk "^4.1.2"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/mdx-loader@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-rc.1.tgz#e78d7d416aacc289f2427c5ccdb9145820acb0cb"
|
||||
integrity sha512-8Fg0c/ceu39knmr7w0dutm7gq3YxKYCqWVS2cB/cPATzChCCNH/AGLfBT6sz/Z4tjVXE+NyREq2pfOFvkhjVXg==
|
||||
"@docusaurus/mdx-loader@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.2.0.tgz#fd558f429e5d9403d284bd4214e54d9768b041a0"
|
||||
integrity sha512-X2bzo3T0jW0VhUU+XdQofcEeozXOTmKQMvc8tUnWRdTnCvj4XEcBVdC3g+/jftceluiwSTNRAX4VBOJdNt18jA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.18.8"
|
||||
"@babel/traverse" "^7.18.8"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@mdx-js/mdx" "^1.6.22"
|
||||
escape-html "^1.0.3"
|
||||
file-loader "^6.2.0"
|
||||
@ -1323,18 +1323,32 @@
|
||||
react-helmet-async "*"
|
||||
react-loadable "npm:@docusaurus/react-loadable@5.5.2"
|
||||
|
||||
"@docusaurus/plugin-content-blog@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-rc.1.tgz#8ae5d5ec2da08c583a057bf2754a5b9278b3eb08"
|
||||
integrity sha512-BVVrAGZujpjS/0rarY2o24rlylRRh2NZuM65kg0JNkkViF79SeEHsepog7IuHyoqGWPm1N/I7LpEp7k+gowZzQ==
|
||||
"@docusaurus/module-type-aliases@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.2.0.tgz#1e23e54a1bbb6fde1961e4fa395b1b69f4803ba5"
|
||||
integrity sha512-wDGW4IHKoOr9YuJgy7uYuKWrDrSpsUSDHLZnWQYM9fN7D5EpSmYHjFruUpKWVyxLpD/Wh0rW8hYZwdjJIQUQCQ==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-common" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/react-loadable" "5.5.2"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router-config" "*"
|
||||
"@types/react-router-dom" "*"
|
||||
react-helmet-async "*"
|
||||
react-loadable "npm:@docusaurus/react-loadable@5.5.2"
|
||||
|
||||
"@docusaurus/plugin-content-blog@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.2.0.tgz#dc55982e76771f4e678ac10e26d10e1da2011dc1"
|
||||
integrity sha512-0mWBinEh0a5J2+8ZJXJXbrCk1tSTNf7Nm4tYAl5h2/xx+PvH/Bnu0V+7mMljYm/1QlDYALNIIaT/JcoZQFUN3w==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-common" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
cheerio "^1.0.0-rc.12"
|
||||
feed "^4.2.2"
|
||||
fs-extra "^10.1.0"
|
||||
@ -1345,18 +1359,18 @@
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.73.0"
|
||||
|
||||
"@docusaurus/plugin-content-docs@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-rc.1.tgz#2dda88166bf21b0eeb3821ef748059b20c8c49f7"
|
||||
integrity sha512-Yk5Hu6uaw3tRplzJnbDygwRhmZ3PCzEXD4SJpBA6cPC73ylfqOEh6qhiU+BWhMTtDXNhY+athk5Kycfk3DW1aQ==
|
||||
"@docusaurus/plugin-content-docs@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.2.0.tgz#0fcb85226fcdb80dc1e2d4a36ef442a650dcc84d"
|
||||
integrity sha512-BOazBR0XjzsHE+2K1wpNxz5QZmrJgmm3+0Re0EVPYFGW8qndCWGNtXW/0lGKhecVPML8yyFeAmnUCIs7xM2wPw==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/module-type-aliases" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/module-type-aliases" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
"@types/react-router-config" "^5.0.6"
|
||||
combine-promises "^1.1.0"
|
||||
fs-extra "^10.1.0"
|
||||
@ -1367,84 +1381,84 @@
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.73.0"
|
||||
|
||||
"@docusaurus/plugin-content-pages@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-rc.1.tgz#2be82f53d6c77e6aa66787726c30dc60b210e6f8"
|
||||
integrity sha512-FdO79WC5hfWDQu3/CTFLRQzTNc0e5n+HNzavm2MNkSzGV08BFJ6RAkbPbtra5CWef+6iXZav6D/tzv2jDPvLzA==
|
||||
"@docusaurus/plugin-content-pages@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.2.0.tgz#e3f40408787bbe229545dd50595f87e1393bc3ae"
|
||||
integrity sha512-+OTK3FQHk5WMvdelz8v19PbEbx+CNT6VSpx7nVOvMNs5yJCKvmqBJBQ2ZSxROxhVDYn+CZOlmyrC56NSXzHf6g==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
fs-extra "^10.1.0"
|
||||
tslib "^2.4.0"
|
||||
webpack "^5.73.0"
|
||||
|
||||
"@docusaurus/plugin-debug@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-rc.1.tgz#73c06ad08d66810941e456d50b07be008f5235cb"
|
||||
integrity sha512-aOsyYrPMbnsyqHwsVZ+0frrMRtnYqm4eaJpG4sC/6LYAJ07IDRQ9j3GOku2dKr5GsFK1Vx7VlE6ZLwe0MaGstg==
|
||||
"@docusaurus/plugin-debug@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.2.0.tgz#b38741d2c492f405fee01ee0ef2e0029cedb689a"
|
||||
integrity sha512-p9vOep8+7OVl6r/NREEYxf4HMAjV8JMYJ7Bos5fCFO0Wyi9AZEo0sCTliRd7R8+dlJXZEgcngSdxAUo/Q+CJow==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
fs-extra "^10.1.0"
|
||||
react-json-view "^1.21.3"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/plugin-google-analytics@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-rc.1.tgz#0136cc7534573ca56e023178ec2bda5c1e89ce71"
|
||||
integrity sha512-f+G8z5OJWfg5QqWDLIdcN2SDoK5J5Gg8HMrqCI6Pfl+rxPb5I1niA+/UkAM+kMCpnekvhSt5AWz2fgkRenkPLA==
|
||||
"@docusaurus/plugin-google-analytics@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.2.0.tgz#63c7137eff5a1208d2059fea04b5207c037d7954"
|
||||
integrity sha512-+eZVVxVeEnV5nVQJdey9ZsfyEVMls6VyWTIj8SmX0k5EbqGvnIfET+J2pYEuKQnDIHxy+syRMoRM6AHXdHYGIg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/plugin-google-gtag@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-rc.1.tgz#61698fdc41a4ace912fb8f6c834efd288edad3c0"
|
||||
integrity sha512-yE1Et9hhhX9qMRnMJzpNq0854qIYiSEc2dZaXNk537HN7Q0rKkr/YONUHz2iqNYwPX2hGOY4LdpTxlMP88uVhA==
|
||||
"@docusaurus/plugin-google-gtag@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.2.0.tgz#7b086d169ac5fe9a88aca10ab0fd2bf00c6c6b12"
|
||||
integrity sha512-6SOgczP/dYdkqUMGTRqgxAS1eTp6MnJDAQMy8VCF1QKbWZmlkx4agHDexihqmYyCujTYHqDAhm1hV26EET54NQ==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/plugin-sitemap@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.1.tgz#0b638e774b253d90e9f2d11663e961250f557bc4"
|
||||
integrity sha512-5JmbNpssUF03odFM4ArvIsrO9bv7HnAJ0VtefXhh0WBpaFs8NgI3rTkCTFimvtRQjDR9U2bh23fXz2vjQQz6oA==
|
||||
"@docusaurus/plugin-sitemap@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.2.0.tgz#876da60937886032d63143253d420db6a4b34773"
|
||||
integrity sha512-0jAmyRDN/aI265CbWZNZuQpFqiZuo+5otk2MylU9iVrz/4J7gSc+ZJ9cy4EHrEsW7PV8s1w18hIEsmcA1YgkKg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-common" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-common" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
fs-extra "^10.1.0"
|
||||
sitemap "^7.1.1"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/preset-classic@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-rc.1.tgz#5e5b1cf80b3dd4e2c3f824c78a111f105858d853"
|
||||
integrity sha512-5jjTVZkhArjyoNHwCI9x4PSG0zPmBJILjZLVrxPcHpm/K0ltkYcp6J3GxYpf5EbMuOh5+yCWM63cSshGcNOo3Q==
|
||||
"@docusaurus/preset-classic@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.2.0.tgz#bece5a043eeb74430f7c6c7510000b9c43669eb7"
|
||||
integrity sha512-yKIWPGNx7BT8v2wjFIWvYrS+nvN04W+UameSFf8lEiJk6pss0kL6SG2MRvyULiI3BDxH+tj6qe02ncpSPGwumg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-blog" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-docs" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-pages" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-debug" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-google-analytics" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-google-gtag" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-sitemap" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-classic" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-common" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-search-algolia" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/plugin-content-blog" "2.2.0"
|
||||
"@docusaurus/plugin-content-docs" "2.2.0"
|
||||
"@docusaurus/plugin-content-pages" "2.2.0"
|
||||
"@docusaurus/plugin-debug" "2.2.0"
|
||||
"@docusaurus/plugin-google-analytics" "2.2.0"
|
||||
"@docusaurus/plugin-google-gtag" "2.2.0"
|
||||
"@docusaurus/plugin-sitemap" "2.2.0"
|
||||
"@docusaurus/theme-classic" "2.2.0"
|
||||
"@docusaurus/theme-common" "2.2.0"
|
||||
"@docusaurus/theme-search-algolia" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
|
||||
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
version "5.5.2"
|
||||
@ -1454,23 +1468,23 @@
|
||||
"@types/react" "*"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
"@docusaurus/theme-classic@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-rc.1.tgz#4ab30745e6b03b0f277837debae786a0a83aee6a"
|
||||
integrity sha512-qNiz7ieeq3AC+V8TbW6S63pWLJph1CbzWDDPTqxDLHgA8VQaNaSmJM8S92pH+yKALRb9u14ogjjYYc75Nj2JmQ==
|
||||
"@docusaurus/theme-classic@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.2.0.tgz#a048bb1bc077dee74b28bec25f4b84b481863742"
|
||||
integrity sha512-kjbg/qJPwZ6H1CU/i9d4l/LcFgnuzeiGgMQlt6yPqKo0SOJIBMPuz7Rnu3r/WWbZFPi//o8acclacOzmXdUUEg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/module-type-aliases" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-blog" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-docs" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-pages" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-common" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-translations" "2.0.0-rc.1"
|
||||
"@docusaurus/types" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-common" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/module-type-aliases" "2.2.0"
|
||||
"@docusaurus/plugin-content-blog" "2.2.0"
|
||||
"@docusaurus/plugin-content-docs" "2.2.0"
|
||||
"@docusaurus/plugin-content-pages" "2.2.0"
|
||||
"@docusaurus/theme-common" "2.2.0"
|
||||
"@docusaurus/theme-translations" "2.2.0"
|
||||
"@docusaurus/types" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-common" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
"@mdx-js/react" "^1.6.22"
|
||||
clsx "^1.2.1"
|
||||
copy-text-to-clipboard "^3.0.1"
|
||||
@ -1485,17 +1499,17 @@
|
||||
tslib "^2.4.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-common@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-rc.1.tgz#ea5d9732a16b03b488555e50107161bfa2abad98"
|
||||
integrity sha512-1r9ZLKD9SeoCYVzWzcdR79Dia4ANlrlRjNl6uzETOEybjK6FF7yEa9Yra8EJcOCbi3coyYz5xFh/r1YHFTFHug==
|
||||
"@docusaurus/theme-common@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.2.0.tgz#2303498d80448aafdd588b597ce9d6f4cfa930e4"
|
||||
integrity sha512-R8BnDjYoN90DCL75gP7qYQfSjyitXuP9TdzgsKDmSFPNyrdE3twtPNa2dIN+h+p/pr+PagfxwWbd6dn722A1Dw==
|
||||
dependencies:
|
||||
"@docusaurus/mdx-loader" "2.0.0-rc.1"
|
||||
"@docusaurus/module-type-aliases" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-blog" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-docs" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-pages" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/mdx-loader" "2.2.0"
|
||||
"@docusaurus/module-type-aliases" "2.2.0"
|
||||
"@docusaurus/plugin-content-blog" "2.2.0"
|
||||
"@docusaurus/plugin-content-docs" "2.2.0"
|
||||
"@docusaurus/plugin-content-pages" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router-config" "*"
|
||||
@ -1505,19 +1519,34 @@
|
||||
tslib "^2.4.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-search-algolia@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-rc.1.tgz#e78c0aeaea6a3717ae3a6ecd75a8652bd7c8e974"
|
||||
integrity sha512-H5yq6V/B4qo6GZrDKMbeSpk3T9e9K2MliDzLonRu0w3QHW9orVGe0c/lZvRbGlDZjnsOo7XGddhXXIDWGwnpaA==
|
||||
"@docusaurus/theme-live-codeblock@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-2.2.0.tgz#a507e496a1a74d261beee30ad072e4341310809a"
|
||||
integrity sha512-4XRFxfZGcyqmbLmNbnbZ2ZOsoY7FYCJUZKsYW5yzhZYjmjGg7lkdJH5trt9otUoKBsZopBpPWvcDZwCu1SENYg==
|
||||
dependencies:
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/theme-common" "2.2.0"
|
||||
"@docusaurus/theme-translations" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
"@philpl/buble" "^0.19.7"
|
||||
clsx "^1.2.1"
|
||||
fs-extra "^10.1.0"
|
||||
react-live "2.2.3"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/theme-search-algolia@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.2.0.tgz#77fd9f7a600917e6024fe3ac7fb6cfdf2ce84737"
|
||||
integrity sha512-2h38B0tqlxgR2FZ9LpAkGrpDWVdXZ7vltfmTdX+4RsDs3A7khiNsmZB+x/x6sA4+G2V2CvrsPMlsYBy5X+cY1w==
|
||||
dependencies:
|
||||
"@docsearch/react" "^3.1.1"
|
||||
"@docusaurus/core" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/plugin-content-docs" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-common" "2.0.0-rc.1"
|
||||
"@docusaurus/theme-translations" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/utils-validation" "2.0.0-rc.1"
|
||||
"@docusaurus/core" "2.2.0"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/plugin-content-docs" "2.2.0"
|
||||
"@docusaurus/theme-common" "2.2.0"
|
||||
"@docusaurus/theme-translations" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
"@docusaurus/utils-validation" "2.2.0"
|
||||
algoliasearch "^4.13.1"
|
||||
algoliasearch-helper "^3.10.0"
|
||||
clsx "^1.2.1"
|
||||
@ -1527,10 +1556,10 @@
|
||||
tslib "^2.4.0"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@docusaurus/theme-translations@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-rc.1.tgz#bd647f78c741ee7f6c6d2cbbd3e3f282ef2f89ad"
|
||||
integrity sha512-JLhNdlnbQhxVQzOnLyiCaTzKFa1lpVrM3nCrkGQKscoG2rY6ARGYMgMN2DkoH6hm7TflQ8+PE1S5MzzASeLs4Q==
|
||||
"@docusaurus/theme-translations@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.2.0.tgz#5fbd4693679806f80c26eeae1381e1f2c23d83e7"
|
||||
integrity sha512-3T140AG11OjJrtKlY4pMZ5BzbGRDjNs2co5hJ6uYJG1bVWlhcaFGqkaZ5lCgKflaNHD7UHBHU9Ec5f69jTdd6w==
|
||||
dependencies:
|
||||
fs-extra "^10.1.0"
|
||||
tslib "^2.4.0"
|
||||
@ -1549,30 +1578,44 @@
|
||||
webpack "^5.73.0"
|
||||
webpack-merge "^5.8.0"
|
||||
|
||||
"@docusaurus/utils-common@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-rc.1.tgz#3e233a28794325d5d9d3af3f7b1c22b59aa8b847"
|
||||
integrity sha512-+iZICpeFPZJ9oGJXuG92WTWee6WRnVx5BdzlcfuKf/f5KQX8PvwXR2tDME78FGGhShB8zr+vjuNEXuLvXT7j2A==
|
||||
"@docusaurus/types@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.2.0.tgz#02c577a4041ab7d058a3c214ccb13647e21a9857"
|
||||
integrity sha512-b6xxyoexfbRNRI8gjblzVOnLr4peCJhGbYGPpJ3LFqpi5nsFfoK4mmDLvWdeah0B7gmJeXabN7nQkFoqeSdmOw==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
commander "^5.1.0"
|
||||
joi "^17.6.0"
|
||||
react-helmet-async "^1.3.0"
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.73.0"
|
||||
webpack-merge "^5.8.0"
|
||||
|
||||
"@docusaurus/utils-common@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.2.0.tgz#a401c1b93a8697dd566baf6ac64f0fdff1641a78"
|
||||
integrity sha512-qebnerHp+cyovdUseDQyYFvMW1n1nv61zGe5JJfoNQUnjKuApch3IVsz+/lZ9a38pId8kqehC1Ao2bW/s0ntDA==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/utils-validation@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-rc.1.tgz#dded12f036cda8a54a19e01694b35859fe0cf1d5"
|
||||
integrity sha512-lj36gm9Ksu4tt/EUeLDWoMbXe3sfBxeIPIUUdqYcBYkF/rpQkh+uL/dncjNGiw6uvBOqXhOfsFVP045HtgShVw==
|
||||
"@docusaurus/utils-validation@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.2.0.tgz#04d4d103137ad0145883971d3aa497f4a1315f25"
|
||||
integrity sha512-I1hcsG3yoCkasOL5qQAYAfnmVoLei7apugT6m4crQjmDGxq+UkiRrq55UqmDDyZlac/6ax/JC0p+usZ6W4nVyg==
|
||||
dependencies:
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/utils" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@docusaurus/utils" "2.2.0"
|
||||
joi "^17.6.0"
|
||||
js-yaml "^4.1.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@docusaurus/utils@2.0.0-rc.1":
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-rc.1.tgz#53584b800df9e13864d5ef1a76aa7655a90ec86e"
|
||||
integrity sha512-ym9I1OwIYbKs1LGaUajaA/vDG8VweJj/6YoZjHp+eDQHhTRIrHXiYoGDqorafRhftKwnA1EnyomuXpNd9bq8Gg==
|
||||
"@docusaurus/utils@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.2.0.tgz#3d6f9b7a69168d5c92d371bf21c556a4f50d1da6"
|
||||
integrity sha512-oNk3cjvx7Tt1Lgh/aeZAmFpGV2pDr5nHKrBVx6hTkzGhrnMuQqLt6UPlQjdYQ3QHXwyF/ZtZMO1D5Pfi0lu7SA==
|
||||
dependencies:
|
||||
"@docusaurus/logger" "2.0.0-rc.1"
|
||||
"@docusaurus/logger" "2.2.0"
|
||||
"@svgr/webpack" "^6.2.1"
|
||||
file-loader "^6.2.0"
|
||||
fs-extra "^10.1.0"
|
||||
@ -1588,6 +1631,11 @@
|
||||
url-loader "^4.1.1"
|
||||
webpack "^5.73.0"
|
||||
|
||||
"@excalidraw/excalidraw@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.14.2.tgz#150cb4b7a1bf0d11cd64295936c930e7e0db8375"
|
||||
integrity sha512-8LdjpTBWEK5waDWB7Bt/G9YBI4j0OxkstUhvaDGz7dwQGfzF6FW5CXBoYHNEoX0qmb+Fg/NPOlZ7FrKsrSVCqg==
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
@ -1709,6 +1757,21 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@philpl/buble@^0.19.7":
|
||||
version "0.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@philpl/buble/-/buble-0.19.7.tgz#27231e6391393793b64bc1c982fc7b593198b893"
|
||||
integrity sha512-wKTA2DxAGEW+QffRQvOhRQ0VBiYU2h2p8Yc1oBNlqSKws48/8faxqKNIuub0q4iuyTuLwtB8EkwiKwhlfV1PBA==
|
||||
dependencies:
|
||||
acorn "^6.1.1"
|
||||
acorn-class-fields "^0.2.1"
|
||||
acorn-dynamic-import "^4.0.0"
|
||||
acorn-jsx "^5.0.1"
|
||||
chalk "^2.4.2"
|
||||
magic-string "^0.25.2"
|
||||
minimist "^1.2.0"
|
||||
os-homedir "^1.0.1"
|
||||
regexpu-core "^4.5.4"
|
||||
|
||||
"@polka/url@^1.0.0-next.20":
|
||||
version "1.0.0-next.21"
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||
@ -2242,16 +2305,36 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
|
||||
mime-types "~2.1.34"
|
||||
negotiator "0.6.3"
|
||||
|
||||
acorn-class-fields@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn-class-fields/-/acorn-class-fields-0.2.1.tgz#748058bceeb0ef25164bbc671993984083f5a085"
|
||||
integrity sha512-US/kqTe0H8M4LN9izoL+eykVAitE68YMuYZ3sHn3i1fjniqR7oQ3SPvuMK/VT1kjOQHrx5Q88b90TtOKgAv2hQ==
|
||||
|
||||
acorn-dynamic-import@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
|
||||
integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==
|
||||
|
||||
acorn-import-assertions@^1.7.6:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
|
||||
integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
|
||||
|
||||
acorn-jsx@^5.0.1:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn-walk@^8.0.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||
|
||||
acorn@^6.1.1:
|
||||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
|
||||
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
|
||||
|
||||
acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1:
|
||||
version "8.8.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
|
||||
@ -2618,6 +2701,18 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4
|
||||
node-releases "^2.0.6"
|
||||
update-browserslist-db "^1.0.4"
|
||||
|
||||
buble@0.19.6:
|
||||
version "0.19.6"
|
||||
resolved "https://registry.yarnpkg.com/buble/-/buble-0.19.6.tgz#915909b6bd5b11ee03b1c885ec914a8b974d34d3"
|
||||
integrity sha512-9kViM6nJA1Q548Jrd06x0geh+BG2ru2+RMDkIHHgJY/8AcyCs34lTHwra9BX7YdPrZXd5aarkpr/SY8bmPgPdg==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
magic-string "^0.25.1"
|
||||
minimist "^1.2.0"
|
||||
os-homedir "^1.0.1"
|
||||
regexpu-core "^4.2.0"
|
||||
vlq "^1.0.0"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
@ -2697,7 +2792,7 @@ ccount@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043"
|
||||
integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==
|
||||
|
||||
chalk@^2.0.0:
|
||||
chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
@ -2754,7 +2849,7 @@ cheerio@^1.0.0-rc.12:
|
||||
parse5 "^7.0.0"
|
||||
parse5-htmlparser2-tree-adapter "^7.0.0"
|
||||
|
||||
chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
@ -2905,6 +3000,16 @@ commondir@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
||||
|
||||
component-props@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/component-props/-/component-props-1.1.1.tgz#f9b7df9b9927b6e6d97c9bd272aa867670f34944"
|
||||
integrity sha512-69pIRJs9fCCHRqCz3390YF2LV1Lu6iEMZ5zuVqqUn+G20V9BNXlMs0cWawWeW9g4Ynmg29JmkG6R7/lUJoGd1Q==
|
||||
|
||||
component-xor@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/component-xor/-/component-xor-0.0.4.tgz#c55d83ccc1b94cd5089a4e93fa7891c7263e59aa"
|
||||
integrity sha512-ZIt6sla8gfo+AFVRZoZOertcnD5LJaY2T9CKE2j13NJxQt/mUafD69Bl7/Y4AnpI2LGjiXH7cOfJDx/n2G9edA==
|
||||
|
||||
compressible@~2.0.16:
|
||||
version "2.0.18"
|
||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
||||
@ -3016,6 +3121,11 @@ core-js-pure@^3.20.2:
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.0.tgz#10eeb90dbf0d670a6b22b081aecc7deb2faec7e1"
|
||||
integrity sha512-uzMmW8cRh7uYw4JQtzqvGWRyC2T5+4zipQLQdi2FmiRqP83k3d6F3stv2iAlNhOs6cXN401FCD5TL0vvleuHgA==
|
||||
|
||||
core-js@^2.4.1:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
core-js@^3.23.3:
|
||||
version "3.24.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.0.tgz#4928d4e99c593a234eb1a1f9abd3122b04d3ac57"
|
||||
@ -3345,6 +3455,13 @@ dns-packet@^5.2.2:
|
||||
dependencies:
|
||||
"@leichtgewicht/ip-codec" "^2.0.1"
|
||||
|
||||
docusaurus-plugin-sass@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/docusaurus-plugin-sass/-/docusaurus-plugin-sass-0.2.3.tgz#5b61f7e560d236cfc1531ed497ac32fc166fc5e2"
|
||||
integrity sha512-FbaE06K8NF8SPUYTwiG+83/jkXrwHJ/Afjqz3SUIGon6QvFwSSoKOcoxGQmUBnjTOk+deUONDx8jNWsegFJcBQ==
|
||||
dependencies:
|
||||
sass-loader "^10.1.1"
|
||||
|
||||
dom-converter@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
|
||||
@ -3352,6 +3469,14 @@ dom-converter@^0.2.0:
|
||||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-iterator@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-iterator/-/dom-iterator-1.0.0.tgz#9c09899846ec41c2d257adc4d6015e4759ef05ad"
|
||||
integrity sha512-7dsMOQI07EMU98gQM8NSB3GsAiIeBYIPKpnxR3c9xOvdvBjChAcOM0iJ222I3p5xyiZO9e5oggkNaCusuTdYig==
|
||||
dependencies:
|
||||
component-props "1.1.1"
|
||||
component-xor "0.0.4"
|
||||
|
||||
dom-serializer@^1.0.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
|
||||
@ -4340,6 +4465,11 @@ immer@^9.0.7:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
|
||||
integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.2.tgz#2da9ff4384a4330c36d4d1bc88e90f9e0b0ccd16"
|
||||
integrity sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==
|
||||
|
||||
import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||
@ -4722,7 +4852,7 @@ kleur@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
klona@^2.0.5:
|
||||
klona@^2.0.4, klona@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
|
||||
integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
|
||||
@ -4851,6 +4981,13 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
magic-string@^0.25.1, magic-string@^0.25.2:
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
|
||||
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||
@ -5208,6 +5345,11 @@ opener@^1.5.2:
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
||||
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
|
||||
|
||||
os-homedir@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==
|
||||
|
||||
p-cancelable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
@ -5737,7 +5879,7 @@ pretty-time@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e"
|
||||
integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==
|
||||
|
||||
prism-react-renderer@^1.3.5:
|
||||
prism-react-renderer@^1.0.1, prism-react-renderer@^1.3.5:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085"
|
||||
integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==
|
||||
@ -5767,7 +5909,7 @@ prompts@^2.4.2:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
@ -5967,6 +6109,19 @@ react-lifecycles-compat@^3.0.4:
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-live@2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-live/-/react-live-2.2.3.tgz#260f99194213799f0005e473e7a4154c699d6a7c"
|
||||
integrity sha512-tpKruvfytNETuzO3o1mrQUj180GVrq35IE8F5gH1NJVPt4szYCx83/dOSCOyjgRhhc3gQvl0pQ3k/CjOjwJkKQ==
|
||||
dependencies:
|
||||
buble "0.19.6"
|
||||
core-js "^2.4.1"
|
||||
dom-iterator "^1.0.0"
|
||||
prism-react-renderer "^1.0.1"
|
||||
prop-types "^15.5.8"
|
||||
react-simple-code-editor "^0.10.0"
|
||||
unescape "^1.0.1"
|
||||
|
||||
react-loadable-ssr-addon-v5-slorber@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883"
|
||||
@ -6010,6 +6165,11 @@ react-router@5.3.3, react-router@^5.3.3:
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
|
||||
react-simple-code-editor@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.10.0.tgz#73e7ac550a928069715482aeb33ccba36efe2373"
|
||||
integrity sha512-bL5W5mAxSW6+cLwqqVWY47Silqgy2DKDTR4hDBrLrUqC5BXc29YVx17l2IZk5v36VcDEq1Bszu2oHm1qBwKqBA==
|
||||
|
||||
react-textarea-autosize@^8.3.2:
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz#270a343de7ad350534141b02c9cb78903e553524"
|
||||
@ -6082,6 +6242,13 @@ regenerate-unicode-properties@^10.0.1:
|
||||
dependencies:
|
||||
regenerate "^1.4.2"
|
||||
|
||||
regenerate-unicode-properties@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326"
|
||||
integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==
|
||||
dependencies:
|
||||
regenerate "^1.4.2"
|
||||
|
||||
regenerate@^1.4.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
||||
@ -6099,6 +6266,18 @@ regenerator-transform@^0.15.0:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.4"
|
||||
|
||||
regexpu-core@^4.2.0, regexpu-core@^4.5.4:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0"
|
||||
integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==
|
||||
dependencies:
|
||||
regenerate "^1.4.2"
|
||||
regenerate-unicode-properties "^9.0.0"
|
||||
regjsgen "^0.5.2"
|
||||
regjsparser "^0.7.0"
|
||||
unicode-match-property-ecmascript "^2.0.0"
|
||||
unicode-match-property-value-ecmascript "^2.0.0"
|
||||
|
||||
regexpu-core@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d"
|
||||
@ -6125,11 +6304,23 @@ registry-url@^5.0.0:
|
||||
dependencies:
|
||||
rc "^1.2.8"
|
||||
|
||||
regjsgen@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733"
|
||||
integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==
|
||||
|
||||
regjsgen@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d"
|
||||
integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==
|
||||
|
||||
regjsparser@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968"
|
||||
integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==
|
||||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
regjsparser@^0.8.2:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f"
|
||||
@ -6317,6 +6508,26 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sass-loader@^10.1.1:
|
||||
version "10.4.1"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.4.1.tgz#bea4e173ddf512c9d7f53e9ec686186146807cbf"
|
||||
integrity sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==
|
||||
dependencies:
|
||||
klona "^2.0.4"
|
||||
loader-utils "^2.0.0"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
sass@1.57.1:
|
||||
version "1.57.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.57.1.tgz#dfafd46eb3ab94817145e8825208ecf7281119b5"
|
||||
integrity sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
|
||||
sax@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
@ -6594,7 +6805,7 @@ sort-css-media-queries@2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908"
|
||||
integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
@ -6617,6 +6828,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
sourcemap-codec@^1.4.8:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
space-separated-tokens@^1.0.0:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
|
||||
@ -6947,6 +7163,13 @@ ua-parser-js@^0.7.30:
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
|
||||
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
|
||||
|
||||
unescape@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96"
|
||||
integrity sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==
|
||||
dependencies:
|
||||
extend-shallow "^2.0.1"
|
||||
|
||||
unherit@^1.0.4:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22"
|
||||
@ -7203,6 +7426,11 @@ vfile@^4.0.0:
|
||||
unist-util-stringify-position "^2.0.0"
|
||||
vfile-message "^2.0.0"
|
||||
|
||||
vlq@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468"
|
||||
integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==
|
||||
|
||||
wait-on@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e"
|
||||
|
@ -154,7 +154,9 @@ export const actionDeleteSelected = register({
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.delete",
|
||||
keyTest: (event) => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE,
|
||||
keyTest: (event, appState, elements) =>
|
||||
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) &&
|
||||
!event[KEYS.CTRL_OR_CMD],
|
||||
PanelComponent: ({ elements, appState, updateData }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
|
@ -16,8 +16,12 @@ import { AppState } from "../types";
|
||||
import { fixBindingsAfterDuplication } from "../element/binding";
|
||||
import { ActionResult } from "./types";
|
||||
import { GRID_SIZE } from "../constants";
|
||||
import { bindTextToShapeAfterDuplication } from "../element/textElement";
|
||||
import {
|
||||
bindTextToShapeAfterDuplication,
|
||||
getBoundTextElement,
|
||||
} from "../element/textElement";
|
||||
import { isBoundToContainer } from "../element/typeChecks";
|
||||
import { normalizeElementOrder } from "../element/sortElements";
|
||||
import { DuplicateIcon } from "../components/icons";
|
||||
|
||||
export const actionDuplicateSelection = register({
|
||||
@ -64,6 +68,11 @@ const duplicateElements = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
): Partial<ActionResult> => {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// step (1)
|
||||
|
||||
const sortedElements = normalizeElementOrder(elements);
|
||||
const groupIdMap = new Map();
|
||||
const newElements: ExcalidrawElement[] = [];
|
||||
const oldElements: ExcalidrawElement[] = [];
|
||||
@ -85,42 +94,112 @@ const duplicateElements = (
|
||||
return newElement;
|
||||
};
|
||||
|
||||
const finalElements: ExcalidrawElement[] = [];
|
||||
|
||||
let index = 0;
|
||||
const selectedElementIds = arrayToMap(
|
||||
getSelectedElements(elements, appState, true),
|
||||
getSelectedElements(sortedElements, appState, true),
|
||||
);
|
||||
while (index < elements.length) {
|
||||
const element = elements[index];
|
||||
|
||||
// Ids of elements that have already been processed so we don't push them
|
||||
// into the array twice if we end up backtracking when retrieving
|
||||
// discontiguous group of elements (can happen due to a bug, or in edge
|
||||
// cases such as a group containing deleted elements which were not selected).
|
||||
//
|
||||
// This is not enough to prevent duplicates, so we do a second loop afterwards
|
||||
// to remove them.
|
||||
//
|
||||
// For convenience we mark even the newly created ones even though we don't
|
||||
// loop over them.
|
||||
const processedIds = new Map<ExcalidrawElement["id"], true>();
|
||||
|
||||
const markAsProcessed = (elements: ExcalidrawElement[]) => {
|
||||
for (const element of elements) {
|
||||
processedIds.set(element.id, true);
|
||||
}
|
||||
return elements;
|
||||
};
|
||||
|
||||
const elementsWithClones: ExcalidrawElement[] = [];
|
||||
|
||||
let index = -1;
|
||||
|
||||
while (++index < sortedElements.length) {
|
||||
const element = sortedElements[index];
|
||||
|
||||
if (processedIds.get(element.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const boundTextElement = getBoundTextElement(element);
|
||||
if (selectedElementIds.get(element.id)) {
|
||||
if (element.groupIds.length) {
|
||||
// if a group or a container/bound-text, duplicate atomically
|
||||
if (element.groupIds.length || boundTextElement) {
|
||||
const groupId = getSelectedGroupForElement(appState, element);
|
||||
// if group selected, duplicate it atomically
|
||||
if (groupId) {
|
||||
const groupElements = getElementsInGroup(elements, groupId);
|
||||
finalElements.push(
|
||||
...groupElements,
|
||||
...groupElements.map((element) =>
|
||||
duplicateAndOffsetElement(element),
|
||||
),
|
||||
const groupElements = getElementsInGroup(sortedElements, groupId);
|
||||
elementsWithClones.push(
|
||||
...markAsProcessed([
|
||||
...groupElements,
|
||||
...groupElements.map((element) =>
|
||||
duplicateAndOffsetElement(element),
|
||||
),
|
||||
]),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (boundTextElement) {
|
||||
elementsWithClones.push(
|
||||
...markAsProcessed([
|
||||
element,
|
||||
boundTextElement,
|
||||
duplicateAndOffsetElement(element),
|
||||
duplicateAndOffsetElement(boundTextElement),
|
||||
]),
|
||||
);
|
||||
index = index + groupElements.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
finalElements.push(element, duplicateAndOffsetElement(element));
|
||||
elementsWithClones.push(
|
||||
...markAsProcessed([element, duplicateAndOffsetElement(element)]),
|
||||
);
|
||||
} else {
|
||||
finalElements.push(element);
|
||||
elementsWithClones.push(...markAsProcessed([element]));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// step (2)
|
||||
|
||||
// second pass to remove duplicates. We loop from the end as it's likelier
|
||||
// that the last elements are in the correct order (contiguous or otherwise).
|
||||
// Thus we need to reverse as the last step (3).
|
||||
|
||||
const finalElementsReversed: ExcalidrawElement[] = [];
|
||||
|
||||
const finalElementIds = new Map<ExcalidrawElement["id"], true>();
|
||||
index = elementsWithClones.length;
|
||||
|
||||
while (--index >= 0) {
|
||||
const element = elementsWithClones[index];
|
||||
if (!finalElementIds.get(element.id)) {
|
||||
finalElementIds.set(element.id, true);
|
||||
finalElementsReversed.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
// step (3)
|
||||
|
||||
const finalElements = finalElementsReversed.reverse();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bindTextToShapeAfterDuplication(
|
||||
finalElements,
|
||||
elementsWithClones,
|
||||
oldElements,
|
||||
oldIdToDuplicatedId,
|
||||
);
|
||||
fixBindingsAfterDuplication(
|
||||
elementsWithClones,
|
||||
oldElements,
|
||||
oldIdToDuplicatedId,
|
||||
);
|
||||
fixBindingsAfterDuplication(finalElements, oldElements, oldIdToDuplicatedId);
|
||||
|
||||
return {
|
||||
elements: finalElements,
|
||||
|
@ -8,6 +8,7 @@ export type ShortcutName =
|
||||
ActionName,
|
||||
| "toggleTheme"
|
||||
| "loadScene"
|
||||
| "clearCanvas"
|
||||
| "cut"
|
||||
| "copy"
|
||||
| "paste"
|
||||
@ -41,6 +42,7 @@ const shortcutMap: Record<ShortcutName, string[]> = {
|
||||
toggleTheme: [getShortcutKey("Shift+Alt+D")],
|
||||
saveScene: [getShortcutKey("CtrlOrCmd+S")],
|
||||
loadScene: [getShortcutKey("CtrlOrCmd+O")],
|
||||
clearCanvas: [getShortcutKey("CtrlOrCmd+Delete")],
|
||||
imageExport: [getShortcutKey("CtrlOrCmd+Shift+E")],
|
||||
cut: [getShortcutKey("CtrlOrCmd+X")],
|
||||
copy: [getShortcutKey("CtrlOrCmd+C")],
|
||||
|
@ -21,7 +21,7 @@ export const getClientColors = (clientId: string, appState: AppState) => {
|
||||
};
|
||||
|
||||
export const getClientInitials = (userName?: string | null) => {
|
||||
if (!userName) {
|
||||
if (!userName?.trim()) {
|
||||
return "?";
|
||||
}
|
||||
return userName.trim()[0].toUpperCase();
|
||||
|
@ -226,6 +226,7 @@ import {
|
||||
setEraserCursor,
|
||||
updateActiveTool,
|
||||
getShortcutKey,
|
||||
isTransparent,
|
||||
} from "../utils";
|
||||
import {
|
||||
ContextMenu,
|
||||
@ -264,6 +265,7 @@ import {
|
||||
getContainerCenter,
|
||||
getContainerDims,
|
||||
getTextBindableContainerAtPosition,
|
||||
isHittingContainerStroke,
|
||||
isValidTextContainer,
|
||||
} from "../element/textElement";
|
||||
import { isHittingElementNotConsideringBoundingBox } from "../element/collision";
|
||||
@ -279,6 +281,8 @@ import { shouldShowBoundingBox } from "../element/transformHandles";
|
||||
import { Fonts } from "../scene/Fonts";
|
||||
import { actionPaste } from "../actions/actionClipboard";
|
||||
import { actionToggleHandTool } from "../actions/actionCanvas";
|
||||
import { jotaiStore } from "../jotai";
|
||||
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
|
||||
|
||||
const deviceContextInitialValue = {
|
||||
isSmScreen: false,
|
||||
@ -834,7 +838,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
},
|
||||
};
|
||||
}
|
||||
const scene = restore(initialData, null, null);
|
||||
const scene = restore(initialData, null, null, { repairBindings: true });
|
||||
scene.appState = {
|
||||
...scene.appState,
|
||||
theme: this.props.theme || scene.appState.theme,
|
||||
@ -1952,7 +1956,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
);
|
||||
|
||||
// Input handling
|
||||
|
||||
private onKeyDown = withBatchedUpdates(
|
||||
(event: React.KeyboardEvent | KeyboardEvent) => {
|
||||
// normalize `event.key` when CapsLock is pressed #2372
|
||||
@ -2194,6 +2197,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
event[KEYS.CTRL_OR_CMD] &&
|
||||
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
|
||||
) {
|
||||
jotaiStore.set(activeConfirmDialogAtom, "clearCanvas");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -2754,7 +2764,19 @@ class App extends React.Component<AppProps, AppState> {
|
||||
sceneY,
|
||||
);
|
||||
if (container) {
|
||||
if (isArrowElement(container) || hasBoundTextElement(container)) {
|
||||
if (
|
||||
isArrowElement(container) ||
|
||||
hasBoundTextElement(container) ||
|
||||
!isTransparent(
|
||||
(container as ExcalidrawTextContainer).backgroundColor,
|
||||
) ||
|
||||
isHittingContainerStroke(
|
||||
sceneX,
|
||||
sceneY,
|
||||
container,
|
||||
this.state.zoom.value,
|
||||
)
|
||||
) {
|
||||
const midPoint = getContainerCenter(container, this.state);
|
||||
|
||||
sceneX = midPoint.x;
|
||||
|
@ -273,22 +273,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
className="HelpDialog__island--editor"
|
||||
caption={t("helpDialog.editor")}
|
||||
>
|
||||
<Shortcut
|
||||
label={t("labels.selectAll")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+A")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.multiSelect")}
|
||||
shortcuts={[getShortcutKey(`Shift+${t("helpDialog.click")}`)]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("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={[
|
||||
@ -297,6 +281,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
]}
|
||||
isOr={true}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("buttons.clearReset")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+Delete")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.delete")}
|
||||
shortcuts={[getShortcutKey("Delete")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.cut")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+X")]}
|
||||
@ -313,6 +305,22 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
label={t("labels.pasteAsPlaintext")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+V")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.selectAll")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+A")]}
|
||||
/>
|
||||
<Shortcut
|
||||
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")}`)]}
|
||||
/>
|
||||
{/* firefox supports clipboard API under a flag, so we'll
|
||||
show users what they can do in the error message */}
|
||||
{(probablySupportsClipboardBlob || isFirefox) && (
|
||||
@ -329,10 +337,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
label={t("labels.pasteStyles")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+V")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.delete")}
|
||||
shortcuts={[getShortcutKey("Delete")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.sendToBack")}
|
||||
shortcuts={[
|
||||
|
@ -124,7 +124,6 @@ const LayerUI = ({
|
||||
children,
|
||||
}: LayerUIProps) => {
|
||||
const device = useDevice();
|
||||
|
||||
const tunnels = useInitializeTunnels();
|
||||
|
||||
const renderJSONExportDialog = () => {
|
||||
@ -409,6 +408,7 @@ const LayerUI = ({
|
||||
renderCustomStats={renderCustomStats}
|
||||
renderSidebars={renderSidebars}
|
||||
device={device}
|
||||
renderWelcomeScreen={renderWelcomeScreen}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -41,6 +41,7 @@ type MobileMenuProps = {
|
||||
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
|
||||
renderSidebars: () => JSX.Element | null;
|
||||
device: Device;
|
||||
renderWelcomeScreen: boolean;
|
||||
};
|
||||
|
||||
export const MobileMenu = ({
|
||||
@ -57,12 +58,13 @@ export const MobileMenu = ({
|
||||
renderCustomStats,
|
||||
renderSidebars,
|
||||
device,
|
||||
renderWelcomeScreen,
|
||||
}: MobileMenuProps) => {
|
||||
const { welcomeScreenCenterTunnel, mainMenuTunnel } = useTunnels();
|
||||
const renderToolbar = () => {
|
||||
return (
|
||||
<FixedSideContainer side="top" className="App-top-bar">
|
||||
<welcomeScreenCenterTunnel.Out />
|
||||
{renderWelcomeScreen && <welcomeScreenCenterTunnel.Out />}
|
||||
<Section heading="shapes">
|
||||
{(heading: React.ReactNode) => (
|
||||
<Stack.Col gap={4} align="center">
|
||||
|
@ -95,6 +95,9 @@
|
||||
--color-gray-90: #1e1e1e;
|
||||
--color-gray-100: #121212;
|
||||
|
||||
--color-warning: #fceeca;
|
||||
--color-text-warning: var(--text-primary-color);
|
||||
|
||||
--color-danger: #db6965;
|
||||
--color-promo: #e70078;
|
||||
|
||||
@ -163,6 +166,8 @@
|
||||
--color-primary-darkest: #beb9ff;
|
||||
--color-primary-light: #4f4d6f;
|
||||
|
||||
--color-text-warning: var(--color-gray-80);
|
||||
|
||||
--color-danger: #ffa8a5;
|
||||
--color-promo: #d297ff;
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ export const loadSceneOrLibraryFromBlob = async (
|
||||
},
|
||||
localAppState,
|
||||
localElements,
|
||||
{ repairBindings: true },
|
||||
),
|
||||
};
|
||||
} else if (isValidLibrary(data)) {
|
||||
|
@ -339,7 +339,7 @@ export const restoreElements = (
|
||||
elements: ImportedDataState["elements"],
|
||||
/** NOTE doesn't serve for reconciliation */
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
refreshDimensions = false,
|
||||
opts?: { refreshDimensions?: boolean; repairBindings?: boolean } | undefined,
|
||||
): ExcalidrawElement[] => {
|
||||
const localElementsMap = localElements ? arrayToMap(localElements) : null;
|
||||
const restoredElements = (elements || []).reduce((elements, element) => {
|
||||
@ -348,7 +348,7 @@ export const restoreElements = (
|
||||
if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
|
||||
let migratedElement: ExcalidrawElement | null = restoreElement(
|
||||
element,
|
||||
refreshDimensions,
|
||||
opts?.refreshDimensions,
|
||||
);
|
||||
if (migratedElement) {
|
||||
const localElement = localElementsMap?.get(element.id);
|
||||
@ -361,6 +361,10 @@ export const restoreElements = (
|
||||
return elements;
|
||||
}, [] as ExcalidrawElement[]);
|
||||
|
||||
if (!opts?.repairBindings) {
|
||||
return restoredElements;
|
||||
}
|
||||
|
||||
// repair binding. Mutates elements.
|
||||
const restoredElementsMap = arrayToMap(restoredElements);
|
||||
for (const element of restoredElements) {
|
||||
@ -497,9 +501,10 @@ export const restore = (
|
||||
*/
|
||||
localAppState: Partial<AppState> | null | undefined,
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
elementsConfig?: { refreshDimensions?: boolean; repairBindings?: boolean },
|
||||
): RestoredDataState => {
|
||||
return {
|
||||
elements: restoreElements(data?.elements, localElements),
|
||||
elements: restoreElements(data?.elements, localElements, elementsConfig),
|
||||
appState: restoreAppState(data?.appState, localAppState || null),
|
||||
files: data?.files || {},
|
||||
};
|
||||
|
402
src/element/sortElements.test.ts
Normal file
@ -0,0 +1,402 @@
|
||||
import { API } from "../tests/helpers/api";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
import { normalizeElementOrder } from "./sortElements";
|
||||
import { ExcalidrawElement } from "./types";
|
||||
|
||||
const assertOrder = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
expectedOrder: string[],
|
||||
) => {
|
||||
const actualOrder = elements.map((element) => element.id);
|
||||
expect(actualOrder).toEqual(expectedOrder);
|
||||
};
|
||||
|
||||
describe("normalizeElementsOrder", () => {
|
||||
it("sort bound-text elements", () => {
|
||||
const container = API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
});
|
||||
const boundText = API.createElement({
|
||||
id: "boundText",
|
||||
type: "text",
|
||||
containerId: container.id,
|
||||
});
|
||||
const otherElement = API.createElement({
|
||||
id: "otherElement",
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
});
|
||||
const otherElement2 = API.createElement({
|
||||
id: "otherElement2",
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
});
|
||||
|
||||
mutateElement(container, {
|
||||
boundElements: [{ type: "text", id: boundText.id }],
|
||||
});
|
||||
|
||||
assertOrder(normalizeElementOrder([container, boundText]), [
|
||||
"container",
|
||||
"boundText",
|
||||
]);
|
||||
assertOrder(normalizeElementOrder([boundText, container]), [
|
||||
"container",
|
||||
"boundText",
|
||||
]);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
boundText,
|
||||
container,
|
||||
otherElement,
|
||||
otherElement2,
|
||||
]),
|
||||
["container", "boundText", "otherElement", "otherElement2"],
|
||||
);
|
||||
assertOrder(normalizeElementOrder([container, otherElement, boundText]), [
|
||||
"container",
|
||||
"boundText",
|
||||
"otherElement",
|
||||
]);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
container,
|
||||
otherElement,
|
||||
otherElement2,
|
||||
boundText,
|
||||
]),
|
||||
["container", "boundText", "otherElement", "otherElement2"],
|
||||
);
|
||||
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
boundText,
|
||||
otherElement,
|
||||
container,
|
||||
otherElement2,
|
||||
]),
|
||||
["otherElement", "container", "boundText", "otherElement2"],
|
||||
);
|
||||
|
||||
// noop
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
otherElement,
|
||||
container,
|
||||
boundText,
|
||||
otherElement2,
|
||||
]),
|
||||
["otherElement", "container", "boundText", "otherElement2"],
|
||||
);
|
||||
|
||||
// text has existing containerId, but container doesn't list is
|
||||
// as a boundElement
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "boundText",
|
||||
type: "text",
|
||||
containerId: "container",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
}),
|
||||
]),
|
||||
["boundText", "container"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "boundText",
|
||||
type: "text",
|
||||
containerId: "container",
|
||||
}),
|
||||
]),
|
||||
["boundText"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
}),
|
||||
]),
|
||||
["container"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
boundElements: [{ id: "x", type: "text" }],
|
||||
}),
|
||||
]),
|
||||
["container"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "arrow",
|
||||
type: "arrow",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
boundElements: [{ id: "arrow", type: "arrow" }],
|
||||
}),
|
||||
]),
|
||||
["arrow", "container"],
|
||||
);
|
||||
});
|
||||
|
||||
it("normalize group order", () => {
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "A_rect1",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect2",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect3",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect4",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect5",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect6",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect7",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
]),
|
||||
["A_rect1", "A_rect4", "A_rect5", "A_rect7", "rect2", "rect3", "rect6"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "A_rect1",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect2",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "B_rect3",
|
||||
type: "rectangle",
|
||||
groupIds: ["B"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect4",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "B_rect5",
|
||||
type: "rectangle",
|
||||
groupIds: ["B"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect6",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect7",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
]),
|
||||
["A_rect1", "A_rect4", "A_rect7", "rect2", "B_rect3", "B_rect5", "rect6"],
|
||||
);
|
||||
// nested groups
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "A_rect1",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "BA_rect2",
|
||||
type: "rectangle",
|
||||
groupIds: ["B", "A"],
|
||||
}),
|
||||
]),
|
||||
["A_rect1", "BA_rect2"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "BA_rect1",
|
||||
type: "rectangle",
|
||||
groupIds: ["B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect2",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
]),
|
||||
["BA_rect1", "A_rect2"],
|
||||
);
|
||||
assertOrder(
|
||||
normalizeElementOrder([
|
||||
API.createElement({
|
||||
id: "BA_rect1",
|
||||
type: "rectangle",
|
||||
groupIds: ["B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect2",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "CBA_rect3",
|
||||
type: "rectangle",
|
||||
groupIds: ["C", "B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect4",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "A_rect5",
|
||||
type: "rectangle",
|
||||
groupIds: ["A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "BA_rect5",
|
||||
type: "rectangle",
|
||||
groupIds: ["B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "BA_rect6",
|
||||
type: "rectangle",
|
||||
groupIds: ["B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "CBA_rect7",
|
||||
type: "rectangle",
|
||||
groupIds: ["C", "B", "A"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "X_rect8",
|
||||
type: "rectangle",
|
||||
groupIds: ["X"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "rect9",
|
||||
type: "rectangle",
|
||||
}),
|
||||
API.createElement({
|
||||
id: "YX_rect10",
|
||||
type: "rectangle",
|
||||
groupIds: ["Y", "X"],
|
||||
}),
|
||||
API.createElement({
|
||||
id: "X_rect11",
|
||||
type: "rectangle",
|
||||
groupIds: ["X"],
|
||||
}),
|
||||
]),
|
||||
[
|
||||
"BA_rect1",
|
||||
"BA_rect5",
|
||||
"BA_rect6",
|
||||
"A_rect2",
|
||||
"A_rect5",
|
||||
"CBA_rect3",
|
||||
"CBA_rect7",
|
||||
"rect4",
|
||||
"X_rect8",
|
||||
"X_rect11",
|
||||
"YX_rect10",
|
||||
"rect9",
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
// TODO
|
||||
it.skip("normalize boundElements array", () => {
|
||||
const container = API.createElement({
|
||||
id: "container",
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
});
|
||||
const boundText = API.createElement({
|
||||
id: "boundText",
|
||||
type: "text",
|
||||
containerId: container.id,
|
||||
});
|
||||
|
||||
mutateElement(container, {
|
||||
boundElements: [
|
||||
{ type: "text", id: boundText.id },
|
||||
{ type: "text", id: "xxx" },
|
||||
],
|
||||
});
|
||||
|
||||
expect(normalizeElementOrder([container, boundText])).toEqual([
|
||||
expect.objectContaining({
|
||||
id: container.id,
|
||||
}),
|
||||
expect.objectContaining({ id: boundText.id }),
|
||||
]);
|
||||
});
|
||||
|
||||
// should take around <100ms for 10K iterations (@dwelle's PC 22-05-25)
|
||||
it.skip("normalizeElementsOrder() perf", () => {
|
||||
const makeElements = (iterations: number) => {
|
||||
const elements: ExcalidrawElement[] = [];
|
||||
while (iterations--) {
|
||||
const container = API.createElement({
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
groupIds: ["B", "A"],
|
||||
});
|
||||
const boundText = API.createElement({
|
||||
type: "text",
|
||||
containerId: container.id,
|
||||
groupIds: ["A"],
|
||||
});
|
||||
const otherElement = API.createElement({
|
||||
type: "rectangle",
|
||||
boundElements: [],
|
||||
groupIds: ["C", "A"],
|
||||
});
|
||||
mutateElement(container, {
|
||||
boundElements: [{ type: "text", id: boundText.id }],
|
||||
});
|
||||
|
||||
elements.push(boundText, otherElement, container);
|
||||
}
|
||||
return elements;
|
||||
};
|
||||
|
||||
const elements = makeElements(10000);
|
||||
const t0 = Date.now();
|
||||
normalizeElementOrder(elements);
|
||||
console.info(`${Date.now() - t0}ms`);
|
||||
});
|
||||
});
|
123
src/element/sortElements.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { arrayToMapWithIndex } from "../utils";
|
||||
import { ExcalidrawElement } from "./types";
|
||||
|
||||
const normalizeGroupElementOrder = (elements: readonly ExcalidrawElement[]) => {
|
||||
const origElements: ExcalidrawElement[] = elements.slice();
|
||||
const sortedElements = new Set<ExcalidrawElement>();
|
||||
|
||||
const orderInnerGroups = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
): ExcalidrawElement[] => {
|
||||
const firstGroupSig = elements[0]?.groupIds?.join("");
|
||||
const aGroup: ExcalidrawElement[] = [elements[0]];
|
||||
const bGroup: ExcalidrawElement[] = [];
|
||||
for (const element of elements.slice(1)) {
|
||||
if (element.groupIds?.join("") === firstGroupSig) {
|
||||
aGroup.push(element);
|
||||
} else {
|
||||
bGroup.push(element);
|
||||
}
|
||||
}
|
||||
return bGroup.length ? [...aGroup, ...orderInnerGroups(bGroup)] : aGroup;
|
||||
};
|
||||
|
||||
const groupHandledElements = new Map<string, true>();
|
||||
|
||||
origElements.forEach((element, idx) => {
|
||||
if (groupHandledElements.has(element.id)) {
|
||||
return;
|
||||
}
|
||||
if (element.groupIds?.length) {
|
||||
const topGroup = element.groupIds[element.groupIds.length - 1];
|
||||
const groupElements = origElements.slice(idx).filter((element) => {
|
||||
const ret = element?.groupIds?.some((id) => id === topGroup);
|
||||
if (ret) {
|
||||
groupHandledElements.set(element!.id, true);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
for (const elem of orderInnerGroups(groupElements)) {
|
||||
sortedElements.add(elem);
|
||||
}
|
||||
} else {
|
||||
sortedElements.add(element);
|
||||
}
|
||||
});
|
||||
|
||||
// if there's a bug which resulted in losing some of the elements, return
|
||||
// original instead as that's better than losing data
|
||||
if (sortedElements.size !== elements.length) {
|
||||
console.error("normalizeGroupElementOrder: lost some elements... bailing!");
|
||||
return elements;
|
||||
}
|
||||
|
||||
return [...sortedElements];
|
||||
};
|
||||
|
||||
/**
|
||||
* In theory, when we have text elements bound to a container, they
|
||||
* should be right after the container element in the elements array.
|
||||
* However, this is not guaranteed due to old and potential future bugs.
|
||||
*
|
||||
* This function sorts containers and their bound texts together. It prefers
|
||||
* original z-index of container (i.e. it moves bound text elements after
|
||||
* containers).
|
||||
*/
|
||||
const normalizeBoundElementsOrder = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
) => {
|
||||
const elementsMap = arrayToMapWithIndex(elements);
|
||||
|
||||
const origElements: (ExcalidrawElement | null)[] = elements.slice();
|
||||
const sortedElements = new Set<ExcalidrawElement>();
|
||||
|
||||
origElements.forEach((element, idx) => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (element.boundElements?.length) {
|
||||
sortedElements.add(element);
|
||||
origElements[idx] = null;
|
||||
element.boundElements.forEach((boundElement) => {
|
||||
const child = elementsMap.get(boundElement.id);
|
||||
if (child && boundElement.type === "text") {
|
||||
sortedElements.add(child[0]);
|
||||
origElements[child[1]] = null;
|
||||
}
|
||||
});
|
||||
} else if (element.type === "text" && element.containerId) {
|
||||
const parent = elementsMap.get(element.containerId);
|
||||
if (!parent?.[0].boundElements?.find((x) => x.id === element.id)) {
|
||||
sortedElements.add(element);
|
||||
origElements[idx] = null;
|
||||
|
||||
// if element has a container and container lists it, skip this element
|
||||
// as it'll be taken care of by the container
|
||||
}
|
||||
} else {
|
||||
sortedElements.add(element);
|
||||
origElements[idx] = null;
|
||||
}
|
||||
});
|
||||
|
||||
// if there's a bug which resulted in losing some of the elements, return
|
||||
// original instead as that's better than losing data
|
||||
if (sortedElements.size !== elements.length) {
|
||||
console.error(
|
||||
"normalizeBoundElementsOrder: lost some elements... bailing!",
|
||||
);
|
||||
return elements;
|
||||
}
|
||||
|
||||
return [...sortedElements];
|
||||
};
|
||||
|
||||
export const normalizeElementOrder = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
) => {
|
||||
// console.time();
|
||||
const ret = normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
|
||||
// console.timeEnd();
|
||||
return ret;
|
||||
};
|
@ -28,6 +28,7 @@ import {
|
||||
resetOriginalContainerCache,
|
||||
updateOriginalContainerCache,
|
||||
} from "./textWysiwyg";
|
||||
import { rotatePoint } from "../math";
|
||||
|
||||
export const normalizeText = (text: string) => {
|
||||
return (
|
||||
@ -127,10 +128,16 @@ export const bindTextToShapeAfterDuplication = (
|
||||
const newContainer = sceneElementMap.get(newElementId);
|
||||
if (newContainer) {
|
||||
mutateElement(newContainer, {
|
||||
boundElements: (newContainer.boundElements || []).concat({
|
||||
type: "text",
|
||||
id: newTextElementId,
|
||||
}),
|
||||
boundElements: (element.boundElements || [])
|
||||
.filter(
|
||||
(boundElement) =>
|
||||
boundElement.id !== newTextElementId &&
|
||||
boundElement.id !== boundTextElementId,
|
||||
)
|
||||
.concat({
|
||||
type: "text",
|
||||
id: newTextElementId,
|
||||
}),
|
||||
});
|
||||
}
|
||||
const newTextElement = sceneElementMap.get(newTextElementId);
|
||||
@ -717,3 +724,93 @@ export const isValidTextContainer = (element: ExcalidrawElement) => {
|
||||
isArrowElement(element)
|
||||
);
|
||||
};
|
||||
|
||||
export const isHittingContainerStroke = (
|
||||
x: number,
|
||||
y: number,
|
||||
container: ExcalidrawTextContainer,
|
||||
zoom: number,
|
||||
) => {
|
||||
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(container);
|
||||
const topLeft = [x1, y1];
|
||||
const topRight = [x2, y1];
|
||||
const bottomLeft = [x1, y2];
|
||||
const bottomRight = [x2, y2];
|
||||
|
||||
const [counterRotateX, counterRotateY] = rotatePoint(
|
||||
[x, y],
|
||||
[cx, cy],
|
||||
-container.angle,
|
||||
);
|
||||
|
||||
const strokeWidth = container.strokeWidth;
|
||||
if (container.type === "ellipse") {
|
||||
const threshold = 10 * zoom;
|
||||
const h = (topLeft[0] + topRight[0]) / 2;
|
||||
const k = (topLeft[1] + bottomLeft[1]) / 2;
|
||||
let a = container.width / 2 + threshold;
|
||||
let b = container.height / 2 + threshold;
|
||||
const checkPointOnOuterEllipse =
|
||||
Math.pow(counterRotateX - h, 2) / Math.pow(a, 2) +
|
||||
Math.pow(counterRotateY - k, 2) / Math.pow(b, 2);
|
||||
|
||||
a = container.width / 2 - strokeWidth - threshold;
|
||||
b = container.height / 2 - strokeWidth - threshold;
|
||||
|
||||
const checkPointOnInnerEllipse =
|
||||
Math.pow(counterRotateX - h, 2) / Math.pow(a, 2) +
|
||||
Math.pow(counterRotateY - k, 2) / Math.pow(b, 2);
|
||||
|
||||
// The expression evaluates to 1 means point is on ellipse,
|
||||
// < 1 means inside ellipse and > 1 means outside ellipse
|
||||
if (
|
||||
checkPointOnInnerEllipse === 1 ||
|
||||
checkPointOnOuterEllipse === 1 ||
|
||||
(checkPointOnInnerEllipse > 1 && checkPointOnOuterEllipse < 1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const threshold = 10 / zoom;
|
||||
|
||||
// Left Stroke
|
||||
if (
|
||||
counterRotateX >= topLeft[0] - threshold &&
|
||||
counterRotateX <= topLeft[0] + strokeWidth + threshold &&
|
||||
counterRotateY >= topLeft[1] - threshold &&
|
||||
counterRotateY <= bottomRight[1] + threshold
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// Top stroke
|
||||
if (
|
||||
counterRotateX >= topLeft[0] - threshold &&
|
||||
counterRotateX <= topRight[0] + threshold &&
|
||||
counterRotateY >= topLeft[1] - threshold &&
|
||||
counterRotateY <= topLeft[1] + threshold + strokeWidth
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Right stroke
|
||||
if (
|
||||
counterRotateX >= topRight[0] - threshold - strokeWidth &&
|
||||
counterRotateX <= topRight[0] + threshold &&
|
||||
counterRotateY >= topRight[1] - threshold &&
|
||||
counterRotateY <= bottomRight[1] + threshold
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bottom Stroke
|
||||
if (
|
||||
counterRotateX >= bottomLeft[0] - threshold &&
|
||||
counterRotateX <= bottomRight[0] + threshold &&
|
||||
counterRotateY >= bottomLeft[1] - threshold - strokeWidth &&
|
||||
counterRotateY <= bottomLeft[1] + threshold
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -75,6 +75,7 @@ import { jotaiStore } from "../../jotai";
|
||||
export const collabAPIAtom = atom<CollabAPI | null>(null);
|
||||
export const collabDialogShownAtom = atom(false);
|
||||
export const isCollaboratingAtom = atom(false);
|
||||
export const isOfflineAtom = atom(false);
|
||||
|
||||
interface CollabState {
|
||||
errorMessage: string;
|
||||
@ -152,6 +153,8 @@ class Collab extends PureComponent<Props, CollabState> {
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload);
|
||||
window.addEventListener("online", this.onOfflineStatusToggle);
|
||||
window.addEventListener("offline", this.onOfflineStatusToggle);
|
||||
window.addEventListener(EVENT.UNLOAD, this.onUnload);
|
||||
|
||||
const collabAPI: CollabAPI = {
|
||||
@ -165,6 +168,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
||||
};
|
||||
|
||||
jotaiStore.set(collabAPIAtom, collabAPI);
|
||||
this.onOfflineStatusToggle();
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV === ENV.TEST ||
|
||||
@ -180,7 +184,13 @@ class Collab extends PureComponent<Props, CollabState> {
|
||||
}
|
||||
}
|
||||
|
||||
onOfflineStatusToggle = () => {
|
||||
jotaiStore.set(isOfflineAtom, !window.navigator.onLine);
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("online", this.onOfflineStatusToggle);
|
||||
window.removeEventListener("offline", this.onOfflineStatusToggle);
|
||||
window.removeEventListener(EVENT.BEFORE_UNLOAD, this.beforeUnload);
|
||||
window.removeEventListener(EVENT.UNLOAD, this.onUnload);
|
||||
window.removeEventListener(EVENT.POINTER_MOVE, this.onPointerMove);
|
||||
@ -600,7 +610,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
||||
const localElements = this.getSceneElementsIncludingDeleted();
|
||||
const appState = this.excalidrawAPI.getAppState();
|
||||
|
||||
remoteElements = restoreElements(remoteElements, null, false);
|
||||
remoteElements = restoreElements(remoteElements, null);
|
||||
|
||||
const reconciledElements = _reconcileElements(
|
||||
localElements,
|
||||
|
@ -144,7 +144,7 @@ const RoomDialog = ({
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
value={username || ""}
|
||||
value={username.trim() || ""}
|
||||
className="RoomDialog-username TextInput"
|
||||
onChange={(event) => onUsernameChange(event.target.value)}
|
||||
onKeyPress={(event) => event.key === "Enter" && handleClose()}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { PRECEDING_ELEMENT_KEY } from "../../constants";
|
||||
import { ExcalidrawElement } from "../../element/types";
|
||||
import { AppState } from "../../types";
|
||||
import { arrayToMapWithIndex } from "../../utils";
|
||||
|
||||
export type ReconciledElements = readonly ExcalidrawElement[] & {
|
||||
_brand: "reconciledElements";
|
||||
@ -33,30 +34,13 @@ const shouldDiscardRemoteElement = (
|
||||
return false;
|
||||
};
|
||||
|
||||
const getElementsMapWithIndex = <T extends ExcalidrawElement>(
|
||||
elements: readonly T[],
|
||||
) =>
|
||||
elements.reduce(
|
||||
(
|
||||
acc: {
|
||||
[key: string]: [element: T, index: number] | undefined;
|
||||
},
|
||||
element: T,
|
||||
idx,
|
||||
) => {
|
||||
acc[element.id] = [element, idx];
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
export const reconcileElements = (
|
||||
localElements: readonly ExcalidrawElement[],
|
||||
remoteElements: readonly BroadcastedExcalidrawElement[],
|
||||
localAppState: AppState,
|
||||
): ReconciledElements => {
|
||||
const localElementsData =
|
||||
getElementsMapWithIndex<ExcalidrawElement>(localElements);
|
||||
arrayToMapWithIndex<ExcalidrawElement>(localElements);
|
||||
|
||||
const reconciledElements: ExcalidrawElement[] = localElements.slice();
|
||||
|
||||
@ -69,7 +53,7 @@ export const reconcileElements = (
|
||||
for (const remoteElement of remoteElements) {
|
||||
remoteElementIdx++;
|
||||
|
||||
const local = localElementsData[remoteElement.id];
|
||||
const local = localElementsData.get(remoteElement.id);
|
||||
|
||||
if (shouldDiscardRemoteElement(localAppState, local?.[0], remoteElement)) {
|
||||
if (remoteElement[PRECEDING_ELEMENT_KEY]) {
|
||||
@ -105,21 +89,21 @@ export const reconcileElements = (
|
||||
offset++;
|
||||
if (cursor === 0) {
|
||||
reconciledElements.unshift(remoteElement);
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
cursor - offset,
|
||||
];
|
||||
]);
|
||||
} else {
|
||||
reconciledElements.splice(cursor + 1, 0, remoteElement);
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
cursor + 1 - offset,
|
||||
];
|
||||
]);
|
||||
cursor++;
|
||||
}
|
||||
} else {
|
||||
let idx = localElementsData[parent]
|
||||
? localElementsData[parent]![1]
|
||||
let idx = localElementsData.has(parent)
|
||||
? localElementsData.get(parent)![1]
|
||||
: null;
|
||||
if (idx != null) {
|
||||
idx += offset;
|
||||
@ -127,38 +111,38 @@ export const reconcileElements = (
|
||||
if (idx != null && idx >= cursor) {
|
||||
reconciledElements.splice(idx + 1, 0, remoteElement);
|
||||
offset++;
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
idx + 1 - offset,
|
||||
];
|
||||
]);
|
||||
cursor = idx + 1;
|
||||
} else if (idx != null) {
|
||||
reconciledElements.splice(cursor + 1, 0, remoteElement);
|
||||
offset++;
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
cursor + 1 - offset,
|
||||
];
|
||||
]);
|
||||
cursor++;
|
||||
} else {
|
||||
reconciledElements.push(remoteElement);
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
reconciledElements.length - 1 - offset,
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
||||
// no parent z-index information, local element exists → replace in place
|
||||
} else if (local) {
|
||||
reconciledElements[local[1]] = remoteElement;
|
||||
localElementsData[remoteElement.id] = [remoteElement, local[1]];
|
||||
localElementsData.set(remoteElement.id, [remoteElement, local[1]]);
|
||||
// otherwise push to the end
|
||||
} else {
|
||||
reconciledElements.push(remoteElement);
|
||||
localElementsData[remoteElement.id] = [
|
||||
localElementsData.set(remoteElement.id, [
|
||||
remoteElement,
|
||||
reconciledElements.length - 1 - offset,
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,9 +263,12 @@ export const loadScene = async (
|
||||
await importFromBackend(id, privateKey),
|
||||
localDataState?.appState,
|
||||
localDataState?.elements,
|
||||
{ repairBindings: true },
|
||||
);
|
||||
} else {
|
||||
data = restore(localDataState || null, null, null);
|
||||
data = restore(localDataState || null, null, null, {
|
||||
repairBindings: true,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -45,6 +45,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collab-offline-warning {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 6.5rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
border-radius: var(--border-radius-md);
|
||||
background-color: var(--color-warning);
|
||||
color: var(--color-text-warning);
|
||||
z-index: 6;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.excalidraw-app.is-collaborating {
|
||||
|
@ -52,6 +52,7 @@ import Collab, {
|
||||
collabAPIAtom,
|
||||
collabDialogShownAtom,
|
||||
isCollaboratingAtom,
|
||||
isOfflineAtom,
|
||||
} from "./collab/Collab";
|
||||
import {
|
||||
exportToBackend,
|
||||
@ -66,10 +67,7 @@ import {
|
||||
} from "./data/localStorage";
|
||||
import CustomStats from "./CustomStats";
|
||||
import { restore, restoreAppState, RestoredDataState } from "../data/restore";
|
||||
|
||||
import "./index.scss";
|
||||
import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
|
||||
|
||||
import { updateStaleImageStatuses } from "./data/FileManager";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { isInitializedImageElement } from "../element/typeChecks";
|
||||
@ -77,7 +75,7 @@ import { loadFilesFromFirebase } from "./data/firebase";
|
||||
import { LocalData } from "./data/LocalData";
|
||||
import { isBrowserStorageStateNewer } from "./data/tabSync";
|
||||
import clsx from "clsx";
|
||||
import { atom, Provider, useAtom } from "jotai";
|
||||
import { atom, Provider, useAtom, useAtomValue } from "jotai";
|
||||
import { jotaiStore, useAtomWithInitialValue } from "../jotai";
|
||||
import { reconcileElements } from "./collab/reconciliation";
|
||||
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
|
||||
@ -85,6 +83,8 @@ import { AppMainMenu } from "./components/AppMainMenu";
|
||||
import { AppWelcomeScreen } from "./components/AppWelcomeScreen";
|
||||
import { AppFooter } from "./components/AppFooter";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
polyfill();
|
||||
|
||||
window.EXCALIDRAW_THROTTLE_RENDER = true;
|
||||
@ -362,7 +362,7 @@ const ExcalidrawWrapper = () => {
|
||||
if (data.scene) {
|
||||
excalidrawAPI.updateScene({
|
||||
...data.scene,
|
||||
...restore(data.scene, null, null),
|
||||
...restore(data.scene, null, null, { repairBindings: true }),
|
||||
commitToHistory: true,
|
||||
});
|
||||
}
|
||||
@ -599,6 +599,8 @@ const ExcalidrawWrapper = () => {
|
||||
localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems);
|
||||
};
|
||||
|
||||
const isOffline = useAtomValue(isOfflineAtom);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ height: "100%" }}
|
||||
@ -661,6 +663,11 @@ const ExcalidrawWrapper = () => {
|
||||
/>
|
||||
<AppWelcomeScreen setCollabDialogShown={setCollabDialogShown} />
|
||||
<AppFooter />
|
||||
{isCollaborating && isOffline && (
|
||||
<div className="collab-offline-warning">
|
||||
{t("alerts.collabOfflineWarning")}
|
||||
</div>
|
||||
)}
|
||||
</Excalidraw>
|
||||
{excalidrawAPI && <Collab excalidrawAPI={excalidrawAPI} />}
|
||||
{errorMessage && (
|
||||
|
@ -109,35 +109,35 @@
|
||||
"decreaseFontSize": "تصغير حجم الخط",
|
||||
"increaseFontSize": "تكبير حجم الخط",
|
||||
"unbindText": "فك ربط النص",
|
||||
"bindText": "",
|
||||
"bindText": "ربط النص بالحاوية",
|
||||
"link": {
|
||||
"edit": "تعديل الرابط",
|
||||
"create": "إنشاء رابط",
|
||||
"label": "رابط"
|
||||
},
|
||||
"lineEditor": {
|
||||
"edit": "",
|
||||
"exit": ""
|
||||
"edit": "تحرير السطر",
|
||||
"exit": "الخروج من المُحرر"
|
||||
},
|
||||
"elementLock": {
|
||||
"lock": "",
|
||||
"unlock": "",
|
||||
"lockAll": "",
|
||||
"unlockAll": ""
|
||||
"lock": "قفل",
|
||||
"unlock": "فتح",
|
||||
"lockAll": "قفل الكل",
|
||||
"unlockAll": "فتح الكل"
|
||||
},
|
||||
"statusPublished": "",
|
||||
"sidebarLock": ""
|
||||
"statusPublished": "نُشر",
|
||||
"sidebarLock": "إبقاء الشريط الجانبي مفتوح"
|
||||
},
|
||||
"library": {
|
||||
"noItems": "",
|
||||
"hint_emptyLibrary": "",
|
||||
"hint_emptyPrivateLibrary": ""
|
||||
"noItems": "لا توجد عناصر أضيفت بعد...",
|
||||
"hint_emptyLibrary": "حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.",
|
||||
"hint_emptyPrivateLibrary": "حدد عنصر على القماش لإضافته هنا."
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "إعادة تعيين اللوحة",
|
||||
"exportJSON": "صدر الملف",
|
||||
"exportImage": "",
|
||||
"export": "",
|
||||
"exportImage": "تصدير الصورة...",
|
||||
"export": "حفظ إلى...",
|
||||
"exportToPng": "تصدير بصيغة PNG",
|
||||
"exportToSvg": "تصدير بصيغة SVG",
|
||||
"copyToClipboard": "نسخ إلى الحافظة",
|
||||
@ -179,7 +179,7 @@
|
||||
"couldNotLoadInvalidFile": "تعذر التحميل، الملف غير صالح",
|
||||
"importBackendFailed": "فشل الاستيراد من الخادوم.",
|
||||
"cannotExportEmptyCanvas": "لا يمكن تصدير لوحة فارغة.",
|
||||
"couldNotCopyToClipboard": "",
|
||||
"couldNotCopyToClipboard": "تعذر النسخ إلى الحافظة.",
|
||||
"decryptFailed": "تعذر فك تشفير البيانات.",
|
||||
"uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",
|
||||
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
|
||||
@ -200,10 +200,10 @@
|
||||
"fileTooBig": "الملف كبير جداً. الحد الأقصى المسموح به للحجم هو {{maxSize}}.",
|
||||
"svgImageInsertError": "تعذر إدراج صورة SVG. يبدو أن ترميز SVG غير صحيح.",
|
||||
"invalidSVGString": "SVG غير صالح.",
|
||||
"cannotResolveCollabServer": "",
|
||||
"importLibraryError": "",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"cannotResolveCollabServer": "تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.",
|
||||
"importLibraryError": "تعذر تحميل المكتبة",
|
||||
"collabSaveFailed": "تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.",
|
||||
"collabSaveFailed_sizeExceeded": "تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "تحديد",
|
||||
@ -217,9 +217,10 @@
|
||||
"text": "نص",
|
||||
"library": "مكتبة",
|
||||
"lock": "الحفاظ على أداة التحديد نشطة بعد الرسم",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": "ممحاة"
|
||||
"penMode": "وضع القلم - امنع اللمس",
|
||||
"link": "إضافة/تحديث الرابط للشكل المحدد",
|
||||
"eraser": "ممحاة",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "إجراءات اللوحة",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "الأشكال"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "لتحريك لوحة الرسم ، استمر في الضغط على عجلة الماوس أو مفتاح المسافة أثناء السحب",
|
||||
"canvasPanning": "لتحريك القماش، اضغط على عجلة الفأرة أو مفتاح المسافة أثناء السحب، أو استخدم أداة اليد",
|
||||
"linearElement": "انقر لبدء نقاط متعددة، اسحب لخط واحد",
|
||||
"freeDraw": "انقر واسحب، افرج عند الانتهاء",
|
||||
"text": "نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار",
|
||||
@ -238,14 +239,15 @@
|
||||
"resize": "يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،\nاضغط على ALT لتغيير الحجم من المركز",
|
||||
"resizeImage": "يمكنك تغيير الحجم بحرية بالضغط بأستمرار على SHIFT،\nاضغط بأستمرار على ALT أيضا لتغيير الحجم من المركز",
|
||||
"rotate": "يمكنك تقييد الزوايا من خلال الضغط على SHIFT أثناء الدوران",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_info": "اضغط على مفتاح (Ctrl أو Cmd) و انقر بشكل مزدوج، أو اضغط على مفتاحي (Ctrl أو Cmd) و (Enter) لتعديل النقاط",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"placeImage": "",
|
||||
"publishLibrary": "نشر مكتبتك",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "تعذر عرض المعاينة",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Поддържайте избрания инструмент активен след рисуване",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": ""
|
||||
"eraser": "",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Действия по платното",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "Натиснете Enter, за да добавите",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Невъзможност за показване на preview",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "আঁকার পরে নির্বাচিত টুল সক্রিয় রাখুন",
|
||||
"penMode": "",
|
||||
"link": "একটি নির্বাচিত আকৃতির জন্য লিঙ্ক যোগ বা আপডেট করুন",
|
||||
"eraser": "ঝাড়ন"
|
||||
"eraser": "ঝাড়ন",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "ক্যানভাস কার্যকলাপ",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "আকার(গুলি)"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "ক্যানভাস সরানোর জন্য মাউস হুইল বা স্পেসবার ধরে টানুন",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "একাধিক বিন্দু শুরু করতে ক্লিক করুন, একক লাইনের জন্য টেনে আনুন",
|
||||
"freeDraw": "ক্লিক করুন এবং টেনে আনুন, আপনার কাজ শেষ হলে ছেড়ে দিন",
|
||||
"text": "বিশেষ্য: আপনি নির্বাচন টুলের সাথে যে কোনো জায়গায় ডাবল-ক্লিক করে পাঠ্য যোগ করতে পারেন",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "আপনার নিজস্ব সংগ্রহ প্রকাশ করুন",
|
||||
"bindTextToElement": "লেখা যোগ করতে এন্টার টিপুন",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "মুছে ফেলার জন্য চিহ্নিত উপাদানগুলিকে ফিরিয়ে আনতে অল্ট ধরে রাখুন"
|
||||
"eraserRevert": "মুছে ফেলার জন্য চিহ্নিত উপাদানগুলিকে ফিরিয়ে আনতে অল্ট ধরে রাখুন",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "প্রিভিউ দেখাতে অপারগ",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Mantenir activa l'eina seleccionada desprès de dibuixar",
|
||||
"penMode": "",
|
||||
"link": "Afegeix / actualitza l'enllaç per a la forma seleccionada",
|
||||
"eraser": "Esborrador"
|
||||
"eraser": "Esborrador",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Accions del llenç",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formes"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Per a moure el llenç, mantingueu premuda la roda del ratolí o la tecla espai mentre l'arrossegueu",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Feu clic per a dibuixar múltiples punts; arrossegueu per a una sola línia",
|
||||
"freeDraw": "Feu clic i arrossegueu, deixeu anar per a finalitzar",
|
||||
"text": "Consell: també podeu afegir text fent doble clic en qualsevol lloc amb l'eina de selecció",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publiqueu la vostra pròpia llibreria",
|
||||
"bindTextToElement": "Premeu enter per a afegir-hi text",
|
||||
"deepBoxSelect": "Manteniu CtrlOrCmd per a selecció profunda, i per a evitar l'arrossegament",
|
||||
"eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar"
|
||||
"eraserRevert": "Mantingueu premuda Alt per a revertir els elements seleccionats per a esborrar",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "No es pot mostrar la previsualització",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": "Guma"
|
||||
"eraser": "Guma",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": ""
|
||||
"eraser": "",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Ausgewähltes Werkzeug nach Zeichnen aktiv lassen",
|
||||
"penMode": "Stift-Modus - Berührung verhindern",
|
||||
"link": "Link für ausgewählte Form hinzufügen / aktualisieren",
|
||||
"eraser": "Radierer"
|
||||
"eraser": "Radierer",
|
||||
"hand": "Hand (Schwenkwerkzeug)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Aktionen für Zeichenfläche",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formen"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Um die Zeichenfläche zu verschieben, halte das Mausrad oder die Leertaste während des Ziehens",
|
||||
"canvasPanning": "Um die Zeichenfläche zu verschieben, halte das Mausrad oder die Leertaste während des Ziehens, oder verwende das Hand-Werkzeug",
|
||||
"linearElement": "Klicken für Linie mit mehreren Punkten, Ziehen für einzelne Linie",
|
||||
"freeDraw": "Klicke und ziehe. Lass los, wenn du fertig bist",
|
||||
"text": "Tipp: Du kannst auch Text hinzufügen, indem du mit dem Auswahlwerkzeug auf eine beliebige Stelle doppelklickst",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Veröffentliche deine eigene Bibliothek",
|
||||
"bindTextToElement": "Zum Hinzufügen Eingabetaste drücken",
|
||||
"deepBoxSelect": "Halte CtrlOrCmd gedrückt, um innerhalb der Gruppe auszuwählen, und um Ziehen zu vermeiden",
|
||||
"eraserRevert": "Halte Alt gedrückt, um die zum Löschen markierten Elemente zurückzusetzen"
|
||||
"eraserRevert": "Halte Alt gedrückt, um die zum Löschen markierten Elemente zurückzusetzen",
|
||||
"firefox_clipboard_write": "Diese Funktion kann wahrscheinlich aktiviert werden, indem die Einstellung \"dom.events.asyncClipboard.clipboardItem\" auf \"true\" gesetzt wird. Um die Browsereinstellungen in Firefox zu ändern, besuche die Seite \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Vorschau kann nicht angezeigt werden",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Κράτησε επιλεγμένο το εργαλείο μετά το σχέδιο",
|
||||
"penMode": "Λειτουργία μολυβιού - αποτροπή αφής",
|
||||
"link": "Προσθήκη/ Ενημέρωση συνδέσμου για ένα επιλεγμένο σχήμα",
|
||||
"eraser": "Γόμα"
|
||||
"eraser": "Γόμα",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Ενέργειες καμβά",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Σχήματα"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Για να μετακινήσετε καμβά, κρατήστε πατημένο τον τροχό του ποντικιού ή το πλήκτρο διαστήματος ενώ σύρετε",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Κάνε κλικ για να ξεκινήσεις πολλαπλά σημεία, σύρε για μια γραμμή",
|
||||
"freeDraw": "Κάντε κλικ και σύρτε, απελευθερώσατε όταν έχετε τελειώσει",
|
||||
"text": "Tip: μπορείτε επίσης να προσθέστε κείμενο με διπλό-κλικ οπουδήποτε με το εργαλείο επιλογών",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Δημοσιεύστε τη δική σας βιβλιοθήκη",
|
||||
"bindTextToElement": "Πατήστε Enter για προσθήκη κειμένου",
|
||||
"deepBoxSelect": "Κρατήστε πατημένο το CtrlOrCmd για να επιλέξετε βαθιά, και να αποτρέψετε τη μεταφορά",
|
||||
"eraserRevert": "Κρατήστε πατημένο το Alt για να επαναφέρετε τα στοιχεία που σημειώθηκαν για διαγραφή"
|
||||
"eraserRevert": "Κρατήστε πατημένο το Alt για να επαναφέρετε τα στοιχεία που σημειώθηκαν για διαγραφή",
|
||||
"firefox_clipboard_write": "Αυτή η επιλογή μπορεί πιθανώς να ενεργοποιηθεί αλλάζοντας την ρύθμιση \"dom.events.asyncClipboard.clipboardItem\" σε \"true\". Για να αλλάξετε τις ρυθμίσεις του προγράμματος περιήγησης στο Firefox, επισκεφθείτε τη σελίδα \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Αδυναμία εμφάνισης προεπισκόπησης",
|
||||
|
@ -193,7 +193,8 @@
|
||||
"invalidSceneUrl": "Couldn't import scene from the supplied URL. It's either malformed, or doesn't contain valid Excalidraw JSON data.",
|
||||
"resetLibrary": "This will clear your library. Are you sure?",
|
||||
"removeItemsFromsLibrary": "Delete {{count}} item(s) from library?",
|
||||
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled."
|
||||
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled.",
|
||||
"collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!"
|
||||
},
|
||||
"errors": {
|
||||
"unsupportedFileType": "Unsupported file type.",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Mantener la herramienta seleccionada activa después de dibujar",
|
||||
"penMode": "Modo Lápiz - previene toque",
|
||||
"link": "Añadir/Actualizar enlace para una forma seleccionada",
|
||||
"eraser": "Borrar"
|
||||
"eraser": "Borrar",
|
||||
"hand": "Mano (herramienta de panoramización)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Acciones del lienzo",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formas"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Para mover el lienzo, mantenga la rueda del ratón o la barra de espacio mientras arrastra",
|
||||
"canvasPanning": "Para mover el lienzo, mantenga la rueda del ratón o la barra espaciadora mientras arrastra o utilice la herramienta de mano",
|
||||
"linearElement": "Haz clic para dibujar múltiples puntos, arrastrar para solo una línea",
|
||||
"freeDraw": "Haz clic y arrastra, suelta al terminar",
|
||||
"text": "Consejo: también puedes añadir texto haciendo doble clic en cualquier lugar con la herramienta de selección",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publica tu propia biblioteca",
|
||||
"bindTextToElement": "Presione Entrar para agregar",
|
||||
"deepBoxSelect": "Mantén CtrlOrCmd para seleccionar en profundidad, y para evitar arrastrar",
|
||||
"eraserRevert": "Mantenga pulsado Alt para revertir los elementos marcados para su eliminación"
|
||||
"eraserRevert": "Mantenga pulsado Alt para revertir los elementos marcados para su eliminación",
|
||||
"firefox_clipboard_write": "Esta característica puede ser habilitada estableciendo la bandera \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar las banderas del navegador en Firefox, visite la página \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "No se puede mostrar la vista previa",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Mantendu aktibo hautatutako tresna marraztu ondoren",
|
||||
"penMode": "Luma modua - ukipena saihestu",
|
||||
"link": "Gehitu / Eguneratu esteka hautatutako forma baterako",
|
||||
"eraser": "Borragoma"
|
||||
"eraser": "Borragoma",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Canvas ekintzak",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formak"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Oihala mugitzeko, sakatu saguaren gurpila edo zuriune-barra arrastatzean",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Egin klik hainbat puntu hasteko, arrastatu lerro bakarrerako",
|
||||
"freeDraw": "Egin klik eta arrastatu, askatu amaitutakoan",
|
||||
"text": "Aholkua: testua gehitu dezakezu edozein lekutan klik bikoitza eginez hautapen tresnarekin",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Argitaratu zure liburutegia",
|
||||
"bindTextToElement": "Sakatu Sartu testua gehitzeko",
|
||||
"deepBoxSelect": "Eutsi Ctrl edo Cmd sakatuta aukeraketa sakona egiteko eta arrastatzea saihesteko",
|
||||
"eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko"
|
||||
"eraserRevert": "Eduki Alt sakatuta ezabatzeko markatutako elementuak leheneratzeko",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Ezin da oihala aurreikusi",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "ابزار انتخاب شده را بعد از کشیدن نگه دار",
|
||||
"penMode": "حالت قلم - جلوگیری از تماس",
|
||||
"link": "افزودن/بهروزرسانی پیوند برای شکل انتخابی",
|
||||
"eraser": "پاک کن"
|
||||
"eraser": "پاک کن",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "عملیات روی بوم",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "شکلها"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "برای حرکت دادن بوم، چرخ ماوس یا فاصله را در حین کشیدن نگه دارید",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "برای چند نقطه کلیک و برای یک خط بکشید",
|
||||
"freeDraw": "کلیک کنید و بکشید و وقتی کار تمام شد رها کنید",
|
||||
"text": "نکته: با برنامه انتخاب شده شما میتوانید با دوبار کلیک کردن هرکجا میخواید متن اظاف کنید",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "کتابخانه خود را منتشر کنید",
|
||||
"bindTextToElement": "برای افزودن اینتر را بزنید",
|
||||
"deepBoxSelect": "CtrlOrCmd را برای انتخاب عمیق و جلوگیری از کشیدن نگه دارید",
|
||||
"eraserRevert": "Alt را نگه دارید تا عناصر علامت گذاری شده برای حذف برگردند"
|
||||
"eraserRevert": "Alt را نگه دارید تا عناصر علامت گذاری شده برای حذف برگردند",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "پیش نمایش نشان داده نمی شود",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Pidä valittu työkalu aktiivisena piirron jälkeen",
|
||||
"penMode": "",
|
||||
"link": "Lisää/päivitä linkki valitulle muodolle",
|
||||
"eraser": "Poistotyökalu"
|
||||
"eraser": "Poistotyökalu",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Piirtoalueen toiminnot",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Muodot"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Liikuttaaksesi piirtoaluetta, raahaa hiiren vieritysrulla tai välilyöntinäppäin alaspainettuna",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Klikkaa piirtääksesi useampi piste, raahaa piirtääksesi yksittäinen viiva",
|
||||
"freeDraw": "Paina ja raahaa, päästä irti kun olet valmis",
|
||||
"text": "Vinkki: voit myös lisätä tekstiä kaksoisnapsauttamalla mihin tahansa valintatyökalulla",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Julkaise oma kirjasto",
|
||||
"bindTextToElement": "Lisää tekstiä painamalla enter",
|
||||
"deepBoxSelect": "Käytä syvävalintaa ja estä raahaus painamalla CtrlOrCmd",
|
||||
"eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen"
|
||||
"eraserRevert": "Pidä Alt alaspainettuna, kumotaksesi merkittyjen elementtien poistamisen",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Esikatselua ei voitu näyttää",
|
||||
|
@ -45,7 +45,7 @@
|
||||
"exportEmbedScene": "Intégrer la scène",
|
||||
"exportEmbedScene_details": "Les données de scène seront enregistrées dans le fichier PNG/SVG exporté, afin que la scène puisse être restaurée à partir de celui-ci.\nCela augmentera la taille du fichier exporté.",
|
||||
"addWatermark": "Ajouter \"Réalisé avec Excalidraw\"",
|
||||
"handDrawn": "Manuscrit",
|
||||
"handDrawn": "À main levée",
|
||||
"normal": "Normale",
|
||||
"code": "Code",
|
||||
"small": "Petite",
|
||||
@ -219,7 +219,8 @@
|
||||
"lock": "Garder l'outil sélectionné actif après le dessin",
|
||||
"penMode": "Mode stylo - évite le toucher",
|
||||
"link": "Ajouter/mettre à jour le lien pour une forme sélectionnée",
|
||||
"eraser": "Gomme"
|
||||
"eraser": "Gomme",
|
||||
"hand": "Mains (outil de déplacement de la vue)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Actions du canevas",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formes"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Pour déplacer la zone de dessin, maintenez la molette de la souris enfoncée ou la barre d'espace tout en faisant glisser",
|
||||
"canvasPanning": "Pour déplacer la zone de dessin, maintenez la molette de la souris enfoncée ou la barre d'espace tout en faisant glisser, ou utiliser l'outil main.",
|
||||
"linearElement": "Cliquez pour démarrer plusieurs points, faites glisser pour une seule ligne",
|
||||
"freeDraw": "Cliquez et faites glissez, relâchez quand vous avez terminé",
|
||||
"text": "Astuce : vous pouvez aussi ajouter du texte en double-cliquant n'importe où avec l'outil de sélection",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publier votre propre bibliothèque",
|
||||
"bindTextToElement": "Appuyer sur Entrée pour ajouter du texte",
|
||||
"deepBoxSelect": "Maintenir Ctrl ou Cmd pour sélectionner dans les groupes et empêcher le déplacement",
|
||||
"eraserRevert": "Maintenez Alt enfoncé pour annuler les éléments marqués pour suppression"
|
||||
"eraserRevert": "Maintenez Alt enfoncé pour annuler les éléments marqués pour suppression",
|
||||
"firefox_clipboard_write": "Cette fonctionnalité devrait pouvoir être activée en définissant l'option \"dom.events.asyncClipboard.clipboard.clipboardItem\" à \"true\". Pour modifier les paramètres du navigateur dans Firefox, visitez la page \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Impossible d’afficher l’aperçu",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Manter a ferramenta seleccionada activa despois de debuxar",
|
||||
"penMode": "Modo lapis - evitar o contacto",
|
||||
"link": "Engadir/ Actualizar ligazón para a forma seleccionada",
|
||||
"eraser": "Goma de borrar"
|
||||
"eraser": "Goma de borrar",
|
||||
"hand": "Man (ferramenta de desprazamento)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Accións do lenzo",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formas"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Para mover o lenzo, manteña a roda do rato ou a barra de espazo mentres arrastra",
|
||||
"canvasPanning": "Para mover o lenzo, manteña pulsada a roda do rato ou a barra de espazo mentres arrastra, ou utilice a ferramenta da man",
|
||||
"linearElement": "Faga clic para iniciar varios puntos, arrastre para unha sola liña",
|
||||
"freeDraw": "Fai clic e arrastra, solta cando acabes",
|
||||
"text": "Consello: tamén podes engadir texto facendo dobre-clic en calquera lugar coa ferramenta de selección",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publica a túa propia biblioteca",
|
||||
"bindTextToElement": "Prema a tecla enter para engadir texto",
|
||||
"deepBoxSelect": "Manteña pulsado CtrlOrCmd para seleccionar en profundidade e evitar o arrastre",
|
||||
"eraserRevert": "Manteña pulsado Alt para reverter os elementos marcados para a súa eliminación"
|
||||
"eraserRevert": "Manteña pulsado Alt para reverter os elementos marcados para a súa eliminación",
|
||||
"firefox_clipboard_write": "Esta función pódese activar establecendo a opción \"dom.events.asyncClipboard.clipboardItem\" a \"true\". Para cambiar as opcións do navegador en Firefox, visita a páxina \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Non se pode mostrar a vista previa",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "השאר את הכלי הנבחר פעיל גם לאחר סיום הציור",
|
||||
"penMode": "",
|
||||
"link": "הוספה/עדכון של קישור עבור הצורה הנבחרת",
|
||||
"eraser": "מחק"
|
||||
"eraser": "מחק",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "פעולות הלוח",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "צורות"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "כדי להזיז את הקנבס לחצו על גלגל העכבר או על מקש הרווח תוך כדי גרירה",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "הקלק בשביל לבחור נקודות מרובות, גרור בשביל קו בודד",
|
||||
"freeDraw": "לחץ וגרור, שחרר כשסיימת",
|
||||
"text": "טיפ: אפשר להוסיף טקסט על ידי לחיצה כפולה בכל מקום עם כלי הבחירה",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "פירסום ספריה אישית",
|
||||
"bindTextToElement": "יש להקיש Enter כדי להוסיף טקסט",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "לא הצלחנו להציג את התצוגה המקדימה",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "ड्राइंग के बाद चयनित टूल को सक्रिय रखें",
|
||||
"penMode": "पेन का मोड - स्पर्श टाले",
|
||||
"link": "",
|
||||
"eraser": "रबड़"
|
||||
"eraser": "रबड़",
|
||||
"hand": "हाथ ( खिसकाने का औज़ार)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "कैनवास क्रिया",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "आकृतियाँ"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "",
|
||||
"canvasPanning": "कैनवास को सरकाने के लिए, ड्रैग करते समय माउस व्हील को पकड़े रखे या स्पेसबार को दबाए रखे, अथवा हाथ वाले औज़ार का उपयोग करें",
|
||||
"linearElement": "कई बिंदुओं को शुरू करने के लिए क्लिक करें, सिंगल लाइन के लिए खींचें",
|
||||
"freeDraw": "क्लिक करें और खींचें। समाप्त करने के लिए, छोड़ो",
|
||||
"text": "आप चयन टूल से कहीं भी डबल-क्लिक करके टेक्स्ट जोड़ सकते हैं",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "मिटाने के लिए चुने हुए चीजों को ना चुनने के लिए Alt साथ में दबाए"
|
||||
"eraserRevert": "मिटाने के लिए चुने हुए चीजों को ना चुनने के लिए Alt साथ में दबाए",
|
||||
"firefox_clipboard_write": "\"dom.events.asyncClipboard.clipboardItem\" फ़्लैग को \"true\" पर सेट करके इस सुविधा को संभवतः सक्षम किया जा सकता है। Firefox में ब्राउज़र फ़्लैग बदलने के लिए, \"about:config\" पृष्ठ पर जाएँ।"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "पूर्वावलोकन नहीं दिखा सकते हैं",
|
||||
@ -448,15 +450,15 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
"center_heading": "आपका सर्व डेटा ब्राउज़र के भीतर स्थानिक जगह पे सुरक्षित किया गया.",
|
||||
"center_heading_plus": "बजाय आपको Excalidraw+ पर जाना है?",
|
||||
"menuHint": "निर्यात, पसंद, भाषायें, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
"menuHint": "निर्यात, पसंद, और भी...",
|
||||
"center_heading": "चित्रांकन। बनाया गया। सरल।",
|
||||
"toolbarHint": "एक औजार चुने और चित्रकारी प्रारंभ करे!",
|
||||
"helpHint": "शॉर्ट्कट और सहाय्य"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Rajzolás után az aktív eszközt tartsa kijelölve",
|
||||
"penMode": "",
|
||||
"link": "Hivatkozás hozzáadása/frissítése a kiválasztott alakzathoz",
|
||||
"eraser": ""
|
||||
"eraser": "",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Vászon műveletek",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Alakzatok"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "A vászon mozgatásához tartsd lenyomva az egér görgőjét vagy a szóköz billentyűt húzás közben",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Kattintással görbe, az eger húzásával pedig egyenes nyilat rajzolhatsz",
|
||||
"freeDraw": "Kattints és húzd, majd engedd el, amikor végeztél",
|
||||
"text": "Tipp: A kijelölés eszközzel a dupla kattintás új szöveget hoz létre",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Tedd közzé saját könyvtáradat",
|
||||
"bindTextToElement": "Nyomd meg az Entert szöveg hozzáadáshoz",
|
||||
"deepBoxSelect": "Tartsd lenyomva a Ctrl/Cmd billentyűt a mély kijelöléshez és a húzás megakadályozásához",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Előnézet nem jeleníthető meg",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Biarkan alat yang dipilih aktif setelah menggambar",
|
||||
"penMode": "Mode pena - mencegah sentuhan",
|
||||
"link": "Tambah/Perbarui tautan untuk bentuk yang dipilih",
|
||||
"eraser": "Penghapus"
|
||||
"eraser": "Penghapus",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Opsi Kanvas",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Bentuk"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Untuk memindahkan kanvas, tekan roda mouse atau spasi ketika menarik",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Klik untuk memulai banyak poin, seret untuk satu baris",
|
||||
"freeDraw": "Klik dan seret, lepaskan jika Anda selesai",
|
||||
"text": "Tip: Anda juga dapat menambahkan teks dengan klik ganda di mana saja dengan alat pemilihan",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Terbitkan pustaka Anda",
|
||||
"bindTextToElement": "Tekan enter untuk tambahkan teks",
|
||||
"deepBoxSelect": "Tekan Ctrl atau Cmd untuk memilih yang di dalam, dan mencegah penggeseran",
|
||||
"eraserRevert": "Tahan Alt untuk mengembalikan elemen yang ditandai untuk dihapus"
|
||||
"eraserRevert": "Tahan Alt untuk mengembalikan elemen yang ditandai untuk dihapus",
|
||||
"firefox_clipboard_write": "Fitur ini dapat diaktifkan melalui pengaturan flag \"dom.events.asyncClipboard.clipboardItem\" ke \"true\". Untuk mengganti flag di Firefox, pergi ke laman \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Tidak dapat menampilkan pratinjau",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Mantieni lo strumento selezionato attivo dopo aver disegnato",
|
||||
"penMode": "Modalità penna - previene il tocco",
|
||||
"link": "Aggiungi/ aggiorna il link per una forma selezionata",
|
||||
"eraser": "Gomma"
|
||||
"eraser": "Gomma",
|
||||
"hand": "Mano (strumento di panoramica)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Azioni sulla Tela",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Forme"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Per spostare la tela, tieni premuta la rotella del mouse o la barra spaziatrice mentre la trascini",
|
||||
"canvasPanning": "Per spostare la tela, tieni premuta la rotellina del mouse o la barra spaziatrice mentre trascini oppure usa lo strumento mano",
|
||||
"linearElement": "Clicca per iniziare una linea in più punti, trascina per singola linea",
|
||||
"freeDraw": "Clicca e trascina, rilascia quando avrai finito",
|
||||
"text": "Suggerimento: puoi anche aggiungere del testo facendo doppio clic ovunque con lo strumento di selezione",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Pubblica la tua libreria",
|
||||
"bindTextToElement": "Premi invio per aggiungere il testo",
|
||||
"deepBoxSelect": "Tieni premuto CtrlOCmd per selezionare in profondità e per impedire il trascinamento",
|
||||
"eraserRevert": "Tieni premuto Alt per ripristinare gli elementi contrassegnati per l'eliminazione"
|
||||
"eraserRevert": "Tieni premuto Alt per ripristinare gli elementi contrassegnati per l'eliminazione",
|
||||
"firefox_clipboard_write": "Questa funzione può essere abilitata impostando il flag \"dom.events.asyncClipboard.clipboardItem\" su \"true\". Per modificare i flag del browser in Firefox, visitare la pagina \"about:config\"."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Impossibile visualizzare l'anteprima",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "描画後も使用中のツールを選択したままにする",
|
||||
"penMode": "ペンモード - タッチ防止",
|
||||
"link": "選択した図形のリンクを追加/更新",
|
||||
"eraser": "消しゴム"
|
||||
"eraser": "消しゴム",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "キャンバス操作",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "図形"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "キャンバスを移動するには、マウスホイールまたはスペースバーを押しながらドラッグします",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "クリックすると複数の頂点からなる曲線を開始、ドラッグすると直線",
|
||||
"freeDraw": "クリックしてドラッグします。離すと終了します",
|
||||
"text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "自分のライブラリを公開",
|
||||
"bindTextToElement": "Enterを押してテキストを追加",
|
||||
"deepBoxSelect": "CtrlOrCmd を押し続けることでドラッグを抑止し、深い選択を行います",
|
||||
"eraserRevert": "Alt を押し続けることで削除マークされた要素を元に戻す"
|
||||
"eraserRevert": "Alt を押し続けることで削除マークされた要素を元に戻す",
|
||||
"firefox_clipboard_write": "この機能は、\"dom.events.asyncClipboard.clipboardItem\" フラグを \"true\" に設定することで有効になる可能性があります。Firefox でブラウザーの設定を変更するには、\"about:config\" ページを参照してください。"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "プレビューを表示できません",
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG armeɣtu.",
|
||||
"cannotResolveCollabServer": "Ulamek tuqqna s aqeddac n umyalel. Ma ulac uɣilif ales asali n usebter sakin eɛreḍ tikkelt-nniḍen.",
|
||||
"importLibraryError": "Ur d-ssalay ara tamkarḍit",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Ulamek asekles deg uzadur n yisefka deg ugilal. Ma ikemmel wugur, isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem.",
|
||||
"collabSaveFailed_sizeExceeded": "Ulamek asekles deg uzadur n yisefka deg ugilal, taɣzut n usuneɣ tettban-d temqer aṭas. Isefk ad teskelseḍ afaylu s wudem adigan akken ad tetḥeqqeḍ ur tesruḥuyeḍ ara amahil-inek•inem."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Tafrayt",
|
||||
@ -217,9 +217,10 @@
|
||||
"text": "Aḍris",
|
||||
"library": "Tamkarḍit",
|
||||
"lock": "Eǧǧ afecku n tefrayt yermed mbaɛd asuneɣ",
|
||||
"penMode": "",
|
||||
"penMode": "Askar n yimru - gdel tanalit",
|
||||
"link": "Rnu/leqqem aseɣwen i talɣa yettwafernen",
|
||||
"eraser": "Sfeḍ"
|
||||
"eraser": "Sfeḍ",
|
||||
"hand": "Afus (afecku n usmutti n tmuɣli)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Tigawin n teɣzut n usuneɣ",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Talɣiwin"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Akken ad tesmuttiḍ taɣzut n usuneɣ, ṭṭef ṛṛuda n umumed, neɣ afeggag n tallunt mi ara tzuɣreḍ",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Ssit akken ad tebduḍ aṭas n tenqiḍin, zuɣer i yiwen n yizirig",
|
||||
"freeDraw": "Ssit yerna zuɣer, serreḥ ticki tfukeḍ",
|
||||
"text": "Tixidest: tzemreḍ daɣen ad ternuḍ aḍris s usiti snat n tikkal anida tebɣiḍ s ufecku n tefrayt",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Siẓreg tamkarḍit-inek•inem",
|
||||
"bindTextToElement": "Ssed ɣef kcem akken ad ternuḍ aḍris",
|
||||
"deepBoxSelect": "Ṭṭef CtrlOrCmd akken ad tferneḍ s telqey, yerna ad trewleḍ i uzuɣer",
|
||||
"eraserRevert": "Ssed Alt akken ad tsefsxeḍ iferdisen yettwacerḍen i tukksa"
|
||||
"eraserRevert": "Ssed Alt akken ad tsefsxeḍ iferdisen yettwacerḍen i tukksa",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Ulamek abeqqeḍ n teskant",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": ""
|
||||
"eraser": "",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "선택된 도구 유지하기",
|
||||
"penMode": "펜 모드 - 터치 방지",
|
||||
"link": "선택한 도형에 대해서 링크를 추가/업데이트",
|
||||
"eraser": "지우개"
|
||||
"eraser": "지우개",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "캔버스 동작",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "모양"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "캔버스를 옮기려면 마우스 휠이나 스페이스바를 누르고 드래그하기",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "여러 점을 연결하려면 클릭하고, 직선을 그리려면 바로 드래그하세요.",
|
||||
"freeDraw": "클릭 후 드래그하세요. 완료되면 놓으세요.",
|
||||
"text": "팁: 선택 툴로 아무 곳이나 더블 클릭해 텍스트를 추가할 수도 있습니다.",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "당신만의 라이브러리를 게시하기",
|
||||
"bindTextToElement": "Enter 키를 눌러서 텍스트 추가하기",
|
||||
"deepBoxSelect": "CtrlOrCmd 키를 눌러서 깊게 선택하고, 드래그하지 않도록 하기",
|
||||
"eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기"
|
||||
"eraserRevert": "Alt를 눌러서 삭제하도록 지정된 요소를 되돌리기",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "미리보기를 볼 수 없습니다",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "ئامێرە دیاریکراوەکان چالاک بهێڵەوە دوای وێنەکێشان",
|
||||
"penMode": "شێوازی قەڵەم - دەست لێدان ڕابگرە",
|
||||
"link": "زیادکردن/ نوێکردنەوەی لینک بۆ شێوەی دیاریکراو",
|
||||
"eraser": "سڕەر"
|
||||
"eraser": "سڕەر",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "کردارەکانی تابلۆ",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "شێوەکان"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "بۆ جوڵاندنی تابلۆ، لە کاتی ڕاکێشاندا ویلی ماوس یان شریتی بۆشایی دابگرە",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "کرتە بکە بۆ دەستپێکردنی چەند خاڵێک، ڕایبکێشە بۆ یەک هێڵ",
|
||||
"freeDraw": "کرتە بکە و ڕایبکێشە، کاتێک تەواو بوویت دەست هەڵگرە",
|
||||
"text": "زانیاری: هەروەها دەتوانیت دەق زیادبکەیت بە دوو کرتەکردن لە هەر شوێنێک لەگەڵ ئامڕازی دەستنیشانکردن",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "کتێبخانەی تایبەت بە خۆت بڵاوبکەرەوە",
|
||||
"bindTextToElement": "بۆ زیادکردنی دەق enter بکە",
|
||||
"deepBoxSelect": "CtrlOrCmd ڕابگرە بۆ هەڵبژاردنی قووڵ، و بۆ ڕێگریکردن لە ڕاکێشان",
|
||||
"eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە"
|
||||
"eraserRevert": "بۆ گەڕاندنەوەی ئەو توخمانەی کە بۆ سڕینەوە نیشانە کراون، Alt ڕابگرە",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "ناتوانرێ پێشبینین پیشان بدرێت",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Baigus piešti, išlaikyti pasirinktą įrankį",
|
||||
"penMode": "Rašyklio režimas - neleisti prisilietimų",
|
||||
"link": "Pridėti / Atnaujinti pasirinktos figūros nuorodą",
|
||||
"eraser": "Trintukas"
|
||||
"eraser": "Trintukas",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Veiksmai su drobe",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Figūros"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Norint judinti drobę, judink pelę kartu įspaudus pelės ratuką arba tarpo klavišą",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Paspaudimai sukurs papildomus taškus, nepertraukiamas tempimas sukurs liniją",
|
||||
"freeDraw": "Spausk ir tempk, paleisk kai norėsi pabaigti",
|
||||
"text": "Užuomina: tekstą taip pat galima pridėti bet kur su dvigubu pelės paspaudimu, kol parinkas žymėjimo įrankis",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Paturēt izvēlēto rīku pēc darbības",
|
||||
"penMode": "Pildspalvas režīms – novērst pieskaršanos",
|
||||
"link": "Pievienot/rediģēt atlasītās figūras saiti",
|
||||
"eraser": "Dzēšgumija"
|
||||
"eraser": "Dzēšgumija",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Tāfeles darbības",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formas"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Lai bīdītu tāfeli, turiet nospiestu ritināšanas vai atstarpes taustiņu, velkot ar peli",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Klikšķiniet, lai sāktu zīmēt vairākus punktus; velciet, lai zīmētu līniju",
|
||||
"freeDraw": "Spiediet un velciet; atlaidiet, kad pabeidzat",
|
||||
"text": "Ieteikums: lai pievienotu tekstu, varat arī jebkur dubultklikšķināt ar atlases rīku",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publicēt savu bibliotēku",
|
||||
"bindTextToElement": "Spiediet ievades taustiņu, lai pievienotu tekstu",
|
||||
"deepBoxSelect": "Turient nospiestu Ctrl vai Cmd, lai atlasītu dziļumā un lai nepieļautu objektu pavilkšanu",
|
||||
"eraserRevert": "Turiet Alt, lai noņemtu elementus no dzēsšanas atlases"
|
||||
"eraserRevert": "Turiet Alt, lai noņemtu elementus no dzēsšanas atlases",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Nevar rādīt priekšskatījumu",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "निवडलेले यंत्र चित्रकरण झाल्या नंतर ही सक्रिय ठेवा",
|
||||
"penMode": "पेन चा मोड - स्पर्श टाळा",
|
||||
"link": "निवडलेल्या आकारासाठी दुवा जोडा/बदल करा",
|
||||
"eraser": "खोड रबर"
|
||||
"eraser": "खोड रबर",
|
||||
"hand": "हात ( सरकवण्या चे उपकरण)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "पटल क्रिया",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "आकार"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "पटल हलवण्यासाठी, ड्रैग करताना माउस वील ला पकड़ा किव्हा स्पेसबार दाबून ठेवा",
|
||||
"canvasPanning": "कॅनव्हास सरकवण्या साठी, ड्रॅग करताना माउस व्हील धरा किंवा स्पेसबार दाबून ठेवा अथवा हात वालं उपकरण वापरा",
|
||||
"linearElement": "अनेक बिंदु साठी क्लिक करा, रेघे साठी ड्रैग करा",
|
||||
"freeDraw": "क्लिक आणि ड्रैग करा, झालं तेव्हा सोडा",
|
||||
"text": "टीप: तुम्हीं निवड यंत्रानी कोठेही दुहेरी क्लिक करून टेक्स्ट जोडू शकता",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "आपला खाजगी संग्रह प्रकाशित करा",
|
||||
"bindTextToElement": "मजकूर जोडण्यासाठी एंटर की दाबा",
|
||||
"deepBoxSelect": "खोल निवड ह्या साठी कंट्रोल किव्हा कमांड दाबून ठेवा, आणि बाहेर खेचणे वाचवण्या साठी पण",
|
||||
"eraserRevert": "खोडण्या साठी घेतलेल्या वस्तु ना घेण्या साठी Alt दाबून ठेवावे"
|
||||
"eraserRevert": "खोडण्या साठी घेतलेल्या वस्तु ना घेण्या साठी Alt दाबून ठेवावे",
|
||||
"firefox_clipboard_write": "हे वैशिष्ट्य \"dom.events.asyncClipboard.clipboardItem\" फ्लॅग \"सत्य\" वर सेट करून शक्यतो सक्षम केले जाऊ शकते. Firefox मध्ये ब्राउझर फ्लॅग बदलण्यासाठी, \"about:config\" पृष्ठावर जा."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "पूर्वावलोकन दाखवू शकत नाही",
|
||||
@ -448,15 +450,15 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
"center_heading": "तुमचा सर्व डेटा तुमच्या ब्राउझरमध्ये स्थानिक पातळीवर जतन केला जातो.",
|
||||
"center_heading_plus": "त्याऐवजी तुम्हाला Excalidraw+ वर जायचे आहे का?",
|
||||
"menuHint": "निर्यात, आवड़ी-निवडी, भाषा, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
"menuHint": "निर्यात, आवड़ी निवडी आणि आणकिही...",
|
||||
"center_heading": "आकृत्या. काढणे. सोपे.",
|
||||
"toolbarHint": "एक साधन निवडा आणि चित्रीकरण सुरु करा!",
|
||||
"helpHint": "शॉर्टकट आणि सहाय्य"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "ရွေးချယ်ထားသောကိရိယာကိုသာဆက်သုံး",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": ""
|
||||
"eraser": "",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "ကားချပ်လုပ်ဆောင်ချက်",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "နမူနာမပြသနိုင်ပါ",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Behold merket verktøy som aktivt",
|
||||
"penMode": "Pennemodus - forhindre berøring",
|
||||
"link": "Legg til / oppdater link for en valgt figur",
|
||||
"eraser": "Viskelær"
|
||||
"eraser": "Viskelær",
|
||||
"hand": "Hånd (panoreringsverktøy)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Handlinger: lerret",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Former"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "For å flytte lerretet, hold musehjulet eller mellomromstasten mens du drar",
|
||||
"canvasPanning": "For å flytte lerretet, hold musehjulet eller mellomromstasten mens du drar, eller bruk hånd-verktøyet",
|
||||
"linearElement": "Klikk for å starte linje med flere punkter, eller dra for en enkel linje",
|
||||
"freeDraw": "Klikk og dra, slipp når du er ferdig",
|
||||
"text": "Tips: du kan også legge til tekst ved å dobbeltklikke hvor som helst med utvalgsverktøyet",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publiser ditt eget bibliotek",
|
||||
"bindTextToElement": "Trykk Enter for å legge til tekst",
|
||||
"deepBoxSelect": "Hold CTRL/CMD for å markere dypt og forhindre flytting",
|
||||
"eraserRevert": "Hold Alt for å reversere elementene merket for sletting"
|
||||
"eraserRevert": "Hold Alt for å reversere elementene merket for sletting",
|
||||
"firefox_clipboard_write": "Denne funksjonen kan sannsynligvis aktiveres ved å sette \"dom.events.asyncClipboard.clipboardItem\" flagget til \"true\". For å endre nettleserens flagg i Firefox, besøk \"about:config\"-siden."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Kan ikke vise forhåndsvisning",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Geselecteerde tool actief houden na tekenen",
|
||||
"penMode": "Pen modus - Blokkeer aanraken",
|
||||
"link": "",
|
||||
"eraser": "Gum"
|
||||
"eraser": "Gum",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Canvasacties",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Vormen"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Om canvas te verplaatsen, houd muiswiel of spatiebalk ingedrukt tijdens slepen",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Klik om meerdere punten te starten, sleep voor één lijn",
|
||||
"freeDraw": "Klik en sleep, laat los als je klaar bent",
|
||||
"text": "Tip: je kunt tekst toevoegen door ergens dubbel te klikken met de selectietool",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publiceer je eigen bibliotheek",
|
||||
"bindTextToElement": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Kan voorbeeld niet tonen",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "Hald fram med valt verktøy",
|
||||
"penMode": "",
|
||||
"link": "Legg til/ oppdater lenke til valt figur",
|
||||
"eraser": "Viskelêr"
|
||||
"eraser": "Viskelêr",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Handlingar: lerret",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formar"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "For å flytte lerretet, hald inne musehjulet eller mellomromstasten medan du dreg",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Klikk for å starte linje med fleire punkt, eller drag for ei enkel linje",
|
||||
"freeDraw": "Klikk og drag, slepp når du er ferdig",
|
||||
"text": "Tips: du kan òg leggje til tekst ved å dobbeltklikke kor som helst med utvalgsverktyet",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publiser ditt eige bibliotek",
|
||||
"bindTextToElement": "Trykk på enter for å legge til tekst",
|
||||
"deepBoxSelect": "Hald inne Ctrl / Cmd for å velje djupt, og forhindre flytting",
|
||||
"eraserRevert": "Hald inne Alt for å reversere markering av element for sletting"
|
||||
"eraserRevert": "Hald inne Alt for å reversere markering av element for sletting",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Kan ikkje vise førehandsvising",
|
||||
|
@ -38,8 +38,8 @@
|
||||
"arrowhead_bar": "Barra",
|
||||
"arrowhead_dot": "Ponch",
|
||||
"arrowhead_triangle": "Triangle",
|
||||
"fontSize": "Talha poliça",
|
||||
"fontFamily": "Familha de poliça",
|
||||
"fontSize": "Talha polissa",
|
||||
"fontFamily": "Familha de polissa",
|
||||
"onlySelected": "Seleccion sonque",
|
||||
"withBackground": "Rèireplan",
|
||||
"exportEmbedScene": "Scèna embarcada",
|
||||
@ -106,8 +106,8 @@
|
||||
"toggleTheme": "Alternar tèma",
|
||||
"personalLib": "Bibliotèca personala",
|
||||
"excalidrawLib": "Bibliotèca Excalidraw",
|
||||
"decreaseFontSize": "Reduire talha poliça",
|
||||
"increaseFontSize": "Aumentar talha poliça",
|
||||
"decreaseFontSize": "Reduire talha polissa",
|
||||
"increaseFontSize": "Aumentar talha polissa",
|
||||
"unbindText": "Dessociar lo tèxte",
|
||||
"bindText": "Ligar lo tèxt al contenidor",
|
||||
"link": {
|
||||
@ -219,7 +219,8 @@
|
||||
"lock": "Mantenir activa l’aisina aprèp dessenhar",
|
||||
"penMode": "Mòde estilo - empachar lo contact",
|
||||
"link": "Apondre/Actualizar lo ligam per una fòrma seleccionada",
|
||||
"eraser": "Goma"
|
||||
"eraser": "Goma",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Accions del canabàs",
|
||||
@ -227,7 +228,7 @@
|
||||
"shapes": "Formas"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "Per desplaçar los canabasses, tenètz la rodeta de la mirga o la barra d’espaci pendent lo desplaçament",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Clicatz per començar mantun punt, lisatz per una sola linha",
|
||||
"freeDraw": "Clicatz e lisatz, relargatz un còp acabat",
|
||||
"text": "Astúcia : podètz tanben apondre de tèxt en doble clicant ont que siá amb l’aisina de seleccion",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "Publicar vòstra pròpria bibliotèca",
|
||||
"bindTextToElement": "Quichatz Entrada per apondre de tèxte",
|
||||
"deepBoxSelect": "Gardar CtrlOCmd per una seleccion gropada e empachar lo desplaçament",
|
||||
"eraserRevert": "Tenètz quichat Alt per anullar los elements marcats per supression"
|
||||
"eraserRevert": "Tenètz quichat Alt per anullar los elements marcats per supression",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Afichatge impossible de l’apercebut",
|
||||
|
@ -219,7 +219,8 @@
|
||||
"lock": "ਡਰਾਇੰਗ ਤੋਂ ਬਾਅਦ ਵੀ ਚੁਣੇ ਹੋਏ ਸੰਦ ਨੂੰ ਸਰਗਰਮ ਰੱਖੋ ",
|
||||
"penMode": "",
|
||||
"link": "",
|
||||
"eraser": "ਰਬੜ"
|
||||
"eraser": "ਰਬੜ",
|
||||
"hand": ""
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "ਕੈਨਵਸ ਦੀਆਂ ਕਾਰਵਾਈਆਂ",
|
||||
@ -245,7 +246,8 @@
|
||||
"publishLibrary": "ਆਪਣੀ ਲਾਇਬ੍ਰੇਰੀ ਪ੍ਰਕਾਸ਼ਿਤ ਕਰੋ",
|
||||
"bindTextToElement": "ਪਾਠ ਜੋੜਨ ਲਈ ਐੰਟਰ ਦਬਾਓ",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": ""
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": ""
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "ਝਲਕ ਨਹੀਂ ਦਿਖਾ ਸਕਦੇ",
|
||||
|