Implement load transitions and fix resetting filters (#365)
This PR implements: * Fade-in transitions when cells load in, making navigation and loading appear less janky. * When scrolling, skeleton reps show up before the actual ones load in. * Resetting filters will also reset any set inclusions or exclusions
This commit is contained in:
parent
6dd2579e6e
commit
2d1af335c3
15 changed files with 371 additions and 69 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import TableField from '~components/common/TableField'
|
import TableField from '~components/common/TableField'
|
||||||
import MentionTypeahead from '../MentionTypeahead'
|
import MentionTypeahead from '../MentionTypeahead'
|
||||||
|
import Typeahead from 'react-bootstrap-typeahead/types/core/Typeahead'
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
extends React.DetailedHTMLProps<
|
extends React.DetailedHTMLProps<
|
||||||
|
|
@ -11,6 +12,7 @@ interface Props
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
inclusions: MentionItem[]
|
inclusions: MentionItem[]
|
||||||
exclusions: MentionItem[]
|
exclusions: MentionItem[]
|
||||||
|
typeaheadRef: React.Ref<Typeahead>
|
||||||
onUpdate: (content: MentionItem[]) => void
|
onUpdate: (content: MentionItem[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,6 +22,7 @@ const MentionTableField = ({
|
||||||
placeholder,
|
placeholder,
|
||||||
inclusions,
|
inclusions,
|
||||||
exclusions,
|
exclusions,
|
||||||
|
typeaheadRef,
|
||||||
...props
|
...props
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -31,6 +34,7 @@ const MentionTableField = ({
|
||||||
label={label}
|
label={label}
|
||||||
>
|
>
|
||||||
<MentionTypeahead
|
<MentionTypeahead
|
||||||
|
ref={typeaheadRef}
|
||||||
label={label}
|
label={label}
|
||||||
description={description}
|
description={description}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React from 'react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { getCookie } from 'cookies-next'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -14,6 +15,7 @@ import {
|
||||||
RenderMenuProps,
|
RenderMenuProps,
|
||||||
Token,
|
Token,
|
||||||
} from 'react-bootstrap-typeahead'
|
} from 'react-bootstrap-typeahead'
|
||||||
|
import Typeahead from 'react-bootstrap-typeahead/types/core/Typeahead'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
import { numberToElement } from '~utils/elements'
|
import { numberToElement } from '~utils/elements'
|
||||||
|
|
@ -41,14 +43,10 @@ interface RawSearchResponse {
|
||||||
element: number
|
element: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const MentionTypeahead = ({
|
const MentionTypeahead = React.forwardRef<Typeahead, Props>(function Typeahead(
|
||||||
label,
|
{ label, description, placeholder, inclusions, exclusions, ...props }: Props,
|
||||||
description,
|
forwardedRef
|
||||||
placeholder,
|
) {
|
||||||
inclusions,
|
|
||||||
exclusions,
|
|
||||||
...props
|
|
||||||
}: Props) => {
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const locale = getCookie('NEXT_LOCALE')
|
const locale = getCookie('NEXT_LOCALE')
|
||||||
? (getCookie('NEXT_LOCALE') as string)
|
? (getCookie('NEXT_LOCALE') as string)
|
||||||
|
|
@ -159,6 +157,7 @@ const MentionTypeahead = ({
|
||||||
return (
|
return (
|
||||||
<AsyncTypeahead
|
<AsyncTypeahead
|
||||||
multiple
|
multiple
|
||||||
|
ref={forwardedRef}
|
||||||
className={styles.typeahead}
|
className={styles.typeahead}
|
||||||
id={label}
|
id={label}
|
||||||
align="left"
|
align="left"
|
||||||
|
|
@ -166,7 +165,6 @@ const MentionTypeahead = ({
|
||||||
labelKey={(option) => (option as MentionItem).name[locale]}
|
labelKey={(option) => (option as MentionItem).name[locale]}
|
||||||
defaultSelected={inclusions}
|
defaultSelected={inclusions}
|
||||||
filterBy={() => true}
|
filterBy={() => true}
|
||||||
minLength={3}
|
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
options={options}
|
options={options}
|
||||||
useCache={false}
|
useCache={false}
|
||||||
|
|
@ -181,6 +179,6 @@ const MentionTypeahead = ({
|
||||||
onChange={(selected) => props.onUpdate(selected as MentionItem[])}
|
onChange={(selected) => props.onUpdate(selected as MentionItem[])}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
export default MentionTypeahead
|
export default MentionTypeahead
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { getCookie, setCookie } from 'cookies-next'
|
import { getCookie, setCookie } from 'cookies-next'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
|
@ -10,16 +10,16 @@ import DialogContent from '~components/common/DialogContent'
|
||||||
|
|
||||||
import Button from '~components/common/Button'
|
import Button from '~components/common/Button'
|
||||||
import InputTableField from '~components/common/InputTableField'
|
import InputTableField from '~components/common/InputTableField'
|
||||||
|
import MentionTableField from '~components/common/MentionTableField'
|
||||||
import SelectTableField from '~components/common/SelectTableField'
|
import SelectTableField from '~components/common/SelectTableField'
|
||||||
import SliderTableField from '~components/common/SliderTableField'
|
import SliderTableField from '~components/common/SliderTableField'
|
||||||
import SwitchTableField from '~components/common/SwitchTableField'
|
import SwitchTableField from '~components/common/SwitchTableField'
|
||||||
import SelectItem from '~components/common/SelectItem'
|
import SelectItem from '~components/common/SelectItem'
|
||||||
|
|
||||||
import type { DialogProps } from '@radix-ui/react-dialog'
|
import type { DialogProps } from '@radix-ui/react-dialog'
|
||||||
|
import Typeahead from 'react-bootstrap-typeahead/types/core/Typeahead'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import MentionTableField from '~components/common/MentionTableField'
|
|
||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
defaultFilterSet: FilterSet
|
defaultFilterSet: FilterSet
|
||||||
|
|
@ -43,6 +43,8 @@ const FilterModal = (props: Props) => {
|
||||||
// Refs
|
// Refs
|
||||||
const headerRef = React.createRef<HTMLDivElement>()
|
const headerRef = React.createRef<HTMLDivElement>()
|
||||||
const footerRef = React.createRef<HTMLDivElement>()
|
const footerRef = React.createRef<HTMLDivElement>()
|
||||||
|
const inclusionRef = useRef<Typeahead>(null)
|
||||||
|
const exclusionRef = useRef<Typeahead>(null)
|
||||||
|
|
||||||
// States
|
// States
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
@ -88,7 +90,6 @@ const FilterModal = (props: Props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.open !== undefined) {
|
if (props.open !== undefined) {
|
||||||
setOpen(props.open)
|
setOpen(props.open)
|
||||||
|
|
||||||
// When should we reset the filter state?
|
// When should we reset the filter state?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -160,6 +161,12 @@ const FilterModal = (props: Props) => {
|
||||||
setUserQuality(props.defaultFilterSet.user_quality)
|
setUserQuality(props.defaultFilterSet.user_quality)
|
||||||
setNameQuality(props.defaultFilterSet.name_quality)
|
setNameQuality(props.defaultFilterSet.name_quality)
|
||||||
setOriginalOnly(props.defaultFilterSet.original)
|
setOriginalOnly(props.defaultFilterSet.original)
|
||||||
|
|
||||||
|
setInclusions([])
|
||||||
|
inclusionRef.current?.clear()
|
||||||
|
|
||||||
|
setExclusions([])
|
||||||
|
exclusionRef.current?.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openChange() {
|
function openChange() {
|
||||||
|
|
@ -413,6 +420,7 @@ const FilterModal = (props: Props) => {
|
||||||
exclusions={exclusions}
|
exclusions={exclusions}
|
||||||
placeholder={t('modals.filters.placeholders.inclusion')}
|
placeholder={t('modals.filters.placeholders.inclusion')}
|
||||||
label={t('modals.filters.labels.inclusion')}
|
label={t('modals.filters.labels.inclusion')}
|
||||||
|
typeaheadRef={inclusionRef}
|
||||||
onUpdate={storeInclusions}
|
onUpdate={storeInclusions}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
@ -424,6 +432,7 @@ const FilterModal = (props: Props) => {
|
||||||
exclusions={inclusions}
|
exclusions={inclusions}
|
||||||
placeholder={t('modals.filters.placeholders.exclusion')}
|
placeholder={t('modals.filters.placeholders.exclusion')}
|
||||||
label={t('modals.filters.labels.exclusion')}
|
label={t('modals.filters.labels.exclusion')}
|
||||||
|
typeaheadRef={exclusionRef}
|
||||||
onUpdate={storeExclusions}
|
onUpdate={storeExclusions}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import DOMPurify from 'dompurify'
|
||||||
import Button from '~components/common/Button'
|
import Button from '~components/common/Button'
|
||||||
import SegmentedControl from '~components/common/SegmentedControl'
|
import SegmentedControl from '~components/common/SegmentedControl'
|
||||||
import Segment from '~components/common/Segment'
|
import Segment from '~components/common/Segment'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/reps/GridRepCollection'
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/reps/GridRep'
|
||||||
import RemixTeamAlert from '~components/dialogs/RemixTeamAlert'
|
import RemixTeamAlert from '~components/dialogs/RemixTeamAlert'
|
||||||
import RemixedToast from '~components/toasts/RemixedToast'
|
import RemixedToast from '~components/toasts/RemixedToast'
|
||||||
import EditPartyModal from '../EditPartyModal'
|
import EditPartyModal from '../EditPartyModal'
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,16 @@
|
||||||
padding: $unit-2x;
|
padding: $unit-2x;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--grid-rep-hover);
|
background: var(--grid-rep-hover);
|
||||||
|
|
@ -40,10 +40,16 @@ const GridRep = (props: Props) => {
|
||||||
const locale =
|
const locale =
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
const [mainhand, setMainhand] = useState<Weapon>()
|
const [mainhand, setMainhand] = useState<Weapon>()
|
||||||
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
||||||
const [grid, setGrid] = useState<GridArray<GridWeapon>>({})
|
const [grid, setGrid] = useState<GridArray<GridWeapon>>({})
|
||||||
|
|
||||||
|
const gridRepStyles = classNames({
|
||||||
|
[styles.gridRep]: true,
|
||||||
|
[styles.visible]: visible,
|
||||||
|
[styles.hidden]: !visible,
|
||||||
|
})
|
||||||
const titleClass = classNames({
|
const titleClass = classNames({
|
||||||
empty: !props.name,
|
empty: !props.name,
|
||||||
})
|
})
|
||||||
|
|
@ -68,6 +74,14 @@ const GridRep = (props: Props) => {
|
||||||
[styles.grid]: true,
|
[styles.grid]: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVisible(false) // Trigger fade out
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
setVisible(true) // Trigger fade in
|
||||||
|
}, 300) // You can adjust the timing based on your preference
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newWeapons = Array(numWeapons)
|
const newWeapons = Array(numWeapons)
|
||||||
const gridWeapons = Array(numWeapons)
|
const gridWeapons = Array(numWeapons)
|
||||||
|
|
@ -249,7 +263,7 @@ const GridRep = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link legacyBehavior href={`/p/${props.shortcode}`}>
|
<Link legacyBehavior href={`/p/${props.shortcode}`}>
|
||||||
<a className={styles.gridRep}>
|
<a className={gridRepStyles}>
|
||||||
{detailsWithUsername}
|
{detailsWithUsername}
|
||||||
<div className={styles.weaponGrid}>
|
<div className={styles.weaponGrid}>
|
||||||
<div className={mainhandClasses}>{generateMainhandImage()}</div>
|
<div className={mainhandClasses}>{generateMainhandImage()}</div>
|
||||||
150
components/reps/LoadingRep/index.module.scss
Normal file
150
components/reps/LoadingRep/index.module.scss
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
.gridRep {
|
||||||
|
aspect-ratio: 3/2;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: $card-corner;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
gap: $unit;
|
||||||
|
padding: $unit-2x;
|
||||||
|
min-width: 320px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
// background: var(--placeholder-bg);
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-name: placeHolderShimmer;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
background-color: #f6f7f8;
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--placeholder-bg) 8%,
|
||||||
|
var(--placeholder-bg-accent) 18%,
|
||||||
|
var(--placeholder-bg) 33%
|
||||||
|
);
|
||||||
|
background-size: 1200px 104px;
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
border-radius: calc($font-small / 2);
|
||||||
|
height: $font-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.regular {
|
||||||
|
border-radius: calc($font-regular / 2);
|
||||||
|
height: $font-regular;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .weaponGrid {
|
||||||
|
aspect-ratio: 2/0.95;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 3.36fr; /* left column takes up 1 fraction, right column takes up 3 fractions */
|
||||||
|
grid-gap: $unit; /* add a gap of 8px between grid items */
|
||||||
|
|
||||||
|
.weapon {
|
||||||
|
border-radius: $item-corner-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainhand.weapon {
|
||||||
|
aspect-ratio: 73/153;
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1 / 2; /* spans one column */
|
||||||
|
height: calc(100% - $unit-fourth);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapons {
|
||||||
|
display: grid; /* make the right-images container a grid */
|
||||||
|
grid-template-columns: repeat(
|
||||||
|
3,
|
||||||
|
1fr
|
||||||
|
); /* create 3 columns, each taking up 1 fraction */
|
||||||
|
grid-template-rows: repeat(
|
||||||
|
3,
|
||||||
|
1fr
|
||||||
|
); /* create 3 rows, each taking up 1 fraction */
|
||||||
|
gap: $unit;
|
||||||
|
// column-gap: $unit;
|
||||||
|
// row-gap: $unit-2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid.weapon {
|
||||||
|
aspect-ratio: 280 / 160;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainhand.weapon img[src*='jpg'],
|
||||||
|
.grid.weapon img[src*='jpg'] {
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $unit-half;
|
||||||
|
|
||||||
|
.top {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: calc($unit / 2);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: calc($unit / 2);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributed {
|
||||||
|
display: flex;
|
||||||
|
gap: $unit-half;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.user {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: calc($unit / 2);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
$diameter: 18px;
|
||||||
|
|
||||||
|
border-radius: calc($diameter / 2);
|
||||||
|
height: $diameter;
|
||||||
|
width: $diameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
border-radius: calc($font-small / 2);
|
||||||
|
height: $font-small;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes placeHolderShimmer {
|
||||||
|
$width: 400px;
|
||||||
|
|
||||||
|
0% {
|
||||||
|
background-position: ($width * -1) 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: $width 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
81
components/reps/LoadingRep/index.tsx
Normal file
81
components/reps/LoadingRep/index.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
|
interface Props {}
|
||||||
|
|
||||||
|
const LoadingRep = (props: Props) => {
|
||||||
|
const numWeapons: number = 9
|
||||||
|
|
||||||
|
const mainhandClasses = classNames({
|
||||||
|
[styles.weapon]: true,
|
||||||
|
[styles.mainhand]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const weaponClasses = classNames({
|
||||||
|
[styles.weapon]: true,
|
||||||
|
[styles.grid]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.gridRep}>
|
||||||
|
<div className={styles.details}>
|
||||||
|
<div className={styles.top}>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
[styles.title]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
[styles.regular]: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<div className={styles.properties}>
|
||||||
|
<span className={styles.raid} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.attributed}>
|
||||||
|
<span className={styles.user}>
|
||||||
|
<figure
|
||||||
|
className={classNames({
|
||||||
|
[styles.image]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
[styles.text]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
[styles.small]: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
[styles.timestamp]: true,
|
||||||
|
[styles.placeholder]: true,
|
||||||
|
[styles.small]: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.weaponGrid}>
|
||||||
|
<div className={mainhandClasses} />
|
||||||
|
|
||||||
|
<ul className={styles.weapons}>
|
||||||
|
{Array.from(Array(numWeapons)).map((x, i) => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={`placeholder-${Math.random()}`}
|
||||||
|
className={weaponClasses}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingRep
|
||||||
|
|
@ -15,8 +15,9 @@ import { permissiveFilterset } from '~utils/defaultFilters'
|
||||||
import { elements, allElement } from '~data/elements'
|
import { elements, allElement } from '~data/elements'
|
||||||
import { emptyPaginationObject } from '~utils/emptyStates'
|
import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
|
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/reps/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/reps/GridRepCollection'
|
||||||
|
import LoadingRep from '~components/reps/LoadingRep'
|
||||||
import ErrorSection from '~components/ErrorSection'
|
import ErrorSection from '~components/ErrorSection'
|
||||||
import FilterBar from '~components/filters/FilterBar'
|
import FilterBar from '~components/filters/FilterBar'
|
||||||
import ProfileHead from '~components/head/ProfileHead'
|
import ProfileHead from '~components/head/ProfileHead'
|
||||||
|
|
@ -265,6 +266,26 @@ const ProfileRoute: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
function renderLoading(number: number) {
|
||||||
|
return (
|
||||||
|
<GridRepCollection>
|
||||||
|
{Array.from(Array(number)).map((x, i) => (
|
||||||
|
<LoadingRep key={`loading-${i}`} />
|
||||||
|
))}
|
||||||
|
</GridRepCollection>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderInfiniteScroll = (
|
||||||
|
<InfiniteScroll
|
||||||
|
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
||||||
|
next={() => setCurrentPage(currentPage + 1)}
|
||||||
|
hasMore={totalPages > currentPage}
|
||||||
|
loader={renderLoading(3)}
|
||||||
|
>
|
||||||
|
<GridRepCollection>{renderParties()}</GridRepCollection>
|
||||||
|
</InfiniteScroll>
|
||||||
|
)
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -285,18 +306,7 @@ const ProfileRoute: React.FC<Props> = ({
|
||||||
</FilterBar>
|
</FilterBar>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<InfiniteScroll
|
{renderInfiniteScroll}
|
||||||
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
|
||||||
next={() => setCurrentPage(currentPage + 1)}
|
|
||||||
hasMore={totalPages > currentPage}
|
|
||||||
loader={
|
|
||||||
<div className="notFound">
|
|
||||||
<h2>{t('loading')}</h2>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GridRepCollection>{renderParties()}</GridRepCollection>
|
|
||||||
</InfiniteScroll>
|
|
||||||
|
|
||||||
{parties.length == 0 ? (
|
{parties.length == 0 ? (
|
||||||
<div className="notFound">
|
<div className="notFound">
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,9 @@ import { elements, allElement } from '~data/elements'
|
||||||
import { emptyPaginationObject } from '~utils/emptyStates'
|
import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
|
|
||||||
import ErrorSection from '~components/ErrorSection'
|
import ErrorSection from '~components/ErrorSection'
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/reps/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/reps/GridRepCollection'
|
||||||
|
import LoadingRep from '~components/reps/LoadingRep'
|
||||||
import FilterBar from '~components/filters/FilterBar'
|
import FilterBar from '~components/filters/FilterBar'
|
||||||
import SavedHead from '~components/head/SavedHead'
|
import SavedHead from '~components/head/SavedHead'
|
||||||
|
|
||||||
|
|
@ -306,6 +307,27 @@ const SavedRoute: React.FC<Props> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLoading(number: number) {
|
||||||
|
return (
|
||||||
|
<GridRepCollection>
|
||||||
|
{Array.from(Array(number)).map((x, i) => (
|
||||||
|
<LoadingRep key={`loading-${i}`} />
|
||||||
|
))}
|
||||||
|
</GridRepCollection>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderInfiniteScroll = (
|
||||||
|
<InfiniteScroll
|
||||||
|
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
||||||
|
next={() => setCurrentPage(currentPage + 1)}
|
||||||
|
hasMore={totalPages > currentPage}
|
||||||
|
loader={renderLoading(3)}
|
||||||
|
>
|
||||||
|
<GridRepCollection>{renderParties()}</GridRepCollection>
|
||||||
|
</InfiniteScroll>
|
||||||
|
)
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
return (
|
return (
|
||||||
<div className="teams">
|
<div className="teams">
|
||||||
|
|
@ -325,18 +347,7 @@ const SavedRoute: React.FC<Props> = ({
|
||||||
</FilterBar>
|
</FilterBar>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<InfiniteScroll
|
{renderInfiniteScroll}
|
||||||
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
|
||||||
next={() => setCurrentPage(currentPage + 1)}
|
|
||||||
hasMore={totalPages > currentPage}
|
|
||||||
loader={
|
|
||||||
<div className="notFound">
|
|
||||||
<h2>{t('loading')}</h2>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GridRepCollection>{renderParties()}</GridRepCollection>
|
|
||||||
</InfiniteScroll>
|
|
||||||
|
|
||||||
{parties.length == 0 ? (
|
{parties.length == 0 ? (
|
||||||
<div className="notFound">
|
<div className="notFound">
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@ import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
import { convertAdvancedFilters } from '~utils/convertAdvancedFilters'
|
import { convertAdvancedFilters } from '~utils/convertAdvancedFilters'
|
||||||
|
|
||||||
import ErrorSection from '~components/ErrorSection'
|
import ErrorSection from '~components/ErrorSection'
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/reps/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/reps/GridRepCollection'
|
||||||
|
import LoadingRep from '~components/reps/LoadingRep'
|
||||||
import FilterBar from '~components/filters/FilterBar'
|
import FilterBar from '~components/filters/FilterBar'
|
||||||
import TeamsHead from '~components/head/TeamsHead'
|
import TeamsHead from '~components/head/TeamsHead'
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
// Set up app-specific states
|
// Set up app-specific states
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
const [scrolled, setScrolled] = useState(false)
|
const [scrolled, setScrolled] = useState(false)
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
// Set up page-specific states
|
// Set up page-specific states
|
||||||
const [parties, setParties] = useState<Party[]>([])
|
const [parties, setParties] = useState<Party[]>([])
|
||||||
|
|
@ -122,6 +124,8 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
appState.version = version
|
appState.version = version
|
||||||
}
|
}
|
||||||
setCurrentPage(1)
|
setCurrentPage(1)
|
||||||
|
|
||||||
|
setIsLoading(false)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Add scroll event listener for shadow on FilterBar on mount
|
// Add scroll event listener for shadow on FilterBar on mount
|
||||||
|
|
@ -151,6 +155,8 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
|
|
||||||
const fetchTeams = useCallback(
|
const fetchTeams = useCallback(
|
||||||
({ replace }: { replace: boolean }) => {
|
({ replace }: { replace: boolean }) => {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
const filters: {
|
const filters: {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
} = {
|
} = {
|
||||||
|
|
@ -183,6 +189,10 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
if (replace) replaceResults(meta.count, results)
|
if (replace) replaceResults(meta.count, results)
|
||||||
else appendResults(results)
|
else appendResults(results)
|
||||||
})
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('You are here')
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
.catch((error) => handleError(error))
|
.catch((error) => handleError(error))
|
||||||
},
|
},
|
||||||
[currentPage, parties, element, raid, recency, advancedFilters]
|
[currentPage, parties, element, raid, recency, advancedFilters]
|
||||||
|
|
@ -318,6 +328,27 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLoading(number: number) {
|
||||||
|
return (
|
||||||
|
<GridRepCollection>
|
||||||
|
{Array.from(Array(number)).map((x, i) => (
|
||||||
|
<LoadingRep key={`loading-${i}`} />
|
||||||
|
))}
|
||||||
|
</GridRepCollection>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderInfiniteScroll = (
|
||||||
|
<InfiniteScroll
|
||||||
|
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
||||||
|
next={() => setCurrentPage(currentPage + 1)}
|
||||||
|
hasMore={totalPages > currentPage}
|
||||||
|
loader={renderLoading(3)}
|
||||||
|
>
|
||||||
|
<GridRepCollection>{renderParties()}</GridRepCollection>
|
||||||
|
</InfiniteScroll>
|
||||||
|
)
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
return (
|
return (
|
||||||
<div className="teams">
|
<div className="teams">
|
||||||
|
|
@ -336,28 +367,7 @@ const TeamsRoute: React.FC<Props> = ({
|
||||||
<h1>{t('teams.title')}</h1>
|
<h1>{t('teams.title')}</h1>
|
||||||
</FilterBar>
|
</FilterBar>
|
||||||
|
|
||||||
<section>
|
<section>{renderInfiniteScroll}</section>
|
||||||
<InfiniteScroll
|
|
||||||
dataLength={parties && parties.length > 0 ? parties.length : 0}
|
|
||||||
next={() => setCurrentPage(currentPage + 1)}
|
|
||||||
hasMore={totalPages > currentPage}
|
|
||||||
loader={
|
|
||||||
<div className="notFound">
|
|
||||||
<h2>{t('loading')}</h2>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GridRepCollection>{renderParties()}</GridRepCollection>
|
|
||||||
</InfiniteScroll>
|
|
||||||
|
|
||||||
{parties.length == 0 ? (
|
|
||||||
<div className="notFound">
|
|
||||||
<h2>{t('teams.not_found')}</h2>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else return pageError()
|
} else return pageError()
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@
|
||||||
--selected-item-bg: #{$selected--item--bg--light};
|
--selected-item-bg: #{$selected--item--bg--light};
|
||||||
--selected-item-bg-hover: #{$selected--item--bg--light--hover};
|
--selected-item-bg-hover: #{$selected--item--bg--light--hover};
|
||||||
--anonymous-bg: #{$anonymous--bg--light};
|
--anonymous-bg: #{$anonymous--bg--light};
|
||||||
|
|
||||||
--placeholder-bg: #{$grey-80};
|
--placeholder-bg: #{$grey-80};
|
||||||
|
--placeholder-bg-accent: #{$grey-75};
|
||||||
|
|
||||||
--transparent-stroke: #{$transparent--stroke--light};
|
--transparent-stroke: #{$transparent--stroke--light};
|
||||||
|
|
||||||
|
|
@ -252,7 +254,9 @@
|
||||||
--selected-item-bg-hover: #{$selected--item--bg--dark--hover};
|
--selected-item-bg-hover: #{$selected--item--bg--dark--hover};
|
||||||
|
|
||||||
--anonymous-bg: #{$anonymous--bg--dark};
|
--anonymous-bg: #{$anonymous--bg--dark};
|
||||||
|
|
||||||
--placeholder-bg: #{$grey-40};
|
--placeholder-bg: #{$grey-40};
|
||||||
|
--placeholder-bg-accent: #{$grey-45};
|
||||||
|
|
||||||
--transparent-stroke: #{$transparent--stroke--dark};
|
--transparent-stroke: #{$transparent--stroke--dark};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ $grey-20: #212121;
|
||||||
$grey-25: #232323;
|
$grey-25: #232323;
|
||||||
$grey-30: #2f2f2f;
|
$grey-30: #2f2f2f;
|
||||||
$grey-40: #444;
|
$grey-40: #444;
|
||||||
|
$grey-45: #515151;
|
||||||
$grey-50: #777;
|
$grey-50: #777;
|
||||||
$grey-55: #888;
|
$grey-55: #888;
|
||||||
$grey-60: #a9a9a9;
|
$grey-60: #a9a9a9;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue