@tamagui/switch 1.0.1-beta.138 → 1.0.1-beta.141

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.
File without changes
File without changes
package/dist/cjs/index.js CHANGED
File without changes
File without changes
File without changes
File without changes
package/dist/esm/index.js CHANGED
File without changes
File without changes
@@ -109,8 +109,7 @@ const Switch = withStaticProperties(SwitchFrame.extractable(React.forwardRef((pr
109
109
  }
110
110
  return <SwitchProvider scope={__scopeSwitch} checked={checked} disabled={disabled} size={size}>
111
111
  <SwitchFrame size={size} role="switch" aria-checked={checked} aria-labelledby={labelledBy} aria-required={required} data-state={getState(checked)} data-disabled={disabled ? "" : void 0} disabled={disabled} theme={checked ? "active" : null} themeShallow tabIndex={disabled ? void 0 : 0} value={value} {...switchProps} ref={composedRefs} onPress={(event) => {
112
- var _a;
113
- (_a = props.onPress) == null ? void 0 : _a.call(props, event);
112
+ props.onPress?.(event);
114
113
  setChecked((prevChecked) => !prevChecked);
115
114
  if (isWeb && isFormControl) {
116
115
  hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/Switch.tsx"],
4
4
  "sourcesContent": ["// via radix\n// https://github.com/radix-ui/primitives/blob/main/packages/react/switch/src/Switch.tsx\n\nimport { usePrevious } from '@radix-ui/react-use-previous'\nimport { useComposedRefs } from '@tamagui/compose-refs'\nimport {\n GetProps,\n SizeTokens,\n calc,\n getSize,\n getVariableValue,\n isWeb,\n styled,\n withStaticProperties,\n} from '@tamagui/core'\nimport { ScopedProps, createContextScope } from '@tamagui/create-context'\nimport { registerFocusable } from '@tamagui/focusable'\nimport { useLabelContext } from '@tamagui/label'\nimport { ThemeableStack, XStack } from '@tamagui/stacks'\nimport { useControllableState } from '@tamagui/use-controllable-state'\nimport * as React from 'react'\nimport { View } from 'react-native'\n\nconst SWITCH_NAME = 'Switch'\n\n// TODO make customizable\nconst getSwitchHeight = (val: SizeTokens) => Math.round(getVariableValue(getSize(val)) * 0.65)\nconst getSwitchWidth = (val: SizeTokens) => getSwitchHeight(val) * 2\n\nconst scopeContexts = createContextScope(SWITCH_NAME)\nconst [createSwitchContext] = scopeContexts\nexport const createSwitchScope = scopeContexts[1]\n\nconst [SwitchProvider, useSwitchContext] = createSwitchContext<{\n checked: boolean\n disabled?: boolean\n size: SizeTokens\n}>(SWITCH_NAME)\n\n/* -------------------------------------------------------------------------------------------------\n * SwitchThumb\n * -----------------------------------------------------------------------------------------------*/\n\nconst THUMB_NAME = 'SwitchThumb'\n\nexport const SwitchThumbFrame = styled(ThemeableStack, {\n name: 'SwitchThumb',\n backgroundColor: '$background',\n borderRadius: 1000,\n\n variants: {\n size: {\n '...size': (val) => ({\n height: getSwitchHeight(val),\n width: getSwitchHeight(val),\n }),\n },\n },\n\n defaultVariants: {\n size: '$4',\n },\n})\n\nexport type SwitchThumbProps = GetProps<typeof SwitchThumbFrame>\n\nexport const SwitchThumb = SwitchThumbFrame.extractable(\n React.forwardRef<React.ElementRef<'span'>, SwitchThumbProps>(\n (props: ScopedProps<SwitchThumbProps, 'Switch'>, forwardedRef) => {\n const { __scopeSwitch, ...thumbProps } = props\n const { size, disabled, checked } = useSwitchContext(THUMB_NAME, __scopeSwitch)\n return (\n <SwitchThumbFrame\n size={size}\n data-state={getState(checked)}\n data-disabled={disabled ? '' : undefined}\n {...thumbProps}\n x={\n checked\n ? getVariableValue(getSwitchWidth(size)) - getVariableValue(getSwitchHeight(size))\n : 0\n }\n ref={forwardedRef}\n />\n )\n }\n )\n)\n\nSwitchThumb.displayName = THUMB_NAME\n\n/* -------------------------------------------------------------------------------------------------\n * Switch\n * -----------------------------------------------------------------------------------------------*/\n\nexport const SwitchFrame = styled(XStack, {\n name: 'Switch',\n tag: 'button',\n borderRadius: 1000,\n borderWidth: 2,\n borderColor: 'transparent',\n backgroundColor: '$background',\n\n focusStyle: {\n borderColor: '$borderColorFocus',\n },\n\n variants: {\n size: {\n '...size': (val) => {\n const height = calc(getSwitchHeight(val), '+', 4)\n const width = calc(getSwitchWidth(val), '+', 4)\n return {\n height,\n minHeight: height,\n width,\n }\n },\n },\n },\n\n defaultVariants: {\n size: '$4',\n },\n})\n\ntype SwitchButtonProps = GetProps<typeof SwitchFrame>\n\nexport type SwitchProps = SwitchButtonProps & {\n labeledBy?: string\n name?: string\n value?: string\n checked?: boolean\n defaultChecked?: boolean\n required?: boolean\n onCheckedChange?(checked: boolean): void\n}\n\nexport const Switch = withStaticProperties(\n SwitchFrame.extractable(\n React.forwardRef<HTMLButtonElement | View, SwitchProps>(\n (props: ScopedProps<SwitchProps, 'Switch'>, forwardedRef) => {\n const {\n __scopeSwitch,\n labeledBy: ariaLabelledby,\n name,\n checked: checkedProp,\n defaultChecked,\n required,\n disabled,\n value = 'on',\n onCheckedChange,\n size = '$4',\n ...switchProps\n } = props\n const [button, setButton] = React.useState<HTMLButtonElement | null>(null)\n const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node as any))\n const labelId = useLabelContext(button)\n const labelledBy = ariaLabelledby || labelId\n const hasConsumerStoppedPropagationRef = React.useRef(false)\n // We set this to true by default so that events bubble to forms without JS (SSR)\n const isFormControl = isWeb ? (button ? Boolean(button.closest('form')) : true) : false\n const [checked = false, setChecked] = useControllableState({\n prop: checkedProp,\n defaultProp: defaultChecked || false,\n onChange: onCheckedChange,\n })\n\n if (!isWeb) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n React.useEffect(() => {\n if (!props.id) return\n return registerFocusable(props.id, {\n focus: () => {\n setChecked((x) => !x)\n },\n })\n }, [props.id, setChecked])\n }\n\n return (\n <SwitchProvider scope={__scopeSwitch} checked={checked} disabled={disabled} size={size}>\n <SwitchFrame\n size={size}\n // @ts-ignore\n role=\"switch\"\n aria-checked={checked}\n aria-labelledby={labelledBy}\n aria-required={required}\n data-state={getState(checked)}\n data-disabled={disabled ? '' : undefined}\n disabled={disabled}\n theme={checked ? 'active' : null}\n themeShallow\n // @ts-ignore\n tabIndex={disabled ? undefined : 0}\n // @ts-ignore\n value={value}\n {...switchProps}\n ref={composedRefs}\n onPress={(event) => {\n props.onPress?.(event)\n setChecked((prevChecked) => !prevChecked)\n if (isWeb && isFormControl) {\n hasConsumerStoppedPropagationRef.current = event.isPropagationStopped()\n // if switch is in a form, stop propagation from the button so that we only propagate\n // one click event (from the input). We propagate changes from an input so that native\n // form validation works and form events reflect switch updates.\n if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation()\n }\n }}\n />\n {isWeb && isFormControl && (\n <BubbleInput\n control={button}\n bubbles={!hasConsumerStoppedPropagationRef.current}\n name={name}\n value={value}\n checked={checked}\n required={required}\n disabled={disabled}\n // We transform because the input is absolutely positioned but we have\n // rendered it **after** the button. This pulls it back to sit on top\n // of the button.\n style={{ transform: 'translateX(-100%)' }}\n />\n )}\n </SwitchProvider>\n )\n }\n )\n ),\n {\n Thumb: SwitchThumb,\n }\n)\n\n/* ---------------------------------------------------------------------------------------------- */\n\ntype InputProps = any //Radix.ComponentPropsWithoutRef<'input'>\ninterface BubbleInputProps extends Omit<InputProps, 'checked'> {\n checked: boolean\n control: HTMLElement | null\n bubbles: boolean\n}\n\n// TODO make this native friendly\nconst BubbleInput = (props: BubbleInputProps) => {\n const { control, checked, bubbles = true, ...inputProps } = props\n const ref = React.useRef<HTMLInputElement>(null)\n const prevChecked = usePrevious(checked)\n // const controlSize = useSize(control)\n\n // Bubble checked change to parents (e.g form change event)\n React.useEffect(() => {\n const input = ref.current!\n const inputProto = window.HTMLInputElement.prototype\n const descriptor = Object.getOwnPropertyDescriptor(inputProto, 'checked') as PropertyDescriptor\n const setChecked = descriptor.set\n if (prevChecked !== checked && setChecked) {\n const event = new Event('click', { bubbles })\n setChecked.call(input, checked)\n input.dispatchEvent(event)\n }\n }, [prevChecked, checked, bubbles])\n\n return (\n <input\n type=\"checkbox\"\n aria-hidden\n defaultChecked={checked}\n {...inputProps}\n tabIndex={-1}\n ref={ref}\n style={{\n ...props.style,\n // ...controlSize,\n position: 'absolute',\n pointerEvents: 'none',\n opacity: 0,\n margin: 0,\n }}\n />\n )\n}\n\nfunction getState(checked: boolean) {\n return checked ? 'checked' : 'unchecked'\n}\n"],
5
- "mappings": "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",
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/jsx/index.js CHANGED
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/switch",
3
- "version": "1.0.1-beta.138",
3
+ "version": "1.0.1-beta.141",
4
4
  "sideEffects": [
5
5
  "*.css"
6
6
  ],
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@radix-ui/react-use-previous": "^0.1.1",
27
- "@tamagui/compose-refs": "^1.0.1-beta.138",
28
- "@tamagui/core": "^1.0.1-beta.138",
29
- "@tamagui/create-context": "^1.0.1-beta.138",
30
- "@tamagui/focusable": "^1.0.1-beta.138",
31
- "@tamagui/label": "^1.0.1-beta.138",
32
- "@tamagui/stacks": "^1.0.1-beta.138",
33
- "@tamagui/use-controllable-state": "^1.0.1-beta.138"
27
+ "@tamagui/compose-refs": "^1.0.1-beta.141",
28
+ "@tamagui/core": "^1.0.1-beta.141",
29
+ "@tamagui/create-context": "^1.0.1-beta.141",
30
+ "@tamagui/focusable": "^1.0.1-beta.141",
31
+ "@tamagui/label": "^1.0.1-beta.141",
32
+ "@tamagui/stacks": "^1.0.1-beta.141",
33
+ "@tamagui/use-controllable-state": "^1.0.1-beta.141"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "react": "*",
@@ -38,7 +38,7 @@
38
38
  "react-native": "*"
39
39
  },
40
40
  "devDependencies": {
41
- "@tamagui/build": "^1.0.1-beta.138",
41
+ "@tamagui/build": "^1.0.1-beta.141",
42
42
  "@types/react-native": "^0.69.2",
43
43
  "react": "*",
44
44
  "react-dom": "*",
@@ -0,0 +1,340 @@
1
+ // via radix
2
+ // https://github.com/radix-ui/primitives/blob/main/packages/react/switch/src/Switch.tsx
3
+
4
+ import { usePrevious } from '@radix-ui/react-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
+ // eslint-disable-next-line react-hooks/rules-of-hooks
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
+ }