fix(SW-1446): handle empty history and performance better

This commit is contained in:
Michael Zetterberg
2025-04-08 04:03:07 +02:00
parent b2ff5124ec
commit 2953b3571d
6 changed files with 134 additions and 105 deletions

View File

@@ -89,7 +89,7 @@
border-radius: var(--Corner-radius-Large); border-radius: var(--Corner-radius-Large);
padding: var(--Space-x2); padding: var(--Space-x2);
width: 360px; width: 360px;
max-height: 430px; max-height: 400px;
box-sizing: content-box; box-sizing: content-box;
box-shadow: var(--BoxShadow-Level-4); box-shadow: var(--BoxShadow-Level-4);
position: absolute; position: absolute;

View File

@@ -13,7 +13,7 @@ import { useIntl } from "react-intl"
import { useIsMounted } from "usehooks-ts" import { useIsMounted } from "usehooks-ts"
import { Button } from "@scandic-hotels/design-system/Button" import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { Results } from "../Results" import { Results } from "../Results"
@@ -35,7 +35,8 @@ export function ClientInline({
const isMounted = useIsMounted() const isMounted = useIsMounted()
const showResults = !!results const showResults = !!results
const showHistory = isMounted() && (!results || results.length === 0) const showHistory =
latest.length > 0 && isMounted() && (!results || results.length === 0)
return ( return (
<Autocomplete> <Autocomplete>
@@ -101,6 +102,7 @@ export function ClientInline({
</form> </form>
)} )}
</SearchField> </SearchField>
{showResults || showHistory ? (
<div className={styles.results}> <div className={styles.results}>
<div <div
className={cx({ className={cx({
@@ -127,6 +129,7 @@ export function ClientInline({
) : null} ) : null}
</div> </div>
</div> </div>
) : null}
</div> </div>
</Autocomplete> </Autocomplete>
) )

View File

@@ -16,7 +16,7 @@ import {
} from "react-aria-components" } from "react-aria-components"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { Results } from "../Results" import { Results } from "../Results"

View File

@@ -23,51 +23,51 @@ export function ResultsSkeleton() {
<div> <div>
<div className={styles.item}> <div className={styles.item}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<SkeletonShimmer width="50%" /> <SkeletonShimmer width="50%" display="inline-block" />
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<div className={styles.itemDescription}> <div className={styles.itemDescription}>
<SkeletonShimmer width="38%" /> <SkeletonShimmer width="38%" display="inline-block" />
</div> </div>
</Typography> </Typography>
</div> </div>
<div className={styles.item}> <div className={styles.item}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<SkeletonShimmer width="40%" /> <SkeletonShimmer width="40%" display="inline-block" />
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<div className={styles.itemDescription}> <div className={styles.itemDescription}>
<SkeletonShimmer width="23%" /> <SkeletonShimmer width="23%" display="inline-block" />
</div> </div>
</Typography> </Typography>
</div> </div>
<div className={styles.item}> <div className={styles.item}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<SkeletonShimmer width="55%" /> <SkeletonShimmer width="55%" display="inline-block" />
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<div className={styles.itemDescription}> <div className={styles.itemDescription}>
<SkeletonShimmer width="40%" /> <SkeletonShimmer width="40%" display="inline-block" />
</div> </div>
</Typography> </Typography>
</div> </div>
<div className={styles.item}> <div className={styles.item}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<SkeletonShimmer width="27%" /> <SkeletonShimmer width="27%" display="inline-block" />
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<div className={styles.itemDescription}> <div className={styles.itemDescription}>
<SkeletonShimmer width="33%" /> <SkeletonShimmer width="33%" display="inline-block" />
</div> </div>
</Typography> </Typography>
</div> </div>
<div className={styles.item}> <div className={styles.item}>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<SkeletonShimmer width="45%" /> <SkeletonShimmer width="45%" display="inline-block" />
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<div className={styles.itemDescription}> <div className={styles.itemDescription}>
<SkeletonShimmer width="37%" /> <SkeletonShimmer width="37%" display="inline-block" />
</div> </div>
</Typography> </Typography>
</div> </div>

View File

@@ -3,14 +3,16 @@
import { import {
Collection, Collection,
Header, Header,
ListLayout,
Menu, Menu,
MenuItem, MenuItem,
MenuSection, MenuSection,
Text, Text,
Virtualizer,
} from "react-aria-components" } from "react-aria-components"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import styles from "./results.module.css" import styles from "./results.module.css"
@@ -25,6 +27,13 @@ export function Results({
const intl = useIntl() const intl = useIntl()
return ( return (
<Virtualizer
layout={ListLayout}
layoutOptions={{
estimatedRowHeight: 64,
estimatedHeadingHeight: 41,
}}
>
<Menu <Menu
aria-label={ariaLabel} aria-label={ariaLabel}
onAction={onAction} onAction={onAction}
@@ -38,7 +47,9 @@ export function Results({
if (section.id === "actions") { if (section.id === "actions") {
return ( return (
<MenuSection key={section.id} className={styles.actionsSection}> <MenuSection key={section.id} className={styles.actionsSection}>
<Header className="sr-only">{section.name}</Header> <Header className={styles.menuDivider}>
<span className="sr-only">{section.name}</span>
</Header>
<Collection items={section.children}> <Collection items={section.children}>
{(item) => ( {(item) => (
<MenuItem <MenuItem
@@ -101,5 +112,6 @@ export function Results({
) )
}} }}
</Menu> </Menu>
</Virtualizer>
) )
} }

View File

@@ -1,13 +1,20 @@
.menu { .menu {
display: flex; height: 100%;
flex-direction: column; overflow-y: auto;
gap: var(--Space-x2);
} }
.sectionHeader { .sectionHeader {
color: var(--UI-Text-Placeholder); color: var(--UI-Text-Placeholder);
padding-left: var(--Space-x1); padding-left: var(--Space-x1);
padding-bottom: var(--Space-x05); padding-bottom: var(--Space-x05);
/* Due to Virtualizer we cannot use gap in .menu,
instead we use padding-top on each section header */
padding-top: var(--Space-x2);
/* Except for the first section header */
.menu > div > div:first-child & {
padding-top: 0;
}
} }
.item { .item {
@@ -45,9 +52,16 @@
color: var(--Text-Tertiary); color: var(--Text-Tertiary);
} }
.actionsSection { .menuDivider {
border-top: solid 1px var(--Border-Divider-Subtle); padding-top: var(--Space-x2);
padding-top: var(--Space-x2); /* match gap of .menu */ padding-bottom: var(--Space-x2);
}
.menuDivider:before {
display: block;
content: "";
height: 1px;
background: var(--Border-Divider-Subtle);
} }
.actionsSection .item { .actionsSection .item {