@startupjs-ui/menu 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/MenuItem/index.cssx.styl +28 -0
- package/MenuItem/index.tsx +83 -0
- package/README.mdx +311 -0
- package/context.ts +10 -0
- package/index.cssx.styl +3 -0
- package/index.d.ts +25 -0
- package/index.tsx +55 -0
- package/package.json +23 -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/menu
|
|
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
|
+
* **menu:** refactor Menu component. Fix useColors types. ([ffd4671](https://github.com/startupjs/startupjs-ui/commit/ffd46719c308f99d856d8020ebfc070bb479ab28))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
_borderSize = 2px
|
|
2
|
+
|
|
3
|
+
.border
|
|
4
|
+
position absolute
|
|
5
|
+
|
|
6
|
+
&.top
|
|
7
|
+
&.bottom
|
|
8
|
+
left 0
|
|
9
|
+
right 0
|
|
10
|
+
height _borderSize
|
|
11
|
+
|
|
12
|
+
&.top
|
|
13
|
+
top 0
|
|
14
|
+
|
|
15
|
+
&.bottom
|
|
16
|
+
bottom 0
|
|
17
|
+
|
|
18
|
+
&.left
|
|
19
|
+
&.right
|
|
20
|
+
top 0
|
|
21
|
+
bottom 0
|
|
22
|
+
width _borderSize
|
|
23
|
+
|
|
24
|
+
&.left
|
|
25
|
+
left 0
|
|
26
|
+
|
|
27
|
+
&.right
|
|
28
|
+
right 0
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useContext, type ReactNode } from 'react'
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native'
|
|
3
|
+
import { pug, observer } from 'startupjs'
|
|
4
|
+
import { themed, useColors } from '@startupjs-ui/core'
|
|
5
|
+
import Div from '@startupjs-ui/div'
|
|
6
|
+
import Icon, { type IconProps } from '@startupjs-ui/icon'
|
|
7
|
+
import Item, { type ItemProps } from '@startupjs-ui/item'
|
|
8
|
+
import Span from '@startupjs-ui/span'
|
|
9
|
+
import MenuContext from '../context'
|
|
10
|
+
import './index.cssx.styl'
|
|
11
|
+
|
|
12
|
+
export const _PropsJsonSchema = {/* MenuItemProps */} // used in docs generation
|
|
13
|
+
|
|
14
|
+
export interface MenuItemProps extends Omit<ItemProps, 'style' | 'onPress' | 'icon' | 'to'> {
|
|
15
|
+
/** Custom styles applied to the content wrapper */
|
|
16
|
+
containerStyle?: StyleProp<ViewStyle>
|
|
17
|
+
/** Content rendered inside the item */
|
|
18
|
+
children?: ReactNode
|
|
19
|
+
/** Highlight item as active @default false */
|
|
20
|
+
active?: boolean
|
|
21
|
+
/** Active border position @default 'none' */
|
|
22
|
+
activeBorder?: 'top' | 'bottom' | 'left' | 'right' | 'none'
|
|
23
|
+
/** Color applied to active item text, icon and border */
|
|
24
|
+
activeColor?: string
|
|
25
|
+
/** Render text in bold */
|
|
26
|
+
bold?: boolean
|
|
27
|
+
/** Icon displayed alongside the label */
|
|
28
|
+
icon?: IconProps['icon']
|
|
29
|
+
/** Icon position relative to the label @default 'left' */
|
|
30
|
+
iconPosition?: 'left' | 'right'
|
|
31
|
+
/** Text/icon color override */
|
|
32
|
+
color?: string
|
|
33
|
+
/** Navigation target passed to underlying Item */
|
|
34
|
+
to?: string
|
|
35
|
+
/** Handler called on item press */
|
|
36
|
+
onPress?: ItemProps['onPress']
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function MenuItem ({
|
|
40
|
+
children,
|
|
41
|
+
containerStyle,
|
|
42
|
+
active = false,
|
|
43
|
+
bold,
|
|
44
|
+
icon,
|
|
45
|
+
...props
|
|
46
|
+
}: MenuItemProps): ReactNode {
|
|
47
|
+
const context = useContext(MenuContext)
|
|
48
|
+
const getColor = useColors()
|
|
49
|
+
|
|
50
|
+
// TODO
|
|
51
|
+
// we should think about a better api
|
|
52
|
+
// and remove color, activeColor, activeBorder props
|
|
53
|
+
let color: string | undefined = props.color ?? context.color
|
|
54
|
+
color = getColor(color) ?? color
|
|
55
|
+
let activeColor = props.activeColor ?? context.activeColor
|
|
56
|
+
activeColor = getColor(activeColor) ?? activeColor
|
|
57
|
+
const activeBorder = props.activeBorder ?? context.activeBorder ?? 'none'
|
|
58
|
+
const iconPosition = props.iconPosition ?? context.iconPosition ?? 'left'
|
|
59
|
+
|
|
60
|
+
// TODO: prevent click if already active (for link and for div)
|
|
61
|
+
color = active ? (activeColor ?? getColor('text-primary')) : (color ?? getColor('text-main'))
|
|
62
|
+
const borderStyle: StyleProp<ViewStyle> = { backgroundColor: activeColor ?? getColor('border-primary') }
|
|
63
|
+
|
|
64
|
+
return pug`
|
|
65
|
+
Div
|
|
66
|
+
Item(...props)
|
|
67
|
+
if icon && iconPosition === 'left'
|
|
68
|
+
Item.Left
|
|
69
|
+
Icon(icon=icon style={ color })
|
|
70
|
+
|
|
71
|
+
Item.Content(style=[containerStyle])
|
|
72
|
+
Span(bold=bold style={ color })= children
|
|
73
|
+
|
|
74
|
+
if icon && iconPosition === 'right'
|
|
75
|
+
Item.Right
|
|
76
|
+
Icon(icon=icon style={ color })
|
|
77
|
+
|
|
78
|
+
if activeBorder !== 'none' && active
|
|
79
|
+
Div.border(styleName=[activeBorder] style=borderStyle)
|
|
80
|
+
`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default observer(themed('MenuItem', MenuItem))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { pug, u } from 'startupjs'
|
|
3
|
+
import useRouter from 'startupjs/useRouter'
|
|
4
|
+
import { faShareAlt, faHouseUser, faTable } from '@fortawesome/free-solid-svg-icons'
|
|
5
|
+
import Menu, { _PropsJsonSchema as MenuPropsJsonSchema } from './index'
|
|
6
|
+
import MenuItem, { _PropsJsonSchema as MenuItemPropsJsonSchema } from './MenuItem'
|
|
7
|
+
import Collapse from '@startupjs-ui/collapse'
|
|
8
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
9
|
+
|
|
10
|
+
# Menu
|
|
11
|
+
|
|
12
|
+
Inherits [Div props](/docs/components/Div).
|
|
13
|
+
|
|
14
|
+
Menu displays a list of choices and also can be used for navigation.
|
|
15
|
+
|
|
16
|
+
```jsx
|
|
17
|
+
import { Menu } from 'startupjs-ui'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Simple example
|
|
21
|
+
|
|
22
|
+
```jsx example
|
|
23
|
+
const [active, setActive] = useState('option-1')
|
|
24
|
+
return (
|
|
25
|
+
<Menu>
|
|
26
|
+
<Menu.Item
|
|
27
|
+
active={active === 'option-1'}
|
|
28
|
+
onPress={() => setActive('option-1')}
|
|
29
|
+
>
|
|
30
|
+
Option-1
|
|
31
|
+
</Menu.Item>
|
|
32
|
+
<Menu.Item
|
|
33
|
+
active={active === 'option-2'}
|
|
34
|
+
onPress={() => setActive('option-2')}
|
|
35
|
+
>
|
|
36
|
+
Option-2
|
|
37
|
+
</Menu.Item>
|
|
38
|
+
</Menu>
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Highlighted active item
|
|
43
|
+
|
|
44
|
+
By default the active menu item is not highlighted. To highlight it use the `activeBorder` property.
|
|
45
|
+
|
|
46
|
+
```jsx example
|
|
47
|
+
const [active, setActive] = useState('option-1')
|
|
48
|
+
return (
|
|
49
|
+
<Menu activeBorder='left'>
|
|
50
|
+
<Menu.Item
|
|
51
|
+
active={active === 'option-1'}
|
|
52
|
+
onPress={() => setActive('option-1')}
|
|
53
|
+
>
|
|
54
|
+
Option-1
|
|
55
|
+
</Menu.Item>
|
|
56
|
+
<Menu.Item
|
|
57
|
+
active={active === 'option-2'}
|
|
58
|
+
onPress={() => setActive('option-2')}
|
|
59
|
+
>
|
|
60
|
+
Option-2
|
|
61
|
+
</Menu.Item>
|
|
62
|
+
<Menu.Item
|
|
63
|
+
active={active === 'option-3'}
|
|
64
|
+
onPress={() => setActive('option-3')}
|
|
65
|
+
>
|
|
66
|
+
Option-3
|
|
67
|
+
</Menu.Item>
|
|
68
|
+
</Menu>
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Horizontal menu
|
|
73
|
+
|
|
74
|
+
By default the menu shows vertically. To make it horizontal, you need pass the string `horizontal` to the `variant` property.
|
|
75
|
+
|
|
76
|
+
```jsx example
|
|
77
|
+
const [active, setActive] = useState('option-1')
|
|
78
|
+
return (
|
|
79
|
+
<Menu variant='horizontal'>
|
|
80
|
+
<Menu.Item
|
|
81
|
+
active={active === 'option-1'}
|
|
82
|
+
onPress={() => setActive('option-1')}
|
|
83
|
+
>
|
|
84
|
+
Option-1
|
|
85
|
+
</Menu.Item>
|
|
86
|
+
<Menu.Item
|
|
87
|
+
active={active === 'option-2'}
|
|
88
|
+
onPress={() => setActive('option-2')}
|
|
89
|
+
>
|
|
90
|
+
Option-2
|
|
91
|
+
</Menu.Item>
|
|
92
|
+
<Menu.Item
|
|
93
|
+
active={active === 'option-3'}
|
|
94
|
+
onPress={() => setActive('option-3')}
|
|
95
|
+
>
|
|
96
|
+
Option-3
|
|
97
|
+
</Menu.Item>
|
|
98
|
+
</Menu>
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Menu item icon
|
|
103
|
+
|
|
104
|
+
To display icon in menu item pass the `icon` property to it. Position of icon can be changed by passing `iconPosition` to `Menu` component (`left` by default) and can be override in menu item.
|
|
105
|
+
|
|
106
|
+
```jsx example
|
|
107
|
+
const [active, setActive] = useState('option-1')
|
|
108
|
+
return (
|
|
109
|
+
<Menu variant='horizontal'>
|
|
110
|
+
<Menu.Item
|
|
111
|
+
active={active === 'option-1'}
|
|
112
|
+
icon={faShareAlt}
|
|
113
|
+
onPress={() => setActive('option-1')}
|
|
114
|
+
>
|
|
115
|
+
Option-1
|
|
116
|
+
</Menu.Item>
|
|
117
|
+
<Menu.Item
|
|
118
|
+
active={active === 'option-2'}
|
|
119
|
+
icon={faHouseUser}
|
|
120
|
+
onPress={() => setActive('option-2')}
|
|
121
|
+
>
|
|
122
|
+
Option-2
|
|
123
|
+
</Menu.Item>
|
|
124
|
+
<Menu.Item
|
|
125
|
+
active={active === 'option-3'}
|
|
126
|
+
icon={faTable}
|
|
127
|
+
onPress={() => setActive('option-3')}
|
|
128
|
+
>
|
|
129
|
+
Option-3
|
|
130
|
+
</Menu.Item>
|
|
131
|
+
</Menu>
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Change color of active item
|
|
136
|
+
|
|
137
|
+
By default, the active menu item has a `primary` color. Use the `activeColor` property to set the desired color for the active element. This property will also change the color of border and icon (if you passed an `activeBorder` property for a Menu or an `icon` for a Menu.Item). `activeColor` accepts color as a string which will be compatible with the CSS color property.(ex. `red`, `#fff`, `rgb(123, 23, 122)`)
|
|
138
|
+
|
|
139
|
+
```jsx example
|
|
140
|
+
const [active, setActive] = useState('option-1')
|
|
141
|
+
return (
|
|
142
|
+
<Menu
|
|
143
|
+
activeBorder='bottom'
|
|
144
|
+
variant='horizontal'
|
|
145
|
+
activeColor='rgb(123, 23, 122)'
|
|
146
|
+
>
|
|
147
|
+
<Menu.Item
|
|
148
|
+
active={active === 'option-1'}
|
|
149
|
+
icon={faShareAlt}
|
|
150
|
+
onPress={() => setActive('option-1')}
|
|
151
|
+
>
|
|
152
|
+
Option-1
|
|
153
|
+
</Menu.Item>
|
|
154
|
+
<Menu.Item
|
|
155
|
+
active={active === 'option-2'}
|
|
156
|
+
icon={faHouseUser}
|
|
157
|
+
onPress={() => setActive('option-2')}
|
|
158
|
+
>
|
|
159
|
+
Option-2
|
|
160
|
+
</Menu.Item>
|
|
161
|
+
<Menu.Item
|
|
162
|
+
active={active === 'option-3'}
|
|
163
|
+
icon={faTable}
|
|
164
|
+
onPress={() => setActive('option-3')}
|
|
165
|
+
>
|
|
166
|
+
Option-3
|
|
167
|
+
</Menu.Item>
|
|
168
|
+
</Menu>
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Menu item as link
|
|
173
|
+
|
|
174
|
+
Sometimes you want using menu item as link, for example in navigation menu. See below how you can do this.
|
|
175
|
+
|
|
176
|
+
```jsx example
|
|
177
|
+
const { usePathname } = useRouter()
|
|
178
|
+
const url = usePathname()
|
|
179
|
+
const components = [
|
|
180
|
+
{ label: 'Menu', to: '/docs/Menu' },
|
|
181
|
+
{ label: 'Div', to: '/docs/Div' },
|
|
182
|
+
{ label: 'Button', to: '/docs/Button' }
|
|
183
|
+
]
|
|
184
|
+
return pug`
|
|
185
|
+
Menu(activeBorder='left')
|
|
186
|
+
each component, index in components
|
|
187
|
+
- const to = component.to
|
|
188
|
+
Menu.Item(
|
|
189
|
+
key=index
|
|
190
|
+
active=to === url
|
|
191
|
+
to=to
|
|
192
|
+
)= component.label
|
|
193
|
+
`
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Complex interaction (integration with other components)
|
|
197
|
+
|
|
198
|
+
An example of a collapsible menu with submenus nested in.
|
|
199
|
+
|
|
200
|
+
```jsx example
|
|
201
|
+
const [showMenu, setShowMenu] = useState(true)
|
|
202
|
+
const [showSubMenu, setShowSubMenu] = useState(false)
|
|
203
|
+
const [active, setActive] = useState('option-1')
|
|
204
|
+
return (
|
|
205
|
+
<Collapse
|
|
206
|
+
variant='pure'
|
|
207
|
+
open={showMenu}
|
|
208
|
+
onChange={() => setShowMenu(!showMenu)}
|
|
209
|
+
>
|
|
210
|
+
<Collapse.Header>
|
|
211
|
+
<Menu.Item
|
|
212
|
+
style={{ paddingLeft: 0 }}
|
|
213
|
+
active={showMenu}
|
|
214
|
+
>
|
|
215
|
+
Main menu
|
|
216
|
+
</Menu.Item>
|
|
217
|
+
</Collapse.Header>
|
|
218
|
+
<Collapse.Content>
|
|
219
|
+
<Menu activeBorder='left'>
|
|
220
|
+
<Menu.Item
|
|
221
|
+
active={active === 'option-1'}
|
|
222
|
+
onPress={() => setActive('option-1')}
|
|
223
|
+
>
|
|
224
|
+
Option-1
|
|
225
|
+
</Menu.Item>
|
|
226
|
+
<Menu.Item
|
|
227
|
+
active={active === 'option-2'}
|
|
228
|
+
onPress={() => setActive('option-2')}
|
|
229
|
+
>
|
|
230
|
+
Option-2
|
|
231
|
+
</Menu.Item>
|
|
232
|
+
<Collapse
|
|
233
|
+
style={{ marginLeft: u(2) }}
|
|
234
|
+
variant='pure'
|
|
235
|
+
open={showSubMenu}
|
|
236
|
+
onChange={() => setShowSubMenu(!showSubMenu)}
|
|
237
|
+
>
|
|
238
|
+
<Collapse.Header>
|
|
239
|
+
<Menu.Item
|
|
240
|
+
style={{ paddingLeft: 0 }}
|
|
241
|
+
active={showSubMenu}
|
|
242
|
+
>
|
|
243
|
+
Option-3
|
|
244
|
+
</Menu.Item>
|
|
245
|
+
</Collapse.Header>
|
|
246
|
+
<Collapse.Content>
|
|
247
|
+
<Menu activeBorder='left'>
|
|
248
|
+
<Menu.Item
|
|
249
|
+
active={active === 'option-4'}
|
|
250
|
+
onPress={() => setActive('option-4')}
|
|
251
|
+
>
|
|
252
|
+
Option-4
|
|
253
|
+
</Menu.Item>
|
|
254
|
+
<Menu.Item
|
|
255
|
+
active={active === 'option-5'}
|
|
256
|
+
onPress={() => setActive('option-5')}
|
|
257
|
+
>
|
|
258
|
+
Option-5
|
|
259
|
+
</Menu.Item>
|
|
260
|
+
</Menu>
|
|
261
|
+
</Collapse.Content>
|
|
262
|
+
</Collapse>
|
|
263
|
+
</Menu>
|
|
264
|
+
</Collapse.Content>
|
|
265
|
+
</Collapse>
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Sandbox
|
|
270
|
+
|
|
271
|
+
### Menu
|
|
272
|
+
|
|
273
|
+
<Sandbox
|
|
274
|
+
Component={Menu}
|
|
275
|
+
propsJsonSchema={MenuPropsJsonSchema}
|
|
276
|
+
props={{
|
|
277
|
+
children: [
|
|
278
|
+
<MenuItem
|
|
279
|
+
key='option-1'
|
|
280
|
+
icon={faHouseUser}
|
|
281
|
+
active={true}
|
|
282
|
+
onPress={() => alert('"onPress" event on "Option-1"')}
|
|
283
|
+
>
|
|
284
|
+
Option-1
|
|
285
|
+
</MenuItem>,
|
|
286
|
+
<MenuItem
|
|
287
|
+
key='option-2'
|
|
288
|
+
active={false}
|
|
289
|
+
onPress={() => alert('"onPress" event on "Option-2"')}
|
|
290
|
+
>
|
|
291
|
+
Option-2
|
|
292
|
+
</MenuItem>
|
|
293
|
+
]
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
|
|
297
|
+
### Menu.Item
|
|
298
|
+
|
|
299
|
+
<Sandbox
|
|
300
|
+
Component={Menu.Item}
|
|
301
|
+
propsJsonSchema={MenuItemPropsJsonSchema}
|
|
302
|
+
extraParams={{
|
|
303
|
+
icon: {
|
|
304
|
+
showIconSelect: true
|
|
305
|
+
}
|
|
306
|
+
}}
|
|
307
|
+
props={{
|
|
308
|
+
children: 'Option-1',
|
|
309
|
+
onPress: () => alert('"onPress" event on "Option-1"')
|
|
310
|
+
}}
|
|
311
|
+
/>
|
package/context.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface MenuContextValue {
|
|
4
|
+
activeBorder?: 'top' | 'bottom' | 'left' | 'right' | 'none'
|
|
5
|
+
activeColor?: string
|
|
6
|
+
iconPosition?: 'left' | 'right'
|
|
7
|
+
color?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default createContext<MenuContextValue>({})
|
package/index.cssx.styl
ADDED
package/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type ReactNode } from 'react';
|
|
5
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
6
|
+
import { type DivProps } from '@startupjs-ui/div';
|
|
7
|
+
import './index.cssx.styl';
|
|
8
|
+
export declare const _PropsJsonSchema: {};
|
|
9
|
+
export interface MenuProps extends Omit<DivProps, 'variant' | 'style'> {
|
|
10
|
+
/** Custom styles applied to the root view */
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
/** Content rendered inside Menu */
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
/** Active border position passed to Menu items */
|
|
15
|
+
activeBorder?: 'top' | 'bottom' | 'left' | 'right' | 'none';
|
|
16
|
+
/** Color applied to active Menu items */
|
|
17
|
+
activeColor?: string;
|
|
18
|
+
/** Icon position applied to nested Menu items @default 'left' */
|
|
19
|
+
iconPosition?: 'left' | 'right';
|
|
20
|
+
/** Layout orientation @default 'vertical' */
|
|
21
|
+
variant?: 'vertical' | 'horizontal';
|
|
22
|
+
}
|
|
23
|
+
declare const ObservedMenu: any;
|
|
24
|
+
export { default as MenuItem } from './MenuItem';
|
|
25
|
+
export default ObservedMenu;
|
package/index.tsx
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useMemo, 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, { type DivProps } from '@startupjs-ui/div'
|
|
6
|
+
import MenuItem from './MenuItem'
|
|
7
|
+
import Context, { type MenuContextValue } from './context'
|
|
8
|
+
import './index.cssx.styl'
|
|
9
|
+
|
|
10
|
+
export const _PropsJsonSchema = {/* MenuProps */} // used in docs generation
|
|
11
|
+
|
|
12
|
+
export interface MenuProps extends Omit<DivProps, 'variant' | 'style'> {
|
|
13
|
+
/** Custom styles applied to the root view */
|
|
14
|
+
style?: StyleProp<ViewStyle>
|
|
15
|
+
/** Content rendered inside Menu */
|
|
16
|
+
children?: ReactNode
|
|
17
|
+
/** Active border position passed to Menu items */
|
|
18
|
+
activeBorder?: 'top' | 'bottom' | 'left' | 'right' | 'none'
|
|
19
|
+
/** Color applied to active Menu items */
|
|
20
|
+
activeColor?: string
|
|
21
|
+
/** Icon position applied to nested Menu items @default 'left' */
|
|
22
|
+
iconPosition?: 'left' | 'right'
|
|
23
|
+
/** Layout orientation @default 'vertical' */
|
|
24
|
+
variant?: 'vertical' | 'horizontal'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function Menu ({
|
|
28
|
+
style,
|
|
29
|
+
children,
|
|
30
|
+
variant = 'vertical',
|
|
31
|
+
activeBorder,
|
|
32
|
+
iconPosition,
|
|
33
|
+
activeColor,
|
|
34
|
+
...props
|
|
35
|
+
}: MenuProps): ReactNode {
|
|
36
|
+
const value = useMemo<MenuContextValue>(() => {
|
|
37
|
+
return { activeBorder, activeColor, iconPosition }
|
|
38
|
+
}, [activeBorder, activeColor, iconPosition])
|
|
39
|
+
|
|
40
|
+
return pug`
|
|
41
|
+
Context.Provider(value=value)
|
|
42
|
+
Div.root(
|
|
43
|
+
style=style
|
|
44
|
+
styleName=[variant]
|
|
45
|
+
...props
|
|
46
|
+
)= children
|
|
47
|
+
`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ObservedMenu: any = observer(themed('Menu', Menu))
|
|
51
|
+
|
|
52
|
+
ObservedMenu.Item = MenuItem
|
|
53
|
+
|
|
54
|
+
export { default as MenuItem } from './MenuItem'
|
|
55
|
+
export default ObservedMenu
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/menu",
|
|
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/core": "^0.1.3",
|
|
12
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/icon": "^0.1.3",
|
|
14
|
+
"@startupjs-ui/item": "^0.1.3",
|
|
15
|
+
"@startupjs-ui/span": "^0.1.3"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react": "*",
|
|
19
|
+
"react-native": "*",
|
|
20
|
+
"startupjs": "*"
|
|
21
|
+
},
|
|
22
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
23
|
+
}
|