jfs-components 0.0.85 → 0.0.95
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/CHANGELOG.md +15 -0
- package/lib/commonjs/assets.d.js +1 -0
- package/lib/commonjs/components/AllocationComparisonChart/AllocationComparisonChart.js +299 -0
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +104 -94
- package/lib/commonjs/components/Icon/Icon.js +112 -0
- package/lib/commonjs/components/index.js +14 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/assets.d.js +1 -0
- package/lib/module/components/AllocationComparisonChart/AllocationComparisonChart.js +293 -0
- package/lib/module/components/FullscreenModal/FullscreenModal.js +106 -96
- package/lib/module/components/Icon/Icon.js +106 -0
- package/lib/module/components/index.js +2 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/AllocationComparisonChart/AllocationComparisonChart.d.ts +118 -0
- package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +39 -29
- package/lib/typescript/src/components/Icon/Icon.d.ts +75 -0
- package/lib/typescript/src/components/index.d.ts +2 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/assets.d.ts +24 -0
- package/src/components/AllocationComparisonChart/AllocationComparisonChart.tsx +450 -0
- package/src/components/FullscreenModal/FullscreenModal.tsx +131 -126
- package/src/components/Icon/Icon.tsx +167 -0
- package/src/components/index.ts +2 -0
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import Svg, { Line } from 'react-native-svg'
|
|
10
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
11
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider'
|
|
12
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
13
|
+
import MetricLegendItem from '../MetricLegendItem/MetricLegendItem'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One vertical pill in the {@link AllocationComparisonChartProps.data} array.
|
|
17
|
+
*
|
|
18
|
+
* Each segment renders a single bar whose **height encodes `value`** (the
|
|
19
|
+
* "current" reading) and, when supplied, a **`baseline`** overlay drawn from
|
|
20
|
+
* the bottom up with a dashed marker line (the "recommended" reading). Both
|
|
21
|
+
* are measured against the same shared scale so bars and baselines are
|
|
22
|
+
* directly comparable across segments.
|
|
23
|
+
*/
|
|
24
|
+
export type AllocationSegment = {
|
|
25
|
+
/** Stable React key (falls back to the array index). */
|
|
26
|
+
key?: React.Key
|
|
27
|
+
/** Caption rendered under the bar — e.g. `"Small & Mid"`. */
|
|
28
|
+
label: React.ReactNode
|
|
29
|
+
/**
|
|
30
|
+
* Primary value driving the bar's height (the "current" reading). Scaled
|
|
31
|
+
* against {@link AllocationComparisonChartProps.max}.
|
|
32
|
+
*/
|
|
33
|
+
value: number
|
|
34
|
+
/**
|
|
35
|
+
* Optional comparison value (the "recommended" reading). When provided, a
|
|
36
|
+
* filled overlay is drawn from the bottom of the bar up to this level.
|
|
37
|
+
* Omit to render a plain bar.
|
|
38
|
+
*/
|
|
39
|
+
baseline?: number
|
|
40
|
+
/**
|
|
41
|
+
* Whether to draw the dashed reference line + value label at the top of the
|
|
42
|
+
* `baseline` overlay. Defaults to `true` **only for the first segment that
|
|
43
|
+
* has a `baseline`** and `false` for the rest, so the marker stays a
|
|
44
|
+
* focused callout rather than repeating on every bar. Set explicitly to
|
|
45
|
+
* force it on/off per segment. Has no effect without a `baseline`.
|
|
46
|
+
*/
|
|
47
|
+
showMarker?: boolean
|
|
48
|
+
/**
|
|
49
|
+
* Text shown above the bar. Defaults to `formatValue(value)`. Pass `null`
|
|
50
|
+
* to hide it.
|
|
51
|
+
*/
|
|
52
|
+
valueLabel?: React.ReactNode
|
|
53
|
+
/**
|
|
54
|
+
* Text shown beside the dashed baseline marker. Defaults to
|
|
55
|
+
* `formatValue(baseline)`. Pass `null` to hide just the marker label while
|
|
56
|
+
* keeping the dashed line.
|
|
57
|
+
*/
|
|
58
|
+
baselineLabel?: React.ReactNode
|
|
59
|
+
/** Hard-override the bar (current) fill color. */
|
|
60
|
+
color?: string
|
|
61
|
+
/** Hard-override the baseline (recommended) overlay color. */
|
|
62
|
+
baselineColor?: string
|
|
63
|
+
/** Per-segment accessibility label. */
|
|
64
|
+
accessibilityLabel?: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type AllocationComparisonChartProps = {
|
|
68
|
+
/**
|
|
69
|
+
* The segments to plot, left → right. Each carries its `value`, optional
|
|
70
|
+
* `baseline` and its `label`, so a bar can never drift from its caption.
|
|
71
|
+
*/
|
|
72
|
+
data?: AllocationSegment[]
|
|
73
|
+
/**
|
|
74
|
+
* Maximum value used to scale every bar **and** baseline. Defaults to the
|
|
75
|
+
* largest `value`/`baseline` across `data`. Pass an explicit `max` (e.g.
|
|
76
|
+
* `100` for percentages) for a fixed scale.
|
|
77
|
+
*/
|
|
78
|
+
max?: number
|
|
79
|
+
/**
|
|
80
|
+
* Height in px of the bar area — i.e. the height of a bar whose value
|
|
81
|
+
* equals `max`. Excludes the value/caption label rows. Default: `154`.
|
|
82
|
+
*/
|
|
83
|
+
height?: number
|
|
84
|
+
/** Width of each pill bar in px. Default: the `segmentIndicator/track/width` token (`28`). */
|
|
85
|
+
barWidth?: number
|
|
86
|
+
/** Show the legend row above the chart. Default: `true`. */
|
|
87
|
+
showLegend?: boolean
|
|
88
|
+
/** Legend label for the bar (current) series. Default: `"Current"`. */
|
|
89
|
+
valueLegendLabel?: React.ReactNode
|
|
90
|
+
/**
|
|
91
|
+
* Legend label for the baseline (recommended) series. Default:
|
|
92
|
+
* `"Recommended"`. The baseline legend item only appears when at least one
|
|
93
|
+
* segment defines a `baseline`.
|
|
94
|
+
*/
|
|
95
|
+
baselineLegendLabel?: React.ReactNode
|
|
96
|
+
/**
|
|
97
|
+
* Formats numeric `value`/`baseline` into the default labels. Default:
|
|
98
|
+
* `(v) => \`${v}%\``.
|
|
99
|
+
*/
|
|
100
|
+
formatValue?: (value: number) => string
|
|
101
|
+
/** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
|
|
102
|
+
modes?: Record<string, any>
|
|
103
|
+
/** Container style override. */
|
|
104
|
+
style?: StyleProp<ViewStyle>
|
|
105
|
+
/** Style applied to the bars row. */
|
|
106
|
+
chartStyle?: StyleProp<ViewStyle>
|
|
107
|
+
/** Style applied to the legend row. */
|
|
108
|
+
legendStyle?: StyleProp<ViewStyle>
|
|
109
|
+
/** Accessibility label for the whole chart. */
|
|
110
|
+
accessibilityLabel?: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const DEFAULT_DATA: AllocationSegment[] = [
|
|
114
|
+
{ label: 'Small & Mid', value: 65, baseline: 35 },
|
|
115
|
+
{ label: 'Large', value: 25 },
|
|
116
|
+
{ label: 'Others', value: 10 },
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
const toNumber = (value: unknown, fallback: number): number => {
|
|
120
|
+
if (typeof value === 'number') {
|
|
121
|
+
return Number.isFinite(value) ? value : fallback
|
|
122
|
+
}
|
|
123
|
+
if (typeof value === 'string') {
|
|
124
|
+
const parsed = Number(value)
|
|
125
|
+
return Number.isFinite(parsed) ? parsed : fallback
|
|
126
|
+
}
|
|
127
|
+
return fallback
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const toFontWeight = (
|
|
131
|
+
value: unknown,
|
|
132
|
+
fallback: TextStyle['fontWeight']
|
|
133
|
+
): TextStyle['fontWeight'] => {
|
|
134
|
+
if (typeof value === 'number') {
|
|
135
|
+
return String(value) as TextStyle['fontWeight']
|
|
136
|
+
}
|
|
137
|
+
if (typeof value === 'string') {
|
|
138
|
+
return value as TextStyle['fontWeight']
|
|
139
|
+
}
|
|
140
|
+
return fallback
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const isShown = (node: React.ReactNode): boolean =>
|
|
144
|
+
node !== undefined && node !== null && node !== false
|
|
145
|
+
|
|
146
|
+
type SegmentTheme = {
|
|
147
|
+
barWidth: number
|
|
148
|
+
pillRadius: number
|
|
149
|
+
gap: number
|
|
150
|
+
currentColor: string
|
|
151
|
+
baselineColor: string
|
|
152
|
+
lineColor: string
|
|
153
|
+
lineSize: number
|
|
154
|
+
labelStyle: TextStyle
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
type SegmentBarProps = {
|
|
158
|
+
segment: AllocationSegment
|
|
159
|
+
barHeightPx: number
|
|
160
|
+
baselineHeightPx: number | null
|
|
161
|
+
baselineLabel: React.ReactNode
|
|
162
|
+
showMarker: boolean
|
|
163
|
+
theme: SegmentTheme
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Internal: one vertical pill column (the Figma "Segment Indicator"). Not
|
|
168
|
+
* exported — the ergonomic public unit is the chart driven by `data`. The
|
|
169
|
+
* `segmentIndicator/*` token names are mirrored here so design ↔ code token
|
|
170
|
+
* alignment is preserved.
|
|
171
|
+
*/
|
|
172
|
+
function SegmentBar({
|
|
173
|
+
segment,
|
|
174
|
+
barHeightPx,
|
|
175
|
+
baselineHeightPx,
|
|
176
|
+
baselineLabel,
|
|
177
|
+
showMarker,
|
|
178
|
+
theme,
|
|
179
|
+
}: SegmentBarProps) {
|
|
180
|
+
const { barWidth, pillRadius, gap, currentColor, baselineColor, lineColor, lineSize, labelStyle } =
|
|
181
|
+
theme
|
|
182
|
+
|
|
183
|
+
const fillColor = segment.color ?? currentColor
|
|
184
|
+
const overlayColor = segment.baselineColor ?? baselineColor
|
|
185
|
+
const showValueLabel = isShown(segment.valueLabel)
|
|
186
|
+
|
|
187
|
+
const hasBaseline = baselineHeightPx !== null && baselineHeightPx > 0
|
|
188
|
+
const overlayHeight = hasBaseline ? Math.min(baselineHeightPx as number, barHeightPx) : 0
|
|
189
|
+
const overlayRadius = Math.min(pillRadius, barWidth / 2, overlayHeight / 2)
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<View
|
|
193
|
+
style={{ flex: 1, alignItems: 'center', justifyContent: 'flex-end', gap }}
|
|
194
|
+
accessibilityLabel={segment.accessibilityLabel}
|
|
195
|
+
>
|
|
196
|
+
{showValueLabel ? (
|
|
197
|
+
<Text numberOfLines={1} style={labelStyle}>
|
|
198
|
+
{segment.valueLabel}
|
|
199
|
+
</Text>
|
|
200
|
+
) : null}
|
|
201
|
+
|
|
202
|
+
<View
|
|
203
|
+
style={{
|
|
204
|
+
width: barWidth,
|
|
205
|
+
height: Math.max(barHeightPx, 1),
|
|
206
|
+
borderRadius: pillRadius,
|
|
207
|
+
backgroundColor: fillColor,
|
|
208
|
+
position: 'relative',
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
{hasBaseline ? (
|
|
212
|
+
<>
|
|
213
|
+
<View
|
|
214
|
+
style={{
|
|
215
|
+
position: 'absolute',
|
|
216
|
+
left: 0,
|
|
217
|
+
right: 0,
|
|
218
|
+
bottom: 0,
|
|
219
|
+
height: overlayHeight,
|
|
220
|
+
backgroundColor: overlayColor,
|
|
221
|
+
borderBottomLeftRadius: overlayRadius,
|
|
222
|
+
borderBottomRightRadius: overlayRadius,
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
{showMarker ? (
|
|
226
|
+
<View
|
|
227
|
+
style={{
|
|
228
|
+
position: 'absolute',
|
|
229
|
+
left: 0,
|
|
230
|
+
bottom: overlayHeight,
|
|
231
|
+
height: 0,
|
|
232
|
+
flexDirection: 'row',
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
}}
|
|
235
|
+
pointerEvents="none"
|
|
236
|
+
>
|
|
237
|
+
<Svg width={barWidth} height={Math.max(lineSize, 1)}>
|
|
238
|
+
<Line
|
|
239
|
+
x1={0}
|
|
240
|
+
y1={Math.max(lineSize, 1) / 2}
|
|
241
|
+
x2={barWidth}
|
|
242
|
+
y2={Math.max(lineSize, 1) / 2}
|
|
243
|
+
stroke={lineColor}
|
|
244
|
+
strokeWidth={lineSize}
|
|
245
|
+
strokeDasharray="2 2"
|
|
246
|
+
/>
|
|
247
|
+
</Svg>
|
|
248
|
+
{isShown(baselineLabel) ? (
|
|
249
|
+
<Text numberOfLines={1} style={[labelStyle, { marginLeft: 6 }]}>
|
|
250
|
+
{baselineLabel}
|
|
251
|
+
</Text>
|
|
252
|
+
) : null}
|
|
253
|
+
</View>
|
|
254
|
+
) : null}
|
|
255
|
+
</>
|
|
256
|
+
) : null}
|
|
257
|
+
</View>
|
|
258
|
+
|
|
259
|
+
<Text numberOfLines={1} style={labelStyle}>
|
|
260
|
+
{segment.label}
|
|
261
|
+
</Text>
|
|
262
|
+
</View>
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* `AllocationComparisonChart` plots a row of vertical pill bars that compare a
|
|
268
|
+
* **current** reading (each bar's height) against an optional **recommended**
|
|
269
|
+
* baseline (a filled overlay drawn from the bottom up, marked with a dashed
|
|
270
|
+
* line). Every bar and baseline shares a single scale, so heights are directly
|
|
271
|
+
* comparable across segments — no axes required.
|
|
272
|
+
*
|
|
273
|
+
* The chart is driven entirely by the `data` array: each entry pairs a
|
|
274
|
+
* `value`, an optional `baseline` and its `label`, so a bar can never drift
|
|
275
|
+
* out of sync with its caption or its baseline marker.
|
|
276
|
+
*
|
|
277
|
+
* Colors, fonts, spacing and the pill radius resolve from the Figma
|
|
278
|
+
* `segmentIndicator/*`, `metricLegendItem/*` and `allocationComparisonChart/*`
|
|
279
|
+
* tokens via the `modes` prop.
|
|
280
|
+
*
|
|
281
|
+
* @component
|
|
282
|
+
*/
|
|
283
|
+
function AllocationComparisonChart({
|
|
284
|
+
data = DEFAULT_DATA,
|
|
285
|
+
max,
|
|
286
|
+
height = 154,
|
|
287
|
+
barWidth,
|
|
288
|
+
showLegend = true,
|
|
289
|
+
valueLegendLabel = 'Current',
|
|
290
|
+
baselineLegendLabel = 'Recommended',
|
|
291
|
+
formatValue = (value: number) => `${value}%`,
|
|
292
|
+
modes: propModes = EMPTY_MODES,
|
|
293
|
+
style,
|
|
294
|
+
chartStyle,
|
|
295
|
+
legendStyle,
|
|
296
|
+
accessibilityLabel,
|
|
297
|
+
}: AllocationComparisonChartProps) {
|
|
298
|
+
const { modes: globalModes } = useTokens()
|
|
299
|
+
const modes = React.useMemo(
|
|
300
|
+
() => ({ ...globalModes, ...propModes }),
|
|
301
|
+
[globalModes, propModes]
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
const trackWidth = toNumber(getVariableByName('segmentIndicator/track/width', modes), 28)
|
|
305
|
+
const resolvedBarWidth = barWidth ?? trackWidth
|
|
306
|
+
const radiusToken = toNumber(getVariableByName('segmentIndicator/indicator/radius', modes), 99999)
|
|
307
|
+
const pillRadius = Math.min(radiusToken, resolvedBarWidth / 2)
|
|
308
|
+
const gap = toNumber(getVariableByName('segmentIndicator/gap', modes), 4)
|
|
309
|
+
const chartGap = toNumber(getVariableByName('allocationComparisonChart/gap', modes), 8)
|
|
310
|
+
|
|
311
|
+
const currentColor =
|
|
312
|
+
(getVariableByName('segmentIndicator/indicator/background', modes) as string | null) ??
|
|
313
|
+
'#5d00b5'
|
|
314
|
+
const baselineColor =
|
|
315
|
+
(getVariableByName('segmentIndicator/indicator/foreground', modes) as string | null) ??
|
|
316
|
+
'#b84fbd'
|
|
317
|
+
const lineColor =
|
|
318
|
+
(getVariableByName('segmentIndicator/indicator/line/color', modes) as string | null) ??
|
|
319
|
+
'#ffffff'
|
|
320
|
+
const lineSize = toNumber(getVariableByName('segmentIndicator/indicator/line/size', modes), 1)
|
|
321
|
+
|
|
322
|
+
const foreground =
|
|
323
|
+
(getVariableByName('segmentIndicator/foreground', modes) as string | null) ?? '#0c0d10'
|
|
324
|
+
const fontFamily =
|
|
325
|
+
(getVariableByName('segmentIndicator/fontFamily', modes) as string | null) ?? 'JioType Var'
|
|
326
|
+
const fontSize = toNumber(getVariableByName('segmentIndicator/fontSize', modes), 12)
|
|
327
|
+
const lineHeight = toNumber(getVariableByName('segmentIndicator/lineHeight', modes), 12)
|
|
328
|
+
const fontWeight = toFontWeight(getVariableByName('segmentIndicator/fontWeight', modes), '400')
|
|
329
|
+
|
|
330
|
+
const labelStyle: TextStyle = {
|
|
331
|
+
color: foreground,
|
|
332
|
+
fontFamily,
|
|
333
|
+
fontSize,
|
|
334
|
+
lineHeight,
|
|
335
|
+
fontWeight,
|
|
336
|
+
textAlign: 'center',
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const computedMax =
|
|
340
|
+
max ??
|
|
341
|
+
data.reduce(
|
|
342
|
+
(acc, seg) => Math.max(acc, seg.value, seg.baseline ?? 0),
|
|
343
|
+
0
|
|
344
|
+
)
|
|
345
|
+
const safeMax = computedMax > 0 ? computedMax : 1
|
|
346
|
+
|
|
347
|
+
const firstBaselineIndex = data.findIndex(
|
|
348
|
+
(seg) => typeof seg.baseline === 'number'
|
|
349
|
+
)
|
|
350
|
+
const hasAnyBaseline = firstBaselineIndex !== -1
|
|
351
|
+
|
|
352
|
+
const theme: SegmentTheme = {
|
|
353
|
+
barWidth: resolvedBarWidth,
|
|
354
|
+
pillRadius,
|
|
355
|
+
gap,
|
|
356
|
+
currentColor,
|
|
357
|
+
baselineColor,
|
|
358
|
+
lineColor,
|
|
359
|
+
lineSize,
|
|
360
|
+
labelStyle,
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const defaultAccessibilityLabel =
|
|
364
|
+
accessibilityLabel ??
|
|
365
|
+
`Allocation comparison of ${data.length} segment${data.length === 1 ? '' : 's'}: ` +
|
|
366
|
+
data
|
|
367
|
+
.map((seg) => {
|
|
368
|
+
const label = typeof seg.label === 'string' ? seg.label : 'segment'
|
|
369
|
+
const base =
|
|
370
|
+
typeof seg.baseline === 'number'
|
|
371
|
+
? `, recommended ${seg.baseline}`
|
|
372
|
+
: ''
|
|
373
|
+
return `${label} ${seg.value}${base}`
|
|
374
|
+
})
|
|
375
|
+
.join('; ')
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<View style={[{ width: '100%' }, style]} accessibilityLabel={defaultAccessibilityLabel}>
|
|
379
|
+
{showLegend ? (
|
|
380
|
+
<View
|
|
381
|
+
style={[
|
|
382
|
+
{ flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: chartGap },
|
|
383
|
+
legendStyle,
|
|
384
|
+
]}
|
|
385
|
+
>
|
|
386
|
+
<MetricLegendItem
|
|
387
|
+
label={valueLegendLabel}
|
|
388
|
+
indicatorColor={currentColor}
|
|
389
|
+
modes={modes}
|
|
390
|
+
style={{ flexGrow: 0, flexShrink: 1 }}
|
|
391
|
+
/>
|
|
392
|
+
{hasAnyBaseline ? (
|
|
393
|
+
<MetricLegendItem
|
|
394
|
+
label={baselineLegendLabel}
|
|
395
|
+
indicatorColor={baselineColor}
|
|
396
|
+
modes={modes}
|
|
397
|
+
style={{ flexGrow: 0, flexShrink: 1 }}
|
|
398
|
+
/>
|
|
399
|
+
) : null}
|
|
400
|
+
</View>
|
|
401
|
+
) : null}
|
|
402
|
+
|
|
403
|
+
<View
|
|
404
|
+
accessibilityRole="image"
|
|
405
|
+
style={[
|
|
406
|
+
{ flexDirection: 'row', alignItems: 'flex-end', gap: 8, width: '100%' },
|
|
407
|
+
chartStyle,
|
|
408
|
+
]}
|
|
409
|
+
>
|
|
410
|
+
{data.map((segment, index) => {
|
|
411
|
+
const ratio = Math.max(0, Math.min(1, segment.value / safeMax))
|
|
412
|
+
const barHeightPx = Math.max(0, height * ratio)
|
|
413
|
+
const baselineHeightPx =
|
|
414
|
+
typeof segment.baseline === 'number'
|
|
415
|
+
? Math.max(0, Math.min(1, segment.baseline / safeMax)) * height
|
|
416
|
+
: null
|
|
417
|
+
const baselineLabel =
|
|
418
|
+
segment.baselineLabel === undefined
|
|
419
|
+
? typeof segment.baseline === 'number'
|
|
420
|
+
? formatValue(segment.baseline)
|
|
421
|
+
: undefined
|
|
422
|
+
: segment.baselineLabel
|
|
423
|
+
const resolvedSegment: AllocationSegment = {
|
|
424
|
+
...segment,
|
|
425
|
+
valueLabel:
|
|
426
|
+
segment.valueLabel === undefined
|
|
427
|
+
? formatValue(segment.value)
|
|
428
|
+
: segment.valueLabel,
|
|
429
|
+
}
|
|
430
|
+
const showMarker =
|
|
431
|
+
segment.showMarker ?? index === firstBaselineIndex
|
|
432
|
+
|
|
433
|
+
return (
|
|
434
|
+
<SegmentBar
|
|
435
|
+
key={segment.key ?? `segment-${index}`}
|
|
436
|
+
segment={resolvedSegment}
|
|
437
|
+
barHeightPx={barHeightPx}
|
|
438
|
+
baselineHeightPx={baselineHeightPx}
|
|
439
|
+
baselineLabel={baselineLabel}
|
|
440
|
+
showMarker={showMarker}
|
|
441
|
+
theme={theme}
|
|
442
|
+
/>
|
|
443
|
+
)
|
|
444
|
+
})}
|
|
445
|
+
</View>
|
|
446
|
+
</View>
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export default AllocationComparisonChart
|