diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f7c56..0b64b2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.1.0 + +### Features + +- If `count` is set to a decimal number like 3.5, the component will display 3 + full-width skeletons followed by 1 half-width skeleton. (#136) + ## 3.0.3 ### Bug Fixes diff --git a/README.md b/README.md index 547266e..5ebd815 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,12 @@ return ( count?: number - The number of lines of skeletons to render. + + The number of lines of skeletons to render. If + count is a decimal number like 3.5, + three full skeletons and one half-width skeleton will be + rendered. + 1 diff --git a/package.json b/package.json index 787a472..6cdfabe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-loading-skeleton", - "version": "3.0.3", + "version": "3.1.0", "description": "Make beautiful, animated loading skeletons that automatically adapt to your app.", "keywords": [ "react", diff --git a/src/Skeleton.tsx b/src/Skeleton.tsx index 73cea5d..7512fe7 100644 --- a/src/Skeleton.tsx +++ b/src/Skeleton.tsx @@ -96,11 +96,33 @@ export function Skeleton({ const elements: ReactElement[] = [] - // Without the
, the skeleton lines will all run together if - // `width` is specified - for (let i = 0; i < count; i++) { + const countCeil = Math.ceil(count) + + for (let i = 0; i < countCeil; i++) { + let thisStyle = style + + if (countCeil > count && i === countCeil - 1) { + // count is not an integer and we've reached the last iteration of + // the loop, so add a "fractional" skeleton. + // + // For example, if count is 3.5, we've already added 3 full + // skeletons, so now we add one more skeleton that is 0.5 times the + // original width. + + const width = thisStyle.width ?? '100%' // 100% is the default since that's what's in the CSS + + const fractionalPart = count % 1 + + const fractionalWidth = + typeof width === 'number' + ? width * fractionalPart + : `calc(${width} * ${fractionalPart})` + + thisStyle = { ...thisStyle, width: fractionalWidth } + } + const skeletonSpan = ( - + ) @@ -108,6 +130,8 @@ export function Skeleton({ if (inline) { elements.push(skeletonSpan) } else { + // Without the
, the skeleton lines will all run together if + // `width` is specified elements.push( {skeletonSpan} diff --git a/src/__stories__/Post.stories.tsx b/src/__stories__/Post.stories.tsx index 935651a..e12289b 100644 --- a/src/__stories__/Post.stories.tsx +++ b/src/__stories__/Post.stories.tsx @@ -7,14 +7,14 @@ export default { title: 'Post', } as Meta -export const Default: React.VFC = () => ( +export const Default: React.FC = () => ( ) -export const Large: React.VFC = () => ( +export const Large: React.FC = () => ( diff --git a/src/__stories__/Skeleton.stories.tsx b/src/__stories__/Skeleton.stories.tsx index ca74791..791344a 100644 --- a/src/__stories__/Skeleton.stories.tsx +++ b/src/__stories__/Skeleton.stories.tsx @@ -16,9 +16,9 @@ export default { title: 'Skeleton', } as Meta -export const Basic: React.VFC = () => +export const Basic: React.FC = () => -export const Inline: React.VFC = () => ( +export const Inline: React.FC = () => (
@@ -30,13 +30,13 @@ export const Inline: React.VFC = () => ( ) -export const InlineWithText: React.VFC = () => ( +export const InlineWithText: React.FC = () => (
Some random text Some more random text
) -export const BlockWrapper: React.VFC = () => ( +export const BlockWrapper: React.FC = () => (
@@ -52,7 +52,7 @@ function InlineWrapperWithMargin({ children }: PropsWithChildren): Reac return {children} } -export const InlineWrapper: React.VFC = () => ( +export const InlineWrapper: React.FC = () => (
@@ -77,7 +77,7 @@ export const InlineWrapper: React.VFC = () => (
) -export const DifferentDurations: React.VFC = () => ( +export const DifferentDurations: React.FC = () => (
@@ -86,7 +86,7 @@ export const DifferentDurations: React.VFC = () => (
) -export const DifferentWidths: React.VFC = () => ( +export const DifferentWidths: React.FC = () => (
@@ -96,7 +96,7 @@ export const DifferentWidths: React.VFC = () => (
) -export const DifferentHeights: React.VFC = () => ( +export const DifferentHeights: React.FC = () => (
@@ -106,11 +106,21 @@ export const DifferentHeights: React.VFC = () => (
) -export const CustomStyles: React.VFC = () => ( +export const CustomStyles: React.FC = () => ( ) -export const Circle: React.VFC = () => +export const Circle: React.FC = () => + +export const DecimalCount: React.FC = () => + +export const DecimalCountPercentWidth: React.FC = () => ( + +) + +export const DecimalCountInline: React.FC = () => ( + +) // Use https://bennettfeely.com/clippy/ to try out other shapes const StarWrapper: React.FC> = ({ children }) => ( @@ -127,7 +137,7 @@ const StarWrapper: React.FC> = ({ children }) => (
) -export const Stars: React.VFC = () => ( +export const Stars: React.FC = () => ( ( /> ) -export const RightToLeft: React.VFC = () => +export const RightToLeft: React.FC = () => -export const DisableAnimation: React.VFC = () => { +export const DisableAnimation: React.FC = () => { const [enabled, setEnabled] = useState(true) return ( @@ -158,7 +168,7 @@ export const DisableAnimation: React.VFC = () => { ) } -export const PercentWidthInFlex: React.VFC = () => ( +export const PercentWidthInFlex: React.FC = () => (

This is a test for{' '} @@ -179,7 +189,7 @@ export const PercentWidthInFlex: React.VFC = () => (

) -export const FillEntireContainer: React.VFC = () => ( +export const FillEntireContainer: React.FC = () => (

This is a test for{' '} @@ -231,7 +241,7 @@ function HeightComparison({ ) } -export const HeightQuirk: React.VFC = () => ( +export const HeightQuirk: React.FC = () => (

This is a demonstration of a Skeleton quirk that was reported in{' '} @@ -287,7 +297,7 @@ export const HeightQuirk: React.VFC = () => (

) -export const ShadowDOM: React.VFC = () => { +export const ShadowDOM: React.FC = () => { const hostRef = useRef(null) const [portalDestination, setPortalDestination] = useState() diff --git a/src/__stories__/SkeletonTheme.stories.tsx b/src/__stories__/SkeletonTheme.stories.tsx index a0a8499..292cc21 100644 --- a/src/__stories__/SkeletonTheme.stories.tsx +++ b/src/__stories__/SkeletonTheme.stories.tsx @@ -16,7 +16,7 @@ const blueHighlightColor = '#5294e0' const lightBaseColor = '#c0c0c0' const lightHighlightColor = '#A0A0A0' -export const WithColors: React.VFC = () => ( +export const WithColors: React.FC = () => (
@@ -27,7 +27,7 @@ export const WithColors: React.VFC = () => (
) -export const NoBorderRadius: React.VFC = () => ( +export const NoBorderRadius: React.FC = () => ( ( ) -export const LightAndDarkThemes: React.VFC = () => { +export const LightAndDarkThemes: React.FC = () => { const [theme, setTheme] = React.useState<'light' | 'dark'>('light') const handleToggle = () => { @@ -74,7 +74,7 @@ export const LightAndDarkThemes: React.VFC = () => { ) } -export const PropsExplicitlySetToUndefined: React.VFC = () => ( +export const PropsExplicitlySetToUndefined: React.FC = () => (

This is a test for{' '} diff --git a/src/__tests__/Skeleton.test.tsx b/src/__tests__/Skeleton.test.tsx index 303480a..bb85775 100644 --- a/src/__tests__/Skeleton.test.tsx +++ b/src/__tests__/Skeleton.test.tsx @@ -118,3 +118,23 @@ it('renders a skeleton with a wrapper', () => { expect(box.querySelector(skeletonSelector)).toBeVisible() }) + +it('renders a half-width skeleton when count = 1.5', () => { + render() + + const skeletons = getAllSkeletons() + expect(skeletons).toHaveLength(2) + + expect(skeletons[0]).toHaveStyle({ width: '' }) + expect(skeletons[1]).toHaveStyle({ width: 'calc(100% * 0.5)' }) +}) + +it('renders a 3/4-width skeleton when count = 1.75 and width is set in pixels', () => { + render() + + const skeletons = getAllSkeletons() + expect(skeletons).toHaveLength(2) + + expect(skeletons[0]).toHaveStyle({ width: '100px' }) + expect(skeletons[1]).toHaveStyle({ width: '75px' }) +})