jfs-components 0.0.79 → 0.0.85
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 +29 -0
- package/lib/commonjs/components/AppBar/AppBar.js +70 -6
- package/lib/commonjs/components/AreaLineChart/AreaLineChart.js +866 -0
- package/lib/commonjs/components/AreaLineChart/chartMath.js +252 -0
- package/lib/commonjs/components/Attached/Attached.js +76 -7
- package/lib/commonjs/components/BubbleChart/BubbleChart.js +191 -0
- package/lib/commonjs/components/BubbleChart/bubblePacking.js +378 -0
- package/lib/commonjs/components/Checkbox/Checkbox.js +18 -2
- package/lib/commonjs/components/ClusterBubble/ClusterBubble.js +272 -0
- package/lib/commonjs/components/Drawer/Drawer.js +6 -1
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -6
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
- package/lib/commonjs/components/FormField/FormField.js +1 -14
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +5 -1
- package/lib/commonjs/components/ListItem/ListItem.js +6 -11
- package/lib/commonjs/components/MessageField/MessageField.js +1 -13
- package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +7 -1
- package/lib/commonjs/components/PaymentFeedback/PaymentFeedback.js +12 -9
- package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +69 -160
- package/lib/commonjs/components/Spinner/Spinner.js +217 -0
- package/lib/commonjs/components/TextInput/TextInput.js +33 -18
- package/lib/commonjs/components/index.js +34 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/components/IconArrowdown.js +19 -0
- package/lib/commonjs/icons/components/IconArrowup.js +19 -0
- package/lib/commonjs/icons/components/IconChevrondowncircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronleftcircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronrightcircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronupcircle.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavback.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavcenter.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavhome.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavtask.js +19 -0
- package/lib/commonjs/icons/components/IconSignin.js +19 -0
- package/lib/commonjs/icons/components/IconSignout.js +19 -0
- package/lib/commonjs/icons/components/index.js +132 -0
- package/lib/commonjs/icons/registry.js +2 -2
- package/lib/module/components/AppBar/AppBar.js +70 -6
- package/lib/module/components/AreaLineChart/AreaLineChart.js +859 -0
- package/lib/module/components/AreaLineChart/chartMath.js +242 -0
- package/lib/module/components/Attached/Attached.js +76 -7
- package/lib/module/components/BubbleChart/BubbleChart.js +185 -0
- package/lib/module/components/BubbleChart/bubblePacking.js +370 -0
- package/lib/module/components/Checkbox/Checkbox.js +18 -2
- package/lib/module/components/ClusterBubble/ClusterBubble.js +267 -0
- package/lib/module/components/Drawer/Drawer.js +6 -1
- package/lib/module/components/DropdownInput/DropdownInput.js +30 -6
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
- package/lib/module/components/FormField/FormField.js +3 -16
- package/lib/module/components/FullscreenModal/FullscreenModal.js +5 -1
- package/lib/module/components/ListItem/ListItem.js +6 -11
- package/lib/module/components/MessageField/MessageField.js +3 -15
- package/lib/module/components/MetricLegendItem/MetricLegendItem.js +7 -1
- package/lib/module/components/PaymentFeedback/PaymentFeedback.js +13 -9
- package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +72 -160
- package/lib/module/components/Spinner/Spinner.js +212 -0
- package/lib/module/components/TextInput/TextInput.js +34 -19
- package/lib/module/components/index.js +4 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/components/IconArrowdown.js +12 -0
- package/lib/module/icons/components/IconArrowup.js +12 -0
- package/lib/module/icons/components/IconChevrondowncircle.js +12 -0
- package/lib/module/icons/components/IconChevronleftcircle.js +12 -0
- package/lib/module/icons/components/IconChevronrightcircle.js +12 -0
- package/lib/module/icons/components/IconChevronupcircle.js +12 -0
- package/lib/module/icons/components/IconOsnavback.js +12 -0
- package/lib/module/icons/components/IconOsnavcenter.js +12 -0
- package/lib/module/icons/components/IconOsnavhome.js +12 -0
- package/lib/module/icons/components/IconOsnavtask.js +12 -0
- package/lib/module/icons/components/IconSignin.js +12 -0
- package/lib/module/icons/components/IconSignout.js +12 -0
- package/lib/module/icons/components/index.js +12 -0
- package/lib/module/icons/registry.js +2 -2
- package/lib/typescript/src/components/AppBar/AppBar.d.ts +12 -1
- package/lib/typescript/src/components/AreaLineChart/AreaLineChart.d.ts +212 -0
- package/lib/typescript/src/components/AreaLineChart/chartMath.d.ts +90 -0
- package/lib/typescript/src/components/Attached/Attached.d.ts +19 -16
- package/lib/typescript/src/components/BubbleChart/BubbleChart.d.ts +81 -0
- package/lib/typescript/src/components/BubbleChart/bubblePacking.d.ts +83 -0
- package/lib/typescript/src/components/ClusterBubble/ClusterBubble.d.ts +76 -0
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +3 -2
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +3 -3
- package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +7 -1
- package/lib/typescript/src/components/PaymentFeedback/PaymentFeedback.d.ts +5 -1
- package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +10 -8
- package/lib/typescript/src/components/Spinner/Spinner.d.ts +45 -0
- package/lib/typescript/src/components/index.d.ts +4 -0
- package/lib/typescript/src/icons/components/IconArrowdown.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconArrowup.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevrondowncircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronleftcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronrightcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronupcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavback.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavcenter.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavhome.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavtask.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconSignin.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconSignout.d.ts +3 -0
- package/lib/typescript/src/icons/components/index.d.ts +12 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +3 -2
- package/src/components/AppBar/AppBar.tsx +92 -12
- package/src/components/AreaLineChart/AreaLineChart.tsx +1161 -0
- package/src/components/AreaLineChart/chartMath.ts +265 -0
- package/src/components/Attached/Attached.tsx +94 -7
- package/src/components/BubbleChart/BubbleChart.tsx +319 -0
- package/src/components/BubbleChart/bubblePacking.ts +397 -0
- package/src/components/Checkbox/Checkbox.tsx +14 -2
- package/src/components/ClusterBubble/ClusterBubble.tsx +359 -0
- package/src/components/Drawer/Drawer.tsx +4 -0
- package/src/components/DropdownInput/DropdownInput.tsx +54 -20
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +13 -9
- package/src/components/FormField/FormField.tsx +3 -19
- package/src/components/FullscreenModal/FullscreenModal.tsx +3 -0
- package/src/components/ListItem/ListItem.tsx +14 -16
- package/src/components/MessageField/MessageField.tsx +3 -18
- package/src/components/MetricLegendItem/MetricLegendItem.tsx +20 -6
- package/src/components/PaymentFeedback/PaymentFeedback.tsx +15 -8
- package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +82 -192
- package/src/components/Spinner/Spinner.tsx +273 -0
- package/src/components/TextInput/TextInput.tsx +37 -19
- package/src/components/index.ts +4 -0
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/components/IconArrowdown.tsx +11 -0
- package/src/icons/components/IconArrowup.tsx +11 -0
- package/src/icons/components/IconChevrondowncircle.tsx +11 -0
- package/src/icons/components/IconChevronleftcircle.tsx +11 -0
- package/src/icons/components/IconChevronrightcircle.tsx +11 -0
- package/src/icons/components/IconChevronupcircle.tsx +11 -0
- package/src/icons/components/IconOsnavback.tsx +11 -0
- package/src/icons/components/IconOsnavcenter.tsx +11 -0
- package/src/icons/components/IconOsnavhome.tsx +11 -0
- package/src/icons/components/IconOsnavtask.tsx +11 -0
- package/src/icons/components/IconSignin.tsx +11 -0
- package/src/icons/components/IconSignout.tsx +11 -0
- package/src/icons/components/index.ts +12 -0
- package/src/icons/registry.ts +49 -1
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
View,
|
|
5
|
+
type LayoutChangeEvent,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
10
|
+
import ClusterBubble, {
|
|
11
|
+
type ClusterBubbleLabelDirection,
|
|
12
|
+
type ClusterBubbleLabelPlacement,
|
|
13
|
+
} from '../ClusterBubble/ClusterBubble'
|
|
14
|
+
import {
|
|
15
|
+
chooseLabelDirections,
|
|
16
|
+
estimateLabelBox,
|
|
17
|
+
fitRadiiToBox,
|
|
18
|
+
scaleRadii,
|
|
19
|
+
simulateCluster,
|
|
20
|
+
type LabelBox,
|
|
21
|
+
} from './bubblePacking'
|
|
22
|
+
|
|
23
|
+
/** One bubble in the chart. */
|
|
24
|
+
export type BubbleDatum = {
|
|
25
|
+
/** Stable React key (falls back to the array index). */
|
|
26
|
+
id?: React.Key
|
|
27
|
+
/**
|
|
28
|
+
* Numeric magnitude controlling the bubble's *area*. Larger values produce
|
|
29
|
+
* larger circles (`sqrt`-scaled between `minBubbleSize` and `maxBubbleSize`).
|
|
30
|
+
*/
|
|
31
|
+
value: number
|
|
32
|
+
/**
|
|
33
|
+
* Bold primary text shown in/under the bubble — e.g. `"40%"`, `"₹270K"`.
|
|
34
|
+
* Defaults to the stringified `value`.
|
|
35
|
+
*/
|
|
36
|
+
display?: React.ReactNode
|
|
37
|
+
/** Secondary caption beside the primary text — e.g. `"Recommended"`. */
|
|
38
|
+
label?: React.ReactNode
|
|
39
|
+
/** `Appearance / DataViz` mode for the fill. Cycles automatically if omitted. */
|
|
40
|
+
appearance?: string
|
|
41
|
+
/** Hard color override (bypasses token resolution). */
|
|
42
|
+
color?: string
|
|
43
|
+
/** Force this bubble's label placement, overriding the chart-level default. */
|
|
44
|
+
labelPlacement?: ClusterBubbleLabelPlacement
|
|
45
|
+
/** Per-bubble accessibility label. */
|
|
46
|
+
accessibilityLabel?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type BubbleChartProps = {
|
|
50
|
+
/** The bubbles to lay out and render. */
|
|
51
|
+
data: BubbleDatum[]
|
|
52
|
+
/** Smallest bubble diameter in px. Defaults to `48`. */
|
|
53
|
+
minBubbleSize?: number
|
|
54
|
+
/** Largest bubble diameter in px. Defaults to `170`. */
|
|
55
|
+
maxBubbleSize?: number
|
|
56
|
+
/** Minimum spacing between packed bubbles in px. Defaults to `8`. */
|
|
57
|
+
gap?: number
|
|
58
|
+
/** Gap in px between a circle's edge and its outside label. Defaults to `gap`. */
|
|
59
|
+
labelGap?: number
|
|
60
|
+
/**
|
|
61
|
+
* Fixed pool height in px. When omitted, the height is derived from the
|
|
62
|
+
* measured width and the total bubble area so the cluster fits comfortably.
|
|
63
|
+
* The cluster is always confined to the `width × height` box and never
|
|
64
|
+
* overflows.
|
|
65
|
+
*/
|
|
66
|
+
height?: number
|
|
67
|
+
/** Default label placement for every bubble. Defaults to `auto`. */
|
|
68
|
+
labelPlacement?: ClusterBubbleLabelPlacement
|
|
69
|
+
/** Diameter (px) at/above which `auto` placement renders the label inside. Defaults to `88`. */
|
|
70
|
+
autoInsideMinSize?: number
|
|
71
|
+
/**
|
|
72
|
+
* Ordering of `appearance` colors assigned to bubbles that don't specify
|
|
73
|
+
* one. Cycles through this list in input order.
|
|
74
|
+
*/
|
|
75
|
+
appearanceCycle?: string[]
|
|
76
|
+
/** Number of force-simulation iterations. Higher = more settled. Defaults to `500`. */
|
|
77
|
+
iterations?: number
|
|
78
|
+
/** Notified when a bubble is pressed. Makes every bubble pressable. */
|
|
79
|
+
onBubblePress?: (datum: BubbleDatum, index: number) => void
|
|
80
|
+
/** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
|
|
81
|
+
modes?: Record<string, any>
|
|
82
|
+
/** Container style override. */
|
|
83
|
+
style?: StyleProp<ViewStyle>
|
|
84
|
+
/** Accessibility label for the whole chart. */
|
|
85
|
+
accessibilityLabel?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const DEFAULT_APPEARANCE_CYCLE = [
|
|
89
|
+
'Primary',
|
|
90
|
+
'Secondary',
|
|
91
|
+
'Tertiary',
|
|
92
|
+
'Quaternary',
|
|
93
|
+
'Quinary',
|
|
94
|
+
'Senary',
|
|
95
|
+
'Neutral',
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
const toText = (node: React.ReactNode, fallback = ''): string =>
|
|
99
|
+
typeof node === 'string' || typeof node === 'number' ? String(node) : fallback
|
|
100
|
+
|
|
101
|
+
type RenderBubble = {
|
|
102
|
+
datum: BubbleDatum
|
|
103
|
+
index: number
|
|
104
|
+
appearance: string
|
|
105
|
+
placement: 'inside' | 'outside'
|
|
106
|
+
direction: ClusterBubbleLabelDirection
|
|
107
|
+
left: number
|
|
108
|
+
top: number
|
|
109
|
+
size: number
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* `BubbleChart` arranges a set of `ClusterBubble`s with a lightweight **force
|
|
114
|
+
* simulation** — bubbles repel one another (collision), a gentle gravity keeps
|
|
115
|
+
* the cluster balanced, and the container box acts as the walls of a pool, so
|
|
116
|
+
* nothing ever overflows. Each datum's `value` drives its area (`sqrt`-scaled),
|
|
117
|
+
* colors resolve from the `dataViz/bg` token via a cycling `appearance`, and the
|
|
118
|
+
* emphasis comes from the `Emphasis / DataViz` mode. When a bubble is too small
|
|
119
|
+
* to hold its text, the label is anchored just outside the circle on whichever
|
|
120
|
+
* side has the most free space.
|
|
121
|
+
*
|
|
122
|
+
* @component
|
|
123
|
+
*/
|
|
124
|
+
function BubbleChart({
|
|
125
|
+
data,
|
|
126
|
+
minBubbleSize = 48,
|
|
127
|
+
maxBubbleSize = 170,
|
|
128
|
+
gap = 8,
|
|
129
|
+
labelGap,
|
|
130
|
+
height,
|
|
131
|
+
labelPlacement = 'auto',
|
|
132
|
+
autoInsideMinSize = 88,
|
|
133
|
+
appearanceCycle = DEFAULT_APPEARANCE_CYCLE,
|
|
134
|
+
iterations = 500,
|
|
135
|
+
onBubblePress,
|
|
136
|
+
modes: propModes = EMPTY_MODES,
|
|
137
|
+
style,
|
|
138
|
+
accessibilityLabel,
|
|
139
|
+
}: BubbleChartProps) {
|
|
140
|
+
const modes = propModes
|
|
141
|
+
const resolvedLabelGap = labelGap ?? gap
|
|
142
|
+
|
|
143
|
+
const [width, setWidth] = useState(0)
|
|
144
|
+
|
|
145
|
+
const handleLayout = useCallback((e: LayoutChangeEvent) => {
|
|
146
|
+
const w = e.nativeEvent.layout.width
|
|
147
|
+
setWidth((prev) => (Math.abs(prev - w) > 0.5 ? w : prev))
|
|
148
|
+
}, [])
|
|
149
|
+
|
|
150
|
+
const layout = useMemo(() => {
|
|
151
|
+
if (data.length === 0 || width <= 0) {
|
|
152
|
+
return { bubbles: [] as RenderBubble[], poolHeight: height ?? 0 }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const minR = Math.max(1, minBubbleSize / 2)
|
|
156
|
+
const maxR = Math.max(minR, maxBubbleSize / 2)
|
|
157
|
+
const baseRadii = scaleRadii(
|
|
158
|
+
data.map((d) => d.value),
|
|
159
|
+
minR,
|
|
160
|
+
maxR
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
// Estimate label sizes up front (independent of radius) so we can
|
|
164
|
+
// reserve a margin band around the bubbles for any outside labels.
|
|
165
|
+
const labelEstimates: LabelBox[] = data.map((d) =>
|
|
166
|
+
estimateLabelBox(toText(d.display, String(d.value)), toText(d.label))
|
|
167
|
+
)
|
|
168
|
+
const mayHaveOutside =
|
|
169
|
+
labelPlacement !== 'inside' &&
|
|
170
|
+
(minBubbleSize < autoInsideMinSize ||
|
|
171
|
+
data.some((d) => d.labelPlacement === 'outside'))
|
|
172
|
+
let insetX = gap
|
|
173
|
+
let insetY = gap
|
|
174
|
+
if (mayHaveOutside) {
|
|
175
|
+
let maxHalfW = 0
|
|
176
|
+
let maxH = 0
|
|
177
|
+
for (const e of labelEstimates) {
|
|
178
|
+
if (e.w / 2 > maxHalfW) maxHalfW = e.w / 2
|
|
179
|
+
if (e.h > maxH) maxH = e.h
|
|
180
|
+
}
|
|
181
|
+
insetX = Math.max(gap, maxHalfW + resolvedLabelGap)
|
|
182
|
+
insetY = Math.max(gap, maxH + resolvedLabelGap)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Derive a pool height from the bubble area when none is supplied, then
|
|
186
|
+
// shrink radii (if needed) so everything fits inside the inner box.
|
|
187
|
+
let circleArea = 0
|
|
188
|
+
for (const r of baseRadii) circleArea += Math.PI * r * r
|
|
189
|
+
const derivedHeight = Math.min(
|
|
190
|
+
width * 1.35,
|
|
191
|
+
Math.max(width * 0.6, circleArea / 0.42 / width + 2 * insetY)
|
|
192
|
+
)
|
|
193
|
+
const poolHeight = Math.max(1, height ?? derivedHeight)
|
|
194
|
+
|
|
195
|
+
const innerW = Math.max(1, width - 2 * insetX)
|
|
196
|
+
const innerH = Math.max(1, poolHeight - 2 * insetY)
|
|
197
|
+
|
|
198
|
+
const radii = fitRadiiToBox(baseRadii, innerW, innerH, {
|
|
199
|
+
density: 0.5,
|
|
200
|
+
labelArea: 0,
|
|
201
|
+
minRadius: 6,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Resolve placement per bubble (outside-labelled bubbles want the
|
|
205
|
+
// perimeter so their labels reach the open band along the walls).
|
|
206
|
+
const placements: Array<'inside' | 'outside'> = data.map((d, i) => {
|
|
207
|
+
const pref = d.labelPlacement ?? labelPlacement
|
|
208
|
+
const diameter = (radii[i] ?? 0) * 2
|
|
209
|
+
if (pref === 'auto') return diameter >= autoInsideMinSize ? 'inside' : 'outside'
|
|
210
|
+
return pref
|
|
211
|
+
})
|
|
212
|
+
const perimeter = placements.map((p) => p === 'outside')
|
|
213
|
+
|
|
214
|
+
const nodes = simulateCluster(radii, {
|
|
215
|
+
width,
|
|
216
|
+
height: poolHeight,
|
|
217
|
+
gap,
|
|
218
|
+
iterations,
|
|
219
|
+
insetX,
|
|
220
|
+
insetY,
|
|
221
|
+
perimeter,
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const labelBoxes: Array<LabelBox | null> = data.map((d, i) => {
|
|
225
|
+
if (placements[i] !== 'outside') return null
|
|
226
|
+
const v = toText(d.display, String(d.value))
|
|
227
|
+
const l = toText(d.label)
|
|
228
|
+
if (!v && !l) return null
|
|
229
|
+
return labelEstimates[i] ?? estimateLabelBox(v, l)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const directions = chooseLabelDirections(nodes, labelBoxes, {
|
|
233
|
+
width,
|
|
234
|
+
height: poolHeight,
|
|
235
|
+
gap: resolvedLabelGap,
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const bubbles: RenderBubble[] = data.map((datum, index) => {
|
|
239
|
+
const node = nodes[index]!
|
|
240
|
+
const r = radii[index] ?? 0
|
|
241
|
+
return {
|
|
242
|
+
datum,
|
|
243
|
+
index,
|
|
244
|
+
appearance:
|
|
245
|
+
datum.appearance ??
|
|
246
|
+
appearanceCycle[index % appearanceCycle.length] ??
|
|
247
|
+
'Primary',
|
|
248
|
+
placement: placements[index] ?? 'inside',
|
|
249
|
+
direction: directions[index] ?? 'bottom',
|
|
250
|
+
left: node.x - r,
|
|
251
|
+
top: node.y - r,
|
|
252
|
+
size: r * 2,
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
return { bubbles, poolHeight }
|
|
257
|
+
}, [
|
|
258
|
+
data,
|
|
259
|
+
width,
|
|
260
|
+
height,
|
|
261
|
+
minBubbleSize,
|
|
262
|
+
maxBubbleSize,
|
|
263
|
+
gap,
|
|
264
|
+
resolvedLabelGap,
|
|
265
|
+
labelPlacement,
|
|
266
|
+
autoInsideMinSize,
|
|
267
|
+
appearanceCycle,
|
|
268
|
+
iterations,
|
|
269
|
+
])
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<View
|
|
273
|
+
style={[styles.container, { height: layout.poolHeight }, style]}
|
|
274
|
+
onLayout={handleLayout}
|
|
275
|
+
accessibilityRole="image"
|
|
276
|
+
accessibilityLabel={accessibilityLabel}
|
|
277
|
+
>
|
|
278
|
+
{layout.bubbles.map((b) => (
|
|
279
|
+
<View
|
|
280
|
+
key={b.datum.id ?? b.index}
|
|
281
|
+
style={[styles.bubble, { left: b.left, top: b.top }]}
|
|
282
|
+
pointerEvents="box-none"
|
|
283
|
+
>
|
|
284
|
+
<ClusterBubble
|
|
285
|
+
size={b.size}
|
|
286
|
+
value={b.datum.display ?? String(b.datum.value)}
|
|
287
|
+
label={b.datum.label}
|
|
288
|
+
appearance={b.appearance}
|
|
289
|
+
labelPlacement={b.placement}
|
|
290
|
+
labelDirection={b.direction}
|
|
291
|
+
labelGap={resolvedLabelGap}
|
|
292
|
+
autoInsideMinSize={autoInsideMinSize}
|
|
293
|
+
modes={modes}
|
|
294
|
+
{...(b.datum.color ? { color: b.datum.color } : null)}
|
|
295
|
+
{...(b.datum.accessibilityLabel
|
|
296
|
+
? { accessibilityLabel: b.datum.accessibilityLabel }
|
|
297
|
+
: null)}
|
|
298
|
+
{...(onBubblePress
|
|
299
|
+
? { onPress: () => onBubblePress(b.datum, b.index) }
|
|
300
|
+
: null)}
|
|
301
|
+
/>
|
|
302
|
+
</View>
|
|
303
|
+
))}
|
|
304
|
+
</View>
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const styles = StyleSheet.create({
|
|
309
|
+
container: {
|
|
310
|
+
width: '100%',
|
|
311
|
+
position: 'relative',
|
|
312
|
+
overflow: 'hidden',
|
|
313
|
+
},
|
|
314
|
+
bubble: {
|
|
315
|
+
position: 'absolute',
|
|
316
|
+
},
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
export default BubbleChart
|