Skip to content

Commit fce50a1

Browse files
refactor: code optimization and better UX (#75)
* feat: implement theme provider and mode toggle for light/dark mode support * fix: update icon color from 'text-secondary' to 'text-foreground' in multiple preview tables * feat: update styles for improved UI consistency across components * fix: reduce transition duration for improved responsiveness across components * feat: enhance ModeToggle component with system theme option and improve button styling * feat: update borderTypeColor to include background colors and dark mode support * feat: enhance CopyButton tooltip styling and improve TableBody dark mode support * feat: update component styles for improved UI consistency and responsiveness * refactor: simplify header layout in account route component * feat: update brand and muted foreground colors for improved accessibility * feat: update InteractiveJsonViewer styles for improved color consistency and accessibility * feat: update AddressChip button styles for improved visual consistency and accessibility; refine SchemaSearch layout for better readability * feat: update ring color variable to use primary color for improved theming consistency * refactor: update Tabs component layout and styling for improved consistency * feat: implement dynamic tab indicator positioning and styling * refactor: improve SchemaSearch component layout and button text for clarity * feat: add default JSON view styles and integrate into JsonBlock and InteractiveJsonViewer components * refactor: simplify Button component structure in SmartLinkGroup * feat: enhance DataTable with row click navigation and keyboard accessibility * fix: address or id not showing on small screen * refactor: remove unused imports from account layout component * refactor: update primary foreground color in Tabs component and CSS * fix: adjust layout styles for improved responsiveness in account component * refactor: move My Activity button from SearcherBar to Navbar * fix: app address overflow * fix: better navbar responsible * fix: remove background and border styles from Footer component * fix: remove unnecessary comments in JsonBlock and DealsRoute components
1 parent c22bf3d commit fce50a1

File tree

14 files changed

+192
-106
lines changed

14 files changed

+192
-106
lines changed

src/components/DataTable.tsx

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useNavigate, useParams } from '@tanstack/react-router';
12
import {
23
ColumnDef,
34
flexRender,
@@ -12,7 +13,6 @@ import {
1213
TableHeader,
1314
TableRow,
1415
} from '@/components/ui/table';
15-
import { ChainLink } from './ChainLink';
1616

1717
interface DataTableProps<TData, TValue> {
1818
columns: ColumnDef<TData, TValue>[];
@@ -27,12 +27,46 @@ export function DataTable<TData, TValue>({
2727
tableLength = 10,
2828
isLoading,
2929
}: DataTableProps<TData, TValue>) {
30+
const { chainSlug } = useParams({ from: '/$chainSlug' });
31+
const navigate = useNavigate();
32+
3033
const table = useReactTable({
3134
data,
3235
columns,
3336
getCoreRowModel: getCoreRowModel(),
3437
});
3538

39+
const handleRowClick = (
40+
destination: string,
41+
event: React.MouseEvent | React.KeyboardEvent
42+
) => {
43+
// Don't navigate if clicking on buttons, links, or other interactive elements
44+
const target = event.target as HTMLElement;
45+
if (
46+
target.tagName === 'BUTTON' ||
47+
target.tagName === 'A' ||
48+
target.closest('button, a')
49+
) {
50+
return;
51+
}
52+
53+
const path =
54+
destination === '/'
55+
? `/${chainSlug}`
56+
: `/${chainSlug}${destination.startsWith('/') ? destination : `/${destination}`}`;
57+
navigate({ to: path });
58+
};
59+
60+
const handleRowKeyDown = (
61+
destination: string,
62+
event: React.KeyboardEvent
63+
) => {
64+
if (event.key === 'Enter' || event.key === ' ') {
65+
event.preventDefault();
66+
handleRowClick(destination, event);
67+
}
68+
};
69+
3670
return (
3771
<Table>
3872
<TableHeader>
@@ -54,22 +88,27 @@ export function DataTable<TData, TValue>({
5488
))}
5589
</TableHeader>
5690
<TableBody>
57-
{table.getRowModel().rows.map((row) => (
58-
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
59-
{row.getVisibleCells().map((cell) => (
60-
<TableCell key={cell.id}>
61-
<ChainLink
62-
to={
63-
(cell.row.original as { destination: string }).destination
64-
}
65-
className="w-full"
66-
>
91+
{table.getRowModel().rows.map((row) => {
92+
const destination = (row.original as { destination: string })
93+
.destination;
94+
95+
return (
96+
<TableRow
97+
key={row.id}
98+
data-state={row.getIsSelected() && 'selected'}
99+
className="cursor-pointer"
100+
tabIndex={0}
101+
onClick={(e) => handleRowClick(destination, e)}
102+
onKeyDown={(e) => handleRowKeyDown(destination, e)}
103+
>
104+
{row.getVisibleCells().map((cell) => (
105+
<TableCell key={cell.id}>
67106
{flexRender(cell.column.columnDef.cell, cell.getContext())}
68-
</ChainLink>
69-
</TableCell>
70-
))}
71-
</TableRow>
72-
))}
107+
</TableCell>
108+
))}
109+
</TableRow>
110+
);
111+
})}
73112
{data.length === 0 && !isLoading && (
74113
<TableRow>
75114
<TableCell colSpan={columns.length} className="text-center">

src/components/Footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function Footer({ className }: { className?: string }) {
7373
return (
7474
<footer
7575
className={cn(
76-
'bg-popover border-muted text-intermediate-foreground flex flex-col gap-6 rounded-3xl border px-6 py-10 sm:px-10 lg:px-20',
76+
'text-intermediate-foreground flex flex-col gap-6 py-10',
7777
className
7878
)}
7979
>

src/components/SmartLinkGroup.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ export default function SmartLinkGroup({
8080
>
8181
<span className="hidden md:inline">{label ?? addressOrId}</span>
8282
<span className="inline md:hidden">
83-
{(label ? truncateAddress(label) : '') ?? addressOrId}
83+
{(label
84+
? truncateAddress(label)
85+
: truncateAddress(addressOrId)) ?? addressOrId}
8486
</span>
8587
{ens ? `(${ens})` : ''}
8688
</Link>
@@ -89,7 +91,8 @@ export default function SmartLinkGroup({
8991
<div>
9092
<span className="hidden md:inline">{label ?? addressOrId}</span>
9193
<span className="inline md:hidden">
92-
{(label ? truncateAddress(label) : '') ?? addressOrId}
94+
{(label ? truncateAddress(label) : truncateAddress(addressOrId)) ??
95+
addressOrId}
9396
</span>
9497
{ens ? `(${ens})` : ''}
9598
</div>

src/components/navbar/NavBar.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { AddressChip } from './AddressChip.tsx';
1010
import { ChainSelector } from './ChainSelector.tsx';
1111

1212
export function Navbar() {
13-
const { isConnected, address } = useUserStore();
13+
const { isConnected, address: userAddress } = useUserStore();
1414
const { logout, login } = useLoginLogout();
1515
const [isMenuOpen, setMenuOpen] = useState(false);
1616
const handleMenuToggle = () => {
@@ -21,11 +21,16 @@ export function Navbar() {
2121
<nav className="flex items-center justify-between py-6">
2222
<ChainLink to="/" className="-m-2 flex items-center gap-2 p-2 font-mono">
2323
<img src={iExecLogo} width="25" height="25" alt="iExec logo" />
24-
<span className="hidden sm:block">iExec Explorer</span>
24+
<span className="hidden lg:block">iExec Explorer</span>
2525
</ChainLink>
2626
<div className="mr-8 flex items-center gap-4 md:mr-0">
2727
{isConnected && (
2828
<div className="hidden md:flex">
29+
<Button variant="link" asChild className="text-foreground">
30+
<ChainLink to={`/address/${userAddress}?from=my_activity`}>
31+
My activity
32+
</ChainLink>
33+
</Button>
2934
<Button variant="link" asChild className="text-foreground">
3035
<ChainLink to="/account">iExec Account</ChainLink>
3136
</Button>
@@ -38,7 +43,7 @@ export function Navbar() {
3843
</div>
3944
{isConnected ? (
4045
<div className="flex max-w-[1260px] items-center gap-2">
41-
<AddressChip address={address!} />
46+
<AddressChip address={userAddress!} />
4247

4348
<button
4449
type="button"
@@ -81,7 +86,7 @@ export function Navbar() {
8186
</ChainLink>
8287
{isConnected ? (
8388
<div className="flex max-w-[1260px] items-center gap-2">
84-
<AddressChip address={address!} />
89+
<AddressChip address={userAddress!} />
8590

8691
<button
8792
type="button"

src/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
--brand-foreground: var(--color-grey-900);
260260

261261
--primary: #c0cbff;
262-
--primary-foreground: #f4f7fc;
262+
--primary-foreground: var(--color-muted);
263263

264264
--secondary: var(--color-grey-600);
265265
--secondary-foreground: var(--color-grey-50);

src/modules/JsonBlock.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,55 @@
1+
import JsonView from '@uiw/react-json-view';
12
import CopyButton from '@/components/CopyButton';
3+
import { defaultJsonViewStyle } from '@/utils/jsonViewStyles';
24

35
type JsonBlockProps = {
46
children: string | object;
7+
className?: string;
8+
collapsed?: number;
9+
enableClipboard?: boolean;
510
};
611

7-
const JsonBlock = ({ children }: JsonBlockProps) => {
8-
let display: string;
12+
const JsonBlock = ({
13+
children,
14+
className,
15+
collapsed = 1,
16+
enableClipboard = true,
17+
}: JsonBlockProps) => {
18+
let jsonData: object;
919
let rawToCopy: string;
1020

1121
try {
1222
if (typeof children === 'object') {
23+
jsonData = children;
1324
rawToCopy = JSON.stringify(children);
14-
display = JSON.stringify(children, null, 2);
1525
} else {
26+
jsonData = JSON.parse(children);
1627
rawToCopy = children;
17-
display = JSON.stringify(JSON.parse(children), null, 2);
1828
}
1929
} catch {
20-
display = String(children);
21-
rawToCopy = String(children);
30+
return (
31+
<div className={`flex min-w-0 items-start gap-1 ${className}`}>
32+
<code className="min-w-0 overflow-x-auto text-sm whitespace-pre">
33+
{String(children)}
34+
</code>
35+
{enableClipboard && <CopyButton textToCopy={String(children)} />}
36+
</div>
37+
);
2238
}
2339

2440
return (
25-
<div className="flex min-w-0 items-start gap-1">
26-
<code className="min-w-0 overflow-x-auto text-sm whitespace-pre">
27-
{display}
28-
</code>
29-
<CopyButton textToCopy={rawToCopy} />
41+
<div className={`flex min-w-0 items-start gap-1 ${className}`}>
42+
<div className="min-w-0 flex-1 overflow-x-auto">
43+
<JsonView
44+
value={jsonData}
45+
displayDataTypes={false}
46+
displayObjectSize={false}
47+
enableClipboard={false}
48+
collapsed={collapsed}
49+
style={defaultJsonViewStyle}
50+
/>
51+
</div>
52+
{enableClipboard && <CopyButton textToCopy={rawToCopy} />}
3053
</div>
3154
);
3255
};

src/modules/Tabs.tsx

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import clsx from 'clsx';
1+
import { cn } from '@/lib/utils';
2+
import { useEffect, useRef, useState } from 'react';
23
import { Button } from '@/components/ui/button';
34
import {
45
Tooltip,
@@ -21,24 +22,65 @@ export function Tabs({
2122
disabledTabs = [],
2223
disabledReasons = {},
2324
}: TabsProps) {
25+
const containerRef = useRef<HTMLDivElement>(null);
26+
const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 });
27+
const [isInitialized, setIsInitialized] = useState(false);
28+
29+
useEffect(() => {
30+
if (!containerRef.current) return;
31+
32+
const activeButton = containerRef.current.querySelector(
33+
`[data-tab-index="${currentTab}"]`
34+
) as HTMLElement;
35+
if (!activeButton) return;
36+
37+
const containerRect = containerRef.current.getBoundingClientRect();
38+
const buttonRect = activeButton.getBoundingClientRect();
39+
40+
const left = buttonRect.left - containerRect.left;
41+
const width = buttonRect.width;
42+
43+
setIndicatorStyle({ left, width });
44+
45+
if (!isInitialized) {
46+
setIsInitialized(true);
47+
}
48+
}, [currentTab, isInitialized]);
49+
2450
return (
25-
<div className="-mb-4 flex w-full items-center gap-6 overflow-x-auto pb-4">
51+
<div
52+
ref={containerRef}
53+
className="bg-primary-foreground relative flex w-fit max-w-full items-center gap-1 overflow-x-auto rounded-full p-1"
54+
>
55+
<div
56+
className={cn(
57+
'bg-background border-border absolute top-1 bottom-1 z-0 rounded-full border transition-all duration-300 ease-out',
58+
!isInitialized && 'opacity-0'
59+
)}
60+
style={{
61+
left: `${indicatorStyle.left}px`,
62+
width: `${indicatorStyle.width}px`,
63+
}}
64+
/>
65+
2666
{tabLabels.map((label, index) => {
2767
const isDisabled = disabledTabs.includes(index);
2868
const reason = disabledReasons[index];
2969

3070
const button = (
3171
<Button
3272
key={label}
33-
variant={
34-
currentTab === index
35-
? 'gradient-outline-active'
36-
: 'gradient-outline'
37-
}
73+
data-tab-index={index}
74+
variant="link"
75+
size={'none'}
3876
onClick={() => {
3977
if (!isDisabled) onTabChange(index);
4078
}}
41-
className={clsx(isDisabled && 'cursor-not-allowed opacity-50')}
79+
className={cn(
80+
'text-foreground relative z-10 border border-transparent px-8 py-2 transition-colors duration-300 hover:no-underline',
81+
isDisabled && 'cursor-not-allowed opacity-50',
82+
currentTab === index && 'text-primary'
83+
)}
4284
>
4385
{label.toUpperCase()}
4486
</Button>

src/modules/datasets/SchemaSearch.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ export function SchemaSearch({
4949
className={cn('flex w-full items-center gap-2 px-10 py-6 duration-200')}
5050
onClick={() => setIsOpen(!isOpen)}
5151
>
52-
<SlidersHorizontal size={16} />
53-
<p className="flex-1 text-left font-bold">
54-
Schema Search{' '}
55-
<span className="text-muted-foreground font-normal">
52+
<div>
53+
<div className="flex items-center gap-2">
54+
<SlidersHorizontal size={16} />
55+
<p className="text-left font-bold">Schema Search </p>
56+
</div>
57+
<p className="text-muted-foreground mt-2 font-normal">
5658
Add field types to filter datasets by their schema structure.
57-
Filters are applied automatically.
58-
</span>
59-
</p>
59+
</p>
60+
</div>
6061
<ChevronDown
6162
className={cn(
6263
'ml-auto transition-transform',
@@ -164,7 +165,7 @@ export function SchemaSearch({
164165
onClick={handleAdd}
165166
disabled={!inputPathValue.trim() || !selectedType}
166167
>
167-
Add filter
168+
{filters.length > 0 ? 'Add filter' : 'Apply filter'}
168169
<Plus size={20} />
169170
</Button>
170171
</div>

src/modules/datasets/dataset/schema/InteractiveJsonViewer.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DatasetSchemaQuery } from '@/graphql/dataprotector/graphql';
22
import JsonView from '@uiw/react-json-view';
3+
import { defaultJsonViewStyle } from '@/utils/jsonViewStyles';
34

45
interface JsonViewerProps {
56
schemaPaths?: Array<{ path?: string | null; type?: string | null }>;
@@ -62,18 +63,7 @@ export function InteractiveJsonViewer({
6263
displayObjectSize={false}
6364
enableClipboard={false}
6465
collapsed={2}
65-
style={
66-
{
67-
color: 'var(--color-foreground)',
68-
'--w-rjv-key-string': 'var(--color-foreground)', // Override key color to light gray
69-
'--w-rjv-line-color': 'var(--color-foreground)',
70-
'--w-rjv-quotes-string-color': 'var(--color-primary)', // Override string color to light gray
71-
'--w-rjv-type-string-color': 'var(--color-primary)',
72-
'--w-rjv-curlybraces-color': '#728cff', // Override curly braces color to light gray
73-
'--w-rjv-quotes-color': '#728cff', // Override quotes color to light gray
74-
'--w-rjv-indent-width': '24px', // Increase indentation for nested keys
75-
} as React.CSSProperties
76-
}
66+
style={defaultJsonViewStyle}
7767
/>
7868
</div>
7969
);

0 commit comments

Comments
 (0)