Skip to content

Commit dacfb17

Browse files
authored
feat: support active, focused, disabled (#47)
1 parent 763f3cb commit dacfb17

File tree

17 files changed

+232
-46
lines changed

17 files changed

+232
-46
lines changed

packages/uniwind/src/components/native/Pressable.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
import { Pressable as RNPressable, PressableProps } from 'react-native'
2+
import { UniwindStore } from '../../core/native'
23
import { copyComponentProperties } from '../utils'
34
import { useStyle } from './useStyle'
45

56
export const Pressable = copyComponentProperties(RNPressable, (props: PressableProps) => {
6-
const style = useStyle(props.className)
7+
const style = useStyle(props.className, {
8+
isDisabled: Boolean(props.disabled),
9+
})
710

811
return (
912
<RNPressable
1013
{...props}
11-
style={state => [style, typeof props.style === 'function' ? props.style(state) : props.style]}
14+
style={state => {
15+
if (state.pressed) {
16+
return [
17+
UniwindStore.getStyles(
18+
props.className,
19+
{ isDisabled: Boolean(props.disabled), isPressed: true },
20+
).styles,
21+
typeof props.style === 'function' ? props.style(state) : props.style,
22+
]
23+
}
24+
25+
return [style, typeof props.style === 'function' ? props.style(state) : props.style]
26+
}}
1227
/>
1328
)
1429
})

packages/uniwind/src/components/native/Switch.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { Switch as RNSwitch, SwitchProps } from 'react-native'
2-
import { useUniwindAccent } from '../../hooks'
2+
import { ComponentState } from '../../core/types'
3+
import { useUniwindAccent } from '../../hooks/useUniwindAccent.native'
34
import { copyComponentProperties } from '../utils'
45
import { useStyle } from './useStyle'
56

67
export const Switch = copyComponentProperties(RNSwitch, (props: SwitchProps) => {
7-
const style = useStyle(props.className)
8-
const trackColorOn = useUniwindAccent(props.trackColorOnClassName)
9-
const trackColorOff = useUniwindAccent(props.trackColorOffClassName)
10-
const thumbColor = useUniwindAccent(props.thumbColorClassName)
11-
const ios_backgroundColor = useUniwindAccent(props.ios_backgroundColorClassName)
8+
const state = {
9+
isDisabled: Boolean(props.disabled),
10+
} satisfies ComponentState
11+
const style = useStyle(props.className, state)
12+
const trackColorOn = useUniwindAccent(props.trackColorOnClassName, state)
13+
const trackColorOff = useUniwindAccent(props.trackColorOffClassName, state)
14+
const thumbColor = useUniwindAccent(props.thumbColorClassName, state)
15+
const ios_backgroundColor = useUniwindAccent(props.ios_backgroundColorClassName, state)
1216

1317
return (
1418
<RNSwitch

packages/uniwind/src/components/native/Text.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { useState } from 'react'
12
import { Text as RNText, TextProps } from 'react-native'
2-
import { useUniwindAccent } from '../../hooks'
3+
import { ComponentState } from '../../core/types'
4+
import { useUniwindAccent } from '../../hooks/useUniwindAccent.native'
35
import { copyComponentProperties } from '../utils'
46
import { useStyle } from './useStyle'
57

@@ -8,15 +10,31 @@ type StyleWithWebkitLineClamp = {
810
}
911

1012
export const Text = copyComponentProperties(RNText, (props: TextProps) => {
11-
const style = useStyle(props.className)
12-
const selectionColor = useUniwindAccent(props.selectionColorClassName)
13+
const [isPressed, setIsPressed] = useState(false)
14+
const state = {
15+
isPressed,
16+
isDisabled: Boolean(props.disabled),
17+
} satisfies ComponentState
18+
const style = useStyle(props.className, state)
19+
const selectionColor = useUniwindAccent(props.selectionColorClassName, state)
1320

1421
return (
1522
<RNText
1623
{...props}
1724
style={[style, props.style]}
1825
selectionColor={props.selectionColor ?? selectionColor}
1926
numberOfLines={(style as StyleWithWebkitLineClamp).WebkitLineClamp ?? props.numberOfLines}
27+
// Without onPress function Text is not clickable, so onPressIn and onPressOut are not working
28+
onPress={event => props.onPress?.(event)}
29+
suppressHighlighting={props.onPress ? props.suppressHighlighting : true}
30+
onPressIn={event => {
31+
setIsPressed(true)
32+
props.onPressIn?.(event)
33+
}}
34+
onPressOut={event => {
35+
setIsPressed(false)
36+
props.onPressOut?.(event)
37+
}}
2038
/>
2139
)
2240
})

packages/uniwind/src/components/native/TextInput.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
import { useState } from 'react'
12
import { TextInput as RNTextInput, TextInputProps } from 'react-native'
2-
import { useUniwindAccent } from '../../hooks'
3+
import { ComponentState } from '../../core/types'
4+
import { useUniwindAccent } from '../../hooks/useUniwindAccent.native'
35
import { copyComponentProperties } from '../utils'
46
import { useStyle } from './useStyle'
57

68
export const TextInput = copyComponentProperties(RNTextInput, (props: TextInputProps) => {
7-
const style = useStyle(props.className)
8-
const cursorColor = useUniwindAccent(props.cursorColorClassName)
9-
const selectionColor = useUniwindAccent(props.selectionColorClassName)
10-
const placeholderTextColor = useUniwindAccent(props.placeholderTextColorClassName)
11-
const selectionHandleColor = useUniwindAccent(props.selectionHandleColorClassName)
12-
const underlineColorAndroid = useUniwindAccent(props.underlineColorAndroidClassName)
9+
const [isFocused, setIsFocused] = useState(false)
10+
const [isPressed, setIsPressed] = useState(false)
11+
const state = {
12+
isDisabled: props.editable === false,
13+
isFocused,
14+
isPressed,
15+
} satisfies ComponentState
16+
const style = useStyle(props.className, state)
17+
const cursorColor = useUniwindAccent(props.cursorColorClassName, state)
18+
const selectionColor = useUniwindAccent(props.selectionColorClassName, state)
19+
const placeholderTextColor = useUniwindAccent(props.placeholderTextColorClassName, state)
20+
const selectionHandleColor = useUniwindAccent(props.selectionHandleColorClassName, state)
21+
const underlineColorAndroid = useUniwindAccent(props.underlineColorAndroidClassName, state)
1322

1423
return (
1524
<RNTextInput
@@ -20,6 +29,22 @@ export const TextInput = copyComponentProperties(RNTextInput, (props: TextInputP
2029
placeholderTextColor={props.placeholderTextColor ?? placeholderTextColor}
2130
selectionHandleColor={props.selectionHandleColor ?? selectionHandleColor}
2231
underlineColorAndroid={props.underlineColorAndroid ?? underlineColorAndroid}
32+
onFocus={event => {
33+
setIsFocused(true)
34+
props.onFocus?.(event)
35+
}}
36+
onBlur={event => {
37+
setIsFocused(false)
38+
props.onBlur?.(event)
39+
}}
40+
onPressIn={event => {
41+
setIsPressed(true)
42+
props.onPressIn?.(event)
43+
}}
44+
onPressOut={event => {
45+
setIsPressed(false)
46+
props.onPressOut?.(event)
47+
}}
2348
/>
2449
)
2550
})

packages/uniwind/src/components/native/TouchableHighlight.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1+
import { useState } from 'react'
12
import { TouchableHighlight as RNTouchableHighlight, TouchableHighlightProps } from 'react-native'
2-
import { useUniwindAccent } from '../../hooks'
3+
import { ComponentState } from '../../core/types'
4+
import { useUniwindAccent } from '../../hooks/useUniwindAccent.native'
35
import { copyComponentProperties } from '../utils'
46
import { useStyle } from './useStyle'
57

68
export const TouchableHighlight = copyComponentProperties(RNTouchableHighlight, (props: TouchableHighlightProps) => {
7-
const style = useStyle(props.className)
8-
const underlayColor = useUniwindAccent(props.underlayColorClassName)
9+
const [isPressed, setIsPressed] = useState(false)
10+
const state = {
11+
isDisabled: Boolean(props.disabled),
12+
isPressed,
13+
} satisfies ComponentState
14+
const style = useStyle(props.className, state)
15+
const underlayColor = useUniwindAccent(props.underlayColorClassName, state)
916

1017
return (
1118
<RNTouchableHighlight
1219
{...props}
1320
style={[style, props.style]}
1421
underlayColor={props.underlayColor ?? underlayColor}
22+
onPressIn={event => {
23+
setIsPressed(true)
24+
props.onPressIn?.(event)
25+
}}
26+
onPressOut={event => {
27+
setIsPressed(false)
28+
props.onPressOut?.(event)
29+
}}
1530
/>
1631
)
1732
})
Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
1+
import { useState } from 'react'
12
import { TouchableNativeFeedback as RNTouchableNativeFeedback, TouchableNativeFeedbackProps } from 'react-native'
3+
import { ComponentState } from '../../core/types'
24
import { copyComponentProperties } from '../utils'
35
import { useStyle } from './useStyle'
46

5-
export const TouchableNativeFeedback = copyComponentProperties(
6-
RNTouchableNativeFeedback,
7-
(props: TouchableNativeFeedbackProps) => {
8-
const style = useStyle(props.className)
7+
export const TouchableNativeFeedback = copyComponentProperties(RNTouchableNativeFeedback, (props: TouchableNativeFeedbackProps) => {
8+
const [isPressed, setIsPressed] = useState(false)
9+
const state = {
10+
isDisabled: Boolean(props.disabled),
11+
isPressed,
12+
} satisfies ComponentState
13+
const style = useStyle(props.className, state)
914

10-
return (
11-
<RNTouchableNativeFeedback
12-
{...props}
13-
style={[style, props.style]}
14-
/>
15-
)
16-
},
17-
)
15+
return (
16+
<RNTouchableNativeFeedback
17+
{...props}
18+
style={[style, props.style]}
19+
onPressIn={event => {
20+
setIsPressed(true)
21+
props.onPressIn?.(event)
22+
}}
23+
onPressOut={event => {
24+
setIsPressed(false)
25+
props.onPressOut?.(event)
26+
}}
27+
/>
28+
)
29+
})
1830

1931
export default TouchableNativeFeedback

packages/uniwind/src/components/native/TouchableOpacity.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1+
import { useState } from 'react'
12
import { TouchableOpacity as RNTouchableOpacity, TouchableOpacityProps } from 'react-native'
3+
import { ComponentState } from '../../core/types'
24
import { copyComponentProperties } from '../utils'
35
import { useStyle } from './useStyle'
46

57
export const TouchableOpacity = copyComponentProperties(RNTouchableOpacity, (props: TouchableOpacityProps) => {
6-
const style = useStyle(props.className)
8+
const [isPressed, setIsPressed] = useState(false)
9+
const state = {
10+
isDisabled: Boolean(props.disabled),
11+
isPressed,
12+
} satisfies ComponentState
13+
const style = useStyle(props.className, state)
714

815
return (
916
<RNTouchableOpacity
1017
{...props}
1118
style={[style, props.style]}
19+
onPressIn={event => {
20+
setIsPressed(true)
21+
props.onPressIn?.(event)
22+
}}
23+
onPressOut={event => {
24+
setIsPressed(false)
25+
props.onPressOut?.(event)
26+
}}
1227
/>
1328
)
1429
})

packages/uniwind/src/components/native/TouchableWithoutFeedback.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1+
import { useState } from 'react'
12
import { TouchableWithoutFeedback as RNTouchableWithoutFeedback, TouchableWithoutFeedbackProps } from 'react-native'
3+
import { ComponentState } from '../../core/types'
24
import { copyComponentProperties } from '../utils'
35
import { useStyle } from './useStyle'
46

57
export const TouchableWithoutFeedback = copyComponentProperties(RNTouchableWithoutFeedback, (props: TouchableWithoutFeedbackProps) => {
6-
const style = useStyle(props.className)
8+
const [isPressed, setIsPressed] = useState(false)
9+
const state = {
10+
isDisabled: Boolean(props.disabled),
11+
isPressed,
12+
} satisfies ComponentState
13+
const style = useStyle(props.className, state)
714

815
return (
916
<RNTouchableWithoutFeedback
1017
{...props}
1118
style={[style, props.style]}
19+
onPressIn={event => {
20+
setIsPressed(true)
21+
props.onPressIn?.(event)
22+
}}
23+
onPressOut={event => {
24+
setIsPressed(false)
25+
props.onPressOut?.(event)
26+
}}
1227
/>
1328
)
1429
})

packages/uniwind/src/components/native/useStyle.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { useEffect, useMemo, useReducer } from 'react'
22
import { UniwindStore } from '../../core/native'
3+
import { ComponentState } from '../../core/types'
34

4-
export const useStyle = (className?: string) => {
5+
export const useStyle = (className?: string, state?: ComponentState) => {
56
const [_, rerender] = useReducer(() => ({}), {})
6-
const styleState = useMemo(() => UniwindStore.getStyles(className), [className, _])
7+
const styleState = useMemo(
8+
() => UniwindStore.getStyles(className, state),
9+
[className, _, state?.isDisabled, state?.isFocused, state?.isPressed],
10+
)
711

812
useEffect(() => {
913
const dispose = UniwindStore.subscribe(() => rerender(), styleState.dependencies)

packages/uniwind/src/core/native/store.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Dimensions } from 'react-native'
22
import { Orientation, StyleDependency } from '../../types'
3-
import { RNStyle, Style, StyleSheets } from '../types'
3+
import { ComponentState, RNStyle, Style, StyleSheets } from '../types'
44
import { parseBoxShadow, parseFontVariant, parseTransformsMutation, resolveGradient } from './parsers'
55
import { UniwindRuntime } from './runtime'
66

@@ -30,7 +30,7 @@ export class UniwindStoreBuilder {
3030
}
3131
}
3232

33-
getStyles(className?: string) {
33+
getStyles(className?: string, state?: ComponentState) {
3434
if (className === undefined) {
3535
return {
3636
styles: {} as RNStyle,
@@ -56,7 +56,7 @@ export class UniwindStoreBuilder {
5656
})
5757
.filter(Boolean)
5858

59-
return this.resolveStyles(styles as Array<[string, Style]>)
59+
return this.resolveStyles(styles as Array<[string, Style]>, state)
6060
}
6161

6262
reload = () => {
@@ -67,7 +67,7 @@ export class UniwindStoreBuilder {
6767
dependencies.forEach(dep => this.listeners[dep].forEach(listener => listener()))
6868
}
6969

70-
private resolveStyles(styles: Array<[string, Style]>) {
70+
private resolveStyles(styles: Array<[string, Style]>, state?: ComponentState) {
7171
const dependencies = [] as Array<StyleDependency>
7272
const filteredStyles = styles.filter(([, style]) => {
7373
dependencies.push(...style.dependencies)
@@ -78,6 +78,9 @@ export class UniwindStoreBuilder {
7878
|| (style.theme !== null && this.runtime.currentThemeName !== style.theme)
7979
|| (style.orientation !== null && this.runtime.orientation !== style.orientation)
8080
|| (style.rtl !== null && this.runtime.rtl !== style.rtl)
81+
|| (style.active !== null && state?.isPressed !== style.active)
82+
|| (style.focus !== null && state?.isFocused !== style.focus)
83+
|| (style.disabled !== null && state?.isDisabled !== style.disabled)
8184
) {
8285
return false
8386
}

0 commit comments

Comments
 (0)