mirror of
https://github.com/apache/superset.git
synced 2026-04-23 18:14:56 +00:00
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:
committed by
GitHub
parent
2cc1ef88c8
commit
dd129fa403
@@ -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})`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user