feat(table): support card layout
This commit is contained in:
@@ -131,6 +131,17 @@ export type DataTablePageProps<TData> = {
|
||||
*/
|
||||
hideMobile?: boolean
|
||||
|
||||
/**
|
||||
* Render the card view on mobile instead of the default {@link MobileCardList}.
|
||||
* When enabled, the mobile layout reuses the same {@link DataTableCardGrid}
|
||||
* (and therefore `renderCard` / `cardGridClassName`) as the desktop card view,
|
||||
* stacked in a single column. Falls back to the generic card content when no
|
||||
* `renderCard` is provided. Ignored when a custom `mobile` slot is supplied.
|
||||
*
|
||||
* Defaults to `false`, so existing pages keep the list-style mobile layout.
|
||||
*/
|
||||
mobileCardView?: boolean
|
||||
|
||||
/**
|
||||
* Row className resolver — applied to both desktop `TableRow` and mobile card.
|
||||
* Composes with the default `data-state="selected"` styling on desktop.
|
||||
@@ -247,7 +258,9 @@ export type DataTablePageProps<TData> = {
|
||||
viewModeStorageKey?: string
|
||||
|
||||
/**
|
||||
* Initial (uncontrolled) view mode. Defaults to `'table'`.
|
||||
* Initial (uncontrolled) view mode. When unset, defaults to `'card'` if
|
||||
* `enableCardView` is `true`, otherwise `'table'`. A persisted selection
|
||||
* (via `viewModeStorageKey`) always takes precedence over this default.
|
||||
*/
|
||||
defaultViewMode?: DataTableViewMode
|
||||
|
||||
@@ -292,7 +305,12 @@ export function DataTablePage<TData>(props: DataTablePageProps<TData>) {
|
||||
|
||||
const [internalViewMode, setInternalViewMode] = useDataTableViewMode({
|
||||
storageKey: props.viewModeStorageKey,
|
||||
defaultMode: props.defaultViewMode,
|
||||
// When card view is enabled, prefer it as the default unless the consumer
|
||||
// explicitly opts into a different initial mode. A persisted choice (via
|
||||
// `viewModeStorageKey`) still takes precedence over this default.
|
||||
defaultMode:
|
||||
props.defaultViewMode ??
|
||||
(props.enableCardView ? DATA_TABLE_VIEW_MODES.CARD : undefined),
|
||||
})
|
||||
const viewMode = props.viewMode ?? internalViewMode
|
||||
const setViewMode = props.onViewModeChange ?? setInternalViewMode
|
||||
@@ -381,16 +399,33 @@ function renderMobile<TData>(
|
||||
(ownGetRowClassName
|
||||
? (row: Row<TData>) => ownGetRowClassName(row, { isMobile: true })
|
||||
: undefined)
|
||||
const mobileContent = props.mobile ?? (
|
||||
<MobileCardList
|
||||
table={props.table}
|
||||
isLoading={props.isLoading}
|
||||
emptyTitle={props.emptyTitle}
|
||||
emptyDescription={props.emptyDescription}
|
||||
getRowKey={props.mobileProps?.getRowKey}
|
||||
getRowClassName={mobileGetRowClassName}
|
||||
/>
|
||||
)
|
||||
|
||||
let mobileContent = props.mobile
|
||||
if (mobileContent === undefined) {
|
||||
mobileContent = props.mobileCardView ? (
|
||||
<DataTableCardGrid
|
||||
table={props.table}
|
||||
isLoading={props.isLoading}
|
||||
emptyTitle={props.emptyTitle}
|
||||
emptyDescription={props.emptyDescription}
|
||||
emptyIcon={props.emptyIcon}
|
||||
renderCard={props.renderCard}
|
||||
gridClassName={props.cardGridClassName ?? 'grid grid-cols-1 gap-3'}
|
||||
skeletonKeyPrefix={props.skeletonKeyPrefix}
|
||||
getRowKey={props.mobileProps?.getRowKey}
|
||||
getRowClassName={mobileGetRowClassName}
|
||||
/>
|
||||
) : (
|
||||
<MobileCardList
|
||||
table={props.table}
|
||||
isLoading={props.isLoading}
|
||||
emptyTitle={props.emptyTitle}
|
||||
emptyDescription={props.emptyDescription}
|
||||
getRowKey={props.mobileProps?.getRowKey}
|
||||
getRowClassName={mobileGetRowClassName}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return <div className='min-h-0 flex-1 overflow-y-auto'>{mobileContent}</div>
|
||||
}
|
||||
|
||||
@@ -50,16 +50,16 @@ export function DataTableViewModeToggle(props: DataTableViewModeToggleProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const segments: Segment[] = [
|
||||
{
|
||||
value: DATA_TABLE_VIEW_MODES.TABLE,
|
||||
icon: Table2,
|
||||
tooltip: t('Table view'),
|
||||
},
|
||||
{
|
||||
value: DATA_TABLE_VIEW_MODES.CARD,
|
||||
icon: Grid2X2,
|
||||
tooltip: t('Card view'),
|
||||
},
|
||||
{
|
||||
value: DATA_TABLE_VIEW_MODES.TABLE,
|
||||
icon: Table2,
|
||||
tooltip: t('Table view'),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
|
||||
+5
-1
@@ -24,6 +24,8 @@ type ProviderBadgeProps = Omit<StatusBadgeProps, 'children' | 'label'> & {
|
||||
iconKey?: string | null
|
||||
iconSize?: number
|
||||
label: string
|
||||
/** Color the label text by provider name. Set false for a neutral label. */
|
||||
colorText?: boolean
|
||||
}
|
||||
|
||||
export function ProviderBadge({
|
||||
@@ -31,6 +33,7 @@ export function ProviderBadge({
|
||||
iconKey,
|
||||
iconSize = 14,
|
||||
label,
|
||||
colorText = true,
|
||||
...badgeProps
|
||||
}: ProviderBadgeProps) {
|
||||
const icon = iconKey ? getLobeIcon(iconKey, iconSize) : null
|
||||
@@ -43,7 +46,8 @@ export function ProviderBadge({
|
||||
{icon && <span className='flex shrink-0 items-center'>{icon}</span>}
|
||||
<StatusBadge
|
||||
label={label}
|
||||
autoColor={label}
|
||||
autoColor={colorText ? label : undefined}
|
||||
variant={colorText ? undefined : 'neutral'}
|
||||
size='sm'
|
||||
className={cn('min-w-0 shrink overflow-hidden', !icon && 'pl-0')}
|
||||
{...badgeProps}
|
||||
|
||||
+6
-6
@@ -86,15 +86,15 @@ export const StatusBadgeTypeContext =
|
||||
React.createContext<StatusBadgeType>('badge')
|
||||
|
||||
const sizeMap = {
|
||||
sm: 'h-5 gap-1 px-1.5 text-xs leading-none',
|
||||
md: 'h-5 gap-1 px-1.5 text-xs leading-none',
|
||||
lg: 'h-6 gap-1.5 px-2 text-xs leading-none',
|
||||
sm: 'h-5 gap-1 px-1.5 text-sm leading-none',
|
||||
md: 'h-5 gap-1 px-1.5 text-sm leading-none',
|
||||
lg: 'h-6 gap-1.5 px-2 text-sm leading-none',
|
||||
} as const
|
||||
|
||||
const textSizeMap = {
|
||||
sm: 'gap-1 text-xs leading-none',
|
||||
md: 'gap-1 text-xs leading-none',
|
||||
lg: 'gap-1.5 text-xs leading-none',
|
||||
sm: 'gap-1 text-sm leading-none',
|
||||
md: 'gap-1 text-sm leading-none',
|
||||
lg: 'gap-1.5 text-sm leading-none',
|
||||
} as const
|
||||
|
||||
export interface StatusBadgeProps extends Omit<
|
||||
|
||||
Reference in New Issue
Block a user