dns dashboard: mobile usability — zone-name truncate, DataTable text-node DRY fix, 44px tap targets, taller back/close
This commit is contained in:
@@ -457,9 +457,9 @@ export function DnsModule(_props: { params: Record<string, string> }) {
|
||||
key: 'name',
|
||||
header: 'Zone',
|
||||
render: (z) => (
|
||||
<XStack items="center" gap="$2">
|
||||
<XStack items="center" gap="$2" flex={1} minW={0}>
|
||||
<Globe size={14} color="#9a9a9a" />
|
||||
<Text fontSize="$3" color="$color12" numberOfLines={1}>{displayZone(z.name)}</Text>
|
||||
<Text fontSize="$3" color="$color12" numberOfLines={1} flex={1} minW={0}>{displayZone(z.name)}</Text>
|
||||
</XStack>
|
||||
),
|
||||
},
|
||||
@@ -479,13 +479,15 @@ export function DnsModule(_props: { params: Record<string, string> }) {
|
||||
{
|
||||
key: 'actions',
|
||||
header: '',
|
||||
width: 92,
|
||||
width: 108,
|
||||
align: 'right',
|
||||
// ≥44px hit areas + a wide Edit↔Delete gap so the destructive Delete is hard
|
||||
// to mis-tap on touch (the icon glyph stays small; the tap target is 44×44).
|
||||
render: (r) =>
|
||||
active ? (
|
||||
<XStack gap="$1" justify="flex-end">
|
||||
<Button size="$2" chromeless icon={<Pencil size={15} />} aria-label="Edit record" onPress={() => setDialog({ kind: 'editRecord', zone: active, record: r })} />
|
||||
<Button size="$2" chromeless icon={<Trash2 size={15} />} aria-label="Delete record" onPress={() => setDialog({ kind: 'deleteRecord', zone: active, record: r })} />
|
||||
<XStack gap="$3" justify="flex-end">
|
||||
<Button chromeless width={44} height={44} icon={<Pencil size={15} />} aria-label="Edit record" onPress={() => setDialog({ kind: 'editRecord', zone: active, record: r })} />
|
||||
<Button chromeless width={44} height={44} icon={<Trash2 size={15} />} aria-label="Delete record" onPress={() => setDialog({ kind: 'deleteRecord', zone: active, record: r })} />
|
||||
</XStack>
|
||||
) : null,
|
||||
},
|
||||
@@ -496,7 +498,7 @@ export function DnsModule(_props: { params: Record<string, string> }) {
|
||||
return (
|
||||
<YStack gap="$4" p="$4">
|
||||
{inZone ? (
|
||||
<Button chromeless size="$2" icon={<ArrowLeft size={15} />} self="flex-start" onPress={backToZones}>
|
||||
<Button chromeless size="$3" minH={44} icon={<ArrowLeft size={15} />} self="flex-start" onPress={backToZones}>
|
||||
All zones
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
@@ -112,30 +112,41 @@ export function DataTable<T>({
|
||||
cursor={onRowPress ? 'pointer' : undefined}
|
||||
onPress={onRowPress ? () => onRowPress(row) : undefined}
|
||||
>
|
||||
{columns.map((c) => (
|
||||
<YStack
|
||||
key={c.key}
|
||||
width={c.width}
|
||||
flex={c.width ? undefined : 1}
|
||||
minW={c.width ? undefined : FLEX_MIN_COL_W}
|
||||
justify="center"
|
||||
items={c.align === 'right' ? 'flex-end' : 'flex-start'}
|
||||
>
|
||||
{c.render ? (
|
||||
c.render(row)
|
||||
) : (
|
||||
<Text
|
||||
fontSize="$3"
|
||||
numberOfLines={1}
|
||||
color="$color12"
|
||||
text={c.align === 'right' ? 'right' : 'left'}
|
||||
className={c.mono ? 'hz-mono' : undefined}
|
||||
>
|
||||
{String((row as Record<string, unknown>)[c.key] ?? '')}
|
||||
</Text>
|
||||
)}
|
||||
</YStack>
|
||||
))}
|
||||
{columns.map((c) => {
|
||||
// A cell value is either a column's rendered node or the raw
|
||||
// `String(row[key])`. Whenever it resolves to a PRIMITIVE
|
||||
// (string|number) — the default cell OR a `render()` that returns
|
||||
// `String(count)` / `ttl` / `priority` — it is wrapped in the ONE
|
||||
// styled Text below, so the primitive can never emit a bare text
|
||||
// node (illegal under a native View → RN red-box + console spam)
|
||||
// and always picks up the numeric mono/align styling. Element
|
||||
// renders (Text/badges/cells) pass through untouched.
|
||||
const cell = c.render ? c.render(row) : String((row as Record<string, unknown>)[c.key] ?? '')
|
||||
return (
|
||||
<YStack
|
||||
key={c.key}
|
||||
width={c.width}
|
||||
flex={c.width ? undefined : 1}
|
||||
minW={c.width ? undefined : FLEX_MIN_COL_W}
|
||||
justify="center"
|
||||
items={c.align === 'right' ? 'flex-end' : 'flex-start'}
|
||||
>
|
||||
{typeof cell === 'string' || typeof cell === 'number' ? (
|
||||
<Text
|
||||
fontSize="$3"
|
||||
numberOfLines={1}
|
||||
color="$color12"
|
||||
text={c.align === 'right' ? 'right' : 'left'}
|
||||
className={c.mono ? 'hz-mono' : undefined}
|
||||
>
|
||||
{cell}
|
||||
</Text>
|
||||
) : (
|
||||
cell
|
||||
)}
|
||||
</YStack>
|
||||
)
|
||||
})}
|
||||
</XStack>
|
||||
))}
|
||||
</YStack>
|
||||
|
||||
@@ -186,7 +186,7 @@ export function SlideOver({
|
||||
{title}
|
||||
</Text>
|
||||
{headerRight}
|
||||
<Button size="$2" chromeless icon={<X size={18} />} onPress={onClose} aria-label="Close" />
|
||||
<Button chromeless width={44} height={44} icon={<X size={18} />} onPress={onClose} aria-label="Close" />
|
||||
</XStack>
|
||||
<ScrollView flex={1}>
|
||||
<YStack flex={1} p="$4" gap="$3">
|
||||
|
||||
Reference in New Issue
Block a user