feat: add AttachmentGrid component - #10561
Conversation
|
Build successful! 🎉 |
|
Build successful! 🎉 |
| ...focusRing() | ||
| }); | ||
|
|
||
| const gridGap = css('gap: 6px;'); |
There was a problem hiding this comment.
I can swap this out for 8px, Figma had 6, wasn't sure.
There was a problem hiding this comment.
you can use custom values in a style macro like so
gap: '[6px]'
or if it needs to respond to font size or scaling, you can make use of size() or space()
https://react-spectrum.adobe.com/style-macro#space
There was a problem hiding this comment.
is it really a static 6px at all sizes? Feels like the scale should change the gap size
| ) | ||
| }; | ||
|
|
||
| export const WithAttachmentGrid: Story = { |
There was a problem hiding this comment.
Thought this would be a useful story, can remove.
| export const Overflow: Story = { | ||
| name: 'Overflow (vertical scroll fade)', | ||
| render: args => ( | ||
| <div style={{width: 320, resize: 'horizontal', overflow: 'hidden'}}> |
There was a problem hiding this comment.
maybe this needs padding
|
Build successful! 🎉 |
| ...focusRing() | ||
| }); | ||
|
|
||
| const gridGap = css('gap: 6px;'); |
There was a problem hiding this comment.
is it really a static 6px at all sizes? Feels like the scale should change the gap size
| let domRef = useDOMRef(ref); | ||
|
|
||
| return ( | ||
| <ListBox |
There was a problem hiding this comment.
should the container have a focus ring? Or are the items focusable its hard to tell
There was a problem hiding this comment.
I don't think the individual items should be focusable/interactable but perhaps the grid/container itself should have a focus ring. You are supposed to be able to scroll it with keyboard when focused.
| const hasContent = ':has([data-slot=content])'; | ||
|
|
||
| const gridStyles = style({ | ||
| display: 'grid', |
There was a problem hiding this comment.
Can there be a mix of cards that are thumbnails and cards that have the description?
There was a problem hiding this comment.
no, the card types can't be mixed (at least following AttachmentList)
| const gridStyles = style({ | ||
| display: 'grid', | ||
| gridTemplateColumns: { | ||
| default: 'repeat(auto-fill, minmax(64px, 1fr))', |
There was a problem hiding this comment.
If the container is wide and there aren't enough thumbs to fill it in the first row, i think they'll end up spaced apart
I think you just want
default: 'repeat(auto-fill, minmax(64px, auto))'
though that only matters if the attachments can be varying sizes, if they can't, then you could just do
default: 'repeat(auto-fill, 64px)'
There was a problem hiding this comment.
We had a discussion about this in one of our team meetings. We're unsure if it should be justified space around as you have it here, or left justified... or what, can you follow up with the designer?
| size?: 'XS' | 'S' | 'M' | 'L' | 'XL'; | ||
| /** Whether the attachment has an error. */ | ||
| isInvalid?: boolean; | ||
| uploadProgress?: number; |
| aria-label={ariaLabel} | ||
| aria-labelledby={ariaLabelledby} | ||
| aria-describedby={ariaDescribedby} |
There was a problem hiding this comment.
these and id are all just getting passed straight through, we can use filterDOMProps with labeling set to true
then we can just spread the result of that
|
Build successful! 🎉 |
## API Changes
@react-spectrum/ai/@react-spectrum/ai:Attachment Attachment {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
- children: ReactNode
+ children: ReactNode | (AttachmentRenderProps) => ReactNode
density?: 'compact' | 'regular' | 'spacious' = 'regular'
download?: boolean | string
href?: Href
hrefLang?: string
isDisabled?: boolean
isInvalid?: boolean
onAction?: () => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, TagRenderProps>
routerOptions?: RouterOptions
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
styles?: StyleString
target?: HTMLAttributeAnchorTarget
textValue?: string
uploadProgress?: number
value?: T
variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
}/@react-spectrum/ai:PromptFieldValue PromptFieldValue {
Direction: any
SelectedRange: any
caretPosition: Position
constructor: (readonly Array<TokenFieldSegment<T>>, TokenFieldValueOptions) => void
delete: (Position, Intl.Segmenter, any, any) => this
deleteLine: (Position, any, any) => this
endCoalescing: () => void
findBoundaryWithSegmenter: (Position, Intl.Segmenter, any) => Position | null
findLineBoundary: (Position, any) => Position | null
findText: (Position, any, string | RegExp) => Position | null
redo: () => this
replaceRange: (Position, Position, string, any) => this
replaceRangeWithSegments: (Position, Position, Array<TokenFieldSegment>, any) => this
segments: readonly Array<TokenFieldSegment<T>>
selectedRange: SelectedRange
slice: (Position, Position) => this
- toString: () => void
+ toString: () => string
tokenize: (string) => Array<TokenFieldSegment>
undo: () => this
withCaretPosition: (Position) => this
withSelectedRange: (SelectedRange) => this/@react-spectrum/ai:PromptField PromptField {
acceptedAttachmentTypes?: Array<string>
- aiDisclaimer?: React.ReactNode
attachments?: Array<PromptFieldAttachment>
brandColor?: string
children: React.ReactNode
defaultAttachments?: Array<PromptFieldAttachment>
isGenerating?: boolean
onAITermsPress?: () => void
onAddAttachments?: (Array<PromptFieldAttachment>) => void
onAttachmentsChange?: (Array<PromptFieldAttachment>) => void
onChange?: (PromptFieldValue) => void
onRemoveAttachments?: (Array<PromptFieldAttachment>) => void
onStop?: () => void
onSubmit?: (PromptFieldValue, Array<PromptFieldAttachment>) => void
size?: 'S' | 'M' = 'M'
styles?: StyleString
value?: PromptFieldValue
variant?: 'balanced' | 'prominent' | 'subtle' = 'balanced'
}/@react-spectrum/ai:PromptToken PromptToken {
children: React.ReactNode
+ className?: ClassNameOrFunction<TokenRenderProps> = 'react-aria-Token'
+ style?: StyleOrFunction<TokenRenderProps>
token: TokenSegment<PromptFieldTokenValue>
}/@react-spectrum/ai:ExecutionTraceItem ExecutionTraceItem {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
- children: string
+ children: ReactNode
detail?: ReactNode
detailMaxHeight?: number = 120
icon?: ReactNode
id?: string
styles?: StyleString
}/@react-spectrum/ai:ResponseStatusTitle ResponseStatusTitle {
- children: string
+ children: React.ReactNode
id?: string
level?: number = 3
pixelLoader?: Array<Cell> | Array<Array<Cell>>
styles?: StyleString/@react-spectrum/ai:ThreadItem ThreadItem {
allowsArrowNavigation?: boolean
- children: ReactNode
+ children?: ChildrenOrFunction<GridListItemRenderProps>
focusMode?: 'child' | 'row'
id?: Key
isStreaming?: boolean
shouldAnnounceOnMount?: boolean
textValue?: string
}/@react-spectrum/ai:AttachmentProps AttachmentProps {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
- children: ReactNode
+ children: ReactNode | (AttachmentRenderProps) => ReactNode
density?: 'compact' | 'regular' | 'spacious' = 'regular'
download?: boolean | string
href?: Href
hrefLang?: string
isDisabled?: boolean
isInvalid?: boolean
onAction?: () => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, TagRenderProps>
routerOptions?: RouterOptions
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
styles?: StyleString
target?: HTMLAttributeAnchorTarget
textValue?: string
uploadProgress?: number
value?: T
variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
}/@react-spectrum/ai:PromptFieldProps PromptFieldProps {
acceptedAttachmentTypes?: Array<string>
- aiDisclaimer?: React.ReactNode
attachments?: Array<PromptFieldAttachment>
brandColor?: string
children: React.ReactNode
defaultAttachments?: Array<PromptFieldAttachment>
isGenerating?: boolean
onAITermsPress?: () => void
onAddAttachments?: (Array<PromptFieldAttachment>) => void
onAttachmentsChange?: (Array<PromptFieldAttachment>) => void
onChange?: (PromptFieldValue) => void
onRemoveAttachments?: (Array<PromptFieldAttachment>) => void
onStop?: () => void
onSubmit?: (PromptFieldValue, Array<PromptFieldAttachment>) => void
size?: 'S' | 'M' = 'M'
styles?: StyleString
value?: PromptFieldValue
variant?: 'balanced' | 'prominent' | 'subtle' = 'balanced'
}/@react-spectrum/ai:PromptTokenProps PromptTokenProps {
children: React.ReactNode
+ className?: ClassNameOrFunction<TokenRenderProps> = 'react-aria-Token'
+ style?: StyleOrFunction<TokenRenderProps>
token: TokenSegment<PromptFieldTokenValue>
}/@react-spectrum/ai:ExecutionTraceItemProps ExecutionTraceItemProps {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
- children: string
+ children: ReactNode
detail?: ReactNode
detailMaxHeight?: number = 120
icon?: ReactNode
id?: string
styles?: StyleString
}/@react-spectrum/ai:ResponseStatusTitleProps ResponseStatusTitleProps {
- children: string
+ children: React.ReactNode
id?: string
level?: number = 3
pixelLoader?: Array<Cell> | Array<Array<Cell>>
styles?: StyleString/@react-spectrum/ai:ThreadItemProps ThreadItemProps {
allowsArrowNavigation?: boolean
- children: ReactNode
+ children?: ChildrenOrFunction<GridListItemRenderProps>
focusMode?: 'child' | 'row'
id?: Key
isStreaming?: boolean
shouldAnnounceOnMount?: boolean
textValue?: string
}/@react-spectrum/ai:AttachmentGrid+AttachmentGrid <T> {
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ children?: ReactNode | (T) => ReactNode
+ dependencies?: ReadonlyArray<any>
+ id?: string
+ items?: Iterable<T>
+ styles?: StyleString
+}/@react-spectrum/ai:AttachmentGridItem+AttachmentGridItem {
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ children: ReactNode | (AttachmentRenderProps) => ReactNode
+ id?: Key
+ isInvalid?: boolean
+ size?: 'XS' | 'S' | 'M' | 'L' | 'XL'
+ styles?: StyleString
+ textValue?: string
+ uploadProgress?: number
+}/@react-spectrum/ai:AttachmentGridProps+AttachmentGridProps <T> {
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ children?: ReactNode | (T) => ReactNode
+ dependencies?: ReadonlyArray<any>
+ id?: string
+ items?: Iterable<T>
+ styles?: StyleString
+}/@react-spectrum/ai:AttachmentGridItemProps+AttachmentGridItemProps {
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ children: ReactNode | (AttachmentRenderProps) => ReactNode
+ id?: Key
+ isInvalid?: boolean
+ size?: 'XS' | 'S' | 'M' | 'L' | 'XL'
+ styles?: StyleString
+ textValue?: string
+ uploadProgress?: number
+}@react-spectrum/s2/@react-spectrum/s2:Menu Menu <T> {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean | FocusStrategy
children: ReactNode | (T) => ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
dependencies?: ReadonlyArray<any>
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
hideLinkOutIcon?: boolean
id?: string
items?: Iterable<T>
- loadingState?: LoadingState
onAction?: (Key, T) => void
onClose?: () => void
- onLoadMore?: () => any
onSelectionChange?: (Selection) => void
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
shouldCloseOnSelect?: boolean
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
styles?: StylesProp
}/@react-spectrum/s2:MenuProps MenuProps <T> {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean | FocusStrategy
children: ReactNode | (T) => ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
dependencies?: ReadonlyArray<any>
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
hideLinkOutIcon?: boolean
id?: string
items?: Iterable<T>
- loadingState?: LoadingState
onAction?: (Key, T) => void
onClose?: () => void
- onLoadMore?: () => any
onSelectionChange?: (Selection) => void
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
shouldCloseOnSelect?: boolean
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
styles?: StylesProp
} |
Closes
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project:
Adobe