@tamagui/button 1.143.0 → 2.0.0-1
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/dist/cjs/Button.cjs +126 -158
- package/dist/cjs/Button.js +102 -120
- package/dist/cjs/Button.js.map +2 -2
- package/dist/cjs/Button.native.js +128 -160
- package/dist/cjs/Button.native.js.map +1 -1
- package/dist/esm/Button.js +108 -126
- package/dist/esm/Button.js.map +2 -2
- package/dist/esm/Button.mjs +127 -155
- package/dist/esm/Button.mjs.map +1 -1
- package/dist/esm/Button.native.js +130 -158
- package/dist/esm/Button.native.js.map +1 -1
- package/dist/jsx/Button.js +108 -126
- package/dist/jsx/Button.js.map +2 -2
- package/dist/jsx/Button.mjs +127 -155
- package/dist/jsx/Button.mjs.map +1 -1
- package/dist/jsx/Button.native.js +128 -160
- package/dist/jsx/Button.native.js.map +1 -1
- package/package.json +12 -11
- package/src/Button.tsx +172 -243
- package/src/v1/Button.test.tsx +21 -0
- package/src/v1/Button.tsx +336 -0
- package/src/v1/index.ts +1 -0
- package/types/Button.d.ts +80 -323
- package/types/Button.d.ts.map +1 -1
- package/types/v1/Button.d.ts +338 -0
- package/types/v1/Button.test.d.ts +2 -0
- package/types/v1/index.d.ts +2 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { getFontSize } from '@tamagui/font-size'
|
|
2
|
+
import { getButtonSized } from '@tamagui/get-button-sized'
|
|
3
|
+
import { withStaticProperties } from '@tamagui/helpers'
|
|
4
|
+
import { useGetThemedIcon } from '@tamagui/helpers-tamagui'
|
|
5
|
+
import { ButtonNestingContext, ThemeableStack } from '@tamagui/stacks'
|
|
6
|
+
import type { TextContextStyles, TextParentStyles } from '@tamagui/text'
|
|
7
|
+
import { SizableText, wrapChildrenInText } from '@tamagui/text'
|
|
8
|
+
import type { FontSizeTokens, GetProps, SizeTokens, ThemeableProps } from '@tamagui/web'
|
|
9
|
+
import { createStyledContext, getVariableValue, styled, useProps } from '@tamagui/web'
|
|
10
|
+
import type { FunctionComponent, JSX } from 'react'
|
|
11
|
+
import { useContext } from 'react'
|
|
12
|
+
import { spacedChildren } from '@tamagui/spacer'
|
|
13
|
+
|
|
14
|
+
type ButtonVariant = 'outlined'
|
|
15
|
+
|
|
16
|
+
export const ButtonContext = createStyledContext<
|
|
17
|
+
Partial<
|
|
18
|
+
TextContextStyles & {
|
|
19
|
+
size: SizeTokens
|
|
20
|
+
variant?: ButtonVariant
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
>({
|
|
24
|
+
// keeping these here means they work with styled() passing down color to text
|
|
25
|
+
color: undefined,
|
|
26
|
+
ellipsis: undefined,
|
|
27
|
+
fontFamily: undefined,
|
|
28
|
+
fontSize: undefined,
|
|
29
|
+
fontStyle: undefined,
|
|
30
|
+
fontWeight: undefined,
|
|
31
|
+
letterSpacing: undefined,
|
|
32
|
+
maxFontSizeMultiplier: undefined,
|
|
33
|
+
size: undefined,
|
|
34
|
+
textAlign: undefined,
|
|
35
|
+
variant: undefined,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
type ButtonIconProps = { color?: any; size?: any }
|
|
39
|
+
type IconProp =
|
|
40
|
+
| JSX.Element
|
|
41
|
+
| FunctionComponent<ButtonIconProps>
|
|
42
|
+
| ((props: ButtonIconProps) => any)
|
|
43
|
+
| null
|
|
44
|
+
|
|
45
|
+
type ButtonExtraProps = TextParentStyles &
|
|
46
|
+
ThemeableProps & {
|
|
47
|
+
/**
|
|
48
|
+
* add icon before, passes color and size automatically if Component
|
|
49
|
+
*/
|
|
50
|
+
icon?: IconProp
|
|
51
|
+
/**
|
|
52
|
+
* add icon after, passes color and size automatically if Component
|
|
53
|
+
*/
|
|
54
|
+
iconAfter?: IconProp
|
|
55
|
+
/**
|
|
56
|
+
* adjust icon relative to size
|
|
57
|
+
*
|
|
58
|
+
* @default 1
|
|
59
|
+
*/
|
|
60
|
+
scaleIcon?: number
|
|
61
|
+
/**
|
|
62
|
+
* make the spacing elements flex
|
|
63
|
+
*/
|
|
64
|
+
spaceFlex?: number | boolean
|
|
65
|
+
/**
|
|
66
|
+
* adjust internal space relative to icon size
|
|
67
|
+
*/
|
|
68
|
+
scaleSpace?: number
|
|
69
|
+
/**
|
|
70
|
+
* remove default styles
|
|
71
|
+
*/
|
|
72
|
+
unstyled?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type ButtonProps = ButtonExtraProps & GetProps<typeof ButtonFrame>
|
|
76
|
+
|
|
77
|
+
const BUTTON_NAME = 'Button'
|
|
78
|
+
|
|
79
|
+
const ButtonFrame = styled(ThemeableStack, {
|
|
80
|
+
name: BUTTON_NAME,
|
|
81
|
+
tag: 'button',
|
|
82
|
+
context: ButtonContext,
|
|
83
|
+
role: 'button',
|
|
84
|
+
focusable: true,
|
|
85
|
+
|
|
86
|
+
variants: {
|
|
87
|
+
unstyled: {
|
|
88
|
+
false: {
|
|
89
|
+
size: '$true',
|
|
90
|
+
justifyContent: 'center',
|
|
91
|
+
alignItems: 'center',
|
|
92
|
+
flexWrap: 'nowrap',
|
|
93
|
+
flexDirection: 'row',
|
|
94
|
+
cursor: 'pointer',
|
|
95
|
+
hoverTheme: true,
|
|
96
|
+
pressTheme: true,
|
|
97
|
+
backgrounded: true,
|
|
98
|
+
borderWidth: 1,
|
|
99
|
+
borderColor: 'transparent',
|
|
100
|
+
|
|
101
|
+
focusVisibleStyle: {
|
|
102
|
+
outlineColor: '$outlineColor',
|
|
103
|
+
outlineStyle: 'solid',
|
|
104
|
+
outlineWidth: 2,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
variant: {
|
|
110
|
+
outlined: {
|
|
111
|
+
backgroundColor: 'transparent',
|
|
112
|
+
borderWidth: 2,
|
|
113
|
+
borderColor: '$borderColor',
|
|
114
|
+
|
|
115
|
+
hoverStyle: {
|
|
116
|
+
backgroundColor: 'transparent',
|
|
117
|
+
borderColor: '$borderColorHover',
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
pressStyle: {
|
|
121
|
+
backgroundColor: 'transparent',
|
|
122
|
+
borderColor: '$borderColorPress',
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
focusVisibleStyle: {
|
|
126
|
+
backgroundColor: 'transparent',
|
|
127
|
+
borderColor: '$borderColorFocus',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
size: {
|
|
133
|
+
'...size': getButtonSized,
|
|
134
|
+
':number': getButtonSized,
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
disabled: {
|
|
138
|
+
true: {
|
|
139
|
+
pointerEvents: 'none',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
} as const,
|
|
143
|
+
|
|
144
|
+
defaultVariants: {
|
|
145
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const ButtonText = styled(SizableText, {
|
|
150
|
+
name: 'Button',
|
|
151
|
+
context: ButtonContext,
|
|
152
|
+
|
|
153
|
+
variants: {
|
|
154
|
+
unstyled: {
|
|
155
|
+
false: {
|
|
156
|
+
userSelect: 'none',
|
|
157
|
+
cursor: 'pointer',
|
|
158
|
+
// flexGrow 1 leads to inconsistent native style where text pushes to start of view
|
|
159
|
+
flexGrow: 0,
|
|
160
|
+
flexShrink: 1,
|
|
161
|
+
ellipsis: true,
|
|
162
|
+
color: '$color',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
} as const,
|
|
166
|
+
|
|
167
|
+
defaultVariants: {
|
|
168
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const ButtonIcon = (props: { children: React.ReactNode; scaleIcon?: number }) => {
|
|
173
|
+
const { children, scaleIcon = 1 } = props
|
|
174
|
+
const { size, color } = useContext(ButtonContext)
|
|
175
|
+
|
|
176
|
+
const iconSize =
|
|
177
|
+
(typeof size === 'number' ? size * 0.5 : getFontSize(size as FontSizeTokens)) *
|
|
178
|
+
scaleIcon
|
|
179
|
+
|
|
180
|
+
const getThemedIcon = useGetThemedIcon({ size: iconSize, color: color as any })
|
|
181
|
+
return getThemedIcon(children)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const ButtonComponent = ButtonFrame.styleable<ButtonExtraProps>(
|
|
185
|
+
function Button(props, ref) {
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
const { props: buttonProps } = useButton(props)
|
|
188
|
+
|
|
189
|
+
return <ButtonFrame data-disable-theme {...buttonProps} ref={ref} />
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
/**
|
|
193
|
+
* @summary A Button is a clickable element that can be used to trigger actions such as submitting forms, navigating to other pages, or performing other actions.
|
|
194
|
+
* @see — Docs https://tamagui.dev/ui/button
|
|
195
|
+
*/
|
|
196
|
+
const Button = withStaticProperties(ButtonComponent, {
|
|
197
|
+
Text: ButtonText,
|
|
198
|
+
Icon: ButtonIcon,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @deprecated Instead of useButton, see the Button docs for the newer and much improved Advanced customization pattern: https://tamagui.dev/docs/components/button
|
|
203
|
+
*/
|
|
204
|
+
function useButton<Props extends ButtonProps>(
|
|
205
|
+
{ textProps, ...propsIn }: Props,
|
|
206
|
+
{ Text = Button.Text }: { Text: any } = { Text: Button.Text }
|
|
207
|
+
) {
|
|
208
|
+
const isNested = useContext(ButtonNestingContext)
|
|
209
|
+
const propsActive = useProps(propsIn, {
|
|
210
|
+
noNormalize: true,
|
|
211
|
+
noExpand: true,
|
|
212
|
+
}) as any as ButtonProps
|
|
213
|
+
|
|
214
|
+
// careful not to destructure and re-order props, order is important
|
|
215
|
+
const {
|
|
216
|
+
icon,
|
|
217
|
+
iconAfter,
|
|
218
|
+
gap,
|
|
219
|
+
spaceFlex,
|
|
220
|
+
scaleIcon = 1,
|
|
221
|
+
scaleSpace = 0.66,
|
|
222
|
+
noTextWrap,
|
|
223
|
+
fontFamily,
|
|
224
|
+
fontSize,
|
|
225
|
+
fontWeight,
|
|
226
|
+
fontStyle,
|
|
227
|
+
letterSpacing,
|
|
228
|
+
tag,
|
|
229
|
+
ellipsis,
|
|
230
|
+
maxFontSizeMultiplier,
|
|
231
|
+
|
|
232
|
+
...restProps
|
|
233
|
+
} = propsActive
|
|
234
|
+
|
|
235
|
+
const size = propsActive.size || (propsActive.unstyled ? undefined : '$true')
|
|
236
|
+
|
|
237
|
+
const color = propsActive.color as any
|
|
238
|
+
|
|
239
|
+
const iconSize =
|
|
240
|
+
(typeof size === 'number'
|
|
241
|
+
? size * 0.5
|
|
242
|
+
: getFontSize(size as FontSizeTokens, {
|
|
243
|
+
font: fontFamily?.[0] === '$' ? (fontFamily as any) : undefined,
|
|
244
|
+
})) * scaleIcon
|
|
245
|
+
|
|
246
|
+
const getThemedIcon = useGetThemedIcon({
|
|
247
|
+
size: iconSize,
|
|
248
|
+
color,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
const [themedIcon, themedIconAfter] = [icon, iconAfter].map(getThemedIcon)
|
|
252
|
+
const spaceSize = gap ?? getVariableValue(iconSize) * scaleSpace
|
|
253
|
+
const contents = noTextWrap
|
|
254
|
+
? [propsIn.children]
|
|
255
|
+
: wrapChildrenInText(
|
|
256
|
+
Text,
|
|
257
|
+
{
|
|
258
|
+
children: propsIn.children,
|
|
259
|
+
fontFamily,
|
|
260
|
+
fontSize,
|
|
261
|
+
textProps,
|
|
262
|
+
fontWeight,
|
|
263
|
+
fontStyle,
|
|
264
|
+
letterSpacing,
|
|
265
|
+
ellipsis,
|
|
266
|
+
maxFontSizeMultiplier,
|
|
267
|
+
},
|
|
268
|
+
Text === ButtonText && propsActive.unstyled !== true
|
|
269
|
+
? {
|
|
270
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
271
|
+
size,
|
|
272
|
+
}
|
|
273
|
+
: undefined
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
const inner = spacedChildren({
|
|
277
|
+
// a bit arbitrary but scaling to font size is necessary so long as button does
|
|
278
|
+
space: spaceSize,
|
|
279
|
+
spaceFlex,
|
|
280
|
+
ensureKeys: true,
|
|
281
|
+
direction:
|
|
282
|
+
propsActive.flexDirection === 'column' ||
|
|
283
|
+
propsActive.flexDirection === 'column-reverse'
|
|
284
|
+
? 'vertical'
|
|
285
|
+
: 'horizontal',
|
|
286
|
+
// for keys to stay the same we keep indices as similar a possible
|
|
287
|
+
// so even if icons are undefined we still pass them
|
|
288
|
+
children: [themedIcon, ...contents, themedIconAfter],
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
const props = {
|
|
292
|
+
size,
|
|
293
|
+
...(propsIn.disabled && {
|
|
294
|
+
// in rnw - false still has keyboard tabIndex, undefined = not actually focusable
|
|
295
|
+
focusable: undefined,
|
|
296
|
+
// even with tabIndex unset, it will keep focusVisibleStyle on web so disable it here
|
|
297
|
+
focusVisibleStyle: {
|
|
298
|
+
borderColor: '$background',
|
|
299
|
+
},
|
|
300
|
+
}),
|
|
301
|
+
// fixes SSR issue + DOM nesting issue of not allowing button in button
|
|
302
|
+
tag:
|
|
303
|
+
tag ??
|
|
304
|
+
(isNested
|
|
305
|
+
? 'span'
|
|
306
|
+
: // defaults to <a /> when accessibilityRole = link
|
|
307
|
+
// see https://github.com/tamagui/tamagui/issues/505
|
|
308
|
+
propsActive.accessibilityRole === 'link' || propsActive.role === 'link'
|
|
309
|
+
? 'a'
|
|
310
|
+
: 'button'),
|
|
311
|
+
|
|
312
|
+
...restProps,
|
|
313
|
+
|
|
314
|
+
children: (
|
|
315
|
+
<ButtonNestingContext.Provider value={true}>{inner}</ButtonNestingContext.Provider>
|
|
316
|
+
),
|
|
317
|
+
// forces it to be a runtime pressStyle so it passes through context text colors
|
|
318
|
+
disableClassName: true,
|
|
319
|
+
} as Props
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
spaceSize,
|
|
323
|
+
isNested,
|
|
324
|
+
props,
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export {
|
|
329
|
+
Button,
|
|
330
|
+
ButtonFrame,
|
|
331
|
+
ButtonIcon,
|
|
332
|
+
ButtonText,
|
|
333
|
+
// legacy
|
|
334
|
+
useButton,
|
|
335
|
+
}
|
|
336
|
+
export type { ButtonProps }
|
package/src/v1/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Button'
|