Merge pull request #101 from mathuo/100-docs

chore: docs
This commit is contained in:
mathuo 2022-05-26 21:43:04 +01:00 committed by GitHub
commit faf4103f6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 13878 additions and 5018 deletions

View File

@ -28,15 +28,16 @@ jobs:
${{ runner.os }}-node-
- run: yarn install
- run: npm run bootstrap
- run: npm run build
- run: npm run test
working-directory: docs
- run: yarn build
working-directory: docs
- run: npm run deploy-docs
working-directory: docs
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: docs # The folder the action should deploy.
TARGET_FOLDER: docs
FOLDER: build # The folder the action should deploy.
TARGET_FOLDER: .
CLEAN: true # Automatically remove deleted files from the deploy branch

View File

@ -1,42 +0,0 @@
name: Deploy Nightly Demo App
on:
schedule:
- cron: '0 3 * * *' # every day at 3 am UTC
# on: [push]
jobs:
deploy-nightly-demo-app:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2.3.1 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '16.x'
- uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-
- run: yarn
- run: npm run bootstrap
- run: npm run build
- run: npm run test
- run: npm run package-all
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: output # The folder the action should deploy.
TARGET_FOLDER: output
CLEAN: true # Automatically remove deleted files from the deploy branch

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ storybook-static/
test-report.xml
*.code-workspace
yarn-error.log
/build

199
README.md
View File

@ -1,7 +1,7 @@
<div align="center">
<h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support</p>
<p>Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support written in TypeScript</p>
</div>
@ -15,12 +15,7 @@
##
A zero dependency layout manager based on the layering of splitview with support for ReactJS components, written in TypeScript.
- [➡️](https://mathuo.github.io/dockview/docs) Documentation
- [➡️](https://mathuo.github.io/dockview/) Live demo
- [➡️](https://github.com/mathuo/dockview/tree/master/packages/dockview-demo/src/stories) Code examples
- [➡️](https://mathuo.github.io/dockview/output/docs/index.html) Generated TypeDocs
Please see the website: https://mathuo.github.io/dockview/docs
Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/
@ -34,27 +29,17 @@ Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@l
- Tabular docking and Drag and Drop support
- Documentation and examples
## Table of contents
- [Quick start](#quick-start)
- [Sandbox examples](#sandbox-examples)
- [Serializated layouts](#serializated-layouts)
- [Drag and drop](#drag-and-drop)
- [Theming](#theming)
- [Performance](#performance)
- [FAQ](#faq)
This project was inspired by many popular IDE editors. Some parts of the core resizable panelling are inspired by code found in the VSCode codebase, [splitview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview) and [gridview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/grid).
## Quick start
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). Please see the [Getting Started Guide](https://mathuo.github.io/dockview/docs/).
```
npm install --save dockview
```
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css`. For example:
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
```css
@import '~dockview/dist/styles/dockview.css';
@ -65,179 +50,3 @@ You should also attach a dockview theme to an element containing your components
```html
<body classname="dockview-theme-dark"></body>
```
Demonstrated below is a high level example of a `DockviewReact` component. You can follow a similar pattern for `GridviewReact`, `SplitviewReact` and `PaneviewReact` components too, see examples for more.
```tsx
import {
DockviewReact,
DockviewReadyEvent,
PanelCollection,
IDockviewPanelProps,
IDockviewPanelHeaderProps,
} from 'dockview';
const components: PanelCollection<IDockviewPanelProps> = {
default: (props: IDockviewPanelProps<{ someProps: string }>) => {
return <div>{props.params.someProps}</div>;
},
};
const headers: PanelCollection<IDockviewPanelHeaderProps> = {
customTab: (props: IDockviewPanelHeaderProps) => {
return (
<div>
<span>{props.api.title}</span>
<span onClick={() => props.api.close()}>{'[x]'}</span>
</div>
);
},
};
const Component = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel1',
component: 'default',
tabComponent: 'customTab', // optional custom header
params: {
someProps: 'Hello',
},
});
event.api.addPanel({
id: 'panel2',
component: 'default',
params: {
someProps: 'World',
},
position: { referencePanel: 'panel1', direction: 'below' },
});
};
return (
<DockviewReact
components={components}
tabComponents={headers} // optional headers renderer
onReady={onReady}
/>
);
};
```
Specifically for `DockviewReact` there exists higher-order components to encapsulate both the tab and contents into one logical component for the user making state sharing between the two simple, which is an optional feature.
```tsx
const components: PanelCollection<IDockviewPanelProps> = {
default: (props: IDockviewPanelProps<{ someProps: string }>) => {
return <div>{props.params.someProps}</div>;
},
fancy: (props: IDockviewPanelProps) => {
return (
<DockviewComponents.Panel>
<DockviewComponents.Tab>
<div>
<span>{props.api.title}</span>
<span onClick={() => props.api.close()}>{'Close'}</span>
</div>
</DockviewComponents.Tab>
<DockviewComponents.Content>
<div>{'Hello world'}</div>
</DockviewComponents.Content>
</DockviewComponents.Panel>
);
},
};
```
## Sandbox examples
- [Dockview](https://codesandbox.io/s/simple-dockview-t6491)
- [Gridview](https://codesandbox.io/s/simple-gridview-jrp0n)
- [Splitview](https://codesandbox.io/s/simple-splitview-l53nn)
- [Paneview](https://codesandbox.io/s/simple-paneview-v8qvb)
## Serializated layouts
All view components support the methods `toJSON()`, `fromJSON(...)` and `onDidLayoutChange()`.
See example [here](https://codesandbox.io/s/workspace-saving-example-euo5d).
## Drag and drop
Both `DockviewReact` and `PaneviewReact` support drag and drop functionality out of the box.
- `DockviewReact` allows tabs to be repositioned using drag and drop.
- `PaneviewReact` allows the repositioning of views using drag and drop on the header section.
You can use the utility methods `getPaneData` and `getPanelData` to manually extract the data transfer metadata associated with an active drag and drop event for either of the above components.
```tsx
import {
getPaneData,
getPanelData,
PanelTransfer,
PaneTransfer,
} from 'dockview';
const panelData: PanelTransfer | undefined = getPanelData();
if (panelData) {
// DockviewReact: data transfer metadata associated with the active drag and drop event
const { viewId, groupId, panelId } = panelData; // deconstructed object
}
const paneData: PaneTransfer | undefined = getPaneData();
if (paneData) {
// PaneviewReact: data transfer metadata associated with the active drag and drop event
const { viewId, paneId } = paneData; // deconstructed object
}
```
You can also intercept custom drag and drop events allowing dockview components to interact with and react to external drag events. `PaneviewReact` supports the method `onDidDrop` and `DockviewReact` supports the methods `onDidDrop` and `showDndOverlay`.
## Theming
The theme can be customized using the below set of CSS properties. You can find the built in themes [here](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/theme.scss) which could be used as an example to extend upon or build your own theme.
| CSS Property | Description |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **General** |
| --dv-active-sash-color | The background color a dividing sash during an interaction |
| --dv-separator-border | The color of the seperator between panels |
| **Paneview** |
| --dv-paneview-header-border-color | - |
| --dv-paneview-active-outline-color | The primary accent color, used for example to highlight the active panel in Paneviews |
| **Dockview -> Dragging** |
| --dv-drag-over-background-color | The overlay color applied to a group when a moving tab is dragged over |
| **Dockview -> Tabs container** |
| --dv-tabs-and-actions-container-font-size | - |
| --dv-tabs-and-actions-container-height | Default tab height |
| --dv-tabs-and-actions-container-background-color | - |
| --dv-tabs-container-scrollbar-color | - |
| --dv-group-view-background-color | - |
| **Dockview -> Tabs** | (see [dockviewComponent.scss](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/dockview/dockviewComponent.scss)) |
| --dv-activegroup-visiblepanel-tab-background-color | The background color of the tab for the visible panel in the active group |
| --dv-activegroup-hiddenpanel-tab-background-color | The background color of the tab for the hidden panel/s in the active group |
| --dv-inactivegroup-visiblepanel-tab-background-color | The background color of the tab for the visible panel in groups other than the active group |
| --dv-inactivegroup-hiddenpanel-tab-background-color | The background color of the tab for the hidden panel/s in groups other than the active group |
| --dv-activegroup-visiblepanel-tab-color | The color of the tab for the visible panel in the active group |
| --dv-activegroup-hiddenpanel-tab-color | The color of the tab for the hidden panel/s in the active group |
| --dv-inactivegroup-visiblepanel-tab-color | The color of the tab for the visible panel in groups other than the active group |
| --dv-inactivegroup-hiddenpanel-tab-color | The color of the tab for the hidden panel/s in groups other than the active group |
| --dv-tab-divider-color | - |
| --dv-tab-close-icon | Default tab close icon |
## Performance
Consider using React.lazy(...) to defer the importing of your panels until they are required. This has the potential to reduce the initial import cost when your application starts.
## FAQ
**Q: Can I use this library without React?**
**A:** In theory, yes. The library is written in plain-old JS and the parts written in ReactJS are merely wrappers around the plain-old JS components. Currently everything is published as one package though so maybe that's something to change in the future.
**Q: Can I use this library with AngularJS/Vue.js or any other arbitrarily named JavaScript library/framework?**
**A:** Yes but with some extra work. Dockview is written in plain-old JS so you can either interact directly with the plain-old JS components or create a wrapper using your prefered library/framework. The React wrapper may give some ideas on how this wrapper implementation could be done for other libraries/frameworks. Maybe that's something to change in the future.

20
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

41
docs/README.md Normal file
View File

@ -0,0 +1,41 @@
# Website
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
### Installation
```
$ yarn
```
### Local Development
```
$ yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
```
$ yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```
$ USE_SSH=true yarn deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

3
docs/babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@ -0,0 +1,24 @@
---
slug: dockview-1.4.1-release
title: Dockview 1.4.1
tags: [release]
---
# Release Notes
## 🚀 Features
- Fix Drag and Drop issues in Dockview on Firefox [#103](https://github.com/mathuo/dockview/pull/103)
## 🛠 Miscs
- Documentation enhancements https://mathuo.github.io/dockview/docs/
## 🔥 Breaking changes
All breaking changes here are designed to simplify the library with only one way to do something.
- Remove `setVisible` and `setActive` from the Splitview API. You can still achieve the same behaviors through calling `setVisible` and `setActive` on the Splitview Panel API. [#105](https://github.com/mathuo/dockview/pull/105)
- Remove `setVisible`, `setActive` and `toggleVisiblity` from Gridview API. You can still achieve the same behaviors through calling `setVisible` and `setActive` on the Gridview Panel API [#105](https://github.com/mathuo/dockview/pull/105)
- Remove `onFocusEvent` from Panel API as this was not intended to be a public method. You can use `onDidFocusChange` instead [#105](https://github.com/mathuo/dockview/pull/105)
- Remove HOC `<DockviewComponents.Panel\>`, `<DockviewComponents.Content>`, `<DockviewComponents.Tab>` and `<DockviewComponents.Actions>` [#105](https://github.com/mathuo/dockview/pull/105)

View File

@ -0,0 +1,15 @@
---
slug: dockview-1.4.2-release
title: Dockview 1.4.2
tags: [release]
---
# Release Notes
## 🚀 Features
- Fix deserialization issue where previously active panel wasn't display correctly after deserialization [#108](https://github.com/mathuo/dockview/pull/108)
## 🔥 Breaking changes
- Rename `onDidAddGroup` to `onDidAddPanel`, `onDidRemoveGroup` to `onDidRemovePanel` and `onDidActiveGroupChange` to `onDidActivePanelChange` on the Gridview API [#106](https://github.com/mathuo/dockview/pull/106)

View File

@ -0,0 +1,24 @@
---
slug: dockview-1.4.3-release
title: Dockview 1.4.3
tags: [release]
---
# Release Notes
## 🚀 Features
- Small adjusted to behaviours of default paneview header componnet [#116](https://github.com/mathuo/dockview/pull/116) [#120](https://github.com/mathuo/dockview/pull/120)
- Improved support for external dnd events in the dockview component. `showDndOverlay` prop on `DockviewReact` exposes more parameters to interact with [#110](https://github.com/mathuo/dockview/pull/110)
- Improved to underlying events exposes through all components [#114](https://github.com/mathuo/dockview/pull/114)
- Add .clear() to the component APIs providing an easy way to clear a layout [#119](https://github.com/mathuo/dockview/pull/119)
- Udate orientation via componnet APIs is now working correctly [#119](https://github.com/mathuo/dockview/pull/119)
## 🛠 Miscs
- Documentation enhancements [#101](https://github.com/mathuo/dockview/pull/101)
- Move documentation to [dockview.dev](https://dockview.dev)
## 🔥 Breaking changes
- Fix typo by renaming `onDidLayoutfromJSON` to `onDidLayoutFromJSON` in dockview component api [#112](https://github.com/mathuo/dockview/pull/112/files)

17
docs/blog/authors.yml Normal file
View File

@ -0,0 +1,17 @@
endi:
name: Endilie Yacop Sucipto
title: Maintainer of Docusaurus
url: https://github.com/endiliey
image_url: https://github.com/endiliey.png
yangshun:
name: Yangshun Tay
title: Front End Engineer @ Facebook
url: https://github.com/yangshun
image_url: https://github.com/yangshun.png
slorber:
name: Sébastien Lorber
title: Docusaurus maintainer
url: https://sebastienlorber.com
image_url: https://github.com/slorber.png

View File

@ -0,0 +1,7 @@
{
"label": "API",
"position": 2,
"link": {
"type": "generated-index"
}
}

View File

@ -1,6 +1,17 @@
import { SimpleDockview } from '../components/simpleDockview';
import { SimpleDockview } from '../../src/components/simpleDockview';
import {
RenderingDockview,
Checkbox,
} from '../../src/components/dockview/rendering';
import { DndDockview } from '../../src/components/dockview/dnd';
import { EventsDockview } from '../../src/components/dockview/events';
import Link from '@docusaurus/Link';
Dockview is an abstraction built on top of [Gridviews](/gridview) where each view is a tabbed container.
# Dockview
## Introduction
Dockview is an abstraction built on top of [Gridviews](/docs/api/gridview) where each view is a tabbed container.
<div
style={{
@ -21,41 +32,24 @@ const anotherPanel = event.api.getPanel('somePanelid');
You can access the panels associated group through the `panel.group` variable.
The group will always be defined and will change if a panel is moved into another group.
### Locked group
Locking a group will disable all drop events for this group ensuring a user can not add additional panels to the group.
You can still add groups to a locked panel programatically using the api.
```tsx
panel.group.locked = true;
```
## Group header
You may wish to hide the header section of a group. This can achieved through setting the `hidden` variable on `panel.group.header`.
```tsx
panel.group.header.hidden = true;
```
## Component Props
## DockviewReact Component
```tsx
import { ReactDockview } from 'dockview';
```
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ------- | ------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| tabComponents | object | Yes | | |
| watermarkComponent | object | Yes | | |
| hideBorders | boolean | Yes | false | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | See [Auto resizing](/basics/#auto-resizing) |
| onTabContextMenu | Event | Yes | false | |
| onDidDrop | Event | Yes | false | |
| showDndOverlay | Event | Yes | false | |
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ------- | --------------------------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| tabComponents | object | Yes | | |
| watermarkComponent | object | Yes | | |
| hideBorders | boolean | Yes | false | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | See <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| onTabContextMenu | Event | Yes | false | |
| onDidDrop | Event | Yes | false | |
| showDndOverlay | Event | Yes | false | |
## Dockview API
@ -73,45 +67,46 @@ const onReady = (event: DockviewReadyEvent) => {
};
```
| Property | Type | Description |
| ---------------------- | ---------------------------------------------------- | ------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumHeight | `number` | |
| maximumHeight | `number` | |
| maximumWidth | `number` | |
| maximumWidth | `number` | |
| length | `number` | Number of panels |
| size | `number` | Number of Groups |
| panels | `IDockviewPanel[]` | |
| groups | `GroupPanel[]` | |
| activePanel | `IDockviewPanel \| undefined` | |
| activeGroup | `IDockviewPanel \| undefined` | |
| | | |
| onDidLayoutChange | `Event<void>` | |
| onDidLayoutFromJSON | `Event<void>` | |
| onDidAddGroup | `Event<GroupPanel>` | |
| onDidRemoveGroup | `Event<GroupPanel>` | |
| onDidActiveGroupChange | `Event<GroupPanel \| undefined>` | |
| onDidAddPanel | `Event<IDockviewPanel>` | |
| onDidRemovePanel | `Event<IDockviewPanel>` | |
| onDidActivePanelChange | `Event<IDockviewPanel \| undefined>` | |
| onDidDrop | `Event<DockviewDropEvent` | |
| | | |
| addPanel | `addPanel(options: AddPanelOptions): IDockviewPanel` | |
| getPanel | `(id: string) \| IDockviewPanel \| undefined` | |
| addEmptyGroup | `(options? AddGroupOptions): void` | |
| closeAllGroups | `(): void` | |
| removeGroup | `(group: GroupPanel): void` | |
| getGroup | `(id: string): GroupPanel \| undefined` | |
| | | |
| getTabHeight | `(): number \| undefined` | |
| setTabHeight | `(hegiht: number \| undefined): void` | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | |
| layout | `(width: number, height:number): void` | See [Auto resizing](/basics/#auto-resizing) |
| fromJSON | `(data: SerializedDockview): void` | See [Serialization](/basics/#serialization) |
| toJSON | `(): SerializedDockview` | See [Serialization](/basics/#serialization) |
| Property | Type | Description |
| ---------------------- | ---------------------------------------------------- | ----------------------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumHeight | `number` | |
| maximumHeight | `number` | |
| maximumWidth | `number` | |
| maximumWidth | `number` | |
| length | `number` | Number of panels |
| size | `number` | Number of Groups |
| panels | `IDockviewPanel[]` | |
| groups | `GroupPanel[]` | |
| activePanel | `IDockviewPanel \| undefined` | |
| activeGroup | `IDockviewPanel \| undefined` | |
| | | |
| onDidLayoutChange | `Event<void>` | |
| onDidLayoutFromJSON | `Event<void>` | |
| onDidAddGroup | `Event<GroupPanel>` | |
| onDidRemoveGroup | `Event<GroupPanel>` | |
| onDidActiveGroupChange | `Event<GroupPanel \| undefined>` | |
| onDidAddPanel | `Event<IDockviewPanel>` | |
| onDidRemovePanel | `Event<IDockviewPanel>` | |
| onDidActivePanelChange | `Event<IDockviewPanel \| undefined>` | |
| onDidDrop | `Event<DockviewDropEvent` | |
| | | |
| addPanel | `addPanel(options: AddPanelOptions): IDockviewPanel` | |
| getPanel | `(id: string) \| IDockviewPanel \| undefined` | |
| addEmptyGroup | `(options? AddGroupOptions): void` | |
| closeAllGroups | `(): void` | |
| removeGroup | `(group: GroupPanel): void` | |
| getGroup | `(id: string): GroupPanel \| undefined` | |
| | | |
| getTabHeight | `(): number \| undefined` | |
| setTabHeight | `(hegiht: number \| undefined): void` | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | |
| layout | `(width: number, height:number): void` | <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| fromJSON | `(data: SerializedDockview): void` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| toJSON | `(): SerializedDockview` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| clear | `(): void` | Clears the current layout |
## Dockview Panel API
@ -134,7 +129,6 @@ const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
| onDidFocusChange | `Event<FocusEvent>` | |
| onDidVisibilityChange | `Event<VisibilityEvent>` | |
| onDidActiveChange | `Event<ActiveEvent>` | |
| onFocusEvent | `Event<void>` | |
| setActive | `(): void` | |
| | | |
| onDidConstraintsChange | `onDidConstraintsChange: Event<PanelConstraintChangeEvent>` | |
@ -147,3 +141,145 @@ const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
| suppressClosable | `boolean` | |
| close | `(): void` | |
| setTitle | `(title: string): void` | |
## Advanced Features
### Locked group
Locking a group will disable all drop events for this group ensuring a user can not add additional panels to the group.
You can still add groups to a locked panel programatically using the api.
```tsx
panel.group.locked = true;
```
### Group header
You may wish to hide the header section of a group. This can achieved through setting the `hidden` variable on `panel.group.header`.
```tsx
panel.group.header.hidden = true;
```
### Context Menu
import { ContextMenuDockview } from '../../src/components/dockview/contextMenu';
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<ContextMenuDockview />
</div>
### Rendering
Although `DockviewReact` will only add those tabs that are visible to the DOM all associated React Components for each tab including those that
are not initially visible will be created.
This will mean that any hooks in those components will run and if you running expensive operations in the tabs you may end up doing a lot of initial
work for what are hidden tabs.
This is the default behaviour to ensure the greatest flexibility for the user but you can create a Higher-Order component wrapping your components that
will ensure the component is only created if the tab is visible as below:
```tsx
import { PanelApi } from 'dockview';
import * as React from 'react';
function RenderWhenVisible<
T extends { api: Pick<PanelApi, 'isVisible' | 'onDidVisibilityChange'> }
>(component: React.FunctionComponent<T>) {
const HigherOrderComponent = (props: T) => {
const [visible, setVisible] = React.useState<boolean>(
props.api.isVisible
);
React.useEffect(() => {
const disposable = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);
return () => {
disposable.dispose();
};
}, [props.api]);
if (!visible) {
return null;
}
return React.createElement(component, props);
};
return HigherOrderComponent;
}
```
```tsx
const component = RenderWhenVisible(MyComponent);
```
Through toggling the checkbox you can see that when you only render those panels which are visible the underling React component is destroyed when it becomes hidden and re-created when it becomes visible.
<Checkbox />
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<RenderingDockview renderVisibleOnly={false} />
</div>
### Drag And Drop
The component exposes some method to help determine whether external drag events should be interacted with or not.
```tsx
/**
* called when an ondrop event which does not originate from the dockview libray and
* passes the showDndOverlay condition occurs
**/
const onDidDrop = (event: DockviewDropEvent) => {
const { group } = event;
event.api.addPanel({
id: 'test',
component: 'default',
position: {
referencePanel: group.activePanel.id,
direction: 'within',
},
});
};
/**
* called for drag over events which do not originate from the dockview library
* allowing the developer to decide where the overlay should be shown for a
* particular drag event
**/
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
return true;
};
return (
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-dark"
onDidDrop={onDidDrop}
showDndOverlay={showDndOverlay}
/>
);
```
<DndDockview />
### Events
<EventsDockview />

View File

@ -1,18 +1,37 @@
## Component Props
import { SimpleGridview } from '../../src/components/simpleGridview';
import { EventsGridview } from '../../src/components/gridview/events';
import Link from '@docusaurus/Link';
# Gridview
## Introduction
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<SimpleGridview />
</div>
## GridviewReact Component
```tsx
import { ReactGridview } from 'dockview';
```
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ---------------------- | ------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| orientation | Orientation | Yes | Orientation.HORIZONTAL | |
| proportionalLayout | boolean | Yes | true | |
| hideBorders | boolean | Yes | false | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | See [Auto resizing](/basics/#auto-resizing) |
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ---------------------- | --------------------------------------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| orientation | Orientation | Yes | Orientation.HORIZONTAL | |
| proportionalLayout | boolean | Yes | true | See <Link to="/docs/basics/#proportional-layout">Proportional layout</Link> |
| hideBorders | boolean | Yes | false | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | See <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
## Gridview API
@ -30,37 +49,35 @@ const onReady = (event: GridviewReadyEvent) => {
};
```
| Property | Type | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumHeight | `number` | |
| maximumHeight | `number` | |
| maximumWidth | `number` | |
| maximumWidth | `number` | |
| length | `number` | Number of panels |
| panels | `ISplitviewPanel[]` | |
| orientation | `Orientation` | |
| | | |
| onDidLayoutChange | `Event<void>` | |
| onDidLayoutFromJSON | `Event<void>` | |
| onDidAddGroup | `Event<IGridviewPanel>` | |
| onDidRemoveGroup | `Event<IGridviewPanel>` | |
| onDidActiveGroupChange | `Event<IGridviewPanel \| undefined>` | |
| | | |
| addPanel | `addPanel(options: AddComponentOptions): IGridviewPanel` | |
| removePanel | `(panel: IGridviewPanel, sizing?: Sizing): void` | |
| movePanel | `(panel: IGridviewPanel, options: {direction: Direction, refernece:string, size?: number}): void` | |
| getPanel | `(id: string) \| IGridviewPanel \| undefined` | |
| | | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| setVisible | `(panel: IGridviewPanel, isVisible: boolean): void` | |
| focus | `(): void` | |
| setActive | `(panel: IGridviewPanel): void` | |
| toggleVisiblity | `(panel: IGridviewPanel): void` | |
| layout | `(width: number, height:number): void` | See [Auto resizing](/basics/#auto-resizing) |
| fromJSON | `(data: SerializedGridview): void` | See [Serialization](/basics/#serialization) |
| toJSON | `(): SerializedGridview` | See [Serialization](/basics/#serialization) |
| Property | Type | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumHeight | `number` | |
| maximumHeight | `number` | |
| maximumWidth | `number` | |
| maximumWidth | `number` | |
| length | `number` | Number of panels |
| panels | `ISplitviewPanel[]` | all panels |
| orientation | `Orientation` | |
| | | |
| onDidLayoutChange | `Event<void>` | Fires on layout change |
| onDidLayoutFromJSON | `Event<void>` | Fires of layout change caused by a fromJSON deserialization call |
| onDidAddPanel | `Event<IGridviewPanel>` | Fires when a view is added |
| onDidRemovePanel | `Event<IGridviewPanel>` | Fires when a view is removed |
| onDidActivePanelChange | `Event<IGridviewPanel \| undefined>` | Fires when the active group changes |
| | | |
| addPanel | `addPanel(options: AddComponentOptions): IGridviewPanel` | |
| removePanel | `(panel: IGridviewPanel, sizing?: Sizing): void` | |
| movePanel | `(panel: IGridviewPanel, options: {direction: Direction, refernece:string, size?: number}): void` | |
| getPanel | `(id: string) \| IGridviewPanel \| undefined` | |
| | | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | Focus the active panel, if exists |
| layout | `(width: number, height:number): void` | <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| fromJSON | `(data: SerializedGridview): void` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| toJSON | `(): SerializedGridview` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| clear | `(): void` | Clears the current layout |
## Gridview Panel API
@ -85,10 +102,15 @@ const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => {
| onDidFocusChange | `Event<FocusEvent>` | |
| onDidVisibilityChange | `Event<VisibilityEvent>` | |
| onDidActiveChange | `Event<ActiveEvent>` | |
| onFocusEvent | `Event<void>` | |
| onDidConstraintsChange | `onDidConstraintsChange: Event<PanelConstraintChangeEvent>` | |
| | | |
| setVisible | `(isVisible: boolean): void` | |
| setActive | `(): void` | |
| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | |
| setSize | `(event: SizeEvent): void` | |
## Events
`GridviewReact` exposes a number of events that the developer can listen to and below is a simple example with a log panel showing those events that occur.
<EventsGridview />

View File

@ -1,20 +1,38 @@
## Component Props
import { SimplePaneview } from '../../src/components/simplePaneview';
import Link from '@docusaurus/Link';
# Paneview
# Introduction
<div
style={{
height: '400px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<SimplePaneview />
</div>
## PaneviewReact Component
```tsx
import { ReactPaneview } from 'dockview';
```
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ------- | ------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| headerComponents | object | Yes | | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | See [Auto resizing](/basics/#auto-resizing) |
| disableDnd | boolean | Yes | false | |
| onDidDrop | Event | Yes | | |
| Property | Type | Optional | Default | Description |
| ------------------- | ------------------------------------ | -------- | ------- | ----------------------------------------------------------- |
| onReady | (event: SplitviewReadyEvent) => void | No | | |
| components | object | No | | |
| headerComponents | object | Yes | | |
| className | string | Yes | '' | |
| disableAutoResizing | boolean | Yes | false | <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| disableDnd | boolean | Yes | false | |
| onDidDrop | Event | Yes | | |
## Gridview API
## Paneview API
```tsx
const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => {
@ -30,30 +48,31 @@ const onReady = (event: GridviewReadyEvent) => {
};
```
| Property | Type | Description |
| ------------------- | ---------------------------------------------------------------- | ------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumSize | `number` | |
| maximumSize | `number` | |
| length | `number` | Number of panels |
| panels | `IPaneviewPanel[]` | |
| | | |
| onDidLayoutChange | `Event<void>` | |
| onDidLayoutFromJSON | `Event<void>` | |
| onDidAddView | `Event<IPaneviewPanel>` | |
| onDidRemoveView | `Event<IPaneviewPanel>` | |
| onDidDrop | `Event<PaneviewDropEvent` | |
| | | |
| addPanel | `addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel` | |
| removePanel | `(panel: IPaneviewPanel): void` | |
| movePanel | `(from: number, to: number): void` | |
| getPanel | `(id:string): IPaneviewPanel \| undefined` | |
| | | |
| focus | `(): void` | |
| layout | `(width: number, height:number): void` | See [Auto resizing](/basics/#auto-resizing) |
| fromJSON | `(data: SerializedPaneview): void` | See [Serialization](/basics/#serialization) |
| toJSON | `(): SerializedPaneview` | See [Serialization](/basics/#serialization) |
| Property | Type | Description |
| ------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumSize | `number` | The sum of the `minimumSize` property for each panel |
| maximumSize | `number` | The sum of the `maximumSize` property for each panel |
| length | `number` | Number of panels |
| panels | `IPaneviewPanel[]` | All panels |
| | | |
| onDidLayoutChange | `Event<void>` | Fires on layout change |
| onDidLayoutFromJSON | `Event<void>` | Fires of layout change caused by a fromJSON deserialization call |
| onDidAddView | `Event<IPaneviewPanel>` | Fires when a view is added |
| onDidRemoveView | `Event<IPaneviewPanel>` | Fires when a view is removed |
| onDidDrop | `Event<PaneviewDropEvent` | Fires on an external drop event (See <Link to="/docs/api/paneview/#drag-and-drop">Drag and Drop</Link>) |
| | | |
| addPanel | `addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel` | |
| removePanel | `(panel: IPaneviewPanel): void` | |
| movePanel | `(from: number, to: number): void` | |
| getPanel | `(id:string): IPaneviewPanel \| undefined` | |
| | | |
| focus | `(): void` | Focus the active panel, if exists |
| layout | `(width: number, height:number): void` | See <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| fromJSON | `(data: SerializedPaneview): void` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| toJSON | `(): SerializedPaneview` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| clear | `(): void` | Clears the current layout |
## Gridview Panel API
@ -78,10 +97,89 @@ const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => {
| onDidFocusChange | `Event<FocusEvent>` | |
| onDidVisibilityChange | `Event<VisibilityEvent>` | |
| onDidActiveChange | `Event<ActiveEvent>` | |
| onFocusEvent | `Event<void>` | |
| onDidConstraintsChange | `onDidConstraintsChange: Event<PanelConstraintChangeEvent>` | |
| | |
| setVisible | `(isVisible: boolean): void` | |
| setActive | `(): void` | |
| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | |
| setSize | `(event: SizeEvent): void` | |
## Advanced Features
### Custom Header
The above example shows the default header and will render the `title` along with a small icon to indicate a collapsed or expanded state.
You can provide a custom header:
```tsx
const onReady = (event: PaneviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
headerComponent: 'myHeaderComponent',
params: {
valueA: 'A',
},
title: 'Panel 1',
});
};
```
The above example shows the default header and will render the `title` along with a small icon to indicate a collapsed or expanded state.
You can provide a custom header:
```tsx
const onReady = (event: PaneviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
headerComponent: 'myHeaderComponent',
params: {
valueA: 'A',
},
title: 'Panel 1',
});
};
```
You can define a header component and listen to the expanded state to update the component accordingly.
```tsx
const CustomHeader = (props: IPaneviewPanelProps) => {
const [expanded, setExpanded] = React.useState<boolean>(
props.api.isExpanded
);
React.useEffect(() => {
const disposable = props.api.onDidExpansionChange((event) => {
setExpanded(event.isExpanded);
});
return () => {
disposable.dispose();
};
}, []);
const onClick = () => {
props.api.setExpanded(!expanded);
};
return (
<div>
<a
onClick={onClick}
className={expanded ? 'expanded' : 'collapsed'}
/>
<span>{props.params.title}</span>
</div>
);
};
```
You should provide a value for the `headerComponents` React prop.
```tsx
const headerComponents = { myHeaderComponent: CustomHeader };
```
### Drag And Drop

243
docs/docs/api/splitview.mdx Normal file
View File

@ -0,0 +1,243 @@
import { SimpleSplitview } from '../../src/components/simpleSplitview';
import { SplitviewExample1 } from '../../src/components/splitview/active';
import Link from '@docusaurus/Link';
# Splitview
## Introduction
A Splitview is a collection resizable horizontally or vertically stacked panels.
The Splitview exposes a component level API through the `onReady` event and through the `props.containerApi` variable on the panel props.
A panel level API is exposed on each panel through it's props as `props.api`.
<div
style={{
height: '100px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<SimpleSplitview />
</div>
```tsx title="Simple Splitview example"
import {
ISplitviewPanelProps,
Orientation,
SplitviewReact,
SplitviewReadyEvent,
} from 'dockview';
const components = {
default: (props: ISplitviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
export const SimpleSplitview = () => {
const onReady = (event: SplitviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
};
return (
<SplitviewReact
components={components}
onReady={onReady}
orientation={Orientation.HORIZONTAL}
className="dockview-theme-dark"
/>
);
};
```
## SplitviewReact Component
You can create a Splitview through the use of the `ReactSplitview` component.
```tsx
import { ReactSplitview } from 'dockview';
```
Using the `onReady` prop you can access to the component `api` and add panels either through deserialization or indivial addition of panels.
| Property | Type | Optional | Default | Description |
| ------------------- | -------------------------------------- | -------- | ------------------------ | --------------------------------------------------------------------------- |
| onReady | `(event: SplitviewReadyEvent) => void` | No | | Function |
| components | `Record<string, ISplitviewPanelProps>` | No | | Panel renderers |
| orientation | `Orientation` | Yes | `Orientation.HORIZONTAL` | Orientation of the Splitview |
| proportionalLayout | `boolean` | Yes | `true` | See <Link to="/docs/basics/#proportional-layout">Proportional layout</Link> |
| hideBorders | `boolean` | Yes | `false` | Hide the borders between panels |
| className | `string` | Yes | `''` | Attaches a classname |
| disableAutoResizing | `boolean` | Yes | `false` | See <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
## Splitview API
The Splitview API is exposed both at the `onReady` event and on each panel through `props.containerApi`.
```tsx title="Splitview API via Panel component"
const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
// props.containerApi...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```
```tsx title="Splitview API via the onReady callback"
const onReady = (event: SplitviewReadyEvent) => {
// event.api...
};
```
| Property | Type | Description |
| ------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumSize | `number` | The sum of the `minimumSize` property for each panel |
| maximumSize | `number` | The sum of the `maximumSize` property for each panel |
| length | `number` | Number of panels |
| panels | `ISplitviewPanel[]` | All panels |
| | | |
| onDidLayoutChange | `Event<void>` | Fires on layout change |
| onDidLayoutFromJSON | `Event<void>` | Fires of layout change caused by a fromJSON deserialization call |
| onDidAddView | `Event<IView>` | Fires when a view is added |
| onDidRemoveView | `Event<IView>` | Fires when a view is removed |
| | | |
| addPanel | `addPanel(options: AddSplitviewComponentOptions): ISplitviewPanel` | |
| removePanel | `(panel: ISplitviewPanel, sizing?: Sizing): void` | |
| getPanel | `(id:string): ISplitviewPanel \| undefined` | |
| movePanel | `(from: number, to: number): void` | |
| | |
| updateOptions | `(options: SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | Focus the active panel, if exists |
| layout | `(width: number, height:number): void` | See <Link to="/docs/basics/#auto-resizing">Auto Resizing</Link> |
| fromJSON | `(data: SerializedSplitview): void` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| toJSON | `(): SerializedSplitview` | <Link to="/docs/basics/#serialization">Serialization</Link> |
| clear | `(): void` | Clears the current layout |
## Splitview Panel API
The Splitview panel API is exposed on each panel and contains actions and variables specific to that panel.
```tsx title="Splitview panel API via Panel component"
const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
// props.api...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```
| Property | Type | Description |
| ---------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| id | `string` | Panel id |
| isFocused | `boolean` | Is panel focsed |
| isActive | `boolean` | Is panel active |
| isVisible | `boolean` | Is panel visible |
| width | `number` | Panel width |
| height | `number` | Panel height |
| | | |
| onDidDimensionsChange | `Event<PanelDimensionChangeEvent>` | Fires when panel dimensions change |
| onDidFocusChange | `Event<FocusEvent>` | Fire when panel is focused and blurred |
| onDidVisibilityChange | `Event<VisibilityEvent>` | Fires when the panels visiblity property is changed (see <Link to="/docs/api/splitview/#visibility">Panel Visibility</Link>) |
| onDidActiveChange | `Event<ActiveEvent>` | Fires when the panels active property is changed (see <Link to="/docs/api/splitview/#active">Active Panel</Link>) |
| onDidConstraintsChange | `onDidConstraintsChange: Event<PanelConstraintChangeEvent>` | Fires when the panels size contrainsts change (see <Link to="/docs/api/splitview/#contraints">Panel Constraints</Link>) |
| | | |
| setVisible | `(isVisible: boolean): void` | |
| setActive | `(): void` | |
| | | |
| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | |
| setSize | `(event: PanelSizeEvent): void` | |
## Advanced Features
Listed below are some functionality avalaible to you through both the panel and component APIs. The live demo shows examples of these in real-time.
<div
style={{
height: '200px',
margin: '20px 0px',
}}
>
<SplitviewExample1 />
</div>
### Visibility
A panels visibility can be controlled and monitoring through the following code.
A panel with visibility set to `false` will remain as a part of the components list of panels but will not be rendered.
```tsx
const disposable = props.api.onDidVisibilityChange(({ isVisible }) => {
//
});
```
```tsx
api.setVisible(true);
```
### Active
Only one panel in the `splitview` can be the active panel at any one time.
Setting a panel as active will set all the others as inactive.
A focused panel is always the active panel but an active panel is not always focused.
```tsx
const disposable = props.api.onDidActiveChange(({ isActive }) => {
//
});
```
```tsx
api.setActive();
```
### Contraints
When adding a panel you can specify pixel size contraints
```tsx
event.api.addPanel({
id: 'panel_3',
component: 'default',
minimumSize: 100,
maximumSize: 1000,
});
```
These contraints can be updated throughout the lifecycle of the `splitview` using the panel API
```tsx
props.api.onDidConstraintsChange(({ maximumSize, minimumSize }) => {
//
});
```
```tsx
api.setConstraints({
maximumSize: 200,
minimumSize: 400,
});
```

View File

@ -1,5 +1,9 @@
import { SimpleSplitview } from '../components/simpleSplitview';
import { SimpleSplitview2 } from '../components/simpleSplitview2';
---
sidebar_position: 1
---
import { SimpleSplitview } from '../src/components/simpleSplitview';
import { SimpleSplitview2 } from '../src/components/simpleSplitview2';
# Basics
@ -8,6 +12,7 @@ This section will take you through a number of concepts that can be applied to a
## Panels
The below examples use `ReactSplitview` but the logic holds for `ReactPaneview`, `ReactGridview` and `ReactDockview` using their respective implementations and interfaces.
All components require you to provide an `onReady` prop which you can use to build and control your component.
### Adding a panel with parameters
@ -38,7 +43,7 @@ const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
There are two types of API you will interact with using `dockview`.
- The `panel API` is accessible via `props.api` in user defined panels and via the `.api` variable found on panel instances. This API contains actions and variable related to the the individual panel.
- The `component API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole.
- The `container API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole.
```tsx
const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
@ -75,10 +80,28 @@ All components support `toJSON(): T` which returns a Typed object representation
## Auto resizing
`SplitviewReact`, `GridviewReact`, `PaneviewReact` and `DockviewReact` will all automatically resize to fill the size of their parent element.
Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) which some users may need to polyfill.
You can disable this by settings the `disableAutoResizing` prop to be `true`.
You can manually resize a component using the API method `layout(width: number, height: number): void`.
An advanced case may use this in conjunction with `disableAutoResizing=true` to allow a parent component to have ultimate control over the dimensions of the component.
## Events
Many API properties can be listened on using the `Event` pattern. For example `api.onDidFocusChange(() => {...})`.
You should dispose of any event listeners you create cleaning up any listeners you would have created.
```tsx
React.useEffect(() => {
const disposable = api.onDidFocusChange(() => {
// write some code
});
return () => {
disposable.dispose();
};
}, []);
```
## Proportional layout
@ -88,3 +111,7 @@ Although not configurable on `DockviewReact` and `PaneviewReact` these both beha
<SimpleSplitview2 proportional={false} />
<SimpleSplitview2 proportional={true} />
## Browser support
dockview is intended to support all major browsers. Some users may require a polyfill for [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).

View File

@ -1,9 +1,15 @@
import { SimpleSplitview } from '../components/simpleSplitview';
import { SimpleGridview } from '../components/simpleGridview';
import { SimplePaneview } from '../components/simplePaneview';
import { SimpleDockview } from '../components/simpleDockview';
---
sidebar_position: 0
---
# Introduction
import { SimpleSplitview } from '../src/components/simpleSplitview';
import { SimpleGridview } from '../src/components/simpleGridview';
import { SimplePaneview } from '../src/components/simplePaneview';
import { SimpleDockview } from '../src/components/simpleDockview';
# Dockview
## Introduction
**dockview** is a zero dependency layout manager that supports tab, grids and splitviews.
@ -13,7 +19,7 @@ import { SimpleDockview } from '../components/simpleDockview';
- Support for the serialization and deserialization of layouts
- Drag and drop support
# Quick start
## Quick start
`dockview` has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. To install `dockview` you can run:
@ -142,26 +148,3 @@ const Component = () => {
);
};
```
```tsx
const components: PanelCollection<IDockviewPanelProps> = {
default: (props: IDockviewPanelProps<{ someProps: string }>) => {
return <div>{props.params.someProps}</div>;
},
fancy: (props: IDockviewPanelProps) => {
return (
<DockviewComponents.Panel>
<DockviewComponents.Tab>
<div>
<span>{props.api.title}</span>
<span onClick={() => props.api.close()}>{'Close'}</span>
</div>
</DockviewComponents.Tab>
<DockviewComponents.Content>
<div>{'Hello world'}</div>
</DockviewComponents.Content>
</DockviewComponents.Panel>
);
},
};
```

80
docs/docs/theme.mdx Normal file
View File

@ -0,0 +1,80 @@
---
sidebar_position: 3
---
import { CustomCSSDockview } from '../src/components/dockview/customCss';
# Theme
## Introduction
`dockview` requires some css to work correctly.
The css is exported as one file under [`dockview/dict/styles/dockview.css`](https://unpkg.com/browse/dockview@latest/dist/styles/dockview.css)
and depending can be imported
```css
@import './node_modules/dockview/dist/styles/dockview.css';
```
## Customizing Theme
`dockview` supports theming through the use of css properties.
You can view the built-in themes at [`dockview/src/theme.scss`](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/theme.scss)
and are free to build your own themes based on these css properties.
| CSS Property | Description |
| ---------------------------------------------------- | ----------- |
| --dv-paneview-active-outline-color | |
| --dv-tabs-and-actions-container-font-size | |
| --dv-tabs-and-actions-container-height | |
| --dv-tab-close-icon | |
| --dv-drag-over-background-color | |
| --dv-drag-over-border-color | |
| --dv-tabs-container-scrollbar-color | |
| | |
| --dv-group-view-background-color | |
| | |
| --dv-tabs-and-actions-container-background-color | |
| | |
| --dv-activegroup-visiblepanel-tab-background-color | |
| --dv-activegroup-hiddenpanel-tab-background-color | |
| --dv-inactivegroup-visiblepanel-tab-background-color | |
| --dv-inactivegroup-hiddenpanel-tab-background-color | |
| --dv-tab-divider-color | |
| | |
| --dv-activegroup-visiblepanel-tab-color | |
| --dv-activegroup-hiddenpanel-tab-color | |
| --dv-inactivegroup-visiblepanel-tab-color | |
| --dv-inactivegroup-hiddenpanel-tab-color | |
| | |
| --dv-separator-border | |
| --dv-paneview-header-border-color | |
You can further customise the theme through adjusting class properties but this is up you.
As an example if you wanted to add a bottom border to the tab container for an active group in the `DockviewReact` component you could write:
```css
.groupview {
&.active-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color);
}
}
&.inactive-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-inactivegroup-visiblepanel-tab-background-color);
}
}
}
```
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<CustomCSSDockview />
</div>

170
docs/docusaurus.config.js Normal file
View File

@ -0,0 +1,170 @@
// @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');
const path = require('path');
console.log(`isCI: ${process.env.CI}`);
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Dockview',
tagline: 'Zero dependency layout manager for React',
url: 'https://your-docusaurus-test-site.com',
baseUrl: process.env.CI ? `/` : '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: 'mathuo', // Usually your GitHub org/user name.
projectName: 'dockview', // Usually your repo name.
// Even if you don't use internalization, you can use this field to set useful
// metadata like html lang. For example, if your site is Chinese, you may want
// to replace "en" with "zh-Hans".
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
plugins: [
'docusaurus-plugin-sass',
(context, options) => {
return {
name: 'webpack',
configureWebpack: (config, isServer, utils) => {
return {
// externals: ['react', 'react-dom'],
devtool: 'source-map',
resolve: {
alias: {
react: path.join(
__dirname,
'node_modules',
'react'
),
'react-dom': path.join(
__dirname,
'node_modules',
'react-dom'
),
},
},
};
},
};
},
],
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
sidebarPath: require.resolve('./sidebars.js'),
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
},
blog: {
showReadingTime: true,
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
}),
],
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
navbar: {
title: 'Dockview',
logo: {
alt: 'My Site Logo',
src: 'img/logo.svg',
},
items: [
{
type: 'doc',
docId: 'index',
position: 'left',
label: 'Docs',
},
{ to: '/blog', label: 'Blog', position: 'left' },
{
href: 'https://github.com/mathuo/dockview',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Learn',
items: [
{
label: 'Docs',
to: '/docs',
},
],
},
{
title: 'Community',
items: [
{
label: 'Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/dockview',
},
],
},
{
title: 'More',
items: [
{
label: 'Blog',
to: '/blog',
},
{
label: 'GitHub',
href: 'https://github.com/mathuo/dockview',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Dockview, Inc. Built with Docusaurus.`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['java', 'markdown', 'latex'],
magicComments: [
{
className: 'theme-code-block-highlighted-line',
line: 'highlight-next-line',
block: {
start: 'highlight-start',
end: 'highlight-end',
},
},
{
className: 'code-block-error-line',
line: 'This will error',
},
],
},
}),
};
module.exports = config;

54
docs/package.json Normal file
View File

@ -0,0 +1,54 @@
{
"name": "dockview-docs",
"version": "1.4.2",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"deploy-docs": "node scripts/package-docs.js"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.20",
"@docusaurus/preset-classic": "2.0.0-beta.20",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"dockview": "^1.4.2",
"prism-react-renderer": "^1.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"recoil": "^0.7.3-alpha.2"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.20",
"@tsconfig/docusaurus": "^1.0.5",
"docusaurus-plugin-sass": "^0.2.2",
"fs-extra": "^10.1.0",
"install": "^0.13.0",
"sass": "^1.52.1",
"typescript": "^4.6.4"
},
"resolutions": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@ -0,0 +1,8 @@
const fs = require('fs-extra');
const path = require('path');
const output = path.join(__dirname, '../../build');
const docsDir = path.join(__dirname, '../build');
fs.copySync(docsDir, path.join(output));

31
docs/sidebars.js Normal file
View File

@ -0,0 +1,31 @@
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
// @ts-check
/** @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: [
{
type: 'category',
label: 'Tutorial',
items: ['hello'],
},
],
*/
};
module.exports = sidebars;

View File

@ -0,0 +1,70 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
type FeatureItem = {
title: string;
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
description: JSX.Element;
};
const FeatureList: FeatureItem[] = [
{
title: 'Easy to Use',
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
description: (
<>
Docusaurus was designed from the ground up to be easily installed and
used to get your website up and running quickly.
</>
),
},
{
title: 'Focus on What Matters',
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
description: (
<>
Docusaurus lets you focus on your docs, and we&apos;ll do the chores. Go
ahead and move your docs into the <code>docs</code> directory.
</>
),
},
{
title: 'Powered by React',
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
description: (
<>
Extend or customize your website layout by reusing React. Docusaurus can
be extended while reusing the same header and footer.
</>
),
},
];
function Feature({title, Svg, description}: FeatureItem) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>
);
}
export default function HomepageFeatures(): JSX.Element {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,11 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}

View File

@ -0,0 +1,27 @@
.console-container {
background-color: black;
color: white;
padding-left: 8px;
max-height: 200px;
overflow-y: scroll;
overflow-x: auto;
.console-line {
height: 20px;
line-height: 20px;
font-size: 13px;
border-bottom: 1px solid rgb(30, 30, 30);
display: flex;
padding-left: 4px;
.console-line-timestamp {
color: lightgray;
padding-right: 4px;
}
.console-line-text {
padding: 0px 4px;
flex-grow: 1;
}
}
}

View File

@ -0,0 +1,52 @@
import * as React from 'react';
import './console.scss';
const formatTime = (now: Date) => {
const pad = (x: number) => (x < 10 ? `0${x}` : `${x}`);
return `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(
now.getSeconds()
)}.${now.getMilliseconds()}`;
};
export interface Line {
timestamp: Date;
text: string;
css?: React.CSSProperties;
}
export interface IConsoleProps {
lines: Line[];
}
export const Console = (props: IConsoleProps) => {
const ref = React.useRef<HTMLDivElement>();
React.useLayoutEffect(() => {
if (!ref.current) {
return;
}
ref.current.scrollTop = Math.max(
0,
ref.current.scrollHeight - ref.current.clientHeight
);
}, [props.lines]);
return (
<div ref={ref} className="console-container">
{props.lines.map((line, i) => {
return (
<div key={i} className="console-line">
<span className="console-line-timestamp">
{formatTime(line.timestamp)}
</span>
<span className="console-line-text" style={line.css}>
{line.text}
</span>
</div>
);
})}
</div>
);
};

View File

@ -0,0 +1,115 @@
import {
DockviewReact,
DockviewReadyEvent,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
//
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
const DefaultTab = (props: IDockviewPanelHeaderProps) => {
const [active, setActive] = React.useState<boolean>(props.api.isActive);
const [groupActive, setGroupActive] = React.useState<boolean>(
props.api.isGroupActive
);
React.useEffect(() => {
const disposable1 = props.api.onDidActiveChange((e) => {
setActive(e.isActive);
});
const disposable2 = props.containerApi.onDidActiveGroupChange((e) => {
setGroupActive(props.api.isGroupActive);
});
return () => {
disposable1.dispose();
disposable2.dispose();
};
}, [props.api]);
return (
<div
style={{
display: 'flex',
padding: '0px 8px',
alignItems: 'center',
height: '100%',
}}
>
<span style={{ padding: '0px 8px', flexGrow: 1 }}>
{props.api.title}
</span>
<span
className=""
onClick={() => props.api.setActive()}
style={{
display: 'flex',
alignItems: 'center',
paddingRight: '8px',
}}
>
{'✕'}
</span>
</div>
);
};
const tabComponents = {
default: DefaultTab,
};
export const ContextMenuDockview = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
tabComponent: 'default',
params: {
title: 'Panel 1',
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
tabComponent: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
tabComponent: 'default',
params: {
title: 'Panel 3',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
tabComponent: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
};
return (
<DockviewReact
components={components}
tabComponents={tabComponents}
onReady={onReady}
className="dockview-theme-dark"
/>
);
};

View File

@ -1,24 +1,25 @@
import {
IPaneviewPanelProps,
PaneviewReact,
PaneviewReadyEvent,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
//
const components = {
default: (props: IPaneviewPanelProps<{ title: string }>) => {
default: (props: IDockviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
export const SimplePaneview = () => {
const onReady = (event: PaneviewReadyEvent) => {
export const CustomCSSDockview = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
title: 'Panel 1',
});
event.api.addPanel({
@ -27,7 +28,6 @@ export const SimplePaneview = () => {
params: {
title: 'Panel 2',
},
title: 'Panel 2',
});
event.api.addPanel({
@ -36,15 +36,23 @@ export const SimplePaneview = () => {
params: {
title: 'Panel 3',
},
title: 'Panel 3',
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
};
return (
<PaneviewReact
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-dark"
className="dockview-theme-vs"
/>
);
};

View File

@ -0,0 +1,105 @@
import {
DockviewDndOverlayEvent,
DockviewDropEvent,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
return (
<div style={{ padding: '20px' }}>
<div>{props.params.title}</div>
</div>
);
},
};
export const DndDockview = (props: { renderVisibleOnly: boolean }) => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
};
const onDidDrop = (event: DockviewDropEvent) => {
const { group } = event;
event.api.addPanel({
id: 'test',
component: 'default',
position: {
referencePanel: group.activePanel.id,
direction: 'within',
},
});
};
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
return true;
};
return (
<>
<div
style={{
backgroundColor: 'orange',
padding: '0px 8px',
borderRadius: '4px',
width: '100px',
cursor: 'pointer',
}}
draggable={true}
>
Drag me
</div>
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-dark"
onDidDrop={onDidDrop}
showDndOverlay={showDndOverlay}
/>
</div>
</>
);
};

View File

@ -0,0 +1,342 @@
import {
Orientation,
DockviewReact,
DockviewReadyEvent,
DockviewApi,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
import { Console, Line } from '../console/console';
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
export const EventsDockview = () => {
const [lines, setLines] = React.useState<Line[]>([]);
const [checked, setChecked] = React.useState<boolean>(false);
const [api, setApi] = React.useState<DockviewApi | undefined>();
React.useEffect(() => {
if (!api) {
return () => {
//noop
};
}
const disposables = [
api.onDidAddPanel((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidAddPanel: ${panel.id}`,
},
]);
}),
api.onDidRemovePanel((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidRemovePanel: ${panel.id}`,
},
]);
}),
api.onDidActivePanelChange((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidActivePanelChange: ${panel?.id}`,
},
]);
}),
api.onDidAddGroup((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidAddGroup: ${panel.id}`,
},
]);
}),
api.onDidRemoveGroup((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidRemoveGroup: ${panel.id}`,
},
]);
}),
api.onDidActiveGroupChange((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidActiveGroupChange: ${panel?.id}`,
},
]);
}),
api.onDidLayoutChange((panel) => {
setLines((lines) => [
...lines,
{ timestamp: new Date(), text: `onDidLayoutChange` },
]);
}),
api.onDidLayoutFromJSON((panel) => {
setLines((lines) => [
...lines,
{ timestamp: new Date(), text: `onDidLayoutFromJSON` },
]);
}),
];
return () => {
disposables.forEach((disposable) => disposable.dispose());
};
}, [api]);
React.useEffect(() => {
if (!api) {
return;
}
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `Rebuilding view fromJSON:${checked}`,
css: { color: 'yellow', backgroundColor: 'grey' },
},
]);
if (checked) {
api.fromJSON({
grid: {
root: {
type: 'branch',
data: [
{
type: 'leaf',
data: {
views: ['panel_1', 'panel_2', 'panel_3'],
activeView: 'panel_3',
id: '77',
},
size: 262,
},
{
type: 'branch',
data: [
{
type: 'leaf',
data: {
views: ['panel_5'],
activeView: 'panel_5',
id: '79',
},
size: 100,
},
{
type: 'leaf',
data: {
views: ['panel_6', 'panel_8'],
activeView: 'panel_8',
id: '80',
},
size: 100,
},
{
type: 'leaf',
data: {
views: ['panel_7'],
activeView: 'panel_7',
id: '81',
},
size: 100,
},
],
size: 262,
},
{
type: 'leaf',
data: {
views: ['panel_4'],
activeView: 'panel_4',
id: '78',
},
size: 263.75,
},
],
size: 300,
},
width: 787.75,
height: 300,
orientation: Orientation.HORIZONTAL,
},
panels: {
panel_1: {
id: 'panel_1',
view: { content: { id: 'default' } },
params: { title: 'Panel 1' },
title: 'panel_1',
},
panel_2: {
id: 'panel_2',
view: { content: { id: 'default' } },
params: { title: 'Panel 2' },
title: 'panel_2',
},
panel_3: {
id: 'panel_3',
view: { content: { id: 'default' } },
params: { title: 'Panel 3' },
title: 'panel_3',
},
panel_4: {
id: 'panel_4',
view: { content: { id: 'default' } },
params: { title: 'Panel 4' },
title: 'panel_4',
},
panel_5: {
id: 'panel_5',
view: { content: { id: 'default' } },
params: { title: 'Panel 5' },
title: 'panel_5',
},
panel_6: {
id: 'panel_6',
view: { content: { id: 'default' } },
params: { title: 'Panel 6' },
title: 'panel_6',
},
panel_8: {
id: 'panel_8',
view: { content: { id: 'default' } },
params: { title: 'Panel 8' },
title: 'panel_8',
},
panel_7: {
id: 'panel_7',
view: { content: { id: 'default' } },
params: { title: 'Panel 7' },
title: 'panel_7',
},
},
activeGroup: '80',
options: {},
});
return;
}
api.clear();
api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
api.addPanel({
id: 'panel_5',
component: 'default',
params: {
title: 'Panel 5',
},
position: { referencePanel: 'panel_3', direction: 'right' },
});
api.addPanel({
id: 'panel_6',
component: 'default',
params: {
title: 'Panel 6',
},
position: { referencePanel: 'panel_5', direction: 'below' },
});
api.addPanel({
id: 'panel_7',
component: 'default',
params: {
title: 'Panel 7',
},
position: { referencePanel: 'panel_6', direction: 'below' },
});
api.addPanel({
id: 'panel_8',
component: 'default',
params: {
title: 'Panel 8',
},
position: { referencePanel: 'panel_6', direction: 'within' },
});
}, [api, checked]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
};
return (
<>
<label>
<input
type="checkbox"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>
<span>{'fromJSON'}</span>
</label>
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-dark"
/>
</div>
<Console lines={lines} />
</>
);
};

View File

@ -0,0 +1,158 @@
import {
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
PanelApi,
} from 'dockview';
import * as React from 'react';
import { atom, useRecoilState, useRecoilValue } from 'recoil';
const renderVisibleComponentsOnlyAtom = atom<boolean>({
key: 'renderVisibleComponentsOnlyAtom',
default: false,
});
function RenderWhenVisible<
T extends { api: Pick<PanelApi, 'isVisible' | 'onDidVisibilityChange'> }
>(component: React.FunctionComponent<T>) {
const HigherOrderComponent = (props: T) => {
const [visible, setVisible] = React.useState<boolean>(
props.api.isVisible
);
const render = useRecoilValue(renderVisibleComponentsOnlyAtom);
React.useEffect(() => {
const disposable = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);
return () => {
disposable.dispose();
};
}, [props.api]);
if (!visible && render) {
return null;
}
return React.createElement(component, props);
};
return HigherOrderComponent;
}
const formatLine = (line: string) => {
const now = new Date();
const pad = (x: number) => (x < 10 ? `0${x}` : `${x}`);
const time = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(
now.getSeconds()
)}`;
return `[${time}] ${line}`;
};
const components = {
default: RenderWhenVisible(
(props: IDockviewPanelProps<{ title: string }>) => {
const [lines, setLines] = React.useState<string[]>([
formatLine('Component created'),
]);
React.useEffect(() => {
setLines((lines) => [
...lines,
formatLine('Running task for 5 seconds'),
]);
const timeout = setTimeout(() => {
setLines((lines) => [
...lines,
formatLine('Task completed'),
]);
}, 5000);
return () => {
clearTimeout(timeout);
};
}, []);
return (
<div style={{ padding: '20px' }}>
<div>{props.params.title}</div>
{lines.map((line, i) => (
<div key={i}>{line}</div>
))}
</div>
);
}
),
};
export const RenderingDockview = (props: { renderVisibleOnly: boolean }) => {
const [render, setRender] = useRecoilState(renderVisibleComponentsOnlyAtom);
React.useEffect(
() => setRender(props.renderVisibleOnly),
[props.renderVisibleOnly]
);
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
};
return (
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-dark"
/>
);
};
export const Checkbox = () => {
const [render, setRender] = useRecoilState(renderVisibleComponentsOnlyAtom);
return (
<label>
Render only when visible
<input
type="checkbox"
checked={render}
onChange={(e) => setRender(e.target.checked)}
/>
</label>
);
};

View File

@ -0,0 +1,339 @@
import {
IGridviewPanelProps,
Orientation,
GridviewReact,
GridviewReadyEvent,
GridviewApi,
} from 'dockview';
import * as React from 'react';
import { Console, Line } from '../console/console';
const components = {
default: (props: IGridviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
export const EventsGridview = () => {
const [lines, setLines] = React.useState<Line[]>([]);
const [checked, setChecked] = React.useState<boolean>(false);
const [api, setApi] = React.useState<GridviewApi | undefined>();
React.useEffect(() => {
if (!api) {
return () => {
//noop
};
}
const disposables = [
api.onDidAddPanel((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidAddPanel: ${panel.id}`,
},
]);
}),
api.onDidRemovePanel((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidRemovePanel: ${panel.id}`,
},
]);
}),
api.onDidActivePanelChange((panel) => {
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `onDidActivePanelChange: ${panel?.id}`,
},
]);
}),
api.onDidLayoutChange((panel) => {
setLines((lines) => [
...lines,
{ timestamp: new Date(), text: `onDidLayoutChange` },
]);
}),
api.onDidLayoutFromJSON((panel) => {
setLines((lines) => [
...lines,
{ timestamp: new Date(), text: `onDidLayoutFromJSON` },
]);
}),
];
return () => {
disposables.forEach((disposable) => disposable.dispose());
};
}, [api]);
React.useEffect(() => {
if (!api) {
return;
}
setLines((lines) => [
...lines,
{
timestamp: new Date(),
text: `Rebuilding view fromJSON:${checked}`,
css: { color: 'yellow', backgroundColor: 'grey' },
},
]);
if (checked) {
api.fromJSON({
grid: {
root: {
type: 'branch',
data: [
{
type: 'branch',
data: [
{
type: 'leaf',
data: {
id: 'panel_3',
component: 'default',
params: { title: 'Panel 3' },
snap: false,
},
size: 394,
},
{
type: 'branch',
data: [
{
type: 'leaf',
data: {
id: 'panel_5',
component: 'default',
params: {
title: 'Panel 5',
},
snap: false,
},
size: 50,
},
{
type: 'branch',
data: [
{
type: 'leaf',
data: {
id: 'panel_6',
component:
'default',
params: {
title: 'Panel 6',
},
minimumWidth: 10,
snap: false,
},
size: 131,
},
{
type: 'leaf',
data: {
id: 'panel_8',
component:
'default',
params: {
title: 'Panel 8',
},
minimumWidth: 10,
snap: false,
},
size: 131,
},
{
type: 'leaf',
data: {
id: 'panel_7',
component:
'default',
params: {
title: 'Panel 7',
},
minimumWidth: 10,
snap: false,
},
size: 132,
},
],
size: 50,
},
],
size: 394,
},
],
size: 100,
},
{
type: 'leaf',
data: {
id: 'panel_2',
component: 'default',
params: { title: 'Panel 2' },
snap: false,
},
size: 100,
},
{
type: 'branch',
data: [
{
type: 'leaf',
data: {
id: 'panel_1',
component: 'default',
params: { title: 'Panel 1' },
snap: false,
},
size: 394,
},
{
type: 'leaf',
data: {
id: 'panel_4',
component: 'default',
params: { title: 'Panel 4' },
snap: false,
},
size: 394,
},
],
size: 100,
},
],
size: 788,
},
width: 788,
height: 300,
orientation: Orientation.VERTICAL,
},
activePanel: 'panel_8',
});
return;
}
api.clear();
api.orientation = Orientation.VERTICAL;
api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
console.log('sdf');
api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
api.addPanel({
id: 'panel_5',
component: 'default',
params: {
title: 'Panel 5',
},
position: { referencePanel: 'panel_3', direction: 'right' },
});
api.addPanel({
id: 'panel_6',
component: 'default',
params: {
title: 'Panel 6',
},
position: { referencePanel: 'panel_5', direction: 'below' },
minimumWidth: 10,
});
api.addPanel({
id: 'panel_7',
component: 'default',
params: {
title: 'Panel 7',
},
position: { referencePanel: 'panel_6', direction: 'right' },
minimumWidth: 10,
});
api.addPanel({
id: 'panel_8',
component: 'default',
params: {
title: 'Panel 8',
},
position: { referencePanel: 'panel_6', direction: 'right' },
minimumWidth: 10,
});
}, [api, checked]);
const onReady = (event: GridviewReadyEvent) => {
setApi(event.api);
};
return (
<>
<label>
<input
type="checkbox"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>
<span>{'fromJSON'}</span>
</label>
<div
style={{
height: '300px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<GridviewReact
components={components}
onReady={onReady}
proportionalLayout={false}
orientation={Orientation.VERTICAL}
className="dockview-theme-dark"
/>
</div>
<Console lines={lines} />
</>
);
};

View File

@ -0,0 +1,2 @@
import * as React from 'react';
const URL = 'https://api.github.com/repos/mathuo/dockview/releases';

View File

@ -2,7 +2,9 @@ import {
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
PanelApi,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
@ -10,6 +12,31 @@ const components = {
},
};
const RenderWhenVisible = <T,>(
props: T & {
children: React.FunctionComponent<T>;
api: Pick<PanelApi, 'isVisible' | 'onDidVisibilityChange'>;
}
) => {
const [visible, setVisible] = React.useState<boolean>(props.api.isVisible);
React.useEffect(() => {
const disposable = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);
return () => {
disposable.dispose();
};
}, [props.api]);
if (!visible) {
return null;
}
return React.createElement(props.children, props);
};
export const SimpleDockview = () => {
const onReady = (event: DockviewReadyEvent) => {
const panel = event.api.addPanel({

View File

@ -4,6 +4,7 @@ import {
GridviewReact,
GridviewReadyEvent,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: IGridviewPanelProps<{ title: string }>) => {

View File

@ -0,0 +1,102 @@
import {
IPaneviewPanelProps,
PaneviewReact,
PaneviewReadyEvent,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: IPaneviewPanelProps<{ title: string }>) => {
return (
<div
style={{
padding: '10px',
height: '100%',
backgroundColor: 'rgb(60,60,60)',
}}
>
{props.params.title}
</div>
);
},
};
const MyHeaderComponent = (props: IPaneviewPanelProps<{ title: string }>) => {
const [expanded, setExpanded] = React.useState<boolean>(
props.api.isExpanded
);
React.useEffect(() => {
const disposable = props.api.onDidExpansionChange((event) => {
setExpanded(event.isExpanded);
});
return () => {
disposable.dispose();
};
}, []);
const onClick = () => {
props.api.setExpanded(!expanded);
};
return (
<div
style={{
padding: '10px',
height: '100%',
backgroundColor: 'rgb(60,60,60)',
}}
>
<a
onClick={onClick}
className={expanded ? 'expanded' : 'collapsed'}
/>
<span>{props.params.title}</span>
</div>
);
};
const headerComponents = {
myHeaderComponent: MyHeaderComponent,
};
export const SimplePaneview = () => {
const onReady = (event: PaneviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
title: 'Panel 2',
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
title: 'Panel 3',
});
};
return (
<PaneviewReact
components={components}
headerComponents={headerComponents}
onReady={onReady}
className="dockview-theme-dark"
/>
);
};

View File

@ -4,6 +4,7 @@ import {
SplitviewReact,
SplitviewReadyEvent,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: ISplitviewPanelProps<{ title: string }>) => {

View File

@ -0,0 +1,161 @@
import {
ISplitviewPanel,
ISplitviewPanelProps,
Orientation,
SplitviewReact,
SplitviewReadyEvent,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: ISplitviewPanelProps<{ title: string }>) => {
const [active, setActive] = React.useState<boolean>(props.api.isActive);
const [visible, setVisible] = React.useState<boolean>(
props.api.isVisible
);
const [focused, setFocused] = React.useState<boolean>(
props.api.isFocused
);
const [dimensions, setDimensions] = React.useState<{
height: number;
width: number;
}>({
height: props.api.height,
width: props.api.width,
});
React.useEffect(() => {
const disposable1 = props.api.onDidActiveChange((event) =>
setActive(event.isActive)
);
const disposable2 = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);
const disposable3 = props.api.onDidFocusChange((event) =>
setFocused(event.isFocused)
);
const disposable4 = props.api.onDidDimensionsChange((event) => {
setDimensions({ height: event.height, width: event.width });
});
return () => {
disposable1.dispose();
disposable2.dispose();
disposable3.dispose();
disposable4.dispose();
};
}, []);
return (
<div
style={{
padding: '20px',
display: 'grid',
gridTemplateColumns: '100px 100px',
lineHeight: '20px',
gridTemplateRows: 'repeat(6, 20px)',
}}
>
<span>{'Panel ID: '}</span>
<span>{props.api.id}</span>
<span>{'Height: '}</span>
<span>{`${dimensions.height}px`}</span>
<span>{'Width: '}</span>
<span>{`${dimensions.width}px`}</span>
<span>{'Focused: '}</span>
<span style={{ color: focused ? 'green' : 'red' }}>{`${
focused ? 'True' : 'False'
}`}</span>
<span>{'Active: '}</span>
<span style={{ color: active ? 'green' : 'red' }}>{`${
active ? 'True' : 'False'
}`}</span>
<span>{'Visible: '}</span>
<span style={{ color: visible ? 'green' : 'red' }}>{`${
visible ? 'True' : 'False'
}`}</span>
</div>
);
},
};
export const SplitviewExample1 = (props: { proportional?: boolean }) => {
const [panels, setPanels] = React.useState<ISplitviewPanel[]>([]);
const onReady = React.useCallback((event: SplitviewReadyEvent) => {
event.api.onDidAddView((panel) => setPanels(event.api.panels));
event.api.onDidRemoveView((panel) => setPanels(event.api.panels));
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
minimumSize: 100,
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
minimumSize: 100,
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
minimumSize: 100,
});
}, []);
return (
<>
<div
style={{
height: '150px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
}}
>
<SplitviewReact
components={components}
proportionalLayout={props.proportional}
onReady={onReady}
orientation={Orientation.HORIZONTAL}
className="dockview-theme-dark"
/>
</div>
<div style={{ height: '20px', display: 'flex' }}>
{panels.map((panel) => {
return (
<div style={{ padding: '0px 20px' }}>
<div>{panel.id}</div>
<div>
<button
onClick={() =>
panel.api.setVisible(
!panel.api.isVisible
)
}
>
Toggle Visiblity
</button>
<button onClick={() => panel.api.setActive()}>
Set Active
</button>
</div>
</div>
);
})}
</div>
</>
);
};

View File

@ -0,0 +1,63 @@
.splitview-math-blog {
.sash {
background-color: orange;
position: absolute;
top: 0px;
width: 4px;
height: 100%;
z-index: 2;
cursor: ew-resize;
user-select: none;
}
.debug-sash-max {
height: 10px;
width: 1px;
position: absolute;
z-index: 999;
top: -10px;
}
.debug-sash-min {
height: 10px;
width: 1px;
position: absolute;
z-index: 999;
top: 100%;
}
.debug-sash-text {
height: 20px;
line-height: 20px;
width: 80px;
display: flex;
justify-content: center;
position: absolute;
z-index: 999;
font-size: 14px;
}
.sash-container {
position: absolute;
height: 100%;
}
.view-container {
position: relative;
height: 100%;
}
.view {
position: absolute;
height: 100%;
background-color: dodgerblue;
z-index: 1;
top: 0px;
padding: 10px;
color: white;
}
.sash.drag-sash {
background-color: red;
}
}

View File

@ -0,0 +1,342 @@
import * as React from 'react';
import './math.scss';
const min = 100;
const max = 300;
interface IDebugResize {
leftmin: number;
leftmax: number;
rightmin: number;
rightmax: number;
min: number;
max: number;
}
const resize = (
index: number,
delta: number,
sizes: number[],
mode: number
) => {
const nextSizes = [...sizes];
const left = nextSizes.filter((_, i) => i <= index);
const right = nextSizes.filter((_, i) => i > index);
let result: IDebugResize = {
leftmin: undefined,
leftmax: undefined,
rightmin: undefined,
rightmax: undefined,
max: undefined,
min: undefined,
};
// step 3
if (mode > 2) {
const leftMinimumsDelta = left
.map((x) => min - x)
.reduce((x, y) => x + y, 0);
const leftMaximumsDelta = left
.map((x) => max - x)
.reduce((x, y) => x + y, 0);
const rightMinimumsDelta = right
.map((x) => x - min)
.reduce((x, y) => x + y, 0);
const rightMaximumsDelta = right
.map((x) => x - max)
.reduce((x, y) => x + y, 0);
const _min = Math.max(leftMinimumsDelta, rightMaximumsDelta);
const _max = Math.min(leftMaximumsDelta, rightMinimumsDelta);
const clamp = Math.max(_min, Math.min(_max, delta));
result = {
leftmin: leftMinimumsDelta,
leftmax: leftMaximumsDelta,
rightmin: rightMinimumsDelta,
rightmax: rightMaximumsDelta,
max: _max,
min: _min,
};
delta = clamp;
}
let usedDelta = 0;
let remainingDelta = delta;
// Step 1
for (let i = left.length - 1; i > -1; i--) {
const x = Math.max(min, Math.min(max, left[i] + remainingDelta));
const viewDelta = x - left[i];
usedDelta += viewDelta;
remainingDelta -= viewDelta;
left[i] = x;
}
// Step 2
if (mode > 1) {
for (let i = 0; i < right.length; i++) {
const x = Math.max(min, Math.min(max, right[i] - usedDelta));
const viewDelta = x - right[i];
usedDelta += viewDelta;
right[i] = x;
}
}
return { ...result, sizes: [...left, ...right] };
};
interface ILayoutState {
sashes: number[];
views: number[];
deltas: number[];
left: number;
right: number;
debug: IDebugResize;
drag: number;
}
export const Splitview = (props: { mode: number; debug: boolean }) => {
// keep the sashes and views in one state to prevent weird out-of-sync-ness
const [layout, setLayout] = React.useState<ILayoutState>({
sashes: [200, 400],
views: [200, 200, 200],
deltas: [0, 0, 0],
left: 0,
right: 0,
debug: undefined,
drag: -1,
});
const ref = React.useRef<HTMLDivElement>();
const onMouseDown = (index: number) => (ev: React.MouseEvent) => {
const start = ev.clientX;
const sizes = [...layout.views];
const mousemove = (ev: MouseEvent) => {
const current = ev.clientX;
const delta = current - start;
const {
sizes: nextLayout,
rightmin,
rightmax,
leftmin,
leftmax,
max,
min,
} = resize(index, delta, sizes, props.mode);
const sashes = nextLayout.reduce(
(x, y) => [...x, y + (x.length === 0 ? 0 : x[x.length - 1])],
[]
);
sashes.splice(sashes.length - 1, 1);
const deltas = sizes.map((x, i) => nextLayout[i] - x);
const offset = start - ref.current?.getBoundingClientRect().left;
setLayout({
views: nextLayout,
sashes,
deltas,
left: deltas
.filter((_, i) => i <= index)
.reduce((x, y) => x + y, 0),
right: deltas
.filter((_, i) => i > index)
.reduce((x, y) => x + y, 0),
debug: {
leftmax: leftmax + offset,
leftmin: leftmin + offset,
rightmax: rightmax + offset,
rightmin: rightmin + offset,
min: min + offset,
max: max + offset,
},
drag: index,
});
};
const end = (ev: MouseEvent) => {
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', end);
setLayout((_) => ({
..._,
deltas: _.deltas.map((_) => 0),
left: 0,
right: 0,
drag: -1,
}));
};
document.addEventListener('mousemove', mousemove);
document.addEventListener('mouseup', end);
};
const extras = React.useMemo(() => {
if (!props.debug || !layout.debug || props.mode < 3) {
return null;
}
return (
<>
<div
style={{
left: `${layout.debug.leftmax - 40}px`,
top: '-30px',
}}
className="debug-sash-text"
>
left-max
</div>
<div
style={{
left: `${layout.debug.leftmin - 40}px`,
top: '-30px',
}}
className="debug-sash-text"
>
left-min
</div>
<div
style={{
left: `${layout.debug.rightmax - 40}px`,
bottom: '-30px',
}}
className="debug-sash-text"
>
right-max
</div>
<div
style={{
left: `${layout.debug.rightmin - 40}px`,
bottom: '-30px',
}}
className="debug-sash-text"
>
right-min
</div>
<div
className="debug-sash-max"
style={{
left: `${layout.debug.leftmax - 1}px`,
border: '2px solid purple',
}}
/>
<div
className="debug-sash-max"
style={{
left: `${layout.debug.leftmin - 1}px`,
border: '2px solid green',
}}
/>
<div
className="debug-sash-min"
style={{
left: `${layout.debug.rightmax - 1}px`,
border: '2px solid cyan',
}}
/>
<div
className="debug-sash-min"
style={{
left: `${layout.debug.rightmin - 1}px`,
border: '2px solid pink',
}}
/>
</>
);
}, [layout.debug]);
return (
<div
className="splitview-math-blog"
style={{
marginBottom: '40px',
marginTop: '30px',
backgroundColor: 'gray',
}}
>
{props.debug && (
<div style={{ marginBottom: extras ? '25px' : '0px' }}>
<span>{`Change to left ${layout?.left}`}</span>
<span
style={{
marginLeft: '10px',
backgroundColor:
-layout?.right !== layout?.left ? 'red' : '',
}}
>{`Change to right ${layout?.right}`}</span>
<span
style={{ marginLeft: '10px' }}
>{`Total size ${layout?.views.reduce(
(x, y) => x + y,
0
)}`}</span>
</div>
)}
<div
ref={ref}
style={{
height: '100px',
width: '100%',
position: 'relative',
backgroundColor: 'dimgray',
}}
>
<div className="sash-container">
{layout.sashes.map((x, i) => {
const className =
layout.drag === i ? 'sash drag-sash' : 'sash';
return (
<div
key={i}
onMouseDown={onMouseDown(i)}
style={{
left: `${x - 2}px`,
}}
className={className}
></div>
);
})}
{extras}
</div>
<div className="view-container">
{layout.views.map((x, i) => {
const isMax = x >= max;
const isMin = x <= min;
return (
<div
key={i}
style={{
left: `${
i === 0 ? 0 : layout.sashes[i - 1]
}px`,
width: `${x}px`,
}}
className="view"
>
{props.debug && (
<>
<div>
{`${layout.views[i]} (${
layout.deltas[i] > -1 ? '+' : ''
}${layout.deltas[i]})`}
</div>
<div
style={{ fontSize: '12px' }}
>{`isMin = ${isMin}`}</div>
<div
style={{ fontSize: '12px' }}
>{`isMax = ${isMax}`}</div>
</>
)}
</div>
);
})}
</div>
</div>
</div>
);
};

32
docs/src/css/custom.css Normal file
View File

@ -0,0 +1,32 @@
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
@import "~dockview/dist/styles/dockview.css"

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

378
docs/src/misc/math/math.mdx Normal file
View File

@ -0,0 +1,378 @@
import { Splitview } from '../../src/components/splitview/math';
Wondering how to create a resizable split view, pane or window; whichever you may call it?
Here's a langauge indepedant walkthrough of how to design a split view control from scratch and the simple math behind it.
You will find links to all examples which are written using TypeScript.
Here's the end result and you can continue to read on to see how we got here.
<Splitview mode={3} debug={false} />
## First of all, what is a split view control?
Not all split view controls are born equal, but for our purposes
> A split view control is comprised from a number of 'views' layed out either horizontally or vertically.
> Each view can be indepedantly resized by dragging on the edge of a particular view.
In order to explain how this works we are going to need some more in-depth definitions so lets start with the two
fundamental components of this control, the <b>View</b> and the <b>Sash</b> (I will explain).
The below assumes the split view control has <b>n</b> views, where n is positive number.
For example if n=4 then our split view controls has 4 views. This generic approach will make it much easier to explain going forwards.
<div>
<div>View</div>
<ul style={{ marginLeft: '40px' }}>
<li>
The size of the n<sup>th</sup> view will be known as V<sub>n</sub>
</li>
<li>
The minimum size of the n<sup>th</sup> view will be known as V
<sup>min</sup>
<sub>n</sub>
</li>
<li>
The maximum size of the n<sup>th</sup> view will be known as V
<sup>max</sup>
<sub>n</sub>
</li>
</ul>
</div>
Additional by definition we can known V<sup>min</sup><sub>n</sub> <= V<sub>n</sub> <= V<sup>max</sup><sub>n</sub>
To be able to resize a view we need to be able to drag on the edge of a view to increase or decrease it's size.
This can be achieved by introducing a narrow component that sits between each view acting as a 'drag handle'.
We will call this component a <b>Sash</b> because sash is also the word for those [windows](https://en.wikipedia.org/wiki/Sash_window) with movable panels.
<div>
<div>Sash</div>
<ul style={{marginLeft: "40px"}}>
<li>If we have n views then we will have n-1 sashes. There is no sash before V<sub>0</sub> nor after V<sub>n</sub></li>
<li>The sash between V<sub>n</sub> and V<sub>n+1</sub> is known as S<sub>n</sub></li>
<li>The sash is of fixed width, and it's sole purpose is to act a drag-handle for resizing views</li>
</ul>
</div>
This now gives us a definition of the split view control but in additional to that, to calculate the new view sizes
after a sash is dragged we need to know which sash is being dragged.
We will denote the sash S<sub>i</sub> as the sash to drag going forwards we can reference the following diagram.
![alt txt](./visual_1.jpg)
If we are to drag the sash S<sub>i</sub> then we need to also know how far along the x-axis, or the horizontal-axis we have travelled.
We can denote this as the delta, using the symbol Δ.
Delta is only limited by how wide the screen is so for this purpose we can say it ranges from negative to positive infinity, that is -∞ < Δ < ∞ .
In reality as you will see we will apply a set of constraints on the value of Δ reducing it's overall set of valid values.
## Iteration #1 - The naive approach (aka. the accordian)
As I add delta I increase view sizes and as I remove delta I decrease view sizes.
A rather naive approach but it could look something like this:
- as the sash moves left shrink each view to the left and as the sash moves right expand each view to left, from right-most to left-most in both caes.
- If there is enough delta to shrink a view to it's mimimum size then move onto the next view, and if we have enough delta to expand a view to it's maximum size then again move onto the next view.
- Shrink no more once everything to the left is at minimums and expand no more once everything to the left is at maximums
- We don't manipulate any views to the right of the active sash S<sub>i</sub>
You should be able to show each of the four points above hold true for the below interactive example.
You'll see that changes to the right will always remain at zero because we are not manipulating views to the right of the active sash.
<Splitview mode={1} debug={true} />
There are some obvious flaws with this approach but before we go into that lets put this implemenation in psuedocode using our definitions
from above where we drag sash S<sub>i</sub> by an amount Δ
<div
style={{
display: 'inline-block',
marginLeft: '20px',
marginBottom: '20px',
borderLeft: '2px solid black',
}}
>
<div className="markdown-line">
Δ<sub>remaining</sub> = Δ
</div>
<div className="markdown-line">
<span style={{ fontWeight: 'bold' }}>for</span>
<span>
(<span style={{ fontStyle: 'italic' }}>j = i; j >= 0; i--</span>)
</span>
<span style={{ fontWeight: 'bold' }}> do</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sup>next</sup>
<sub>j</sub> = <span style={{ fontWeight: 'bold' }}>Min</span>(V<sup>
max
</sup>
<sub>j</sub>, <span style={{ fontWeight: 'bold' }}>Max</span>(V
<sup>min</sup>
<sub>j</sub>, V<sub>j</sub> + Δ<sub>remaining</sub>))
</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sup>Δ</sup>
<sub>j</sub> = V<sup>next</sup>
<sub>j</sub> - V<sub>j</sub>
</span>
</div>
<div className="markdown-line indent-1">
<span>
Δ<sub>remaining</sub> = Δ<sub>remaining</sub> - V<sup>Δ</sup>
<sub>j</sub>
</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sub>j</sub> = V<sup>next</sup>
<sub>j</sub>
</span>
</div>
</div>
or in plain text
<div
style={{
borderLeft: '2px solid black',
margin: '0px 20px 20px 20px',
display: 'flex',
flexDirection: 'row',
}}
>
<div
style={{ borderRight: '2px solid black', flexShrink: 0, width: '20px' }}
>
<div style={{ display: 'flex', justifyContent: 'center' }}>1</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>2</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>3</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>4</div>
</div>
<div style={{ flexGrow: 1, marginLeft: '20px' }}>
<div>
For each view j to the left of the sash we have dragged, from
right-most to left-most
</div>
<div>
Add the delta to the view j (clamped at either the maximum or
minimum value)
</div>
<div>
Subtract the different between the new and old size (the used delta)
from the remaining delta
</div>
<div>repeat</div>
</div>
</div>
Going back to that obvious flaw, the width of the control does not remain constant. It flexes with Δ.
## Iteration #2 - Δ is added. Then Δ must be removed
We want a component of constant width and we dont want to think too hard.
So If I have added Δ to the left then I should add -Δ (or remove Δ) on the right, and vice-versa; right? Nearly.
<Splitview mode={2} debug={true} />
There are some more subtle flaws with this approach; but lets describe it in psuedocode first.
We now need another variable to track the delta we've added on the left, Δ<sub>used</sub>.
After we've applied changes to the left side we'll substract this Δ<sub>used</sub> from the right side which should keep the width of our control at a
constant size.
<div style={{display: "inline-block", marginLeft: "20px", marginBottom: "20px", borderLeft: "2px solid black"}}>
<div className="markdown-line">
Δ<sub>remaining</sub> = Δ
</div>
<div className="markdown-line markdown-highlight">
Δ<sub>used</sub> = 0
</div>
<div className="markdown-line">
<span style={{fontWeight: "bold"}}>for</span><span>(<span style={{fontStyle: "italic"}}>j = i; j >= 0; i--</span>)</span>
<span style={{fontWeight: "bold"}}> do</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sup>next</sup><sub>j</sub> = <span style={{fontWeight: "bold"}}>Min</span>(V<sup>max</sup><sub>j</sub>, <span style={{fontWeight: "bold"}}>Max</span>(V<sup>min</sup><sub>j</sub>, V<sub>j</sub> + Δ<sub>remaining</sub>))
</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sup>Δ</sup><sub>j</sub> = V<sup>next</sup><sub>j</sub> - V<sub>j</sub>
</span>
</div>
<div className="markdown-line indent-1">
<span>
Δ<sub>remaining</sub> = Δ<sub>remaining</sub> - V<sup>Δ</sup><sub>j</sub>
</span>
</div>
<div className="markdown-line indent-1 markdown-highlight">
<span>
Δ<sub>used</sub> = Δ<sub>used</sub> + V<sup>Δ</sup><sub>j</sub>
</span>
</div>
<div className="markdown-line indent-1">
<span>
V<sub>j</sub> = V<sup>next</sup><sub>j</sub>
</span>
</div>
<div style={{height: "0px", width: "100%", marginBottom: "20px"}}/>
<div className="markdown-line markdown-highlight">
<span style={{fontWeight: "bold"}}>for</span><span>(<span style={{fontStyle: "italic"}}>{"j = i+1; j < n; i++"}</span>)</span>
<span style={{fontWeight: "bold"}}> do</span>
</div>
<div className="markdown-line markdown-highlight indent-1">
<span>
V<sup>next</sup><sub>j</sub> = <span style={{fontWeight: "bold"}}>Min</span>(V<sup>max</sup><sub>j</sub>, <span style={{fontWeight: "bold"}}>Max</span>(V<sup>min</sup><sub>j</sub>,V<sub>j</sub> - Δ<sub>used</sub>))
</span>
</div>
<div className="markdown-line markdown-highlight indent-1">
<span>
V<sup>Δ</sup><sub>j</sub> = V<sup>next</sup><sub>j</sub> - V<sub>j</sub>
</span>
</div>
<div className="markdown-line markdown-highlight indent-1">
<span>
Δ<sub>used</sub> = Δ<sub>used</sub> + V<sup>Δ</sup><sub>j</sub>
</span>
</div>
<div style={{marginBottom: "20px"}} className="markdown-line markdown-highlight indent-1">
<span>
V<sub>j</sub> = V<sup>next</sup><sub>j</sub>
</span>
</div>
</div>
Go back and try to minimise or maximise every view in the container. The width is no longer preserved, you can see at some point the change to the left
is not longer eqaul to the change on the right, which causes the container to once again flex.
## Iteration #3 - Constraining the values of Δ
In the cases where iteration #2 is not working correctly this can be explained as either adding or removing too much delta.
Fortunately it turns out there are a few constraints we can define on delta to help with this.
For a sash S<sub>i</sub> lets think about the minimum and maximum amount of delta we can use.
Try to follow the explainations first and then we will use another interactive example and diagram to reinforce the explaination.
> S<sub>i</sub> can go no further left that the sum of the minimum sizes of the views to the left, and can go no further right than the sum of the
> minimum sizes of the views to the right; because otherwise you would end up with at least one view that is smaller than it's specified minimum.
> Another less obvious condition on S<sub>i</sub> is that it can go no further left that the sum of the maximum sizes of the views to the right, and can go no further
> right than the sum of maximum sizes to the left; because otherwise you would end up with at least one view that is larger than it's specified maximum.
This leaves us with 4 constraints we need to apply on Δ; but since Δ is relative to S<sub>i</sub> first of all we need these
constraints relative to S<sub>i</sub>. That is to say what amount of Δ is equivalent to the constraints we just described.
When the views to the left of S<sub>i</sub> are all at minimum size we will call the distance between here and Δ to be Δ<sup>min</sup><sub>left</sub>.
This distance would be the sum of the differences between
V<sup>min</sup><sub>j</sub> and V<sub>j</sub> for each view, or in more format notation we could write this as
<div style={{ margin: '20px' }}>
<span>
Δ<sup>min</sup>
<sub>left</sub> = Σ V<sup>min</sup>
<sub>j</sub> - V<sub>j</sub>
</span>
<span style={{ marginLeft: '20px' }}>j = i,...0</span>
</div>
Similarly we can work out the distance between S<sub>i</sub> and the point at each every view to the left is at its
maximum size as the sum of differences between V<sup>max</sup><sub>j</sub> an V<sub>j</sub>
<div style={{ margin: '20px' }}>
<span>
Δ<sup>max</sup>
<sub>left</sub> = Σ V<sup>max</sup>
<sub>j</sub> - V<sub>j</sub>
</span>
<span style={{ marginLeft: '20px' }}>j = i,...0</span>
</div>
The same logic can be applied to work out those values for Δ<sup>min</sup><sub>right</sub> and Δ<sup>max</sup><sub>right</sub>
<div style={{ margin: '20px' }}>
<div style={{ marginBottom: '10px' }}>
<span>
Δ<sup>min</sup>
<sub>right</sub> = Σ V<sub>j</sub> - V<sup>min</sup>
<sub>j</sub>
</span>
<span style={{ marginLeft: '20px' }}>j = i+1...n</span>
</div>
<div>
<span>
Δ<sup>max</sup>
<sub>right</sub> = Σ V<sub>j</sub> - V<sup>min</sup>
<sub>j</sub>
</span>
<span style={{ marginLeft: '20px' }}>j = i+1...n</span>
</div>
</div>
We now have two minimum constraints which are V<sup>min</sup><sub>left</sub> and V<sup>max</sup><sub>right</sub> and two maximum
constraints V<sup>max</sup><sub>left</sub> and V<sup>min</sup><sub>right</sub>.
To get one minimum and maximum value for the Δ we should take the maximum of the two minimums and the minimum of the two maximums which will
ensure all four constraints will hold true.
<div style={{ margin: '20px' }}>
<div style={{ marginBottom: '10px' }}>
Δ<sub>min</sub> = MAX ( V<sup>min</sup>
<sub>left</sub> , V<sup>max</sup>
<sub>right</sub> )
</div>
<div>
Δ<sub>max</sub> = MIN ( V<sup>max</sup>
<sub>left</sub> , V<sup>min</sup>
<sub>right</sub> )
</div>
</div>
Finally we must clamp our Δ to be within this minimum and maxium boundary.
This clamped delta can be used in place of delta in the pseudocode from iteration #2.
<div style={{ margin: '20px' }}>
Δ<sub>clamped</sub> = MIN ( V<sub>max</sub> , MAX ( V<sub>min</sub> , Δ ) )
</div>
You can see how this works in this interactive example which also shows the current values of our four constraints for each sash drag event.
<Splitview mode={3} debug={true} />
Additionally you can see in the below diagram how all of our above calculations come together to give a minimum and maximum constraints on Δ.
![alt txt](./constraints.jpg)
## Appendix
### The clamp function
To clamp a value is to say given a value, a minimum and a maximum return
- the minimum, if value < minimum
- the maximum if value > maximum
- otherwise returning the value
In otherwords we have 'clamped' the value to be within our defined minimum and maximum values. Mathematically we can write this as
<div style={{ marginBottom: '20px' }} className="markdown-line">
<b>f</b>(value, value<sub>min</sub>, value<sub>max</sub>) = <b>MIN</b>(value
<sub>max</sub>, <b>MAX</b>(value<sub>min</sub>, value))
</div>
with some examples
<div style={{ marginLeft: '20px', marginBottom: '20px' }}>
<div className="markdown-small-line">
<b>f</b>(10, 20, 30) = 20
</div>
<div className="markdown-small-line">
<b>f</b>(40, 20, 30) = 30
</div>
<div className="markdown-small-line">
<b>f</b>(25, 20, 30) = 25
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,23 @@
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 996px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}

46
docs/src/pages/index.tsx Normal file
View File

@ -0,0 +1,46 @@
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/HomepageFeatures';
import { SimpleDockview } from '../components/simpleDockview';
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
</Link>
</div>
</div>
</header>
);
}
export default function Home(): JSX.Element {
const { siteConfig } = useDocusaurusContext();
return (
<Layout
title={`Hello from ${siteConfig.title}`}
description="Description will go into a meta tag in <head />"
>
<HomepageHeader />
<main className="container">
<div style={{ height: '500px', padding: '20px 0px' }}>
<SimpleDockview />
</div>
{/* <HomepageFeatures /> */}
</main>
</Layout>
);
}

View File

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

7
docs/src/theme/Root.tsx Normal file
View File

@ -0,0 +1,7 @@
import React from 'react';
import { RecoilRoot } from 'recoil';
// Default implementation, that you can customize
export default function Root({ children }) {
return <RecoilRoot>{children}</RecoilRoot>;
}

0
docs/static/.nojekyll vendored Normal file
View File

BIN
docs/static/img/docusaurus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
docs/static/img/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

1
docs/static/img/logo.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,171 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
<title>Easy to Use</title>
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,170 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
<title>Powered by React</title>
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
</g>
<g id="React-icon" transform="translate(906.3 541.56)">
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663">
<title>Focus on What Matters</title>
<circle cx="321" cy="321" r="321" fill="#f2f2f2" />
<ellipse cx="559" cy="635.49998" rx="514" ry="27.50002" fill="#3f3d56" />
<ellipse cx="558" cy="627" rx="460" ry="22" opacity="0.2" />
<rect x="131" y="152.5" width="840" height="50" fill="#3f3d56" />
<path d="M166.5,727.3299A21.67009,21.67009,0,0,0,188.1701,749H984.8299A21.67009,21.67009,0,0,0,1006.5,727.3299V296h-840Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" opacity="0.2" />
<circle cx="181" cy="147.5" r="13" fill="#3f3d56" />
<circle cx="217" cy="147.5" r="13" fill="#3f3d56" />
<circle cx="253" cy="147.5" r="13" fill="#3f3d56" />
<rect x="168" y="213.5" width="337" height="386" rx="5.33505" fill="#606060" />
<rect x="603" y="272.5" width="284" height="22" rx="5.47638" fill="#2e8555" />
<rect x="537" y="352.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="396.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="440.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="484.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="865" y="552.5" width="88" height="26" rx="7.02756" fill="#3ecc5f" />
<path d="M1088.60287,624.61594a30.11371,30.11371,0,0,0,3.98291-15.266c0-13.79652-8.54358-24.98081-19.08256-24.98081s-19.08256,11.18429-19.08256,24.98081a30.11411,30.11411,0,0,0,3.98291,15.266,31.248,31.248,0,0,0,0,30.53213,31.248,31.248,0,0,0,0,30.53208,31.248,31.248,0,0,0,0,30.53208,30.11408,30.11408,0,0,0-3.98291,15.266c0,13.79652,8.54353,24.98081,19.08256,24.98081s19.08256-11.18429,19.08256-24.98081a30.11368,30.11368,0,0,0-3.98291-15.266,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53213Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<ellipse cx="1038.00321" cy="460.31783" rx="19.08256" ry="24.9808" fill="#3f3d56" />
<ellipse cx="1038.00321" cy="429.78574" rx="19.08256" ry="24.9808" fill="#3f3d56" />
<path d="M1144.93871,339.34489a91.61081,91.61081,0,0,0,7.10658-10.46092l-50.141-8.23491,54.22885.4033a91.566,91.566,0,0,0,1.74556-72.42605l-72.75449,37.74139,67.09658-49.32086a91.41255,91.41255,0,1,0-150.971,102.29805,91.45842,91.45842,0,0,0-10.42451,16.66946l65.0866,33.81447-69.40046-23.292a91.46011,91.46011,0,0,0,14.73837,85.83669,91.40575,91.40575,0,1,0,143.68892,0,91.41808,91.41808,0,0,0,0-113.02862Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M981.6885,395.8592a91.01343,91.01343,0,0,0,19.56129,56.51431,91.40575,91.40575,0,1,0,143.68892,0C1157.18982,436.82067,981.6885,385.60008,981.6885,395.8592Z" transform="translate(-35.5 -118.5)" opacity="0.1" />
<path d="M365.62,461.43628H477.094v45.12043H365.62Z" transform="translate(-35.5 -118.5)" fill="#fff" fill-rule="evenodd" />
<path d="M264.76252,608.74122a26.50931,26.50931,0,0,1-22.96231-13.27072,26.50976,26.50976,0,0,0,22.96231,39.81215H291.304V608.74122Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M384.17242,468.57061l92.92155-5.80726V449.49263a26.54091,26.54091,0,0,0-26.54143-26.54143H331.1161l-3.31768-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622-3.31767-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622L301.257,417.205a3.83043,3.83043,0,0,0-6.63536,0L291.304,422.9512c-.02919,0-.05573.004-.08625.004l-5.49674-5.49541a3.8293,3.8293,0,0,0-6.4071,1.71723l-1.81676,6.77338L270.607,424.1031a3.82993,3.82993,0,0,0-4.6912,4.69253l1.84463,6.89148-6.77072,1.81411a3.8315,3.8315,0,0,0-1.71988,6.40975l5.49673,5.49673c0,.02787-.004.05574-.004.08493l-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74621,3.31768L259.0163,466.081a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768L259.0163,558.976a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768A26.54091,26.54091,0,0,0,291.304,635.28265H450.55254A26.5409,26.5409,0,0,0,477.094,608.74122V502.5755l-92.92155-5.80727a14.12639,14.12639,0,0,1,0-28.19762" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M424.01111,635.28265h39.81214V582.19979H424.01111Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M490.36468,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15393-.59852A6.62668,6.62668,0,1,0,482.80568,590.21q-.2203-.22491-.44457-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39414-.10218-.59056-.15262a6.63957,6.63957,0,1,0-13.10086,0c-.1964.05042-.39414.09687-.59056.15262a6.62767,6.62767,0,1,0-11.39688,6.56369,26.52754,26.52754,0,1,0,44.23127,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M437.28182,555.65836H477.094V529.11693H437.28182Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M490.36468,545.70532a3.31768,3.31768,0,0,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M317.84538,466.081a3.31768,3.31768,0,0,1-3.31767-3.31768,9.953,9.953,0,1,0-19.90608,0,3.31768,3.31768,0,1,1-6.63535,0,16.58839,16.58839,0,1,1,33.17678,0,3.31768,3.31768,0,0,1-3.31768,3.31768" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
<path d="M370.92825,635.28265h79.62429A26.5409,26.5409,0,0,0,477.094,608.74122v-92.895H397.46968a26.54091,26.54091,0,0,0-26.54143,26.54143Z" transform="translate(-35.5 -118.5)" fill="#ffff50" fill-rule="evenodd" />
<path d="M457.21444,556.98543H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0-66.10674H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.29459H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414M477.094,474.19076c-.01592,0-.0292-.008-.04512-.00663-4.10064.13934-6.04083,4.24132-7.75274,7.86024-1.78623,3.78215-3.16771,6.24122-5.43171,6.16691-2.50685-.09024-3.94007-2.92222-5.45825-5.91874-1.74377-3.44243-3.73438-7.34667-7.91333-7.20069-4.04227.138-5.98907,3.70784-7.70631,6.857-1.82738,3.35484-3.07084,5.39455-5.46887,5.30033-2.55727-.09289-3.91619-2.39536-5.48877-5.06013-1.75306-2.96733-3.77951-6.30359-7.8775-6.18946-3.97326.13669-5.92537,3.16507-7.64791,5.83912-1.82207,2.82666-3.09872,4.5492-5.52725,4.447-2.61832-.09289-3.9706-2.00388-5.53522-4.21611-1.757-2.4856-3.737-5.299-7.82308-5.16231-3.88567.13271-5.83779,2.61434-7.559,4.80135-1.635,2.07555-2.9116,3.71846-5.61218,3.615a1.32793,1.32793,0,1,0-.09555,2.65414c4.00377.134,6.03154-2.38873,7.79257-4.6275,1.562-1.9853,2.91027-3.69855,5.56441-3.78879,2.55594-.10882,3.75429,1.47968,5.56707,4.04093,1.7212,2.43385,3.67465,5.19416,7.60545,5.33616,4.11789.138,6.09921-2.93946,7.8536-5.66261,1.56861-2.43385,2.92221-4.53461,5.50734-4.62352,2.37944-.08892,3.67466,1.79154,5.50072,4.885,1.72121,2.91557,3.67069,6.21865,7.67977,6.36463,4.14709.14332,6.14965-3.47693,7.89475-6.68181,1.51155-2.77092,2.93814-5.38791,5.46621-5.4755,2.37944-.05573,3.62025,2.11668,5.45558,5.74622,1.71459,3.388,3.65875,7.22591,7.73019,7.37321l.22429.004c4.06614,0,5.99571-4.08074,7.70364-7.68905,1.51154-3.19825,2.94211-6.21069,5.3972-6.33411Z" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
<path d="M344.38682,635.28265h53.08286V582.19979H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M424.01111,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15394-.59852A6.62667,6.62667,0,1,0,416.45211,590.21q-.2203-.22491-.44458-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39413-.10218-.59054-.15262a6.63957,6.63957,0,1,0-13.10084,0c-.19641.05042-.39414.09687-.59055.15262a6.62767,6.62767,0,1,0-11.39689,6.56369,26.52755,26.52755,0,1,0,44.2313,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M344.38682,555.65836h53.08286V529.11693H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M410.74039,545.70532a3.31768,3.31768,0,1,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M424.01111,447.8338a3.60349,3.60349,0,0,1-.65028-.06636,3.34415,3.34415,0,0,1-.62372-.18579,3.44679,3.44679,0,0,1-.572-.30522,5.02708,5.02708,0,0,1-.50429-.4114,3.88726,3.88726,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.60248,3.60248,0,0,1,.06636-.65027,3.82638,3.82638,0,0,1,.18447-.62373,3.48858,3.48858,0,0,1,.30656-.57064,3.197,3.197,0,0,1,.91436-.91568,3.44685,3.44685,0,0,1,.572-.30523,3.344,3.344,0,0,1,.62372-.18578,3.06907,3.06907,0,0,1,1.30053,0,3.22332,3.22332,0,0,1,1.19436.491,5.02835,5.02835,0,0,1,.50429.41139,4.8801,4.8801,0,0,1,.41139.50429,3.38246,3.38246,0,0,1,.30522.57064,3.47806,3.47806,0,0,1,.25215,1.274A3.36394,3.36394,0,0,1,426.36,446.865a5.02708,5.02708,0,0,1-.50429.4114,3.3057,3.3057,0,0,1-1.84463.55737m26.54143-1.65884a3.38754,3.38754,0,0,1-2.35024-.96877,5.04185,5.04185,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.38659,3.38659,0,0,1,.96744-2.34892,5.02559,5.02559,0,0,1,.50429-.41139,3.44685,3.44685,0,0,1,.572-.30523,3.3432,3.3432,0,0,1,.62373-.18579,3.06952,3.06952,0,0,1,1.30052,0,3.22356,3.22356,0,0,1,1.19436.491,5.02559,5.02559,0,0,1,.50429.41139,3.38792,3.38792,0,0,1,.96876,2.34892,3.72635,3.72635,0,0,1-.06636.65026,3.37387,3.37387,0,0,1-.18579.62373,4.71469,4.71469,0,0,1-.30522.57064,4.8801,4.8801,0,0,1-.41139.50429,5.02559,5.02559,0,0,1-.50429.41139,3.30547,3.30547,0,0,1-1.84463.55737" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

7
docs/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@tsconfig/docusaurus/tsconfig.json",
"compilerOptions": {
"baseUrl": "."
}
}

7764
docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
"lint": "eslint packages/**/src/** --ext .ts,.tsx,.js,.jsx",
"package": "node scripts/package.js",
"package-all": "npm run build-demo && npm run docs && node scripts/package.js",
"deploy-docs": "lerna run build --scope dockview-docs && lerna run export --scope dockview-docs && node scripts/package-docs.js",
"build": "lerna run build --scope dockview",
"build-demo": "lerna run build --scope dockview-demo",
"docs": "lerna run docs --scope dockview",

View File

@ -259,7 +259,7 @@ export const TestGrid = (props: IGridviewPanelProps) => {
tabComponents={tabComponents}
onTabContextMenu={onTabContextMenu}
watermarkComponent={Watermark}
showDndOverlay={(ev, target) => {
showDndOverlay={(ev) => {
return true;
}}
onDidDrop={(ev) => {

View File

@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@ -1,35 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo

View File

@ -1,34 +0,0 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -1,58 +0,0 @@
import transpile from 'next-transpile-modules';
import slugs from 'rehype-slug';
import autoLinkHeadings from 'rehype-autolink-headings';
import mdx from '@next/mdx';
import remarkGfm from 'remark-gfm';
const withTM = transpile(['dockview'], {
resolveSymlinks: true,
});
const withMDX = mdx({
extension: /\.mdx$/,
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [
slugs,
[
autoLinkHeadings,
{
behavior: 'append',
},
],
],
providerImportSource: '@mdx-js/react',
},
});
console.log(`CI=${process.env.CI}`);
const extraParams = process.env.CI
? {
basePath: '/dockview/docs',
assetPrefix: '/dockview/docs/',
}
: {};
export default withTM(
withMDX({
...extraParams,
reactStrictMode: true,
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
experimental: {
externalDir: true,
},
webpack(config, options) {
if (options.isServer) {
config.externals = ['react', 'react-dom', ...config.externals];
}
config.module.rules.push({
test: /\.tsx?|\.ts?$/,
use: [options.defaultLoaders.babel],
});
return config;
},
})
);

View File

@ -1,34 +0,0 @@
{
"name": "dockview-docs",
"version": "1.4.2",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"export": "next export"
},
"dependencies": {
"@mdx-js/loader": "^2.1.1",
"@mdx-js/react": "^2.1.1",
"@next/mdx": "^12.1.6",
"dockview": "^1.4.2",
"next": "12.1.6",
"prism-react-renderer": "^1.3.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
"remark-gfm": "^3.0.1"
},
"devDependencies": {
"@types/node": "17.0.31",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.3",
"eslint": "8.15.0",
"eslint-config-next": "12.1.6",
"next-transpile-modules": "^9.0.0",
"typescript": "4.6.4"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>

Before

Width:  |  Height:  |  Size: 350 B

View File

@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,70 +0,0 @@
import React from 'react';
import Highlight, { defaultProps, Language } from 'prism-react-renderer';
import theme from 'prism-react-renderer/themes/palenight';
interface CodeBlockProps {
children: string;
className?: string;
live?: boolean | string;
render?: boolean | string;
url: string;
code?: boolean | string;
center?: boolean | string;
edit?: boolean | string;
}
export const CodeBlock = ({
children,
className = '',
live,
render,
url,
code = true,
center = true,
edit = false,
}: CodeBlockProps) => {
if (!className) {
return (
<code
style={{
backgroundColor: 'rgba(27, 31, 35, 0.05)',
fontSize: '85%',
padding: '0.2em 0.4em',
margin: '0px',
borderRadius: '5px',
}}
>
{children}
</code>
);
}
const language = className.replace(/language-/, '');
return (
<Highlight
{...defaultProps}
theme={theme}
code={children.trim()}
language={language as Language}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre
className={className}
style={{ ...style, padding: '20px' }}
>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span
key={key}
{...getTokenProps({ token, key })}
/>
))}
</div>
))}
</pre>
)}
</Highlight>
);
};

View File

@ -1,24 +0,0 @@
import { PAGES } from '../references/pages';
import { Navigation } from './navigation';
export const Container = (props: { children: React.ReactNode }) => {
return (
<main
style={{
flexGrow: 1,
display: 'flex',
flexDirection: 'row',
}}
>
<div style={{ padding: '20px' }}>
<Navigation pages={PAGES} />
</div>
<div
style={{ flexGrow: 1, padding: '20px' }}
className="markdown-body"
>
{props.children}
</div>
</main>
);
};

View File

@ -1,7 +0,0 @@
export const Footer = (props: {}) => {
return (
<div style={{ height: '100px', backgroundColor: 'rgb(30,30,30)' }}>
footer
</div>
);
};

View File

@ -1,51 +0,0 @@
import { DockviewReact, DockviewReadyEvent } from 'dockview';
const components = {
default: () => {
return <div>Hello World</div>;
},
};
export const Header = (props: {}) => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel1',
component: 'default',
});
event.api.addPanel({
id: 'panel2',
component: 'default',
});
event.api.addPanel({
id: 'panel3',
component: 'default',
position: {
referencePanel: 'panel2',
direction: 'right',
},
});
};
return (
<div
style={{
backgroundColor: 'var(--header-color)',
padding: '0px 8px',
display: 'flex',
alignItems: 'center',
borderBottom: '1px solid var(--border-color)',
}}
>
<div>
<div>
<span style={{ fontSize: '2em' }}>dockview</span>
<span style={{ fontSize: '1.25em' }}>
{' documentation'}
</span>
</div>
</div>
</div>
);
};

View File

@ -1,88 +0,0 @@
import * as React from 'react';
import { Page } from '../references/pages';
import Link from 'next/link';
const LinkHeader = (props: { url: string; title: string }) => {
return (
<Link href={props.url}>
<a>{props.title}</a>
</Link>
);
};
export const CollapsibleNode = (props: {
page: Page;
children: React.ReactNode;
depth: number;
}) => {
const [expanded, setExpaned] = React.useState<boolean>(false);
const onClick = () => {
setExpaned(!expanded);
};
const cn = React.useMemo(() => {
return ['node', expanded ? 'expanded' : 'collapsed'].join(' ');
}, [expanded]);
return (
<>
<div className={cn} onClick={onClick}>
{props.page.url ? (
<LinkHeader url={props.page.url} title={props.page.title} />
) : (
props.page.title
)}
</div>
<div
className="node"
style={{
display: expanded ? '' : 'none',
overflow: 'hidden',
marginLeft: `${props.depth * 8}px`,
}}
>
{props.children}
</div>
</>
);
};
export const Node = (props: { page: Page; depth: number }) => {
if (props.page.routes) {
return (
<CollapsibleNode page={props.page} depth={props.depth + 1}>
{props.page.routes.map((page) => (
<Node
key={page.title}
page={page}
depth={props.depth + 1}
/>
))}
</CollapsibleNode>
);
}
return (
<div className="node">
{props.page.url ? (
<LinkHeader url={props.page.url} title={props.page.title} />
) : (
props.page.title
)}
</div>
);
};
export const Navigation = (props: { pages: Page[] }) => {
return (
<div
className="navigation"
style={{ position: 'sticky', top: '20px', left: '20px' }}
>
{props.pages.map((page) => (
<Node key={page.title} page={page} depth={0} />
))}
</div>
);
};

View File

@ -1,5 +0,0 @@
import * as React from 'react';
export const Test = () => {
return <div>this is a test component</div>;
};

View File

@ -1,33 +0,0 @@
import { Header } from '../components/header';
import { Footer } from '../components/footer';
import { Container } from '../components/container';
import { AppProps } from 'next/app';
import '../styles/globals.css';
import { CodeBlock } from '../components/code';
import { MDXProvider } from '@mdx-js/react';
const components = {
code: CodeBlock,
} as any;
const MyApp = (props: AppProps) => {
return (
<div
style={{
minHeight: '100vh',
display: 'flex',
flexDirection: 'column',
}}
>
<MDXProvider components={components}>
<Header />
<Container>
<props.Component {...props.pageProps} />
</Container>
<Footer />
</MDXProvider>
</div>
);
};
export default MyApp;

View File

@ -1,28 +0,0 @@
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="true"
/>
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
rel="stylesheet"
/>
{/* <link
rel="stylesheet"
href="https://unpkg.com/dracula-prism/dist/css/dracula-prism.css"
/> */}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@ -1,12 +0,0 @@
import * as React from 'react';
import { Test } from '../test';
const Component = () => {
return (
<div style={{ height: '50vh', width: '50vw' }}>
<Test />
</div>
);
};
export default Component;

View File

@ -1,159 +0,0 @@
import { SimpleSplitview } from '../components/simpleSplitview';
# Splitview
<div
style={{
height: '100px',
backgroundColor: 'rgb(30,30,30)',
color: 'white',
margin: '20px 0px',
}}
>
<SimpleSplitview />
</div>
```tsx
import {
ISplitviewPanelProps,
Orientation,
SplitviewReact,
SplitviewReadyEvent,
} from 'dockview';
const components = {
default: (props: ISplitviewPanelProps<{ title: string }>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>;
},
};
export const SimpleSplitview = () => {
const onReady = (event: SplitviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
};
return (
<SplitviewReact
components={components}
onReady={onReady}
orientation={Orientation.HORIZONTAL}
className="dockview-theme-dark"
/>
);
};
```
## Component Props
```tsx
import { ReactSplitview } from 'dockview';
```
The `onReady` prop you will you access to the component `api`.
| Property | Type | Optional | Default | Description |
| ------------------- | -------------------------------------- | -------- | ------------------------ | ------------------------------------------------------- |
| onReady | `(event: SplitviewReadyEvent) => void` | No | | |
| components | `Record<string, ISplitviewPanelProps>` | No | | Panel renderers |
| orientation | `Orientation` | Yes | `Orientation.HORIZONTAL` | Orientation of the Splitview |
| proportionalLayout | `boolean` | Yes | `true` | See [Proportional layout](/basics/#proportional-layout) |
| hideBorders | `boolean` | Yes | `false` | Hide the borders between panels |
| className | `string` | Yes | `''` | Attaches a classname |
| disableAutoResizing | `boolean` | Yes | `false` | See [Auto resizing](/basics/#auto-resizing) |
## Splitview API
```tsx
const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
// props.containerApi...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```
```tsx
const onReady = (event: SplitviewReadyEvent) => {
// event.api...
};
```
| Property | Type | Description |
| ------------------- | ------------------------------------------------------------------ | ------------------------------------------- |
| height | `number` | Component pixel height |
| width | `number` | Component pixel width |
| minimumSize | `number` | |
| maximumSize | `number` | |
| length | `number` | Number of panels |
| panels | `ISplitviewPanel[]` | |
| | | |
| onDidLayoutChange | `Event<void>` | |
| onDidLayoutFromJSON | `Event<void>` | |
| onDidAddView | `Event<IView>` | |
| onDidRemoveView | `Event<IView>` | |
| | | |
| addPanel | `addPanel(options: AddSplitviewComponentOptions): ISplitviewPanel` | |
| removePanel | `(panel: ISplitviewPanel, sizing?: Sizing): void` | |
| getPanel | `(id:string): ISplitviewPanel \| undefined` | |
| movePanel | `(from: number, to: number): void` | |
| | |
| setVisible | `(panel: ISplitviewPanel, isVisible: boolean): void` | |
| setActive | `(panel: ISplitviewPanel): void` | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | |
| layout | `(width: number, height:number): void` | See [Auto resizing](/basics/#auto-resizing) |
| fromJSON | `(data: SerializedSplitview): void` | See [Serialization](/basics/#serialization) |
| toJSON | `(): SerializedSplitview` | See [Serialization](/basics/#serialization) |
## Splitview Panel API
```tsx
const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
// props.api...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```
| Property | Type | Description |
| ---------------------- | ----------------------------------------------------------- | ---------------- |
| id | `string` | Panel id |
| isFocused | `boolean` | Is panel focsed |
| isActive | `boolean` | Is panel active |
| isVisible | `boolean` | Is panel visible |
| width | `number` | Panel width |
| height | `number` | Panel height |
| | |
| onDidDimensionsChange | `Event<PanelDimensionChangeEvent>` | |
| onDidFocusChange | `Event<FocusEvent>` | |
| onDidVisibilityChange | `Event<VisibilityEvent>` | |
| onDidActiveChange | `Event<ActiveEvent>` | |
| onFocusEvent | `Event<void>` | |
| onDidConstraintsChange | `onDidConstraintsChange: Event<PanelConstraintChangeEvent>` | |
| | | |
| setVisible | `(isVisible: boolean): void` | |
| setActive | `(): void` | |
| | | |
| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | |
| setSize | `(event: PanelSizeEvent): void` | |

View File

@ -1,33 +0,0 @@
export interface Page {
title: string;
url?: string;
routes?: Page[];
}
export const PAGES: Page[] = [
{ title: 'Introduction', url: '/#introduction' },
{ title: 'Basics', url: '/basics/#basics' },
{
title: 'API',
url: '#api',
routes: [
{
title: 'Splitview',
url: '/splitview/#splitview',
},
{
title: 'Gridview',
url: '/gridview/#gridview',
},
{
title: 'Dockview',
url: '/dockview/#dockview',
},
{
title: 'Paneview',
url: '/paneview/#paneview',
},
],
},
{ title: 'Guides', url: '#guides' },
];

View File

@ -1,78 +0,0 @@
@import "./navigation.css";
@import "~dockview/dist/styles/dockview.css";
:root {
--primary-text-color:rgb(30,30,30);
--header-color:rgb(235, 235, 235);
--navigation-color:rgb(245, 245, 245);
--border-color:rgb(30,30,30)
}
html,
body {
padding: 0;
margin: 0;
font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
font: 100% /1.65 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
}
.markdown-body a {
text-decoration: none;
color:dodgerblue;
}
.markdown-body a, .markdown-body a:hover, .markdown-body a:visited, .markdown-body a:active {
color:dodgerblue;
}
.markdown-body a:hover {
text-decoration: underline;
}
* {
box-sizing: border-box;
}
.icon {
width: 16px;
height: 16px;
display: inline-block;
margin-left: 8px;
}
.icon-link {
background-image: url(/icon-link.svg);
background-repeat: no-repeat;
background-size: 100%;
background-position: center center;
}
.markdown-body table {
border-collapse: collapse;
border-spacing: 0;
font-size: 12px;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body table tr {
background-color: rgb(255, 255, 255);
border-top: 1px solid rgb(198, 203, 209);
}
.markdown-body table th {
border:1px solid rgb(223, 226, 229);
padding: 6px 13px;
}
.markdown-body table td {
border:1px solid rgb(223, 226, 229);
padding: 6px 13px;
}
.markdown-body table tr:nth-child(2n) {
background-color: rgb(246, 248, 250);
}

View File

@ -1,50 +0,0 @@
.navigation {
padding: 30px;
background-color: var(--navigation-color);
font-size: 16px;
width: 200px;
border-radius: 15px;
}
.node {
padding: 5px 0px;
cursor: pointer;
}
.node a {
text-decoration: none;
color: inherit;
}
.expandable-node::after {
}
.expanded::after {
border-width: 6px 4px 0px;
border-color: grey transparent transparent;
content: '';
width: 0px;
height: 0px;
border-style: solid;
border-radius: 1px;
display: inline-block;
vertical-align: middle;
margin-left: 10px;
margin-bottom: 2px;
}
.collapsed::after {
border-width: 4px 6px 4px 0px;
border-color: transparent grey transparent;
content: '';
width: 0px;
height: 0px;
border-style: solid;
border-radius: 1px;
display: inline-block;
vertical-align: middle;
margin-left: 10px;
margin-bottom: 2px;
}

View File

@ -1,79 +0,0 @@
import {
GridviewReact,
Orientation,
GridviewReadyEvent,
DockviewReact,
DockviewReadyEvent,
} from 'dockview';
const components = {
default: () => {
return <div>hello world</div>;
},
docking: () => {
return <Test2 />;
},
};
export const Test = () => {
const onReady = (event: GridviewReadyEvent) => {
event.api.addPanel({
component: 'default',
id: 'view_1',
});
event.api.addPanel({
component: 'default',
id: 'view_2',
});
event.api.addPanel({
component: 'docking',
id: 'view_3',
});
};
return (
<GridviewReact
components={components}
orientation={Orientation.HORIZONTAL}
onReady={onReady}
className="dockview-theme-dark"
/>
);
};
const components2 = {
default: () => {
return <div>hello world</div>;
},
};
export const Test2 = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
component: 'default',
id: 'view_1',
});
event.api.addPanel({
component: 'default',
id: 'view_2',
});
event.api.addPanel({
component: 'default',
id: 'view_3',
});
};
return (
// <div style={{ height: '100%', width: '100%' }}>
<DockviewReact
components={components2}
onReady={onReady}
className="dockview-theme-dark"
/>
// </div>
);
};

View File

@ -1,24 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"paths": {
"react": ["node_modules/react"],
"react-dom": ["node_modules/react-dom"]
}
}

View File

@ -1,7 +1,7 @@
<div align="center">
<h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support</p>
<p>Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support written in TypeScript</p>
</div>
@ -15,12 +15,7 @@
##
A zero dependency layout manager based on the layering of splitview with support for ReactJS components, written in TypeScript.
- [➡️](https://mathuo.github.io/dockview/docs) Documentation
- [➡️](https://mathuo.github.io/dockview/) Live demo
- [➡️](https://github.com/mathuo/dockview/tree/master/packages/dockview-demo/src/stories) Code examples
- [➡️](https://mathuo.github.io/dockview/output/docs/index.html) Generated TypeDocs
Please see the website: https://mathuo.github.io/dockview/docs
Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/
@ -34,27 +29,17 @@ Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@l
- Tabular docking and Drag and Drop support
- Documentation and examples
## Table of contents
- [Quick start](#quick-start)
- [Sandbox examples](#sandbox-examples)
- [Serializated layouts](#serializated-layouts)
- [Drag and drop](#drag-and-drop)
- [Theming](#theming)
- [Performance](#performance)
- [FAQ](#faq)
This project was inspired by many popular IDE editors. Some parts of the core resizable panelling are inspired by code found in the VSCode codebase, [splitview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview) and [gridview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/grid).
## Quick start
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). Please see the [Getting Started Guide](https://mathuo.github.io/dockview/docs/).
```
npm install --save dockview
```
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css`. For example:
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
```css
@import '~dockview/dist/styles/dockview.css';
@ -65,179 +50,3 @@ You should also attach a dockview theme to an element containing your components
```html
<body classname="dockview-theme-dark"></body>
```
Demonstrated below is a high level example of a `DockviewReact` component. You can follow a similar pattern for `GridviewReact`, `SplitviewReact` and `PaneviewReact` components too, see examples for more.
```tsx
import {
DockviewReact,
DockviewReadyEvent,
PanelCollection,
IDockviewPanelProps,
IDockviewPanelHeaderProps,
} from 'dockview';
const components: PanelCollection<IDockviewPanelProps> = {
default: (props: IDockviewPanelProps<{ someProps: string }>) => {
return <div>{props.params.someProps}</div>;
},
};
const headers: PanelCollection<IDockviewPanelHeaderProps> = {
customTab: (props: IDockviewPanelHeaderProps) => {
return (
<div>
<span>{props.api.title}</span>
<span onClick={() => props.api.close()}>{'[x]'}</span>
</div>
);
},
};
const Component = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel1',
component: 'default',
tabComponent: 'customTab', // optional custom header
params: {
someProps: 'Hello',
},
});
event.api.addPanel({
id: 'panel2',
component: 'default',
params: {
someProps: 'World',
},
position: { referencePanel: 'panel1', direction: 'below' },
});
};
return (
<DockviewReact
components={components}
tabComponents={headers} // optional headers renderer
onReady={onReady}
/>
);
};
```
Specifically for `DockviewReact` there exists higher-order components to encapsulate both the tab and contents into one logical component for the user making state sharing between the two simple, which is an optional feature.
```tsx
const components: PanelCollection<IDockviewPanelProps> = {
default: (props: IDockviewPanelProps<{ someProps: string }>) => {
return <div>{props.params.someProps}</div>;
},
fancy: (props: IDockviewPanelProps) => {
return (
<DockviewComponents.Panel>
<DockviewComponents.Tab>
<div>
<span>{props.api.title}</span>
<span onClick={() => props.api.close()}>{'Close'}</span>
</div>
</DockviewComponents.Tab>
<DockviewComponents.Content>
<div>{'Hello world'}</div>
</DockviewComponents.Content>
</DockviewComponents.Panel>
);
},
};
```
## Sandbox examples
- [Dockview](https://codesandbox.io/s/simple-dockview-t6491)
- [Gridview](https://codesandbox.io/s/simple-gridview-jrp0n)
- [Splitview](https://codesandbox.io/s/simple-splitview-l53nn)
- [Paneview](https://codesandbox.io/s/simple-paneview-v8qvb)
## Serializated layouts
All view components support the methods `toJSON()`, `fromJSON(...)` and `onDidLayoutChange()`.
See example [here](https://codesandbox.io/s/workspace-saving-example-euo5d).
## Drag and drop
Both `DockviewReact` and `PaneviewReact` support drag and drop functionality out of the box.
- `DockviewReact` allows tabs to be repositioned using drag and drop.
- `PaneviewReact` allows the repositioning of views using drag and drop on the header section.
You can use the utility methods `getPaneData` and `getPanelData` to manually extract the data transfer metadata associated with an active drag and drop event for either of the above components.
```tsx
import {
getPaneData,
getPanelData,
PanelTransfer,
PaneTransfer,
} from 'dockview';
const panelData: PanelTransfer | undefined = getPanelData();
if (panelData) {
// DockviewReact: data transfer metadata associated with the active drag and drop event
const { viewId, groupId, panelId } = panelData; // deconstructed object
}
const paneData: PaneTransfer | undefined = getPaneData();
if (paneData) {
// PaneviewReact: data transfer metadata associated with the active drag and drop event
const { viewId, paneId } = paneData; // deconstructed object
}
```
You can also intercept custom drag and drop events allowing dockview components to interact with and react to external drag events. `PaneviewReact` supports the method `onDidDrop` and `DockviewReact` supports the methods `onDidDrop` and `showDndOverlay`.
## Theming
The theme can be customized using the below set of CSS properties. You can find the built in themes [here](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/theme.scss) which could be used as an example to extend upon or build your own theme.
| CSS Property | Description |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **General** |
| --dv-active-sash-color | The background color a dividing sash during an interaction |
| --dv-separator-border | The color of the seperator between panels |
| **Paneview** |
| --dv-paneview-header-border-color | - |
| --dv-paneview-active-outline-color | The primary accent color, used for example to highlight the active panel in Paneviews |
| **Dockview -> Dragging** |
| --dv-drag-over-background-color | The overlay color applied to a group when a moving tab is dragged over |
| **Dockview -> Tabs container** |
| --dv-tabs-and-actions-container-font-size | - |
| --dv-tabs-and-actions-container-height | Default tab height |
| --dv-tabs-and-actions-container-background-color | - |
| --dv-tabs-container-scrollbar-color | - |
| --dv-group-view-background-color | - |
| **Dockview -> Tabs** | (see [dockviewComponent.scss](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/dockview/dockviewComponent.scss)) |
| --dv-activegroup-visiblepanel-tab-background-color | The background color of the tab for the visible panel in the active group |
| --dv-activegroup-hiddenpanel-tab-background-color | The background color of the tab for the hidden panel/s in the active group |
| --dv-inactivegroup-visiblepanel-tab-background-color | The background color of the tab for the visible panel in groups other than the active group |
| --dv-inactivegroup-hiddenpanel-tab-background-color | The background color of the tab for the hidden panel/s in groups other than the active group |
| --dv-activegroup-visiblepanel-tab-color | The color of the tab for the visible panel in the active group |
| --dv-activegroup-hiddenpanel-tab-color | The color of the tab for the hidden panel/s in the active group |
| --dv-inactivegroup-visiblepanel-tab-color | The color of the tab for the visible panel in groups other than the active group |
| --dv-inactivegroup-hiddenpanel-tab-color | The color of the tab for the hidden panel/s in groups other than the active group |
| --dv-tab-divider-color | - |
| --dv-tab-close-icon | Default tab close icon |
## Performance
Consider using React.lazy(...) to defer the importing of your panels until they are required. This has the potential to reduce the initial import cost when your application starts.
## FAQ
**Q: Can I use this library without React?**
**A:** In theory, yes. The library is written in plain-old JS and the parts written in ReactJS are merely wrappers around the plain-old JS components. Currently everything is published as one package though so maybe that's something to change in the future.
**Q: Can I use this library with AngularJS/Vue.js or any other arbitrarily named JavaScript library/framework?**
**A:** Yes but with some extra work. Dockview is written in plain-old JS so you can either interact directly with the plain-old JS components or create a wrapper using your prefered library/framework. The React wrapper may give some ideas on how this wrapper implementation could be done for other libraries/frameworks. Maybe that's something to change in the future.

View File

@ -3,6 +3,6 @@ const path = require('path');
const output = path.join(__dirname, '../');
const docsDir = path.join(__dirname, '../packages/dockview-docs/out');
const docsDir = path.join(__dirname, '../docs/build');
fs.copySync(docsDir, path.join(output, 'docs'));

5561
yarn.lock

File diff suppressed because it is too large Load Diff