Update bface UI and security work

This commit is contained in:
Amer Agovic
2026-05-31 12:30:02 -05:00
parent 6fe23fae86
commit c6f7240912
45 changed files with 4531 additions and 553 deletions
+64 -71
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useRef } from 'react';
import { Button, Checkbox, ScrollView, Separator, Text, XStack, YStack } from 'tamagui';
import { getIcon } from '../IconMapper.jsx';
import { PageNavBar } from '../PageNavBar.jsx';
import { useGridView } from './context.js';
import { getTypographyRoleProps } from '../../styles/index.js';
import {
@@ -98,6 +99,16 @@ export function TableHeader({ visible = true, showTopBorder = true }) {
const selectedCount = allIds.filter((id) => grid.selectedIds.has(id)).length;
const allSelected = allIds.length > 0 && selectedCount === allIds.length;
const someSelected = selectedCount > 0 && !allSelected;
const getNextSortDirection = (column) => {
const activeSort = grid.sortBy.find((entry) => entry.field === column.field);
if (!activeSort) {
return 'ascending';
}
if (activeSort.direction === 'asc') {
return 'descending';
}
return 'none';
};
return (
<XStack
@@ -105,8 +116,11 @@ export function TableHeader({ visible = true, showTopBorder = true }) {
borderTopWidth={showTopBorder ? 1 : 0}
borderBottomWidth={1}
borderColor="$lineSubtle"
backgroundColor="transparent"
backgroundColor="$bgPanel"
paddingHorizontal="$2"
paddingTop="$1"
paddingBottom="$1"
gap="$1"
>
{grid.selectable || grid.nested ? (
<XStack width={36} alignItems="center" justifyContent="center">
@@ -132,41 +146,46 @@ export function TableHeader({ visible = true, showTopBorder = true }) {
const activeSort = grid.sortBy.find((entry) => entry.field === column.field);
const isActive = Boolean(activeSort);
const direction = activeSort?.direction;
const sortButtonLabel = `Sort ${column.label} ${getNextSortDirection(column)}`;
const ChevronIcon = isActive
? (direction === 'asc' ? CaretUp : CaretDown)
: CaretDown;
const iconColor = isActive ? '$textSecondary' : '$textMuted';
return (
<Button
<XStack
key={column.field}
chromeless
disabled={!column.sortable}
onPress={() => column.sortable && grid.toggleSort(column.field)}
justifyContent={getColumnJustify(column.align)}
alignItems="center"
paddingVertical="$3"
minHeight={44}
paddingHorizontal="$2"
paddingVertical="$2"
gap="$2"
{...getColumnLayoutStyle(column)}
hoverStyle={column.sortable ? { backgroundColor: '$bgPage' } : undefined}
pressStyle={column.sortable ? { backgroundColor: '$bgPanelElev' } : undefined}
>
<XStack width="100%" alignItems="center" justifyContent={getColumnJustify(column.align)} gap="$2">
<Text
width="auto"
textAlign={column.align || 'left'}
numberOfLines={1}
{...getTypographyRoleProps('tableHeader')}
>
{column.label}
</Text>
{column.sortable ? (
isActive ? (
direction === 'asc'
? (CaretUp ? <CaretUp size="xs" color="$textSecondary" /> : null)
: (CaretDown ? <CaretDown size="xs" color="$textSecondary" /> : null)
) : (
CaretDown ? <CaretDown size="xs" color="$textMuted" style={{ opacity: 0.6 }} /> : null
)
) : null}
</XStack>
</Button>
<Text
flex={1}
minWidth={0}
textAlign={column.align || 'left'}
numberOfLines={1}
{...getTypographyRoleProps('tableHeader')}
>
{column.label}
</Text>
{column.sortable ? (
<Button
chromeless
circular
size="$2"
flexShrink={0}
aria-label={sortButtonLabel}
onPress={() => grid.toggleSort(column.field)}
hoverStyle={{ backgroundColor: '$bgPage' }}
pressStyle={{ backgroundColor: '$bgPanelElev' }}
icon={ChevronIcon ? <ChevronIcon size="xs" color={iconColor} /> : undefined}
/>
) : null}
</XStack>
);
})}
</XStack>
@@ -247,10 +266,6 @@ export function TableBodyView({ visible = true }) {
export function TableFooter({ visible = true }) {
const grid = useGridView();
const FirstPageIcon = getIcon('first-page');
const PreviousPageIcon = getIcon('chevron-left');
const NextPageIcon = getIcon('chevron-right');
const LastPageIcon = getIcon('last-page');
if (visible === false) {
return null;
@@ -261,8 +276,11 @@ export function TableFooter({ visible = true }) {
alignItems="center"
justifyContent="space-between"
gap="$3"
padding="$3"
minHeight={56}
paddingTop="$1"
paddingRight="$3"
paddingBottom="$1"
paddingLeft="$3"
minHeight={64}
borderTopWidth={1}
borderTopColor="$lineSubtle"
backgroundColor="$bgPanel"
@@ -272,43 +290,18 @@ export function TableFooter({ visible = true }) {
{grid.total} records
</Text>
<XStack gap="$1" alignItems="center" flexWrap="wrap" padding="$1" borderWidth={1} borderColor="$lineSubtle" borderRadius="$radiusMd" backgroundColor="$bgPanel">
<Button
size="$3"
chromeless
circular
disabled={grid.currentPage <= 1}
icon={FirstPageIcon ? <FirstPageIcon size="sm" color="$textSecondary" /> : undefined}
onPress={() => grid.setPage(1)}
/>
<Button
size="$3"
chromeless
circular
disabled={grid.currentPage <= 1}
icon={PreviousPageIcon ? <PreviousPageIcon size="sm" color="$textSecondary" /> : undefined}
onPress={() => grid.setPage(grid.currentPage - 1)}
/>
<Text color="$textSecondary">
Page {grid.currentPage} of {grid.pageCount}
</Text>
<Button
size="$3"
chromeless
circular
disabled={grid.currentPage >= grid.pageCount}
icon={NextPageIcon ? <NextPageIcon size="sm" color="$textSecondary" /> : undefined}
onPress={() => grid.setPage(grid.currentPage + 1)}
/>
<Button
size="$3"
chromeless
circular
disabled={grid.currentPage >= grid.pageCount}
icon={LastPageIcon ? <LastPageIcon size="sm" color="$textSecondary" /> : undefined}
onPress={() => grid.setPage(grid.pageCount)}
/>
</XStack>
<PageNavBar
outlined={false}
label={`Page ${grid.currentPage} of ${grid.pageCount}`}
onFirstPage={() => grid.setPage(1)}
onPreviousPage={() => grid.setPage(grid.currentPage - 1)}
onNextPage={() => grid.setPage(grid.currentPage + 1)}
onLastPage={() => grid.setPage(grid.pageCount)}
firstDisabled={grid.currentPage <= 1}
previousDisabled={grid.currentPage <= 1}
nextDisabled={grid.currentPage >= grid.pageCount}
lastDisabled={grid.currentPage >= grid.pageCount}
/>
</XStack>
);
}