Expand theme system and refresh UI components
This commit is contained in:
@@ -3,6 +3,7 @@ import { Button, Checkbox, Input, Paragraph, ScrollView, Text, XStack, YStack }
|
||||
import { getIcon } from '../IconMapper.jsx';
|
||||
import { useGridView } from './context.js';
|
||||
import { formatValueByColumn } from './utils.js';
|
||||
import { getTypographyRoleProps } from '../../styles/index.js';
|
||||
|
||||
function renderToolbarItem(item) {
|
||||
if (!item) {
|
||||
@@ -22,7 +23,7 @@ function renderToolbarItem(item) {
|
||||
theme={item.theme}
|
||||
chromeless={item.chromeless}
|
||||
disabled={item.disabled}
|
||||
icon={IconComponent ? <IconComponent size={16} /> : undefined}
|
||||
icon={IconComponent ? <IconComponent size="sm" /> : undefined}
|
||||
onPress={item.onClick || item.onPress}
|
||||
>
|
||||
{item.label}
|
||||
@@ -32,21 +33,27 @@ function renderToolbarItem(item) {
|
||||
|
||||
if (item.kind === 'text') {
|
||||
return (
|
||||
<Text key={item.key || item.text} color="$color" opacity={0.7}>
|
||||
<Text key={item.key || item.text} color="$textSecondary">
|
||||
{item.text}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.kind === 'search') {
|
||||
const SearchIcon = getIcon('search');
|
||||
return (
|
||||
<Input
|
||||
key={item.key || item.placeholder || 'search'}
|
||||
width={item.width || 240}
|
||||
value={item.value}
|
||||
placeholder={item.placeholder || 'Search'}
|
||||
onChangeText={(value) => item.onChange?.(value)}
|
||||
/>
|
||||
<XStack key={item.key || item.placeholder || 'search'} alignItems="center" gap="$2">
|
||||
{SearchIcon ? <SearchIcon size="sm" color="$textMuted" /> : null}
|
||||
<Input
|
||||
width={item.width || 240}
|
||||
value={item.value}
|
||||
placeholder={item.placeholder || 'Search'}
|
||||
onChangeText={(value) => item.onChange?.(value)}
|
||||
backgroundColor="$bgPanel"
|
||||
borderColor="$lineSubtle"
|
||||
focusStyle={{ borderColor: '$accent' }}
|
||||
/>
|
||||
</XStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,14 +82,14 @@ function DefaultPanelRecordRenderer({ row }) {
|
||||
return (
|
||||
<YStack gap="$3">
|
||||
<YStack gap="$1">
|
||||
<Text fontSize="$3" letterSpacing={1} textTransform="uppercase" color="$accentColor">
|
||||
<Text fontSize="$2" letterSpacing={1} textTransform="uppercase" color="$textSecondary">
|
||||
Record Summary
|
||||
</Text>
|
||||
<Text fontSize="$6" fontWeight="700">
|
||||
<Text {...getTypographyRoleProps('sectionTitle')}>
|
||||
{titleColumn ? row?.[titleColumn.field] : row?.id}
|
||||
</Text>
|
||||
{subtitleColumn ? (
|
||||
<Paragraph color="$color" opacity={0.7}>
|
||||
<Paragraph color="$textSecondary">
|
||||
{row?.[subtitleColumn.field] || ''}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
@@ -94,15 +101,15 @@ function DefaultPanelRecordRenderer({ row }) {
|
||||
key={`${row.id}-${column.field}-chip`}
|
||||
paddingHorizontal="$3"
|
||||
paddingVertical="$2"
|
||||
borderRadius="$6"
|
||||
backgroundColor="$accentSurface"
|
||||
borderRadius="$radiusMd"
|
||||
backgroundColor="$bgPanel"
|
||||
borderWidth={1}
|
||||
borderColor="$accentBorder"
|
||||
borderColor="$lineSubtle"
|
||||
>
|
||||
<Text fontSize="$2" color="$color" opacity={0.65}>
|
||||
<Text fontSize="$2" color="$textMuted">
|
||||
{column.label}
|
||||
</Text>
|
||||
<Text fontSize="$4" fontWeight="600">
|
||||
<Text {...getTypographyRoleProps('tableHeader', { color: '$textPrimary' })}>
|
||||
{formatValueByColumn(row?.[column.field], column)}
|
||||
</Text>
|
||||
</YStack>
|
||||
@@ -116,16 +123,16 @@ function DefaultPanelRecordRenderer({ row }) {
|
||||
minWidth={160}
|
||||
flex={1}
|
||||
padding="$3"
|
||||
borderRadius="$4"
|
||||
borderRadius="$radiusMd"
|
||||
borderWidth={1}
|
||||
borderColor="$borderColor"
|
||||
backgroundColor="$background"
|
||||
borderColor="$lineSubtle"
|
||||
backgroundColor="$bgPanel"
|
||||
gap="$1"
|
||||
>
|
||||
<Text fontSize="$3" color="$color" opacity={0.65}>
|
||||
<Text fontSize="$3" color="$textMuted">
|
||||
{column.label}
|
||||
</Text>
|
||||
<Text>{formatValueByColumn(row?.[column.field], column)}</Text>
|
||||
<Text color="$textPrimary">{formatValueByColumn(row?.[column.field], column)}</Text>
|
||||
</YStack>
|
||||
))}
|
||||
</XStack>
|
||||
@@ -156,7 +163,7 @@ export function PanelFooterStatusBar({ text, visible = true }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Text color="$color" opacity={0.7}>
|
||||
<Text color="$textMuted">
|
||||
{text || grid.statusText}
|
||||
</Text>
|
||||
);
|
||||
@@ -179,10 +186,10 @@ export function PanelHeader({ title, toolbarItems = [], visible = true, showDivi
|
||||
padding="$3"
|
||||
minHeight={64}
|
||||
borderBottomWidth={showDivider ? 1 : 0}
|
||||
borderBottomColor="$borderColor"
|
||||
backgroundColor="$accentSurface"
|
||||
borderBottomColor="$lineSubtle"
|
||||
backgroundColor="$bgPanel"
|
||||
>
|
||||
<Text fontSize="$6" fontWeight="700" color="$accentColor">
|
||||
<Text {...getTypographyRoleProps('panelTitle', { fontSize: '$6' })}>
|
||||
{title}
|
||||
</Text>
|
||||
|
||||
@@ -192,17 +199,19 @@ export function PanelHeader({ title, toolbarItems = [], visible = true, showDivi
|
||||
size="$3"
|
||||
chromeless
|
||||
circular
|
||||
icon={RefreshIcon ? <RefreshIcon size={16} /> : undefined}
|
||||
icon={RefreshIcon ? <RefreshIcon size="sm" color="$textSecondary" /> : undefined}
|
||||
onPress={grid.reload}
|
||||
/>
|
||||
<Button
|
||||
size="$3"
|
||||
chromeless
|
||||
circular
|
||||
disabled={!grid.close}
|
||||
icon={CloseIcon ? <CloseIcon size={16} /> : undefined}
|
||||
onPress={grid.close}
|
||||
/>
|
||||
{grid.closeable !== false ? (
|
||||
<Button
|
||||
size="$3"
|
||||
chromeless
|
||||
circular
|
||||
disabled={!grid.close}
|
||||
icon={CloseIcon ? <CloseIcon size="sm" color="$textSecondary" /> : undefined}
|
||||
onPress={grid.close}
|
||||
/>
|
||||
) : null}
|
||||
</XStack>
|
||||
</XStack>
|
||||
);
|
||||
@@ -221,8 +230,8 @@ export function PanelFooter({ toolbarItems = [], visible = true }) {
|
||||
padding="$3"
|
||||
minHeight={56}
|
||||
borderTopWidth={1}
|
||||
borderTopColor="$borderColor"
|
||||
backgroundColor="$background"
|
||||
borderTopColor="$lineSubtle"
|
||||
backgroundColor="$bgPanel"
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<PanelFooterStatusBar />
|
||||
@@ -243,9 +252,11 @@ export function PanelBodyView({
|
||||
}
|
||||
|
||||
if (grid.error) {
|
||||
const ErrorIcon = getIcon('error');
|
||||
return (
|
||||
<YStack flex={1} alignItems="center" justifyContent="center" padding="$5">
|
||||
<Text color="#b91c1c">{grid.error}</Text>
|
||||
<YStack flex={1} alignItems="center" justifyContent="center" padding="$5" gap="$2">
|
||||
{ErrorIcon ? <ErrorIcon size="lg" color="$danger" /> : null}
|
||||
<Text color="$danger" fontWeight="600">{grid.error}</Text>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
@@ -253,15 +264,18 @@ export function PanelBodyView({
|
||||
if (grid.isLoading && !grid.rows.length) {
|
||||
return (
|
||||
<YStack flex={1} alignItems="center" justifyContent="center" padding="$5">
|
||||
<Text color="$color" opacity={0.7}>Loading cards...</Text>
|
||||
<Text color="$textMuted">Loading cards...</Text>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
|
||||
if (!grid.rows.length) {
|
||||
const EmptyIcon = getIcon('folder');
|
||||
return (
|
||||
<YStack flex={1} alignItems="center" justifyContent="center" padding="$5">
|
||||
<Text color="$color" opacity={0.7}>No records available.</Text>
|
||||
<YStack flex={1} alignItems="center" justifyContent="center" padding="$5" gap="$2">
|
||||
{EmptyIcon ? <EmptyIcon size="xl" color="$textMuted" /> : null}
|
||||
<Text color="$textSecondary" fontWeight="600">No records available</Text>
|
||||
<Text color="$textMuted" fontSize="$3">There's nothing to show here yet.</Text>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
@@ -272,37 +286,49 @@ export function PanelBodyView({
|
||||
<ScrollView flex={1}>
|
||||
<YStack padding="$4" gap="$3">
|
||||
<XStack gap="$3" flexWrap="wrap">
|
||||
{grid.rows.map((row) => (
|
||||
<YStack
|
||||
key={row.id}
|
||||
minWidth={responsiveColumns > 1 ? 320 : 240}
|
||||
flex={1}
|
||||
flexBasis={responsiveColumns > 1 ? '48%' : '100%'}
|
||||
padding="$4"
|
||||
borderWidth={1}
|
||||
borderColor={grid.selectedIds.has(row.id) ? '$accentBorder' : '$borderColor'}
|
||||
backgroundColor={grid.selectedIds.has(row.id) ? '$accentSurface' : '$background'}
|
||||
borderRadius="$5"
|
||||
gap="$3"
|
||||
>
|
||||
{grid.selectable ? (
|
||||
<XStack justifyContent="flex-start">
|
||||
<Checkbox
|
||||
checked={grid.selectedIds.has(row.id)}
|
||||
onCheckedChange={() => grid.toggleSelectRow(row.id)}
|
||||
>
|
||||
<Checkbox.Indicator />
|
||||
</Checkbox>
|
||||
</XStack>
|
||||
) : null}
|
||||
{grid.rows.map((row) => {
|
||||
const isSelected = grid.selectedIds.has(row.id);
|
||||
return (
|
||||
<YStack
|
||||
key={row.id}
|
||||
minWidth={responsiveColumns > 1 ? 320 : 240}
|
||||
flex={1}
|
||||
flexBasis={responsiveColumns > 1 ? '48%' : '100%'}
|
||||
padding="$4"
|
||||
borderWidth={1}
|
||||
borderColor={isSelected ? '$accent' : '$lineSubtle'}
|
||||
backgroundColor={isSelected ? '$accentBg' : '$bgPanel'}
|
||||
borderRadius="$radiusLg"
|
||||
gap="$3"
|
||||
hoverStyle={isSelected ? undefined : { borderColor: '$lineStrong' }}
|
||||
>
|
||||
{grid.selectable ? (
|
||||
<XStack justifyContent="flex-start">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={() => grid.toggleSelectRow(row.id)}
|
||||
borderColor="$lineStrong"
|
||||
backgroundColor="$bgPanel"
|
||||
focusStyle={{ borderColor: '$accent' }}
|
||||
>
|
||||
<Checkbox.Indicator>
|
||||
{(() => {
|
||||
const Check = getIcon('check');
|
||||
return Check ? <Check size="sm" color="$accent" /> : null;
|
||||
})()}
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox>
|
||||
</XStack>
|
||||
) : null}
|
||||
|
||||
<RecordRenderer row={row} />
|
||||
</YStack>
|
||||
))}
|
||||
<RecordRenderer row={row} />
|
||||
</YStack>
|
||||
);
|
||||
})}
|
||||
</XStack>
|
||||
|
||||
{grid.isLoading ? (
|
||||
<Text color="$color" opacity={0.7}>
|
||||
<Text color="$textMuted">
|
||||
Refreshing records...
|
||||
</Text>
|
||||
) : null}
|
||||
@@ -310,4 +336,3 @@ export function PanelBodyView({
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user