react-spectrum
Version:
Generate colorful text placeholders 🎨
1 lines • 12.6 kB
Source Map (JSON)
{"version":3,"file":"react-spectrum.cjs","sources":["../src/utils/arraysEqual.ts","../src/utils/getRandomInRange.ts","../src/utils/getWords.ts","../src/DrawLine.tsx","../src/usePrevious.tsx","../src/Spectrum.tsx"],"sourcesContent":["function arraysEqual(\n a: Array<string | number>,\n b: Array<string | number>,\n): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (a.length !== b.length) return false;\n\n const sortedA = [...a].sort();\n const sortedB = [...b].sort();\n for (let i = 0; i < sortedA.length; i += 1) {\n if (sortedA[i] !== sortedB[i]) return false;\n }\n\n return true;\n}\n\nexport default arraysEqual;\n","function getRandomInRange(min: number, max: number): number {\n const mn = Math.ceil(min);\n const mx = Math.floor(max);\n\n return Math.floor(Math.random() * (mx - mn + 1)) + mn;\n}\n\nexport default getRandomInRange;\n","import getRandomInRange from './getRandomInRange';\n\nconst getWords = ({\n width,\n colors,\n wordWidths,\n wordDistances,\n truncate,\n}: {\n width: number;\n colors: Array<string>;\n wordWidths: Array<number>;\n wordDistances: Array<number>;\n truncate: boolean;\n}): Array<{ width: number; distance: number; background: string }> => {\n const minWordWidth = Math.min(...wordWidths);\n const maxWordsPerLine = Math.floor(width / minWordWidth);\n const wordForLine = getRandomInRange(\n Math.max(\n 0,\n truncate ? 1 : maxWordsPerLine - Math.floor(maxWordsPerLine / 2),\n ),\n truncate ? Math.floor(maxWordsPerLine / 2) : maxWordsPerLine,\n );\n\n const words = [];\n let totalWidth = 0;\n for (let i = 0; i < wordForLine; i += 1) {\n const wordWidth = wordWidths[getRandomInRange(0, wordWidths.length - 1)];\n let wordDistance =\n wordDistances[getRandomInRange(0, wordDistances.length - 1)];\n\n const totalWidthWithoutDistance = totalWidth + wordWidth;\n totalWidth = totalWidthWithoutDistance + wordDistance;\n\n // If we can just fit the word without margin, push it by resetting margin\n const fitWord = totalWidthWithoutDistance <= width && totalWidth > width;\n const fitWordWithDistance = totalWidth <= width;\n\n if (fitWord || fitWordWithDistance) {\n // If we are fitting the word, assume that it's the last word\n // so, reset it's distance\n if (fitWord && !fitWordWithDistance) {\n wordDistance = 0;\n }\n\n const background = colors[getRandomInRange(0, colors.length - 1)];\n words.push({ width: wordWidth, distance: wordDistance, background });\n\n // If we are fitting the word, it's the last word\n if (fitWord && !fitWordWithDistance) {\n break;\n }\n } else {\n // If we cannot fit the word into the line\n // clear the margin of the last word\n const [last] = [...words].reverse();\n if (last) {\n last.distance = 0;\n }\n break;\n }\n }\n\n return words;\n};\n\nexport default getWords;\n","import * as React from 'react';\nimport arraysEqual from './utils/arraysEqual';\nimport getWords from './utils/getWords';\nimport usePrevious from './usePrevious';\nimport { RenderWordProps } from './Spectrum';\n\ninterface DrawLineProps {\n width: number;\n colors: Array<string>;\n wordWidths: Array<number>;\n wordDistances: Array<number>;\n truncate: boolean;\n wordHeight: number;\n wordRadius: number;\n lineDistance: number;\n renderWord: React.FC<RenderWordProps>;\n}\n\nconst DrawLine = ({\n width,\n colors,\n wordWidths,\n wordDistances,\n wordHeight,\n wordRadius,\n lineDistance,\n truncate,\n renderWord,\n}: DrawLineProps): React.ReactElement => {\n const previous = usePrevious({\n width,\n colors,\n wordWidths,\n wordDistances,\n wordHeight,\n wordRadius,\n lineDistance,\n truncate,\n renderWord,\n });\n const [words, setWords] = React.useState(() =>\n getWords({\n width,\n colors,\n wordWidths,\n wordDistances,\n truncate,\n }),\n );\n\n React.useEffect(() => {\n // Handle props update on the runtime\n // Memo isn't helping here as props contains array\n // whose reference might change on every re-render\n if (\n previous &&\n (previous.width !== width ||\n previous.wordHeight !== wordHeight ||\n previous.wordRadius !== wordRadius ||\n previous.lineDistance !== lineDistance ||\n previous.truncate !== truncate ||\n !arraysEqual(previous.wordWidths, wordWidths) ||\n !arraysEqual(previous.wordDistances, wordDistances) ||\n !arraysEqual(previous.colors, colors))\n ) {\n const newWords = getWords({\n width,\n colors,\n wordWidths,\n wordDistances,\n truncate,\n });\n\n setWords(newWords);\n }\n }, [\n previous,\n width,\n colors,\n wordWidths,\n wordDistances,\n truncate,\n wordHeight,\n wordRadius,\n lineDistance,\n ]);\n return (\n <>\n {words.map(({ width: w, distance, background }, i) => {\n const style = {\n width: w,\n marginRight: distance,\n height: wordHeight,\n background,\n display: 'inline-block',\n borderRadius: wordRadius,\n marginBottom: lineDistance,\n };\n\n // Ensure only synchronous results are rendered\n const node = renderWord({ key: i, style });\n if (node instanceof Promise) {\n throw new Error(\"`renderWord` must be synchronous function and not return a Promise.\");\n }\n return node;\n })}\n </>\n );\n};\n\nexport default React.memo(DrawLine);\n","import * as React from 'react';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction usePrevious(value: any): any {\n const ref = React.useRef(null);\n\n React.useEffect(() => {\n ref.current = value;\n });\n\n return ref.current;\n}\n\nexport default usePrevious;\n","import * as React from 'react';\nimport DrawLine from './DrawLine';\n\nexport interface RenderWordProps {\n key: number;\n style: React.CSSProperties;\n}\n\ntype SpectrumProps = {\n /**\n * Width of the placeholder container\n */\n width?: number;\n /**\n * Possible colors of words, will be picked randomly\n */\n colors?: Array<string>;\n /**\n * Possible widths of words, will be picked randomly\n */\n wordWidths?: Array<number>;\n /**\n * Possible distance between words, will be picked randomly\n */\n wordDistances?: Array<number>;\n /**\n * Height of every word placeholder\n */\n wordHeight?: number;\n /**\n * Border radius of every word\n */\n wordRadius?: number;\n /**\n * Distance(margin) between the lines\n */\n lineDistance?: number;\n /**\n * Lines per paragraph\n * if there are multiple paragraphs, all of them will have same number of lines\n */\n linesPerParagraph?: number;\n /**\n * Number of paragraphs in the placeholder\n */\n paragraphs?: number;\n /**\n * Distance(margin) between the paragraphs\n */\n paragraphDistance?: number;\n /**\n * Show less words in the last line for more natural feel\n */\n truncateLastLine?: boolean;\n /**\n * Render word with customizations\n */\n renderWord?: React.FC<RenderWordProps>;\n};\n\nconst Spectrum = ({\n width = 500,\n colors = ['#eee'],\n wordWidths = [30, 60, 90, 120, 150],\n wordDistances = [4, 8, 12],\n wordHeight = 12,\n wordRadius = 20,\n linesPerParagraph = 8,\n lineDistance = 12,\n paragraphs = 1,\n paragraphDistance = 24,\n truncateLastLine = true,\n renderWord = ({ key, style }: RenderWordProps): React.ReactElement => (\n <span key={key} style={style} />\n ),\n}: SpectrumProps): React.ReactElement => {\n return (\n <>\n {new Array(paragraphs).fill(true).map((_, i) => {\n const lines = new Array(linesPerParagraph).fill(true).map((__, j) => (\n <div data-line={j} key={j}>\n <DrawLine\n width={width}\n colors={colors}\n wordWidths={wordWidths}\n wordDistances={wordDistances}\n wordHeight={wordHeight}\n wordRadius={wordRadius}\n lineDistance={lineDistance}\n truncate={truncateLastLine ? j === linesPerParagraph - 1 : false}\n renderWord={renderWord}\n />\n </div>\n ));\n\n return (\n <div\n data-paragraph={i}\n key={i}\n style={{\n marginBottom: paragraphDistance,\n fontSize: 0,\n }}>\n {lines}\n </div>\n );\n })}\n </>\n );\n};\n\nexport default React.memo(Spectrum);\n"],"names":["arraysEqual","a","b","length","sortedA","sort","sortedB","i","getRandomInRange","min","max","mn","Math","ceil","mx","floor","random","getWords","width","colors","wordWidths","wordDistances","truncate","minWordWidth","maxWordsPerLine","wordForLine","words","totalWidth","wordWidth","wordDistance","totalWidthWithoutDistance","fitWord","fitWordWithDistance","last","reverse","distance","background","push","DrawLine$1","React","memo","wordHeight","wordRadius","lineDistance","renderWord","previous","value","ref","useRef","useEffect","current","usePrevious","setWords","useState","newWords","_jsx","jsx","_Fragment","Fragment","children","map","w","node","key","style","marginRight","height","display","borderRadius","marginBottom","Promise","Error","Spectrum$1","linesPerParagraph","paragraphs","paragraphDistance","truncateLastLine","Array","fill","_","lines","__","j","DrawLine","fontSize"],"mappings":"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"}