Inertia Core Modals
Overview
Inly Core provides an abstraction layer over InertiaUI Modal (@inertiaui/modal-react) that integrates with our design system and server-side configuration.
Key Components:
CoreModal(React): Wraps InertiaUI’sHeadlessModaland renders our UI componentsCoreTableModal(React): Specialized modal for table actions that automatically merges row and bulk action IDsCoreModalPage(PHP): Server-side page configuration for modalsCoreModalLink: React component for opening modals via links
Differences from InertiaUI Modal
1. Server-Side Configuration
InertiaUI Modal: Configuration happens entirely on the client viavisitModal() options.
Inly Core: Configuration can be set server-side using CoreModalPage:
pageData prop.
2. Automatic Title & Description
InertiaUI Modal: Requires manually passing title/description as props or handling them yourself. Inly Core: Automatically reads title and description from server-sidepageData:
3. Type-Safe Sheet Position
InertiaUI Modal: Uses string literals for position:'right' | 'left' | 'top' | 'bottom'
Inly Core: Uses PHP enum SheetPosition:
4. Unified API for Dialogs & Sheets
InertiaUI Modal: Different component APIs for dialog vs slideover modes. Inly Core: SingleCoreModal component handles both automatically based on configuration:
5. Default Max Width via className
InertiaUI Modal: UsesmaxWidth prop for sizing.
Inly Core: Uses className prop mapped to maxWidth internally:
6. Dialog & Sheet Props Merging
InertiaUI Modal: Requires separate handling of dialog vs sheet props. Inly Core: Sheet/Dialog props can be passed viacoreSheetProps and coreDialogProps:
Usage
Opening Modals
You can open modals using either:- Client-side: Use
visitModal()fromuseModalStack()hook - Server-side: Return
CoreModalPagefrom your controller whenrequest()->boolean('modal')is true
Client-Side Opening
Using CoreModalLink
Server-Side Configuration
UseCoreModalPage in your controllers:
React Component Usage
TheCoreModal component automatically reads configuration from pageData:
Function Children & Modal Context
Access modal context using function children:API Reference
CoreModalPage (PHP)
Namespace:Inly\Core\Pages\CoreModalPage
Methods:
make(string $component): static- Create a new modal pagetitle(?string $title): static- Set modal titledescription(?string $description): static- Set modal descriptionsheet(bool $sheet = true, SheetPosition|string|null $position = null): static- Enable sheet mode with optional positionposition(SheetPosition|string $position): static- Set sheet position (only valid when sheet is enabled)with(array $data): static- Add additional data to the pagefromDomainObject(DomainObjectContract|string $domainObject): static- Configure from domain object
CoreModal (React)
Props: ExtendsHeadlessModalProps with these additions:
className?: string- Max width class (e.g.,'max-w-sm')coreSheetProps?: Omit<CoreSheetProps, 'open' | 'onOpenChange' | 'children'>- Props for sheet modecoreDialogProps?: Omit<CoreDialogProps, 'open' | 'onOpenChange' | 'children'>- Props for dialog mode
pageData:
title- Modal titledescription- Modal descriptionsheet- Whether to render as sheetposition- Sheet position
CoreTableModal (React)
Props: ExtendsCoreModalProps (excluding children) with these additions:
selectedItems: string[]- Array of selected item IDs from bulk actions (required)children: (args: { ids: string[]; close: () => void }) => React.ReactNode- Render prop that receives:ids: string[]- Merged array of IDs (either[rowActionId]orselectedItems)close: () => void- Function to close the modal (automatically resetsrowActionId)
- Automatically merges
rowActionId(fromuseTableAction()) andselectedItemsprop - If
rowActionIdexists, uses[rowActionId]; otherwise usesselectedItems - Resets
rowActionIdwhen modal closes - All other props are passed through to
CoreModal
SheetPosition Enum
Namespace:Inly\Core\Enums\SheetPosition
Cases:
SheetPosition::RIGHT- Slide from right (default)SheetPosition::LEFT- Slide from leftSheetPosition::TOP- Slide from topSheetPosition::BOTTOM- Slide from bottom
Data Flow
Complete Example
1. Controller (Server-side):Flow Summary
- User clicks
CoreModalLink→ triggersvisitModal()with modal config - Request includes
?modal=trueparameter - Controller returns
CoreModalPagewith configuration - Configuration is passed to React via
pageDataprop CoreModalreadspageDataand renders as dialog or sheet- Title and description are rendered automatically
CoreTableModal
CoreTableModal is a specialized wrapper around CoreModal designed for table actions. It automatically merges IDs from row actions and bulk actions, eliminating the need to manually handle ID merging logic in your dialog components.
When to Use CoreTableModal
UseCoreTableModal when:
- You need a modal that works with both row actions (single item) and bulk actions (multiple items)
- You want to avoid manually merging
rowActionIdandselectedItemsin every dialog component - Your modal needs to update or operate on table rows
How It Works
CoreTableModal:
- Uses
useTableAction()hook to accessrowActionId(from row actions) - Accepts
selectedItemsprop (from bulk actions via table state) - Automatically merges them: if
rowActionIdexists, uses[rowActionId], otherwise usesselectedItems - Provides merged
idsarray to children via render prop - Handles cleanup of
rowActionIdwhen modal closes
Usage Example
In your table component:Server-Side Handling with WithTableQuery
When usingCoreTableModal for bulk operations, you can use the WithTableQuery trait in your Form Request classes to handle both single item updates (via route parameters) and bulk updates (via ids array) seamlessly. The trait automatically derives the model from your table’s resource builder.
In your Form Request:
WithTableQuery:
getTable(): Returns the table class or instance used for bulk operations (required abstract method)getTableQuery(): Returns a query builder scoped to the provided IDs or route parameter, handling both single and bulk updates. Automatically derives the model from the table’s resource builder.getRouteKeyName(): Returns the route key name (defaults to'id'). Override this method if your route uses a different parameter name.validated(): Automatically excludes theidsfield from validated data, so you only get the fields you need to update- Route parameter detection: Automatically detects if a route parameter exists (single item) vs bulk update (ids array)
- Automatic model resolution: The model is automatically retrieved from
$table->resourceBuilder()->getModel(), so you don’t need to specify it separately
*):
The trait also supports the special '*' value in the IDs array, which represents “select all items”. When '*' is present as the first ID, getTableQuery() applies table filters and search from the previous URL:
ids array is provided, the trait will abort with a 400 status code and the message “Missing route parameter or ids for bulk update”.
Props
CoreTableModal extends CoreModalProps (excluding children) and adds:
selectedItems: string[]- Array of selected item IDs from bulk actions (required)children: (args: { ids: string[]; close: () => void }) => React.ReactNode- Render prop that receives:ids: string[]- Merged array of IDs (either[rowActionId]orselectedItems)close: () => void- Function to close the modal (automatically resetsrowActionId)
Benefits
- No manual ID merging: Automatically handles the logic of combining row and bulk action IDs
- Cleaner components: Dialog components don’t need to import
useTableActionor handle ID merging - Consistent behavior: Ensures ID merging works the same way across all table modals
- Automatic cleanup: Resets
rowActionIdwhen modal closes
Comparison with CoreModal
UseCoreModal when:
- Modal is not related to table actions
- You need full control over modal content and behavior
- Modal doesn’t need to work with table row/bulk selections
CoreTableModal when:
- Modal is triggered from table actions (row or bulk)
- You need to operate on selected table items
- You want automatic ID merging without manual logic
Additional Notes
- Server-side configuration is preferred: Use
CoreModalPagefor consistency and easier maintenance - Props override pageData: React props always take precedence over server configuration
- Position validation: Passing
position()withoutsheet()throws an exception - Modal detection: Controllers should check
request()->boolean('modal')to returnCoreModalPagevs regular page - For local state modals: Use
CoreDialogorCoreSheetdirectly (not route-driven) - Sheet vs Slideover: We use “sheet” in our API, which maps to InertiaUI’s “slideover” prop internally
- Table modals: Use
CoreTableModalfor modals that work with table actions to automatically handle ID merging