diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 53e2d4776..f460a9448 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -20,8 +20,10 @@ "/packages/docs/sandboxes/keyboard-dockview", "/packages/docs/sandboxes/layout-dockview", "/packages/docs/sandboxes/lockedgroup-dockview", + "/packages/docs/sandboxes/maximizegroup-dockview", "/packages/docs/sandboxes/nativeapp-dockview", "/packages/docs/sandboxes/nested-dockview", + "/packages/docs/sandboxes/popoutgroup-dockview", "/packages/docs/sandboxes/rendering-dockview", "/packages/docs/sandboxes/rendermode-dockview", "/packages/docs/sandboxes/resize-dockview", diff --git a/README.md b/README.md index 0f26a8799..e2462eaec 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@

dockview

-

Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support written in TypeScript

+

Zero dependency layout manager supporting tabs, groups, grids and splitviews with ReactJS support written in TypeScript

--- [![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview) +[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview) [![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) @@ -21,16 +22,20 @@ Please see the website: https://dockview.dev ## Features -- Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with - dockable and tabular views -- Extensive API support at the component level and view level -- Themable and customizable -- Serialization / deserialization support -- Tabular docking and Drag and Drop support -- Floating groups, customized header bars and tab -- Documentation and examples +- Serialization / deserialization with full layout management +- Support for split-views, grid-views and 'dockable' views +- Themeable and customizable +- Tab and Group docking / Drag n' Drop +- Popout Windows +- Floating Groups +- Extensive API +- Supports Shadow DOMs +- High test coverage +- Documentation website with live examples +- Transparent builds and Code Analysis +- Security at mind - verifed publishing and builds through GitHub Actions -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ +Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance). ## Quick start diff --git a/packages/dockview-core/README.md b/packages/dockview-core/README.md index 980454e34..32179da7c 100644 --- a/packages/dockview-core/README.md +++ b/packages/dockview-core/README.md @@ -8,6 +8,7 @@ --- [![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview) +[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview) [![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) @@ -15,24 +16,30 @@ ## +![](packages/docs/static/img/splashscreen.gif) + Please see the website: https://dockview.dev ## Features -- Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with - dockable and tabular views -- Extensive API support at the component level and view level -- Themable and customizable -- Serialization / deserialization support -- Tabular docking and Drag and Drop support -- Floating groups, customized header bars and tab -- Documentation and examples +- Serialization / deserialization with full layout management +- Support for split-views, grid-views and 'dockable' views +- Themeable and customizable +- Tab and Group docking / Drag n' Drop +- Popout Windows +- Floating Groups +- Extensive API +- Supports Shadow DOMs +- High test coverage +- Documentation website with live examples +- Transparent builds and Code Analysis +- Security at mind - verifed publishing and builds through GitHub Actions -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview-core@latest/ +Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance). ## Quick start -You can install dockview-core from [npm](https://www.npmjs.com/package/dockview-core). +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-core). ``` npm install --save dockview-core diff --git a/packages/dockview-core/src/dom.ts b/packages/dockview-core/src/dom.ts index a58df3e06..c25b12450 100644 --- a/packages/dockview-core/src/dom.ts +++ b/packages/dockview-core/src/dom.ts @@ -240,7 +240,7 @@ export function getDomNodePagePosition(domNode: Element): { export function isInDocument(element: Element): boolean { let currentElement: Element | ParentNode = element; - while (currentElement && currentElement.parentNode) { + while (currentElement?.parentNode) { if (currentElement.parentNode === document) { return true; } else if (currentElement.parentNode instanceof DocumentFragment) { diff --git a/packages/dockview/README.md b/packages/dockview/README.md index b80c8c188..7a0dc6c1e 100644 --- a/packages/dockview/README.md +++ b/packages/dockview/README.md @@ -8,6 +8,7 @@ --- [![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview) +[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview) [![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview) @@ -19,16 +20,20 @@ Please see the website: https://dockview.dev ## Features -- Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with - dockable and tabular views -- Extensive API support at the component level and view level -- Themable and customizable -- Serialization / deserialization support -- Tabular docking and Drag and Drop support -- Floating groups, customized header bars and tab -- Documentation and examples +- Serialization / deserialization with full layout management +- Support for split-views, grid-views and 'dockable' views +- Themeable and customizable +- Tab and Group docking / Drag n' Drop +- Popout Windows +- Floating Groups +- Extensive API +- Supports Shadow DOMs +- High test coverage +- Documentation website with live examples +- Transparent builds and Code Analysis +- Security at mind - verifed publishing and builds through GitHub Actions -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ +Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance). ## Quick start diff --git a/packages/docs/blog/2024-01-31-dockview-1.9.0.md b/packages/docs/blog/2024-01-15-dockview-1.9.0.md similarity index 88% rename from packages/docs/blog/2024-01-31-dockview-1.9.0.md rename to packages/docs/blog/2024-01-15-dockview-1.9.0.md index 0f40e34cb..1cecdc982 100644 --- a/packages/docs/blog/2024-01-31-dockview-1.9.0.md +++ b/packages/docs/blog/2024-01-15-dockview-1.9.0.md @@ -16,6 +16,8 @@ Please reference to docs @ [dockview.dev](https://dockview.dev). - Advanced panel rendering configurations [#397](https://github.com/mathuo/dockview/issues/397) +- Support for rendering within Shadow DOM elements [#425](https://github.com/mathuo/dockview/issues/425) + ## 🛠 Miscs - Remove hover effect on inactive drag handles [#392](https://github.com/mathuo/dockview/pull/392) diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 35231ef41..0472c1e1a 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -29,6 +29,7 @@ import DockviewFloating from '@site/sandboxes/floatinggroup-dockview/src/app'; import DockviewLockedGroup from '@site/sandboxes/lockedgroup-dockview/src/app'; import DockviewKeyboard from '@site/sandboxes/keyboard-dockview/src/app'; import DockviewPopoutGroup from '@site/sandboxes/popoutgroup-dockview/src/app'; +import DockviewMaximizeGroup from '@site/sandboxes/maximizegroup-dockview/src/app'; import DockviewRenderMode from '@site/sandboxes/rendermode-dockview/src/app'; import { DocRef } from '@site/src/components/ui/reference/docRef'; @@ -403,7 +404,7 @@ To programatically move the popout group back into the main grid you can use the ```tsx // option 1: add absolutely to the right-side of the grid -props.group.api.moveTo({position: 'right'}); +props.group.api.moveTo({ position: 'right' }); // option 2: create a new group and move the contents of the popout group to it const group = props.containerApi.addGroup(); @@ -418,12 +419,44 @@ props.group.api.moveTo({ group }); ## Maximized Groups -To maximize a group you can all +To maximize a group from the component `api`: ```tsx +// maximize a specified group api.maxmimizeGroup(group); + +// check whether a specific group is maximized +const result: boolean = api.isMaximizedGroup(group); + +// if there is any maximized group exit the maximized state +exitMaximizedGroup(); + +// is there a maximized group +const result: boolean = hasMaximizedGroup(); ``` +The following methods are available on both the panel and group `api` objects: + +> `api.` is equivalent to `api.group.api.` for all of these methods. The methods exist on the panel `api` object for convenience. + +```ts +// maximize the group +api.maximize(); + +// is this group maximized (if another group is maximized this method will still return false) +const result: boolean = api.isMaxmized(); + +// exit only if this group is maximzied (if another group is maxmized this has no affect) +api.exitMaximized(); +``` + + + + ## Panels ### Add Panel @@ -667,9 +700,9 @@ Toggling the checkbox you can see that when you only render those panels which a You can provide custom renderers for your tab headers for maximum customization. A default implementation of `DockviewDefaultTab` is provided should you only wish to attach minor changes and events that do not alter the default behaviour, for example to add a custom context menu event -handler. +handler or to hide the close button. -The `DockviewDefaulTab` component accepts a `hideClose` prop if you wish only to hide the close button. +The `DockviewDefaultTab` component accepts a `hideClose` prop if you wish only to hide the close button. ```tsx title="Attaching a custom context menu event handlers to a custom header" import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview'; @@ -679,7 +712,7 @@ const MyCustomheader = (props: IDockviewPanelHeaderProps) => { event.preventDefault(); alert('context menu'); }; - return ; + return ; }; ``` @@ -900,16 +933,17 @@ iFrames required special attention because of a particular behaviour in how iFra You can find many examples of discussions on this. Two reputable forums for example are linked [here](https://bugzilla.mozilla.org/show_bug.cgi?id=254144) and [here](https://github.com/whatwg/html/issues/5484). -The problem with iFrames and `dockview` is that when you hide or move a panel that panels DOM element may be moved within the DOM or removed from the DOM completely. -If your panel contains an iFrame then that iFrame will reload after being re-positioned within the DOM tree and all state in that iFrame will most likely be lost. +To ensure iFrames work as expected you should render them in panels with `renderer: 'always'` to ensure they are never removed from the DOM, alternatively set the defaultRenderer to `always`. -`dockview` does not provide a built-in solution to this because it's too specific of a problem to include in the library. -However the below example does show an implementation of a higher-order component `HoistedDockviewPanel`that you could use to work around this problems and make iFrames behave in `dockview`. +> See the **Panel Rendering** section for more information of render modes. -What the higher-order component is doing is to hoist the panels contents into a DOM element that is always present and then `position: absolute` that element to match the dimensions of it's linked panel. -The visibility of these hoisted elements is then controlled through some exposed api methods to hide elements that shouldn't be currently shown. - -You should open this example in CodeSandbox using the provided link to understand the code and make use of this implemention if required. +```tsx title="Example of a panel using an alternative renderer" +api.addPanel({ + id: 'my_panel_id', + component: 'my_component', + renderer: 'always', +}); + ``` =18, Yarn + Dockview is a layout manager library designed to provide a complete layouting solution. It is written in plain TypeScript and can be used without any framework although an extensive React wrapper has always and will always be provided for those using the React framework. diff --git a/packages/docs/docs/index.mdx b/packages/docs/docs/index.mdx index 9ea7f0923..f167603ac 100644 --- a/packages/docs/docs/index.mdx +++ b/packages/docs/docs/index.mdx @@ -3,10 +3,12 @@ sidebar_position: 0 description: A zero dependency layout manager supporting ReactJS and Vanilla TypeScript --- +import { MultiFrameworkContainer } from '@site/src/components/ui/container'; + import { SimpleSplitview } from '@site/src/components/simpleSplitview'; import { SimpleGridview } from '@site/src/components/simpleGridview'; import { SimplePaneview } from '@site/src/components/simplePaneview'; -import SimpleDockview from '@site/sandboxes/simple-dockview/src/app'; +import DockviewDemo from '@site/sandboxes/demo-dockview/src/app'; import Link from '@docusaurus/Link'; # Introduction @@ -34,16 +36,11 @@ There are 4 components you may want to use:

Dockview

-
- -
+

Splitview

diff --git a/packages/docs/sandboxes/demo-dockview/src/app.tsx b/packages/docs/sandboxes/demo-dockview/src/app.tsx index 259bb70e4..58d0fd440 100644 --- a/packages/docs/sandboxes/demo-dockview/src/app.tsx +++ b/packages/docs/sandboxes/demo-dockview/src/app.tsx @@ -31,74 +31,13 @@ const headerComponents = { }, }; -const Popover = (props: { - children: React.ReactNode; - position?: { x: number; y: number }; - close: () => void; -}) => { - const uuid = React.useMemo(() => v4(), []); - - React.useEffect(() => { - const listener = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - props.close(); - } - }; - const listener2 = (event: MouseEvent) => { - let target = event.target; - - while (target) { - if (target instanceof HTMLElement) { - if (target.classList.contains(uuid)) { - return; - } else { - target = target.parentElement; - } - } else { - target = null; - } - } - - props.close(); - }; - window.addEventListener('keypress', listener); - window.addEventListener('mousedown', listener2); - - return () => { - window.removeEventListener('keypress', listener); - window.removeEventListener('mousedown', listener2); - }; - }, [props.close, uuid]); - - if (!props.position) { - return null; - } - - return ReactDOM.createPortal( -
- {props.children} -
, - document.body - ); -}; - const Icon = (props: { icon: string; + title?: string; onClick?: (event: React.MouseEvent) => void; }) => { return ( -
+
{ return groupControlsComponents[props.activePanel.id]; }, [props.isGroupActive, props.activePanel]); - // const [icon, setIcon] = React.useState( - // props.containerApi.hasMaximizedGroup() - // ? 'collapse_content' - // : 'expand_content' - // ); + const [isMaximized, setIsMaximized] = React.useState( + props.containerApi.hasMaximizedGroup() + ); - // const [popoutIcon, setPopoutIcon] = React.useState( - // props.api.location === 'popout' ? 'close_fullscreen' : 'open_in_new' - // ); + const [isPopout, setIsPopout] = React.useState( + props.api.location === 'popout' + ); - // React.useEffect(() => { - // const disposable = props.containerApi.onDidMaxmizedGroupChange(() => { - // setIcon( - // props.containerApi.hasMaximizedGroup() - // ? 'collapse_content' - // : 'expand_content' - // ); - // }); + React.useEffect(() => { + const disposable = props.containerApi.onDidMaxmizedGroupChange(() => { + setIsMaximized(props.containerApi.hasMaximizedGroup()); + }); - // const disposable2 = props.api.onDidLocationChange(() => { - // setPopoutIcon( - // props.api.location === 'popout' - // ? 'close_fullscreen' - // : 'open_in_new' - // ); - // }); + const disposable2 = props.api.onDidLocationChange(() => { + setIsPopout(props.api.location === 'popout'); + }); - // return () => { - // disposable.dispose(); - // disposable2.dispose(); - // }; - // }, [props.containerApi]); + return () => { + disposable.dispose(); + disposable2.dispose(); + }; + }, [props.containerApi]); - // const onClick = () => { - // if (props.containerApi.hasMaximizedGroup()) { - // props.containerApi.exitMaxmizedGroup(); - // } else { - // props.activePanel?.api.maximize(); - // } - // }; + const onClick = () => { + if (props.containerApi.hasMaximizedGroup()) { + props.containerApi.exitMaxmizedGroup(); + } else { + props.activePanel?.api.maximize(); + } + }; - // const onClick2 = () => { - // if (props.api.location !== 'popout') { - // props.containerApi.addPopoutGroup(props.group); - // } else { - // props.api.moveTo({ position: 'right' }); - // } - // }; + const onClick2 = () => { + if (props.api.location !== 'popout') { + props.containerApi.addPopoutGroup(props.group); + } else { + props.api.moveTo({ position: 'right' }); + } + }; return (
{ > {props.isGroupActive && } {Component && } - {/* - */} + + {!isPopout && ( + + )}
); }; @@ -241,45 +180,62 @@ const PrefixHeaderControls = (props: IDockviewHeaderActionsProps) => { const DockviewDemo = (props: { theme?: string }) => { const onReady = (event: DockviewReadyEvent) => { - event.api.addPanel({ + const panel1 = event.api.addPanel({ id: 'panel_1', component: 'default', title: 'Panel 1', }); + event.api.addPanel({ id: 'panel_2', component: 'default', title: 'Panel 2', - position: { referencePanel: 'panel_1', direction: 'right' }, + position: { referencePanel: panel1 }, }); + event.api.addPanel({ id: 'panel_3', component: 'default', title: 'Panel 3', - position: { referencePanel: 'panel_2', direction: 'below' }, + position: { referencePanel: panel1 }, }); - event.api.addPanel({ + + const panel4 = event.api.addPanel({ id: 'panel_4', component: 'default', title: 'Panel 4', - position: { referencePanel: 'panel_3', direction: 'right' }, + position: { referencePanel: panel1, direction: 'right' }, }); - event.api.addPanel({ + + const panel5 = event.api.addPanel({ id: 'panel_5', component: 'default', title: 'Panel 5', - position: { referencePanel: 'panel_3', direction: 'below' }, + position: { referencePanel: panel4 }, }); - event.api.addPanel({ + + const panel6 = event.api.addPanel({ id: 'panel_6', component: 'default', title: 'Panel 6', - position: { referencePanel: 'panel_3', direction: 'right' }, + position: { referencePanel: panel5, direction: 'below' }, }); - event.api.getPanel('panel_1')!.api.setActive(); + const panel7 = event.api.addPanel({ + id: 'panel_7', + component: 'default', + title: 'Panel 7', + position: { referencePanel: panel6, direction: 'left' }, + }); - console.log(event.api.toJSON()); + event.api.addPanel({ + id: 'panel8', + component: 'default', + title: 'Panel 8', + position: { referencePanel: panel7, direction: 'below' }, + }); + + panel1.api.setActive(); }; return ( diff --git a/packages/docs/sandboxes/iframe-dockview/src/app.tsx b/packages/docs/sandboxes/iframe-dockview/src/app.tsx index 2b200ab12..5a6587484 100644 --- a/packages/docs/sandboxes/iframe-dockview/src/app.tsx +++ b/packages/docs/sandboxes/iframe-dockview/src/app.tsx @@ -1,25 +1,11 @@ -import { - DockviewReact, - DockviewReadyEvent, - IDockviewPanelProps, -} from 'dockview'; +import { DockviewReact, DockviewReadyEvent } from 'dockview'; import * as React from 'react'; -import { HoistedDockviewPanel } from './hoistedDockviewPanel'; const components = { - iframeComponent: (props: IDockviewPanelProps<{ color: string }>) => { - return ( -
-
- {props.api.title} -
-
- ); + iframeComponent: () => { return (