fluent-styles 1.62.1 → 1.62.3
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.
- package/README.md +81 -3709
- package/lib/commonjs/circularProgress/index.js +102 -83
- package/lib/commonjs/circularProgress/index.js.map +1 -1
- package/lib/commonjs/tabBar/TabBar.js +87 -139
- package/lib/commonjs/tabBar/TabBar.js.map +1 -1
- package/lib/commonjs/utiles/styled.js +26 -5
- package/lib/commonjs/utiles/styled.js.map +1 -1
- package/lib/module/circularProgress/index.js +103 -84
- package/lib/module/circularProgress/index.js.map +1 -1
- package/lib/module/tabBar/TabBar.js +88 -140
- package/lib/module/tabBar/TabBar.js.map +1 -1
- package/lib/module/utiles/styled.js +26 -5
- package/lib/module/utiles/styled.js.map +1 -1
- package/lib/typescript/badge/index.d.ts +3 -1
- package/lib/typescript/badge/index.d.ts.map +1 -1
- package/lib/typescript/button/index.d.ts +5 -1
- package/lib/typescript/button/index.d.ts.map +1 -1
- package/lib/typescript/card/index.d.ts +3 -1
- package/lib/typescript/card/index.d.ts.map +1 -1
- package/lib/typescript/circularProgress/index.d.ts +4 -5
- package/lib/typescript/circularProgress/index.d.ts.map +1 -1
- package/lib/typescript/dialog/index.d.ts +3 -1
- package/lib/typescript/dialog/index.d.ts.map +1 -1
- package/lib/typescript/divider/index.d.ts +3 -1
- package/lib/typescript/divider/index.d.ts.map +1 -1
- package/lib/typescript/header/index.d.ts +3 -1
- package/lib/typescript/header/index.d.ts.map +1 -1
- package/lib/typescript/image/index.d.ts +3 -1
- package/lib/typescript/image/index.d.ts.map +1 -1
- package/lib/typescript/pressable/index.d.ts +3 -1
- package/lib/typescript/pressable/index.d.ts.map +1 -1
- package/lib/typescript/safeAreaProvider/index.d.ts +3 -1
- package/lib/typescript/safeAreaProvider/index.d.ts.map +1 -1
- package/lib/typescript/safeAreaView/index.d.ts +3 -1
- package/lib/typescript/safeAreaView/index.d.ts.map +1 -1
- package/lib/typescript/scrollView/index.d.ts +3 -1
- package/lib/typescript/scrollView/index.d.ts.map +1 -1
- package/lib/typescript/shape/cycle.d.ts +3 -1
- package/lib/typescript/shape/cycle.d.ts.map +1 -1
- package/lib/typescript/shape/index.d.ts +3 -1
- package/lib/typescript/shape/index.d.ts.map +1 -1
- package/lib/typescript/spacer/index.d.ts +3 -1
- package/lib/typescript/spacer/index.d.ts.map +1 -1
- package/lib/typescript/stack/index.d.ts +9 -3
- package/lib/typescript/stack/index.d.ts.map +1 -1
- package/lib/typescript/tabBar/TabBar.d.ts.map +1 -1
- package/lib/typescript/text/index.d.ts +3 -1
- package/lib/typescript/text/index.d.ts.map +1 -1
- package/lib/typescript/utiles/styled.d.ts +4 -2
- package/lib/typescript/utiles/styled.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/circularProgress/index.tsx +177 -164
- package/src/tabBar/TabBar.tsx +120 -233
- package/src/utiles/styled.tsx +22 -6
package/src/tabBar/TabBar.tsx
CHANGED
|
@@ -12,11 +12,9 @@ import {
|
|
|
12
12
|
Easing,
|
|
13
13
|
ScrollView,
|
|
14
14
|
StyleSheet,
|
|
15
|
-
Text,
|
|
16
|
-
TouchableOpacity,
|
|
17
15
|
useColorScheme,
|
|
18
|
-
View,
|
|
19
16
|
} from 'react-native'
|
|
17
|
+
import { Stack, StyledText, StyledPressable } from 'fluent-styles'
|
|
20
18
|
|
|
21
19
|
import {
|
|
22
20
|
TAB_BAR_COLORS_DARK,
|
|
@@ -30,59 +28,45 @@ import {
|
|
|
30
28
|
|
|
31
29
|
// ─── Badge ────────────────────────────────────────────────────────────────────
|
|
32
30
|
|
|
33
|
-
const Badge: React.FC<{
|
|
34
|
-
value: number | string
|
|
35
|
-
color: string
|
|
36
|
-
}> = ({ value, color }) => {
|
|
31
|
+
const Badge: React.FC<{ value: number | string; color: string }> = ({ value, color }) => {
|
|
37
32
|
const isDot = value === ''
|
|
38
33
|
return (
|
|
39
|
-
<
|
|
34
|
+
<Stack
|
|
35
|
+
position="absolute"
|
|
36
|
+
top={-4}
|
|
37
|
+
right={isDot ? -4 : -8}
|
|
38
|
+
minWidth={isDot ? 8 : 16}
|
|
39
|
+
height={isDot ? 8 : 16}
|
|
40
|
+
borderRadius={isDot ? 4 : 8}
|
|
41
|
+
alignItems="center"
|
|
42
|
+
justifyContent="center"
|
|
43
|
+
paddingHorizontal={isDot ? 0 : 3}
|
|
44
|
+
backgroundColor={isDot ? undefined : '#ef4444'}
|
|
45
|
+
>
|
|
40
46
|
{isDot ? (
|
|
41
|
-
<
|
|
47
|
+
<Stack width={6} height={6} borderRadius={3} backgroundColor={color} />
|
|
42
48
|
) : (
|
|
43
|
-
<
|
|
49
|
+
<StyledText fontSize={9} fontWeight="700" color={color}>
|
|
44
50
|
{typeof value === 'number' && value > 99 ? '99+' : value}
|
|
45
|
-
</
|
|
51
|
+
</StyledText>
|
|
46
52
|
)}
|
|
47
|
-
</
|
|
53
|
+
</Stack>
|
|
48
54
|
)
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
const badge = StyleSheet.create({
|
|
52
|
-
wrap: {
|
|
53
|
-
position: 'absolute',
|
|
54
|
-
top: -4,
|
|
55
|
-
right: -8,
|
|
56
|
-
minWidth: 16,
|
|
57
|
-
height: 16,
|
|
58
|
-
borderRadius: 8,
|
|
59
|
-
alignItems: 'center',
|
|
60
|
-
justifyContent: 'center',
|
|
61
|
-
paddingHorizontal: 3,
|
|
62
|
-
backgroundColor: '#ef4444',
|
|
63
|
-
},
|
|
64
|
-
dot_wrap: {
|
|
65
|
-
top: 0,
|
|
66
|
-
right: -4,
|
|
67
|
-
minWidth: 8,
|
|
68
|
-
height: 8,
|
|
69
|
-
borderRadius: 4,
|
|
70
|
-
padding: 0,
|
|
71
|
-
},
|
|
72
|
-
dot: { width: 6, height: 6, borderRadius: 3 },
|
|
73
|
-
text: { fontSize: 9, fontWeight: '700', color: '#fff' },
|
|
74
|
-
})
|
|
75
|
-
|
|
76
57
|
// ─── Indicator ────────────────────────────────────────────────────────────────
|
|
58
|
+
// NOTE: Animated.View is kept here intentionally — fluent-styles Stack does not
|
|
59
|
+
// accept Animated values for left/width, which are required for the sliding
|
|
60
|
+
// animation. This is the only place native Animated.View remains.
|
|
77
61
|
|
|
78
62
|
interface IndicatorProps {
|
|
79
|
-
type:
|
|
80
|
-
left:
|
|
81
|
-
width:
|
|
82
|
-
height:
|
|
83
|
-
color:
|
|
84
|
-
radius?:
|
|
85
|
-
visible:
|
|
63
|
+
type: IndicatorStyle
|
|
64
|
+
left: Animated.Value
|
|
65
|
+
width: Animated.Value
|
|
66
|
+
height: number
|
|
67
|
+
color: string
|
|
68
|
+
radius?: number
|
|
69
|
+
visible: boolean
|
|
86
70
|
}
|
|
87
71
|
|
|
88
72
|
const Indicator: React.FC<IndicatorProps> = ({
|
|
@@ -100,7 +84,7 @@ const Indicator: React.FC<IndicatorProps> = ({
|
|
|
100
84
|
width,
|
|
101
85
|
backgroundColor: color,
|
|
102
86
|
borderRadius: radius ?? 999,
|
|
103
|
-
opacity:
|
|
87
|
+
// FIX: removed opacity: 0.15 — pill is now fully opaque
|
|
104
88
|
},
|
|
105
89
|
]}
|
|
106
90
|
/>
|
|
@@ -123,7 +107,7 @@ const Indicator: React.FC<IndicatorProps> = ({
|
|
|
123
107
|
)
|
|
124
108
|
}
|
|
125
109
|
|
|
126
|
-
// line
|
|
110
|
+
// line
|
|
127
111
|
return (
|
|
128
112
|
<Animated.View
|
|
129
113
|
style={{
|
|
@@ -141,32 +125,21 @@ const Indicator: React.FC<IndicatorProps> = ({
|
|
|
141
125
|
|
|
142
126
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
143
127
|
|
|
144
|
-
function resolveIndicatorHeight(
|
|
145
|
-
type: IndicatorStyle | false,
|
|
146
|
-
explicit?: number,
|
|
147
|
-
): number {
|
|
128
|
+
function resolveIndicatorHeight(type: IndicatorStyle | false, explicit?: number): number {
|
|
148
129
|
if (explicit !== undefined) return explicit
|
|
149
130
|
if (type === 'line') return 3
|
|
150
131
|
if (type === 'dot') return 6
|
|
151
132
|
return 3
|
|
152
133
|
}
|
|
153
134
|
|
|
154
|
-
function resolveBarHeight(
|
|
155
|
-
hasIcons: boolean,
|
|
156
|
-
hasIndicator: boolean,
|
|
157
|
-
explicit?: number,
|
|
158
|
-
): number {
|
|
135
|
+
function resolveBarHeight(hasIcons: boolean, hasIndicator: boolean, explicit?: number): number {
|
|
159
136
|
if (explicit !== undefined) return explicit
|
|
160
|
-
if (hasIcons)
|
|
161
|
-
if (hasIndicator)
|
|
137
|
+
if (hasIcons) return 52
|
|
138
|
+
if (hasIndicator) return 44
|
|
162
139
|
return 48
|
|
163
140
|
}
|
|
164
141
|
|
|
165
|
-
function
|
|
166
|
-
variant: TabBarVariant,
|
|
167
|
-
isActive: boolean,
|
|
168
|
-
colors: TabBarColors,
|
|
169
|
-
) {
|
|
142
|
+
function getVariantChipProps(variant: TabBarVariant, isActive: boolean, colors: TabBarColors) {
|
|
170
143
|
if (variant === 'card' || variant === 'solid') {
|
|
171
144
|
return isActive
|
|
172
145
|
? { backgroundColor: colors.activeChipBg, borderRadius: 8 }
|
|
@@ -183,27 +156,23 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
183
156
|
value: controlledValue,
|
|
184
157
|
defaultValue,
|
|
185
158
|
onChange,
|
|
186
|
-
|
|
187
|
-
indicator = false,
|
|
159
|
+
indicator = false,
|
|
188
160
|
indicatorWidth,
|
|
189
161
|
indicatorHeight,
|
|
190
162
|
indicatorColor,
|
|
191
163
|
indicatorRadius,
|
|
192
|
-
|
|
193
|
-
tabAlign = 'center',
|
|
164
|
+
tabAlign = 'center',
|
|
194
165
|
height,
|
|
195
|
-
variant
|
|
196
|
-
labelBulge
|
|
197
|
-
showBorder
|
|
166
|
+
variant = 'default',
|
|
167
|
+
labelBulge = 1,
|
|
168
|
+
showBorder = false,
|
|
198
169
|
tabPaddingHorizontal = 12,
|
|
199
|
-
iconLabelGap
|
|
200
|
-
fontSize
|
|
201
|
-
iconFontSize
|
|
202
|
-
|
|
170
|
+
iconLabelGap = 4,
|
|
171
|
+
fontSize = 14,
|
|
172
|
+
iconFontSize = 11,
|
|
203
173
|
textColor,
|
|
204
174
|
activeTextColor,
|
|
205
175
|
colors: colorOverrides,
|
|
206
|
-
|
|
207
176
|
style,
|
|
208
177
|
contentStyle,
|
|
209
178
|
tabStyle,
|
|
@@ -212,25 +181,19 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
212
181
|
|
|
213
182
|
// ── Colours ──────────────────────────────────────────────────────────────
|
|
214
183
|
const scheme = useColorScheme()
|
|
215
|
-
const baseColors: TabBarColors = scheme === 'dark'
|
|
216
|
-
? TAB_BAR_COLORS_DARK
|
|
217
|
-
: TAB_BAR_COLORS_LIGHT
|
|
218
|
-
|
|
184
|
+
const baseColors: TabBarColors = scheme === 'dark' ? TAB_BAR_COLORS_DARK : TAB_BAR_COLORS_LIGHT
|
|
219
185
|
const colors: TabBarColors = useMemo(
|
|
220
186
|
() => colorOverrides ? { ...baseColors, ...colorOverrides } : baseColors,
|
|
221
187
|
[baseColors, colorOverrides],
|
|
222
188
|
)
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const resolvedIndicatorColor = (indicatorColor as string) ?? colors.indicator
|
|
189
|
+
const resolvedTextColor = (textColor as string) ?? colors.text
|
|
190
|
+
const resolvedActiveTextColor = (activeTextColor as string) ?? colors.activeText
|
|
191
|
+
const resolvedIndicatorColor = (indicatorColor as string) ?? colors.indicator
|
|
227
192
|
|
|
228
193
|
// ── State ────────────────────────────────────────────────────────────────
|
|
229
194
|
const isControlled = controlledValue !== undefined
|
|
230
|
-
const [localValue, setLocalValue] = useState<T>(
|
|
231
|
-
|
|
232
|
-
)
|
|
233
|
-
const active = isControlled ? controlledValue! : localValue
|
|
195
|
+
const [localValue, setLocalValue] = useState<T>(defaultValue ?? options[0]?.value)
|
|
196
|
+
const active = isControlled ? controlledValue! : localValue
|
|
234
197
|
const activeRef = useRef(active)
|
|
235
198
|
activeRef.current = active
|
|
236
199
|
|
|
@@ -241,13 +204,12 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
241
204
|
}, [isControlled, onChange])
|
|
242
205
|
|
|
243
206
|
// ── Layout measurement ────────────────────────────────────────────────────
|
|
244
|
-
const tabCount
|
|
245
|
-
const layouts
|
|
207
|
+
const tabCount = options.length
|
|
208
|
+
const layouts = useRef<{ tab?: LayoutRectangle; text?: LayoutRectangle }[]>(
|
|
246
209
|
Array.from({ length: tabCount }, () => ({})),
|
|
247
210
|
)
|
|
248
211
|
const [layoutReady, setLayoutReady] = useState(false)
|
|
249
212
|
|
|
250
|
-
// Reset when options change
|
|
251
213
|
const prevOptionsLen = useRef(tabCount)
|
|
252
214
|
if (prevOptionsLen.current !== tabCount) {
|
|
253
215
|
prevOptionsLen.current = tabCount
|
|
@@ -256,11 +218,10 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
256
218
|
}
|
|
257
219
|
|
|
258
220
|
const checkReady = useCallback(() => {
|
|
259
|
-
|
|
260
|
-
if (allDone) setLayoutReady(true)
|
|
221
|
+
if (layouts.current.every(l => l.tab && l.text)) setLayoutReady(true)
|
|
261
222
|
}, [])
|
|
262
223
|
|
|
263
|
-
const onLayoutTab
|
|
224
|
+
const onLayoutTab = useCallback((i: number) => (e: LayoutChangeEvent) => {
|
|
264
225
|
layouts.current[i] = { ...layouts.current[i], tab: e.nativeEvent.layout }
|
|
265
226
|
checkReady()
|
|
266
227
|
}, [checkReady])
|
|
@@ -274,52 +235,27 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
274
235
|
const indLeft = useRef(new Animated.Value(0)).current
|
|
275
236
|
const indWidth = useRef(new Animated.Value(0)).current
|
|
276
237
|
const indH = resolveIndicatorHeight(indicator, indicatorHeight)
|
|
277
|
-
|
|
278
238
|
const scrollRef = useRef<ScrollView>(null)
|
|
279
239
|
const barWidthRef = useRef(0)
|
|
280
240
|
|
|
281
241
|
const navigateTo = useCallback((idx: number) => {
|
|
282
242
|
const layout = layouts.current[idx]
|
|
283
243
|
if (!layout.tab || !layout.text) return
|
|
284
|
-
|
|
285
|
-
// Resolve indicator width
|
|
286
|
-
const useTextWidth = indicatorWidth === undefined
|
|
287
|
-
const useTabWidth = indicatorWidth === 0
|
|
288
|
-
const iw = useTextWidth
|
|
244
|
+
const iw = indicatorWidth === undefined
|
|
289
245
|
? (indicator === 'pill' ? layout.tab.width : layout.text.width)
|
|
290
|
-
:
|
|
246
|
+
: indicatorWidth === 0
|
|
291
247
|
? layout.tab.width
|
|
292
248
|
: indicatorWidth!
|
|
293
|
-
|
|
294
|
-
// Center the indicator within the tab
|
|
295
249
|
const il = layout.tab.x + (layout.tab.width - iw) / 2
|
|
296
|
-
|
|
297
250
|
Animated.parallel([
|
|
298
|
-
Animated.timing(indLeft,
|
|
299
|
-
|
|
300
|
-
duration: 220,
|
|
301
|
-
easing: Easing.out(Easing.cubic),
|
|
302
|
-
useNativeDriver: false,
|
|
303
|
-
}),
|
|
304
|
-
Animated.timing(indWidth, {
|
|
305
|
-
toValue: iw,
|
|
306
|
-
duration: 220,
|
|
307
|
-
easing: Easing.out(Easing.cubic),
|
|
308
|
-
useNativeDriver: false,
|
|
309
|
-
}),
|
|
251
|
+
Animated.timing(indLeft, { toValue: il, duration: 220, easing: Easing.out(Easing.cubic), useNativeDriver: false }),
|
|
252
|
+
Animated.timing(indWidth, { toValue: iw, duration: 220, easing: Easing.out(Easing.cubic), useNativeDriver: false }),
|
|
310
253
|
]).start()
|
|
311
|
-
|
|
312
|
-
// Auto-scroll horizontal bar
|
|
313
254
|
if (tabAlign === 'scroll') {
|
|
314
|
-
|
|
315
|
-
scrollRef.current?.scrollTo({
|
|
316
|
-
x: tabCenter - barWidthRef.current / 2,
|
|
317
|
-
animated: true,
|
|
318
|
-
})
|
|
255
|
+
scrollRef.current?.scrollTo({ x: layout.tab.x + layout.tab.width / 2 - barWidthRef.current / 2, animated: true })
|
|
319
256
|
}
|
|
320
257
|
}, [indLeft, indWidth, indicatorWidth, indicator, tabAlign])
|
|
321
258
|
|
|
322
|
-
// Navigate when active tab or layout readiness changes
|
|
323
259
|
useEffect(() => {
|
|
324
260
|
if (!indicator || !layoutReady) return
|
|
325
261
|
const idx = options.findIndex(o => o.value === active)
|
|
@@ -329,175 +265,126 @@ function TabBarInner<T extends TabValue>(props: TabBarProps<T>) {
|
|
|
329
265
|
// ── Dimensions ───────────────────────────────────────────────────────────
|
|
330
266
|
const hasIcons = options.some(o => !!o.iconRender)
|
|
331
267
|
const barHeight = resolveBarHeight(hasIcons, !!indicator, height)
|
|
332
|
-
|
|
333
|
-
const bulgeFactor = typeof labelBulge === 'boolean'
|
|
334
|
-
? (labelBulge ? 1.2 : 1)
|
|
335
|
-
: labelBulge
|
|
268
|
+
const bulgeFactor = typeof labelBulge === 'boolean' ? (labelBulge ? 1.2 : 1) : labelBulge
|
|
336
269
|
|
|
337
270
|
// ── Render each tab ──────────────────────────────────────────────────────
|
|
338
271
|
const renderTab = (item: (typeof options)[number], index: number) => {
|
|
339
272
|
const isActive = item.value === active
|
|
340
273
|
const isDisabled = item.disabled ?? false
|
|
341
274
|
|
|
275
|
+
// FIX: pill/line/dot use resolvedActiveTextColor (label sits on indicator bg)
|
|
276
|
+
// solid/card use activeChipText (label sits on per-tab chip bg)
|
|
342
277
|
const labelColor = isActive
|
|
343
278
|
? (variant === 'solid' || variant === 'card') ? colors.activeChipText : resolvedActiveTextColor
|
|
344
279
|
: isDisabled ? colors.disabled
|
|
345
280
|
: resolvedTextColor
|
|
346
281
|
|
|
347
|
-
const
|
|
282
|
+
const chipProps = getVariantChipProps(variant, isActive, colors)
|
|
348
283
|
|
|
349
284
|
return (
|
|
350
|
-
<
|
|
285
|
+
<StyledPressable
|
|
351
286
|
key={String(item.value)}
|
|
352
|
-
activeOpacity={isDisabled ? 1 : 0.7}
|
|
353
287
|
disabled={isDisabled}
|
|
354
288
|
onPress={() => handlePress(item.value)}
|
|
355
289
|
onLayout={onLayoutTab(index)}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
290
|
+
alignItems="center"
|
|
291
|
+
justifyContent="center"
|
|
292
|
+
height="100%"
|
|
293
|
+
flexDirection="column"
|
|
294
|
+
paddingHorizontal={tabPaddingHorizontal}
|
|
295
|
+
paddingVertical={hasIcons ? 6 : undefined}
|
|
296
|
+
{...(tabAlign === 'center' ? { flex: 1 } : {})}
|
|
297
|
+
{...chipProps}
|
|
298
|
+
{...(tabStyle as object)}
|
|
364
299
|
accessibilityRole="tab"
|
|
365
300
|
accessibilityState={{ selected: isActive, disabled: isDisabled }}
|
|
366
301
|
>
|
|
367
|
-
{/* Icon */}
|
|
368
302
|
{item.iconRender?.(
|
|
369
303
|
isActive ? resolvedActiveTextColor : resolvedTextColor,
|
|
370
304
|
isActive,
|
|
371
305
|
)}
|
|
372
306
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
{ scaleY: bulgeFactor },
|
|
388
|
-
],
|
|
389
|
-
},
|
|
390
|
-
]}
|
|
307
|
+
<Stack
|
|
308
|
+
position="relative"
|
|
309
|
+
alignItems="center"
|
|
310
|
+
onLayout={onLayoutText(index)}
|
|
311
|
+
>
|
|
312
|
+
<StyledText
|
|
313
|
+
fontSize={hasIcons ? iconFontSize : fontSize}
|
|
314
|
+
color={labelColor}
|
|
315
|
+
fontWeight={isActive ? '600' : '400'}
|
|
316
|
+
textAlign="center"
|
|
317
|
+
marginTop={item.iconRender ? iconLabelGap : 0}
|
|
318
|
+
style={isActive && bulgeFactor !== 1
|
|
319
|
+
? { transform: [{ scaleX: bulgeFactor }, { scaleY: bulgeFactor }] }
|
|
320
|
+
: undefined}
|
|
391
321
|
>
|
|
392
322
|
{item.label}
|
|
393
|
-
</
|
|
323
|
+
</StyledText>
|
|
394
324
|
|
|
395
|
-
{/* Badge */}
|
|
396
325
|
{item.badge !== undefined && (
|
|
397
326
|
<Badge value={item.badge} color={colors.badge} />
|
|
398
327
|
)}
|
|
399
|
-
</
|
|
400
|
-
</
|
|
328
|
+
</Stack>
|
|
329
|
+
</StyledPressable>
|
|
401
330
|
)
|
|
402
331
|
}
|
|
403
332
|
|
|
404
333
|
// ── Content ──────────────────────────────────────────────────────────────
|
|
405
|
-
const tabs = options.map(renderTab)
|
|
406
|
-
|
|
407
334
|
const indicatorEl = indicator ? (
|
|
408
335
|
<Indicator
|
|
409
|
-
type={indicator}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
height={indH}
|
|
413
|
-
color={resolvedIndicatorColor}
|
|
414
|
-
radius={indicatorRadius}
|
|
415
|
-
visible={layoutReady}
|
|
336
|
+
type={indicator} left={indLeft} width={indWidth}
|
|
337
|
+
height={indH} color={resolvedIndicatorColor}
|
|
338
|
+
radius={indicatorRadius} visible={layoutReady}
|
|
416
339
|
/>
|
|
417
340
|
) : null
|
|
418
341
|
|
|
419
|
-
|
|
342
|
+
const borderProps = (showBorder || variant === 'underline')
|
|
343
|
+
? { borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: colors.border }
|
|
344
|
+
: {}
|
|
345
|
+
|
|
420
346
|
return (
|
|
421
|
-
<
|
|
347
|
+
<Stack
|
|
422
348
|
testID={testID}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
? StyleSheet.hairlineWidth
|
|
430
|
-
: 0,
|
|
431
|
-
borderTopColor: colors.border,
|
|
432
|
-
},
|
|
433
|
-
style,
|
|
434
|
-
]}
|
|
349
|
+
flexDirection="row"
|
|
350
|
+
overflow="hidden"
|
|
351
|
+
height={barHeight}
|
|
352
|
+
backgroundColor={colors.background}
|
|
353
|
+
{...borderProps}
|
|
354
|
+
{...(style as object)}
|
|
435
355
|
>
|
|
436
356
|
{tabAlign === 'center' ? (
|
|
437
|
-
<
|
|
357
|
+
<Stack
|
|
358
|
+
flex={1}
|
|
359
|
+
flexDirection="row"
|
|
360
|
+
alignItems="center"
|
|
361
|
+
position="relative"
|
|
362
|
+
{...(contentStyle as object)}
|
|
363
|
+
>
|
|
438
364
|
{indicatorEl}
|
|
439
|
-
{
|
|
440
|
-
</
|
|
365
|
+
{options.map(renderTab)}
|
|
366
|
+
</Stack>
|
|
441
367
|
) : (
|
|
442
368
|
<ScrollView
|
|
443
369
|
ref={scrollRef}
|
|
444
370
|
horizontal
|
|
445
371
|
bounces={false}
|
|
446
372
|
showsHorizontalScrollIndicator={false}
|
|
447
|
-
style={
|
|
448
|
-
contentContainerStyle={[
|
|
373
|
+
style={{ flex: 1 }}
|
|
374
|
+
contentContainerStyle={[
|
|
375
|
+
{ flexDirection: 'row', alignItems: 'center', paddingHorizontal: 4, position: 'relative', flexGrow: 1 },
|
|
376
|
+
contentStyle,
|
|
377
|
+
]}
|
|
449
378
|
onLayout={e => { barWidthRef.current = e.nativeEvent.layout.width }}
|
|
450
379
|
>
|
|
451
380
|
{indicatorEl}
|
|
452
|
-
{
|
|
381
|
+
{options.map(renderTab)}
|
|
453
382
|
</ScrollView>
|
|
454
383
|
)}
|
|
455
|
-
</
|
|
384
|
+
</Stack>
|
|
456
385
|
)
|
|
457
386
|
}
|
|
458
387
|
|
|
459
|
-
// ── Static styles ─────────────────────────────────────────────────────────────
|
|
460
|
-
|
|
461
|
-
const S = StyleSheet.create({
|
|
462
|
-
bar: {
|
|
463
|
-
flexDirection: 'row',
|
|
464
|
-
overflow: 'hidden',
|
|
465
|
-
},
|
|
466
|
-
center_content: {
|
|
467
|
-
flex: 1,
|
|
468
|
-
flexDirection: 'row',
|
|
469
|
-
alignItems: 'center',
|
|
470
|
-
position: 'relative',
|
|
471
|
-
},
|
|
472
|
-
scroll: {
|
|
473
|
-
flex: 1,
|
|
474
|
-
},
|
|
475
|
-
scroll_content: {
|
|
476
|
-
flexDirection: 'row',
|
|
477
|
-
alignItems: 'center',
|
|
478
|
-
paddingHorizontal: 4,
|
|
479
|
-
position: 'relative',
|
|
480
|
-
flexGrow: 1,
|
|
481
|
-
},
|
|
482
|
-
tab: {
|
|
483
|
-
alignItems: 'center',
|
|
484
|
-
justifyContent: 'center',
|
|
485
|
-
height: '100%',
|
|
486
|
-
flexDirection: 'column',
|
|
487
|
-
},
|
|
488
|
-
tab_equal: {
|
|
489
|
-
flex: 1,
|
|
490
|
-
},
|
|
491
|
-
label_wrap: {
|
|
492
|
-
position: 'relative',
|
|
493
|
-
alignItems: 'center',
|
|
494
|
-
},
|
|
495
|
-
label: {
|
|
496
|
-
textAlign: 'center',
|
|
497
|
-
includeFontPadding: false,
|
|
498
|
-
},
|
|
499
|
-
})
|
|
500
|
-
|
|
501
388
|
export const TabBar = memo(TabBarInner) as <T extends TabValue>(
|
|
502
389
|
p: TabBarProps<T>,
|
|
503
|
-
) => React.JSX.Element
|
|
390
|
+
) => React.JSX.Element
|
package/src/utiles/styled.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { ComponentType, Ref, forwardRef } from "react";
|
|
3
3
|
import { ViewStyle, TextStyle, ImageStyle } from "react-native";
|
|
4
4
|
|
|
5
5
|
type Style = ViewStyle | TextStyle | ImageStyle;
|
|
@@ -13,13 +13,15 @@ interface StyledOptions {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// React 19 passes ref as a regular prop; React 18 and below do not.
|
|
17
|
+
const isReact19 = typeof React.version === "string" && parseInt(React.version) >= 19;
|
|
18
|
+
|
|
16
19
|
const styled = <P extends object>(
|
|
17
20
|
Component: ComponentType<P>,
|
|
18
21
|
{ base, variants }: StyledOptions = {}
|
|
19
22
|
) => {
|
|
20
|
-
|
|
23
|
+
function buildStyles(options: Record<string, any>): Style {
|
|
21
24
|
const styles: Style = { ...(base || {}) };
|
|
22
|
-
const options = props as Record<string, any>;
|
|
23
25
|
|
|
24
26
|
if (variants) {
|
|
25
27
|
Object.keys(variants).forEach((category) => {
|
|
@@ -28,9 +30,7 @@ const styled = <P extends object>(
|
|
|
28
30
|
|
|
29
31
|
if (typeof variantValue === "function") {
|
|
30
32
|
const style = variantValue(variantSelected, options);
|
|
31
|
-
if (style)
|
|
32
|
-
Object.assign(styles, style);
|
|
33
|
-
}
|
|
33
|
+
if (style) Object.assign(styles, style);
|
|
34
34
|
} else if (variantValue && variantValue[variantSelected]) {
|
|
35
35
|
const value = variantValue[variantSelected];
|
|
36
36
|
Object.assign(
|
|
@@ -41,6 +41,22 @@ const styled = <P extends object>(
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
return styles;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isReact19) {
|
|
48
|
+
// React 19: ref is a plain prop
|
|
49
|
+
function StyledComponent19(props: P & { ref?: Ref<any> }) {
|
|
50
|
+
const { ref, ...rest } = props as any;
|
|
51
|
+
const styles = buildStyles(rest);
|
|
52
|
+
return <Component {...(rest as any)} style={styles} ref={ref} />;
|
|
53
|
+
}
|
|
54
|
+
return StyledComponent19;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// React 18 and below: use forwardRef
|
|
58
|
+
return forwardRef<any, P>((props, ref) => {
|
|
59
|
+
const styles = buildStyles(props as Record<string, any>);
|
|
44
60
|
return <Component {...(props as any)} style={styles} ref={ref} />;
|
|
45
61
|
});
|
|
46
62
|
};
|