Skip to content

Commit 8230865

Browse files
committed
refactor dropdown component add menu + additonal minor updates
1 parent e12bef4 commit 8230865

File tree

10 files changed

+207
-208
lines changed

10 files changed

+207
-208
lines changed

starter_app/src/app/[table]/page.tsx

Lines changed: 96 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,62 @@
22
import React, { useState, useMemo } from 'react';
33
import {TableContainer, Table, TableHeader, TableRow, TableBody, TableHead, TableCell} from '../components/Table';
44
import Dropdown from '../components/DropdownMenu';
5+
import Menu from '../components/Menu';
6+
import Checkbox from '../components/Checkbox';
7+
import RadioGroup from '../components/RadioGroup';
8+
import Button from '../components/Button';
59
import { FunnelIcon as Filter } from '@heroicons/react/24/solid';
610
import { ArrowsUpDownIcon as Sort } from '@heroicons/react/24/solid';
711
import SearchBar from '../components/SearchBox';
812

9-
const filterOptions = [
10-
{ itemLabel: 'Non-Discarded Variables', value: 'non-discarded', suffixText: '11' },
11-
{ itemLabel: 'Discarded Variables', value: 'discarded', suffixText: '7' },
12-
]
13+
const filterOptions = ['Non-Discarded Variables', 'Discarded Variables']
1314

14-
const sortOptions = [
15-
{ itemLabel: 'Name A-Z', value: 'name-asc' },
16-
{ itemLabel: 'Name Z-A', value: 'name-desc' },
17-
{ itemLabel: 'Non-Discarded Variables', value: 'non-discarded', defaultChecked: true },
18-
{ itemLabel: 'Discarded Variables', value: 'discarded' },
19-
]
15+
const sortOptions = ['Name A-Z', 'Name Z-A', 'Non-Discarded Variables', 'Discarded Variables']
2016

21-
interface RowData {
22-
name: string;
23-
steps: (string | null)[];
24-
}
17+
interface RowData {
18+
name: string;
19+
steps: (string | null)[];
20+
}
2521

26-
const initialData: RowData[] = [
27-
{ name: 'tx',
28-
steps: ['123', '456', '789', '101', '112', '131', '415', '161', '718', '192'] },
29-
{ name: 'datum',
30-
steps: ['datum1', 'datum2', 'datum3', 'datum4', 'datum5', 'datum6', 'datum7', 'datum8', 'datum9', 'datum10'] },
31-
{ name: 'redeemer',
32-
steps: ['redeemer1', 'redeemer2', 'redeemer3', 'redeemer4', 'redeemer5', 'redeemer6', 'redeemer7', 'redeemer8', 'redeemer9', 'redeemer10'] },
33-
{ name: 'discarded1',
34-
steps: [ null , null, null, null, null, null, null, null, null, null] },
35-
{ name: 'value',
36-
steps: ['1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000', '10000'] },
37-
{ name: 'script',
38-
steps: ['script1', 'script2', 'script3', 'script4', 'script5', 'script6', 'script7', 'script8', 'script9', 'script10'] },
39-
{ name: 'dataHash',
40-
steps: ['hash1', 'hash2', 'hash3', 'hash4', 'hash5', 'hash6', 'hash7', 'hash8', 'hash9', 'hash10'] },
41-
{ name: 'inlineDatum',
42-
steps: ['inlineDatum1', 'inlineDatum2', 'inlineDatum3', 'inlineDatum4', 'inlineDatum5', 'inlineDatum6', 'inlineDatum7', 'inlineDatum8', 'inlineDatum9', 'inlineDatum10'] },
43-
{ name: 'inlineScript',
44-
steps: ['inlineScript1', 'inlineScript2', 'inlineScript3', 'inlineScript4', 'inlineScript5', 'inlineScript6', 'inlineScript7', 'inlineScript8', 'inlineScript9', 'inlineScript10'] },
45-
{ name: 'discarded2',
46-
steps: [ null , null, null, null, null, null, null, null, null, null] },
47-
{ name: 'referenceScript',
48-
steps: ['referenceScript1', 'referenceScript2', 'referenceScript3', 'referenceScript4', 'referenceScript5', 'referenceScript6', 'referenceScript7', 'referenceScript8', 'referenceScript9', 'referenceScript10'] },
49-
{ name: 'address',
50-
steps: ['address1', 'address2', 'address3', 'address4', 'address5', 'address6', 'address7', 'address8', 'address9', 'address10'] },
51-
{ name: 'cert',
52-
steps: ['cert1', 'cert2', 'cert3', 'cert4', 'cert5', 'cert6', 'cert7', 'cert8', 'cert9', 'cert10'] },
53-
{ name: 'discarded3',
54-
steps: [ null , null, null, null, null, null, null, null, null, null] },
55-
{ name: 'discarded4',
56-
steps: [ null , null, null, null, null, null, null, null, null, null] },
57-
{ name: 'discarded5',
58-
steps: [ null , null, null, null, null, null, null, null, null, null] },
59-
]
22+
const initialData: RowData[] = [
23+
{ name: 'tx',
24+
steps: ['123', '456', '789', '101', '112', '131', '415', '161', '718', '192'] },
25+
{ name: 'datum',
26+
steps: ['datum1', 'datum2', 'datum3', 'datum4', 'datum5', 'datum6', 'datum7', 'datum8', 'datum9', 'datum10'] },
27+
{ name: 'redeemer',
28+
steps: ['redeemer1', 'redeemer2', 'redeemer3', 'redeemer4', 'redeemer5', 'redeemer6', 'redeemer7', 'redeemer8', 'redeemer9', 'redeemer10'] },
29+
{ name: 'discarded1',
30+
steps: [ null , null, null, null, null, null, null, null, null, null] },
31+
{ name: 'value',
32+
steps: ['1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000', '10000'] },
33+
{ name: 'script',
34+
steps: ['script1', 'script2', 'script3', 'script4', 'script5', 'script6', 'script7', 'script8', 'script9', 'script10'] },
35+
{ name: 'dataHash',
36+
steps: ['hash1', 'hash2', 'hash3', 'hash4', 'hash5', 'hash6', 'hash7', 'hash8', 'hash9', 'hash10'] },
37+
{ name: 'inlineDatum',
38+
steps: ['inlineDatum1', 'inlineDatum2', 'inlineDatum3', 'inlineDatum4', 'inlineDatum5', 'inlineDatum6', 'inlineDatum7', 'inlineDatum8', 'inlineDatum9', 'inlineDatum10'] },
39+
{ name: 'inlineScript',
40+
steps: ['inlineScript1', 'inlineScript2', 'inlineScript3', 'inlineScript4', 'inlineScript5', 'inlineScript6', 'inlineScript7', 'inlineScript8', 'inlineScript9', 'inlineScript10'] },
41+
{ name: 'discarded2',
42+
steps: [ null , null, null, null, null, null, null, null, null, null] },
43+
{ name: 'referenceScript',
44+
steps: ['referenceScript1', 'referenceScript2', 'referenceScript3', 'referenceScript4', 'referenceScript5', 'referenceScript6', 'referenceScript7', 'referenceScript8', 'referenceScript9', 'referenceScript10'] },
45+
{ name: 'address',
46+
steps: ['address1', 'address2', 'address3', 'address4', 'address5', 'address6', 'address7', 'address8', 'address9', 'address10'] },
47+
{ name: 'cert',
48+
steps: ['cert1', 'cert2', 'cert3', 'cert4', 'cert5', 'cert6', 'cert7', 'cert8', 'cert9', 'cert10'] },
49+
{ name: 'discarded3',
50+
steps: [ null , null, null, null, null, null, null, null, null, null] },
51+
{ name: 'discarded4',
52+
steps: [ null , null, null, null, null, null, null, null, null, null] },
53+
{ name: 'discarded5',
54+
steps: [ null , null, null, null, null, null, null, null, null, null] },
55+
]
6056

6157
export default function TablePage(){
6258
const [searchTerm, setSearchTerm] = useState('');
63-
const [sortValue, setSortValue] = useState('non-discarded');
64-
const [filterValues, setFilterValues] = useState<string[]>(['discarded']);
59+
const [sortValue, setSortValue] = useState(sortOptions[2]);
60+
const [filterValues, setFilterValues] = useState<string[]>([filterOptions[1]]);
6561

6662
const displayData = useMemo(() => {
6763
let data = initialData;
@@ -71,25 +67,25 @@ export default function TablePage(){
7167
data = data.filter(row => row.name.toLowerCase().includes(lowerTerm));
7268
}
7369

74-
if (filterValues.includes("non-discarded") && !filterValues.includes("discarded")) {
70+
if (filterValues.includes(filterOptions[0]) && !filterValues.includes(filterOptions[1])) {
7571
data = data.filter(row => row.steps.some(step => step === null));
76-
} else if (filterValues.includes("discarded") && !filterValues.includes("non-discarded")) {
72+
} else if (filterValues.includes(filterOptions[1]) && !filterValues.includes(filterOptions[0])) {
7773
data = data.filter(row => row.steps.every(step => step !== null));
7874
} else if (filterValues.length === 2) {
7975
data = [];
8076
}
8177

82-
if (sortValue === "name-asc") {
78+
if (sortValue === sortOptions[0]) {
8379
data = [...data].sort((a, b) => a.name.localeCompare(b.name));
84-
} else if (sortValue === "name-desc") {
80+
} else if (sortValue === sortOptions[1]) {
8581
data = [...data].sort((a, b) => b.name.localeCompare(a.name));
86-
} else if (sortValue === "non-discarded") {
82+
} else if (sortValue === sortOptions[2]) {
8783
data = [...data].sort((a, b) => {
8884
const aDiscarded = a.steps.every(step => step === null) ? 1 : 0;
8985
const bDiscarded = b.steps.every(step => step === null) ? 1 : 0;
9086
return aDiscarded - bDiscarded;
9187
});
92-
} else if (sortValue === "discarded") {
88+
} else if (sortValue === sortOptions[3]) {
9389
data = [...data].sort((a, b) => {
9490
const aDiscarded = a.steps.every(step => step !== null) ? 1 : 0;
9591
const bDiscarded = b.steps.every(step => step !== null) ? 1 : 0;
@@ -98,7 +94,16 @@ export default function TablePage(){
9894
}
9995

10096
return data;
101-
}, [searchTerm, filterValues, sortValue, initialData]);
97+
}, [searchTerm, filterValues, sortValue]);
98+
99+
const handleFilterSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
100+
const val = event.target.value;
101+
if (filterValues.includes(val)) {
102+
setFilterValues(filterValues.filter((v) => v !== val))
103+
} else {
104+
setFilterValues([...filterValues, val])
105+
}
106+
}
102107

103108
return (
104109
<div className="font-[family-name:var(--font-geist-sans)] bg-surface flex h-dvh p-8">
@@ -112,21 +117,43 @@ export default function TablePage(){
112117
/>
113118
<div className="flex gap-2">
114119
<Dropdown
115-
btnLabel="Filter"
116-
btnIcon={{ svg: <Filter /> }}
117-
listItems={filterOptions} type='checkbox'
120+
btnLabel={filterValues.length === 0 ? "Filter" : filterValues.length === 1 ? "Filter: " + filterValues[0] : "Filter: " + filterValues.length }
121+
btnIcon={{ svg: <Filter /> }}
118122
position='right'
119-
selected={filterValues}
120-
onChange={(val) => setFilterValues(val as string[])}
121-
/>
123+
>
124+
<>
125+
{filterOptions.map((i) =>
126+
<Menu.Item
127+
key={i}
128+
className='flex justify-between'>
129+
<Checkbox
130+
label={i}
131+
value={i}
132+
checked={filterValues.includes(i)}
133+
onChange={handleFilterSelect}
134+
/>
135+
</Menu.Item>
136+
)}
137+
<Menu.Divider />
138+
<Button variant="inherit" content="Clear All" onClick={() => setFilterValues([])} fullWidth className='mb-[8px]'/>
139+
</>
140+
</Dropdown>
122141
<Dropdown
123-
btnLabel="Sort"
124-
btnIcon={{ svg: <Sort /> }} listItems={sortOptions}
125-
type='radio'
142+
btnLabel={"Sort: " + sortValue}
143+
btnIcon={{ svg: <Sort /> }}
126144
position='right'
127-
selected={sortValue}
128-
onChange={(val) => setSortValue(val as string)}
129-
/>
145+
>
146+
{sortOptions.map((i) =>
147+
<Menu.Item key={i} className='flex justify-between'>
148+
<RadioGroup.Button
149+
label={i}
150+
value={i}
151+
checked={i === sortValue}
152+
onChange={() => setSortValue(i)}
153+
/>
154+
</Menu.Item>
155+
)}
156+
</Dropdown>
130157
</div>
131158
</div>
132159
<Table>

starter_app/src/app/components/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ export default function Button({
1717
shape = 'rounded',
1818
size = 'medium',
1919
fullWidth = false,
20+
className,
2021
...ButtonProps
2122
}: ButtonProps) {
2223

23-
return <button className={cn(buttonVariants({ variant, shape, size, fullWidth }))} {...ButtonProps}>{startIcon && <Icon size='xsmall' color='text' {...startIcon} />}{typeof content === 'string' ? content : <Icon size='xsmall' color="primary" {...content} />}{endIcon && <Icon size='xsmall' color='text' {...endIcon} />}</button>;
24+
return <button className={cn(buttonVariants({ variant, shape, size, fullWidth }), className)} {...ButtonProps}>{startIcon && <Icon size='xsmall' color='text' {...startIcon} />}{typeof content === 'string' ? content : <Icon size='xsmall' color="primary" {...content} />}{endIcon && <Icon size='xsmall' color='text' {...endIcon} />}</button>;
2425
}
2526

2627
const buttonVariants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors delay-100 duration-200 ease-in-out disabled:opacity-50 disabled:pointer-events-none', {

starter_app/src/app/components/Checkbox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ export default function Checkbox({
3636
{checked && (
3737
<Icon
3838
svg={<CheckIcon />}
39-
size={size === 'small' ? 'xsmall' : 'small'}
39+
size='xsmall'
4040
color="surface"
4141
mode="both"
4242
strokeWidth={1.5}
4343
/>
4444
)}
4545
</span>
46-
{label && <span className='text-onSurface peer-disabled:opacity-50'>{label}</span>}
46+
{label && <span className='text-onSurface mr-4 peer-disabled:opacity-50'>{label}</span>}
4747
</label>
4848
);
4949
}

starter_app/src/app/components/ControlledTextfield.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ export default function ControlledTextField({
9393
placeholder={placeholder}
9494
required={required}
9595
disabled={disabled}
96-
className='w-full border-0 bg-transparent block outline-none text-sm text-onSurface pr-0 pl-[14px] pb-[8.5px] pt-[8.5px] placeholder:text-onSurface/50'
96+
className='w-full border-none bg-transparent block outline-none text-sm text-onSurface pr-0 pl-[14px] pb-[8.5px] pt-[8.5px] placeholder:text-onSurface/50'
9797
/>
9898
<div className="flex align-middle">
9999
<Button
100100
variant="embedded"
101101
onMouseDown={handleClear}
102-
content={hasError && !isFocused ? {svg: <ExclamationCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'error'} : { svg: <XCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'onVariant' }} />
102+
content={hasError && !isFocused ? {svg: <ExclamationCircleIcon />, mode: 'stroke', strokeWidth: 1.5, color: 'error'} : { svg: <XCircleIcon />, mode: 'stroke', strokeWidth: 1.5, color: 'onVariant' }} />
103103
</div>
104104
</div>
105105
<p className={cn('text-xs mt-1', hasError ? 'text-error' : 'text-onSurface/60')}>

0 commit comments

Comments
 (0)