Skip to content

Commit 4d5c134

Browse files
moayuisuda林舸
andauthored
feat: legend-size-autofit (#7245)
* feat: legend size autofit * feat: legend size autofit * feat: add dev skills --------- Co-authored-by: 林舸 <anhaohui.ahh@antgroup.com>
1 parent 2cdfe45 commit 4d5c134

File tree

8 files changed

+643
-276
lines changed

8 files changed

+643
-276
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
name: G2 Legend Expert
3+
description: Expert skill for G2 legend development - provides comprehensive knowledge about legend rendering implementation, component architecture, layout algorithms, and interaction handling. Use when implementing, customizing, or debugging legend functionality in G2 visualizations.
4+
---
5+
6+
# G2 Legend Expert Skill
7+
8+
## Overview
9+
10+
This skill provides comprehensive knowledge about legend rendering in G2, covering the complete legend rendering flow from component creation to layout calculation and interaction handling.
11+
12+
## Legend Rendering Flow in G2
13+
14+
The legend rendering flow in G2 follows a multi-stage process that transforms legend configuration into visual components. This flow involves several key stages:
15+
16+
### 1. Legend Component Inference
17+
18+
Legend components are inferred from the chart's scales and configuration during the initial setup phase. The inference process is handled by the `inferComponent` function in `src/runtime/component.ts`:
19+
20+
- **Scale Analysis**: The system analyzes all scales in the chart, looking for channels like `shape`, `size`, `color`, and `opacity`
21+
- **Legend Type Detection**: Based on the scale types, the system determines whether to create `legendCategory` (for discrete scales) or `legendContinuous` (for continuous scales)
22+
- **Position Inference**: Default positions and orientations are inferred based on the chart type and coordinate system
23+
24+
### 2. Legend Component Creation
25+
26+
Two main legend component types are implemented:
27+
28+
#### LegendCategory
29+
30+
Located in `src/component/legendCategory.ts`, this component handles categorical legends:
31+
32+
- **Data Processing**: Processes domain values from scales to create legend items
33+
- **Marker Inference**: Automatically infers appropriate markers based on the chart's shapes and scales
34+
- **Layout Wrapper**: Uses `LegendCategoryLayout` to handle layout positioning
35+
- **Rendering Options**: Supports both standard GUI rendering and custom HTML rendering via the `render` option
36+
37+
#### LegendContinuous
38+
39+
Located in `src/component/legendContinuous.ts`, this handles continuous legends:
40+
41+
- **Scale Type Handling**: Supports various continuous scale types including linear, log, time, quantize, quantile, and threshold
42+
- **Configuration Inference**: Generates appropriate configuration based on scale properties
43+
- **Ribbon Generation**: Creates color ribbons for continuous scales
44+
- **Label Formatting**: Handles proper formatting of continuous scale labels
45+
46+
### 3. Layout and Sizing Calculation
47+
48+
The layout process is managed in `src/runtime/layout.ts` and involves several critical steps:
49+
50+
#### Component Size Computation
51+
52+
The `computeComponentSize` function handles sizing based on component type:
53+
54+
- **Auto vs Manual Padding**: When padding is set to 'auto', the system dynamically calculates component size based on content. When manually set, it uses default sizes.
55+
- **Size Calculation**: Different calculation strategies for different component types (axis, legend, title, etc.)
56+
- **Cross-size Considerations**: Takes into account cross-size dimensions for proper layout
57+
58+
#### Padding Calculation Logic
59+
60+
The core layout logic is in the `computePadding` function:
61+
62+
```typescript
63+
const autoSizeOf = (d) => {
64+
if (d.size) return;
65+
if (value !== 'auto') sizeOf(d);
66+
else {
67+
// Compute component size dynamically
68+
computeComponentSize(
69+
d,
70+
crossSize,
71+
crossPadding,
72+
position,
73+
theme,
74+
library,
75+
);
76+
defaultSizeOf(d);
77+
}
78+
};
79+
```
80+
81+
- **Auto Padding ('auto')**: Calls `computeComponentSize` to measure actual content dimensions
82+
- **Manual Padding**: Uses `defaultSize` directly, bypassing content measurement
83+
84+
### 4. Component Rendering
85+
86+
The `renderComponent` function in `src/runtime/component.ts` handles the actual rendering:
87+
88+
- **Component Instantiation**: Creates the appropriate component based on type
89+
- **Context Injection**: Provides scale instances, theme, and other context information
90+
- **Display Object Creation**: Returns a DisplayObject that can be added to the chart
91+
92+
### 5. Legend Positioning and BBox Assignment
93+
94+
After size calculation, the `placeComponents` function assigns bounding boxes:
95+
96+
- **Position-based Grouping**: Components are grouped by position (top, bottom, left, right, center)
97+
- **Area Calculation**: Each position gets a specific area in the chart layout
98+
- **Dimension Assignment**: Components receive their final x, y, width, and height values
99+
100+
## Legend Types and Configuration
101+
102+
### Categorical Legends
103+
104+
Categorical legends (LegendCategory) are used for discrete scales and support:
105+
106+
- **Item Markers**: Automatic marker generation based on chart types
107+
- **Flexible Layouts**: Supports both flex and grid layouts
108+
- **Custom Rendering**: HTML-based custom rendering via the `render` option
109+
- **Interaction Features**: Support for filtering and focus modes
110+
111+
### Continuous Legends
112+
113+
Continuous legends (LegendContinuous) handle continuous scales:
114+
115+
- **Color Ranges**: Support for various color interpolation methods
116+
- **Scale Types**: Handles linear, log, time, and quantile scales
117+
- **Threshold Handling**: Special support for threshold scales with custom boundaries
118+
- **Size Control**: Configurable ribbon sizes and label spacing
119+
120+
## Interaction Handling
121+
122+
### Legend Filtering
123+
124+
The legend filtering interaction is implemented in `src/interaction/legendFilter.ts`:
125+
126+
- **Event Delegation**: Handles click, pointerenter, and pointerout events
127+
- **State Management**: Maintains selection states and visual feedback
128+
- **Filter Propagation**: Updates chart data based on legend selections
129+
- **Cross-chart Coordination**: Supports filtering across multiple chart views
130+
131+
### Visual States
132+
133+
Legends support multiple visual states:
134+
135+
- **Selected/Unselected**: Visual distinction between selected and unselected items
136+
- **Hover States**: Pointer interaction feedback
137+
- **Focus Mode**: Special highlighting for focused items
138+
139+
## Customization and Theming
140+
141+
### Theme Integration
142+
143+
Legend components integrate with G2's theming system:
144+
145+
- **Theme Properties**: Respects theme settings for colors, fonts, and spacing
146+
- **Component-specific Themes**: Separate theme settings for different legend types
147+
- **Style Override**: Allows specific style overrides via component options
148+
149+
### Layout Customization
150+
151+
- **Position Control**: Configurable positions (top, bottom, left, right, center)
152+
- **Size Control**: Manual or automatic size calculation
153+
- **Padding Control**: Configurable padding and spacing
154+
- **Orientation Control**: Horizontal or vertical orientation options
155+
156+
## Best Practices
157+
158+
### Performance Considerations
159+
160+
- **Efficient Data Processing**: Minimize legend data processing for large datasets
161+
- **Optimized Rendering**: Use appropriate rendering strategies for different legend types
162+
- **Event Handling**: Properly manage and clean up event listeners
163+
164+
### Accessibility
165+
166+
- **Label Clarity**: Ensure legend labels are clear and descriptive
167+
- **Color Contrast**: Maintain sufficient contrast for color accessibility
168+
- **Interaction Feedback**: Provide clear visual feedback for interactions
169+
170+
## Troubleshooting Common Issues
171+
172+
### Layout Issues
173+
174+
When `paddingTop` is manually set (e.g., `paddingTop: 72`), the height of `legendCategory` changes unexpectedly (e.g., from 60px to 40px). This occurs because:
175+
176+
1. When padding is manually set, the layout engine uses the component's `defaultSize` instead of measuring actual content
177+
2. For `LegendCategory`, the default size is 40 (defined in `src/component/legendCategory.ts`)
178+
3. To fix this, explicitly set the `size` property in the legend configuration to override the default
179+
180+
### Interaction Issues
181+
182+
- **Event Bubbling**: Ensure proper event handling to prevent unwanted interactions
183+
- **State Synchronization**: Keep legend states synchronized across different chart views
184+
- **Performance**: Use throttling for filter events to prevent excessive updates
185+
186+
## References
187+
188+
For more detailed information on legend layout mechanisms and related topics, see the knowledge directory:
189+
190+
- **Legend Layout Mechanisms**: `./knowledge/legendLayout.md` - Detailed analysis of padding behavior and layout calculations
191+
- Manual Padding Effects: How manually set padding values affect legend sizing
192+
- Default Size Fallback: Behavior when padding is set manually vs. 'auto'
193+
- Layout Calculation Logic: The core algorithm in `computePadding` function
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Legend Layout Mechanism with Manual Padding
2+
3+
## Problem Description
4+
When `paddingTop` is manually set (e.g., `paddingTop: 72`), the height of `legendCategory` changes unexpectedly (e.g., from 60px to 40px).
5+
6+
## Cause Analysis
7+
8+
### 1. Layout Calculation Logic
9+
G2's layout engine (located in `src/runtime/layout.ts`) adopts different strategies when calculating component dimensions based on the `padding` configuration. The core logic resides in the `computePadding` function:
10+
11+
```typescript
12+
// src/runtime/layout.ts
13+
14+
const autoSizeOf = (d) => {
15+
if (d.size) return; // If component already has a size, return immediately
16+
17+
// Key logic: If padding is not 'auto' (i.e., manually set value), use default size
18+
if (value !== 'auto') sizeOf(d);
19+
else {
20+
// If padding is 'auto', dynamically calculate component size
21+
computeComponentSize(
22+
d,
23+
crossSize,
24+
crossPadding,
25+
position,
26+
theme,
27+
library,
28+
);
29+
defaultSizeOf(d);
30+
}
31+
};
32+
```
33+
34+
- **`value === 'auto'` (Default)**: Calls `computeComponentSize`, which measures the actual content of the component (text, icons, etc.) to calculate the precise required height (e.g., 60px).
35+
- **`value !== 'auto'` (Manual setting, e.g., 72)**: Calls `sizeOf(d)`.
36+
37+
### 2. Fallback Mechanism (`sizeOf`)
38+
When entering the `sizeOf` branch, the code directly uses the component's `defaultSize`:
39+
40+
```typescript
41+
// src/runtime/layout.ts
42+
43+
const sizeOf = (d) => {
44+
if (d.type === 'group') {
45+
// ...handle grouping...
46+
} else {
47+
d.size = d.defaultSize; // <--- Directly assign defaultSize
48+
}
49+
};
50+
```
51+
52+
In `src/component/legendCategory.ts`, the default size of `LegendCategory` is defined as **40**:
53+
54+
```typescript
55+
// src/component/legendCategory.ts
56+
LegendCategory.props = {
57+
defaultPosition: 'top',
58+
// ...
59+
defaultSize: 40, // <--- Default height
60+
// ...
61+
};
62+
```
63+
64+
Therefore, when you set `paddingTop: 72`, the layout engine assumes you have taken over space allocation. To avoid redundant calculations or conflicts, it no longer measures the actual content height of the legend but directly falls back to the default value of 40px.
65+
66+
## Solution
67+
68+
If you need to fix `paddingTop` but also want to maintain a specific legend height (e.g., 60px), you must explicitly set the `size` property in the legend configuration. The explicitly set `size` has the highest priority and will be adopted immediately in the first line of `autoSizeOf`: `if (d.size) return;`.
69+
70+
```javascript
71+
chart.options({
72+
// ...
73+
paddingTop: 72, // 1. Manually set padding
74+
legend: {
75+
color: {
76+
size: 60, // 2. Explicitly set height to prevent fallback to default value (40)
77+
// ...
78+
},
79+
},
80+
});
81+
```

0 commit comments

Comments
 (0)