@tamagui/switch 1.79.6 → 1.79.8

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.
@@ -4,15 +4,12 @@ import {
4
4
  SizeTokens,
5
5
  StackProps,
6
6
  TamaguiComponentExpectingVariants,
7
- TamaguiElement,
8
7
  composeEventHandlers,
9
- getVariableValue,
10
8
  isWeb,
11
9
  useProps,
12
10
  withStaticProperties,
13
11
  } from '@tamagui/core'
14
12
  import { registerFocusable } from '@tamagui/focusable'
15
- import { getSize } from '@tamagui/get-token'
16
13
  import { useLabelContext } from '@tamagui/label'
17
14
  import { YStack } from '@tamagui/stacks'
18
15
  import { useControllableState } from '@tamagui/use-controllable-state'
@@ -127,157 +124,151 @@ export function createSwitch<F extends SwitchComponent, T extends SwitchThumbCom
127
124
  )
128
125
  })
129
126
 
130
- const SwitchComponent = Frame.extractable(
131
- React.forwardRef<TamaguiElement, SwitchProps>(function SwitchFrame(
132
- propsIn,
133
- forwardedRef
134
- ) {
135
- const styledContext = React.useContext(SwitchContext)
136
- const props = useProps(propsIn, {
137
- noNormalize: true,
138
- noExpand: true,
139
- resolveValues: 'none',
140
- forComponent: Frame,
141
- })
142
- const {
143
- labeledBy: ariaLabelledby,
144
- name,
145
- checked: checkedProp,
146
- defaultChecked,
147
- required,
148
- disabled,
149
- value = 'on',
150
- onCheckedChange,
151
- size = styledContext.size ?? '$true',
152
- unstyled = styledContext.unstyled ?? false,
153
- native: nativeProp,
154
- nativeProps,
155
- children,
156
- ...switchProps
157
- } = props
127
+ const SwitchComponent = Frame.styleable<SwitchExtraProps>(function SwitchFrame(
128
+ propsIn,
129
+ forwardedRef
130
+ ) {
131
+ const styledContext = React.useContext(SwitchContext)
132
+ const props = useProps(propsIn, {
133
+ noNormalize: true,
134
+ noExpand: true,
135
+ resolveValues: 'none',
136
+ forComponent: Frame,
137
+ })
138
+ const {
139
+ labeledBy: ariaLabelledby,
140
+ name,
141
+ checked: checkedProp,
142
+ defaultChecked,
143
+ required,
144
+ disabled,
145
+ value = 'on',
146
+ onCheckedChange,
147
+ size = styledContext.size ?? '$true',
148
+ unstyled = styledContext.unstyled ?? false,
149
+ native: nativeProp,
150
+ nativeProps,
151
+ children,
152
+ ...switchProps
153
+ } = props
158
154
 
159
- const native = Array.isArray(nativeProp) ? nativeProp : [nativeProp]
155
+ const native = Array.isArray(nativeProp) ? nativeProp : [nativeProp]
160
156
 
161
- const shouldRenderMobileNative =
162
- (!isWeb && nativeProp === true) ||
163
- (!isWeb && native.includes('mobile')) ||
164
- (native.includes('android') && Platform.OS === 'android') ||
165
- (native.includes('ios') && Platform.OS === 'ios')
157
+ const shouldRenderMobileNative =
158
+ (!isWeb && nativeProp === true) ||
159
+ (!isWeb && native.includes('mobile')) ||
160
+ (native.includes('android') && Platform.OS === 'android') ||
161
+ (native.includes('ios') && Platform.OS === 'ios')
166
162
 
167
- const [button, setButton] = React.useState<HTMLButtonElement | null>(null)
168
- const composedRefs = useComposedRefs(
169
- forwardedRef,
170
- // @ts-expect-error
171
- setButton
172
- )
173
- const labelId = useLabelContext(button)
174
- const labelledBy = ariaLabelledby || labelId
175
- const hasConsumerStoppedPropagationRef = React.useRef(false)
176
- // We set this to true by default so that events bubble to forms without JS (SSR)
177
- const isFormControl = isWeb
178
- ? button
179
- ? Boolean(button.closest('form'))
180
- : true
181
- : false
163
+ const [button, setButton] = React.useState<HTMLButtonElement | null>(null)
164
+ const composedRefs = useComposedRefs(forwardedRef, setButton)
165
+ const labelId = useLabelContext(button)
166
+ const labelledBy = ariaLabelledby || labelId
167
+ const hasConsumerStoppedPropagationRef = React.useRef(false)
168
+ // We set this to true by default so that events bubble to forms without JS (SSR)
169
+ const isFormControl = isWeb
170
+ ? button
171
+ ? Boolean(button.closest('form'))
172
+ : true
173
+ : false
182
174
 
183
- const [frameWidth, setFrameWidth] = React.useState(0)
175
+ const [frameWidth, setFrameWidth] = React.useState(0)
184
176
 
185
- const [checked = false, setChecked] = useControllableState({
186
- prop: checkedProp,
187
- defaultProp: defaultChecked || false,
188
- onChange: onCheckedChange,
189
- transition: true,
190
- })
177
+ const [checked = false, setChecked] = useControllableState({
178
+ prop: checkedProp,
179
+ defaultProp: defaultChecked || false,
180
+ onChange: onCheckedChange,
181
+ transition: true,
182
+ })
191
183
 
192
- if (shouldRenderMobileNative) {
193
- return (
194
- <NativeSwitch
195
- value={checkedProp}
196
- onValueChange={onCheckedChange}
197
- {...nativeProps}
198
- />
199
- )
200
- }
184
+ if (shouldRenderMobileNative) {
185
+ return (
186
+ <NativeSwitch
187
+ value={checkedProp}
188
+ onValueChange={onCheckedChange}
189
+ {...nativeProps}
190
+ />
191
+ )
192
+ }
201
193
 
202
- if (!isWeb) {
203
- // eslint-disable-next-line react-hooks/rules-of-hooks
204
- React.useEffect(() => {
205
- if (!props.id) return
206
- return registerFocusable(props.id, {
207
- focus: () => {
208
- setChecked((x) => !x)
209
- },
210
- })
211
- }, [props.id, setChecked])
212
- }
194
+ if (!isWeb) {
195
+ // eslint-disable-next-line react-hooks/rules-of-hooks
196
+ React.useEffect(() => {
197
+ if (!props.id) return
198
+ return registerFocusable(props.id, {
199
+ focus: () => {
200
+ setChecked((x) => !x)
201
+ },
202
+ })
203
+ }, [props.id, setChecked])
204
+ }
213
205
 
214
- return (
215
- <>
216
- {/* @ts-ignore */}
217
- <Frame
218
- tag="button"
219
- unstyled={unstyled}
220
- size={size}
206
+ return (
207
+ <>
208
+ {/* @ts-ignore */}
209
+ <Frame
210
+ tag="button"
211
+ unstyled={unstyled}
212
+ size={size}
213
+ checked={checked}
214
+ disabled={disabled}
215
+ frameWidth={frameWidth}
216
+ themeShallow
217
+ {...(!disableActiveTheme && {
218
+ theme: checked ? 'active' : null,
219
+ themeShallow: true,
220
+ })}
221
+ role="switch"
222
+ aria-checked={checked}
223
+ aria-labelledby={labelledBy}
224
+ aria-required={required}
225
+ data-state={getState(checked)}
226
+ data-disabled={disabled ? '' : undefined}
227
+ // @ts-ignore
228
+ tabIndex={disabled ? undefined : 0}
229
+ // @ts-ignore
230
+ value={value}
231
+ {...switchProps}
232
+ ref={composedRefs}
233
+ onPress={composeEventHandlers(props.onPress, (event) => {
234
+ setChecked((prevChecked) => !prevChecked)
235
+ if (isWeb && isFormControl) {
236
+ hasConsumerStoppedPropagationRef.current = event.isPropagationStopped()
237
+ // if switch is in a form, stop propagation from the button so that we only propagate
238
+ // one click event (from the input). We propagate changes from an input so that native
239
+ // form validation works and form events reflect switch updates.
240
+ if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation()
241
+ }
242
+ })}
243
+ >
244
+ <YStack
245
+ alignSelf="stretch"
246
+ flex={1}
247
+ onLayout={(e) => {
248
+ setFrameWidth(e.nativeEvent.layout.width)
249
+ }}
250
+ >
251
+ {typeof children === 'function' ? children(checked) : children}
252
+ </YStack>
253
+ </Frame>
254
+ {isWeb && isFormControl && (
255
+ <BubbleInput
256
+ control={button}
257
+ bubbles={!hasConsumerStoppedPropagationRef.current}
258
+ name={name}
259
+ value={value}
221
260
  checked={checked}
261
+ required={required}
222
262
  disabled={disabled}
223
- frameWidth={frameWidth}
224
- themeShallow
225
- {...(!disableActiveTheme && {
226
- theme: checked ? 'active' : null,
227
- themeShallow: true,
228
- })}
229
- role="switch"
230
- aria-checked={checked}
231
- aria-labelledby={labelledBy}
232
- aria-required={required}
233
- data-state={getState(checked)}
234
- data-disabled={disabled ? '' : undefined}
235
- // @ts-ignore
236
- tabIndex={disabled ? undefined : 0}
237
- // @ts-ignore
238
- value={value}
239
- {...switchProps}
240
- ref={composedRefs}
241
- onPress={composeEventHandlers(props.onPress, (event) => {
242
- setChecked((prevChecked) => !prevChecked)
243
- if (isWeb && isFormControl) {
244
- hasConsumerStoppedPropagationRef.current = event.isPropagationStopped()
245
- // if switch is in a form, stop propagation from the button so that we only propagate
246
- // one click event (from the input). We propagate changes from an input so that native
247
- // form validation works and form events reflect switch updates.
248
- if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation()
249
- }
250
- })}
251
- >
252
- <YStack
253
- alignSelf="stretch"
254
- flex={1}
255
- onLayout={(e) => {
256
- setFrameWidth(e.nativeEvent.layout.width)
257
- }}
258
- >
259
- {typeof children === 'function' ? children(checked) : children}
260
- </YStack>
261
- </Frame>
262
- {isWeb && isFormControl && (
263
- <BubbleInput
264
- control={button}
265
- bubbles={!hasConsumerStoppedPropagationRef.current}
266
- name={name}
267
- value={value}
268
- checked={checked}
269
- required={required}
270
- disabled={disabled}
271
- // We transform because the input is absolutely positioned but we have
272
- // rendered it **after** the button. This pulls it back to sit on top
273
- // of the button.
274
- style={{ transform: 'translateX(-100%)' }}
275
- />
276
- )}
277
- </>
278
- )
279
- })
280
- )
263
+ // We transform because the input is absolutely positioned but we have
264
+ // rendered it **after** the button. This pulls it back to sit on top
265
+ // of the button.
266
+ style={{ transform: 'translateX(-100%)' }}
267
+ />
268
+ )}
269
+ </>
270
+ )
271
+ })
281
272
 
282
273
  /* ---------------------------------------------------------------------------------------------- */
283
274