Skip to content
41 changes: 14 additions & 27 deletions packages/dev/s2-docs/src/ColorSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';
import {colorSwatch, getColorScale} from './color.macro' with {type: 'macro'};
import {focusRing, iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {Header, ListBox, ListBoxItem, ListBoxSection} from 'react-aria-components';
import InfoCircle from '@react-spectrum/s2/icons/InfoCircle';
import {InfoMessage} from './colorSearchData';
// eslint-disable-next-line monorepo/no-internal-import
import NoSearchResults from '@react-spectrum/s2/illustrations/linear/NoSearchResults';
import React, {useCallback, useEffect, useRef, useState} from 'react';
Expand All @@ -19,7 +19,7 @@ const itemStyle = style({
justifyContent: 'center',
alignItems: 'center',
gap: 8,
padding: 8,
paddingY: 8,
backgroundColor: {
default: 'gray-50',
isHovered: 'gray-100',
Expand Down Expand Up @@ -47,13 +47,6 @@ const swatchStyle = style({
forcedColorAdjust: 'none'
});

const listBoxStyle = style({
width: 'full',
display: 'flex',
flexDirection: 'column',
gap: 24
});

const sectionStyle = style({
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))',
Expand Down Expand Up @@ -199,20 +192,6 @@ const scaleSwatches: Record<string, string> = {
};


export function CopyInfoMessage() {
return (
<div className={style({display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 4, padding: 8})}>
<div className={style({display: 'flex', alignItems: 'center', gap: 4})}>
<InfoCircle styles={iconStyle({size: 'XS'})} />
<span className={style({font: 'ui'})}>Press a color to copy its name.</span>
</div>
<span>
See <Link href="styling">styling</Link> for more information.
</span>
</div>
);
}

interface ColorSearchViewProps {
filteredItems: Array<{
id: string,
Expand Down Expand Up @@ -268,8 +247,8 @@ export function ColorSearchView({filteredItems, exactMatches = new Set(), closes
}

return (
<div className={style({display: 'flex', flexDirection: 'column', gap: 8})}>
<CopyInfoMessage />
<>
<InfoMessage>Press a color to copy its name. See <Link href="styling">styling</Link> for more information.</InfoMessage>
<ListBox
aria-label="Colors"
onAction={(key) => {
Expand All @@ -282,7 +261,15 @@ export function ColorSearchView({filteredItems, exactMatches = new Set(), closes
}
}}
layout="grid"
className={listBoxStyle}
className={style({
width: 'full',
display: 'flex',
flexDirection: 'column',
gap: 24,
flexGrow: 1,
overflow: 'auto',
scrollPaddingY: 4
})}
dependencies={[copiedId, exactMatches, closestMatches]}
items={sections}>
{section => (
Expand All @@ -300,7 +287,7 @@ export function ColorSearchView({filteredItems, exactMatches = new Set(), closes
</ListBoxSection>
)}
</ListBox>
</div>
</>
);
}

Expand Down
26 changes: 11 additions & 15 deletions packages/dev/s2-docs/src/IconSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import {Autocomplete, GridLayout, ListBox, ListBoxItem, Size, useFilter, Virtualizer} from 'react-aria-components';
import CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';
import Close from '@react-spectrum/s2/icons/Close';
import {Content, Heading, IllustratedMessage, pressScale, SearchField, Skeleton, Text, ToastQueue} from '@react-spectrum/s2';
import {Content, Heading, IllustratedMessage, Link, pressScale, SearchField, Skeleton, Text, ToastQueue} from '@react-spectrum/s2';
import {focusRing, iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {iconAliases} from './iconAliases.js';
// @ts-ignore
import icons from '/packages/@react-spectrum/s2/s2wf-icons/*.svg';
import InfoCircle from '@react-spectrum/s2/icons/InfoCircle';
import {InfoMessage} from './colorSearchData';
// eslint-disable-next-line monorepo/no-internal-import
import NoSearchResults from '@react-spectrum/s2/illustrations/linear/NoSearchResults';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
Expand All @@ -19,14 +19,19 @@ export const iconList = Object.keys(icons).map(name => ({id: name.replace(/^S2_I
export function useIconFilter() {
let {contains} = useFilter({sensitivity: 'base'});
return useCallback((textValue: string, inputValue: string) => {
const trimmedInput = inputValue.trim();
// If input is empty after trimming, show all items
if (!trimmedInput) {
return true;
}
// Check for alias matches
for (const alias of Object.keys(iconAliases)) {
if (contains(alias, inputValue) && iconAliases[alias].includes(textValue)) {
if (contains(alias, trimmedInput) && iconAliases[alias].includes(textValue)) {
return true;
}
}
// Also compare for substrings in the icon's actual name
return textValue != null && contains(textValue, inputValue);
return textValue != null && contains(textValue, trimmedInput);
}, [contains]);
}

Expand Down Expand Up @@ -57,15 +62,6 @@ export function useCopyImport() {
return {copiedId, handleCopyImport};
}

function CopyInfoMessage() {
return (
<div className={style({display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4})}>
<InfoCircle styles={iconStyle({size: 'XS'})} />
<span className={style({font: 'ui'})}>Press an item to copy its import statement</span>
</div>
);
}

interface IconListBoxProps {
items: typeof iconList,
copiedId: string | null,
Expand Down Expand Up @@ -133,7 +129,7 @@ export function IconSearchView({filteredItems, listBoxClassName}: IconSearchView

return (
<>
<CopyInfoMessage />
<InfoMessage>Press an item to copy its import statement. See <Link href="icons">Icons</Link> for more information.</InfoMessage>
<IconListBox items={filteredItems} copiedId={copiedId} onAction={handleCopyImport} listBoxClassName={listBoxClassName} />
</>
);
Expand Down Expand Up @@ -236,7 +232,7 @@ export function IconsPageSearch() {
<Autocomplete filter={filter}>
<div className={style({display: 'flex', flexDirection: 'column', gap: 8})}>
<SearchField size="L" aria-label="Search icons" placeholder="Search icons" />
<CopyInfoMessage />
<InfoMessage>Press an item to copy its import statement. See <Link href="icons">Icons</Link> for more information.</InfoMessage>
<IconListBox
items={iconList}
copiedId={copiedId}
Expand Down
27 changes: 12 additions & 15 deletions packages/dev/s2-docs/src/IllustrationCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import {Autocomplete, GridLayout, ListBox, ListBoxItem, Size, useFilter, Virtualizer} from 'react-aria-components';
// eslint-disable-next-line monorepo/no-internal-import
import Checkmark from '@react-spectrum/s2/illustrations/gradient/generic1/Checkmark';
import {Content, Heading, IllustratedMessage, pressScale, ProgressCircle, Radio, RadioGroup, SearchField, SegmentedControl, SegmentedControlItem, Text, ToastQueue} from '@react-spectrum/s2';
import {focusRing, iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {Content, Heading, IllustratedMessage, Link, pressScale, ProgressCircle, Radio, RadioGroup, SearchField, SegmentedControl, SegmentedControlItem, Text, ToastQueue} from '@react-spectrum/s2';
import {focusRing, style} from '@react-spectrum/s2/style' with {type: 'macro'};
// @ts-ignore
import Gradient from '@react-spectrum/s2/icons/Gradient';
import {illustrationAliases} from './illustrationAliases.js';
import InfoCircle from '@react-spectrum/s2/icons/InfoCircle';
import {illustrationAliases} from './illustrationAliases.js';
import {InfoMessage} from './colorSearchData';
// eslint-disable-next-line monorepo/no-internal-import
import NoSearchResults from '@react-spectrum/s2/illustrations/linear/NoSearchResults';
import Polygon4 from '@react-spectrum/s2/icons/Polygon4';
Expand Down Expand Up @@ -44,14 +44,19 @@ export function IllustrationCards() {

let {contains} = useFilter({sensitivity: 'base'});
let filter = useCallback((textValue: string, inputValue: string) => {
const trimmedInput = inputValue.trim();
// If input is empty after trimming, show all items
if (!trimmedInput) {
return true;
}
// Check if input matches an alias that maps to this illustration name
for (const alias of Object.keys(illustrationAliases)) {
if (contains(alias, inputValue) && illustrationAliases[alias].includes(textValue)) {
if (contains(alias, trimmedInput) && illustrationAliases[alias].includes(textValue)) {
return true;
}
}
// Also compare for substrings in the illustration's actual name
return textValue != null && contains(textValue, inputValue);
return textValue != null && contains(textValue, trimmedInput);
}, [contains]);

return (
Expand Down Expand Up @@ -80,7 +85,7 @@ export function IllustrationCards() {
<Radio value="generic2">Generic 2</Radio>
</RadioGroup>
)}
<CopyInfoMessage />
<InfoMessage>Press an item to copy its import statement. See <Link href="illustrations">Illustrations</Link> for more information.</InfoMessage>
<Suspense fallback={<Loading />}>
<IllustrationList variant={variant} gradientStyle={gradientStyle} />
</Suspense>
Expand All @@ -97,14 +102,6 @@ function Loading() {
);
}

function CopyInfoMessage() {
return (
<div className={style({display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4})}>
<InfoCircle styles={iconStyle({size: 'XS'})} />
<span className={style({font: 'ui'})}>Press an item to copy its import statement</span>
</div>
);
}

function useCopyImport(variant: string, gradientStyle: string) {
let [copiedId, setCopiedId] = useState<string | null>(null);
Expand Down
20 changes: 6 additions & 14 deletions packages/dev/s2-docs/src/MobileSearchMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ function MobileNav({initialTag}: {initialTag?: string}) {
selectedTagId={selectedSection}
onSectionSelectionChange={handleTagSelectionChange}
onResourceSelectionChange={handleTagSelectionChange}
isMobile
wrapperClassName={style({paddingTop: 0})}
contentClassName={style({display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8, marginX: 0})} />
</div>
Expand Down Expand Up @@ -356,19 +355,12 @@ function MobileNav({initialTag}: {initialTag?: string}) {
</Suspense>
)}
{!showIcons && isColorsSelected && library.id === 'react-spectrum' && (
<div
className={style({
flexGrow: 1,
overflow: 'auto',
paddingBottom: 16
})}>
<Suspense fallback={<ColorSearchSkeleton />}>
<LazyColorSearchView
filteredItems={filteredColors.sections}
exactMatches={filteredColors.exactMatches}
closestMatches={filteredColors.closestMatches} />
</Suspense>
</div>
<Suspense fallback={<ColorSearchSkeleton />}>
<LazyColorSearchView
filteredItems={filteredColors.sections}
exactMatches={filteredColors.exactMatches}
closestMatches={filteredColors.closestMatches} />
</Suspense>
)}
{!showIcons && (!isColorsSelected || library.id !== 'react-spectrum') && (
<ComponentCardView
Expand Down
23 changes: 12 additions & 11 deletions packages/dev/s2-docs/src/SearchMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,17 @@ export function SearchMenu(props: SearchMenuProps) {
</div>

<CloseButton onClose={onClose} />

<SearchTagGroups
sectionTags={sectionTagsForDisplay}
resourceTags={tabResourceTags}
selectedTagId={selectedTagId}
onSectionSelectionChange={handleTagSelectionChange}
onResourceSelectionChange={handleTagSelectionChange}
onHover={tag => {
preloadComponentImages(sections.find(s => s.id === tag)?.children?.map(c => c.name) || []);
}} />
<div className={style({overflow: 'auto', flexShrink: 0, paddingBottom: 8})}>
<SearchTagGroups
sectionTags={sectionTagsForDisplay}
resourceTags={tabResourceTags}
selectedTagId={selectedTagId}
onSectionSelectionChange={handleTagSelectionChange}
onResourceSelectionChange={handleTagSelectionChange}
onHover={tag => {
preloadComponentImages(sections.find(s => s.id === tag)?.children?.map(c => c.name) || []);
}} />
</div>
{isIconsSelected ? (
<div className={style({flexGrow: 1, overflow: 'auto', display: 'flex', flexDirection: 'column'})}>
<Suspense fallback={<IconSearchSkeleton />}>
Expand All @@ -195,7 +196,7 @@ export function SearchMenu(props: SearchMenuProps) {
</div>
) : null}
{selectedTagId === 'colors' && (
<div className={style({flexGrow: 1, overflow: 'auto', paddingX: 16, paddingBottom: 16})}>
<div className={style({flexGrow: 1, overflow: 'auto', display: 'flex', flexDirection: 'column'})}>
<Suspense fallback={<ColorSearchSkeleton />}>
<LazyColorSearchView filteredItems={filteredColors.sections} exactMatches={filteredColors.exactMatches} closestMatches={filteredColors.closestMatches} />
</Suspense>
Expand Down
6 changes: 2 additions & 4 deletions packages/dev/s2-docs/src/SearchTagGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ interface SearchTagGroupsProps {
selectedTagId: string | undefined,
onSectionSelectionChange: (keys: Iterable<Key>) => void,
onResourceSelectionChange?: (keys: Iterable<Key>) => void,
isMobile?: boolean,
wrapperClassName?: string,
contentClassName?: string,
onHover?: (id: Key) => void
Expand All @@ -30,7 +29,6 @@ export function SearchTagGroups({
selectedTagId,
onSectionSelectionChange,
onResourceSelectionChange,
isMobile = false,
wrapperClassName,
contentClassName,
onHover
Expand Down Expand Up @@ -58,7 +56,7 @@ export function SearchTagGroups({
onSelectionChange={onSectionSelectionChange}
aria-label="Sections"
items={sectionTags}
UNSAFE_style={isMobile ? {whiteSpace: 'nowrap'} : undefined}>
UNSAFE_style={{whiteSpace: 'nowrap'}}>
{(tag) => (
<Tag key={tag.id} id={tag.id} onHoverStart={() => onHover?.(tag.id)} onPressStart={() => onHover?.(tag.id)}>
{tag.name}
Expand All @@ -79,7 +77,7 @@ export function SearchTagGroups({
onSelectionChange={onResourceSelectionChange}
aria-label="Resources"
items={resourceTags}
UNSAFE_style={isMobile ? {whiteSpace: 'nowrap'} : undefined}>
UNSAFE_style={{whiteSpace: 'nowrap'}}>
{(tag) => (
<Tag key={tag.id} id={tag.id} href={tag.href} target="_blank">
{tag.name}
Expand Down
24 changes: 20 additions & 4 deletions packages/dev/s2-docs/src/colorSearchData.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use client';

import {CopyInfoMessage} from './ColorSearchView';
import {getColorHexMap} from './color.macro' with {type: 'macro'};
import {Header, ListBox, ListBoxItem, ListBoxSection} from 'react-aria-components';
import {iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import InfoCircle from '@react-spectrum/s2/icons/InfoCircle';
import {Link, Skeleton, Text} from '@react-spectrum/s2';
import React, {useMemo, useRef} from 'react';
import {Skeleton, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

export const colorHexMaps = getColorHexMap();

Expand Down Expand Up @@ -148,6 +148,22 @@ const headerStyle = style({
marginBottom: 4
});

interface InfoMessageProps {
/** The content to display in the message. */
children: React.ReactNode
}

export function InfoMessage({children}: InfoMessageProps) {
return (
<div className={style({display: 'flex', gap: 4, padding: 8, alignItems: 'center'})}>
<InfoCircle styles={iconStyle({size: 'XS'})} />
<span className={style({font: 'ui'})}>
{children}
</span>
</div>
);
}

function SkeletonColorItem({item}: {item: {id: string}}) {
const ref = useRef(null);
return (
Expand Down Expand Up @@ -204,7 +220,7 @@ export function ColorSearchSkeleton() {

return (
<div className={style({display: 'flex', flexDirection: 'column', gap: 8})}>
<CopyInfoMessage />
<InfoMessage>Press a color to copy its name. See <Link href="styling">styling</Link> for more information.</InfoMessage>
<Skeleton isLoading>
<ListBox
aria-label="Colors loading"
Expand Down