diff --git a/packages/@react-spectrum/s2/src/TagGroup.tsx b/packages/@react-spectrum/s2/src/TagGroup.tsx index b1616897b39..353e2224bf1 100644 --- a/packages/@react-spectrum/s2/src/TagGroup.tsx +++ b/packages/@react-spectrum/s2/src/TagGroup.tsx @@ -323,7 +323,45 @@ function TagGroupInner({ style={item.props.UNSAFE_style} key={item.key} className={item.props.className({size, allowsRemoving: Boolean(onRemove)})}> - {item.props.children({size, allowsRemoving: Boolean(onRemove), isInCtx: true})} +
+ + {item.props.children({size, allowsRemoving: Boolean(onRemove), isInCtx: true})} + +
+ {Boolean(onRemove) && ( + + )} ); })} @@ -516,41 +554,41 @@ function TagWrapper({children, isDisabled, allowsRemoving, isInRealDOM, isEmphas return ( <> {isInRealDOM && ( -
- - {children} - -
- )} +
+ + {children} + +
+ )} {!isInRealDOM && children} {allowsRemoving && isInRealDOM && ( {item.name}} - /*- end highlight -*/ ); } ``` diff --git a/packages/dev/s2-docs/pages/s2/TagGroup.mdx b/packages/dev/s2-docs/pages/s2/TagGroup.mdx new file mode 100644 index 00000000000..c60e068740f --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/TagGroup.mdx @@ -0,0 +1,218 @@ +import {Layout} from '../../src/Layout'; +export default Layout; + +import docs from 'docs:@react-spectrum/s2'; + +# TagGroup + +{docs.exports.TagGroup.description} + +```tsx render docs={docs.exports.TagGroup} links={docs.links} props={['size', 'labelPosition', 'labelAlign', 'errorMessage', 'maxRows', 'description', 'label', 'isEmphasized', 'isInvalid']} initialProps={{label: 'Ice cream flavors', selectionMode: 'multiple', maxRows: 2}} type="s2" +import {TagGroup, Tag} from '@react-spectrum/s2'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + + + Chocolate + Mint + Strawberry + Vanilla + Chocolate Chip Cookie Dough + Rocky Road + Butter Pecan + Neapolitan + Salted Caramel + Mint Chocolate Chip + Tonight Dough + Lemon Cookie + Cookies and Cream + Phish Food + Peanut Butter Cup + Coffee + Pistachio + Cherry + +``` + +## Content + +`TagGroup` follows the **Collection Components API**, accepting both static and dynamic collections. +This example shows a dynamic collection, passing a list of objects to the `items` prop, and a function to render the children. +Items can be removed via the `onRemove` event. + +```tsx render type="s2" +"use client"; +import {TagGroup, Tag, TextField, Button} from '@react-spectrum/s2'; +import {useListData} from 'react-stately'; +import {useState} from 'react'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + +///- begin collapse -/// +const initialItems = [ + {name: 'Landscape', id: 'landscape'}, + {name: 'Portrait', id: 'portrait'}, + {name: 'Night', id: 'night'}, + {name: 'Dual', id: 'dual'}, + {name: 'Golden Hour', id: 'golden-hour'} +]; +///- end collapse -/// + +function PhotoCategories() { + let list = useListData({initialItems}); + let [newTag, setNewTag] = useState(''); + + return ( +
+ {/*- begin collapse -*/} +
+ + +
+ {/*- end collapse -*/} + list.remove(...keys)} + ///- end highlight -/// + > + {(item) => {item.name}} + +
+ ); +} +``` + +### Links + +Use the `href` prop on a `` to create a link. See the **client side routing guide** to learn how to integrate with your framework. Link interactions vary depending on the selection behavior. See the [selection guide](selection.html) for more details. + +```tsx render type="s2" +"use client"; +import {TagGroup, Tag} from '@react-spectrum/s2'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + + + {/*- begin highlight -*/} + Landscape + {/*- end highlight -*/} + Portrait + Macro + Night + Dual + Golden Hour + +``` + +### Empty state + +If the collection is empty, the `renderEmptyState` prop will be called. + +```tsx render type="s2" +"use client"; +import {TagGroup} from '@react-spectrum/s2'; + + 'No categories'}> + {[]} + +``` + +### Selection + +Use the `selectionMode` prop to enable single or multiple selection. +The selected items can be controlled via the `selectedKeys` prop, matching the `id` prop of the items. +Items can be disabled with the `isDisabled` prop. See the [selection guide](selection.html) for more details. + +```tsx render docs={docs.exports.TagGroup} links={docs.links} props={['selectionMode', 'selectionBehavior', 'disallowEmptySelection']} initialProps={{selectionMode: 'multiple'}} wide +"use client"; +import {TagGroup, Tag} from '@react-spectrum/s2'; +import {useState} from 'react'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + +function Example(props) { + let [selected, setSelected] = useState(new Set()); + + return ( +
+ + Laundry + Fitness center + Parking + Swimming pool + Breakfast + +

Current selection: {selected === 'all' ? 'all' : [...selected].join(', ')}

+
+ ); +} +``` + + +### Group actions + +You can add a group action to the tag group by providing a `groupActionLabel` and `onGroupAction` prop. + +```tsx render type="s2" +"use client"; +import {TagGroup, Tag} from '@react-spectrum/s2'; +import {useState} from 'react'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + +///- begin collapse -/// +const initialTags = [ + {name: 'Landscape', id: 'landscape'}, + {name: 'Portrait', id: 'portrait'}, + {name: 'Macro', id: 'macro'}, + {name: 'Night', id: 'night'}, + {name: 'Dual', id: 'dual'}, + {name: 'Golden Hour', id: 'golden-hour'}, +]; +///- end collapse -/// + +function CopyAll(props) { + let [tags, setTags] = useState(initialTags); + let [selectedTags, setSelectedTags] = useState([]); + return ( + { + alert([...selectedTags].map(tag => tags.find(t => t.id === tag)?.name).join(', ')); + }} + ///- end highlight -/// + selectionMode="multiple" + selectedKeys={selectedTags} + onSelectionChange={setSelectedTags} + items={tags} + label="Photo categories" + styles={style({width: 320})}> + {tag => {tag.name}} + + ) +} +``` + +## Props + +### TagGroup + + + +### Tag + +