Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/react-wordcloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import ReactWordcloudSrc, { Props } from '..';
export * from '..';

export default function ReactWordcloud(props: Props): JSX.Element {
const canvasAllowed = typeof document !== 'undefined' && document.createElement('canvas').getContext('2d').getImageData(0, 0, 1, 1).data.every(v => v === 0);
const canvasAllowed =
typeof document !== 'undefined' &&
document
.createElement('canvas')
.getContext('2d')
.getImageData(0, 0, 1, 1)
.data.every(v => v === 0);

if (!canvasAllowed) {
return (
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
"@types/d3-selection": "^1.4.1",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.debounce": "^4.0.6",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/seedrandom": "^2.4.28",
"docz": "^2.3.1",
"eslint-config-xo-react": "^0.23.0",
Expand All @@ -60,7 +62,7 @@
"microbundle": "^0.12.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"typescript": "^3.8.3",
"typescript": "^5.0.0",
"xo": "^0.32.1"
},
"peerDependencies": {
Expand Down
10 changes: 6 additions & 4 deletions src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export function useResponsiveSvgSelection(minSize, initialSize, svgAttributes) {

let width = 0;
let height = 0;
if (initialSize === undefined) {
if (initialSize === undefined && element) {
// Use parentNode size if resized has not occurred
width = element.parentElement.offsetWidth;
height = element.parentElement.offsetHeight;
} else {
// @ts-ignore - TypeScript incorrectly infers element as never
width = element.parentElement?.offsetWidth || 0;
// @ts-ignore - TypeScript incorrectly infers element as never
height = element.parentElement?.offsetHeight || 0;
} else if (initialSize) {
// Use initialSize if it is provided
[width, height] = initialSize;
}
Expand Down
35 changes: 21 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import debounce from 'lodash.debounce';
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useMemo, useRef } from 'react';

import { useResponsiveSvgSelection } from './hooks';
import { layout } from './layout';
Expand All @@ -9,8 +9,10 @@ export const defaultCallbacks = {
getWordTooltip: ({ text, value }) => `${text} (${value})`,
};

const defaultColors = getDefaultColors();

export const defaultOptions = {
colors: getDefaultColors(),
colors: defaultColors,
deterministic: false,
enableOptimizations: false,
enableTooltip: true,
Expand All @@ -26,19 +28,27 @@ export const defaultOptions = {
transitionDuration: 600,
};

const DEFAULT_MIN_SIZE = [300, 300];

function ReactWordCloud({
callbacks,
callbacks = defaultCallbacks,
maxWords = 100,
minSize,
options,
minSize: minSizeProp,
options = defaultOptions,
size: initialSize,
words,
...rest
}) {
const minSize = useMemo(() => minSizeProp || DEFAULT_MIN_SIZE, [minSizeProp]);

const svgAttributes = useMemo(() => options.svgAttributes, [
options.svgAttributes,
]);

const [ref, selection, size] = useResponsiveSvgSelection(
minSize,
initialSize,
options.svgAttributes,
svgAttributes,
);

const render = useRef(debounce(layout, 100));
Expand All @@ -57,16 +67,13 @@ function ReactWordCloud({
words,
});
}
}, [maxWords, callbacks, options, selection, size, words]);
// Note: 'size' is intentionally excluded from dependencies to prevent
// potential infinite loops, as it's updated by useResponsiveSvgSelection.
// The debounced render function will use the latest size value via closure.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [callbacks, maxWords, options, selection, words]);

return <div ref={ref} style={{ height: '100%', width: '100%' }} {...rest} />;
}

ReactWordCloud.defaultProps = {
callbacks: defaultCallbacks,
maxWords: 100,
minSize: [300, 300],
options: defaultOptions,
};

export default ReactWordCloud;
2 changes: 1 addition & 1 deletion src/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function render({ callbacks, options, random, selection, words }) {
animation: 'scale',
arrow: true,
content: () => getWordTooltip(word),
onHidden: (instance) => {
onHidden: instance => {
instance.destroy();
tooltipInstance = null;
},
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"esModuleInterop": true,
"jsx": "react",
"noEmit": true,
"skipLibCheck": true,
"strict": false
},
"exclude": ["**/dist", "**/node_modules"]
}