@startupjs-ui/modal 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 +20 -0
- package/ModalActions/index.cssx.styl +7 -0
- package/ModalActions/index.tsx +56 -0
- package/ModalContent/index.cssx.styl +2 -0
- package/ModalContent/index.tsx +45 -0
- package/ModalHeader/index.cssx.styl +20 -0
- package/ModalHeader/index.tsx +49 -0
- package/README.mdx +204 -0
- package/index.cssx.styl +34 -0
- package/index.d.ts +63 -0
- package/index.mdx.cssx.styl +2 -0
- package/index.tsx +198 -0
- package/layout.tsx +204 -0
- package/package.json +25 -0
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/modal
|
|
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
|
+
* **modal:** refactor Modal component ([e5d2901](https://github.com/startupjs/startupjs-ui/commit/e5d29013594b1898df339acfd4450450e8625bb9))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react'
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Div from '@startupjs-ui/div'
|
|
6
|
+
import Button from '@startupjs-ui/button'
|
|
7
|
+
import './index.cssx.styl'
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_CANCEL_LABEL = 'Cancel'
|
|
10
|
+
export const DEFAULT_CONFIRM_LABEL = 'Confirm'
|
|
11
|
+
|
|
12
|
+
export const _PropsJsonSchema = {/* ModalActionsProps */}
|
|
13
|
+
|
|
14
|
+
export interface ModalActionsProps {
|
|
15
|
+
/** Custom styles applied to the actions container */
|
|
16
|
+
style?: StyleProp<ViewStyle>
|
|
17
|
+
/** Custom actions content */
|
|
18
|
+
children?: ReactNode
|
|
19
|
+
/** Text for cancel button @default 'Cancel' */
|
|
20
|
+
cancelLabel?: string
|
|
21
|
+
/** Text for confirm button @default 'Confirm' */
|
|
22
|
+
confirmLabel?: string
|
|
23
|
+
/** Cancel button handler */
|
|
24
|
+
onCancel?: (event: any) => void | Promise<void>
|
|
25
|
+
/** Confirm button handler */
|
|
26
|
+
onConfirm?: (event: any) => void | Promise<void>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ModalActions ({
|
|
30
|
+
style,
|
|
31
|
+
children,
|
|
32
|
+
cancelLabel = DEFAULT_CANCEL_LABEL,
|
|
33
|
+
confirmLabel = DEFAULT_CONFIRM_LABEL,
|
|
34
|
+
onCancel,
|
|
35
|
+
onConfirm
|
|
36
|
+
}: ModalActionsProps): ReactNode {
|
|
37
|
+
return pug`
|
|
38
|
+
Div.root(row style=style align='right')
|
|
39
|
+
if children
|
|
40
|
+
= children
|
|
41
|
+
else
|
|
42
|
+
if onCancel
|
|
43
|
+
Button.action(
|
|
44
|
+
color='primary'
|
|
45
|
+
onPress=onCancel
|
|
46
|
+
)= cancelLabel
|
|
47
|
+
if onConfirm
|
|
48
|
+
Button.action(
|
|
49
|
+
color='primary'
|
|
50
|
+
variant='flat'
|
|
51
|
+
onPress=onConfirm
|
|
52
|
+
)= confirmLabel
|
|
53
|
+
`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default observer(themed('ModalActions', ModalActions))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { type ReactNode, type ComponentType } from 'react'
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Span from '@startupjs-ui/span'
|
|
6
|
+
import ScrollView from '@startupjs-ui/scroll-view'
|
|
7
|
+
import './index.cssx.styl'
|
|
8
|
+
|
|
9
|
+
export const _PropsJsonSchema = {/* ModalContentProps */}
|
|
10
|
+
|
|
11
|
+
export interface ModalContentProps {
|
|
12
|
+
/** Custom styles applied to the content */
|
|
13
|
+
style?: StyleProp<ViewStyle>
|
|
14
|
+
/** Content rendered inside modal */
|
|
15
|
+
children?: ReactNode
|
|
16
|
+
/** Component used for wrapping content @default ScrollView */
|
|
17
|
+
ContentComponent?: ComponentType<any>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ModalContent ({
|
|
21
|
+
style,
|
|
22
|
+
children,
|
|
23
|
+
ContentComponent,
|
|
24
|
+
...props
|
|
25
|
+
}: ModalContentProps): ReactNode {
|
|
26
|
+
const content = React.Children.map(children, (child): ReactNode => {
|
|
27
|
+
if (typeof child === 'string') {
|
|
28
|
+
return pug`
|
|
29
|
+
Span= child
|
|
30
|
+
`
|
|
31
|
+
}
|
|
32
|
+
return child
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (!ContentComponent) ContentComponent = ScrollView
|
|
36
|
+
|
|
37
|
+
return pug`
|
|
38
|
+
ContentComponent.root(
|
|
39
|
+
style=style
|
|
40
|
+
...props
|
|
41
|
+
)= content
|
|
42
|
+
`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default observer(themed('ModalContent', ModalContent))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
$gutter = $UI.gutters.m
|
|
2
|
+
|
|
3
|
+
.root
|
|
4
|
+
padding $gutter
|
|
5
|
+
|
|
6
|
+
&.between
|
|
7
|
+
justify-content space-between
|
|
8
|
+
|
|
9
|
+
&.right
|
|
10
|
+
justify-content flex-end
|
|
11
|
+
|
|
12
|
+
.title
|
|
13
|
+
font(h5)
|
|
14
|
+
flex 1
|
|
15
|
+
|
|
16
|
+
.close
|
|
17
|
+
margin-left 2u
|
|
18
|
+
|
|
19
|
+
.icon
|
|
20
|
+
color: var(--color-text-description)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react'
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import Div from '@startupjs-ui/div'
|
|
6
|
+
import Icon from '@startupjs-ui/icon'
|
|
7
|
+
import Span from '@startupjs-ui/span'
|
|
8
|
+
import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes'
|
|
9
|
+
import './index.cssx.styl'
|
|
10
|
+
|
|
11
|
+
export const _PropsJsonSchema = {/* ModalHeaderProps */}
|
|
12
|
+
|
|
13
|
+
export interface ModalHeaderProps {
|
|
14
|
+
/** Custom styles applied to the header container */
|
|
15
|
+
style?: StyleProp<ViewStyle>
|
|
16
|
+
/** Header content */
|
|
17
|
+
children?: ReactNode
|
|
18
|
+
/** Handler for closing cross @private */
|
|
19
|
+
onCrossPress?: (event: any) => void
|
|
20
|
+
/** Icon rendered inside close button @default faTimes */
|
|
21
|
+
closeIcon?: object
|
|
22
|
+
/** Style applied to the close icon */
|
|
23
|
+
iconStyle?: StyleProp<ViewStyle>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ModalHeader ({
|
|
27
|
+
style,
|
|
28
|
+
children,
|
|
29
|
+
onCrossPress, // @private
|
|
30
|
+
closeIcon = faTimes,
|
|
31
|
+
iconStyle
|
|
32
|
+
}: ModalHeaderProps): ReactNode {
|
|
33
|
+
return pug`
|
|
34
|
+
Div.root(row style=style styleName=children ? 'between' : 'right' vAlign='center')
|
|
35
|
+
if typeof children === 'string'
|
|
36
|
+
Span.title(numberOfLines=1)= children
|
|
37
|
+
else
|
|
38
|
+
= children
|
|
39
|
+
if onCrossPress
|
|
40
|
+
Div.close(onPress=onCrossPress)
|
|
41
|
+
Icon.icon(
|
|
42
|
+
style=iconStyle
|
|
43
|
+
icon=closeIcon
|
|
44
|
+
size='xl'
|
|
45
|
+
)
|
|
46
|
+
`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default observer(themed('ModalHeader', ModalHeader))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { useState, useRef } from 'react'
|
|
2
|
+
import { $ } from 'startupjs'
|
|
3
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
4
|
+
import Modal, {
|
|
5
|
+
_PropsJsonSchema as ModalPropsJsonSchema
|
|
6
|
+
} from './index'
|
|
7
|
+
import { _PropsJsonSchema as ModalHeaderPropsJsonSchema } from './ModalHeader'
|
|
8
|
+
import { _PropsJsonSchema as ModalContentPropsJsonSchema } from './ModalContent'
|
|
9
|
+
import { _PropsJsonSchema as ModalActionsPropsJsonSchema } from './ModalActions'
|
|
10
|
+
import Button from '@startupjs-ui/button'
|
|
11
|
+
import Span from '@startupjs-ui/span'
|
|
12
|
+
import { Table, Thead, Tbody, Tr, Th, Td } from '@startupjs-ui/table'
|
|
13
|
+
import './index.mdx.cssx.styl'
|
|
14
|
+
|
|
15
|
+
# Modal
|
|
16
|
+
|
|
17
|
+
Inherits [React Native Modal](https://reactnative.dev/docs/modal).
|
|
18
|
+
|
|
19
|
+
Modal can be used when the user needs to inform about critical information, require decisions or interact a complex sub-application without navigating to a new page or interrupting the workflow.
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { Modal } from 'startupjs-ui'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Simple example
|
|
26
|
+
|
|
27
|
+
```jsx example
|
|
28
|
+
const [visible, setVisible] = useState(false)
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<Modal
|
|
33
|
+
visible={visible}
|
|
34
|
+
title='Text in modal'
|
|
35
|
+
onRequestClose={setVisible}
|
|
36
|
+
>
|
|
37
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
38
|
+
</Modal>
|
|
39
|
+
<Button onPress={() => setVisible(true)}>
|
|
40
|
+
Open modal
|
|
41
|
+
</Button>
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Managing visibility
|
|
47
|
+
|
|
48
|
+
There are three options for managing visiblity of a modal:
|
|
49
|
+
|
|
50
|
+
1. By passing the scoped model to the `$visible` property from the state of which visibility is controlled.
|
|
51
|
+
|
|
52
|
+
```jsx example
|
|
53
|
+
const $visible = $()
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<React.Fragment>
|
|
57
|
+
<Button onPress={() => $visible.set(true)}>
|
|
58
|
+
Open
|
|
59
|
+
</Button>
|
|
60
|
+
<Modal
|
|
61
|
+
title='Example'
|
|
62
|
+
$visible={$visible}
|
|
63
|
+
>
|
|
64
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
65
|
+
</Modal>
|
|
66
|
+
</React.Fragment>
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. By passing the `visible` property that determines whether modal is visible.
|
|
71
|
+
|
|
72
|
+
```jsx example
|
|
73
|
+
const [visible, setVisible] = useState(false)
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<React.Fragment>
|
|
77
|
+
<Button onPress={() => setVisible(true)}>
|
|
78
|
+
Open
|
|
79
|
+
</Button>
|
|
80
|
+
<Modal
|
|
81
|
+
title='Example'
|
|
82
|
+
visible={visible}
|
|
83
|
+
onRequestClose={() => setVisible(false)}
|
|
84
|
+
>
|
|
85
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
86
|
+
</Modal>
|
|
87
|
+
</React.Fragment>
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
3. By passing `ref`, which will receive the `open()` and `close()` methods to control visibility.
|
|
92
|
+
|
|
93
|
+
```jsx example
|
|
94
|
+
const modalRef = useRef()
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<React.Fragment>
|
|
98
|
+
<Button onPress={() => modalRef.current.open()}>
|
|
99
|
+
Open
|
|
100
|
+
</Button>
|
|
101
|
+
<Modal
|
|
102
|
+
title='Example'
|
|
103
|
+
ref={modalRef}
|
|
104
|
+
>
|
|
105
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
106
|
+
</Modal>
|
|
107
|
+
</React.Fragment>
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Fullscreen modal
|
|
112
|
+
|
|
113
|
+
By default the modal shows like window in center of the page. To make it fullscreen, you need pass the string `fullscreen` to the `variant` property.
|
|
114
|
+
|
|
115
|
+
```jsx example
|
|
116
|
+
const $visible = $(false)
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<React.Fragment>
|
|
120
|
+
<Button onPress={() => $visible.set(true)}>
|
|
121
|
+
Open fullscreen modal
|
|
122
|
+
</Button>
|
|
123
|
+
<Modal
|
|
124
|
+
variant='fullscreen'
|
|
125
|
+
title='Fullscreen example'
|
|
126
|
+
$visible={$visible}
|
|
127
|
+
onCancel={() => console.log('onCancel')}
|
|
128
|
+
onConfirm={() => console.log('onConfirm')}
|
|
129
|
+
>
|
|
130
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
131
|
+
</Modal>
|
|
132
|
+
</React.Fragment>
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Advanced usage
|
|
137
|
+
|
|
138
|
+
Modal consists of three parts - `Header`, `Content` and `Actions`. These parts can be used to add custom markup, the `Header` is used instead of `title`, the `Content` is used instead of children and the `Actions` is used instead of handlers `onCancel`, `onConfirm`. They can be used separately.
|
|
139
|
+
|
|
140
|
+
```jsx example
|
|
141
|
+
const $visible = $(false)
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<React.Fragment>
|
|
145
|
+
<Button onPress={() => $visible.set(true)}>
|
|
146
|
+
Open advanced modal
|
|
147
|
+
</Button>
|
|
148
|
+
<Modal $visible={$visible}>
|
|
149
|
+
<Modal.Header>Advanced modal</Modal.Header>
|
|
150
|
+
<Modal.Content>
|
|
151
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.
|
|
152
|
+
</Modal.Content>
|
|
153
|
+
<Modal.Actions>
|
|
154
|
+
<Button
|
|
155
|
+
shape='circle'
|
|
156
|
+
onPress={() => $visible.set(false)}
|
|
157
|
+
>
|
|
158
|
+
Custom close
|
|
159
|
+
</Button>
|
|
160
|
+
<Button
|
|
161
|
+
pushed
|
|
162
|
+
shape='circle'
|
|
163
|
+
onPress={() => $visible.set(false)}
|
|
164
|
+
>
|
|
165
|
+
Custom confirm
|
|
166
|
+
</Button>
|
|
167
|
+
</Modal.Actions>
|
|
168
|
+
</Modal>
|
|
169
|
+
</React.Fragment>
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Sandbox
|
|
174
|
+
|
|
175
|
+
### Modal
|
|
176
|
+
|
|
177
|
+
<Sandbox
|
|
178
|
+
Component={Modal}
|
|
179
|
+
propsJsonSchema={ModalPropsJsonSchema}
|
|
180
|
+
props={{ /* $visible: $.session.Props.Modal.visible */ }}
|
|
181
|
+
$props={$.session.Props.Modal}
|
|
182
|
+
{...{ _comment: "HACK to make alive sandbox when user click on 'visible' checkbox" }}
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
### Modal.Header
|
|
186
|
+
|
|
187
|
+
<Sandbox
|
|
188
|
+
Component={Modal.Header}
|
|
189
|
+
propsJsonSchema={ModalHeaderPropsJsonSchema}
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
### Modal.Content
|
|
193
|
+
|
|
194
|
+
<Sandbox
|
|
195
|
+
Component={Modal.Content}
|
|
196
|
+
propsJsonSchema={ModalContentPropsJsonSchema}
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
### Modal.Actions
|
|
200
|
+
|
|
201
|
+
<Sandbox
|
|
202
|
+
Component={Modal.Actions}
|
|
203
|
+
propsJsonSchema={ModalActionsPropsJsonSchema}
|
|
204
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
$modalBg = var(--color-bg-main-strong)
|
|
2
|
+
$gutter = $UI.gutters.m
|
|
3
|
+
|
|
4
|
+
.root
|
|
5
|
+
height 100%
|
|
6
|
+
|
|
7
|
+
&.window
|
|
8
|
+
padding 0 $gutter
|
|
9
|
+
|
|
10
|
+
.overlay
|
|
11
|
+
position absolute
|
|
12
|
+
top 0
|
|
13
|
+
right 0
|
|
14
|
+
left 0
|
|
15
|
+
bottom 0
|
|
16
|
+
background-color var(--Modal-overlayBg)
|
|
17
|
+
|
|
18
|
+
+web()
|
|
19
|
+
cursor pointer
|
|
20
|
+
|
|
21
|
+
.modal
|
|
22
|
+
flex-shrink 1
|
|
23
|
+
background-color $modalBg
|
|
24
|
+
|
|
25
|
+
&.window
|
|
26
|
+
margin auto
|
|
27
|
+
max-height 90%
|
|
28
|
+
max-width 776px
|
|
29
|
+
min-width 280px
|
|
30
|
+
border-radius 4px
|
|
31
|
+
shadow(4)
|
|
32
|
+
|
|
33
|
+
&.fullscreen
|
|
34
|
+
height 100%
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type ReactNode, type ComponentType, type RefObject } from 'react';
|
|
5
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
6
|
+
type SupportedOrientation = 'portrait' | 'portrait-upside-down' | 'landscape' | 'landscape-left' | 'landscape-right';
|
|
7
|
+
export declare const _PropsJsonSchema: {};
|
|
8
|
+
export interface ModalProps {
|
|
9
|
+
/** Custom styles applied to the root view */
|
|
10
|
+
style?: StyleProp<ViewStyle>;
|
|
11
|
+
/** Custom styles applied to the modal content container */
|
|
12
|
+
modalStyle?: StyleProp<ViewStyle>;
|
|
13
|
+
/** Content rendered inside the modal */
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
/** Layout variant @default 'window' */
|
|
16
|
+
variant?: 'window' | 'fullscreen';
|
|
17
|
+
/** Controlled visibility flag */
|
|
18
|
+
visible?: boolean;
|
|
19
|
+
/** Model binding for two-way visibility control */
|
|
20
|
+
$visible?: any;
|
|
21
|
+
/** Imperative ref to open/close modal when not controlled */
|
|
22
|
+
ref?: RefObject<any>;
|
|
23
|
+
/** Header title text */
|
|
24
|
+
title?: string;
|
|
25
|
+
/** Label for cancel action @default 'Cancel' */
|
|
26
|
+
cancelLabel?: string;
|
|
27
|
+
/** Label for confirm action @default 'Confirm' */
|
|
28
|
+
confirmLabel?: string;
|
|
29
|
+
/** Show a cross in the header @default true */
|
|
30
|
+
showCross?: boolean;
|
|
31
|
+
/** Makes the backdrop clickable @default true */
|
|
32
|
+
enableBackdropPress?: boolean;
|
|
33
|
+
/** Component used as modal container @default SafeAreaView */
|
|
34
|
+
ModalElement?: ComponentType<any>;
|
|
35
|
+
/** Modal animation type @default 'fade' */
|
|
36
|
+
animationType?: 'slide' | 'fade' | 'none';
|
|
37
|
+
/** Render modal with transparent background @default true */
|
|
38
|
+
transparent?: boolean;
|
|
39
|
+
/** Control status bar translucency on Android */
|
|
40
|
+
statusBarTranslucent?: boolean;
|
|
41
|
+
/** Allowed screen orientations */
|
|
42
|
+
supportedOrientations?: SupportedOrientation[];
|
|
43
|
+
/** Callback fired after modal becomes visible */
|
|
44
|
+
onShow?: () => void;
|
|
45
|
+
/** Called when user clicks on the cross */
|
|
46
|
+
onCrossPress?: (event: any) => void | Promise<void>;
|
|
47
|
+
/** Show only the one `OK` button that uses this handler */
|
|
48
|
+
onCancel?: (event: any) => void | Promise<void>;
|
|
49
|
+
/** Show two buttons: `OK` uses this handler and `Cancel` uses the `onCancel` handler */
|
|
50
|
+
onConfirm?: (event: any) => void | Promise<void>;
|
|
51
|
+
/** Called when the user clicks on the backdrop (requires enableBackdropPress to be true) */
|
|
52
|
+
onBackdropPress?: (event: any) => void | Promise<void>;
|
|
53
|
+
/** Called when the orientation changes while the modal is displayed */
|
|
54
|
+
onOrientationChange?: (event: any) => void;
|
|
55
|
+
/** Called when user requests to close the modal (hardware back, menu button, Esc, etc.). Also fired after cross/backdrop/cancel/confirm unless handlers call event.preventDefault() */
|
|
56
|
+
onRequestClose?: (value?: boolean) => void;
|
|
57
|
+
/** Called once the modal has been dismissed */
|
|
58
|
+
onDismiss?: () => void;
|
|
59
|
+
/** DEPRECATED: use onRequestClose instead */
|
|
60
|
+
onChange?: (value?: boolean) => void;
|
|
61
|
+
}
|
|
62
|
+
declare const ObservedModal: any;
|
|
63
|
+
export default ObservedModal;
|
package/index.tsx
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useImperativeHandle,
|
|
4
|
+
type ReactNode,
|
|
5
|
+
type ComponentType,
|
|
6
|
+
type RefObject
|
|
7
|
+
} from 'react'
|
|
8
|
+
import { SafeAreaView, Modal as RNModal, type StyleProp, type ViewStyle } from 'react-native'
|
|
9
|
+
import { pug, observer, $ } from 'startupjs'
|
|
10
|
+
import { themed } from '@startupjs-ui/core'
|
|
11
|
+
import Portal from '@startupjs-ui/portal'
|
|
12
|
+
import Layout from './layout'
|
|
13
|
+
import ModalHeader from './ModalHeader'
|
|
14
|
+
import ModalContent from './ModalContent'
|
|
15
|
+
import ModalActions, { DEFAULT_CANCEL_LABEL, DEFAULT_CONFIRM_LABEL } from './ModalActions'
|
|
16
|
+
|
|
17
|
+
type SupportedOrientation =
|
|
18
|
+
| 'portrait'
|
|
19
|
+
| 'portrait-upside-down'
|
|
20
|
+
| 'landscape'
|
|
21
|
+
| 'landscape-left'
|
|
22
|
+
| 'landscape-right'
|
|
23
|
+
|
|
24
|
+
const SUPPORTED_ORIENTATIONS: SupportedOrientation[] = [
|
|
25
|
+
'portrait',
|
|
26
|
+
'portrait-upside-down',
|
|
27
|
+
'landscape',
|
|
28
|
+
'landscape-left',
|
|
29
|
+
'landscape-right'
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
export const _PropsJsonSchema = {/* ModalProps */}
|
|
33
|
+
|
|
34
|
+
export interface ModalProps {
|
|
35
|
+
/** Custom styles applied to the root view */
|
|
36
|
+
style?: StyleProp<ViewStyle>
|
|
37
|
+
/** Custom styles applied to the modal content container */
|
|
38
|
+
modalStyle?: StyleProp<ViewStyle>
|
|
39
|
+
/** Content rendered inside the modal */
|
|
40
|
+
children?: ReactNode
|
|
41
|
+
/** Layout variant @default 'window' */
|
|
42
|
+
variant?: 'window' | 'fullscreen'
|
|
43
|
+
/** Controlled visibility flag */
|
|
44
|
+
visible?: boolean
|
|
45
|
+
/** Model binding for two-way visibility control */
|
|
46
|
+
$visible?: any
|
|
47
|
+
/** Imperative ref to open/close modal when not controlled */
|
|
48
|
+
ref?: RefObject<any>
|
|
49
|
+
/** Header title text */
|
|
50
|
+
title?: string
|
|
51
|
+
/** Label for cancel action @default 'Cancel' */
|
|
52
|
+
cancelLabel?: string
|
|
53
|
+
/** Label for confirm action @default 'Confirm' */
|
|
54
|
+
confirmLabel?: string
|
|
55
|
+
/** Show a cross in the header @default true */
|
|
56
|
+
showCross?: boolean
|
|
57
|
+
/** Makes the backdrop clickable @default true */
|
|
58
|
+
enableBackdropPress?: boolean
|
|
59
|
+
/** Component used as modal container @default SafeAreaView */
|
|
60
|
+
ModalElement?: ComponentType<any>
|
|
61
|
+
/** Modal animation type @default 'fade' */
|
|
62
|
+
animationType?: 'slide' | 'fade' | 'none'
|
|
63
|
+
/** Render modal with transparent background @default true */
|
|
64
|
+
transparent?: boolean
|
|
65
|
+
/** Control status bar translucency on Android */
|
|
66
|
+
statusBarTranslucent?: boolean
|
|
67
|
+
/** Allowed screen orientations */
|
|
68
|
+
supportedOrientations?: SupportedOrientation[]
|
|
69
|
+
/** Callback fired after modal becomes visible */
|
|
70
|
+
onShow?: () => void
|
|
71
|
+
/** Called when user clicks on the cross */
|
|
72
|
+
onCrossPress?: (event: any) => void | Promise<void>
|
|
73
|
+
/** Show only the one `OK` button that uses this handler */
|
|
74
|
+
onCancel?: (event: any) => void | Promise<void>
|
|
75
|
+
/** Show two buttons: `OK` uses this handler and `Cancel` uses the `onCancel` handler */
|
|
76
|
+
onConfirm?: (event: any) => void | Promise<void>
|
|
77
|
+
/** Called when the user clicks on the backdrop (requires enableBackdropPress to be true) */
|
|
78
|
+
onBackdropPress?: (event: any) => void | Promise<void>
|
|
79
|
+
/** Called when the orientation changes while the modal is displayed */
|
|
80
|
+
onOrientationChange?: (event: any) => void
|
|
81
|
+
/** Called when user requests to close the modal (hardware back, menu button, Esc, etc.). Also fired after cross/backdrop/cancel/confirm unless handlers call event.preventDefault() */
|
|
82
|
+
onRequestClose?: (value?: boolean) => void
|
|
83
|
+
/** Called once the modal has been dismissed */
|
|
84
|
+
onDismiss?: () => void
|
|
85
|
+
/** DEPRECATED: use onRequestClose instead */
|
|
86
|
+
onChange?: (value?: boolean) => void
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ModalRoot ({
|
|
90
|
+
style,
|
|
91
|
+
modalStyle,
|
|
92
|
+
children,
|
|
93
|
+
variant = 'window',
|
|
94
|
+
visible,
|
|
95
|
+
$visible,
|
|
96
|
+
ref,
|
|
97
|
+
title,
|
|
98
|
+
cancelLabel = DEFAULT_CANCEL_LABEL,
|
|
99
|
+
confirmLabel = DEFAULT_CONFIRM_LABEL,
|
|
100
|
+
showCross = true,
|
|
101
|
+
enableBackdropPress = true,
|
|
102
|
+
ModalElement = SafeAreaView,
|
|
103
|
+
animationType = 'fade',
|
|
104
|
+
transparent = true,
|
|
105
|
+
supportedOrientations = SUPPORTED_ORIENTATIONS,
|
|
106
|
+
statusBarTranslucent,
|
|
107
|
+
onChange, // DEPRECATED
|
|
108
|
+
onRequestClose,
|
|
109
|
+
onDismiss,
|
|
110
|
+
onShow,
|
|
111
|
+
onCrossPress,
|
|
112
|
+
onBackdropPress,
|
|
113
|
+
onCancel,
|
|
114
|
+
onConfirm,
|
|
115
|
+
onOrientationChange,
|
|
116
|
+
...props
|
|
117
|
+
}: ModalProps): ReactNode {
|
|
118
|
+
if (onChange) {
|
|
119
|
+
console.warn('[@startupjs/ui] Modal: onChange is DEPRECATED, use onRequestClose instead.')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const _$visible = $() // used for uncontrolled mode
|
|
123
|
+
if (visible == null) {
|
|
124
|
+
if ($visible) {
|
|
125
|
+
visible = $visible.get()
|
|
126
|
+
} else if (_$visible.get()) {
|
|
127
|
+
visible = _$visible.get()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// WORKAROUND
|
|
132
|
+
// convert 'visible' to boolean
|
|
133
|
+
// because modal window appears for undefined value on web
|
|
134
|
+
visible = !!visible
|
|
135
|
+
|
|
136
|
+
const _onRequestClose = useCallback(() => {
|
|
137
|
+
onRequestClose?.()
|
|
138
|
+
if ($visible) {
|
|
139
|
+
$visible.del()
|
|
140
|
+
} else {
|
|
141
|
+
_$visible.del()
|
|
142
|
+
}
|
|
143
|
+
}, [onRequestClose, $visible, _$visible])
|
|
144
|
+
|
|
145
|
+
useImperativeHandle(ref, () => ({
|
|
146
|
+
open: () => {
|
|
147
|
+
if ($visible) {
|
|
148
|
+
$visible.set(true)
|
|
149
|
+
} else {
|
|
150
|
+
_$visible.set(true)
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
close: () => {
|
|
154
|
+
_onRequestClose()
|
|
155
|
+
}
|
|
156
|
+
}), [_onRequestClose, $visible, _$visible])
|
|
157
|
+
|
|
158
|
+
return pug`
|
|
159
|
+
RNModal(
|
|
160
|
+
visible=visible
|
|
161
|
+
transparent=transparent
|
|
162
|
+
supportedOrientations=supportedOrientations
|
|
163
|
+
animationType=animationType
|
|
164
|
+
statusBarTranslucent=statusBarTranslucent
|
|
165
|
+
onRequestClose=_onRequestClose
|
|
166
|
+
onOrientationChange=onOrientationChange
|
|
167
|
+
onShow=onShow
|
|
168
|
+
onDismiss=onDismiss
|
|
169
|
+
...props
|
|
170
|
+
)
|
|
171
|
+
Portal.Provider
|
|
172
|
+
if visible
|
|
173
|
+
Layout(
|
|
174
|
+
style=style
|
|
175
|
+
modalStyle=modalStyle
|
|
176
|
+
variant=variant
|
|
177
|
+
title=title
|
|
178
|
+
cancelLabel=cancelLabel
|
|
179
|
+
confirmLabel=confirmLabel
|
|
180
|
+
showCross=showCross
|
|
181
|
+
enableBackdropPress=enableBackdropPress
|
|
182
|
+
ModalElement=ModalElement
|
|
183
|
+
onRequestClose=_onRequestClose
|
|
184
|
+
onCrossPress=onCrossPress
|
|
185
|
+
onBackdropPress=onBackdropPress
|
|
186
|
+
onCancel=onCancel
|
|
187
|
+
onConfirm=onConfirm
|
|
188
|
+
)= children
|
|
189
|
+
`
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const ObservedModal = observer(themed('Modal', ModalRoot)) as any
|
|
193
|
+
|
|
194
|
+
ObservedModal.Header = ModalHeader
|
|
195
|
+
ObservedModal.Content = ModalContent
|
|
196
|
+
ObservedModal.Actions = ModalActions
|
|
197
|
+
|
|
198
|
+
export default ObservedModal
|
package/layout.tsx
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import React, { type ReactNode, type ComponentType } from 'react'
|
|
2
|
+
import { View, TouchableOpacity, type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed } from '@startupjs-ui/core'
|
|
5
|
+
import ModalHeader from './ModalHeader'
|
|
6
|
+
import ModalContent from './ModalContent'
|
|
7
|
+
import ModalActions, { DEFAULT_CANCEL_LABEL, DEFAULT_CONFIRM_LABEL } from './ModalActions'
|
|
8
|
+
import './index.cssx.styl'
|
|
9
|
+
|
|
10
|
+
export interface ModalLayoutProps {
|
|
11
|
+
/** Custom styles applied to the root view */
|
|
12
|
+
style?: StyleProp<ViewStyle>
|
|
13
|
+
/** Custom styles applied to the modal surface */
|
|
14
|
+
modalStyle?: StyleProp<ViewStyle>
|
|
15
|
+
/** Children rendered inside modal sections */
|
|
16
|
+
children?: ReactNode
|
|
17
|
+
/** Layout variant @default 'window' */
|
|
18
|
+
variant?: 'window' | 'fullscreen'
|
|
19
|
+
/** Title rendered when no custom header provided */
|
|
20
|
+
title?: string
|
|
21
|
+
/** DEPRECATED: use cancelLabel instead */
|
|
22
|
+
dismissLabel?: string
|
|
23
|
+
/** Cancel action label @default 'Cancel' */
|
|
24
|
+
cancelLabel?: string
|
|
25
|
+
/** Confirm action label @default 'Confirm' */
|
|
26
|
+
confirmLabel?: string
|
|
27
|
+
/** Component used to wrap modal content */
|
|
28
|
+
ModalElement?: ComponentType<any>
|
|
29
|
+
/** Show cross button in header */
|
|
30
|
+
showCross?: boolean
|
|
31
|
+
/** Enable closing modal by tapping backdrop */
|
|
32
|
+
enableBackdropPress?: boolean
|
|
33
|
+
/** Request close handler */
|
|
34
|
+
onRequestClose?: () => void
|
|
35
|
+
/** Cross press handler */
|
|
36
|
+
onCrossPress?: (event: any) => void | Promise<void>
|
|
37
|
+
/** Backdrop press handler */
|
|
38
|
+
onBackdropPress?: (event: any) => void | Promise<void>
|
|
39
|
+
/** Cancel action handler */
|
|
40
|
+
onCancel?: (event: any) => void | Promise<void>
|
|
41
|
+
/** Confirm action handler */
|
|
42
|
+
onConfirm?: (event: any) => void | Promise<void>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Modal ({
|
|
46
|
+
style,
|
|
47
|
+
modalStyle,
|
|
48
|
+
children,
|
|
49
|
+
variant,
|
|
50
|
+
title,
|
|
51
|
+
dismissLabel,
|
|
52
|
+
cancelLabel = DEFAULT_CANCEL_LABEL,
|
|
53
|
+
confirmLabel = DEFAULT_CONFIRM_LABEL,
|
|
54
|
+
ModalElement,
|
|
55
|
+
showCross,
|
|
56
|
+
enableBackdropPress,
|
|
57
|
+
onRequestClose,
|
|
58
|
+
onCrossPress,
|
|
59
|
+
onBackdropPress,
|
|
60
|
+
onCancel,
|
|
61
|
+
onConfirm
|
|
62
|
+
}: ModalLayoutProps): ReactNode {
|
|
63
|
+
// DEPRECATED
|
|
64
|
+
if (dismissLabel) {
|
|
65
|
+
console.warn(
|
|
66
|
+
'[@startupjs/ui] Modal: dismissLabel is DEPRECATED, use cancelLabel instead'
|
|
67
|
+
)
|
|
68
|
+
cancelLabel = dismissLabel
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Deconstruct template variables
|
|
72
|
+
let header: ReactNode | undefined
|
|
73
|
+
let actions: ReactNode | undefined
|
|
74
|
+
let content: ReactNode | undefined
|
|
75
|
+
const contentChildren: ReactNode[] = []
|
|
76
|
+
|
|
77
|
+
React.Children.forEach(children, child => {
|
|
78
|
+
if (!child) return
|
|
79
|
+
|
|
80
|
+
switch ((child as any).type) {
|
|
81
|
+
case ModalHeader:
|
|
82
|
+
if (header) throw Error('[ui -> Modal] You must specify a single <Modal.Header>')
|
|
83
|
+
header = child
|
|
84
|
+
break
|
|
85
|
+
case ModalActions:
|
|
86
|
+
if (actions) throw Error('[ui -> Modal] You must specify a single <Modal.Actions>')
|
|
87
|
+
actions = child
|
|
88
|
+
break
|
|
89
|
+
case ModalContent:
|
|
90
|
+
if (content) throw Error('[ui -> Modal] You must specify a single <Modal.Content>')
|
|
91
|
+
content = child
|
|
92
|
+
break
|
|
93
|
+
default:
|
|
94
|
+
contentChildren.push(child)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
if (content && contentChildren.length > 0) {
|
|
99
|
+
throw Error('[ui -> Modal] React elements found directly within <Modal>. ' +
|
|
100
|
+
'If <Modal.Content> is specified, you have to put all your content inside it')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let _onConfirm
|
|
104
|
+
let _onCancel
|
|
105
|
+
const isWindowLayout = variant === 'window'
|
|
106
|
+
const hasActions = !!onCancel || !!onConfirm
|
|
107
|
+
const hasHeader = !!title || !!showCross
|
|
108
|
+
|
|
109
|
+
const _onCrossPress = async (event: any) => {
|
|
110
|
+
event.persist() // TODO: remove in react 17
|
|
111
|
+
const promise: any = onCrossPress?.(event)
|
|
112
|
+
if (promise && typeof promise.then === 'function') await promise
|
|
113
|
+
if (event.defaultPrevented) return
|
|
114
|
+
if (onRequestClose) onRequestClose()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const _onBackdropPress = async (event: any) => {
|
|
118
|
+
event.persist() // TODO: remove in react 17
|
|
119
|
+
const promise: any = onBackdropPress?.(event)
|
|
120
|
+
if (promise && typeof promise.then === 'function') await promise
|
|
121
|
+
if (event.defaultPrevented) return
|
|
122
|
+
if (onRequestClose) onRequestClose()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (onConfirm) {
|
|
126
|
+
_onConfirm = async (event: any) => {
|
|
127
|
+
event.persist() // TODO: remove in react 17
|
|
128
|
+
const promise: any = onConfirm(event)
|
|
129
|
+
if (promise && typeof promise.then === 'function') await promise
|
|
130
|
+
if (event.defaultPrevented) return
|
|
131
|
+
if (onRequestClose) onRequestClose()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (hasActions) {
|
|
136
|
+
_onCancel = async (event: any) => {
|
|
137
|
+
event.persist() // TODO: remove in react 17
|
|
138
|
+
const promise: any = onCancel?.(event)
|
|
139
|
+
if (promise && typeof promise.then === 'function') await promise
|
|
140
|
+
if (event.defaultPrevented) return
|
|
141
|
+
if (onRequestClose) onRequestClose()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!onConfirm && cancelLabel === DEFAULT_CANCEL_LABEL) {
|
|
146
|
+
cancelLabel = 'OK'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Handle <Modal.Header>
|
|
150
|
+
const headerProps = {
|
|
151
|
+
onCrossPress: showCross ? _onCrossPress : undefined
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
header = header
|
|
155
|
+
? React.cloneElement(header as any, { ...headerProps, ...(header as any).props })
|
|
156
|
+
: hasHeader
|
|
157
|
+
? React.createElement(ModalHeader, headerProps, title)
|
|
158
|
+
: null
|
|
159
|
+
|
|
160
|
+
// Handle <Modal.Actions>
|
|
161
|
+
const actionsProps = {
|
|
162
|
+
cancelLabel,
|
|
163
|
+
confirmLabel,
|
|
164
|
+
onCancel: _onCancel,
|
|
165
|
+
onConfirm: _onConfirm
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
actions = actions
|
|
169
|
+
? React.cloneElement(actions as any, { ...actionsProps, ...(actions as any).props })
|
|
170
|
+
: hasActions
|
|
171
|
+
? React.createElement(ModalActions, actionsProps)
|
|
172
|
+
: null
|
|
173
|
+
|
|
174
|
+
// Handle <Modal.Content>
|
|
175
|
+
const contentStyle: StyleProp<ViewStyle> = {}
|
|
176
|
+
|
|
177
|
+
if (header) contentStyle.paddingTop = 0
|
|
178
|
+
if (actions) contentStyle.paddingBottom = 0
|
|
179
|
+
|
|
180
|
+
const contentProps = { variant, style: contentStyle }
|
|
181
|
+
|
|
182
|
+
// content part should always present
|
|
183
|
+
content = content
|
|
184
|
+
? React.cloneElement(content as any, { ...contentProps, ...(content as any).props })
|
|
185
|
+
: React.createElement(ModalContent, contentProps, contentChildren)
|
|
186
|
+
|
|
187
|
+
return pug`
|
|
188
|
+
View.root(style=style styleName=[variant])
|
|
189
|
+
if isWindowLayout
|
|
190
|
+
TouchableOpacity.overlay(
|
|
191
|
+
activeOpacity=1
|
|
192
|
+
onPress=enableBackdropPress ? _onBackdropPress : undefined
|
|
193
|
+
)
|
|
194
|
+
ModalElement.modal(
|
|
195
|
+
style=modalStyle
|
|
196
|
+
styleName=[variant]
|
|
197
|
+
)
|
|
198
|
+
= header
|
|
199
|
+
= content
|
|
200
|
+
= actions
|
|
201
|
+
`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default observer(themed('Modal', Modal))
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/modal",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"main": "index.tsx",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@startupjs-ui/button": "^0.1.3",
|
|
12
|
+
"@startupjs-ui/core": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
14
|
+
"@startupjs-ui/icon": "^0.1.3",
|
|
15
|
+
"@startupjs-ui/portal": "^0.1.3",
|
|
16
|
+
"@startupjs-ui/scroll-view": "^0.1.3",
|
|
17
|
+
"@startupjs-ui/span": "^0.1.3"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "*",
|
|
21
|
+
"react-native": "*",
|
|
22
|
+
"startupjs": "*"
|
|
23
|
+
},
|
|
24
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
25
|
+
}
|