Inline Entity Manager
Compound inline choose/create/manage entity pattern built on Popover for contextual selection without leaving the current workflow.
Overview
InlineEntityManager is a compound component for choosing, searching, creating, and often editing related entities without navigating away from the current surface.
This is one of the platform's main "stay in context" interaction patterns.
It is built on top of Popover, adds shared search state, and provides a reusable panel structure for entity management UI.
There are two layers:
| Layer | Role |
|---|---|
InlineEntityManager.* | Low-level compound API for custom inline entity flows |
InlineEntityManagerDefault | Opinionated default chooser panel for common entity-selection cases |
When To Use It
Use InlineEntityManager when the user needs to:
- choose a related entity in context
- search through available entities
- create a missing entity without leaving the current flow
- manage lightweight entity metadata or actions inline
Prefer it over a normal Select when the entity has richer identity, search/create flows, or management actions that do not fit a simple dropdown.
Compound API
| Part | Purpose |
|---|---|
InlineEntityManager.Root | Root wrapper. Handles popover state, search state, and placement. |
InlineEntityManager.Trigger | Trigger button/anchor for opening the manager. |
InlineEntityManager.Panel | Floating panel surface. |
InlineEntityManager.PanelHeader | Standard panel header with icon and title. |
InlineEntityManager.SearchInput | Search field wired to shared manager state. |
InlineEntityManager.List | Scrollable list container. |
InlineEntityManager.Item | Selectable entity row with checkbox-style selection. |
InlineEntityManager.HelperText | Small helper/empty-state text. |
InlineEntityManager.Footer | Footer area for create-new or extra actions. |
InlineEntityManager.Badge | Compact selected-entity badge, also usable outside the panel. |
useInlineEntityManager | Accesses shared manager state (searchValue, setSearchValue, open, close). |
Default Pattern
For most standard inline entity pickers, start with InlineEntityManagerDefault.
<InlineEntityManager.Root>
<InlineEntityManager.Trigger>
<InlineEntityManager.Badge
color={getDealStatusColor(selected)}
iconId={getDealStatusIcon(selected)}
label={getDealStatusLabel(selected)}
/>
</InlineEntityManager.Trigger>
<InlineEntityManagerDefault
headerIcon="CircleHalfFill"
headerTitle="Change Status"
entities={statusEntities}
selectedIds={[selected]}
onToggleEntity={(status) => onSelect(status.id)}
withSearch={false}
helperText="Select a status"
/>
</InlineEntityManager.Root>This is the production pattern used for inline status management in Deals.components.tsx.
Custom Composition
Drop down to the compound primitives when the panel needs more than the default chooser.
<InlineEntityManager.Root className="w-full">
<InlineEntityManager.Trigger className="flex w-full items-center justify-between rounded-md border border-strong bg-gray-bg-surface px-3 py-2 text-left">
<span className="text-sm text-dim">Select category</span>
<Icon name="ChevronBottom" className="h-4 w-4 text-dim" />
</InlineEntityManager.Trigger>
<InlineEntityManager.Panel className="w-[380px]">
<InlineEntityManager.PanelHeader icon="Folders" title="Category" />
<div className="px-3">
<InlineEntityManager.SearchInput placeholder="Search categories..." />
</div>
<InlineEntityManager.List>
{/* custom item rendering */}
</InlineEntityManager.List>
<InlineEntityManager.Footer className="px-3">
<button type="button">Create Category</button>
</InlineEntityManager.Footer>
</InlineEntityManager.Panel>
</InlineEntityManager.Root>This is the real pattern used in LmsCategoryEditor.tsx.
Search State
InlineEntityManager.SearchInput and useInlineEntityManager() share the same local search state.
That means custom list components can filter based on the manager's searchValue instead of re-creating local search state.
const { searchValue } = useInlineEntityManager();This is one of the reasons InlineEntityManager works well as a reusable interaction pattern rather than just a styled popover.
Root Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Required. Trigger and panel content. |
className | string | — | Additional root wrapper classes. |
initialSearchValue | string | — | Initial search text. |
resetSearchOnClose | boolean | true | Resets search text when the manager closes. |
placement | Placement | — | Popover placement. |
Default Props
InlineEntityManagerDefault
| Prop | Type | Default | Description |
|---|---|---|---|
withSearch | boolean | true | Shows the built-in search input. |
headerIcon | IconName | — | Required header icon. |
headerTitle | string | — | Required header title. |
entities | Entity[] | [] | Available entities. |
selectedIds | string[] | [] | Selected entity ids. |
onToggleEntity | (entity) => void | Promise<void> | — | Called when an entity is toggled. |
searchPlaceholder | string | 'Search' | Search placeholder text. |
helperText | string | 'Select an option' | Helper text above the list. |
emptyText | string | 'Nothing yet.' | Empty-state text. |
loadingState | ReactNode | — | Custom loading-state content. |
isLoading | boolean | — | Shows loading state. |
footerConfig | { text, onClick, icon? } | — | Optional footer action for create-new or related actions. |
contextMenuItems | (entity) => ContextMenuItem[] | — | Optional per-entity context actions. |
panelClassName | string | — | Additional panel classes. |
renderItemContent | (entity) => ReactNode | — | Custom item content override. |
Brand Dashboard Usage
Representative usages:
| Pattern | Path | Notes |
|---|---|---|
| Inline deal status manager | fsai/apps/brand-dashboard/src/pages/Deals/Deals.components.tsx | Best simple example of InlineEntityManagerDefault. |
| LMS category selector | fsai/apps/brand-dashboard/src/pages/Learning/components/LmsCategorySelector.tsx | Searchable chooser pattern with custom list wiring. |
| LMS category choose/create/edit flow | fsai/apps/brand-dashboard/src/pages/Learning/components/LmsCategoryEditor.tsx | Best example of richer custom composition with create and edit states. |
| Helpdesk category editor | fsai/apps/brand-dashboard/src/pages/AdminPanel/Helpdesk/HelpdeskCategoryEditor.tsx | Another strong create/manage-in-place example. |
| Badge-only status display | fsai/apps/brand-dashboard/src/pages/Leads/Leads.components.tsx | Shows InlineEntityManager.Badge used outside the manager itself. |
Important Conventions
InlineEntityManageris fundamentally a contextual management pattern, not just a fancy dropdown.InlineEntityManager.Rootstops click and mouse-down propagation because it is often embedded inside clickable rows or dense surfaces.InlineEntityManager.Itemuses checkbox-style selection UI, which makes it feel different from a normalMenuorSelectitem.InlineEntityManagerDefaultis the right starting point when the panel is mostly a searchable selectable list.- If the flow needs create/edit/delete states or richer panel content, switch to the compound primitives instead of overloading the default wrapper.
Relationship To Other Components
- Use
Selectfor normal choose-from-list fields. - Use
AutocompleteSelectwhen search-first form selection is the primary need, especially if add-new behavior can still keep the user in context. - Use
InlineEntityManagerwhen entity selection also needs inline creation, management, or richer contextual identity than a field-style selector normally provides. - Use
GuidedFloworPanelwhen the inline task grows into a larger staged workflow.
Guidelines
- Do use
InlineEntityManagerfor related-entity choose/create/manage flows that should stay in context - Do start with
InlineEntityManagerDefaultfor ordinary searchable choosers - Do switch to the compound primitives when the panel needs custom flows or editing states
- Don't use it for simple form dropdowns that
Selectalready handles well - Don't navigate users away for small related-entity tasks that fit comfortably inline