separate non featured articles on quadrant view
This commit is contained in:
@@ -1,31 +1,43 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Link from '../Link/Link';
|
||||
import Flag from '../Flag/Flag';
|
||||
import { Item as mItem } from '../../model';
|
||||
import './item.scss';
|
||||
type ItemProps = {
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import Link from "../Link/Link";
|
||||
import Flag from "../Flag/Flag";
|
||||
import { Item as mItem } from "../../model";
|
||||
import "./item.scss";
|
||||
|
||||
type Props = {
|
||||
item: mItem;
|
||||
noLeadingBorder?: boolean;
|
||||
active?: boolean;
|
||||
style: React.CSSProperties;
|
||||
greyedOut?: boolean;
|
||||
};
|
||||
|
||||
export default function Item({ item, noLeadingBorder = false, active = false, style = {} }: ItemProps) {
|
||||
return (
|
||||
<Link
|
||||
className={classNames('item', {
|
||||
'item--no-leading-border': noLeadingBorder,
|
||||
'is-active': active,
|
||||
const Item: React.FC<Props> = ({
|
||||
item,
|
||||
noLeadingBorder = false,
|
||||
active = false,
|
||||
style = {},
|
||||
greyedOut = false,
|
||||
}) => (
|
||||
<Link
|
||||
className={classNames("item", {
|
||||
"item--no-leading-border": noLeadingBorder,
|
||||
"is-active": active,
|
||||
})}
|
||||
pageName={`${item.quadrant}/${item.name}`}
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
className={classNames("item__title", {
|
||||
"greyed-out": greyedOut,
|
||||
})}
|
||||
pageName={`${item.quadrant}/${item.name}`}
|
||||
style={style}
|
||||
>
|
||||
<div className='item__title'>
|
||||
{item.title}
|
||||
<Flag item={item} />
|
||||
</div>
|
||||
{item.info && <div className='item__info'>{item.info}</div>}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
{item.title}
|
||||
<Flag item={item} />
|
||||
</div>
|
||||
{item.info && <div className="item__info">{item.info}</div>}
|
||||
</Link>
|
||||
);
|
||||
|
||||
export default Item;
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
&__title {
|
||||
font-size: 16px;
|
||||
color: var(--color-white);
|
||||
|
||||
&.greyed-out {
|
||||
color: var(--color-gray-light-alt);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
import React from 'react';
|
||||
import Item from '../Item/Item';
|
||||
import { Item as mItem } from '../../model';
|
||||
import './item-list.scss';
|
||||
import React from "react";
|
||||
import Item from "../Item/Item";
|
||||
import { Item as mItem } from "../../model";
|
||||
import "./item-list.scss";
|
||||
|
||||
type ItemListProps = {
|
||||
items: mItem[];
|
||||
activeItem?: mItem;
|
||||
noLeadingBorder?: boolean;
|
||||
headerStyle?: React.CSSProperties;
|
||||
itemStyle?: React.CSSProperties[];
|
||||
greyedOut?: boolean;
|
||||
};
|
||||
|
||||
export default function ItemList({ children, items, activeItem, noLeadingBorder, headerStyle = {}, itemStyle = [] }: React.PropsWithChildren<ItemListProps>) {
|
||||
return (
|
||||
<div className='item-list'>
|
||||
<div className='item-list__header' style={headerStyle}>
|
||||
{children}
|
||||
</div>
|
||||
<div className='item-list__list'>
|
||||
{items.map((item, i) => (
|
||||
<Item
|
||||
key={item.name}
|
||||
item={item}
|
||||
noLeadingBorder={noLeadingBorder}
|
||||
active={activeItem !== null && activeItem !== undefined && activeItem.name === item.name}
|
||||
style={itemStyle[i]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
const ItemList: React.FC<ItemListProps> = ({
|
||||
children,
|
||||
items,
|
||||
activeItem,
|
||||
noLeadingBorder,
|
||||
headerStyle = {},
|
||||
itemStyle = [],
|
||||
greyedOut = false,
|
||||
}) => (
|
||||
<div className="item-list">
|
||||
<div className="item-list__header" style={headerStyle}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div className="item-list__list">
|
||||
{items.map((item, i) => (
|
||||
<Item
|
||||
key={item.name}
|
||||
item={item}
|
||||
noLeadingBorder={noLeadingBorder}
|
||||
active={activeItem?.name === item.name}
|
||||
style={itemStyle[i]}
|
||||
greyedOut={greyedOut}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ItemList;
|
||||
|
||||
@@ -1,239 +1,147 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import Badge from '../Badge/Badge';
|
||||
import ItemList from '../ItemList/ItemList';
|
||||
import Link from '../Link/Link';
|
||||
import FooterEnd from '../FooterEnd/FooterEnd';
|
||||
import SetTitle from '../SetTitle';
|
||||
import ItemRevisions from '../ItemRevisions/ItemRevisions';
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import Badge from "../Badge/Badge";
|
||||
import ItemList from "../ItemList/ItemList";
|
||||
import Link from "../Link/Link";
|
||||
import FooterEnd from "../FooterEnd/FooterEnd";
|
||||
import SetTitle from "../SetTitle";
|
||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||
import { useAnimations } from "./useAnimations";
|
||||
import "./item-page.scss";
|
||||
import { translate } from "../../config";
|
||||
import {
|
||||
AnimationStates,
|
||||
createAnimation,
|
||||
createAnimationRunner
|
||||
} from '../../animation';
|
||||
import './item-page.scss';
|
||||
import {translate} from '../../config';
|
||||
import {groupByQuadrants, Item} from '../../model';
|
||||
featuredOnly,
|
||||
nonFeaturedOnly,
|
||||
groupByQuadrants,
|
||||
Item,
|
||||
} from "../../model";
|
||||
|
||||
const getItem = (pageName: string, items: Item[]) => {
|
||||
const [quadrantName, itemName] = pageName.split('/');
|
||||
return items.filter((item) => item.quadrant === quadrantName && item.name === itemName)[0];
|
||||
const [quadrantName, itemName] = pageName.split("/");
|
||||
return items.filter(
|
||||
(item) => item.quadrant === quadrantName && item.name === itemName
|
||||
)[0];
|
||||
};
|
||||
|
||||
const getItemsInRing = (pageName: string, items: Item[]) => {
|
||||
const item = getItem(pageName, items);
|
||||
return groupByQuadrants(items)[item.quadrant][item.ring];
|
||||
const item = getItem(pageName, items);
|
||||
return groupByQuadrants(items)[item.quadrant][item.ring];
|
||||
};
|
||||
|
||||
type PageItemProps = {
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
type Props = {
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
export default function PageItem({pageName, items, leaving, onLeave}: PageItemProps) {
|
||||
const itemsInRing = getItemsInRing(pageName, items);
|
||||
const PageItem: React.FC<Props> = ({ pageName, items, leaving, onLeave }) => {
|
||||
const itemsInRing = getItemsInRing(pageName, items);
|
||||
const featuredItemsInRing = featuredOnly(itemsInRing);
|
||||
const nonFeaturedItemsInRing = nonFeaturedOnly(itemsInRing);
|
||||
|
||||
const animationsIn = {
|
||||
background: createAnimation(
|
||||
{
|
||||
transform: 'translateX(calc((100vw - 1200px) / 2 + 800px))',
|
||||
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
|
||||
},
|
||||
{
|
||||
transition: 'transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)',
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
0
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
{
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
300
|
||||
),
|
||||
text: createAnimation(
|
||||
{
|
||||
transform: 'translateY(-20px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateY(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
600
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
{
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
400 + 100 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(-40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(0px)',
|
||||
opacity: '1',
|
||||
},
|
||||
600 + itemsInRing.length * 100
|
||||
),
|
||||
};
|
||||
const { getAnimationState, getAnimationStates } = useAnimations({
|
||||
itemsInRing,
|
||||
featuredItemsInRing,
|
||||
nonFeaturedItemsInRing,
|
||||
onLeave,
|
||||
leaving,
|
||||
});
|
||||
|
||||
const animationsOut = {
|
||||
background: createAnimation(animationsIn.background.stateB, animationsIn.background.stateA, 300 + itemsInRing.length * 50),
|
||||
navHeader: createAnimation(
|
||||
animationsIn.navHeader.stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
0
|
||||
),
|
||||
text: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transform: 'translateY(20px)',
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
opacity: '0',
|
||||
},
|
||||
0
|
||||
),
|
||||
items: itemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
animationsIn.items[i].stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
100 + 50 * i
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transition: 'opacity 150ms ease-out, transform 300ms ease-out',
|
||||
transform: 'translateX(40px)',
|
||||
opacity: '0',
|
||||
},
|
||||
200 + itemsInRing.length * 50
|
||||
),
|
||||
};
|
||||
const item = getItem(pageName, items);
|
||||
|
||||
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
||||
return leaving ? createAnimationRunner(animationsIn).getState() : {}
|
||||
});
|
||||
|
||||
const [stateLeaving, setStateLeaving] = useState(leaving);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stateLeaving && leaving) {
|
||||
let animationRunner = createAnimationRunner(
|
||||
animationsOut,
|
||||
() => setAnimations(animationRunner.getState),
|
||||
)
|
||||
animationRunner.run();
|
||||
animationRunner.awaitAnimationComplete(onLeave);
|
||||
setStateLeaving(true)
|
||||
}
|
||||
if (stateLeaving && !leaving) {
|
||||
let animationRunner = createAnimationRunner(
|
||||
animationsIn,
|
||||
() => setAnimations(animationRunner.getState),
|
||||
)
|
||||
animationRunner.run();
|
||||
setStateLeaving(false)
|
||||
}
|
||||
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave])
|
||||
|
||||
const getAnimationStates = (name: string) => {
|
||||
if (!animations) {
|
||||
return undefined;
|
||||
}
|
||||
return animations[name];
|
||||
}
|
||||
|
||||
const getAnimationState = (name: string) => {
|
||||
const animations = getAnimationStates(name)
|
||||
if (animations === undefined || animations.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return animations[0]
|
||||
};
|
||||
|
||||
const item = getItem(pageName, items);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SetTitle title={item.title}/>
|
||||
<div className='item-page'>
|
||||
<div className='item-page__nav'>
|
||||
<div className='item-page__nav__inner'>
|
||||
<div className='item-page__header' style={getAnimationState('navHeader')}>
|
||||
<h3 className='headline'>{translate(item.quadrant)}</h3>
|
||||
</div>
|
||||
|
||||
<ItemList items={itemsInRing} activeItem={item} headerStyle={getAnimationState('navHeader')}
|
||||
itemStyle={getAnimationStates('items')}>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<Link className='icon-link' pageName={item.quadrant}>
|
||||
<span className='icon icon--pie icon-link__icon'/>
|
||||
Quadrant Overview
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ItemList>
|
||||
<div className='item-page__footer' style={getAnimationState('footer')}>
|
||||
<FooterEnd modifier='in-sidebar'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='item-page__content' style={getAnimationState('background')}>
|
||||
<div className='item-page__content__inner' style={getAnimationState('text')}>
|
||||
<div className='item-page__header'>
|
||||
<div className='split'>
|
||||
<div className='split__left'>
|
||||
<h1 className='hero-headline hero-headline--inverse'>{item.title}</h1>
|
||||
</div>
|
||||
<div className='split__right'>
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='markdown' dangerouslySetInnerHTML={{__html: item.body}}/>
|
||||
{item.revisions.length > 1 && <ItemRevisions revisions={item.revisions.slice(1)}/>}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<SetTitle title={item.title} />
|
||||
<div className="item-page">
|
||||
<div className="item-page__nav">
|
||||
<div className="item-page__nav__inner">
|
||||
<div
|
||||
className="item-page__header"
|
||||
style={getAnimationState("navHeader")}
|
||||
>
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
</div>
|
||||
<ItemList
|
||||
items={featuredItemsInRing}
|
||||
activeItem={item}
|
||||
headerStyle={getAnimationState("navHeader")}
|
||||
itemStyle={getAnimationStates("featuredItems")}
|
||||
>
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="split__right">
|
||||
<Link className="icon-link" pageName={item.quadrant}>
|
||||
<span className="icon icon--pie icon-link__icon" />
|
||||
Quadrant Overview
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</ItemList>
|
||||
|
||||
{nonFeaturedItemsInRing.length > 0 && (
|
||||
<div className="item-page__non-featured-items">
|
||||
<ItemList
|
||||
items={nonFeaturedItemsInRing}
|
||||
activeItem={item}
|
||||
headerStyle={getAnimationState("nonFeaturedItems")}
|
||||
itemStyle={getAnimationStates("nonFeaturedItems")}
|
||||
greyedOut={true}
|
||||
>
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h4 className="headline">Not featured anymore</h4>
|
||||
</div>
|
||||
</div>
|
||||
</ItemList>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="item-page__footer"
|
||||
style={getAnimationState("footer")}
|
||||
>
|
||||
<FooterEnd modifier="in-sidebar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div
|
||||
className="item-page__content"
|
||||
style={getAnimationState("background")}
|
||||
>
|
||||
<div
|
||||
className="item-page__content__inner"
|
||||
style={getAnimationState("text")}
|
||||
>
|
||||
<div className="item-page__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h1 className="hero-headline hero-headline--inverse">
|
||||
{item.title}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="split__right">
|
||||
<Badge big type={item.ring}>
|
||||
{item.ring}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{ __html: item.body }}
|
||||
/>
|
||||
{item.revisions.length > 1 && (
|
||||
<ItemRevisions revisions={item.revisions.slice(1)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageItem;
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
padding: 0 10px 0 100px;
|
||||
}
|
||||
}
|
||||
|
||||
&__non-featured-items {
|
||||
margin-top: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-item-page {
|
||||
|
||||
217
src/components/PageItem/useAnimations.tsx
Normal file
217
src/components/PageItem/useAnimations.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import {
|
||||
Animation,
|
||||
AnimationConfig as AbstractAnimationConfig,
|
||||
AnimationStates,
|
||||
createAnimation,
|
||||
createAnimationRunner,
|
||||
} from "../../animation";
|
||||
import { Item } from "../../model";
|
||||
|
||||
interface Props {
|
||||
itemsInRing: Item[];
|
||||
featuredItemsInRing: Item[];
|
||||
nonFeaturedItemsInRing: Item[];
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
}
|
||||
|
||||
export const useAnimations = ({
|
||||
itemsInRing,
|
||||
featuredItemsInRing,
|
||||
nonFeaturedItemsInRing,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: Props) => {
|
||||
interface AnimationConfig extends AbstractAnimationConfig {
|
||||
background: Animation;
|
||||
navHeader: Animation;
|
||||
text: Animation;
|
||||
featuredItems: Animation[];
|
||||
nonFeaturedItems: Animation[];
|
||||
footer: Animation;
|
||||
}
|
||||
|
||||
type AnimationNames = keyof AnimationConfig;
|
||||
|
||||
const animationsIn: AnimationConfig = useMemo(
|
||||
() => ({
|
||||
background: createAnimation(
|
||||
{
|
||||
transform: "translateX(calc((100vw - 1200px) / 2 + 800px))",
|
||||
transition: "transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)",
|
||||
},
|
||||
{
|
||||
transition: "transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98)",
|
||||
transform: "translateX(0)",
|
||||
},
|
||||
0
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
{
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
300
|
||||
),
|
||||
text: createAnimation(
|
||||
{
|
||||
transform: "translateY(-20px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateY(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
600
|
||||
),
|
||||
featuredItems: featuredItemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
{
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
400 + 100 * i
|
||||
)
|
||||
),
|
||||
nonFeaturedItems: nonFeaturedItemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
{
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
400 + 100 * (featuredItemsInRing.length + i)
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(-40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(0px)",
|
||||
opacity: "1",
|
||||
},
|
||||
600 + itemsInRing.length * 100
|
||||
),
|
||||
}),
|
||||
[itemsInRing, featuredItemsInRing, nonFeaturedItemsInRing]
|
||||
);
|
||||
|
||||
const animationsOut: AnimationConfig = useMemo(
|
||||
() => ({
|
||||
background: createAnimation(
|
||||
animationsIn.background.stateB,
|
||||
animationsIn.background.stateA,
|
||||
300 + itemsInRing.length * 50
|
||||
),
|
||||
navHeader: createAnimation(
|
||||
animationsIn.navHeader.stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
0
|
||||
),
|
||||
text: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transform: "translateY(20px)",
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
opacity: "0",
|
||||
},
|
||||
0
|
||||
),
|
||||
featuredItems: featuredItemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
animationsIn.featuredItems[i].stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
100 + 50 * i
|
||||
)
|
||||
),
|
||||
nonFeaturedItems: nonFeaturedItemsInRing.map((item, i) =>
|
||||
createAnimation(
|
||||
animationsIn.nonFeaturedItems[i].stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
100 + 50 * (featuredItemsInRing.length + i)
|
||||
)
|
||||
),
|
||||
footer: createAnimation(
|
||||
animationsIn.text.stateB,
|
||||
{
|
||||
transition: "opacity 150ms ease-out, transform 300ms ease-out",
|
||||
transform: "translateX(40px)",
|
||||
opacity: "0",
|
||||
},
|
||||
200 + itemsInRing.length * 50
|
||||
),
|
||||
}),
|
||||
[itemsInRing, featuredItemsInRing, nonFeaturedItemsInRing, animationsIn]
|
||||
);
|
||||
|
||||
const [animations, setAnimations] = useState<AnimationStates>(() => {
|
||||
return leaving ? createAnimationRunner(animationsIn).getState() : {};
|
||||
});
|
||||
|
||||
const [stateLeaving, setStateLeaving] = useState(leaving);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stateLeaving && leaving) {
|
||||
let animationRunner = createAnimationRunner(animationsOut, () =>
|
||||
setAnimations(animationRunner.getState)
|
||||
);
|
||||
animationRunner.run();
|
||||
animationRunner.awaitAnimationComplete(onLeave);
|
||||
setStateLeaving(true);
|
||||
}
|
||||
if (stateLeaving && !leaving) {
|
||||
let animationRunner = createAnimationRunner(animationsIn, () =>
|
||||
setAnimations(animationRunner.getState)
|
||||
);
|
||||
animationRunner.run();
|
||||
setStateLeaving(false);
|
||||
}
|
||||
}, [stateLeaving, leaving, animationsIn, animationsOut, onLeave]);
|
||||
|
||||
const getAnimationStates = (name: AnimationNames) => animations[name];
|
||||
|
||||
const getAnimationState = (name: AnimationNames) => {
|
||||
const animations = getAnimationStates(name);
|
||||
if (animations === undefined || animations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return animations[0];
|
||||
};
|
||||
|
||||
return {
|
||||
getAnimationStates,
|
||||
getAnimationState,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user