Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Button, ConfirmationDialog, Form, Host, Section, Text } from '@expo/ui/swift-ui';
import { foregroundStyle } from '@expo/ui/swift-ui/modifiers';
import React, { useState } from 'react';

export default function ConfirmationDialogScreen() {
const [showBasic, setShowBasic] = useState(false);
const [showDestructive, setShowDestructive] = useState(false);
const [showWithMessage, setShowWithMessage] = useState(false);
const [showHiddenTitle, setShowHiddenTitle] = useState(false);
const [lastAction, setLastAction] = useState<string>('None');

return (
<Host style={{ flex: 1 }}>
<Form>
<Section title="Last Action">
<Text modifiers={[foregroundStyle('secondaryLabel')]}>{lastAction}</Text>
</Section>

<Section title="Basic">
<ConfirmationDialog
title="Are you sure?"
isPresented={showBasic}
onIsPresentedChange={setShowBasic}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button label="Show Basic Dialog" onPress={() => setShowBasic(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button
label="Confirm"
onPress={() => {
setLastAction('Basic: Confirmed');
setShowBasic(false);
}}
/>
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
</ConfirmationDialog>
</Section>

<Section title="Destructive Action">
<ConfirmationDialog
title="Delete Item?"
isPresented={showDestructive}
onIsPresentedChange={setShowDestructive}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button
label="Delete Item"
role="destructive"
onPress={() => setShowDestructive(true)}
/>
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button
label="Delete"
role="destructive"
onPress={() => {
setLastAction('Destructive: Deleted');
setShowDestructive(false);
}}
/>
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>This action cannot be undone.</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Section>

<Section title="With Message">
<ConfirmationDialog
title="Save Changes?"
isPresented={showWithMessage}
onIsPresentedChange={setShowWithMessage}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button label="Show Dialog with Message" onPress={() => setShowWithMessage(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button
label="Save"
onPress={() => {
setLastAction('With Message: Saved');
setShowWithMessage(false);
}}
/>
<Button
label="Discard"
role="destructive"
onPress={() => {
setLastAction('With Message: Discarded');
setShowWithMessage(false);
}}
/>
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>You have unsaved changes. What would you like to do?</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Section>

<Section title="Hidden Title">
<ConfirmationDialog
title="This title is hidden"
isPresented={showHiddenTitle}
onIsPresentedChange={setShowHiddenTitle}
titleVisibility="hidden">
<ConfirmationDialog.Trigger>
<Button label="Show Dialog (Hidden Title)" onPress={() => setShowHiddenTitle(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button
label="OK"
onPress={() => {
setLastAction('Hidden Title: OK');
setShowHiddenTitle(false);
}}
/>
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>The title above is hidden via titleVisibility.</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Section>
</Form>
</Host>
);
}

ConfirmationDialogScreen.navigationOptions = {
title: 'ConfirmationDialog',
};
8 changes: 8 additions & 0 deletions apps/native-component-list/src/screens/UI/UIScreen.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ export const UIScreens = [
return optionalRequire(() => require('./MenuScreen'));
},
},
{
name: 'ConfirmationDialog component',
route: 'ui/confirmation-dialog',
options: {},
getComponent() {
return optionalRequire(() => require('./ConfirmationDialogScreen'));
},
},
{
name: 'ContextMenu component',
route: 'ui/context-menu',
Expand Down
167 changes: 167 additions & 0 deletions docs/pages/versions/unversioned/sdk/ui/swift-ui/confirmationdialog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: ConfirmationDialog
description: A SwiftUI ConfirmationDialog component for presenting confirmation prompts.
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
packageName: '@expo/ui'
platforms: ['ios', 'tvos']
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

Expo UI ConfirmationDialog matches the official SwiftUI [confirmationDialog API](<https://developer.apple.com/documentation/swiftui/view/confirmationdialog(_:ispresented:titlevisibility:actions:message:)>) and presents an action sheet-style dialog with a title, actions, and an optional message.

## Installation

<APIInstallSection />

## Usage

### Basic confirmation dialog

Use `ConfirmationDialog.Trigger` to define the visible element and `ConfirmationDialog.Actions` to provide the dialog buttons.

```tsx BasicConfirmationDialogExample.tsx
import { useState } from 'react';
import { Host, ConfirmationDialog, Button, Text } from '@expo/ui/swift-ui';

export default function BasicConfirmationDialogExample() {
const [isPresented, setIsPresented] = useState(false);

return (
<Host matchContents>
<ConfirmationDialog
title="Are you sure?"
isPresented={isPresented}
onIsPresentedChange={setIsPresented}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button label="Show Dialog" onPress={() => setIsPresented(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button label="Confirm" onPress={() => setIsPresented(false)} />
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
</ConfirmationDialog>
</Host>
);
}
```

### Destructive action confirmation

Use `role="destructive"` on a `Button` inside `ConfirmationDialog.Actions` to style it as a destructive action.

```tsx DestructiveConfirmationDialogExample.tsx
import { useState } from 'react';
import { Host, ConfirmationDialog, Button, Text } from '@expo/ui/swift-ui';

export default function DestructiveConfirmationDialogExample() {
const [isPresented, setIsPresented] = useState(false);

return (
<Host matchContents>
<ConfirmationDialog
title="Delete Item?"
isPresented={isPresented}
onIsPresentedChange={setIsPresented}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button label="Delete" role="destructive" onPress={() => setIsPresented(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button
label="Delete"
role="destructive"
onPress={() => {
console.log('Deleted');
setIsPresented(false);
}}
/>
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>This action cannot be undone.</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Host>
);
}
```

### With message and multiple actions

Use `ConfirmationDialog.Message` to display a descriptive message below the title, and include multiple action buttons for different choices.

```tsx MultiActionConfirmationDialogExample.tsx
import { useState } from 'react';
import { Host, ConfirmationDialog, Button, Text } from '@expo/ui/swift-ui';

export default function MultiActionConfirmationDialogExample() {
const [isPresented, setIsPresented] = useState(false);

return (
<Host matchContents>
<ConfirmationDialog
title="Save Changes?"
isPresented={isPresented}
onIsPresentedChange={setIsPresented}
titleVisibility="visible">
<ConfirmationDialog.Trigger>
<Button label="Close Document" onPress={() => setIsPresented(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button label="Save" onPress={() => console.log('Saved')} />
<Button label="Discard" role="destructive" onPress={() => console.log('Discarded')} />
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>You have unsaved changes. What would you like to do?</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Host>
);
}
```

### Hidden title

Set `titleVisibility="hidden"` to hide the dialog title while still showing the actions and message. You should still provide a `title` for accessibility.

```tsx HiddenTitleConfirmationDialogExample.tsx
import { useState } from 'react';
import { Host, ConfirmationDialog, Button, Text } from '@expo/ui/swift-ui';

export default function HiddenTitleConfirmationDialogExample() {
const [isPresented, setIsPresented] = useState(false);

return (
<Host matchContents>
<ConfirmationDialog
title="Hidden Title"
isPresented={isPresented}
onIsPresentedChange={setIsPresented}
titleVisibility="hidden">
<ConfirmationDialog.Trigger>
<Button label="Show Dialog" onPress={() => setIsPresented(true)} />
</ConfirmationDialog.Trigger>
<ConfirmationDialog.Actions>
<Button label="OK" onPress={() => setIsPresented(false)} />
<Button label="Cancel" role="cancel" />
</ConfirmationDialog.Actions>
<ConfirmationDialog.Message>
<Text>Only the message and actions are visible.</Text>
</ConfirmationDialog.Message>
</ConfirmationDialog>
</Host>
);
}
```

## API

```tsx
import { ConfirmationDialog } from '@expo/ui/swift-ui';
```

<APISection packageName="expo-ui/swift-ui/confirmationdialog" apiName="ConfirmationDialog" />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":"2.0","name":"expo-ui/swift-ui/confirmationdialog","variant":"project","kind":1,"children":[{"name":"ConfirmationDialogProps","variant":"declaration","kind":2097152,"comment":{"summary":[{"kind":"text","text":"Props of the "},{"kind":"code","text":"`ConfirmationDialog`"},{"kind":"text","text":" component."}]},"type":{"type":"intersection","types":[{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"children","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"The contents of the confirmation dialog.\nShould include "},{"kind":"code","text":"`ConfirmationDialog.Trigger`"},{"kind":"text","text":", "},{"kind":"code","text":"`ConfirmationDialog.Actions`"},{"kind":"text","text":", and optionally "},{"kind":"code","text":"`ConfirmationDialog.Message`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.ReactNode"},"name":"React.ReactNode","package":"@types/react"}},{"name":"isPresented","variant":"declaration","kind":1024,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"Whether the confirmation dialog is presented."}]},"type":{"type":"intrinsic","name":"boolean"}},{"name":"onIsPresentedChange","variant":"declaration","kind":1024,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"A callback that is called when the "},{"kind":"code","text":"`isPresented`"},{"kind":"text","text":" state changes."}]},"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"parameters":[{"name":"isPresented","variant":"param","kind":32768,"type":{"type":"intrinsic","name":"boolean"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"name":"title","variant":"declaration","kind":1024,"comment":{"summary":[{"kind":"text","text":"The title of the confirmation dialog."}]},"type":{"type":"intrinsic","name":"string"}},{"name":"titleVisibility","variant":"declaration","kind":1024,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"The visibility of the dialog title."}],"blockTags":[{"tag":"@default","content":[{"kind":"text","text":"'automatic'"}]}]},"type":{"type":"union","types":[{"type":"literal","value":"automatic"},{"type":"literal","value":"visible"},{"type":"literal","value":"hidden"}]}}]}},{"type":"reference","target":{"packageName":"@expo/ui","packagePath":"src/swift-ui/types.ts","qualifiedName":"CommonViewModifierProps"},"name":"CommonViewModifierProps","package":"@expo/ui"}]}},{"name":"ConfirmationDialog","variant":"declaration","kind":64,"children":[{"name":"Actions","variant":"declaration","kind":1024,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"The action buttons displayed in the confirmation dialog. Use "},{"kind":"code","text":"`Button`"},{"kind":"text","text":" components from "},{"kind":"code","text":"`@expo/ui/swift-ui`"},{"kind":"text","text":" as children."}]},"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"children","variant":"declaration","kind":1024,"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.ReactNode"},"name":"ReactNode","package":"@types/react","qualifiedName":"React.ReactNode"}}]}}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"React.JSX.Element"}}]}}},{"name":"Message","variant":"declaration","kind":1024,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"An optional message displayed below the title in the confirmation dialog."}]},"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"children","variant":"declaration","kind":1024,"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.ReactNode"},"name":"ReactNode","package":"@types/react","qualifiedName":"React.ReactNode"}}]}}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"React.JSX.Element"}}]}}},{"name":"Trigger","variant":"declaration","kind":1024,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"signatures":[{"name":"__type","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"text","text":"The component visible all the time that triggers the confirmation dialog presentation."}]},"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"reflection","declaration":{"name":"__type","variant":"declaration","kind":65536,"children":[{"name":"children","variant":"declaration","kind":1024,"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.ReactNode"},"name":"ReactNode","package":"@types/react","qualifiedName":"React.ReactNode"}}]}}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"React.JSX.Element"}}]}}}],"signatures":[{"name":"ConfirmationDialog","variant":"signature","kind":4096,"comment":{"summary":[{"kind":"code","text":"`ConfirmationDialog`"},{"kind":"text","text":" presents a confirmation dialog with a title, optional message, and action buttons."}],"blockTags":[{"tag":"@see","content":[{"kind":"text","text":"https://developer.apple.com/documentation/swiftui/view/confirmationdialog(_:ispresented:titlevisibility:actions:message:)"}]}]},"parameters":[{"name":"props","variant":"param","kind":32768,"type":{"type":"reference","name":"ConfirmationDialogProps","package":"@expo/ui"}}],"type":{"type":"reference","target":{"packageName":"@types/react","packagePath":"index.d.ts","qualifiedName":"React.JSX.Element"},"name":"Element","package":"@types/react","qualifiedName":"React.JSX.Element"}}]}],"packageName":"@expo/ui"}
Loading
Loading