@hanzogui/alert-dialog 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cjs/AlertDialog.cjs +276 -0
- package/dist/cjs/AlertDialog.native.js +379 -0
- package/dist/cjs/AlertDialog.native.js.map +1 -0
- package/dist/cjs/index.cjs +18 -0
- package/dist/cjs/index.native.js +21 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/esm/AlertDialog.mjs +231 -0
- package/dist/esm/AlertDialog.mjs.map +1 -0
- package/dist/esm/AlertDialog.native.js +333 -0
- package/dist/esm/AlertDialog.native.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +2 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/jsx/AlertDialog.mjs +231 -0
- package/dist/jsx/AlertDialog.mjs.map +1 -0
- package/dist/jsx/AlertDialog.native.js +379 -0
- package/dist/jsx/AlertDialog.native.js.map +1 -0
- package/dist/jsx/index.js +2 -0
- package/dist/jsx/index.js.map +1 -0
- package/dist/jsx/index.mjs +2 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +21 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/package.json +66 -0
- package/src/AlertDialog.tsx +515 -0
- package/src/index.ts +1 -0
- package/types/AlertDialog.d.ts +169 -0
- package/types/index.d.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA,cAAc","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA,cAAc","ignoreList":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __copyProps = (to, from, except, desc) => {
|
|
8
|
+
if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
|
|
9
|
+
get: () => from[key],
|
|
10
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
11
|
+
});
|
|
12
|
+
return to;
|
|
13
|
+
},
|
|
14
|
+
__reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
15
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
16
|
+
value: !0
|
|
17
|
+
}), mod);
|
|
18
|
+
var index_exports = {};
|
|
19
|
+
module.exports = __toCommonJS(index_exports);
|
|
20
|
+
__reExport(index_exports, require("./AlertDialog.native.js"), module.exports);
|
|
21
|
+
//# sourceMappingURL=index.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA","ignoreList":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hanzogui/alert-dialog",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"source": "src/index.ts",
|
|
5
|
+
"files": [
|
|
6
|
+
"src",
|
|
7
|
+
"types",
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": [
|
|
12
|
+
"*.css"
|
|
13
|
+
],
|
|
14
|
+
"main": "dist/cjs",
|
|
15
|
+
"module": "dist/esm",
|
|
16
|
+
"types": "./types/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
"./package.json": "./package.json",
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./types/index.d.ts",
|
|
21
|
+
"react-native": "./dist/esm/index.native.js",
|
|
22
|
+
"browser": "./dist/esm/index.mjs",
|
|
23
|
+
"module": "./dist/esm/index.mjs",
|
|
24
|
+
"import": "./dist/esm/index.mjs",
|
|
25
|
+
"require": "./dist/cjs/index.cjs",
|
|
26
|
+
"default": "./dist/esm/index.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "hanzo-gui-build",
|
|
34
|
+
"watch": "hanzo-gui-build --watch",
|
|
35
|
+
"clean": "hanzo-gui-build clean",
|
|
36
|
+
"clean:build": "hanzo-gui-build clean:build"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@hanzogui/animate-presence": "2.0.0",
|
|
40
|
+
"@hanzogui/compose-refs": "2.0.0-rc.29",
|
|
41
|
+
"@hanzogui/constants": "2.0.0-rc.29",
|
|
42
|
+
"@hanzogui/core": "2.0.0-rc.29",
|
|
43
|
+
"@hanzogui/create-context": "2.0.0-rc.29",
|
|
44
|
+
"@hanzogui/dialog": "2.0.0",
|
|
45
|
+
"@hanzogui/dismissable": "2.0.0",
|
|
46
|
+
"@hanzogui/focus-scope": "2.0.0",
|
|
47
|
+
"@hanzogui/helpers": "2.0.0-rc.29",
|
|
48
|
+
"@hanzogui/polyfill-dev": "2.0.0-rc.29",
|
|
49
|
+
"@hanzogui/portal": "2.0.0",
|
|
50
|
+
"@hanzogui/remove-scroll": "2.0.0",
|
|
51
|
+
"@hanzogui/stacks": "2.0.0",
|
|
52
|
+
"@hanzogui/text": "2.0.0",
|
|
53
|
+
"@hanzogui/use-controllable-state": "2.0.0-rc.29"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@hanzogui/build": "2.0.0-rc.29",
|
|
57
|
+
"react": ">=19",
|
|
58
|
+
"react-native": "0.83.2"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"react": ">=19",
|
|
62
|
+
"react-native": "*"
|
|
63
|
+
},
|
|
64
|
+
"module:jsx": "dist/jsx",
|
|
65
|
+
"removeSideEffects": true
|
|
66
|
+
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// forked from radix-ui
|
|
2
|
+
// https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/src/AlertDialog.tsx
|
|
3
|
+
|
|
4
|
+
import { useComposedRefs } from '@hanzogui/compose-refs'
|
|
5
|
+
import { isWeb, useIsomorphicLayoutEffect } from '@hanzogui/constants'
|
|
6
|
+
import type { GuiElement } from '@hanzogui/core'
|
|
7
|
+
import {
|
|
8
|
+
Slottable,
|
|
9
|
+
View,
|
|
10
|
+
createStyledContext,
|
|
11
|
+
isGuiElement,
|
|
12
|
+
styled,
|
|
13
|
+
} from '@hanzogui/core'
|
|
14
|
+
import type {
|
|
15
|
+
DialogCloseProps,
|
|
16
|
+
DialogContentProps,
|
|
17
|
+
DialogDescriptionProps,
|
|
18
|
+
DialogOverlayExtraProps,
|
|
19
|
+
DialogOverlayProps,
|
|
20
|
+
DialogPortalProps,
|
|
21
|
+
DialogProps,
|
|
22
|
+
DialogTitleProps,
|
|
23
|
+
DialogTriggerProps,
|
|
24
|
+
} from '@hanzogui/dialog'
|
|
25
|
+
import {
|
|
26
|
+
Dialog,
|
|
27
|
+
DialogClose,
|
|
28
|
+
DialogContent,
|
|
29
|
+
DialogDescription,
|
|
30
|
+
DialogOverlay,
|
|
31
|
+
DialogOverlayFrame,
|
|
32
|
+
DialogPortal,
|
|
33
|
+
DialogTitle,
|
|
34
|
+
DialogTrigger,
|
|
35
|
+
DialogWarningProvider,
|
|
36
|
+
} from '@hanzogui/dialog'
|
|
37
|
+
import { composeEventHandlers, withStaticProperties } from '@hanzogui/helpers'
|
|
38
|
+
import { useControllableState } from '@hanzogui/use-controllable-state'
|
|
39
|
+
import * as React from 'react'
|
|
40
|
+
import { Alert } from 'react-native'
|
|
41
|
+
|
|
42
|
+
const getAlertDialogScope = (scope?: string) => scope
|
|
43
|
+
|
|
44
|
+
/* -------------------------------------------------------------------------------------------------
|
|
45
|
+
* AlertDialog
|
|
46
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
47
|
+
|
|
48
|
+
const ROOT_NAME = 'AlertDialog'
|
|
49
|
+
|
|
50
|
+
export type AlertDialogScopes = string
|
|
51
|
+
|
|
52
|
+
type ScopedProps<P> = Omit<P, 'scope'> & { scope?: AlertDialogScopes }
|
|
53
|
+
|
|
54
|
+
type AlertDialogProps = ScopedProps<DialogProps> & {
|
|
55
|
+
native?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* -------------------------------------------------------------------------------------------------
|
|
59
|
+
* AlertDialogTrigger
|
|
60
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
61
|
+
|
|
62
|
+
const TRIGGER_NAME = 'AlertDialogTrigger'
|
|
63
|
+
|
|
64
|
+
type AlertDialogTriggerProps = ScopedProps<DialogTriggerProps>
|
|
65
|
+
|
|
66
|
+
const NativeAlertDialogTriggerFrame = styled(View, {
|
|
67
|
+
name: TRIGGER_NAME,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const AlertDialogTrigger =
|
|
71
|
+
NativeAlertDialogTriggerFrame.styleable<AlertDialogTriggerProps>(
|
|
72
|
+
function AlertDialogTrigger(props, forwardedRef) {
|
|
73
|
+
if (props['__native']) {
|
|
74
|
+
const { __native, onPress, __onPress, ...rest } = props as any
|
|
75
|
+
return (
|
|
76
|
+
<NativeAlertDialogTriggerFrame
|
|
77
|
+
{...rest}
|
|
78
|
+
onPress={composeEventHandlers(onPress, __onPress)}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { scope, ...triggerProps } = props
|
|
84
|
+
return (
|
|
85
|
+
<DialogTrigger
|
|
86
|
+
scope={getAlertDialogScope(scope)}
|
|
87
|
+
{...triggerProps}
|
|
88
|
+
ref={forwardedRef}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
/* -------------------------------------------------------------------------------------------------
|
|
95
|
+
* AlertDialogPortal
|
|
96
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
97
|
+
|
|
98
|
+
const PORTAL_NAME = 'AlertDialogPortal'
|
|
99
|
+
|
|
100
|
+
type AlertDialogPortalProps = ScopedProps<DialogPortalProps>
|
|
101
|
+
|
|
102
|
+
const AlertDialogPortal: React.FC<AlertDialogPortalProps> = function AlertDialogPortal(
|
|
103
|
+
props: ScopedProps<AlertDialogPortalProps>
|
|
104
|
+
) {
|
|
105
|
+
const { scope, ...portalProps } = props
|
|
106
|
+
return <DialogPortal scope={getAlertDialogScope(scope)} {...portalProps} />
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* -------------------------------------------------------------------------------------------------
|
|
110
|
+
* AlertDialogOverlay
|
|
111
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
112
|
+
|
|
113
|
+
const OVERLAY_NAME = 'AlertDialogOverlay'
|
|
114
|
+
|
|
115
|
+
const AlertDialogOverlayFrame = styled(DialogOverlayFrame, {
|
|
116
|
+
name: OVERLAY_NAME,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
type AlertDialogOverlayExtraProps = ScopedProps<{}> & DialogOverlayExtraProps
|
|
120
|
+
type AlertDialogOverlayProps = AlertDialogOverlayExtraProps & DialogOverlayProps
|
|
121
|
+
|
|
122
|
+
const AlertDialogOverlay = AlertDialogOverlayFrame.styleable<AlertDialogOverlayProps>(
|
|
123
|
+
function AlertDialogOverlay(props, forwardedRef) {
|
|
124
|
+
const { scope, ...overlayProps } = props
|
|
125
|
+
return (
|
|
126
|
+
<DialogOverlay
|
|
127
|
+
scope={getAlertDialogScope(scope)}
|
|
128
|
+
{...overlayProps}
|
|
129
|
+
ref={forwardedRef}
|
|
130
|
+
/>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
/* -------------------------------------------------------------------------------------------------
|
|
136
|
+
* AlertDialogContent
|
|
137
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
138
|
+
|
|
139
|
+
const CONTENT_NAME = 'AlertDialogContent'
|
|
140
|
+
|
|
141
|
+
type AlertDialogContentContextValue = {
|
|
142
|
+
cancelRef?: React.RefObject<GuiElement | null>
|
|
143
|
+
destructiveRef?: React.RefObject<GuiElement | null>
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const {
|
|
147
|
+
Provider: AlertDialogContextProvider,
|
|
148
|
+
useStyledContext: useAlertDialogContentContext,
|
|
149
|
+
} = createStyledContext<AlertDialogContentContextValue>({}, 'AlertDialogContext')
|
|
150
|
+
|
|
151
|
+
type AlertDialogContentProps = ScopedProps<
|
|
152
|
+
Omit<DialogContentProps, 'onPointerDownOutside' | 'onInteractOutside'>
|
|
153
|
+
>
|
|
154
|
+
|
|
155
|
+
const AlertDialogContent = React.forwardRef<GuiElement, AlertDialogContentProps>(
|
|
156
|
+
function AlertDialogContent(props, forwardedRef) {
|
|
157
|
+
const { scope, children, ...contentProps } = props
|
|
158
|
+
const dialogScope = getAlertDialogScope(scope)
|
|
159
|
+
const contentRef = React.useRef<GuiElement>(null)
|
|
160
|
+
const composedRefs = useComposedRefs(forwardedRef, contentRef)
|
|
161
|
+
const cancelRef = React.useRef<GuiElement | null>(null)
|
|
162
|
+
const destructiveRef = React.useRef<GuiElement | null>(null)
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<DialogWarningProvider
|
|
166
|
+
contentName={CONTENT_NAME}
|
|
167
|
+
titleName={TITLE_NAME}
|
|
168
|
+
docsSlug="alert-dialog"
|
|
169
|
+
>
|
|
170
|
+
<AlertDialogContextProvider
|
|
171
|
+
scope={scope}
|
|
172
|
+
cancelRef={cancelRef}
|
|
173
|
+
destructiveRef={destructiveRef}
|
|
174
|
+
>
|
|
175
|
+
<DialogContent
|
|
176
|
+
role="alertdialog"
|
|
177
|
+
aria-modal={true}
|
|
178
|
+
scope={dialogScope}
|
|
179
|
+
{...contentProps}
|
|
180
|
+
ref={composedRefs}
|
|
181
|
+
onOpenAutoFocus={composeEventHandlers(
|
|
182
|
+
contentProps.onOpenAutoFocus,
|
|
183
|
+
(event) => {
|
|
184
|
+
event.preventDefault()
|
|
185
|
+
if (isWeb) {
|
|
186
|
+
cancelRef.current?.focus({ preventScroll: true })
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
)}
|
|
190
|
+
onPointerDownOutside={(event) => event.preventDefault()}
|
|
191
|
+
onInteractOutside={(event) => event.preventDefault()}
|
|
192
|
+
>
|
|
193
|
+
{/**
|
|
194
|
+
* We have to use `Slottable` here as we cannot wrap the `AlertDialogContentProvider`
|
|
195
|
+
* around everything, otherwise the `DescriptionWarning` would be rendered straight away.
|
|
196
|
+
* This is because we want the accessibility checks to run only once the content is actually
|
|
197
|
+
* open and that behaviour is already encapsulated in `DialogContent`.
|
|
198
|
+
*/}
|
|
199
|
+
<Slottable>{children}</Slottable>
|
|
200
|
+
{process.env.NODE_ENV === 'development' && (
|
|
201
|
+
<DescriptionWarning contentRef={contentRef} />
|
|
202
|
+
)}
|
|
203
|
+
</DialogContent>
|
|
204
|
+
</AlertDialogContextProvider>
|
|
205
|
+
</DialogWarningProvider>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
/* -------------------------------------------------------------------------------------------------
|
|
211
|
+
* AlertDialogTitle
|
|
212
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
213
|
+
|
|
214
|
+
const TITLE_NAME = 'AlertDialogTitle'
|
|
215
|
+
|
|
216
|
+
type AlertDialogTitleProps = ScopedProps<DialogTitleProps>
|
|
217
|
+
|
|
218
|
+
const AlertDialogTitleFrame = styled(View, {
|
|
219
|
+
name: TITLE_NAME,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const AlertDialogTitle = AlertDialogTitleFrame.styleable<AlertDialogTitleProps>(
|
|
223
|
+
function AlertDialogTitle(props, forwardedRef) {
|
|
224
|
+
const { scope, ...titleProps } = props
|
|
225
|
+
return (
|
|
226
|
+
<DialogTitle
|
|
227
|
+
scope={getAlertDialogScope(scope)}
|
|
228
|
+
{...titleProps}
|
|
229
|
+
ref={forwardedRef}
|
|
230
|
+
/>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
/* -------------------------------------------------------------------------------------------------
|
|
236
|
+
* AlertDialogDescription
|
|
237
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
238
|
+
|
|
239
|
+
const DESCRIPTION_NAME = 'AlertDialogDescription'
|
|
240
|
+
|
|
241
|
+
type AlertDialogDescriptionProps = ScopedProps<DialogDescriptionProps>
|
|
242
|
+
|
|
243
|
+
const AlertDialogDescriptionFrame = styled(View, {
|
|
244
|
+
name: DESCRIPTION_NAME,
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
const AlertDialogDescription =
|
|
248
|
+
AlertDialogDescriptionFrame.styleable<AlertDialogDescriptionProps>(
|
|
249
|
+
function AlertDialogDescription(props, forwardedRef) {
|
|
250
|
+
const { scope, ...descriptionProps } = props
|
|
251
|
+
return (
|
|
252
|
+
<DialogDescription
|
|
253
|
+
scope={getAlertDialogScope(scope)}
|
|
254
|
+
{...descriptionProps}
|
|
255
|
+
ref={forwardedRef}
|
|
256
|
+
/>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
/* -------------------------------------------------------------------------------------------------
|
|
262
|
+
* AlertDialogAction
|
|
263
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
264
|
+
|
|
265
|
+
const ACTION_NAME = 'AlertDialogAction'
|
|
266
|
+
|
|
267
|
+
type AlertDialogActionProps = ScopedProps<DialogCloseProps>
|
|
268
|
+
|
|
269
|
+
const AlertDialogActionFrame = styled(View, {
|
|
270
|
+
name: ACTION_NAME,
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const AlertDialogAction = AlertDialogActionFrame.styleable<AlertDialogActionProps>(
|
|
274
|
+
function AlertDialogAction(props, forwardedRef) {
|
|
275
|
+
const { scope, ...actionProps } = props
|
|
276
|
+
return (
|
|
277
|
+
<DialogClose
|
|
278
|
+
scope={getAlertDialogScope(scope)}
|
|
279
|
+
{...actionProps}
|
|
280
|
+
ref={forwardedRef}
|
|
281
|
+
/>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
/* -------------------------------------------------------------------------------------------------
|
|
287
|
+
* AlertDialogCancel
|
|
288
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
289
|
+
|
|
290
|
+
const CANCEL_NAME = 'AlertDialogCancel'
|
|
291
|
+
|
|
292
|
+
type AlertDialogCancelProps = ScopedProps<DialogCloseProps>
|
|
293
|
+
|
|
294
|
+
const AlertDialogCancelFrame = styled(View, {
|
|
295
|
+
name: CANCEL_NAME,
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
const AlertDialogCancel = AlertDialogCancelFrame.styleable<AlertDialogCancelProps>(
|
|
299
|
+
function AlertDialogCancel(props, forwardedRef) {
|
|
300
|
+
const { scope, ...cancelProps } = props
|
|
301
|
+
const { cancelRef } = useAlertDialogContentContext(scope)
|
|
302
|
+
const ref = useComposedRefs(forwardedRef, cancelRef)
|
|
303
|
+
return <DialogClose scope={getAlertDialogScope(scope)} {...cancelProps} ref={ref} />
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
/* -------------------------------------------------------------------------------------------------
|
|
308
|
+
* AlertDialogDestructive
|
|
309
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
310
|
+
|
|
311
|
+
const DESTRUCTIVE_NAME = 'AlertDialogDestructive'
|
|
312
|
+
|
|
313
|
+
type AlertDialogDestructiveProps = ScopedProps<DialogCloseProps>
|
|
314
|
+
|
|
315
|
+
const AlertDialogDestructiveFrame = styled(View, {
|
|
316
|
+
name: DESTRUCTIVE_NAME,
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
const AlertDialogDestructive =
|
|
320
|
+
AlertDialogDestructiveFrame.styleable<AlertDialogDestructiveProps>(
|
|
321
|
+
function AlertDialogDestructive(props, forwardedRef) {
|
|
322
|
+
const { scope, ...destructiveProps } = props
|
|
323
|
+
const { destructiveRef } = useAlertDialogContentContext(scope)
|
|
324
|
+
const ref = useComposedRefs(forwardedRef, destructiveRef)
|
|
325
|
+
return (
|
|
326
|
+
<DialogClose scope={getAlertDialogScope(scope)} {...destructiveProps} ref={ref} />
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
/* ---------------------------------------------------------------------------------------------- */
|
|
332
|
+
|
|
333
|
+
type DescriptionWarningProps = {
|
|
334
|
+
contentRef: React.RefObject<GuiElement | null>
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const DescriptionWarning: React.FC<DescriptionWarningProps> = ({ contentRef }) => {
|
|
338
|
+
if (process.env.NODE_ENV === 'development') {
|
|
339
|
+
React.useEffect(() => {
|
|
340
|
+
if (!isWeb) return
|
|
341
|
+
const hasDescription = document.getElementById(
|
|
342
|
+
// @ts-ignore
|
|
343
|
+
contentRef.current?.getAttribute('aria-describedby')
|
|
344
|
+
)
|
|
345
|
+
if (!hasDescription) {
|
|
346
|
+
console.warn(`\`${CONTENT_NAME}\` requires a description for the component to be accessible for screen reader users.
|
|
347
|
+
|
|
348
|
+
You can add a description to the \`${CONTENT_NAME}\` by passing a \`${DESCRIPTION_NAME}\` component as a child, which also benefits sighted users by adding visible context to the dialog.
|
|
349
|
+
|
|
350
|
+
Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${CONTENT_NAME}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component.
|
|
351
|
+
|
|
352
|
+
For more information, see https://gui.dev/docs/components/alert-dialog`)
|
|
353
|
+
}
|
|
354
|
+
}, [contentRef])
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return null
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const AlertDialogInner: React.FC<AlertDialogProps> = (props) => {
|
|
361
|
+
const { scope, native, ...alertDialogProps } = props
|
|
362
|
+
const dialogScope = getAlertDialogScope(scope)
|
|
363
|
+
|
|
364
|
+
if (process.env.HANZO_GUI_TARGET === 'native') {
|
|
365
|
+
const [open, setOpen] = useControllableState({
|
|
366
|
+
prop: props.open,
|
|
367
|
+
defaultProp: props.defaultOpen || false,
|
|
368
|
+
onChange: props.onOpenChange,
|
|
369
|
+
transition: true,
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
let triggerElement: any = null
|
|
373
|
+
let title = ''
|
|
374
|
+
let description = ''
|
|
375
|
+
const buttons: {
|
|
376
|
+
text: string
|
|
377
|
+
onPress: (value?: string | undefined) => void
|
|
378
|
+
style?: 'default' | 'cancel' | 'destructive'
|
|
379
|
+
}[] = []
|
|
380
|
+
|
|
381
|
+
forEachChildDeep(React.Children.toArray(props.children), (child) => {
|
|
382
|
+
if (!React.isValidElement(child)) return false
|
|
383
|
+
const name = isGuiElement(child)
|
|
384
|
+
? child.type.staticConfig.componentName
|
|
385
|
+
: (child.type['displayName'] as string | undefined)
|
|
386
|
+
switch (name) {
|
|
387
|
+
case TRIGGER_NAME: {
|
|
388
|
+
triggerElement = React.cloneElement(child as any, {
|
|
389
|
+
__native: true,
|
|
390
|
+
})
|
|
391
|
+
return false
|
|
392
|
+
}
|
|
393
|
+
case TITLE_NAME: {
|
|
394
|
+
title = getStringChildren(child)
|
|
395
|
+
return false
|
|
396
|
+
}
|
|
397
|
+
case DESCRIPTION_NAME: {
|
|
398
|
+
description = getStringChildren(child)
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
case ACTION_NAME:
|
|
402
|
+
case DESTRUCTIVE_NAME:
|
|
403
|
+
case CANCEL_NAME: {
|
|
404
|
+
const style =
|
|
405
|
+
name === ACTION_NAME
|
|
406
|
+
? 'default'
|
|
407
|
+
: name === DESTRUCTIVE_NAME
|
|
408
|
+
? 'destructive'
|
|
409
|
+
: 'cancel'
|
|
410
|
+
const text = getStringChildren(child)
|
|
411
|
+
const onPress = () => {
|
|
412
|
+
const childProps = child.props as any
|
|
413
|
+
childProps?.onPress?.({ native: true })
|
|
414
|
+
setOpen(false)
|
|
415
|
+
}
|
|
416
|
+
buttons.push({
|
|
417
|
+
style,
|
|
418
|
+
text,
|
|
419
|
+
// @ts-ignore
|
|
420
|
+
onPress,
|
|
421
|
+
})
|
|
422
|
+
return false
|
|
423
|
+
}
|
|
424
|
+
default: {
|
|
425
|
+
return true
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
useIsomorphicLayoutEffect(() => {
|
|
431
|
+
if (!open || !native) return
|
|
432
|
+
if (title || description) {
|
|
433
|
+
Alert.alert(title, description, buttons)
|
|
434
|
+
}
|
|
435
|
+
}, [native, open])
|
|
436
|
+
|
|
437
|
+
if (native) {
|
|
438
|
+
return React.cloneElement(triggerElement, {
|
|
439
|
+
__onPress: () => {
|
|
440
|
+
setOpen(true)
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return <Dialog scope={dialogScope} {...alertDialogProps} modal />
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function forEachChildDeep(
|
|
450
|
+
children: React.ReactNode[],
|
|
451
|
+
onChild: (el: React.ReactElement) => boolean
|
|
452
|
+
) {
|
|
453
|
+
for (const child of children) {
|
|
454
|
+
if (!React.isValidElement(child)) continue
|
|
455
|
+
if (!onChild(child)) continue
|
|
456
|
+
// TODO react 19 doesn't like child.props
|
|
457
|
+
const childProps = child.props as unknown as any
|
|
458
|
+
if (childProps.children) {
|
|
459
|
+
forEachChildDeep(React.Children.toArray(childProps.children), onChild)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function getStringChildren(child: React.ReactElement) {
|
|
465
|
+
let string = ''
|
|
466
|
+
forEachChildDeep(React.Children.toArray(child), (child) => {
|
|
467
|
+
if (typeof (child.props as Record<string, any>).children === 'string') {
|
|
468
|
+
string = (child.props as Record<string, any>).children
|
|
469
|
+
return false
|
|
470
|
+
}
|
|
471
|
+
return true
|
|
472
|
+
})
|
|
473
|
+
return string
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const AlertDialog = withStaticProperties(AlertDialogInner, {
|
|
477
|
+
Trigger: AlertDialogTrigger,
|
|
478
|
+
Portal: AlertDialogPortal,
|
|
479
|
+
Overlay: AlertDialogOverlay,
|
|
480
|
+
Content: AlertDialogContent,
|
|
481
|
+
Action: AlertDialogAction,
|
|
482
|
+
Cancel: AlertDialogCancel,
|
|
483
|
+
Destructive: AlertDialogDestructive,
|
|
484
|
+
Title: AlertDialogTitle,
|
|
485
|
+
Description: AlertDialogDescription,
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
AlertDialog.displayName = ROOT_NAME
|
|
489
|
+
|
|
490
|
+
export {
|
|
491
|
+
//
|
|
492
|
+
AlertDialog,
|
|
493
|
+
AlertDialogAction,
|
|
494
|
+
AlertDialogCancel,
|
|
495
|
+
AlertDialogDestructive,
|
|
496
|
+
AlertDialogContent,
|
|
497
|
+
AlertDialogDescription,
|
|
498
|
+
AlertDialogOverlay,
|
|
499
|
+
AlertDialogPortal,
|
|
500
|
+
AlertDialogTitle,
|
|
501
|
+
AlertDialogTrigger,
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export type {
|
|
505
|
+
AlertDialogActionProps,
|
|
506
|
+
AlertDialogCancelProps,
|
|
507
|
+
AlertDialogDestructiveProps,
|
|
508
|
+
AlertDialogContentProps,
|
|
509
|
+
AlertDialogDescriptionProps,
|
|
510
|
+
AlertDialogOverlayProps,
|
|
511
|
+
AlertDialogPortalProps,
|
|
512
|
+
AlertDialogProps,
|
|
513
|
+
AlertDialogTitleProps,
|
|
514
|
+
AlertDialogTriggerProps,
|
|
515
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AlertDialog'
|