chore: docs (#527)

* chore: docs
This commit is contained in:
mathuo 2024-03-02 21:49:09 +00:00 committed by GitHub
parent f00b3d88e1
commit 0634f4fe52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 31843 additions and 4414 deletions

View File

@ -667,21 +667,29 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
}
/**
* Invoked before an overlay is shown indicating a drop target.
*
* Calling `event.preventDefault()` will prevent the overlay being shown and prevent
* the any subsequent drop event.
*/
get onWillShowOverlay(): Event<WillShowOverlayLocationEvent> {
return this.component.onWillShowOverlay;
}
/**
* Invoked before a group is dragged. Exposed for custom Drag'n'Drop functionality.
* Invoked before a group is dragged.
*
* Calling `event.nativeEvent.preventDefault()` will prevent the group drag starting.
*
*/
get onWillDragGroup(): Event<GroupDragEvent> {
return this.component.onWillDragGroup;
}
/**
* Invoked before a panel is dragged. Exposed for custom Drag'n'Drop functionality.
* Invoked before a panel is dragged.
*
* Calling `event.nativeEvent.preventDefault()` will prevent the panel drag starting.
*/
get onWillDragPanel(): Event<TabDragEvent> {
return this.component.onWillDragPanel;

View File

@ -6,6 +6,10 @@ sidebar_position: 3
import { DocRef } from '@site/src/components/ui/reference/docRef';
:::info
You may want to combine this with `locked={true}` to provide a locked grid with no dnd funtionality. See [Locked](/docs/core/locked) for more.
:::
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="DockviewComponentOptions" methods={["disableDnd"]} />
</FrameworkSpecific>

View File

@ -5,7 +5,6 @@ sidebar_position: 1
import useBaseUrl from '@docusaurus/useBaseUrl';
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import LiveExample from '@site/src/components/ui/exampleFrame';
@ -16,7 +15,7 @@ The dock makes heavy use of drag and drop functionalities.
<DocRef declaration="DockviewApi"
methods={[
'onWillDragPanel', 'onWillDragGroup',
'onWillDrop', 'onDidDrop', 'onWillShowOverlay'
'onWillDrop', 'onWillShowOverlay'
]}
/>
@ -79,12 +78,6 @@ return (
);
```
## Intercepting Drag Events
You can intercept drag events to attach your own metadata using the `onWillDragPanel` and `onWillDragGroup` api methods.
<MultiFrameworkContainer sandboxId="dnd-dockview" react={DndDockview} />
## Third Party Dnd Libraries
This shows a simple example of a third-party library used inside a panel that relies on drag

View File

@ -0,0 +1,31 @@
---
title: 'External Dnd Events'
sidebar_position: 3
---
External Dnd events can be intercepted through a number of utilities.
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
<DocRef declaration="DockviewApi"
methods={['onDidDrop']}
/>
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['showDndOverlay']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions"
methods={['showDndOverlay']}
/>
</FrameworkSpecific>
## Intercepting Drag Events
You can intercept drag events to attach your own metadata using the `onWillDragPanel` and `onWillDragGroup` api methods.
<LiveExample framework='react' id="dockview/dnd-external" />

View File

@ -4,11 +4,6 @@ sidebar_position: 0
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import { DocRef } from '@site/src/components/ui/reference/docRef';
Dockview supports a wide variety of built-in Drag and Drop possibilities.

View File

@ -9,4 +9,8 @@ import LiveExample from '@site/src/components/ui/exampleFrame';
This section describes how to lock the dock to prevent movement.
:::info
You may want to combine this with `disableDnd={true}` to provide a locked grid with no dnd funtionality. See [Disable Dnd](/docs/core/dnd/disable) for more.
:::
<LiveExample framework='react' id='dockview/locked'/>

View File

@ -3,5 +3,15 @@ title: Overview
sidebar_position: 0
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
The implementation of the dock is a collection of nested *splitview* controls forming a *gridview*
which is exposed as a seperate component to be used independantly.
<DocRef declaration="IGridviewReactProps"/>
## Live Example
<LiveExample framework='react' id='gridview/simple' />

View File

@ -2,5 +2,13 @@
title: Overview
sidebar_position: 0
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
A *splitview* control where each panel contains a header and collapsable content.
<DocRef declaration="IPaneviewReactProps"/>
## Live Example
<LiveExample framework='react' id='paneview/simple' />

View File

@ -5,3 +5,12 @@ sidebar_position: 0
The implementation of the dock is a collection of nested *splitview* controls
which is exposed as a seperate component to be used independantly.
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="ISplitviewReactProps"/>
## Live Example
<LiveExample framework='react' id='splitview/simple' height={200} />

View File

@ -0,0 +1,10 @@
---
title: Tabview
sidebar_position: 3
---
A *tabview* can be created using a dock and preventing some default behaviours.
import LiveExample from '@site/src/components/ui/exampleFrame';
<LiveExample framework='react' id='dockview/tabview' />

View File

@ -35,6 +35,7 @@
align-items: center;
justify-content: center;
background-color: #1c254a;
color: white;
border: none;
cursor: pointer;
outline: 1px solid #4c65d4;

View File

@ -28,7 +28,7 @@ const components = {
position: 'relative',
}}
>
<Table data={metadata} />
{/* <Table data={metadata} /> */}
<span
style={{
position: 'absolute',
@ -68,8 +68,20 @@ const headerComponents = {
},
};
const colors = [
'rgba(255,0,0,0.2)',
'rgba(0,255,0,0.2)',
'rgba(0,0,255,0.2)',
'rgba(255,255,0,0.2)',
'rgba(0,255,255,0.2)',
'rgba(255,0,255,0.2)',
];
let count = 0;
const DockviewDemo = (props: { theme?: string }) => {
const [logLines, setLogLines] = React.useState<any[]>([]);
const [logLines, setLogLines] = React.useState<
{ text: string; timestamp?: Date; backgroundColor?: string }[]
>([]);
const [panels, setPanels] = React.useState<string[]>([]);
const [groups, setGroups] = React.useState<string[]>([]);
@ -78,16 +90,39 @@ const DockviewDemo = (props: { theme?: string }) => {
const [activePanel, setActivePanel] = React.useState<string>();
const [activeGroup, setActiveGroup] = React.useState<string>();
const [pending, setPending] = React.useState<
{ text: string; timestamp?: Date }[]
>([]);
const addLogLine = (message: string) => {
setPending((line) => [
{ text: message, timestamp: new Date() },
...line,
]);
};
React.useLayoutEffect(() => {
if (pending.length === 0) {
return;
}
const color = colors[count++ % colors.length];
setLogLines((lines) => [
...pending.map((_) => ({ ..._, backgroundColor: color })),
...lines,
]);
setPending([]);
}, [pending]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
event.api.onDidAddPanel((event) => {
setPanels((_) => [..._, event.id]);
setLogLines((line) => [`Panel Added ${event.id}`, ...line]);
addLogLine(`Panel Added ${event.id}`);
});
event.api.onDidActivePanelChange((event) => {
setActivePanel(event?.id);
setLogLines((line) => [`Panel Activated ${event?.id}`, ...line]);
addLogLine(`Panel Activated ${event?.id}`);
});
event.api.onDidRemovePanel((event) => {
setPanels((_) => {
@ -99,12 +134,12 @@ const DockviewDemo = (props: { theme?: string }) => {
return next;
});
setLogLines((line) => [`Panel Removed ${event.id}`, ...line]);
addLogLine(`Panel Removed ${event.id}`);
});
event.api.onDidAddGroup((event) => {
setGroups((_) => [..._, event.id]);
setLogLines((line) => [`Group Added ${event.id}`, ...line]);
addLogLine(`Group Added ${event.id}`);
});
event.api.onDidRemoveGroup((event) => {
@ -117,12 +152,12 @@ const DockviewDemo = (props: { theme?: string }) => {
return next;
});
setLogLines((line) => [`Group Removed ${event.id}`, ...line]);
addLogLine(`Group Removed ${event.id}`);
});
event.api.onDidActiveGroupChange((event) => {
setActiveGroup(event?.id);
setLogLines((line) => [`Group Activated ${event?.id}`, ...line]);
addLogLine(`Group Activated ${event?.id}`);
});
const state = localStorage.getItem('dv-demo-state');
@ -146,6 +181,9 @@ const DockviewDemo = (props: { theme?: string }) => {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
padding: '8px',
backgroundColor: 'rgba(0,0,50,0.25)',
borderRadius: '8px',
}}
>
<div>
@ -167,6 +205,7 @@ const DockviewDemo = (props: { theme?: string }) => {
overflow: 'hidden',
// flexBasis: 0
height: 0,
display: 'flex',
}}
>
<DockviewReact
@ -178,10 +217,10 @@ const DockviewDemo = (props: { theme?: string }) => {
onReady={onReady}
className={props.theme || 'dockview-theme-abyss'}
/>
</div>
<div
style={{
height: '200px',
// height: '200px',
width: '300px',
backgroundColor: 'black',
color: 'white',
overflow: 'auto',
@ -189,24 +228,53 @@ const DockviewDemo = (props: { theme?: string }) => {
>
{logLines.map((line, i) => {
return (
<div key={i}>
<div
style={{
height: '30px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: '13px',
display: 'flex',
alignItems: 'center',
backgroundColor: line.backgroundColor,
}}
key={i}
>
<span
style={{
display: 'inline-block',
width: '30px',
width: '20px',
color: 'gray',
borderRight: '1px solid gray',
marginRight: '4px',
paddingLeft: '2px',
height: '100%',
}}
>
{logLines.length - i}
</span>
<span>{line}</span>
<span>
{line.timestamp && (
<span
style={{
fontSize: '0.7em',
padding: '0px 2px',
}}
>
{line.timestamp
.toISOString()
.substring(11, 23)}
</span>
)}
<span>{line.text}</span>
</span>
</div>
);
})}
</div>
</div>
</div>
);
};

View File

@ -11,7 +11,7 @@ export const nextId = (() => {
export function defaultConfig(api: DockviewApi) {
const panel1 = api.addPanel({
id: 'panel_1',
component: 'iframe',
component: 'default',
renderer: 'always',
title: 'Panel 1',
});

View File

@ -0,0 +1,32 @@
{
"name": "dockview.dnd-external",
"description": "",
"keywords": [
"dockview"
],
"version": "1.0.0",
"main": "src/index.tsx",
"dependencies": {
"dockview": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"typescript": "^4.9.5",
"react-scripts": "*"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@ -1,7 +1,7 @@
import {
DockviewApi,
DockviewDndOverlayEvent,
DockviewDropEvent,
DockviewDidDropEvent,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
@ -123,7 +123,7 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
setApi(event.api);
};
const onDidDrop = (event: DockviewDropEvent) => {
const onDidDrop = (event: DockviewDidDropEvent) => {
event.api.addPanel({
id: 'test',
component: 'default',

View File

@ -1,5 +1,5 @@
{
"name": "dnd-dockview",
"name": "dockview.tabview",
"description": "",
"keywords": [
"dockview"

View File

@ -0,0 +1,82 @@
import {
DockviewApi,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
const Default = (props: IDockviewPanelProps) => {
return (
<div style={{ height: '100%' }}>
<div>{props.api.title}</div>
</div>
);
};
const components = {
default: Default,
};
const Component = (props: { theme?: string }) => {
const [api, setApi] = React.useState<DockviewApi>();
React.useEffect(() => {
if (!api) {
return;
}
const disposables = [
api.onWillShowOverlay((e) => {
if (e.kind === 'header_space' || e.kind === 'tab') {
return;
}
e.preventDefault();
}),
];
return () => {
disposables.forEach((disposable) => {
disposable.dispose();
});
};
}, [api]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
});
event.api.addPanel({
id: 'panel_5',
component: 'default',
});
};
return (
<DockviewReact
className={`${props.theme || 'dockview-theme-abyss'}`}
onReady={onReady}
components={components}
disableFloatingGroups={true}
/>
);
};
export default Component;

View File

@ -1,5 +1,5 @@
{
"name": "simple-gridview",
"name": "gridview.simple",
"description": "",
"keywords": [
"dockview"

View File

@ -1,5 +1,5 @@
{
"name": "simple-paneview",
"name": "paneview.simple",
"description": "",
"keywords": [
"dockview"

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,20 @@
import { StrictMode } from 'react';
import * as ReactDOMClient from 'react-dom/client';
import './styles.css';
import 'dockview/dist/styles/dockview.css';
import App from './app';
const rootElement = document.getElementById('root');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<StrictMode>
<div className="app">
<App />
</div>
</StrictMode>
);
}

View File

@ -0,0 +1,16 @@
body {
margin: 0px;
color: white;
font-family: sans-serif;
text-align: center;
}
#root {
height: 100vh;
width: 100vw;
}
.app {
height: 100%;
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}

View File

@ -0,0 +1,32 @@
{
"name": "splitview.simple",
"description": "",
"keywords": [
"dockview"
],
"version": "1.0.0",
"main": "src/index.tsx",
"dependencies": {
"dockview": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"typescript": "^4.9.5",
"react-scripts": "*"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,59 @@
import {
SplitviewReact,
SplitviewReadyEvent,
ISplitviewPanelProps,
} from 'dockview';
import * as React from 'react';
const components = {
default: (props: ISplitviewPanelProps<{ title: string }>) => {
return (
<div
style={{
padding: '10px',
height: '100%',
}}
>
{props.params.title}
</div>
);
},
};
export const App: React.FC = (props: { theme?: string }) => {
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}
className={props.theme || 'dockview-theme-abyss'}
/>
);
};
export default App;

View File

@ -0,0 +1,20 @@
import { StrictMode } from 'react';
import * as ReactDOMClient from 'react-dom/client';
import './styles.css';
import 'dockview/dist/styles/dockview.css';
import App from './app';
const rootElement = document.getElementById('root');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<StrictMode>
<div className="app">
<App />
</div>
</StrictMode>
);
}

View File

@ -0,0 +1,16 @@
body {
margin: 0px;
color: white;
font-family: sans-serif;
text-align: center;
}
#root {
height: 100vh;
width: 100vw;
}
.app {
height: 100%;
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}

View File

@ -1,6 +1,7 @@
import BrowserOnly from '@docusaurus/BrowserOnly';
import { DockviewEmitter } from 'dockview';
import * as React from 'react';
import { IS_PROD } from '../flags';
const frameworks = [
{ value: 'JavaScript', label: 'JavaScript' },
@ -35,7 +36,7 @@ function useActiveFramework(): [string, (value: string) => void] {
activeFrameworkGlobal.fire(value);
}, []);
return [value, setter];
return [IS_PROD ? frameworks[1].value : value, setter];
}
const FrameworkSelector1 = () => {
@ -45,6 +46,10 @@ const FrameworkSelector1 = () => {
setActiveFramework(event.target.value),
];
if (IS_PROD) {
return null;
}
return (
<select onChange={onChange} value={activeFramework}>
{frameworks.map((framework) => {

View File

@ -8,6 +8,7 @@ export interface DocRefProps {
}
import docsJson_ from '../../../generated/api.output.json';
import { ExportedTypeFile, TypeSystem, codify, firstLevel } from './types';
const docsJson = docsJson_ as any as DocsJson;
type DocsContent = { kind: string; text: string; tag?: string };
@ -16,19 +17,57 @@ type DocsComment = {
summary?: DocsContent[];
blockTags?: DocsTag[];
};
type Piece = {
kind: 'return' | 'paramter' | 'signature' | 'typearg' | 'typearg_default';
value: string;
};
type Doc = {
name: string;
code: string;
comment?: DocsComment;
kind: 'accessor' | 'property' | 'method';
pieces: string[];
pieces: Piece[];
};
type DocsJson = {
[index: string]: {
type DocJson = {
kind: string;
metadata?: Doc;
children: Doc[];
};
type DocsJson = {
[index: string]: DocJson;
};
const newJson = docsJson_ as ExportedTypeFile;
export const DocumentRef = (props: { value: TypeSystem.Type }) => {
const code = React.useMemo(() => {
if (!props.value) {
return null;
}
switch (props.value.kind) {
case 'typeAlias':
return codify(props.value);
case 'interface':
return codify(props.value);
case 'class':
return codify(props.value);
case 'function':
return codify(props.value);
default:
return null;
}
}, [props.value]);
if (!code) {
return null;
}
return <CodeBlock language="tsx">{code}</CodeBlock>;
};
export const Text = (props: { content: DocsContent[] }) => {
@ -81,25 +120,11 @@ export const Markdown = (props: { children: string }) => {
return <span>{props.children}</span>;
};
const Piece = (props: { piece: string }) => {
const item = docsJson[props.piece];
if (!item) {
return;
}
if (item.kind === 'interface') {
return;
}
if (!item.metadata?.code) {
return;
}
return <CodeBlock language="tsx">{item.metadata.code}</CodeBlock>;
};
const Row = (props: { doc: Doc }) => {
const Row = (props: { doc: TypeSystem.Type }) => {
const comment =
props.doc.kind === 'accessor'
? props.doc.value.comment
: props.doc.comment;
return (
<tr>
<th
@ -151,12 +176,8 @@ const Row = (props: { doc: Doc }) => {
<th style={{ width: '60%' }}>
{/* <div>{'-'}</div> */}
<div>
<div>
{props.doc.comment && (
<Summary summary={props.doc.comment} />
)}
</div>
<CodeBlock language="tsx">{props.doc.code}</CodeBlock>
<div>{comment && <Summary summary={comment} />}</div>
<CodeBlock language="tsx">{codify(props.doc)}</CodeBlock>
</div>
</th>
</tr>
@ -165,18 +186,23 @@ const Row = (props: { doc: Doc }) => {
export const DocRef = (props: DocRefProps) => {
const docs = React.useMemo(
() => (docsJson as DocsJson)[props.declaration],
() => newJson[props.declaration],
[props.declaration]
);
const filteredDocs = React.useMemo(
() =>
docs?.children?.filter((child) => {
if (props.methods && !props.methods.includes(child.name)) {
docs.kind === 'class' || docs.kind === 'interface'
? docs.children.filter((child) => {
if (
props.methods &&
!props.methods.includes(child.name)
) {
return false;
}
return true;
}),
})
: [],
[docs]
);
@ -191,6 +217,13 @@ export const DocRef = (props: DocRefProps) => {
return (
<>
<Row key={i} doc={doc} />
{/* <th colSpan={2}>
{firstLevel(doc).map((x) => (
<span style={{ padding: '0px 2px' }}>
<DocumentRef value={newJson[x]} />
</span>
))}
</th> */}
{/* {doc.pieces?.map((piece) => (
<tr>
<th colSpan={2}>

View File

@ -0,0 +1,446 @@
export type ExportedTypeFile = Record<string, TypeSystem.Type>;
export function firstLevelTypes(value: TypeDescriptor.Type | null) {
if (!value) {
return null;
}
switch (value.type) {
case 'array':
return firstLevelTypes(value.value);
case 'literal':
return [];
case 'intrinsic':
return [];
case 'or':
return value.values.flatMap(firstLevelTypes);
case 'intersection':
return value.values.flatMap(firstLevelTypes);
case 'predicate':
return [];
case 'reference': {
const result = [];
if (
value.source.startsWith('dockview') &&
!value.refersToTypeParameter
) {
result.push(value.value);
}
if (value.typeArguments) {
result.push(...value.typeArguments.flatMap(firstLevelTypes));
}
return result;
}
case 'reflection':
return firstLevel(value.value);
case 'tuple':
return value.value.map(codifyType);
default:
throw new Error('unreachable');
}
}
export function firstLevel(value: TypeSystem.Type | null) {
const results: string[] = [];
switch (value.kind) {
case null:
break;
case 'property':
results.push(...firstLevelTypes(value.type));
break;
case 'accessor':
results.push(...firstLevelTypes(value.value.returnType));
break;
case 'method':
results.push(...value.signature.flatMap(firstLevel));
break;
case 'constructor':
break;
case 'typeLiteral':
if (value.properties) {
results.push(...value.properties.flatMap(firstLevel));
}
if (value.signatures) {
results.push(...value.signatures.flatMap(firstLevel));
}
break;
case 'callSignature':
results.push(
...firstLevelTypes(value.returnType),
...value.typeParameters.flatMap((_) => {
return [...firstLevelTypes(_.extends)];
}),
...value.parameters.flatMap(firstLevel)
);
break;
case 'parameter':
results.push(...firstLevelTypes(value.type));
break;
default:
console.log('test', value);
throw new Error('unreachable');
}
return Array.from(new Set(results));
}
export function codifyType(value: TypeDescriptor.Type | null, tabs = 0) {
if (!value) {
return null;
}
switch (value.type) {
case 'array':
return `${codifyType(value.value)}[]`;
case 'literal':
return `'${value.value}'`;
case 'intrinsic':
return value.value;
case 'or':
return `${value.values
.map((_) => {
const isComparator =
_.type === 'or' || _.type === 'intersection';
const code = codifyType(_);
return isComparator ? `(${code})` : code;
})
.join(' | ')}`;
case 'intersection':
return `${value.values
.map((_) => {
const isComparator =
_.type === 'or' || _.type === 'intersection';
const code = codifyType(_);
return isComparator ? `(${code})` : code;
})
.join(' & ')}`;
case 'predicate':
return `${value.lhs} is ${value.rhs}`;
case 'reference': {
if (value.typeArguments) {
return `${value.value}<${value.typeArguments.map(codifyType)}>`;
}
return `${value.value}`;
}
case 'reflection':
return codify(value.value, tabs);
case 'tuple':
return `[${value.value.map(codifyType).join(', ')}]`;
default:
throw new Error('unreachable');
}
}
export function codify(value: TypeSystem.Type | null, tabs = 0) {
if (!value) {
return null;
}
if (value.kind === 'accessor') {
const signature = value.value;
return `${'\t'.repeat(tabs)}${signature.name}: ${codifyType(
signature.returnType
)}`;
}
if (value.kind === 'property') {
let code = '\t'.repeat(tabs);
if (value.flags.isProtected) {
code += 'protected ';
}
if (value.flags.isReadonly) {
code += 'readonly ';
}
code += value.name;
if (value.flags.isOptional) {
code += '?';
}
code += `: ${codifyType(value.type, tabs + 1)}`;
return code;
}
if (value.kind === 'method') {
return `${'\t'.repeat(tabs)}${value.name}${value.signature
.map(codify)
.join('\n')}`;
}
if (value.kind === 'callSignature') {
let code = ``;
if (value.typeParameters.length > 0) {
code += '<';
code += value.typeParameters.map((typeParameter) => {
let typeCode = `${typeParameter.name}`;
if (typeParameter.extends) {
typeCode += ' extends';
typeCode += ` ${codifyType(typeParameter.extends)}`;
}
if (typeParameter.default) {
typeCode += ' =';
typeCode += ` ${typeParameter.default}`;
}
return typeCode;
});
code += '>';
}
code += '(';
code += value.parameters
.map((parameter) => {
return codify(parameter, tabs + 1);
})
.join(', ');
code += `): ${codifyType(value.returnType)}`;
return code;
}
if (value.kind === 'parameter') {
return `${value.name}: ${codifyType(value.type, tabs + 1)}`;
}
if (value.kind === 'typeLiteral') {
if (value.properties) {
return `{\n${value.properties
.map((_) => codify(_, tabs))
.join(',\n')}\n${'\t'.repeat(Math.max(0, tabs - 1))}}`;
}
if (value.signatures) {
return value.signatures.map(codify).join('\n');
}
}
if (value.kind === 'constructor') {
return '';
}
if (value.kind === 'interface') {
return `interface ${value.name} {\n${value.children
.map((_) => codify(_, tabs + 1))
.join(';\n')};\n}`;
}
if (value.kind === 'class') {
return `interface ${value.name} {\n${value.children
.filter((_) => _.kind !== 'constructor')
.map((_) => codify(_, tabs + 1))
.join(';\n')};\n}`;
}
if (value.kind === 'typeAlias') {
return `type ${value.name} = ${codifyType(value.type)}`;
}
console.log('unreachable', value);
throw new Error(`unreachable`);
}
export namespace TypeSystem {
export type Comment = any;
export type TypeParameter = {
name: string;
extends: TypeDescriptor.Type;
default: string;
comment?: Comment;
};
export type Accessor = {
name: string;
kind: 'accessor';
comment?: Comment;
value: TypeSystem.GetSignature;
};
export type GetSignature = {
kind: 'getSignature';
name: string;
returnType: TypeDescriptor.Type;
comment?: Comment;
};
export type CallSignature = {
kind: 'callSignature';
typeParameters: TypeSystem.TypeParameter[];
parameters: TypeSystem.Type[];
returnType: TypeDescriptor.Type;
name: string;
comment?: Comment;
};
export type Method = {
name: string;
kind: 'method';
signature: TypeSystem.CallSignature[];
comment?: Comment;
};
export type Function = {
name: string;
kind: 'function';
signature: TypeSystem.CallSignature;
comment?: Comment;
};
export type Property = {
kind: 'property';
name: string;
type: TypeDescriptor.Type;
flags: TypeDescriptor.Flags;
comment?: Comment;
};
export type TypeAlias = {
name: string;
kind: 'typeAlias';
typeParameters: TypeSystem.TypeParameter[];
type: TypeDescriptor.Type;
comment?: Comment;
};
export type Enum = {
name: string;
kind: 'enum';
children: TypeSystem.EnumMember[];
comment?: Comment;
};
export type EnumMember = {
kind: 'enumMember';
name: string;
comment?: Comment;
};
export type Class = {
name: string;
kind: 'class';
children: TypeSystem.Type[];
comment?: Comment;
};
export type Interface = {
name: string;
kind: 'interface';
children: TypeSystem.Type[];
comment?: Comment;
};
export type Parameter = {
name: string;
kind: 'parameter';
type: TypeDescriptor.Type;
comment?: Comment;
};
export type Constructor = {
kind: 'constructor';
name: string;
comment?: Comment;
};
export type ConstructorSignature = {
kind: 'constructorSignature';
name: string;
comment?: Comment;
};
export type TypeLiteral = {
kind: 'typeLiteral';
name: string;
signatures?: (ConstructorSignature | TypeSystem.CallSignature)[];
properties?: TypeSystem.Property[];
comment?: Comment;
};
export type Type =
| TypeSystem.Accessor
| TypeSystem.GetSignature
| TypeSystem.CallSignature
| TypeSystem.Method
| TypeSystem.Property
| TypeSystem.TypeAlias
| TypeSystem.Enum
| TypeSystem.EnumMember
| TypeSystem.Class
| TypeSystem.Constructor
| TypeSystem.ConstructorSignature
| TypeSystem.TypeLiteral
| TypeSystem.Parameter
| TypeSystem.Interface
| TypeSystem.Function;
}
export namespace TypeDescriptor {
export interface Union {
type: 'or';
values: TypeDescriptor.Type[];
}
export interface Intrinsic {
type: 'intrinsic';
value: string;
}
export interface Literal {
type: 'literal';
value: string;
}
export type Reflection = { type: 'reflection'; value: TypeSystem.Type };
export interface Reference {
type: 'reference';
value: string;
source: string;
typeArguments?: TypeDescriptor.Type[];
refersToTypeParameter?: boolean;
}
export interface Array {
type: 'array';
value: TypeDescriptor.Type;
}
export interface Intersection {
type: 'intersection';
values: TypeDescriptor.Type[];
}
export interface Predicate {
type: 'predicate';
lhs: string;
rhs: TypeDescriptor.Type;
}
export interface Tuple {
type: 'tuple';
value: TypeDescriptor.Type[];
}
export type Type =
| TypeDescriptor.Union
| TypeDescriptor.Intrinsic
| TypeDescriptor.Literal
| TypeDescriptor.Reflection
| TypeDescriptor.Reference
| TypeDescriptor.Array
| TypeDescriptor.Intersection
| TypeDescriptor.Predicate
| TypeDescriptor.Tuple;
export type Flags = {
isReadonly?: boolean;
isProtected?: boolean;
isOptional?: boolean;
};
}

View File

@ -154,6 +154,7 @@
}
code {
// white-space: pre-wrap;
.token {
&.maybe-class-name {
color: #cf8cff;

View File

@ -0,0 +1 @@
export const IS_PROD = true;

File diff suppressed because it is too large Load Diff

View File

@ -2,23 +2,15 @@ import React from 'react';
import Layout from '@theme/Layout';
import { themeConfig } from '../config/theme.config';
import ExampleFrame from '../components/ui/exampleFrame';
import BrowserOnly from '@docusaurus/BrowserOnly';
export default function Popout() {
// const [theme, setTheme] = React.useState<string>(
// new URLSearchParams(location.search).get('theme') ?? themeConfig[3].id
// );
const ThemeToggle: React.FC = () => {
const [theme, setTheme] = React.useState<string>(
new URLSearchParams(location.search).get('theme') ?? themeConfig[3].id
);
return (
<Layout noFooter={true}>
<div
style={{
height: 'calc(100% - var(--ifm-navbar-height))',
flexGrow: 1,
padding: '10px',
display: 'flex',
flexDirection: 'column',
}}
>
<>
<div
style={{
height: '40px',
@ -32,20 +24,36 @@ export default function Popout() {
url.searchParams.set('theme', event.target.value);
window.location.href = url.toString();
}}
// value={theme}
value={theme}
>
{themeConfig.map((theme) => {
return <option>{theme.id}</option>;
})}
</select>
</div>
<ExampleFrame
// theme={theme}
theme={theme}
framework="react"
height="100%"
id="dockview/demo-dockview"
/>
</>
);
};
export default function Popout() {
return (
<Layout noFooter={true}>
<div
style={{
height: 'calc(100% - var(--ifm-navbar-height))',
flexGrow: 1,
padding: '10px',
display: 'flex',
flexDirection: 'column',
}}
>
<BrowserOnly>{() => <ThemeToggle />}</BrowserOnly>
</div>
</Layout>
);

View File

@ -105,9 +105,89 @@ function parseType(obj) {
}
}
function parseComplexType(obj) {
switch (obj.type) {
case 'union':
return {
type: 'or',
values: obj.types.map(parseComplexType).reverse(),
};
case 'intrinsic':
return { type: obj.type, value: obj.name };
case 'literal':
return { type: obj.type, value: obj.value };
case 'reflection':
return { type: obj.type, value: parse(obj.declaration) };
case 'reference': {
if (obj.refersToTypeParameter) {
return {
type: obj.type,
value: obj.name,
source: obj.package,
refersToTypeParameter: true,
};
}
if (obj.qualifiedName) {
return {
type: obj.type,
value: obj.qualifiedName,
source: obj.sourceFileName
? obj.sourceFileName.startsWith('packages/dockview')
? 'dockview'
: 'external'
: obj.package,
typeArguments: obj.typeArguments?.map(parseComplexType),
};
}
return {
type: obj.type,
value: obj.name,
source: obj.package,
typeArguments: obj.typeArguments?.map(parseComplexType),
};
}
case 'array':
return {
type: obj.type,
value: parseComplexType(obj.elementType),
};
case 'intersection':
return {
type: obj.type,
values: obj.types.map(parseComplexType).reverse(),
};
case 'predicate':
return {
type: obj.type,
lhs: obj.name,
rhs: parseComplexType(obj.targetType),
};
case 'tuple':
return {
type: obj.type,
values: obj.elements.map(parseComplexType),
};
default:
throw new Error(`unhandled type ${obj.type}`);
}
}
function extractPiecesFromType(obj) {
if (obj.type === 'reference' && obj.package?.startsWith('dockview-')) {
return obj.name;
let result = { name: obj.name };
if (Array.isArray(obj.typeArguments)) {
const typeArgs = obj.typeArguments
.map(extractPiecesFromType)
.filter(Boolean);
if (typeArgs.length > 0) {
result.typeArgs = typeArgs;
}
}
return result;
}
return null;
}
@ -126,14 +206,15 @@ function parse(data) {
const getSignature = parse(data.getSignature);
code += getSignature.code;
pieces.push(...getSignature.pieces);
// pieces.push(...getSignature.pieces);
return {
name,
code,
kind: 'accessor',
value: getSignature,
comment: getSignature.comment,
pieces,
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Method: // 2048
const methodSignature = data.signatures.map((signature) =>
@ -149,20 +230,25 @@ function parse(data) {
name,
code,
kind: 'method',
signature: methodSignature,
comment: data.signatures[0].comment,
pieces,
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Property: // 1024
code += parseType(data.type);
pieces.push(extractPiecesFromType(data.type));
pieces.push({
kind: 'property',
value: extractPiecesFromType(data.type),
});
return {
name,
code,
kind: 'property',
type: parseComplexType(data.type),
flags,
comment,
pieces,
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Parameter: // 32768
@ -174,14 +260,21 @@ function parse(data) {
code += ': ';
code += parseType(data.type);
pieces.push(extractPiecesFromType(data.type));
pieces.push({
kind: 'parameter',
value: extractPiecesFromType(data.type),
});
return {
name,
code,
pieces,
type: parseComplexType(data.type),
kind: 'parameter',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.TypeLiteral: // 65536
let result = {};
if (Array.isArray(data.children)) {
code += `{ `;
code += data.children
@ -192,46 +285,91 @@ function parse(data) {
code += '?';
}
const childData = parse(child);
pieces.push(...childData.pieces);
// pieces.push(...childData.pieces);
code += `: ${childData.code}`;
return code;
})
.join(', ');
code += ` }`;
result.properties = data.children.map((_) => {
const result = parse(_);
if (result.kind !== 'property') {
throw new Error(`invalid ${result.kind}`);
}
return result;
});
}
if (Array.isArray(data.signatures)) {
const signatures = data.signatures.map((signature) =>
parse(signature)
);
const signatures = data.signatures.map((signature) => {
const result = parse(signature);
if (
result.kind !== 'callSignature' &&
result.kind !== 'constructorSignature'
) {
throw new Error(`invalid ${result.kind}`);
}
return result;
});
code += signatures
.map((signature) => signature.code)
.join(', ');
pieces.push(...signatures.flatMap((_) => _.pieces));
// pieces.push(...signatures.flatMap((_) => _.pieces));
if (result.type) {
throw new Error('anc');
}
return { name, code, pieces };
result.signatures = signatures;
}
return {
name,
code,
// pieces,
kind: 'typeLiteral',
properties: result.properties,
signatures: result.signatures,
};
case ReflectionKind.CallSignature: // 4096
// don't care for constrcutors
const typeParameters = [];
let _parameters = [];
if (Array.isArray(data.typeParameter)) {
code += `<${data.typeParameter.map((typeParameter) => {
let type = `${typeParameter.name}`;
const result = { name: type };
if (typeParameter.type) {
type += ` extends ${parseType(typeParameter.type)}`;
pieces.push(extractPiecesFromType(typeParameter.type));
result.extends = parseComplexType(typeParameter.type);
pieces.push({
kind: 'typearg',
value: extractPiecesFromType(typeParameter.type),
});
}
if (typeParameter.default) {
type += ` = ${typeParameter.default.name}`;
pieces.push(
extractPiecesFromType(typeParameter.default)
);
pieces.push({
kind: 'typearg_default',
value: extractPiecesFromType(typeParameter.default),
});
result.default = typeParameter.default.name;
}
typeParameters.push(result);
return type;
})}>`;
}
@ -242,113 +380,155 @@ function parse(data) {
const parameters = data.parameters.map((parameter) =>
parse(parameter)
);
_parameters = parameters;
code += `${parameters
.map((parameter) => parameter.code)
.join(', ')}`;
pieces.push(...parameters.flatMap((_) => _.pieces));
// pieces.push(...parameters.flatMap((_) => _.pieces));
}
code += '): ';
code += parseType(data.type);
pieces.push(extractPiecesFromType(data.type));
pieces.push({
kind: 'return',
value: extractPiecesFromType(data.type),
});
return {
name,
comment,
typeParameters,
parameters: _parameters,
returnType: parseComplexType(data.type),
code,
pieces,
kind: 'callSignature',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.GetSignature: // 524288
code += parseType(data.type);
pieces.push(extractPiecesFromType(data.type));
pieces.push({
kind: 'signature',
value: extractPiecesFromType(data.type),
});
return {
name,
comment,
code,
pieces,
kind: 'getSignature',
returnType: parseComplexType(data.type),
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Function: // 64
if (data.signatures.length > 1) {
throw new Error('unhandled');
}
const functionSignature = parse(data.signatures[0]);
pieces.push(...functionSignature.pieces);
// pieces.push(...functionSignature.pieces);
code += functionSignature.code;
return {
name,
comment,
code,
pieces,
signature: parse(data.signatures[0]),
kind: 'function',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Variable: // 32
return {
name,
comment,
code,
pieces,
kind: 'variable',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.EnumMember: // 16
return {
name,
comment,
code,
pieces,
kind: 'enumMember',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Interface: // 16
return {
name,
comment,
code,
pieces,
kind: 'interface',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.ConstructorSignature: // 16384
return {
name,
comment,
code,
pieces,
kind: 'constructorSignature',
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.Constructor: // 512
// don't care for constrcutors
return {
name,
comment,
kind: 'constructor',
code,
pieces,
// pieces: pieces.filter((_) => _.value !== null),
};
case ReflectionKind.TypeAlias: // 2097152
const typeParameters1 = [];
if (Array.isArray(data.typeParameter)) {
code += `<${data.typeParameter.map((typeParameter) => {
let type = `${typeParameter.name}`;
const result = { name: typeParameter.name };
if (typeParameter.type) {
type += ` extends ${parseType(typeParameter.type)}`;
pieces.push(extractPiecesFromType(typeParameter.type));
result.extends = parseComplexType(typeParameter.type);
pieces.push({
kind: 'typearg',
value: extractPiecesFromType(typeParameter.type),
});
}
if (typeParameter.default) {
type += ` = ${typeParameter.default.name}`;
pieces.push(
extractPiecesFromType(typeParameter.default)
);
pieces.push({
kind: 'typearg_default',
value: extractPiecesFromType(typeParameter.default),
});
result.default = typeParameter.default.name;
}
typeParameters1.push(result);
return type;
})}>`;
}
code += parseType(data.type);
pieces.push(extractPiecesFromType(data.type));
pieces.push({
kind: 'typearg',
value: extractPiecesFromType(data.type),
});
return {
name,
comment,
code,
pieces,
typeParameters: typeParameters1,
type: parseComplexType(data.type),
kind: 'typeAlias',
// pieces: pieces.filter((_) => _.value !== null),
};
default:
throw new Error(`unhandled kind ${data.kind}`);
@ -395,11 +575,15 @@ function createDocument(declarations) {
documentation[name] = {
...metadata,
name,
children: [],
};
if (!children) {
documentation[name].metadata = parse(declaration);
documentation[name] = {
...parse(declaration),
};
// documentation[name].metadata = parse(declaration);
}
if (children) {
@ -417,6 +601,8 @@ function createDocument(declarations) {
output.pieces = Array.from(new Set(output.pieces))
.filter(Boolean)
.sort();
delete output.pieces;
// delete output.comment;
documentation[name].children.push(output);
}