@tamagui/switch 2.7.7 → 3.0.0-beta.643.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 (97) hide show
  1. package/dist/cjs/StyledContext.cjs +19 -23
  2. package/dist/cjs/StyledContext.native.cjs +33 -0
  3. package/dist/cjs/StyledContext.native.js +19 -22
  4. package/dist/cjs/StyledContext.native.js.map +1 -1
  5. package/dist/cjs/Switch.cjs +140 -86
  6. package/dist/cjs/Switch.native.cjs +163 -0
  7. package/dist/cjs/Switch.native.js +147 -90
  8. package/dist/cjs/Switch.native.js.map +1 -1
  9. package/dist/cjs/index.cjs +14 -25
  10. package/dist/cjs/index.native.cjs +31 -0
  11. package/dist/cjs/index.native.js +15 -25
  12. package/dist/cjs/index.native.js.map +1 -1
  13. package/dist/cjs/types.cjs +10 -11
  14. package/dist/cjs/types.native.cjs +19 -0
  15. package/dist/cjs/types.native.js +10 -10
  16. package/dist/cjs/types.native.js.map +1 -1
  17. package/dist/cjs/useSwitchNative.cjs +16 -19
  18. package/dist/cjs/useSwitchNative.native.cjs +65 -0
  19. package/dist/cjs/useSwitchNative.native.js +44 -54
  20. package/dist/cjs/useSwitchNative.native.js.map +1 -1
  21. package/dist/esm/StyledContext.mjs +6 -5
  22. package/dist/esm/StyledContext.mjs.map +1 -1
  23. package/dist/esm/StyledContext.native.js +6 -5
  24. package/dist/esm/StyledContext.native.js.map +1 -1
  25. package/dist/esm/Switch.mjs +119 -71
  26. package/dist/esm/Switch.mjs.map +1 -1
  27. package/dist/esm/Switch.native.js +126 -76
  28. package/dist/esm/Switch.native.js.map +1 -1
  29. package/dist/esm/index.js +8 -12
  30. package/dist/esm/index.mjs +8 -12
  31. package/dist/esm/index.native.js +8 -12
  32. package/dist/esm/types.mjs +0 -2
  33. package/dist/esm/types.native.js +0 -2
  34. package/dist/esm/useSwitchNative.mjs +2 -1
  35. package/dist/esm/useSwitchNative.mjs.map +1 -1
  36. package/dist/esm/useSwitchNative.native.js +30 -31
  37. package/dist/esm/useSwitchNative.native.js.map +1 -1
  38. package/dist/jsx/StyledContext.mjs +6 -5
  39. package/dist/jsx/StyledContext.mjs.map +1 -1
  40. package/dist/jsx/StyledContext.native.cjs +33 -0
  41. package/dist/jsx/StyledContext.native.js +19 -22
  42. package/dist/jsx/StyledContext.native.js.map +1 -1
  43. package/dist/jsx/Switch.mjs +119 -71
  44. package/dist/jsx/Switch.mjs.map +1 -1
  45. package/dist/jsx/Switch.native.cjs +163 -0
  46. package/dist/jsx/Switch.native.js +147 -90
  47. package/dist/jsx/Switch.native.js.map +1 -1
  48. package/dist/jsx/index.js +8 -12
  49. package/dist/jsx/index.mjs +8 -12
  50. package/dist/jsx/index.native.cjs +31 -0
  51. package/dist/jsx/index.native.js +15 -25
  52. package/dist/jsx/index.native.js.map +1 -1
  53. package/dist/jsx/types.mjs +0 -2
  54. package/dist/jsx/types.native.cjs +19 -0
  55. package/dist/jsx/types.native.js +10 -10
  56. package/dist/jsx/types.native.js.map +1 -1
  57. package/dist/jsx/useSwitchNative.mjs +2 -1
  58. package/dist/jsx/useSwitchNative.mjs.map +1 -1
  59. package/dist/jsx/useSwitchNative.native.cjs +65 -0
  60. package/dist/jsx/useSwitchNative.native.js +44 -54
  61. package/dist/jsx/useSwitchNative.native.js.map +1 -1
  62. package/package.json +7 -14
  63. package/src/StyledContext.tsx +1 -3
  64. package/src/Switch.tsx +190 -89
  65. package/src/index.ts +0 -20
  66. package/src/types.ts +3 -5
  67. package/types/StyledContext.d.ts +2 -3
  68. package/types/StyledContext.d.ts.map +1 -1
  69. package/types/Switch.d.ts +209 -37
  70. package/types/Switch.d.ts.map +1 -1
  71. package/types/index.d.ts +0 -146
  72. package/types/index.d.ts.map +1 -1
  73. package/types/types.d.ts +3 -4
  74. package/types/types.d.ts.map +1 -1
  75. package/dist/cjs/createSwitch.cjs +0 -186
  76. package/dist/cjs/createSwitch.native.js +0 -194
  77. package/dist/cjs/createSwitch.native.js.map +0 -1
  78. package/dist/esm/createSwitch.mjs +0 -150
  79. package/dist/esm/createSwitch.mjs.map +0 -1
  80. package/dist/esm/createSwitch.native.js +0 -155
  81. package/dist/esm/createSwitch.native.js.map +0 -1
  82. package/dist/esm/index.js.map +0 -1
  83. package/dist/esm/index.mjs.map +0 -1
  84. package/dist/esm/index.native.js.map +0 -1
  85. package/dist/esm/types.mjs.map +0 -1
  86. package/dist/esm/types.native.js.map +0 -1
  87. package/dist/jsx/createSwitch.mjs +0 -150
  88. package/dist/jsx/createSwitch.mjs.map +0 -1
  89. package/dist/jsx/createSwitch.native.js +0 -194
  90. package/dist/jsx/createSwitch.native.js.map +0 -1
  91. package/dist/jsx/index.js.map +0 -1
  92. package/dist/jsx/index.mjs.map +0 -1
  93. package/dist/jsx/types.mjs.map +0 -1
  94. package/src/Switch.draggable-tmp +0 -340
  95. package/src/createSwitch.tsx +0 -201
  96. package/types/createSwitch.d.ts +0 -141
  97. package/types/createSwitch.d.ts.map +0 -1
@@ -1,340 +0,0 @@
1
- // via radix
2
- // https://github.com/radix-ui/primitives/blob/main/packages/react/switch/src/Switch.tsx
3
-
4
- import { usePrevious } from '@tamagui/use-previous'
5
- import { useComposedRefs } from '@tamagui/compose-refs'
6
- import {
7
- GetProps,
8
- SizeTokens,
9
- calc,
10
- getSize,
11
- getVariableValue,
12
- isWeb,
13
- styled,
14
- withStaticProperties,
15
- } from '@tamagui/core'
16
- import { ScopedProps, createContextScope } from '@tamagui/create-context'
17
- import { registerFocusable } from '@tamagui/focusable'
18
- import { useLabelContext } from '@tamagui/label'
19
- import { ThemeableStack, XStack } from '@tamagui/stacks'
20
- import { useControllableState } from '@tamagui/use-controllable-state'
21
- import * as React from 'react'
22
- import { Animated, LayoutRectangle, PanResponder, View } from 'react-native'
23
-
24
- const SWITCH_NAME = 'Switch'
25
-
26
- // TODO make customizable
27
- const getSwitchHeight = (val: SizeTokens) => Math.round(getVariableValue(getSize(val)) * 0.65)
28
- const getSwitchWidth = (val: SizeTokens) => getSwitchHeight(val) * 2
29
-
30
- const scopeContexts = createContextScope(SWITCH_NAME)
31
- const [createSwitchContext] = scopeContexts
32
- export const createSwitchScope = scopeContexts[1]
33
-
34
- const [SwitchProvider, useSwitchContext] = createSwitchContext<{
35
- checked: boolean
36
- disabled?: boolean
37
- size: SizeTokens
38
- frameLayout?: LayoutRectangle
39
- }>(SWITCH_NAME)
40
-
41
- /* -------------------------------------------------------------------------------------------------
42
- * SwitchThumb
43
- * -----------------------------------------------------------------------------------------------*/
44
-
45
- const THUMB_NAME = 'SwitchThumb'
46
-
47
- export const SwitchThumbFrame = styled(ThemeableStack, {
48
- name: 'SwitchThumb',
49
- backgroundColor: '$background',
50
- borderRadius: 1000,
51
-
52
- variants: {
53
- size: {
54
- '...size': (val) => ({
55
- height: getSwitchHeight(val),
56
- width: getSwitchHeight(val),
57
- }),
58
- },
59
- },
60
-
61
- defaultVariants: {
62
- size: '$4',
63
- },
64
- })
65
-
66
- export type SwitchThumbProps = GetProps<typeof SwitchThumbFrame>
67
-
68
- export const SwitchThumb = SwitchThumbFrame.extractable(
69
- React.forwardRef<React.ElementRef<'span'>, SwitchThumbProps>(
70
- (props: ScopedProps<SwitchThumbProps, 'Switch'>, forwardedRef) => {
71
- const { __scopeSwitch, ...thumbProps } = props
72
- const { size, disabled, checked, frameLayout } = useSwitchContext(THUMB_NAME, __scopeSwitch)
73
- const [position] = React.useState(new Animated.ValueXY(0))
74
- const [thumbWidth, setThumbWidth] = React.useState(0)
75
- const responder = React.useMemo(() => {
76
- return PanResponder.create({
77
- onStartShouldSetPanResponder: () => true,
78
- onPanResponderMove: Animated.event(
79
- [
80
- null,
81
- {
82
- dx: position.x,
83
- },
84
- ],
85
- {
86
- useNativeDriver: false,
87
- }
88
- ),
89
- })
90
- }, [])
91
-
92
- React.useEffect(() => {
93
- position.addListener((val) => {
94
- console.log('x', val)
95
- })
96
- }, [])
97
-
98
- const maxMove =
99
- getVariableValue(getSwitchWidth(size)) - getVariableValue(getSwitchHeight(size))
100
-
101
- const maxDrag = (frameLayout?.width || 0) - thumbWidth
102
-
103
- return (
104
- <Animated.View
105
- style={[
106
- {
107
- transform: [{ translateX: position.x }],
108
- },
109
- ]}
110
- >
111
- <SwitchThumbFrame
112
- size={size}
113
- data-state={getState(checked)}
114
- data-disabled={disabled ? '' : undefined}
115
- {...responder.panHandlers}
116
- {...thumbProps}
117
- onLayout={({ nativeEvent }) => {
118
- console.log('thumb', nativeEvent.layout)
119
- setThumbWidth(nativeEvent.layout.width)
120
- }}
121
- x={checked ? maxMove : 0}
122
- ref={forwardedRef}
123
- />
124
- </Animated.View>
125
- )
126
- }
127
- )
128
- )
129
-
130
- SwitchThumb.displayName = THUMB_NAME
131
-
132
- /* -------------------------------------------------------------------------------------------------
133
- * Switch
134
- * -----------------------------------------------------------------------------------------------*/
135
-
136
- export const SwitchFrame = styled(XStack, {
137
- name: 'Switch',
138
- tag: 'button',
139
- borderRadius: 1000,
140
- borderWidth: 2,
141
- borderColor: 'transparent',
142
- backgroundColor: '$background',
143
-
144
- focusStyle: {
145
- borderColor: '$borderColorFocus',
146
- },
147
-
148
- variants: {
149
- size: {
150
- '...size': (val) => {
151
- const height = calc(getSwitchHeight(val), '+', 4)
152
- const width = calc(getSwitchWidth(val), '+', 4)
153
- return {
154
- height,
155
- minHeight: height,
156
- width,
157
- }
158
- },
159
- },
160
- },
161
-
162
- defaultVariants: {
163
- size: '$4',
164
- },
165
- })
166
-
167
- type SwitchButtonProps = GetProps<typeof SwitchFrame>
168
-
169
- export type SwitchProps = SwitchButtonProps & {
170
- labeledBy?: string
171
- name?: string
172
- value?: string
173
- checked?: boolean
174
- defaultChecked?: boolean
175
- required?: boolean
176
- onCheckedChange?(checked: boolean): void
177
- }
178
-
179
- export const Switch = withStaticProperties(
180
- SwitchFrame.extractable(
181
- React.forwardRef<HTMLButtonElement | View, SwitchProps>(
182
- (props: ScopedProps<SwitchProps, 'Switch'>, forwardedRef) => {
183
- const {
184
- __scopeSwitch,
185
- labeledBy: ariaLabelledby,
186
- name,
187
- checked: checkedProp,
188
- defaultChecked,
189
- required,
190
- disabled,
191
- value = 'on',
192
- onCheckedChange,
193
- size = '$4',
194
- ...switchProps
195
- } = props
196
- const [button, setButton] = React.useState<HTMLButtonElement | null>(null)
197
- const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node as any))
198
- const labelId = useLabelContext(button)
199
- const labelledBy = ariaLabelledby || labelId
200
- const hasConsumerStoppedPropagationRef = React.useRef(false)
201
- const [frameLayout, setFrameLayout] = React.useState<LayoutRectangle>()
202
- // We set this to true by default so that events bubble to forms without JS (SSR)
203
- const isFormControl = isWeb ? (button ? Boolean(button.closest('form')) : true) : false
204
- const [checked = false, setChecked] = useControllableState({
205
- prop: checkedProp,
206
- defaultProp: defaultChecked || false,
207
- onChange: onCheckedChange,
208
- })
209
-
210
- if (!isWeb) {
211
-
212
- React.useEffect(() => {
213
- if (!props.id) return
214
- return registerFocusable(props.id, {
215
- focus: () => {
216
- setChecked((x) => !x)
217
- },
218
- })
219
- }, [props.id, setChecked])
220
- }
221
-
222
- return (
223
- <SwitchProvider
224
- scope={__scopeSwitch}
225
- checked={checked}
226
- disabled={disabled}
227
- size={size}
228
- frameLayout={frameLayout}
229
- >
230
- <SwitchFrame
231
- size={size}
232
- onLayout={({ nativeEvent }) => {
233
- console.log('?', nativeEvent.layout)
234
- setFrameLayout(nativeEvent.layout)
235
- }}
236
- // @ts-ignore
237
- role="switch"
238
- aria-checked={checked}
239
- aria-labelledby={labelledBy}
240
- aria-required={required}
241
- data-state={getState(checked)}
242
- data-disabled={disabled ? '' : undefined}
243
- disabled={disabled}
244
- theme={checked ? 'active' : null}
245
- themeShallow
246
- // @ts-ignore
247
- tabIndex={disabled ? undefined : 0}
248
- // @ts-ignore
249
- value={value}
250
- {...switchProps}
251
- ref={composedRefs}
252
- onPress={(event) => {
253
- props.onPress?.(event)
254
- setChecked((prevChecked) => !prevChecked)
255
- if (isWeb && isFormControl) {
256
- hasConsumerStoppedPropagationRef.current = event.isPropagationStopped()
257
- // if switch is in a form, stop propagation from the button so that we only propagate
258
- // one click event (from the input). We propagate changes from an input so that native
259
- // form validation works and form events reflect switch updates.
260
- if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation()
261
- }
262
- }}
263
- />
264
- {isWeb && isFormControl && (
265
- <BubbleInput
266
- control={button}
267
- bubbles={!hasConsumerStoppedPropagationRef.current}
268
- name={name}
269
- value={value}
270
- checked={checked}
271
- required={required}
272
- disabled={disabled}
273
- // We transform because the input is absolutely positioned but we have
274
- // rendered it **after** the button. This pulls it back to sit on top
275
- // of the button.
276
- style={{ transform: 'translateX(-100%)' }}
277
- />
278
- )}
279
- </SwitchProvider>
280
- )
281
- }
282
- )
283
- ),
284
- {
285
- Thumb: SwitchThumb,
286
- }
287
- )
288
-
289
- /* ---------------------------------------------------------------------------------------------- */
290
-
291
- type InputProps = any //Radix.ComponentPropsWithoutRef<'input'>
292
- interface BubbleInputProps extends Omit<InputProps, 'checked'> {
293
- checked: boolean
294
- control: HTMLElement | null
295
- bubbles: boolean
296
- }
297
-
298
- // TODO make this native friendly
299
- const BubbleInput = (props: BubbleInputProps) => {
300
- const { control, checked, bubbles = true, ...inputProps } = props
301
- const ref = React.useRef<HTMLInputElement>(null)
302
- const prevChecked = usePrevious(checked)
303
- // const controlSize = useSize(control)
304
-
305
- // Bubble checked change to parents (e.g form change event)
306
- React.useEffect(() => {
307
- const input = ref.current!
308
- const inputProto = window.HTMLInputElement.prototype
309
- const descriptor = Object.getOwnPropertyDescriptor(inputProto, 'checked') as PropertyDescriptor
310
- const setChecked = descriptor.set
311
- if (prevChecked !== checked && setChecked) {
312
- const event = new Event('click', { bubbles })
313
- setChecked.call(input, checked)
314
- input.dispatchEvent(event)
315
- }
316
- }, [prevChecked, checked, bubbles])
317
-
318
- return (
319
- <input
320
- type="checkbox"
321
- aria-hidden
322
- defaultChecked={checked}
323
- {...inputProps}
324
- tabIndex={-1}
325
- ref={ref}
326
- style={{
327
- ...props.style,
328
- // ...controlSize,
329
- position: 'absolute',
330
- pointerEvents: 'none',
331
- opacity: 0,
332
- margin: 0,
333
- }}
334
- />
335
- )
336
- }
337
-
338
- function getState(checked: boolean) {
339
- return checked ? 'checked' : 'unchecked'
340
- }
@@ -1,201 +0,0 @@
1
- import {
2
- composeEventHandlers,
3
- getVariableValue,
4
- isWeb,
5
- View,
6
- withStaticProperties,
7
- } from '@tamagui/core'
8
- import { useSwitch } from '@tamagui/switch-headless'
9
- import { useControllableState } from '@tamagui/use-controllable-state'
10
- import * as React from 'react'
11
- import type { LayoutChangeEvent } from 'react-native'
12
- import { SwitchStyledContext } from './StyledContext'
13
- import { SwitchFrame as DefaultSwitchFrame, SwitchThumb } from './Switch'
14
- import type {
15
- SwitchComponent,
16
- SwitchExtraProps,
17
- SwitchProps,
18
- SwitchThumbComponent,
19
- SwitchThumbProps,
20
- } from './types'
21
- import { useSwitchNative } from './useSwitchNative'
22
-
23
- export type { SwitchExtraProps, SwitchProps, SwitchThumbProps }
24
-
25
- export function createSwitch<
26
- F extends SwitchComponent,
27
- T extends SwitchThumbComponent,
28
- >(createProps: { Frame?: F; Thumb?: T }) {
29
- const { Frame = DefaultSwitchFrame, Thumb = SwitchThumb } = createProps as any as {
30
- Frame: typeof DefaultSwitchFrame
31
- Thumb: typeof SwitchThumb
32
- }
33
-
34
- if (process.env.NODE_ENV === 'development') {
35
- if (
36
- (Frame !== DefaultSwitchFrame &&
37
- Frame.staticConfig.context &&
38
- Frame.staticConfig.context !== SwitchStyledContext) ||
39
- (Thumb !== SwitchThumb &&
40
- Thumb.staticConfig.context &&
41
- Thumb.staticConfig.context !== SwitchStyledContext)
42
- ) {
43
- console.warn(
44
- `Warning: createSwitch() needs to control context to pass checked state from Frame to Thumb, any custom context passed will be overridden.`
45
- )
46
- }
47
- }
48
-
49
- Frame.staticConfig.context = SwitchStyledContext
50
- Thumb.staticConfig.context = SwitchStyledContext
51
-
52
- const SwitchThumbComponent = Thumb.styleable<SwitchThumbProps>(
53
- function SwitchThumb(props, forwardedRef) {
54
- const { size: sizeProp, unstyled: unstyledProp, activeStyle, ...thumbProps } = props
55
- const styledContext = SwitchStyledContext.useStyledContext()
56
- const {
57
- unstyled: unstyledContext,
58
- size: sizeContext,
59
- active,
60
- disabled,
61
- frameWidth = 0,
62
- } = styledContext
63
- const unstyled =
64
- process.env.TAMAGUI_HEADLESS === '1'
65
- ? true
66
- : (unstyledProp ?? unstyledContext ?? false)
67
- const size = sizeProp ?? sizeContext ?? '$true'
68
- const initialChecked = React.useRef(active).current
69
- const initialWidth = getVariableValue(props.width || size, 'size')
70
- const [thumbWidth, setThumbWidth] = React.useState(
71
- typeof initialWidth === 'number' ? initialWidth : 0
72
- )
73
- const distance = frameWidth - thumbWidth
74
- const x = initialChecked ? (active ? 0 : -distance) : active ? distance : 0
75
-
76
- return (
77
- <Thumb
78
- ref={forwardedRef}
79
- unstyled={unstyled}
80
- {...(unstyled === false && {
81
- size,
82
- })}
83
- alignSelf={initialChecked ? 'flex-end' : 'flex-start'}
84
- x={x}
85
- onLayout={composeEventHandlers(props.onLayout, (e) => {
86
- const next = e.nativeEvent.layout.width
87
- setThumbWidth(next)
88
- })}
89
- disabled={disabled}
90
- {...thumbProps}
91
- {...(active && activeStyle)}
92
- />
93
- )
94
- }
95
- )
96
-
97
- const SwitchComponent = Frame.styleable<SwitchProps>(
98
- function SwitchFrame(_props, forwardedRef) {
99
- const {
100
- native,
101
- nativeProps,
102
- checked: checkedProp,
103
- defaultChecked,
104
- onCheckedChange,
105
- activeStyle,
106
- unstyled: unstyledProp,
107
- activeTheme: activeThemeProp,
108
- ...props
109
- } = _props
110
- const [checked, setChecked] = useControllableState({
111
- prop: checkedProp,
112
- defaultProp: defaultChecked || false,
113
- onChange: onCheckedChange,
114
- transition: true,
115
- })
116
-
117
- const styledContext = React.useContext(SwitchStyledContext.context)
118
-
119
- // this is actually inner width
120
- const [frameWidth, setFrameInnerWidth] = React.useState(0)
121
-
122
- const { switchProps, bubbleInput, switchRef } = useSwitch(
123
- props as any,
124
- [checked, setChecked],
125
- // @ts-ignore TODO tamagui react 19 type error
126
- forwardedRef
127
- )
128
-
129
- const nativeSwitch = useSwitchNative({
130
- id: props.id,
131
- disabled: props.disabled,
132
- native,
133
- nativeProps,
134
- checked,
135
- setChecked,
136
- })
137
-
138
- if (nativeSwitch) {
139
- return nativeSwitch
140
- }
141
-
142
- const disabled = props.disabled
143
-
144
- const handleLayout = (e: LayoutChangeEvent) => {
145
- const next = e.nativeEvent.layout.width
146
- if (next !== frameWidth) {
147
- setFrameInnerWidth(next)
148
- }
149
- }
150
-
151
- const unstyled = styledContext.unstyled ?? unstyledProp ?? false
152
-
153
- return (
154
- <>
155
- <SwitchStyledContext.Provider
156
- size={styledContext.size ?? props.size ?? '$true'}
157
- unstyled={unstyled}
158
- active={checked}
159
- disabled={disabled}
160
- frameWidth={frameWidth}
161
- >
162
- <Frame
163
- ref={switchRef}
164
- render="button"
165
- // activeThemeProp should be a theme name string like "active", not a style object
166
- theme={activeThemeProp ?? null}
167
- {...(isWeb && { type: 'button' })}
168
- {...(!unstyled && {
169
- size: styledContext.size ?? props.size ?? '$true',
170
- })}
171
- unstyled={unstyled}
172
- {...props}
173
- {...(switchProps as any)}
174
- disabled={disabled}
175
- {...(checked && {
176
- ...(!unstyled &&
177
- !activeStyle && {
178
- backgroundColor: '$backgroundActive',
179
- }),
180
- ...activeStyle,
181
- })}
182
- >
183
- <View alignSelf="stretch" flex={1} onLayout={handleLayout}>
184
- {props.children}
185
- </View>
186
- </Frame>
187
- </SwitchStyledContext.Provider>
188
-
189
- {bubbleInput}
190
- </>
191
- )
192
- },
193
- {
194
- disableTheme: true,
195
- }
196
- )
197
-
198
- return withStaticProperties(SwitchComponent, {
199
- Thumb: SwitchThumbComponent,
200
- })
201
- }