@tamagui/button 1.142.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.
Files changed (81) hide show
  1. package/dist/cjs/Button.cjs +126 -158
  2. package/dist/cjs/Button.js +102 -120
  3. package/dist/cjs/Button.js.map +2 -2
  4. package/dist/cjs/Button.native.js +128 -160
  5. package/dist/cjs/Button.native.js.map +1 -1
  6. package/dist/cjs/v1/Button.cjs +259 -0
  7. package/dist/cjs/v1/Button.js +208 -0
  8. package/dist/cjs/v1/Button.js.map +6 -0
  9. package/dist/cjs/v1/Button.native.js +265 -0
  10. package/dist/cjs/v1/Button.native.js.map +1 -0
  11. package/dist/cjs/v1/Button.test.cjs +9 -0
  12. package/dist/cjs/v1/Button.test.js +8 -0
  13. package/dist/cjs/v1/Button.test.js.map +6 -0
  14. package/dist/cjs/v1/Button.test.native.js +12 -0
  15. package/dist/cjs/v1/Button.test.native.js.map +1 -0
  16. package/dist/cjs/v1/index.cjs +18 -0
  17. package/dist/cjs/v1/index.js +15 -0
  18. package/dist/cjs/v1/index.js.map +6 -0
  19. package/dist/cjs/v1/index.native.js +21 -0
  20. package/dist/cjs/v1/index.native.js.map +1 -0
  21. package/dist/esm/Button.js +108 -126
  22. package/dist/esm/Button.js.map +2 -2
  23. package/dist/esm/Button.mjs +127 -155
  24. package/dist/esm/Button.mjs.map +1 -1
  25. package/dist/esm/Button.native.js +130 -158
  26. package/dist/esm/Button.native.js.map +1 -1
  27. package/dist/esm/v1/Button.js +201 -0
  28. package/dist/esm/v1/Button.js.map +6 -0
  29. package/dist/esm/v1/Button.mjs +231 -0
  30. package/dist/esm/v1/Button.mjs.map +1 -0
  31. package/dist/esm/v1/Button.native.js +234 -0
  32. package/dist/esm/v1/Button.native.js.map +1 -0
  33. package/dist/esm/v1/Button.test.js +10 -0
  34. package/dist/esm/v1/Button.test.js.map +6 -0
  35. package/dist/esm/v1/Button.test.mjs +10 -0
  36. package/dist/esm/v1/Button.test.mjs.map +1 -0
  37. package/dist/esm/v1/Button.test.native.js +10 -0
  38. package/dist/esm/v1/Button.test.native.js.map +1 -0
  39. package/dist/esm/v1/index.js +2 -0
  40. package/dist/esm/v1/index.js.map +6 -0
  41. package/dist/esm/v1/index.mjs +2 -0
  42. package/dist/esm/v1/index.mjs.map +1 -0
  43. package/dist/esm/v1/index.native.js +2 -0
  44. package/dist/esm/v1/index.native.js.map +1 -0
  45. package/dist/jsx/Button.js +108 -126
  46. package/dist/jsx/Button.js.map +2 -2
  47. package/dist/jsx/Button.mjs +127 -155
  48. package/dist/jsx/Button.mjs.map +1 -1
  49. package/dist/jsx/Button.native.js +128 -160
  50. package/dist/jsx/Button.native.js.map +1 -1
  51. package/dist/jsx/v1/Button.js +201 -0
  52. package/dist/jsx/v1/Button.js.map +6 -0
  53. package/dist/jsx/v1/Button.mjs +231 -0
  54. package/dist/jsx/v1/Button.mjs.map +1 -0
  55. package/dist/jsx/v1/Button.native.js +265 -0
  56. package/dist/jsx/v1/Button.native.js.map +1 -0
  57. package/dist/jsx/v1/Button.test.js +10 -0
  58. package/dist/jsx/v1/Button.test.js.map +6 -0
  59. package/dist/jsx/v1/Button.test.mjs +10 -0
  60. package/dist/jsx/v1/Button.test.mjs.map +1 -0
  61. package/dist/jsx/v1/Button.test.native.js +12 -0
  62. package/dist/jsx/v1/Button.test.native.js.map +1 -0
  63. package/dist/jsx/v1/index.js +2 -0
  64. package/dist/jsx/v1/index.js.map +6 -0
  65. package/dist/jsx/v1/index.mjs +2 -0
  66. package/dist/jsx/v1/index.mjs.map +1 -0
  67. package/dist/jsx/v1/index.native.js +21 -0
  68. package/dist/jsx/v1/index.native.js.map +1 -0
  69. package/package.json +12 -11
  70. package/src/Button.tsx +172 -243
  71. package/src/v1/Button.test.tsx +21 -0
  72. package/src/v1/Button.tsx +336 -0
  73. package/src/v1/index.ts +1 -0
  74. package/types/Button.d.ts +80 -323
  75. package/types/Button.d.ts.map +1 -1
  76. package/types/v1/Button.d.ts +338 -0
  77. package/types/v1/Button.d.ts.map +1 -0
  78. package/types/v1/Button.test.d.ts +2 -0
  79. package/types/v1/Button.test.d.ts.map +1 -0
  80. package/types/v1/index.d.ts +2 -0
  81. package/types/v1/index.d.ts.map +1 -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 }
@@ -0,0 +1 @@
1
+ export * from './Button'