@hanzogui/button 2.0.0

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/Button.cjs +218 -0
  3. package/dist/cjs/Button.native.js +224 -0
  4. package/dist/cjs/Button.native.js.map +1 -0
  5. package/dist/cjs/Button.test.cjs +36 -0
  6. package/dist/cjs/Button.test.native.js +39 -0
  7. package/dist/cjs/Button.test.native.js.map +1 -0
  8. package/dist/cjs/index.cjs +18 -0
  9. package/dist/cjs/index.native.js +21 -0
  10. package/dist/cjs/index.native.js.map +1 -0
  11. package/dist/cjs/v1/Button.cjs +259 -0
  12. package/dist/cjs/v1/Button.native.js +265 -0
  13. package/dist/cjs/v1/Button.native.js.map +1 -0
  14. package/dist/cjs/v1/Button.test.cjs +9 -0
  15. package/dist/cjs/v1/Button.test.native.js +12 -0
  16. package/dist/cjs/v1/Button.test.native.js.map +1 -0
  17. package/dist/cjs/v1/index.cjs +18 -0
  18. package/dist/cjs/v1/index.native.js +21 -0
  19. package/dist/cjs/v1/index.native.js.map +1 -0
  20. package/dist/esm/Button.mjs +194 -0
  21. package/dist/esm/Button.mjs.map +1 -0
  22. package/dist/esm/Button.native.js +197 -0
  23. package/dist/esm/Button.native.js.map +1 -0
  24. package/dist/esm/Button.test.mjs +37 -0
  25. package/dist/esm/Button.test.mjs.map +1 -0
  26. package/dist/esm/Button.test.native.js +37 -0
  27. package/dist/esm/Button.test.native.js.map +1 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/esm/index.js.map +1 -0
  30. package/dist/esm/index.mjs +2 -0
  31. package/dist/esm/index.mjs.map +1 -0
  32. package/dist/esm/index.native.js +2 -0
  33. package/dist/esm/index.native.js.map +1 -0
  34. package/dist/esm/v1/Button.mjs +231 -0
  35. package/dist/esm/v1/Button.mjs.map +1 -0
  36. package/dist/esm/v1/Button.native.js +234 -0
  37. package/dist/esm/v1/Button.native.js.map +1 -0
  38. package/dist/esm/v1/Button.test.mjs +10 -0
  39. package/dist/esm/v1/Button.test.mjs.map +1 -0
  40. package/dist/esm/v1/Button.test.native.js +10 -0
  41. package/dist/esm/v1/Button.test.native.js.map +1 -0
  42. package/dist/esm/v1/index.mjs +2 -0
  43. package/dist/esm/v1/index.mjs.map +1 -0
  44. package/dist/esm/v1/index.native.js +2 -0
  45. package/dist/esm/v1/index.native.js.map +1 -0
  46. package/dist/jsx/Button.mjs +194 -0
  47. package/dist/jsx/Button.mjs.map +1 -0
  48. package/dist/jsx/Button.native.js +224 -0
  49. package/dist/jsx/Button.native.js.map +1 -0
  50. package/dist/jsx/Button.test.mjs +37 -0
  51. package/dist/jsx/Button.test.mjs.map +1 -0
  52. package/dist/jsx/Button.test.native.js +39 -0
  53. package/dist/jsx/Button.test.native.js.map +1 -0
  54. package/dist/jsx/index.js +2 -0
  55. package/dist/jsx/index.js.map +1 -0
  56. package/dist/jsx/index.mjs +2 -0
  57. package/dist/jsx/index.mjs.map +1 -0
  58. package/dist/jsx/index.native.js +21 -0
  59. package/dist/jsx/index.native.js.map +1 -0
  60. package/dist/jsx/v1/Button.mjs +231 -0
  61. package/dist/jsx/v1/Button.mjs.map +1 -0
  62. package/dist/jsx/v1/Button.native.js +265 -0
  63. package/dist/jsx/v1/Button.native.js.map +1 -0
  64. package/dist/jsx/v1/Button.test.mjs +10 -0
  65. package/dist/jsx/v1/Button.test.mjs.map +1 -0
  66. package/dist/jsx/v1/Button.test.native.js +12 -0
  67. package/dist/jsx/v1/Button.test.native.js.map +1 -0
  68. package/dist/jsx/v1/index.mjs +2 -0
  69. package/dist/jsx/v1/index.mjs.map +1 -0
  70. package/dist/jsx/v1/index.native.js +21 -0
  71. package/dist/jsx/v1/index.native.js.map +1 -0
  72. package/package.json +59 -0
  73. package/src/Button.test.tsx +45 -0
  74. package/src/Button.tsx +272 -0
  75. package/src/index.ts +1 -0
  76. package/src/v1/Button.test.tsx +21 -0
  77. package/src/v1/Button.tsx +336 -0
  78. package/src/v1/index.ts +1 -0
  79. package/types/Button.d.ts +123 -0
  80. package/types/Button.test.d.ts +2 -0
  81. package/types/index.d.ts +2 -0
  82. package/types/v1/Button.d.ts +302 -0
  83. package/types/v1/Button.test.d.ts +2 -0
  84. package/types/v1/index.d.ts +2 -0
@@ -0,0 +1,45 @@
1
+ import { getDefaultGuiConfig } from '@hanzogui/config-default'
2
+ import { createGui } from '@hanzogui/core'
3
+ import { describe, expect, test } from 'vitest'
4
+ import { Button } from './Button'
5
+
6
+ const conf = createGui(getDefaultGuiConfig())
7
+
8
+ describe('Button', () => {
9
+ test(`123`, () => {
10
+ expect(true).toBeTruthy()
11
+ })
12
+
13
+ // type tests for native button props (issue #3914)
14
+ test('accepts native button html props', () => {
15
+ // these should type check without errors
16
+ const _submitBtn = <Button type="submit">Submit</Button>
17
+ const _resetBtn = <Button type="reset">Reset</Button>
18
+ const _buttonBtn = <Button type="button">Button</Button>
19
+ const _formBtn = (
20
+ <Button
21
+ type="submit"
22
+ form="myForm"
23
+ formAction="/submit"
24
+ formMethod="post"
25
+ formTarget="_blank"
26
+ formNoValidate
27
+ name="submitBtn"
28
+ value="submit"
29
+ >
30
+ Submit
31
+ </Button>
32
+ )
33
+ expect(true).toBeTruthy()
34
+ })
35
+
36
+ // test(`Adapts to a when given accessibilityRole="link"`, async () => {
37
+ // const { container } = render(
38
+ // <GuiProvider config={conf} defaultTheme="light">
39
+ // <Button href="http://google.com" accessibilityRole="link" />
40
+ // </GuiProvider>
41
+ // )
42
+
43
+ // expect(container.firstChild).toMatchSnapshot()
44
+ // })
45
+ })
package/src/Button.tsx ADDED
@@ -0,0 +1,272 @@
1
+ import { getFontSize } from '@hanzogui/font-size'
2
+ import { getButtonSized } from '@hanzogui/get-button-sized'
3
+ import { getIcon, useCurrentColor } from '@hanzogui/helpers'
4
+ import { ButtonNestingContext, getElevation, themeableVariants } from '@hanzogui/stacks'
5
+ import { SizableText, wrapChildrenInText } from '@hanzogui/text'
6
+ import type { ColorTokens, GetProps, SizeTokens, Token } from '@hanzogui/web'
7
+ import {
8
+ createStyledContext,
9
+ getTokenValue,
10
+ styled,
11
+ useProps,
12
+ View,
13
+ withStaticProperties,
14
+ } from '@hanzogui/web'
15
+ import type { FunctionComponent, JSX } from 'react'
16
+ import { useContext } from 'react'
17
+
18
+ type ButtonVariant = 'outlined'
19
+
20
+ const context = createStyledContext<{
21
+ size?: SizeTokens
22
+ variant?: ButtonVariant
23
+ color?: ColorTokens | string
24
+ elevation?: SizeTokens | number
25
+ }>({
26
+ size: undefined,
27
+ variant: undefined,
28
+ color: undefined,
29
+ elevation: undefined,
30
+ })
31
+
32
+ const Frame = styled(View, {
33
+ context,
34
+ name: 'Button',
35
+ role: 'button',
36
+ render: <button type="button" />,
37
+ tabIndex: 0,
38
+
39
+ variants: {
40
+ unstyled: {
41
+ false: {
42
+ size: '$true',
43
+ justifyContent: 'center',
44
+ alignItems: 'center',
45
+ flexWrap: 'nowrap',
46
+ flexDirection: 'row',
47
+ cursor: 'pointer',
48
+ backgroundColor: '$background',
49
+ borderWidth: 1,
50
+ borderColor: 'transparent',
51
+
52
+ hoverStyle: {
53
+ backgroundColor: '$backgroundHover',
54
+ borderColor: '$borderColorHover',
55
+ },
56
+
57
+ pressStyle: {
58
+ backgroundColor: '$backgroundPress',
59
+ borderColor: '$borderColorHover',
60
+ },
61
+
62
+ focusVisibleStyle: {
63
+ outlineColor: '$outlineColor',
64
+ outlineStyle: 'solid',
65
+ outlineWidth: 2,
66
+ },
67
+ },
68
+ },
69
+
70
+ variant: {
71
+ outlined:
72
+ process.env.HANZO_GUI_HEADLESS === '1'
73
+ ? {}
74
+ : {
75
+ backgroundColor: 'transparent',
76
+ borderWidth: 1,
77
+ borderColor: '$borderColor',
78
+
79
+ hoverStyle: {
80
+ backgroundColor: 'transparent',
81
+ borderColor: '$borderColorHover',
82
+ },
83
+
84
+ pressStyle: {
85
+ backgroundColor: 'transparent',
86
+ borderColor: '$borderColorPress',
87
+ },
88
+ },
89
+ },
90
+
91
+ circular: themeableVariants.circular,
92
+
93
+ chromeless: themeableVariants.chromeless,
94
+
95
+ size: {
96
+ '...size': (val, extras) => {
97
+ const buttonStyle = getButtonSized(val, extras)
98
+ const gap = getTokenValue(val as Token)
99
+ return {
100
+ ...buttonStyle,
101
+ gap,
102
+ }
103
+ },
104
+ ':number': (val, extras) => {
105
+ const buttonStyle = getButtonSized(val, extras)
106
+ const gap = val * 0.4
107
+ return {
108
+ ...buttonStyle,
109
+ gap,
110
+ }
111
+ },
112
+ },
113
+
114
+ elevation: {
115
+ '...size': getElevation,
116
+ ':number': getElevation,
117
+ },
118
+
119
+ disabled: {
120
+ true: {
121
+ pointerEvents: 'none',
122
+ // @ts-ignore
123
+ 'aria-disabled': true,
124
+ },
125
+ },
126
+ } as const,
127
+
128
+ defaultVariants: {
129
+ unstyled: process.env.HANZO_GUI_HEADLESS === '1',
130
+ },
131
+ })
132
+
133
+ const Text = styled(SizableText, {
134
+ context,
135
+
136
+ variants: {
137
+ unstyled: {
138
+ false: {
139
+ userSelect: 'none',
140
+ cursor: 'pointer',
141
+ // flexGrow 1 leads to inconsistent native style where text pushes to start of view
142
+ flexGrow: 0,
143
+ flexShrink: 1,
144
+ ellipsis: true,
145
+ color: '$color',
146
+ },
147
+ },
148
+ } as const,
149
+
150
+ defaultVariants: {
151
+ unstyled: process.env.HANZO_GUI_HEADLESS === '1',
152
+ },
153
+ })
154
+
155
+ const Icon = (props: {
156
+ children: React.ReactNode
157
+ scaleIcon?: number
158
+ size?: SizeTokens
159
+ }) => {
160
+ const { children, scaleIcon = 1, size } = props
161
+ const styledContext = context.useStyledContext()
162
+ if (!styledContext) {
163
+ throw new Error('Button.Icon must be used within a Button')
164
+ }
165
+
166
+ const sizeToken = size ?? styledContext.size
167
+ const iconColor = useCurrentColor(styledContext.color)
168
+
169
+ const iconSize =
170
+ (typeof sizeToken === 'number' ? sizeToken * 0.5 : getFontSize(sizeToken as Token)) *
171
+ scaleIcon
172
+
173
+ return getIcon(children, {
174
+ size: iconSize,
175
+ color: iconColor,
176
+ })
177
+ }
178
+
179
+ export const ButtonContext = createStyledContext<{
180
+ size?: SizeTokens
181
+ variant?: ButtonVariant
182
+ color?: ColorTokens | string
183
+ }>({
184
+ size: undefined,
185
+ variant: undefined,
186
+ color: undefined,
187
+ })
188
+
189
+ type IconProp = JSX.Element | FunctionComponent<{ color?: any; size?: any }> | null
190
+
191
+ type ButtonExtraProps = {
192
+ icon?: IconProp
193
+ iconAfter?: IconProp
194
+ scaleIcon?: number
195
+ iconSize?: SizeTokens
196
+
197
+ // native button html props
198
+ type?: 'submit' | 'reset' | 'button'
199
+ form?: string
200
+ formAction?: string
201
+ formEncType?: string
202
+ formMethod?: string
203
+ formNoValidate?: boolean
204
+ formTarget?: string
205
+ name?: string
206
+ value?: string | readonly string[] | number
207
+ }
208
+
209
+ const ButtonComponent = Frame.styleable<ButtonExtraProps>((propsIn, ref) => {
210
+ const isNested = useContext(ButtonNestingContext)
211
+
212
+ // Process props through useProps to expand shorthands (like br -> borderRadius)
213
+ const processedProps = useProps(propsIn, {
214
+ noNormalize: true,
215
+ noExpand: true,
216
+ })
217
+
218
+ const { children, iconSize, icon, iconAfter, scaleIcon = 1, ...props } = processedProps
219
+
220
+ const size = propsIn.size || (propsIn.unstyled ? undefined : '$true')
221
+
222
+ const styledContext = context.useStyledContext()
223
+ const iconColor = useCurrentColor(styledContext?.color)
224
+ const finalSize = iconSize ?? size ?? styledContext?.size
225
+ const iconSizeNumber =
226
+ (typeof finalSize === 'number' ? finalSize * 0.5 : getFontSize(finalSize as Token)) *
227
+ scaleIcon
228
+
229
+ const [themedIcon, themedIconAfter] = [icon, iconAfter].map((icon) => {
230
+ if (!icon) return null
231
+ return getIcon(icon, {
232
+ size: iconSizeNumber,
233
+ color: iconColor,
234
+ // No marginLeft or marginRight needed - spacing is handled by the gap property in Frame's size variants
235
+ })
236
+ })
237
+
238
+ const wrappedChildren = wrapChildrenInText(
239
+ Text,
240
+ { children },
241
+ {
242
+ unstyled: process.env.HANZO_GUI_HEADLESS === '1',
243
+ size: finalSize ?? styledContext?.size,
244
+ }
245
+ )
246
+
247
+ return (
248
+ <ButtonNestingContext.Provider value={true}>
249
+ <Frame
250
+ ref={ref}
251
+ {...props}
252
+ {...(isNested && { render: 'span' })}
253
+ // Pass resolved size to circular variant when no explicit size provided
254
+ {...(props.circular && !propsIn.size && { size })}
255
+ tabIndex={0}
256
+ >
257
+ {themedIcon}
258
+ {wrappedChildren}
259
+ {themedIconAfter}
260
+ </Frame>
261
+ </ButtonNestingContext.Provider>
262
+ )
263
+ })
264
+
265
+ export const Button = withStaticProperties(ButtonComponent, {
266
+ Apply: context.Provider,
267
+ Frame,
268
+ Text,
269
+ Icon,
270
+ })
271
+
272
+ export type ButtonProps = GetProps<typeof ButtonComponent>
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './Button'
@@ -0,0 +1,21 @@
1
+ import { getDefaultGuiConfig } from '@hanzogui/config-default'
2
+ import { createGui } from '@hanzogui/core'
3
+ import { describe, expect, test } from 'vitest'
4
+
5
+ const conf = createGui(getDefaultGuiConfig())
6
+
7
+ describe('Button', () => {
8
+ test(`123`, () => {
9
+ expect(true).toBeTruthy()
10
+ })
11
+
12
+ // test(`Adapts to a when given accessibilityRole="link"`, async () => {
13
+ // const { container } = render(
14
+ // <GuiProvider config={conf} defaultTheme="light">
15
+ // <Button href="http://google.com" accessibilityRole="link" />
16
+ // </GuiProvider>
17
+ // )
18
+
19
+ // expect(container.firstChild).toMatchSnapshot()
20
+ // })
21
+ })
@@ -0,0 +1,336 @@
1
+ import { getFontSize } from '@hanzogui/font-size'
2
+ import { getButtonSized } from '@hanzogui/get-button-sized'
3
+ import { withStaticProperties } from '@hanzogui/helpers'
4
+ import { useGetThemedIcon } from '@hanzogui/helpers'
5
+ import { ButtonNestingContext, ThemeableStack } from '@hanzogui/stacks'
6
+ import type { TextContextStyles, TextParentStyles } from '@hanzogui/text'
7
+ import { SizableText, wrapChildrenInText } from '@hanzogui/text'
8
+ import type { FontSizeTokens, GetProps, SizeTokens, ThemeableProps } from '@hanzogui/web'
9
+ import { createStyledContext, getVariableValue, styled, useProps } from '@hanzogui/web'
10
+ import type { FunctionComponent, JSX } from 'react'
11
+ import { useContext } from 'react'
12
+ import { spacedChildren } from '@hanzogui/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
+ render: '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
+ backgroundColor: '$background',
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.HANZO_GUI_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.HANZO_GUI_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://gui.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://gui.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
+ render,
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.HANZO_GUI_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
+ render:
303
+ render ??
304
+ (isNested
305
+ ? 'span'
306
+ : // defaults to <a /> when accessibilityRole = link
307
+ // see https://github.com/gui/gui/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'