Skip to content
Merged
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
89 changes: 45 additions & 44 deletions packages/ui/src/elements/PillSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,52 +30,53 @@ export type Props = {
* If `draggable` is true, the pills can be reordered by dragging.
*/
export const PillSelector: React.FC<Props> = ({ draggable, onClick, pills }) => {
const Wrapper = React.useMemo(() => {
if (draggable) {
return ({ children }) => (
<DraggableSortable
className={baseClass}
ids={pills.map((pill) => pill.name)}
onDragEnd={({ moveFromIndex, moveToIndex }) => {
draggable.onDragEnd({
moveFromIndex,
moveToIndex,
})
// IMPORTANT: Do NOT wrap DraggableSortable in a dynamic component function using useMemo.
// BAD: useMemo(() => ({ children }) => <DraggableSortable>...</DraggableSortable>, [deps])
// This creates a new function reference on each recomputation, causing React to treat it as a
// different component type, triggering unmount/mount cycles instead of just updating props.
// GOOD: Use conditional rendering directly: draggable ? <DraggableSortable /> : <div />
const pillElements = React.useMemo(() => {
return pills.map((pill, i) => {
return (
<Pill
alignIcon="left"
aria-checked={pill.selected}
className={[`${baseClass}__pill`, pill.selected && `${baseClass}__pill--selected`]
.filter(Boolean)
.join(' ')}
draggable={Boolean(draggable)}
icon={pill.selected ? <XIcon /> : <PlusIcon />}
id={pill.name}
key={pill.key ?? `${pill.name}-${i}`}
onClick={() => {
if (onClick) {
void onClick({ pill })
}
}}
size="small"
>
{children}
</DraggableSortable>
{pill.Label ?? <span className={`${baseClass}__pill-label`}>{pill.name}</span>}
</Pill>
)
} else {
return ({ children }) => <div className={baseClass}>{children}</div>
}
}, [draggable, pills])
})
}, [pills, onClick, draggable])

return (
<Wrapper>
{pills.map((pill, i) => {
return (
<Pill
alignIcon="left"
aria-checked={pill.selected}
className={[`${baseClass}__pill`, pill.selected && `${baseClass}__pill--selected`]
.filter(Boolean)
.join(' ')}
draggable={Boolean(draggable)}
icon={pill.selected ? <XIcon /> : <PlusIcon />}
id={pill.name}
key={pill.key ?? `${pill.name}-${i}`}
onClick={() => {
if (onClick) {
void onClick({ pill })
}
}}
size="small"
>
{pill.Label ?? <span className={`${baseClass}__pill-label`}>{pill.name}</span>}
</Pill>
)
})}
</Wrapper>
)
if (draggable) {
return (
<DraggableSortable
className={baseClass}
ids={pills.map((pill) => pill.name)}
onDragEnd={({ moveFromIndex, moveToIndex }) => {
draggable.onDragEnd({
moveFromIndex,
moveToIndex,
})
}}
>
{pillElements}
</DraggableSortable>
)
}

return <div className={baseClass}>{pillElements}</div>
}
Loading