mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
Both old names are owned by a different npm account (artemis-prime), so publishing them returns 403 — which is how this surfaced: the 8.0.0 run shipped @hanzogui/logo and @hanzogui/demos depending on [email protected], a version that cannot be published and therefore does not exist. Those two were uninstallable. @hanzo/gui and @hanzogui/* are ours, so the framework now carries the name it is imported by everywhere downstream, and the loader moves under the scope we control. The rewrite is limited to package references. `DEBUG?.startsWith('hanzogui')` is a log-namespace filter and join(dir, 'hanzogui') is a path — rewriting either would change behaviour, so both are untouched. Co-authored-by: Hanzo Dev <[email protected]>
344 lines
8.0 KiB
Plaintext
344 lines
8.0 KiB
Plaintext
---
|
|
title: Dialog
|
|
description: Show a modal with configurable layout and accessible actions.
|
|
name: dialog
|
|
component: Dialog
|
|
package: dialog
|
|
demoName: Dialog
|
|
---
|
|
|
|
<HeroContainer showAnimationDriverControl>
|
|
<DialogDemo />
|
|
</HeroContainer>
|
|
|
|
```tsx hero template=Dialog
|
|
|
|
```
|
|
|
|
<Highlights
|
|
features={[
|
|
`Comes with styling, yet completely customizable and themeable.`,
|
|
`Accepts animations, themes, size props and more.`,
|
|
`Accessible with dev-time checks to ensure ARIA props.`,
|
|
]}
|
|
/>
|
|
|
|
<Notice>
|
|
Dialog does not work on native, instead you can Adapt it to a Sheet as shown in the demo
|
|
code above.
|
|
</Notice>
|
|
|
|
## Installation
|
|
|
|
Dialog is already installed in `hanzogui`, or you can install it independently:
|
|
|
|
```bash
|
|
npm install @hanzogui/dialog
|
|
```
|
|
|
|
In order to use this component independently of `hanzogui`, you'll first need to install the `@hanzogui/portal` package:
|
|
|
|
```bash
|
|
npm install @hanzogui/portal
|
|
```
|
|
|
|
Then add `PortalProvider` to the root of your app:
|
|
|
|
```tsx fileName="App.tsx"
|
|
import { PortalProvider } from '@hanzogui/portal'
|
|
import YourApp from './components/YourApp'
|
|
|
|
function App() {
|
|
return (
|
|
<PortalProvider shouldAddRootHost>
|
|
<YourApp />
|
|
</PortalProvider>
|
|
)
|
|
}
|
|
|
|
export default App
|
|
```
|
|
|
|
## Anatomy
|
|
|
|
```tsx
|
|
import { Dialog } from '@hanzo/gui' // or '@hanzogui/dialog'
|
|
|
|
export default () => (
|
|
<Dialog>
|
|
<Dialog.Trigger />
|
|
<Dialog.Portal>
|
|
<Dialog.Overlay />
|
|
<Dialog.Content>
|
|
<Dialog.Title />
|
|
<Dialog.Description />
|
|
<Dialog.Close />
|
|
{/* ... */}
|
|
</Dialog.Content>
|
|
</Dialog.Portal>
|
|
|
|
{/* Optional: Control focus behavior */}
|
|
<Dialog.FocusScope loop trapped focusOnIdle={true}>
|
|
<Dialog.FocusScope.Scope>
|
|
{/* Focus scope will be applied to children */}
|
|
</Dialog.FocusScope.Scope>
|
|
</Dialog.FocusScope>
|
|
</Dialog>
|
|
)
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Dialog
|
|
|
|
Contains every component for the dialog. Beyond [Gui Props](/docs/intro/props), adds:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'children',
|
|
type: 'React.ReactNode',
|
|
required: true,
|
|
description: `Must contain Dialog.Content`,
|
|
},
|
|
{
|
|
name: 'size',
|
|
type: 'SizeTokens',
|
|
description: `Passes size down too all sub-components when set for padding, arrow, borderRadius`,
|
|
},
|
|
{
|
|
name: 'open',
|
|
type: 'boolean',
|
|
description: ``,
|
|
},
|
|
{
|
|
name: 'defaultOpen',
|
|
type: 'boolean',
|
|
},
|
|
{
|
|
name: 'onOpenChange',
|
|
type: '(open: boolean) => void',
|
|
},
|
|
{
|
|
name: 'modal',
|
|
type: 'boolean',
|
|
default: 'true',
|
|
description: `Renders into root of app instead of inline`,
|
|
},
|
|
{
|
|
name: 'disableRemoveScroll',
|
|
type: 'boolean',
|
|
required: false,
|
|
description: `Used to disable the automatic removal of scrolling from the page when open.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Dialog.Trigger
|
|
|
|
Just [Gui Props](/docs/intro/props).
|
|
|
|
### Dialog.Portal
|
|
|
|
Renders Dialog into appropriate container. Beyond [Gui Props](/docs/intro/props), adds:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'forceMount',
|
|
type: 'boolean',
|
|
required: false,
|
|
description: `Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.`,
|
|
},
|
|
{
|
|
name: 'unstyled',
|
|
required: false,
|
|
type: `boolean`,
|
|
description: `Removes all default Gui styles.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Dialog.Content
|
|
|
|
Main container for Dialog content, this is where you should apply animations.
|
|
|
|
Beyond [Gui Props](/docs/intro/props), adds:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'forceMount',
|
|
type: 'boolean',
|
|
required: false,
|
|
description: `Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.`,
|
|
},
|
|
{
|
|
name: 'unstyled',
|
|
required: false,
|
|
type: `boolean`,
|
|
description: `Removes all default Gui styles.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Dialog.Overlay
|
|
|
|
Displays behind Content. Beyond [Gui Props](/docs/intro/props), adds:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'forceMount',
|
|
type: 'boolean',
|
|
required: false,
|
|
description: `Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Dialog.Title
|
|
|
|
Required. Can wrap in VisuallyHidden to hide.
|
|
|
|
Defaults to H2, see [Headings](/docs/components/headings).
|
|
|
|
### Dialog.Description
|
|
|
|
Required. Can wrap in VisuallyHidden to hide.
|
|
|
|
Defaults to Paragraph, see [Paragraph](/docs/components/text).
|
|
|
|
### Dialog.Close
|
|
|
|
Closes the Dialog, accepts the same props as YStack. Recommended to use with your own component and `asChild`.
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'displayWhenAdapted',
|
|
type: 'boolean',
|
|
description: `By default Close elements hide when Adapt is active. If set to true, they will show when adapted.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
Just [Gui Props](/docs/intro/props).
|
|
|
|
### Dialog.FocusScope
|
|
|
|
Provides access to the underlying FocusScope component used by Dialog for focus management. Can be used to control focus behavior from a parent component.
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'enabled',
|
|
type: 'boolean',
|
|
default: 'true',
|
|
description: `Whether focus management is enabled`,
|
|
},
|
|
{
|
|
name: 'loop',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, tabbing from last item will focus first tabbable and shift+tab from first item will focus last tabbable`,
|
|
},
|
|
{
|
|
name: 'trapped',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, focus cannot escape the focus scope via keyboard, pointer, or programmatic focus`,
|
|
},
|
|
{
|
|
name: 'focusOnIdle',
|
|
type: 'boolean | number',
|
|
default: 'false',
|
|
description: `When true, waits for idle before focusing. When a number, waits that many ms. This prevents reflows during animations`,
|
|
},
|
|
{
|
|
name: 'onMountAutoFocus',
|
|
type: '(event: Event) => void',
|
|
description: `Event handler called when auto-focusing on mount. Can be prevented`,
|
|
},
|
|
{
|
|
name: 'onUnmountAutoFocus',
|
|
type: '(event: Event) => void',
|
|
description: `Event handler called when auto-focusing on unmount. Can be prevented`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Dialog.Sheet
|
|
|
|
When used with `Adapt`, Dialog will render as a sheet when that breakpoint is active.
|
|
|
|
See [Sheet](/docs/components/sheet) for more props.
|
|
|
|
Must use `Adapt.Contents` inside the `Dialog.Sheet.Frame` to insert the contents given to `Dialog.Content`
|
|
|
|
```tsx
|
|
import { Dialog } from '@hanzo/gui' // or '@hanzogui/dialog'
|
|
|
|
export default () => (
|
|
<Dialog>
|
|
<Dialog.Trigger />
|
|
|
|
<Dialog.Portal>
|
|
<Dialog.Overlay />
|
|
<Dialog.Content>
|
|
<Dialog.Title />
|
|
<Dialog.Description />
|
|
<Dialog.Close />
|
|
{/* ... */}
|
|
</Dialog.Content>
|
|
</Dialog.Portal>
|
|
|
|
{/* optionally change to sheet when small screen */}
|
|
<Dialog.Adapt when="maxMd">
|
|
<Dialog.Sheet>
|
|
<Dialog.Sheet.Frame>
|
|
<Dialog.Adapt.Contents />
|
|
</Dialog.Sheet.Frame>
|
|
<Dialog.Sheet.Overlay />
|
|
</Dialog.Sheet>
|
|
</Dialog.Adapt>
|
|
</Dialog>
|
|
)
|
|
```
|
|
|
|
<Notice>
|
|
Note that Dialog.Sheet currently doesn't preserve state of the contents when it
|
|
transitions between Sheet and Portal. In the future, we can do this on the web using
|
|
react-reparenting.
|
|
</Notice>
|
|
|
|
### PortalProvider
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'shouldAddRootHost',
|
|
type: 'boolean',
|
|
required: false,
|
|
description: `Defines whether to add a default root host or not.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
## Examples
|
|
|
|
### Inside native modals
|
|
|
|
If you're using native modals (maybe from react-navigation), you'll notice the Dialogs won't show up inside the modal. To get around this, you should wrap your screen inside `PortalProvider`, like so:
|
|
|
|
```tsx
|
|
import { PortalProvider } from '@hanzo/gui'
|
|
|
|
// this component used in react-navigation/expo-router with `presentation: "modal"`
|
|
export function Page() {
|
|
return (
|
|
<PortalProvider>{/* rest of your page, including the Dialog... */}</PortalProvider>
|
|
)
|
|
}
|
|
```
|