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.
Files changed (54) hide show
  1. package/README.md +81 -3709
  2. package/lib/commonjs/circularProgress/index.js +102 -83
  3. package/lib/commonjs/circularProgress/index.js.map +1 -1
  4. package/lib/commonjs/tabBar/TabBar.js +87 -139
  5. package/lib/commonjs/tabBar/TabBar.js.map +1 -1
  6. package/lib/commonjs/utiles/styled.js +26 -5
  7. package/lib/commonjs/utiles/styled.js.map +1 -1
  8. package/lib/module/circularProgress/index.js +103 -84
  9. package/lib/module/circularProgress/index.js.map +1 -1
  10. package/lib/module/tabBar/TabBar.js +88 -140
  11. package/lib/module/tabBar/TabBar.js.map +1 -1
  12. package/lib/module/utiles/styled.js +26 -5
  13. package/lib/module/utiles/styled.js.map +1 -1
  14. package/lib/typescript/badge/index.d.ts +3 -1
  15. package/lib/typescript/badge/index.d.ts.map +1 -1
  16. package/lib/typescript/button/index.d.ts +5 -1
  17. package/lib/typescript/button/index.d.ts.map +1 -1
  18. package/lib/typescript/card/index.d.ts +3 -1
  19. package/lib/typescript/card/index.d.ts.map +1 -1
  20. package/lib/typescript/circularProgress/index.d.ts +4 -5
  21. package/lib/typescript/circularProgress/index.d.ts.map +1 -1
  22. package/lib/typescript/dialog/index.d.ts +3 -1
  23. package/lib/typescript/dialog/index.d.ts.map +1 -1
  24. package/lib/typescript/divider/index.d.ts +3 -1
  25. package/lib/typescript/divider/index.d.ts.map +1 -1
  26. package/lib/typescript/header/index.d.ts +3 -1
  27. package/lib/typescript/header/index.d.ts.map +1 -1
  28. package/lib/typescript/image/index.d.ts +3 -1
  29. package/lib/typescript/image/index.d.ts.map +1 -1
  30. package/lib/typescript/pressable/index.d.ts +3 -1
  31. package/lib/typescript/pressable/index.d.ts.map +1 -1
  32. package/lib/typescript/safeAreaProvider/index.d.ts +3 -1
  33. package/lib/typescript/safeAreaProvider/index.d.ts.map +1 -1
  34. package/lib/typescript/safeAreaView/index.d.ts +3 -1
  35. package/lib/typescript/safeAreaView/index.d.ts.map +1 -1
  36. package/lib/typescript/scrollView/index.d.ts +3 -1
  37. package/lib/typescript/scrollView/index.d.ts.map +1 -1
  38. package/lib/typescript/shape/cycle.d.ts +3 -1
  39. package/lib/typescript/shape/cycle.d.ts.map +1 -1
  40. package/lib/typescript/shape/index.d.ts +3 -1
  41. package/lib/typescript/shape/index.d.ts.map +1 -1
  42. package/lib/typescript/spacer/index.d.ts +3 -1
  43. package/lib/typescript/spacer/index.d.ts.map +1 -1
  44. package/lib/typescript/stack/index.d.ts +9 -3
  45. package/lib/typescript/stack/index.d.ts.map +1 -1
  46. package/lib/typescript/tabBar/TabBar.d.ts.map +1 -1
  47. package/lib/typescript/text/index.d.ts +3 -1
  48. package/lib/typescript/text/index.d.ts.map +1 -1
  49. package/lib/typescript/utiles/styled.d.ts +4 -2
  50. package/lib/typescript/utiles/styled.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/circularProgress/index.tsx +177 -164
  53. package/src/tabBar/TabBar.tsx +120 -233
  54. package/src/utiles/styled.tsx +22 -6
@@ -6,7 +6,6 @@ import React, {
6
6
  import {
7
7
  Animated,
8
8
  Easing,
9
- StyleSheet,
10
9
  } from 'react-native'
11
10
  import Svg, { Circle, Defs, LinearGradient, Stop } from 'react-native-svg'
12
11
 
@@ -54,38 +53,41 @@ export interface StyledCircularProgressProps {
54
53
  colors?: Partial<CircularProgressColors>
55
54
 
56
55
  /**
57
- * Controls how built-in text is arranged.
58
- * - 'stacked': display text first, then label / sublabel underneath
59
- * - 'center': display text, label, and sublabel are centered inside the ring
60
- * @default 'stacked'
56
+ * 'inside' — percentage / label rendered centred inside the ring (default)
57
+ * 'stacked' text sits below the ring
58
+ * 'center' alias for 'inside' (backwards-compat)
61
59
  */
62
- contentPosition?: 'center' | 'stacked'
60
+ contentPosition?: 'inside' | 'center' | 'stacked'
63
61
 
64
62
  children?: React.ReactNode
65
63
  }
66
64
 
65
+ // ─── Size presets ─────────────────────────────────────────────────────────────
66
+
67
67
  const SIZE_MAP: Record<
68
68
  CircularProgressSize,
69
69
  { diameter: number; stroke: number; primaryFont: number; secondaryFont: number }
70
70
  > = {
71
- xs: { diameter: 48, stroke: 4, primaryFont: 11, secondaryFont: 8 },
72
- sm: { diameter: 64, stroke: 5, primaryFont: 13, secondaryFont: 9 },
73
- md: { diameter: 80, stroke: 6, primaryFont: 15, secondaryFont: 10 },
71
+ xs: { diameter: 48, stroke: 4, primaryFont: 11, secondaryFont: 8 },
72
+ sm: { diameter: 64, stroke: 5, primaryFont: 13, secondaryFont: 9 },
73
+ md: { diameter: 80, stroke: 6, primaryFont: 15, secondaryFont: 10 },
74
74
  lg: { diameter: 100, stroke: 7, primaryFont: 18, secondaryFont: 11 },
75
75
  xl: { diameter: 128, stroke: 8, primaryFont: 22, secondaryFont: 12 },
76
76
  }
77
77
 
78
78
  const DEFAULT_COLORS: CircularProgressColors = {
79
- arc: theme.colors.indigo?.[500] ?? '#6366f1',
80
- track: theme.colors.gray[200],
81
- label: theme.colors.gray[800],
82
- sublabel: theme.colors.gray[400],
79
+ arc: theme.colors.indigo?.[500] ?? '#6366f1',
80
+ track: theme.colors.gray[200],
81
+ label: theme.colors.gray[800],
82
+ sublabel: theme.colors.gray[400],
83
83
  gradientFrom: theme.colors.violet?.[500] ?? '#8b5cf6',
84
- gradientTo: theme.colors.cyan?.[400] ?? '#22d3ee',
84
+ gradientTo: theme.colors.cyan?.[400] ?? '#22d3ee',
85
85
  }
86
86
 
87
87
  const AnimatedCircle = Animated.createAnimatedComponent(Circle)
88
88
 
89
+ // ─── Component ────────────────────────────────────────────────────────────────
90
+
89
91
  export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
90
92
  value,
91
93
  total = 100,
@@ -100,25 +102,25 @@ export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
100
102
  animated = true,
101
103
  duration = 900,
102
104
  colors: colorOverrides,
103
- contentPosition = 'stacked',
105
+ contentPosition = 'inside',
104
106
  children,
105
107
  }) => {
106
- const preset = SIZE_MAP[size]
107
- const diameter = diameterProp ?? preset.diameter
108
+ const preset = SIZE_MAP[size]
109
+ const diameter = diameterProp ?? preset.diameter
108
110
  const strokeWidth = strokeWidthProp ?? preset.stroke
109
111
 
110
112
  const isDashboard = variant === 'dashboard'
111
113
 
112
- const radius = (diameter - strokeWidth) / 2
114
+ const radius = (diameter - strokeWidth) / 2
113
115
  const circumference = 2 * Math.PI * radius
114
- const arcLength = isDashboard ? circumference / 2 : circumference
116
+ const arcLength = isDashboard ? circumference / 2 : circumference
115
117
 
116
118
  const colors: CircularProgressColors = useMemo(
117
119
  () => (colorOverrides ? { ...DEFAULT_COLORS, ...colorOverrides } : DEFAULT_COLORS),
118
- [colorOverrides]
120
+ [colorOverrides],
119
121
  )
120
122
 
121
- const clamped = Math.min(Math.max(value, 0), total)
123
+ const clamped = Math.min(Math.max(value, 0), total)
122
124
  const fraction = total > 0 ? clamped / total : 0
123
125
 
124
126
  const progress = useRef(new Animated.Value(animated ? 0 : fraction)).current
@@ -128,59 +130,117 @@ export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
128
130
  progress.setValue(fraction)
129
131
  return
130
132
  }
131
-
132
133
  Animated.timing(progress, {
133
- toValue: fraction,
134
+ toValue: fraction,
134
135
  duration,
135
- easing: Easing.out(Easing.cubic),
136
+ easing: Easing.out(Easing.cubic),
136
137
  useNativeDriver: false,
137
138
  }).start()
138
139
  }, [fraction, animated, duration, progress])
139
140
 
140
141
  const strokeDashoffset = progress.interpolate({
141
- inputRange: [0, 1],
142
+ inputRange: [0, 1],
142
143
  outputRange: [arcLength, 0],
143
144
  })
144
145
 
145
- const center = diameter / 2
146
+ const center = diameter / 2
146
147
  const gradientId = `cpGrad-${Math.round(diameter)}-${variant}`
147
148
 
148
149
  const centreText = useMemo(() => {
149
150
  switch (display) {
150
- case 'percent':
151
- return `${Math.round(fraction * 100)}%`
152
- case 'fraction':
153
- return `${clamped}/${total}`
154
- case 'value':
155
- return String(clamped)
156
- case 'label':
157
- return label ?? ''
158
- case 'none':
159
- return null
160
- default:
161
- return null
151
+ case 'percent': return `${Math.round(fraction * 100)}%`
152
+ case 'fraction': return `${clamped}/${total}`
153
+ case 'value': return String(clamped)
154
+ case 'label': return label ?? ''
155
+ case 'none': return null
156
+ default: return null
162
157
  }
163
158
  }, [display, fraction, clamped, total, label])
164
159
 
165
- const arcStroke = variant === 'gradient' ? `url(#${gradientId})` : colors.arc
160
+ const arcStroke = variant === 'gradient' ? `url(#${gradientId})` : colors.arc
166
161
  const trackOpacity = variant === 'ghost' ? 0 : 1
167
- const trackDash = isDashboard ? [arcLength, circumference - arcLength] : undefined
162
+ const trackDash = isDashboard ? [arcLength, circumference - arcLength] : undefined
163
+
164
+ // 'inside' and 'center' both mean text lives inside the ring
165
+ const isInside = contentPosition === 'inside' || contentPosition === 'center'
168
166
 
169
- const dashboardTextOffset = isDashboard ? -(diameter / 4) : 0
167
+ // ── SVG height for dashboard variant ──────────────────────────────────────
168
+ const svgHeight = isDashboard ? diameter / 2 + strokeWidth : diameter
169
+ const containerH = isDashboard ? diameter / 2 + strokeWidth : diameter
170
+
171
+ // ── Text overlay rendered inside the ring ─────────────────────────────────
172
+ const renderInsideText = () => {
173
+ if (children) return null // custom children take over
170
174
 
171
- const renderTextBlock = () => {
172
- if (contentPosition === 'center') {
173
175
  return (
174
176
  <Stack
175
177
  position="absolute"
176
- left={strokeWidth + 6}
177
- right={strokeWidth + 6}
178
178
  top={0}
179
- bottom={0}
179
+ left={0}
180
+ width={diameter}
181
+ height={containerH}
180
182
  alignItems="center"
181
183
  justifyContent="center"
182
- gap={1}
183
184
  pointerEvents="none"
185
+ >
186
+ <Stack
187
+ alignItems="center"
188
+ justifyContent="center"
189
+ gap={2}
190
+ paddingBottom={isDashboard ? strokeWidth + 4 : 0}
191
+ >
192
+ {centreText !== null && centreText !== '' && (
193
+ <StyledText
194
+ fontSize={preset.primaryFont}
195
+ fontWeight={theme.fontWeight.bold}
196
+ color={colors.label}
197
+ lineHeight={preset.primaryFont + 3}
198
+ textAlign="center"
199
+ >
200
+ {centreText}
201
+ </StyledText>
202
+ )}
203
+
204
+ {display !== 'label' && display !== 'none' && !!label && (
205
+ <StyledText
206
+ fontSize={preset.secondaryFont}
207
+ color={colors.sublabel}
208
+ lineHeight={preset.secondaryFont + 2}
209
+ textAlign="center"
210
+ >
211
+ {label}
212
+ </StyledText>
213
+ )}
214
+
215
+ {!!sublabel && (
216
+ <StyledText
217
+ fontSize={Math.max(preset.secondaryFont - 1, 8)}
218
+ color={colors.sublabel}
219
+ lineHeight={preset.secondaryFont + 1}
220
+ textAlign="center"
221
+ >
222
+ {sublabel}
223
+ </StyledText>
224
+ )}
225
+ </Stack>
226
+ </Stack>
227
+ )
228
+ }
229
+
230
+ // ── Text block rendered below the ring ────────────────────────────────────
231
+ const renderStackedText = () => {
232
+ if (children) return null
233
+
234
+ const showLabel = display !== 'label' && display !== 'none' && !!label
235
+ const showSublabel = !!sublabel
236
+
237
+ return (
238
+ <Stack
239
+ alignItems="center"
240
+ justifyContent="center"
241
+ gap={1}
242
+ marginTop={isDashboard ? -(diameter / 4) : 0}
243
+ paddingHorizontal={strokeWidth + 4}
184
244
  >
185
245
  {centreText !== null && centreText !== '' && (
186
246
  <StyledText
@@ -194,7 +254,7 @@ export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
194
254
  </StyledText>
195
255
  )}
196
256
 
197
- {!!label && (
257
+ {showLabel && (
198
258
  <StyledText
199
259
  fontSize={preset.secondaryFont}
200
260
  color={colors.sublabel}
@@ -205,11 +265,13 @@ export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
205
265
  </StyledText>
206
266
  )}
207
267
 
208
- {!!sublabel && (
268
+ {showSublabel && (
209
269
  <StyledText
210
- fontSize={Math.max(preset.secondaryFont - 1, 8)}
270
+ fontSize={showLabel
271
+ ? Math.max(preset.secondaryFont - 1, 8)
272
+ : preset.secondaryFont}
211
273
  color={colors.sublabel}
212
- lineHeight={preset.secondaryFont + 1}
274
+ lineHeight={preset.secondaryFont + 2}
213
275
  textAlign="center"
214
276
  >
215
277
  {sublabel}
@@ -219,131 +281,82 @@ export const StyledCircularProgress: React.FC<StyledCircularProgressProps> = ({
219
281
  )
220
282
  }
221
283
 
222
- const showSecondLine = display !== 'label' && display !== 'none' && !!label
223
- const showSubLabel = !!sublabel
224
-
225
- return (
226
- <Stack
227
- alignItems="center"
228
- justifyContent="center"
229
- gap={1}
230
- marginTop={dashboardTextOffset}
231
- paddingHorizontal={strokeWidth + 4}
232
- >
233
- {centreText !== null && centreText !== '' && (
234
- <StyledText
235
- fontSize={preset.primaryFont}
236
- fontWeight={theme.fontWeight.bold}
237
- color={colors.label}
238
- lineHeight={preset.primaryFont + 3}
239
- textAlign="center"
240
- >
241
- {centreText}
242
- </StyledText>
243
- )}
244
-
245
- {showSecondLine && (
246
- <StyledText
247
- fontSize={preset.secondaryFont}
248
- color={colors.sublabel}
249
- lineHeight={preset.secondaryFont + 2}
250
- textAlign="center"
251
- >
252
- {label}
253
- </StyledText>
254
- )}
255
-
256
- {showSubLabel && !showSecondLine && (
257
- <StyledText
258
- fontSize={preset.secondaryFont}
259
- color={colors.sublabel}
260
- lineHeight={preset.secondaryFont + 2}
261
- textAlign="center"
262
- >
263
- {sublabel}
264
- </StyledText>
265
- )}
266
-
267
- {showSubLabel && showSecondLine && (
268
- <StyledText
269
- fontSize={Math.max(preset.secondaryFont - 1, 8)}
270
- color={colors.sublabel}
271
- lineHeight={preset.secondaryFont + 1}
272
- textAlign="center"
273
- >
274
- {sublabel}
275
- </StyledText>
276
- )}
277
- </Stack>
278
- )
279
- }
284
+ // ─────────────────────────────────────────────────────────────────────────
280
285
 
281
286
  return (
282
287
  <Stack
283
288
  width={diameter}
284
- height={isDashboard ? diameter / 2 + strokeWidth : diameter}
289
+ height={isInside ? containerH : undefined}
285
290
  alignItems="center"
286
291
  justifyContent="center"
287
- position="relative"
292
+ position="relative"
288
293
  >
289
- <Stack style={S.svg_wrap}>
290
- <Svg
291
- width={diameter}
292
- height={diameter}
293
- style={isDashboard ? { marginTop: -(diameter / 2) } : undefined}
294
- >
295
- {variant === 'gradient' && (
296
- <Defs>
297
- <LinearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="0%">
298
- <Stop offset="0%" stopColor={colors.gradientFrom} />
299
- <Stop offset="100%" stopColor={colors.gradientTo} />
300
- </LinearGradient>
301
- </Defs>
302
- )}
303
-
304
- <Circle
305
- cx={center}
306
- cy={center}
307
- r={radius}
308
- stroke={colors.track}
309
- strokeWidth={strokeWidth}
310
- strokeDasharray={trackDash}
311
- strokeLinecap="butt"
312
- fill="none"
313
- opacity={trackOpacity}
314
- rotation={isDashboard ? 90 : 0}
315
- origin={`${center}, ${center}`}
316
- />
317
-
318
- <AnimatedCircle
319
- cx={center}
320
- cy={center}
321
- r={radius}
322
- stroke={arcStroke as any}
323
- strokeWidth={strokeWidth}
324
- fill="none"
325
- strokeDasharray={arcLength}
326
- strokeDashoffset={strokeDashoffset as any}
327
- strokeLinecap={lineCap}
328
- rotation={isDashboard ? 90 : -90}
329
- origin={`${center}, ${center}`}
330
- />
331
- </Svg>
332
- </Stack>
294
+ {/* SVG ring */}
295
+ <Svg
296
+ width={diameter}
297
+ height={svgHeight}
298
+ style={isDashboard ? { marginTop: -(diameter / 2) } : undefined}
299
+ >
300
+ {variant === 'gradient' && (
301
+ <Defs>
302
+ <LinearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="0%">
303
+ <Stop offset="0%" stopColor={colors.gradientFrom} />
304
+ <Stop offset="100%" stopColor={colors.gradientTo} />
305
+ </LinearGradient>
306
+ </Defs>
307
+ )}
333
308
 
309
+ {/* Track */}
310
+ <Circle
311
+ cx={center}
312
+ cy={center}
313
+ r={radius}
314
+ stroke={colors.track}
315
+ strokeWidth={strokeWidth}
316
+ strokeDasharray={trackDash}
317
+ strokeLinecap="butt"
318
+ fill="none"
319
+ opacity={trackOpacity}
320
+ rotation={isDashboard ? 90 : 0}
321
+ origin={`${center}, ${center}`}
322
+ />
323
+
324
+ {/* Progress arc */}
325
+ <AnimatedCircle
326
+ cx={center}
327
+ cy={center}
328
+ r={radius}
329
+ stroke={arcStroke as any}
330
+ strokeWidth={strokeWidth}
331
+ fill="none"
332
+ strokeDasharray={arcLength}
333
+ strokeDashoffset={strokeDashoffset as any}
334
+ strokeLinecap={lineCap}
335
+ rotation={isDashboard ? 90 : -90}
336
+ origin={`${center}, ${center}`}
337
+ />
338
+ </Svg>
339
+
340
+ {/* Text / children */}
334
341
  {children ? (
335
- <Stack alignItems="center" justifyContent="center">
342
+ <Stack
343
+ position="absolute"
344
+ top={0}
345
+ left={0}
346
+ width={diameter}
347
+ height={containerH}
348
+ alignItems="center"
349
+ justifyContent="center"
350
+ pointerEvents="none"
351
+ gap={2}
352
+ >
336
353
  {children}
337
354
  </Stack>
355
+ ) : isInside ? (
356
+ renderInsideText()
338
357
  ) : (
339
- renderTextBlock()
358
+ renderStackedText()
340
359
  )}
341
360
  </Stack>
342
361
  )
343
- }
344
-
345
- const S = StyleSheet.create({
346
- svg_wrap: {
347
- position: 'absolute',
348
- },
349
- })
362
+ }