@startupjs-ui/div 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 +34 -0
- package/README.mdx +212 -0
- package/index.cssx.styl +110 -0
- package/index.d.ts +68 -0
- package/index.tsx +360 -0
- package/package.json +25 -0
- package/useTooltip.tsx +166 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
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/div
|
|
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
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* disable wrong TS error ([199c088](https://github.com/startupjs/startupjs-ui/commit/199c088718256d88bef037536c9f1e71fd39c7be))
|
|
20
|
+
* **div:** fix tooltip typings ([97f7bf7](https://github.com/startupjs/startupjs-ui/commit/97f7bf7bcf289f0430e8ff5c72c1bfb21207dca2))
|
|
21
|
+
* **div:** Refactor to use Pressable and get rid of an extra wrapper when the Div is pressable ([9e7c852](https://github.com/startupjs/startupjs-ui/commit/9e7c852f383f540cab58b7a31aba0bd64a531adc))
|
|
22
|
+
* **div:** refactor tooltip-related logic to move it fully out into a separate hook to decorate props ([2fa9cf0](https://github.com/startupjs/startupjs-ui/commit/2fa9cf0821a6c726d5e2ad621a6c15d4091e59c7))
|
|
23
|
+
* remove async ([9782d01](https://github.com/startupjs/startupjs-ui/commit/9782d01aa728b4970cf3919055a87eaee27a5d16))
|
|
24
|
+
* remove async await in components ([67c97ec](https://github.com/startupjs/startupjs-ui/commit/67c97eccce202095f0f4c85b1d7c204f6cdc4a6e))
|
|
25
|
+
* specify explicit return for react components ([20648e6](https://github.com/startupjs/startupjs-ui/commit/20648e67c9e004c1e7e8eb01a92bbaf36a0362d0))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* 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))
|
|
31
|
+
* auto-generate .d.ts files for components on commit ([7d51efe](https://github.com/startupjs/startupjs-ui/commit/7d51efed601aabd217670490ae1cd438b7852970))
|
|
32
|
+
* **div:** bring back tooltip support ([7ea1f62](https://github.com/startupjs/startupjs-ui/commit/7ea1f62b2537f536c9c01803c3a4ae61fbcc17b0))
|
|
33
|
+
* **div:** Migrate Div component ([d43c2f7](https://github.com/startupjs/startupjs-ui/commit/d43c2f784fab74cb4537932d30ecb974dbf4541a))
|
|
34
|
+
* **icon:** refactor Icon component ([70237be](https://github.com/startupjs/startupjs-ui/commit/70237be3cd372154f1010def67735ef08db0c02a))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { pug, u } from 'startupjs'
|
|
3
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
4
|
+
import { styl } from 'startupjs'
|
|
5
|
+
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'
|
|
6
|
+
import Div, { _PropsJsonSchema as DivPropsJsonSchema } from './index.tsx'
|
|
7
|
+
import Span from '@startupjs-ui/span'
|
|
8
|
+
import Icon from '@startupjs-ui/icon'
|
|
9
|
+
|
|
10
|
+
# Div
|
|
11
|
+
|
|
12
|
+
Div is a basic building block for layouts. It is a wrapper around `View` with some extra features.
|
|
13
|
+
|
|
14
|
+
```jsx
|
|
15
|
+
import { Div } from 'startupjs-ui'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Simple example
|
|
19
|
+
```jsx example
|
|
20
|
+
return (
|
|
21
|
+
<Div>
|
|
22
|
+
<Span>Div</Span>
|
|
23
|
+
</Div>
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Clickable
|
|
28
|
+
|
|
29
|
+
```jsx example
|
|
30
|
+
const divStyle = {
|
|
31
|
+
width: 150,
|
|
32
|
+
height: 100,
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
justifyContent: 'center',
|
|
35
|
+
backgroundColor: '#00AED6'
|
|
36
|
+
}
|
|
37
|
+
const [counter, setCounter] = useState(0)
|
|
38
|
+
return (
|
|
39
|
+
<Div
|
|
40
|
+
style={divStyle}
|
|
41
|
+
onLongPress={() => setCounter(counter + 10)}
|
|
42
|
+
onPress={() => setCounter(counter + 1)}
|
|
43
|
+
>
|
|
44
|
+
<Span>Clicked {counter} times</Span>
|
|
45
|
+
</Div>
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Layout
|
|
50
|
+
|
|
51
|
+
You can specify properties to manage layout:
|
|
52
|
+
|
|
53
|
+
- `row` - aligns children from left to right (default: `false`)
|
|
54
|
+
- `reverse` - aligns the children in reverse order depending on the direction (default: `false`)
|
|
55
|
+
- `wrap` - controls the wrapping of children into multiple rows (default: `false`)
|
|
56
|
+
- `align` - controls horizontal alignment (possible values: `left`, `center`, `right`)
|
|
57
|
+
- `vAlign` - controls vertical alignment (possible values: `top`, `center`, `bottom`)
|
|
58
|
+
- `gap` - defines the size of the gap between items in [units](/docs/tutorial/TricksWithStyles#units) or the value `true` is equivalent to `2`
|
|
59
|
+
|
|
60
|
+
```jsx example
|
|
61
|
+
return (
|
|
62
|
+
<Div
|
|
63
|
+
style={{ height: u(10), backgroundColor: 'yellow' }}
|
|
64
|
+
row
|
|
65
|
+
align='center'
|
|
66
|
+
vAlign='center'
|
|
67
|
+
>
|
|
68
|
+
<Div style={{ width: u(5), height: u(5), backgroundColor: 'red' }} />
|
|
69
|
+
<Div style={{ width: u(5), height: u(5), backgroundColor: 'blue' }} pushed />
|
|
70
|
+
</Div>
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Full width (or height)
|
|
75
|
+
|
|
76
|
+
To make `Div` take all remaining space in the parent container (according to its `flex-direction`), pass the `full` property.
|
|
77
|
+
|
|
78
|
+
This property just sets `flex: 1` to it.
|
|
79
|
+
|
|
80
|
+
```jsx example
|
|
81
|
+
return (
|
|
82
|
+
<Div row vAlign='center'>
|
|
83
|
+
<Div full>
|
|
84
|
+
<Span>Tesla Model S</Span>
|
|
85
|
+
</Div>
|
|
86
|
+
<Span description>1000 HP</Span>
|
|
87
|
+
</Div>
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Indentation between multiple Div
|
|
92
|
+
|
|
93
|
+
When displaying multiple Div(s) in a line spacing between them can be adjusted using `pushed` prop to specify indent from the previous Div. Possible values of `pushed` prop can be found in the `Sandbox` section at the bottom of the page. The `true` value of prop is equivalent to `m` value.
|
|
94
|
+
|
|
95
|
+
Though instead of pushing each individual Div, in most cases it is better to use `gap` prop on the parent Div to set spacing between all children at once.
|
|
96
|
+
|
|
97
|
+
```jsx example
|
|
98
|
+
const divStyle = {
|
|
99
|
+
width: 100,
|
|
100
|
+
height: 80,
|
|
101
|
+
textAlign: 'center',
|
|
102
|
+
justifyContent: 'center',
|
|
103
|
+
backgroundColor: '#00AED6'
|
|
104
|
+
}
|
|
105
|
+
return (
|
|
106
|
+
<Div row>
|
|
107
|
+
<Div style={divStyle}>
|
|
108
|
+
<Span>Div 1</Span>
|
|
109
|
+
</Div>
|
|
110
|
+
<Div pushed='s' style={divStyle}>
|
|
111
|
+
<Span>Div 2</Span>
|
|
112
|
+
</Div>
|
|
113
|
+
<Div pushed style={divStyle}>
|
|
114
|
+
<Span>Div 3</Span>
|
|
115
|
+
</Div>
|
|
116
|
+
<Div pushed='l' style={divStyle}>
|
|
117
|
+
<Span>Div 4</Span>
|
|
118
|
+
</Div>
|
|
119
|
+
</Div>
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Different clickable states
|
|
124
|
+
|
|
125
|
+
There are two clickable variants of component, controlled by `variant` property.
|
|
126
|
+
|
|
127
|
+
`opacity` variant - on press down, opacity of the component is decreased.
|
|
128
|
+
|
|
129
|
+
`highlight` variant - on press down, the opacity of the component background is decreased.
|
|
130
|
+
|
|
131
|
+
```jsx example
|
|
132
|
+
const divStyle = {
|
|
133
|
+
width: 100,
|
|
134
|
+
height: 80,
|
|
135
|
+
textAlign: 'center',
|
|
136
|
+
justifyContent: 'center',
|
|
137
|
+
backgroundColor: '#00AED6'
|
|
138
|
+
}
|
|
139
|
+
return (
|
|
140
|
+
<Div row>
|
|
141
|
+
<Div style={divStyle} onPress={() => {}}>
|
|
142
|
+
<Span>Div opacity variant</Span>
|
|
143
|
+
</Div>
|
|
144
|
+
<Div style={divStyle} variant='highlight' pushed onPress={() => {}}>
|
|
145
|
+
<Span>Div highlight variant</Span>
|
|
146
|
+
</Div>
|
|
147
|
+
</Div>
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Levels of emphasis
|
|
152
|
+
|
|
153
|
+
Div `level` property determines different levels of emphasis by adding shadow to component. Possible values of `level` prop can be found in the `Sandbox` section at the bottom of the page.
|
|
154
|
+
**IMPORTANT**: Shadow does not work without background color on mobile devices.
|
|
155
|
+
|
|
156
|
+
```jsx example
|
|
157
|
+
const divStyle = {
|
|
158
|
+
width: 100,
|
|
159
|
+
height: 80,
|
|
160
|
+
textAlign: 'center',
|
|
161
|
+
justifyContent: 'center',
|
|
162
|
+
backgroundColor: 'white'
|
|
163
|
+
}
|
|
164
|
+
return (
|
|
165
|
+
<Div row>
|
|
166
|
+
<Div style={divStyle} level={1}>
|
|
167
|
+
<Span>Div level 1</Span>
|
|
168
|
+
</Div>
|
|
169
|
+
<Div style={divStyle} level={4} pushed>
|
|
170
|
+
<Span>Div level 4</Span>
|
|
171
|
+
</Div>
|
|
172
|
+
</Div>
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Tooltip
|
|
177
|
+
|
|
178
|
+
```jsx example
|
|
179
|
+
return pug`
|
|
180
|
+
Div.root(tooltip='Tooltip content')
|
|
181
|
+
Icon(icon=faInfoCircle size='xl')
|
|
182
|
+
`
|
|
183
|
+
|
|
184
|
+
styl`
|
|
185
|
+
.root
|
|
186
|
+
align-self flex-start
|
|
187
|
+
`
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Sandbox
|
|
191
|
+
|
|
192
|
+
<Sandbox
|
|
193
|
+
Component={Div}
|
|
194
|
+
propsJsonSchema={DivPropsJsonSchema}
|
|
195
|
+
props={{
|
|
196
|
+
children: (
|
|
197
|
+
<Div style={{
|
|
198
|
+
height: 15 * 8,
|
|
199
|
+
width: 15 * 8,
|
|
200
|
+
margin: 'auto',
|
|
201
|
+
backgroundColor: '#00AED6'
|
|
202
|
+
}} />
|
|
203
|
+
),
|
|
204
|
+
style: {
|
|
205
|
+
width: 160,
|
|
206
|
+
height: 160,
|
|
207
|
+
backgroundColor: '#2962FF'
|
|
208
|
+
},
|
|
209
|
+
onPress: () => alert('"onPress" event on "Div" component'),
|
|
210
|
+
onLongPress: () => alert('"onLongPress" event on "Div" component')
|
|
211
|
+
}}
|
|
212
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// -----------------------------------------
|
|
2
|
+
// CONFIGURATION. $UI.Div
|
|
3
|
+
// -----------------------------------------
|
|
4
|
+
|
|
5
|
+
$this = merge({
|
|
6
|
+
defaultHoverOpacity: 0.8,
|
|
7
|
+
defaultActiveOpacity: 0.5
|
|
8
|
+
}, $UI.Div, true)
|
|
9
|
+
|
|
10
|
+
// -----------------------------------------
|
|
11
|
+
// COMPONENT
|
|
12
|
+
// -----------------------------------------
|
|
13
|
+
|
|
14
|
+
_pushedSizes = ('xs' 's' 'm' 'l' 'xl' 'xxl')
|
|
15
|
+
|
|
16
|
+
// skip level 0 for shadow
|
|
17
|
+
// because it needed only when you want to override shadow from style sheet
|
|
18
|
+
_shadowLevels = ('1' '2' '3' '4')
|
|
19
|
+
|
|
20
|
+
.root
|
|
21
|
+
outline-color var(--color-primary)
|
|
22
|
+
|
|
23
|
+
&.column
|
|
24
|
+
&.left
|
|
25
|
+
align-items stretch
|
|
26
|
+
|
|
27
|
+
&.center
|
|
28
|
+
align-items center
|
|
29
|
+
|
|
30
|
+
&.right
|
|
31
|
+
align-items flex-end
|
|
32
|
+
|
|
33
|
+
&.v_top
|
|
34
|
+
justify-content flex-start
|
|
35
|
+
|
|
36
|
+
&.v_center
|
|
37
|
+
justify-content center
|
|
38
|
+
|
|
39
|
+
&.v_bottom
|
|
40
|
+
justify-content flex-end
|
|
41
|
+
|
|
42
|
+
&.row
|
|
43
|
+
flex-direction row
|
|
44
|
+
|
|
45
|
+
&.left
|
|
46
|
+
justify-content flex-start
|
|
47
|
+
|
|
48
|
+
&.center
|
|
49
|
+
justify-content center
|
|
50
|
+
|
|
51
|
+
&.right
|
|
52
|
+
justify-content flex-end
|
|
53
|
+
|
|
54
|
+
&.v_top
|
|
55
|
+
align-items flex-start
|
|
56
|
+
|
|
57
|
+
&.v_center
|
|
58
|
+
align-items center
|
|
59
|
+
|
|
60
|
+
&.v_bottom
|
|
61
|
+
align-items flex-end
|
|
62
|
+
|
|
63
|
+
&.wrap
|
|
64
|
+
flex-wrap wrap
|
|
65
|
+
|
|
66
|
+
&.reverse
|
|
67
|
+
&.column
|
|
68
|
+
flex-direction column-reverse
|
|
69
|
+
|
|
70
|
+
&.row
|
|
71
|
+
flex-direction row-reverse
|
|
72
|
+
|
|
73
|
+
&.rounded
|
|
74
|
+
radius()
|
|
75
|
+
|
|
76
|
+
&.circle
|
|
77
|
+
radius(circle)
|
|
78
|
+
|
|
79
|
+
for size in _pushedSizes
|
|
80
|
+
&.pushed-{size}
|
|
81
|
+
margin-left: $UI.gutters[size]
|
|
82
|
+
|
|
83
|
+
for level in _shadowLevels
|
|
84
|
+
&.shadow-{level}
|
|
85
|
+
shadow(level)
|
|
86
|
+
|
|
87
|
+
&.bleed
|
|
88
|
+
bleed()
|
|
89
|
+
|
|
90
|
+
&.full
|
|
91
|
+
flex-grow 1
|
|
92
|
+
flex-shrink 1
|
|
93
|
+
|
|
94
|
+
&.clickable
|
|
95
|
+
cursor pointer
|
|
96
|
+
user-select none
|
|
97
|
+
|
|
98
|
+
&.disabled
|
|
99
|
+
opacity 0.5
|
|
100
|
+
|
|
101
|
+
+web()
|
|
102
|
+
cursor default
|
|
103
|
+
|
|
104
|
+
// -----------------------------------------
|
|
105
|
+
// JS EXPORTS
|
|
106
|
+
// -----------------------------------------
|
|
107
|
+
|
|
108
|
+
:export
|
|
109
|
+
config: $this
|
|
110
|
+
shadows: $UI.shadows
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
5
|
+
import { type StyleProp, type ViewStyle, type ViewProps, type AccessibilityRole } from 'react-native';
|
|
6
|
+
declare const _default: import("react").ComponentType<DivProps>;
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const _PropsJsonSchema: {};
|
|
9
|
+
export interface DivProps extends ViewProps {
|
|
10
|
+
/** Ref to access underlying <View> or <Pressable> */
|
|
11
|
+
ref?: RefObject<any>;
|
|
12
|
+
/** Custom styles applied to the root view */
|
|
13
|
+
style?: StyleProp<ViewStyle>;
|
|
14
|
+
/** Content rendered inside Div */
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
/** Visual feedback variant @default 'opacity' */
|
|
17
|
+
variant?: 'opacity' | 'highlight';
|
|
18
|
+
/** Render children in a horizontal row */
|
|
19
|
+
row?: boolean;
|
|
20
|
+
/** Allow wrapping when row is enabled */
|
|
21
|
+
wrap?: boolean;
|
|
22
|
+
/** Reverse children order for row layouts */
|
|
23
|
+
reverse?: boolean;
|
|
24
|
+
/** Horizontal alignment when using row/column */
|
|
25
|
+
align?: 'left' | 'center' | 'right';
|
|
26
|
+
/** Vertical alignment when using row/column */
|
|
27
|
+
vAlign?: 'top' | 'center' | 'bottom';
|
|
28
|
+
/** Spacing between children (true maps to default gap) */
|
|
29
|
+
gap?: boolean | number;
|
|
30
|
+
/** Enable press feedback styles (hover and active states) @default true */
|
|
31
|
+
feedback?: boolean;
|
|
32
|
+
/** Custom style for hover state */
|
|
33
|
+
hoverStyle?: StyleProp<ViewStyle>;
|
|
34
|
+
/** Custom style for active state */
|
|
35
|
+
activeStyle?: StyleProp<ViewStyle>;
|
|
36
|
+
/** Disable interactions and apply disabled styles */
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
/** Elevation level controlling shadow intensity */
|
|
39
|
+
level?: 0 | 1 | 2 | 3 | 4 | 5;
|
|
40
|
+
/** Shape of the container corners */
|
|
41
|
+
shape?: 'squared' | 'rounded' | 'circle';
|
|
42
|
+
/** Add more space from the previous sibling */
|
|
43
|
+
pushed?: boolean | 's' | 'm' | 'l';
|
|
44
|
+
/** Stretch container into negative spacing area */
|
|
45
|
+
bleed?: boolean;
|
|
46
|
+
/** Expand to take full available height (or width if 'row' is true) */
|
|
47
|
+
full?: boolean;
|
|
48
|
+
/** Simple tooltip text */
|
|
49
|
+
tooltip?: string;
|
|
50
|
+
/** Style overrides for tooltip element */
|
|
51
|
+
tooltipStyle?: StyleProp<ViewStyle>;
|
|
52
|
+
/** onPress handler */
|
|
53
|
+
onPress?: (e: any) => void;
|
|
54
|
+
/** onLongPress handler */
|
|
55
|
+
onLongPress?: (e: any) => void;
|
|
56
|
+
/** onPressIn handler */
|
|
57
|
+
onPressIn?: (e: any) => void;
|
|
58
|
+
/** onPressOut handler */
|
|
59
|
+
onPressOut?: (e: any) => void;
|
|
60
|
+
/** Whether view is accessible and focusable (if you can press it it's focusable by default) */
|
|
61
|
+
accessible?: boolean;
|
|
62
|
+
/** Accessibility role passed to native view (if you can press it it's a 'button') */
|
|
63
|
+
accessibilityRole?: AccessibilityRole;
|
|
64
|
+
/** Deprecated custom tooltip renderer @deprecated */
|
|
65
|
+
renderTooltip?: any;
|
|
66
|
+
/** Test ID for testing purposes */
|
|
67
|
+
'data-testid'?: string;
|
|
68
|
+
}
|
package/index.tsx
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { useState, useRef, type ReactNode, type RefObject } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Pressable,
|
|
5
|
+
Platform,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
type StyleProp,
|
|
8
|
+
type ViewStyle,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type AccessibilityRole
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
import { pug, observer, u, useDidUpdate } from 'startupjs'
|
|
13
|
+
import { colorToRGBA, getCssVariable, themed } from '@startupjs-ui/core'
|
|
14
|
+
import { useDecorateTooltipProps } from './useTooltip'
|
|
15
|
+
import STYLES from './index.cssx.styl'
|
|
16
|
+
|
|
17
|
+
const DEPRECATED_PUSHED_VALUES = ['xs', 'xl', 'xxl']
|
|
18
|
+
const PRESSABLE_PROPS = ['onPress', 'onLongPress', 'onPressIn', 'onPressOut']
|
|
19
|
+
const isWeb = Platform.OS === 'web'
|
|
20
|
+
const isNative = Platform.OS !== 'web'
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
config: {
|
|
24
|
+
defaultHoverOpacity,
|
|
25
|
+
defaultActiveOpacity
|
|
26
|
+
}
|
|
27
|
+
} = STYLES
|
|
28
|
+
|
|
29
|
+
export default observer(themed('Div', Div))
|
|
30
|
+
|
|
31
|
+
export const _PropsJsonSchema = {/* DivProps */}
|
|
32
|
+
|
|
33
|
+
export interface DivProps extends ViewProps {
|
|
34
|
+
/** Ref to access underlying <View> or <Pressable> */
|
|
35
|
+
ref?: RefObject<any>
|
|
36
|
+
/** Custom styles applied to the root view */
|
|
37
|
+
style?: StyleProp<ViewStyle>
|
|
38
|
+
/** Content rendered inside Div */
|
|
39
|
+
children?: ReactNode
|
|
40
|
+
/** Visual feedback variant @default 'opacity' */
|
|
41
|
+
variant?: 'opacity' | 'highlight'
|
|
42
|
+
/** Render children in a horizontal row */
|
|
43
|
+
row?: boolean
|
|
44
|
+
/** Allow wrapping when row is enabled */
|
|
45
|
+
wrap?: boolean
|
|
46
|
+
/** Reverse children order for row layouts */
|
|
47
|
+
reverse?: boolean
|
|
48
|
+
/** Horizontal alignment when using row/column */
|
|
49
|
+
align?: 'left' | 'center' | 'right'
|
|
50
|
+
/** Vertical alignment when using row/column */
|
|
51
|
+
vAlign?: 'top' | 'center' | 'bottom'
|
|
52
|
+
/** Spacing between children (true maps to default gap) */
|
|
53
|
+
gap?: boolean | number
|
|
54
|
+
/** Enable press feedback styles (hover and active states) @default true */
|
|
55
|
+
feedback?: boolean
|
|
56
|
+
/** Custom style for hover state */
|
|
57
|
+
hoverStyle?: StyleProp<ViewStyle>
|
|
58
|
+
/** Custom style for active state */
|
|
59
|
+
activeStyle?: StyleProp<ViewStyle>
|
|
60
|
+
/** Disable interactions and apply disabled styles */
|
|
61
|
+
disabled?: boolean
|
|
62
|
+
/** Elevation level controlling shadow intensity */
|
|
63
|
+
level?: 0 | 1 | 2 | 3 | 4 | 5
|
|
64
|
+
/** Shape of the container corners */
|
|
65
|
+
shape?: 'squared' | 'rounded' | 'circle'
|
|
66
|
+
/** Add more space from the previous sibling */
|
|
67
|
+
pushed?: boolean | 's' | 'm' | 'l'
|
|
68
|
+
/** Stretch container into negative spacing area */
|
|
69
|
+
bleed?: boolean
|
|
70
|
+
/** Expand to take full available height (or width if 'row' is true) */
|
|
71
|
+
full?: boolean
|
|
72
|
+
/** Simple tooltip text */
|
|
73
|
+
tooltip?: string
|
|
74
|
+
/** Style overrides for tooltip element */
|
|
75
|
+
tooltipStyle?: StyleProp<ViewStyle>
|
|
76
|
+
/** onPress handler */
|
|
77
|
+
onPress?: (e: any) => void
|
|
78
|
+
/** onLongPress handler */
|
|
79
|
+
onLongPress?: (e: any) => void
|
|
80
|
+
/** onPressIn handler */
|
|
81
|
+
onPressIn?: (e: any) => void
|
|
82
|
+
/** onPressOut handler */
|
|
83
|
+
onPressOut?: (e: any) => void
|
|
84
|
+
/** Whether view is accessible and focusable (if you can press it it's focusable by default) */
|
|
85
|
+
accessible?: boolean
|
|
86
|
+
/** Accessibility role passed to native view (if you can press it it's a 'button') */
|
|
87
|
+
accessibilityRole?: AccessibilityRole
|
|
88
|
+
/** Deprecated custom tooltip renderer @deprecated */
|
|
89
|
+
renderTooltip?: any // Deprecated
|
|
90
|
+
/** Test ID for testing purposes */
|
|
91
|
+
'data-testid'?: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function Div ({
|
|
95
|
+
style = [],
|
|
96
|
+
children,
|
|
97
|
+
variant = 'opacity',
|
|
98
|
+
row,
|
|
99
|
+
wrap,
|
|
100
|
+
reverse,
|
|
101
|
+
align,
|
|
102
|
+
vAlign,
|
|
103
|
+
gap,
|
|
104
|
+
hoverStyle,
|
|
105
|
+
activeStyle,
|
|
106
|
+
feedback = true,
|
|
107
|
+
disabled,
|
|
108
|
+
level = 0,
|
|
109
|
+
shape,
|
|
110
|
+
pushed, // History: for some reason the prop 'push' was ignored
|
|
111
|
+
bleed,
|
|
112
|
+
full,
|
|
113
|
+
accessible,
|
|
114
|
+
accessibilityRole,
|
|
115
|
+
tooltip,
|
|
116
|
+
tooltipStyle,
|
|
117
|
+
renderTooltip,
|
|
118
|
+
ref,
|
|
119
|
+
...props
|
|
120
|
+
}: DivProps): ReactNode {
|
|
121
|
+
assertDeprecatedValues({ pushed, renderTooltip })
|
|
122
|
+
style = StyleSheet.flatten(style)
|
|
123
|
+
// on RN row-reverse switches margins and paddings sides, so we switch them back
|
|
124
|
+
if (isNative && reverse) style = reverseMarginPaddingSides(style)
|
|
125
|
+
if (gap === true) gap = 2
|
|
126
|
+
const isPressable = hasPressHandler(props)
|
|
127
|
+
const fallbackRef = useRef<any>(null)
|
|
128
|
+
const rootRef = ref ?? fallbackRef
|
|
129
|
+
|
|
130
|
+
let pressableStyle: StyleProp<ViewStyle> = {}
|
|
131
|
+
;({
|
|
132
|
+
props,
|
|
133
|
+
pressableStyle,
|
|
134
|
+
accessibilityRole
|
|
135
|
+
} = useDecoratePressableProps({
|
|
136
|
+
props,
|
|
137
|
+
style,
|
|
138
|
+
activeStyle,
|
|
139
|
+
hoverStyle,
|
|
140
|
+
variant,
|
|
141
|
+
isPressable,
|
|
142
|
+
disabled,
|
|
143
|
+
accessibilityRole,
|
|
144
|
+
feedback
|
|
145
|
+
}))
|
|
146
|
+
|
|
147
|
+
let tooltipElement
|
|
148
|
+
;({
|
|
149
|
+
props,
|
|
150
|
+
tooltipElement
|
|
151
|
+
} = useDecorateTooltipProps({
|
|
152
|
+
props,
|
|
153
|
+
style: tooltipStyle,
|
|
154
|
+
anchorRef: rootRef,
|
|
155
|
+
tooltip
|
|
156
|
+
}))
|
|
157
|
+
|
|
158
|
+
let pushedModifier
|
|
159
|
+
if (pushed) {
|
|
160
|
+
if (typeof pushed === 'boolean') pushed = 'm'
|
|
161
|
+
pushedModifier = `pushed-${pushed}`
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let levelModifier
|
|
165
|
+
if (level) levelModifier = `shadow-${level}`
|
|
166
|
+
|
|
167
|
+
if (!accessible) accessibilityRole = undefined
|
|
168
|
+
|
|
169
|
+
const Component = isPressable ? Pressable : View
|
|
170
|
+
const testID = props.testID ?? props['data-testid']
|
|
171
|
+
const divElement = pug`
|
|
172
|
+
Component.root(
|
|
173
|
+
ref=rootRef
|
|
174
|
+
style=[
|
|
175
|
+
gap ? { gap: u(gap) } : undefined,
|
|
176
|
+
style,
|
|
177
|
+
pressableStyle
|
|
178
|
+
]
|
|
179
|
+
styleName=[
|
|
180
|
+
row ? 'row' : 'column',
|
|
181
|
+
{ wrap, reverse },
|
|
182
|
+
align,
|
|
183
|
+
'v_' + vAlign,
|
|
184
|
+
{
|
|
185
|
+
clickable: isWeb && isPressable,
|
|
186
|
+
bleed,
|
|
187
|
+
full,
|
|
188
|
+
disabled
|
|
189
|
+
},
|
|
190
|
+
shape,
|
|
191
|
+
pushedModifier,
|
|
192
|
+
levelModifier
|
|
193
|
+
]
|
|
194
|
+
accessible=accessible
|
|
195
|
+
accessibilityRole=accessibilityRole
|
|
196
|
+
testID=testID
|
|
197
|
+
...props
|
|
198
|
+
)= children
|
|
199
|
+
`
|
|
200
|
+
|
|
201
|
+
if (tooltipElement) {
|
|
202
|
+
return pug`
|
|
203
|
+
= divElement
|
|
204
|
+
= tooltipElement
|
|
205
|
+
`
|
|
206
|
+
} else return divElement
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function useDecoratePressableProps ({
|
|
210
|
+
props,
|
|
211
|
+
style,
|
|
212
|
+
activeStyle,
|
|
213
|
+
hoverStyle,
|
|
214
|
+
variant,
|
|
215
|
+
isPressable,
|
|
216
|
+
disabled,
|
|
217
|
+
accessibilityRole,
|
|
218
|
+
feedback
|
|
219
|
+
}: {
|
|
220
|
+
props: Record<string, any>
|
|
221
|
+
style: StyleProp<ViewStyle>
|
|
222
|
+
activeStyle: StyleProp<ViewStyle>
|
|
223
|
+
hoverStyle: StyleProp<ViewStyle>
|
|
224
|
+
variant: 'opacity' | 'highlight'
|
|
225
|
+
isPressable: boolean
|
|
226
|
+
disabled?: boolean
|
|
227
|
+
accessibilityRole?: AccessibilityRole
|
|
228
|
+
feedback?: boolean
|
|
229
|
+
}): {
|
|
230
|
+
props: Record<string, any>
|
|
231
|
+
pressableStyle?: StyleProp<ViewStyle>
|
|
232
|
+
accessibilityRole?: AccessibilityRole
|
|
233
|
+
} {
|
|
234
|
+
let pressableStyle: StyleProp<ViewStyle> = {}
|
|
235
|
+
const [hover, setHover] = useState(false)
|
|
236
|
+
const [active, setActive] = useState(false)
|
|
237
|
+
|
|
238
|
+
// If component become not clickable, for example received 'disabled'
|
|
239
|
+
// prop while hover or active, state wouldn't update without this effect
|
|
240
|
+
useDidUpdate(() => {
|
|
241
|
+
if (!isPressable) return
|
|
242
|
+
if (disabled) {
|
|
243
|
+
if (hover) setHover(false)
|
|
244
|
+
if (active) setActive(false)
|
|
245
|
+
}
|
|
246
|
+
}, [isPressable, disabled])
|
|
247
|
+
|
|
248
|
+
// decorate the element state (hover, active) only if it's pressable
|
|
249
|
+
if (!isPressable) return { props }
|
|
250
|
+
|
|
251
|
+
accessibilityRole ??= 'button'
|
|
252
|
+
props.focusable ??= true
|
|
253
|
+
|
|
254
|
+
// setup hover and active states styles and props
|
|
255
|
+
if (feedback) {
|
|
256
|
+
const { onPressIn, onPressOut } = props
|
|
257
|
+
|
|
258
|
+
props.onPressIn = (...args: any[]) => {
|
|
259
|
+
setActive(true)
|
|
260
|
+
onPressIn?.(...args)
|
|
261
|
+
}
|
|
262
|
+
props.onPressOut = (...args: any[]) => {
|
|
263
|
+
setActive(false)
|
|
264
|
+
onPressOut?.(...args)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (isWeb && !disabled) {
|
|
268
|
+
const { onMouseEnter, onMouseLeave } = props
|
|
269
|
+
|
|
270
|
+
props.onMouseEnter = (...args: any[]) => {
|
|
271
|
+
setHover(true)
|
|
272
|
+
onMouseEnter?.(...args)
|
|
273
|
+
}
|
|
274
|
+
props.onMouseLeave = (...args: any[]) => {
|
|
275
|
+
setHover(false)
|
|
276
|
+
onMouseLeave?.(...args)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// hover or active state styles
|
|
280
|
+
// active state takes precedence over hover state
|
|
281
|
+
if (active) {
|
|
282
|
+
pressableStyle = activeStyle ?? getDefaultStyle(style, 'active', variant)
|
|
283
|
+
} else if (hover) {
|
|
284
|
+
pressableStyle = hoverStyle ?? getDefaultStyle(style, 'hover', variant)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// if disabled, disable all press handlers
|
|
289
|
+
for (const prop of PRESSABLE_PROPS) {
|
|
290
|
+
const pressHandler = props[prop]
|
|
291
|
+
if (!pressHandler) continue
|
|
292
|
+
props[prop] = (...args: any[]) => {
|
|
293
|
+
if (disabled) return
|
|
294
|
+
pressHandler(...args)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return { props, pressableStyle, accessibilityRole }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function getDefaultStyle (
|
|
302
|
+
style: StyleProp<ViewStyle>,
|
|
303
|
+
type: 'hover' | 'active',
|
|
304
|
+
variant?: 'opacity' | 'highlight'
|
|
305
|
+
): StyleProp<ViewStyle> | undefined {
|
|
306
|
+
if (variant === 'opacity') {
|
|
307
|
+
if (type === 'hover') return { opacity: defaultHoverOpacity }
|
|
308
|
+
if (type === 'active') return { opacity: defaultActiveOpacity }
|
|
309
|
+
} else {
|
|
310
|
+
style = StyleSheet.flatten(style)
|
|
311
|
+
let backgroundColor = style.backgroundColor
|
|
312
|
+
if (backgroundColor === 'transparent') backgroundColor = undefined
|
|
313
|
+
|
|
314
|
+
if (type === 'hover') {
|
|
315
|
+
if (backgroundColor) {
|
|
316
|
+
return { backgroundColor: colorToRGBA(backgroundColor as string, defaultHoverOpacity) }
|
|
317
|
+
} else {
|
|
318
|
+
// If no color exists, we treat it as a light background and just dim it a bit
|
|
319
|
+
return { backgroundColor: getCssVariable('--Div-hoverBg') as string | undefined }
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (type === 'active') {
|
|
324
|
+
if (backgroundColor) {
|
|
325
|
+
return { backgroundColor: colorToRGBA(backgroundColor as string, defaultActiveOpacity) }
|
|
326
|
+
} else {
|
|
327
|
+
// If no color exists, we treat it as a light background and just dim it a bit
|
|
328
|
+
return { backgroundColor: getCssVariable('--Div-activeBg') as string | undefined }
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function hasPressHandler (props: Record<string, any>): boolean {
|
|
335
|
+
return PRESSABLE_PROPS.some(prop => props[prop])
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function reverseMarginPaddingSides (style: StyleProp<ViewStyle>) {
|
|
339
|
+
style = StyleSheet.flatten(style)
|
|
340
|
+
const { paddingLeft, paddingRight, marginLeft, marginRight } = style
|
|
341
|
+
style.marginLeft = marginRight
|
|
342
|
+
style.marginRight = marginLeft
|
|
343
|
+
style.paddingLeft = paddingRight
|
|
344
|
+
style.paddingRight = paddingLeft
|
|
345
|
+
return style
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function assertDeprecatedValues ({ pushed, renderTooltip }: { pushed?: any, renderTooltip?: any }) {
|
|
349
|
+
if (DEPRECATED_PUSHED_VALUES.includes(pushed)) console.warn(ERRORS.DEPRECATED_PUSHED(pushed))
|
|
350
|
+
if (renderTooltip) console.warn(ERRORS.DEPRECATED_RENDER_TOOLTIP)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const ERRORS = {
|
|
354
|
+
DEPRECATED_PUSHED: (pushed: string) => `
|
|
355
|
+
[@startupjs/ui] Div: variant='${pushed}' is DEPRECATED, use one of 's', 'm', 'l' instead.
|
|
356
|
+
`,
|
|
357
|
+
DEPRECATED_RENDER_TOOLTIP: `
|
|
358
|
+
[@startupjs/ui] Div: renderTooltip is DEPRECATED, use 'tooltip' property instead.
|
|
359
|
+
`
|
|
360
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/div",
|
|
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
|
+
"./useTooltip": "./useTooltip.tsx"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@startupjs-ui/abstract-popover": "^0.1.3",
|
|
16
|
+
"@startupjs-ui/core": "^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
|
+
}
|
package/useTooltip.tsx
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo, useCallback, type ReactNode, type RefObject } from 'react'
|
|
2
|
+
import { Platform, View } from 'react-native'
|
|
3
|
+
import { pug, styl } from 'startupjs'
|
|
4
|
+
import Span from '@startupjs-ui/span'
|
|
5
|
+
import AbstractPopover from '@startupjs-ui/abstract-popover'
|
|
6
|
+
|
|
7
|
+
const isWeb = Platform.OS === 'web'
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TOOLTIP_PROPS = {
|
|
10
|
+
position: 'top',
|
|
11
|
+
attachment: 'center',
|
|
12
|
+
arrow: true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UseTooltipProps {
|
|
16
|
+
style?: any
|
|
17
|
+
anchorRef: RefObject<any>
|
|
18
|
+
tooltip?: ReactNode | (() => ReactNode)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type TooltipEventHandler = 'onMouseEnter' | 'onMouseLeave' | 'onLongPress' | 'onPressOut'
|
|
22
|
+
export type TooltipEventHandlers = Partial<Record<TooltipEventHandler, any>>
|
|
23
|
+
export const tooltipEventHandlersList: TooltipEventHandler[] = [
|
|
24
|
+
'onMouseEnter',
|
|
25
|
+
'onMouseLeave',
|
|
26
|
+
'onLongPress',
|
|
27
|
+
'onPressOut'
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
interface TooltipResult {
|
|
31
|
+
tooltipElement?: ReactNode
|
|
32
|
+
tooltipEventHandlers: TooltipEventHandlers
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function useTooltip ({ style, anchorRef, tooltip }: UseTooltipProps) {
|
|
36
|
+
const result: TooltipResult = { tooltipEventHandlers: {} }
|
|
37
|
+
const [visible, setVisible] = useState(false)
|
|
38
|
+
|
|
39
|
+
const cbSetVisibleTrue = useCallback(() => { setVisible(true) }, [])
|
|
40
|
+
const cbSetVisibleFalse = useCallback(() => { setVisible(false) }, [])
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!isWeb) return
|
|
44
|
+
if (!tooltip) return
|
|
45
|
+
|
|
46
|
+
window.addEventListener('wheel', cbSetVisibleFalse, true)
|
|
47
|
+
return () => {
|
|
48
|
+
window.removeEventListener('wheel', cbSetVisibleFalse, true)
|
|
49
|
+
}
|
|
50
|
+
}, [cbSetVisibleFalse, cbSetVisibleTrue, tooltip])
|
|
51
|
+
|
|
52
|
+
const tooltipEventHandlers = useMemo(() => {
|
|
53
|
+
const handlers: TooltipEventHandlers = {}
|
|
54
|
+
if (!tooltip) return handlers
|
|
55
|
+
|
|
56
|
+
if (isWeb) {
|
|
57
|
+
handlers.onMouseEnter = cbSetVisibleTrue
|
|
58
|
+
handlers.onMouseLeave = cbSetVisibleFalse
|
|
59
|
+
} else {
|
|
60
|
+
handlers.onLongPress = cbSetVisibleTrue
|
|
61
|
+
handlers.onPressOut = cbSetVisibleFalse
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return handlers
|
|
65
|
+
}, [cbSetVisibleFalse, cbSetVisibleTrue, tooltip])
|
|
66
|
+
|
|
67
|
+
result.tooltipEventHandlers = tooltipEventHandlers
|
|
68
|
+
|
|
69
|
+
if (tooltip) {
|
|
70
|
+
result.tooltipElement = pug`
|
|
71
|
+
AbstractPopover.tooltip(
|
|
72
|
+
style=style
|
|
73
|
+
anchorRef=anchorRef
|
|
74
|
+
visible=visible
|
|
75
|
+
...DEFAULT_TOOLTIP_PROPS
|
|
76
|
+
)
|
|
77
|
+
//- case for DEPRECATED renderTooltip property
|
|
78
|
+
if typeof tooltip === 'function'
|
|
79
|
+
= tooltip()
|
|
80
|
+
else
|
|
81
|
+
//- HACK
|
|
82
|
+
//- Wrap to row, because for small texts it does not correctly hyphenate in the text
|
|
83
|
+
//- For example $500,000, Copy, etc...
|
|
84
|
+
View(style={ flexDirection: 'row' })
|
|
85
|
+
Span.tooltip-text= tooltip
|
|
86
|
+
`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
styl`
|
|
92
|
+
.tooltip
|
|
93
|
+
background-color var(--Div-tooltipBg)
|
|
94
|
+
max-width 260px
|
|
95
|
+
radius()
|
|
96
|
+
shadow(3)
|
|
97
|
+
padding 1u 2u
|
|
98
|
+
|
|
99
|
+
+tablet()
|
|
100
|
+
max-width 320px
|
|
101
|
+
|
|
102
|
+
&-text
|
|
103
|
+
color var(--Div-tooltipText)
|
|
104
|
+
`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface UseDecorateTooltipProps {
|
|
108
|
+
props: Record<string, any>
|
|
109
|
+
style?: any
|
|
110
|
+
anchorRef: RefObject<any>
|
|
111
|
+
tooltip?: ReactNode | (() => ReactNode)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface DecorateTooltipResult {
|
|
115
|
+
props: Record<string, any>
|
|
116
|
+
tooltipElement?: ReactNode
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function useDecorateTooltipProps ({
|
|
120
|
+
props,
|
|
121
|
+
style,
|
|
122
|
+
anchorRef,
|
|
123
|
+
tooltip
|
|
124
|
+
}: UseDecorateTooltipProps): DecorateTooltipResult {
|
|
125
|
+
const { tooltipElement, tooltipEventHandlers } = useTooltip({ style, anchorRef, tooltip })
|
|
126
|
+
const {
|
|
127
|
+
onMouseEnter,
|
|
128
|
+
onMouseLeave,
|
|
129
|
+
onLongPress,
|
|
130
|
+
onPressOut
|
|
131
|
+
} = props as TooltipEventHandlers
|
|
132
|
+
|
|
133
|
+
const extraEventHandlerProps: TooltipEventHandlers = useMemo(() => {
|
|
134
|
+
const res: TooltipEventHandlers = {}
|
|
135
|
+
const divHandlers: TooltipEventHandlers = {
|
|
136
|
+
onMouseEnter,
|
|
137
|
+
onMouseLeave,
|
|
138
|
+
onLongPress,
|
|
139
|
+
onPressOut
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const handlerName of tooltipEventHandlersList) {
|
|
143
|
+
const tooltipHandler = tooltipEventHandlers[handlerName]
|
|
144
|
+
if (!tooltipHandler) continue
|
|
145
|
+
const divHandler = divHandlers[handlerName]
|
|
146
|
+
|
|
147
|
+
res[handlerName] = divHandler
|
|
148
|
+
? (...args: any[]) => {
|
|
149
|
+
tooltipHandler(...args)
|
|
150
|
+
divHandler(...args)
|
|
151
|
+
}
|
|
152
|
+
: tooltipHandler
|
|
153
|
+
}
|
|
154
|
+
return res
|
|
155
|
+
}, [
|
|
156
|
+
tooltipEventHandlers,
|
|
157
|
+
onMouseEnter,
|
|
158
|
+
onMouseLeave,
|
|
159
|
+
onLongPress,
|
|
160
|
+
onPressOut
|
|
161
|
+
])
|
|
162
|
+
|
|
163
|
+
Object.assign(props, extraEventHandlerProps)
|
|
164
|
+
|
|
165
|
+
return { props, tooltipElement }
|
|
166
|
+
}
|