feat(theming): land Ant Design v5 overhaul — dynamic themes, real dark mode + massive styling refactor (#31590)

Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com>
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
Co-authored-by: Geido <60598000+geido@users.noreply.github.com>
Co-authored-by: Alexandru Soare <37236580+alexandrusoare@users.noreply.github.com>
Co-authored-by: Damian Pendrak <dpendrak@gmail.com>
Co-authored-by: Pius Iniobong <67148161+payose@users.noreply.github.com>
Co-authored-by: Enzo Martellucci <enzomartellucci@gmail.com>
Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
This commit is contained in:
Maxime Beauchemin
2025-06-20 13:38:58 -07:00
committed by GitHub
parent 2cc1ef88c8
commit dd129fa403
1267 changed files with 32958 additions and 23592 deletions

View File

@@ -16,9 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { PureComponent } from 'react';
import cloudLayout, { Word } from 'd3-cloud';
import cloudLayout from 'd3-cloud';
import {
PlainObject,
createEncoderFactory,
@@ -26,7 +25,7 @@ import {
Encoder,
} from 'encodable';
import {
SupersetThemeProps,
SupersetTheme,
withTheme,
seed,
CategoricalColorNamespace,
@@ -34,9 +33,20 @@ import {
import { isEqual } from 'lodash';
const seedRandom = seed('superset-ui');
// Polyfill Word type since it's not exported from 'd3-cloud'
export type Word = {
text: string;
size: number;
x?: number;
y?: number;
rotate?: number;
font?: string;
weight?: string | number;
};
export const ROTATION = {
flat: () => 0,
// this calculates a random rotation between -90 and 90 degrees.
random: () => Math.floor(seedRandom() * 6 - 3) * 30,
square: () => Math.floor(seedRandom() * 2) * 90,
};
@@ -53,9 +63,6 @@ type WordCloudEncodingConfig = {
text: ['Text', string];
};
/**
* These props should be stored when saving the chart.
*/
export interface WordCloudVisualProps {
encoding?: Partial<WordCloudEncoding>;
rotation?: RotationType;
@@ -80,20 +87,14 @@ const defaultProps: Required<WordCloudVisualProps> = {
};
type FullWordCloudProps = WordCloudProps &
typeof defaultProps &
SupersetThemeProps;
typeof defaultProps & { theme: SupersetTheme };
const SCALE_FACTOR_STEP = 0.5;
const MAX_SCALE_FACTOR = 3;
// Percentage of top results that will always be displayed.
// Needed to avoid clutter when shrinking a chart with many records.
const TOP_RESULTS_PERCENTAGE = 0.1;
class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
static defaultProps = defaultProps;
// Cannot name it isMounted because of conflict
// with React's component function name
isComponentMounted = false;
wordCloudEncoderFactory = createEncoderFactory<WordCloudEncodingConfig>({
@@ -106,18 +107,22 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
},
defaultEncoding: {
color: { value: this.props.theme.colors.grayscale.dark2 },
fontFamily: { value: this.props.theme.typography.families.sansSerif },
fontFamily: { value: this.props.theme.fontFamily },
fontSize: { value: 20 },
fontWeight: { value: 'bold' },
text: { value: '' },
},
});
createEncoder = (encoding?: Partial<WordCloudEncoding>) => {
const selector = this.wordCloudEncoderFactory.createSelector();
createEncoder = (
encoding?: Partial<WordCloudEncoding>,
): Encoder<WordCloudEncodingConfig> => {
const selector: (
e: Partial<WordCloudEncoding>,
) => Encoder<WordCloudEncodingConfig> =
this.wordCloudEncoderFactory.createSelector();
// @ts-ignore
return selector(encoding as any);
return selector(encoding ?? {});
};
constructor(props: FullWordCloudProps) {
@@ -136,7 +141,6 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
componentDidUpdate(prevProps: WordCloudProps) {
const { data, encoding, width, height, rotation } = this.props;
if (
!isEqual(prevProps.data, data) ||
!isEqual(prevProps.encoding, encoding) ||
@@ -161,8 +165,7 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
update() {
const { data, encoding } = this.props;
const encoder: Encoder<WordCloudEncodingConfig> =
this.createEncoder(encoding);
const encoder = this.createEncoder(encoding);
encoder.setDomainFromDataset(data);
const sortedData = [...data].sort(
@@ -176,7 +179,6 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
);
const topResults = sortedData.slice(0, topResultsCount);
// Ensure top results are always included in the final word cloud by scaling chart down if needed
this.generateCloud(encoder, 1, (words: Word[]) =>
topResults.every((d: PlainObject) =>
words.find(
@@ -195,16 +197,12 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
cloudLayout()
.size([width * scaleFactor, height * scaleFactor])
// clone the data because cloudLayout mutates input
.words(data.map(d => ({ ...d })))
.words(data.map((d: Word) => ({ ...d })))
.padding(5)
.rotate(ROTATION[rotation] || ROTATION.flat)
.text((d: PlainObject) => encoder.channels.text.getValueFromDatum(d))
.font((d: PlainObject) =>
encoder.channels.fontFamily.encodeDatum(
d,
this.props.theme.typography.families.sansSerif,
),
encoder.channels.fontFamily.encodeDatum(d, this.props.theme.fontFamily),
)
.fontWeight((d: PlainObject) =>
encoder.channels.fontWeight.encodeDatum(d, 'normal'),
@@ -212,9 +210,7 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
.fontSize((d: PlainObject) => encoder.channels.fontSize.encodeDatum(d, 0))
.on('end', (words: Word[]) => {
if (isValid(words) || scaleFactor > MAX_SCALE_FACTOR) {
if (this.isComponentMounted) {
this.setState({ words, scaleFactor });
}
this.setWords(words);
} else {
this.generateCloud(encoder, scaleFactor + SCALE_FACTOR_STEP, isValid);
}
@@ -223,17 +219,13 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
}
render() {
const { scaleFactor } = this.state;
const { scaleFactor, words } = this.state;
const { width, height, encoding, sliceId, colorScheme } = this.props;
const { words } = this.state;
// @ts-ignore
const encoder = this.createEncoder(encoding);
encoder.channels.color.setDomainFromDataset(words);
const { getValueFromDatum } = encoder.channels.color;
const colorFn = CategoricalColorNamespace.getScale(colorScheme);
const viewBoxWidth = width * scaleFactor;
const viewBoxHeight = height * scaleFactor;
@@ -241,9 +233,7 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
<svg
width={width}
height={height}
viewBox={`-${viewBoxWidth / 2} -${
viewBoxHeight / 2
} ${viewBoxWidth} ${viewBoxHeight}`}
viewBox={`-${viewBoxWidth / 2} -${viewBoxHeight / 2} ${viewBoxWidth} ${viewBoxHeight}`}
>
<g>
{words.map(w => (
@@ -252,7 +242,10 @@ class WordCloud extends PureComponent<FullWordCloudProps, WordCloudState> {
fontSize={`${w.size}px`}
fontWeight={w.weight}
fontFamily={w.font}
fill={colorFn(getValueFromDatum(w) as string, sliceId)}
fill={colorFn(
encoder.channels.color.getValueFromDatum(w) as string,
sliceId,
)}
textAnchor="middle"
transform={`translate(${w.x}, ${w.y}) rotate(${w.rotate})`}
>