Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
// This will be marshalled to either the first or last item depending on where focus came from.
let tabIndex: number | undefined = undefined;
if (!shouldUseVirtualFocus) {
tabIndex = manager.isFocused ? -1 : 0;
tabIndex = manager.focusedKey == null ? 0 : -1;
}

let collectionId = useCollectionId(manager.collection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {FocusScope} from '@react-aria/focus';
import {getInteractionModality} from '@react-aria/interactions';
import helpStyles from '@adobe/spectrum-css-temp/components/contextualhelp/vars.css';
import {nodeContains} from '@react-aria/utils';
import {Popover} from '@react-spectrum/overlays';
import {Popover} from './Popover';
import React, {JSX, KeyboardEventHandler, ReactElement, useEffect, useRef, useState} from 'react';
import ReactDOM from 'react-dom';
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
Expand Down
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
Loading