1- import React , { useEffect , useMemo , useState } from "react" ;
1+ import React , { useMemo } from "react" ;
22import {
33 forceSimulation ,
44 forceCollide ,
@@ -11,16 +11,19 @@ import {
1111 type SimulationLinkDatum ,
1212} from "d3-force" ;
1313import { createDecorators } from "@next-core/element" ;
14- import { ReactNextElement , wrapBrick } from "@next-core/react-element" ;
14+ import { ReactNextElement } from "@next-core/react-element" ;
1515import "@next-core/theme" ;
16- import ResizeObserver from "resize-observer-polyfill" ;
17- import type { Tag , TagProps } from "@next-bricks/basic/tag" ;
16+ import { formatValue } from "../shared/formatValue" ;
17+ import { CornerIndicator } from "../shared/CornerIndicator" ;
18+ import { useContainerScale } from "../shared/useContainerScale" ;
19+ import { useCenterScale } from "../shared/useCenterScale" ;
1820import "../fonts/ALiBaBaPuHuiTi.css" ;
1921import "../fonts/PangMenZhengDaoBiaoTiTi.css" ;
2022import styleText from "./styles.shadow.css" ;
23+ import cornerStyleText from "../shared/CornerIndicator.shadow.css" ;
2124
22- const BASE_WIDTH = 800 ;
23- const BASE_HEIGHT = 640 ;
25+ const BASE_WIDTH = 900 ;
26+ const BASE_HEIGHT = 700 ;
2427const CENTER_BUBBLE_RADIUS = 196 ;
2528const OTHER_BUBBLE_MAX_RADIUS = 81 ;
2629const OTHER_BUBBLE_MIN_RADIUS = 40 ;
@@ -29,12 +32,6 @@ const RANDOM_BUBBLE_MIN_RADIUS = 10;
2932const RANDOM_BUBBLE_MAX_RADIUS = 22 ;
3033const TOTAL_BUBBLE_COUNT = 18 ;
3134
32- const numberFormatter = new Intl . NumberFormat ( "zh-CN" , {
33- useGrouping : true ,
34- } ) ;
35-
36- const WrappedTag = wrapBrick < Tag , TagProps > ( "eo-tag" ) ;
37-
3835const { defineElement, property } = createDecorators ( ) ;
3936
4037export interface BubblesIndicatorProps {
@@ -47,6 +44,10 @@ export interface BubblesIndicatorProps {
4744export interface DataItem {
4845 label : string ;
4946 value : string | number ;
47+ /**
48+ * 用于计算气泡相对大小的数值。
49+ */
50+ numberValue ?: number ;
5051}
5152
5253export interface CornerDataItem extends DataItem {
@@ -74,7 +75,7 @@ interface NumberedDataItem extends DataItem {
7475 */
7576export
7677@defineElement ( "data-view.bubbles-indicator" , {
77- styleTexts : [ styleText ] ,
78+ styleTexts : [ styleText , cornerStyleText ] ,
7879} )
7980class BubblesIndicator
8081 extends ReactNextElement
@@ -129,32 +130,24 @@ export function BubblesIndicatorComponent({
129130 cornerDataSource,
130131 maxScale,
131132} : BubblesIndicatorComponentProps ) {
132- const [ scale , setScale ] = useState < number | null > ( null ) ;
133-
134- useEffect ( ( ) => {
135- // 当容器宽高低于预设值时,图形会自动缩小
136- const observer = new ResizeObserver ( ( entries ) => {
137- for ( const entry of entries ) {
138- if ( entry . target === root ) {
139- const { width, height } = entry . contentRect ;
140- // 宽度大于高度,因为有水平方向排列的标签文字
141- setScale (
142- Math . min ( maxScale ?? 1 , width / BASE_WIDTH , height / BASE_HEIGHT )
143- ) ;
144- }
145- }
146- } ) ;
147- observer . observe ( root ) ;
148- return ( ) => observer . disconnect ( ) ;
149- } , [ maxScale , root ] ) ;
133+ const scale = useContainerScale ( {
134+ width : BASE_WIDTH ,
135+ height : BASE_HEIGHT ,
136+ root,
137+ maxScale,
138+ } ) ;
139+ const [ centerValueScale , centerValueRef ] = useCenterScale ( 280 ) ;
150140
151141 // 使用 d3 力学布局计算气泡位置,将普通数据排列在中心数据周围,并填充一些小的气泡
152142 const labels = useMemo ( ( ) => {
153143 const numberedDataSource : NumberedDataItem [ ] =
154144 dataSource ?. slice ( 0 , 12 ) ?. map ( ( item ) => ( {
155145 ...item ,
156146 positiveNumberValue : Math . abs (
157- typeof item . value === "number" ? item . value : parseFloat ( item . value )
147+ item . numberValue ??
148+ ( typeof item . value === "number"
149+ ? item . value
150+ : parseFloat ( item . value ) )
158151 ) ,
159152 } ) ) ?? [ ] ;
160153 const positiveNumberValues = numberedDataSource . map (
@@ -249,7 +242,14 @@ export function BubblesIndicatorComponent({
249242 < div className = "inner-ring" > </ div >
250243 < div className = "center" >
251244 < div className = "center-label" > { centerDataSource ?. label } </ div >
252- < div className = "center-value" >
245+ < div
246+ className = "center-value"
247+ ref = { centerValueRef }
248+ style = { {
249+ visibility : centerValueScale === null ? "hidden" : "visible" ,
250+ transform : `scale(${ centerValueScale ?? 1 } )` ,
251+ } }
252+ >
253253 { formatValue ( centerDataSource ?. value ) }
254254 </ div >
255255 </ div >
@@ -277,32 +277,11 @@ export function BubblesIndicatorComponent({
277277 ) ) }
278278 </ div >
279279 </ div >
280- < div className = "corner" >
281- { cornerDataSource ?. map ( ( item , index ) => (
282- < div key = { index } className = "corner-item" >
283- < div className = "corner-label" > { item . label } </ div >
284- < WrappedTag
285- className = "corner-value"
286- outline
287- color = { item . color }
288- tagStyle = { {
289- fontSize : 18 ,
290- padding : "2px 16px" ,
291- } }
292- >
293- { formatValue ( item . value ) }
294- </ WrappedTag >
295- </ div >
296- ) ) }
297- </ div >
280+ < CornerIndicator cornerDataSource = { cornerDataSource } />
298281 </ >
299282 ) ;
300283}
301284
302- function formatValue ( value : string | number ) : string {
303- return typeof value === "number" ? numberFormatter . format ( value ) : value ;
304- }
305-
306285function manuallyTickToTheEnd (
307286 simulation : Simulation < ForceNode , SimulationLinkDatum < ForceNode > >
308287) : void {
0 commit comments