@startupjs-ui/dialogs 0.1.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
7
+
8
+ **Note:** Version bump only for package @startupjs-ui/dialogs
9
+
10
+
11
+
12
+
13
+
14
+ ## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
15
+
16
+
17
+ ### Features
18
+
19
+ * add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
20
+ * **dialogs:** refactor DialogsProvider, alert, confirm, prompt ([ec05fd3](https://github.com/startupjs/startupjs-ui/commit/ec05fd38ff1a4254b0acbc219605b3c94091b99f))
@@ -0,0 +1,33 @@
1
+ import { useEffect, useState, type ReactNode } from 'react'
2
+ import { pug, observer } from 'startupjs'
3
+ import Modal from '@startupjs-ui/modal'
4
+ import { setUpdateDialogState } from './helpers'
5
+
6
+ export const _PropsJsonSchema = {/* DialogsProviderProps */}
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
9
+ export interface DialogsProviderProps {}
10
+
11
+ function DialogsProviderRoot (): ReactNode {
12
+ const [dialog = {}, setDialog] = useState<any>()
13
+
14
+ // used by alert/confirm/prompt to show a dialog
15
+ setUpdateDialogState(setDialog)
16
+
17
+ useEffect(() => {
18
+ return () => {
19
+ setUpdateDialogState(undefined)
20
+ }
21
+ }, [])
22
+
23
+ return pug`
24
+ if dialog.visible
25
+ Modal(
26
+ ...dialog
27
+ visible=!!dialog.visible
28
+ onRequestClose=() => { setDialog(undefined) }
29
+ )= dialog.children
30
+ `
31
+ }
32
+
33
+ export default observer(DialogsProviderRoot) as any
@@ -0,0 +1,74 @@
1
+ import { Fragment, useState } from 'react'
2
+ import { pug } from 'startupjs'
3
+ import Button from '@startupjs-ui/button'
4
+ import Br from '@startupjs-ui/br'
5
+ import Span from '@startupjs-ui/span'
6
+ import { alert, confirm, prompt } from './index'
7
+
8
+ export function DialogsProviderSandbox () {
9
+ async function onPressAlert () {
10
+ await alert({ title: 'Alert', message: 'I am an alert box' })
11
+ }
12
+
13
+ async function onPressConfirm () {
14
+ await confirm({ title: 'Confirm', message: 'Press a button' })
15
+ }
16
+
17
+ async function onPressPrompt () {
18
+ await prompt({ title: 'Prompt', message: 'Please enter your name', defaultValue: 'Paha' })
19
+ }
20
+
21
+ return pug`
22
+ Fragment
23
+ Button(onPress=onPressAlert) Show alert
24
+ Br
25
+ Button(onPress=onPressConfirm) Show confirm
26
+ Br
27
+ Button(onPress=onPressPrompt) Show prompt
28
+ `
29
+ }
30
+
31
+ export function AlertSandbox ({ title, message }) {
32
+ async function onPress () {
33
+ await alert({ title, message })
34
+ }
35
+
36
+ return pug`
37
+ Fragment
38
+ Button(onPress=onPress) Show alert
39
+ `
40
+ }
41
+
42
+ export function ConfirmSandbox ({ title, message }) {
43
+ const [pressedButtonText, setPressedButtonText] = useState()
44
+
45
+ async function onPress () {
46
+ const isConfirmed = await confirm({ title, message })
47
+ setPressedButtonText(isConfirmed ? 'OK' : 'Cancel')
48
+ }
49
+
50
+ return pug`
51
+ Fragment
52
+ Button(onPress=onPress) Show confirm
53
+ if pressedButtonText
54
+ Br
55
+ Span= 'You pressed ' + pressedButtonText + '!'
56
+ `
57
+ }
58
+
59
+ export function PromptSandbox ({ title, message, defaultValue }) {
60
+ const [result, setResult] = useState()
61
+
62
+ async function onPress () {
63
+ const next = await prompt({ title, message, defaultValue })
64
+ setResult(next)
65
+ }
66
+
67
+ return pug`
68
+ Fragment
69
+ Button(onPress=onPress) Show prompt
70
+ if result != null
71
+ Br
72
+ Span= result === '' ? 'Empty input' : 'You typed: ' + result
73
+ `
74
+ }
package/README.mdx ADDED
@@ -0,0 +1,155 @@
1
+ import { useState } from 'react'
2
+ import { pug } from 'startupjs'
3
+ import { Sandbox } from '@startupjs-ui/docs'
4
+ import Button from '@startupjs-ui/button'
5
+ import Br from '@startupjs-ui/br'
6
+ import Span from '@startupjs-ui/span'
7
+ import { alert, confirm, prompt } from './index'
8
+ import { _PropsJsonSchema as DialogsProviderPropsJsonSchema } from './DialogsProvider'
9
+ import { _PropsJsonSchema as AlertOptionsPropsJsonSchema } from './alert'
10
+ import { _PropsJsonSchema as ConfirmOptionsPropsJsonSchema } from './confirm'
11
+ import { _PropsJsonSchema as PromptOptionsPropsJsonSchema } from './prompt'
12
+ import {
13
+ DialogsProviderSandbox,
14
+ AlertSandbox,
15
+ ConfirmSandbox,
16
+ PromptSandbox
17
+ } from './README.helpers'
18
+ import './index.mdx.cssx.styl'
19
+
20
+ # Dialogs
21
+
22
+ There are three kinds of dialog boxes: `alert`, `confirm` and `prompt`.
23
+
24
+ ```js
25
+ import { DialogsProvider, alert, confirm, prompt } from 'startupjs-ui'
26
+ ```
27
+
28
+ ## Installation
29
+
30
+ Render `DialogsProvider` once (without children) as a sibling after your main app content.
31
+
32
+ ```jsx
33
+ import { DialogsProvider, Portal } from 'startupjs-ui'
34
+ import { Stack } from 'expo-router'
35
+
36
+ export default function RootLayout () {
37
+ return (
38
+ <>
39
+ <Portal.Provider>
40
+ <Stack />
41
+ </Portal.Provider>
42
+ <DialogsProvider />
43
+ </>
44
+ )
45
+ }
46
+ ```
47
+
48
+ ## Alert box
49
+
50
+ An alert box is used if you want to display a message to the user that requires their attention.
51
+
52
+ When an alert box pops up, the user will have to click `OK` to proceed.
53
+
54
+ ```js
55
+ await alert(message | { title?, message })
56
+ ```
57
+
58
+ ```jsx example
59
+ async function onPress () {
60
+ await alert('I am an alert box')
61
+ }
62
+
63
+ return pug`
64
+ Button(onPress=onPress) Show alert box
65
+ `
66
+ ```
67
+
68
+ ## Confirm box
69
+
70
+ A confirm box is used if you want to get user's permission for something.
71
+
72
+ When a confirm box pops up, the user will have to click either `OK` or `Cancel` to proceed.
73
+
74
+ If the user clicks `OK`, the function returns `true`. If the user clicks `Cancel`, the function returns `false`.
75
+
76
+ ```js
77
+ const result = await confirm(message | { title?, message })
78
+ ```
79
+
80
+ ```jsx example
81
+ const [pressedButtonText, setPressedButtonText] = useState()
82
+
83
+ async function onPress () {
84
+ const isConfirmed = await confirm('Press a button')
85
+ setPressedButtonText(isConfirmed ? 'OK' : 'Cancel')
86
+ }
87
+
88
+ return pug`
89
+ Button(onPress=onPress) Show confirm box
90
+ if pressedButtonText
91
+ Br
92
+ Span= 'You pressed ' + pressedButtonText + '!'
93
+ `
94
+ ```
95
+
96
+ ## Prompt box
97
+
98
+ A prompt box is used if you want the user to input a value to do further actions.
99
+
100
+ When a prompt box pops up, the user will have to click either `Cancel` or enter an input value and click `OK` to proceed.
101
+
102
+ If the user clicks `OK` the function returns the input value. If the user clicks `Cancel` the function returns `null`.
103
+
104
+ ```js
105
+ const result = await prompt(message | { title?, message, defaultValue? }, defaultValue?)
106
+ ```
107
+
108
+ ```jsx example
109
+ const [name, setName] = useState()
110
+
111
+ async function onPress () {
112
+ const name = await prompt('Please enter your name', 'Paha')
113
+ setName(name)
114
+ }
115
+
116
+ return pug`
117
+ Button(onPress=onPress) Show prompt box
118
+ if name
119
+ Br
120
+ Span= 'Hello ' + name + '.'
121
+ `
122
+ ```
123
+
124
+ ## Sandbox
125
+
126
+ ### DialogsProvider
127
+
128
+ <Sandbox
129
+ Component={DialogsProviderSandbox}
130
+ propsJsonSchema={DialogsProviderPropsJsonSchema}
131
+ />
132
+
133
+ ### alert
134
+
135
+ <Sandbox
136
+ Component={AlertSandbox}
137
+ props={{ title: 'Alert', message: 'I am an alert box' }}
138
+ propsJsonSchema={AlertOptionsPropsJsonSchema}
139
+ />
140
+
141
+ ### confirm
142
+
143
+ <Sandbox
144
+ Component={ConfirmSandbox}
145
+ props={{ title: 'Confirm', message: 'Press a button' }}
146
+ propsJsonSchema={ConfirmOptionsPropsJsonSchema}
147
+ />
148
+
149
+ ### prompt
150
+
151
+ <Sandbox
152
+ Component={PromptSandbox}
153
+ props={{ title: 'Prompt', message: 'Please enter your name', defaultValue: 'Paha' }}
154
+ propsJsonSchema={PromptOptionsPropsJsonSchema}
155
+ />
package/alert.tsx ADDED
@@ -0,0 +1,43 @@
1
+ import { openDialog } from './helpers'
2
+
3
+ export const _PropsJsonSchema = {/* AlertOptions */}
4
+
5
+ export interface AlertOptions {
6
+ /** An optional dialog title displayed above the message */
7
+ title?: string
8
+ /** The message displayed inside the dialog */
9
+ message: string
10
+ }
11
+
12
+ export default async function alert (options: string | AlertOptions): Promise<void> {
13
+ let title: unknown
14
+ let message: unknown
15
+
16
+ if (typeof options === 'string') {
17
+ message = options
18
+ } else if (options && typeof options === 'object') {
19
+ title = (options as any).title
20
+ message = (options as any).message
21
+ }
22
+
23
+ if (title != null && typeof title !== 'string') {
24
+ throw new Error('[@startupjs-ui/dialogs] alert: title should be a string')
25
+ }
26
+
27
+ if (typeof message !== 'string') {
28
+ throw new Error('[@startupjs-ui/dialogs] alert: message should be a string')
29
+ }
30
+
31
+ const normalizedTitle = typeof title === 'string' ? title : undefined
32
+
33
+ await new Promise<void>(resolve => {
34
+ openDialog({
35
+ title: normalizedTitle,
36
+ children: message,
37
+ cancelLabel: 'OK',
38
+ onCancel: () => { resolve() },
39
+ showCross: false,
40
+ enableBackdropPress: false
41
+ })
42
+ })
43
+ }
package/confirm.tsx ADDED
@@ -0,0 +1,47 @@
1
+ import { openDialog } from './helpers'
2
+
3
+ export const _PropsJsonSchema = {/* ConfirmOptions */}
4
+
5
+ export interface ConfirmOptions {
6
+ /** An optional dialog title displayed above the message */
7
+ title?: string
8
+ /** The message displayed inside the dialog */
9
+ message: string
10
+ }
11
+
12
+ export default async function confirm (options: string | ConfirmOptions): Promise<boolean> {
13
+ let title: unknown
14
+ let message: unknown
15
+
16
+ if (typeof options === 'string') {
17
+ message = options
18
+ } else if (options && typeof options === 'object') {
19
+ title = (options as any).title
20
+ message = (options as any).message
21
+ }
22
+
23
+ if (title != null && typeof title !== 'string') {
24
+ throw new Error('[@startupjs-ui/dialogs] confirm: title should be a string')
25
+ }
26
+
27
+ if (typeof message !== 'string') {
28
+ throw new Error('[@startupjs-ui/dialogs] confirm: message should be a string')
29
+ }
30
+
31
+ const normalizedTitle = typeof title === 'string' ? title : undefined
32
+
33
+ const result = await new Promise<boolean>(resolve => {
34
+ openDialog({
35
+ title: normalizedTitle,
36
+ children: message,
37
+ cancelLabel: 'Cancel',
38
+ confirmLabel: 'OK',
39
+ showCross: false,
40
+ enableBackdropPress: false,
41
+ onCancel: () => { resolve(false) },
42
+ onConfirm: () => { resolve(true) }
43
+ })
44
+ })
45
+
46
+ return result
47
+ }
package/helpers.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { type ModalProps } from '@startupjs-ui/modal'
2
+
3
+ export type UpdateDialogState = (dialog?: any) => void
4
+
5
+ export let updateDialogState: UpdateDialogState | undefined
6
+
7
+ export const setUpdateDialogState = (fn?: UpdateDialogState): void => {
8
+ updateDialogState = fn
9
+ }
10
+
11
+ export type OpenDialogOptions = Omit<ModalProps, 'visible' | '$visible' | 'ref'>
12
+
13
+ export const openDialog = (options: OpenDialogOptions): void => {
14
+ if (!updateDialogState) {
15
+ throw Error(
16
+ '[@startupjs-ui/dialogs] dialogs: DialogsProvider is not mounted. ' +
17
+ 'Render <DialogsProvider /> once in your app (usually in _layout) as a sibling after your main content.'
18
+ )
19
+ }
20
+ updateDialogState({ visible: true, ...options })
21
+ }
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /* eslint-disable */
2
+ // DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
3
+
4
+ export { default as DialogsProvider, type DialogsProviderProps } from './DialogsProvider';
5
+ export { default as alert, type AlertOptions } from './alert';
6
+ export { default as confirm, type ConfirmOptions } from './confirm';
7
+ export { default as prompt, type PromptOptions } from './prompt';
@@ -0,0 +1,3 @@
1
+ .table
2
+ background-color white
3
+
package/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ export { default as DialogsProvider, type DialogsProviderProps } from './DialogsProvider'
2
+ export { default as alert, type AlertOptions } from './alert'
3
+ export { default as confirm, type ConfirmOptions } from './confirm'
4
+ export { default as prompt, type PromptOptions } from './prompt'
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@startupjs-ui/dialogs",
3
+ "version": "0.1.3",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "main": "index.tsx",
8
+ "types": "index.d.ts",
9
+ "type": "module",
10
+ "exports": {
11
+ ".": "./index.tsx",
12
+ "./DialogsProvider": "./DialogsProvider.tsx",
13
+ "./alert": "./alert.tsx",
14
+ "./confirm": "./confirm.tsx",
15
+ "./prompt": "./prompt.tsx"
16
+ },
17
+ "dependencies": {
18
+ "@startupjs-ui/br": "^0.1.3",
19
+ "@startupjs-ui/core": "^0.1.3",
20
+ "@startupjs-ui/modal": "^0.1.3",
21
+ "@startupjs-ui/span": "^0.1.3",
22
+ "@startupjs-ui/text-input": "^0.1.3"
23
+ },
24
+ "peerDependencies": {
25
+ "react": "*",
26
+ "react-native": "*",
27
+ "startupjs": "*"
28
+ },
29
+ "gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
30
+ }
package/prompt.tsx ADDED
@@ -0,0 +1,73 @@
1
+ import { type ReactNode } from 'react'
2
+ import { pug, $, observer } from 'startupjs'
3
+ import TextInput from '@startupjs-ui/text-input'
4
+ import Br from '@startupjs-ui/br'
5
+ import Span from '@startupjs-ui/span'
6
+ import { openDialog } from './helpers'
7
+
8
+ export const _PropsJsonSchema = {/* PromptOptions */}
9
+
10
+ export interface PromptOptions {
11
+ /** An optional dialog title displayed above the message */
12
+ title?: string
13
+ /** The message displayed above the input field */
14
+ message: string
15
+ /** An optional initial value for the input field */
16
+ defaultValue?: string
17
+ }
18
+
19
+ export default async function prompt (message: string, defaultValue?: string): Promise<string | null>
20
+ export default async function prompt (options: PromptOptions, defaultValue?: string): Promise<string | null>
21
+ export default async function prompt (options: string | PromptOptions, defaultValue?: string): Promise<string | null> {
22
+ let title: unknown
23
+ let message: unknown
24
+
25
+ if (typeof options === 'string') {
26
+ message = options
27
+ } else if (options && typeof options === 'object') {
28
+ title = (options as any).title
29
+ message = (options as any).message
30
+ defaultValue = defaultValue ?? (options as any).defaultValue
31
+ }
32
+
33
+ if (title != null && typeof title !== 'string') {
34
+ throw new Error('[@startupjs-ui/dialogs] prompt: title should be a string')
35
+ }
36
+
37
+ if (typeof message !== 'string') {
38
+ throw new Error('[@startupjs-ui/dialogs] prompt: message should be a string')
39
+ }
40
+
41
+ const normalizedTitle = typeof title === 'string' ? title : undefined
42
+
43
+ const result = await new Promise<string | null>(resolve => {
44
+ const $prompt = $(defaultValue)
45
+
46
+ openDialog({
47
+ title: normalizedTitle,
48
+ children: pug`
49
+ Span= message
50
+ Br(half)
51
+ TextInputWrapper($prompt=$prompt)
52
+ `,
53
+ showCross: false,
54
+ enableBackdropPress: false,
55
+ cancelLabel: 'Cancel',
56
+ confirmLabel: 'OK',
57
+ onCancel: () => { resolve(null) },
58
+ onConfirm: () => { resolve(($prompt.get() as string | undefined) ?? '') }
59
+ })
60
+ })
61
+
62
+ return result
63
+ }
64
+
65
+ // We need to make an observable wrapper so that in auto-rerenders when signal changes
66
+ const TextInputWrapper = observer(({ $prompt }: { $prompt: any }): ReactNode => {
67
+ return pug`
68
+ TextInput(
69
+ value=$prompt.get()
70
+ onChangeText=text => $prompt.set(text)
71
+ )
72
+ `
73
+ })