Skip to content

Commit 212d4e6

Browse files
authored
Merge pull request #6 from tinybirdco/sparks
Sparks
2 parents ecde48b + 8f65cd1 commit 212d4e6

File tree

19 files changed

+1123
-155
lines changed

19 files changed

+1123
-155
lines changed

.github/workflows/tinybird-cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- uses: actions/checkout@v3
3030

3131
- name: Install Tinybird CLI
32-
run: curl -LsSf https://api.tinybird.co/static/install.sh | sh
32+
run: curl https://tinybird.co | sh
3333

3434
- name: Build project
3535
run: tb build

.github/workflows/tinybird-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
steps:
3232
- uses: actions/checkout@v3
3333
- name: Install Tinybird CLI
34-
run: curl -LsSf https://api.tinybird.co/static/install.sh | sh
34+
run: curl https://tinybird.co | sh
3535
- name: Build project
3636
run: tb build
3737
- name: Test project
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import { RiSearchLine } from '@remixicon/react';
5+
import { BarList as TremorBarList, Card, Dialog, DialogPanel, TextInput } from '@tremor/react';
6+
7+
interface BarListItem {
8+
name: string;
9+
value: number;
10+
}
11+
12+
interface BarListProps {
13+
data: Array<{
14+
name: string;
15+
value: number;
16+
}>;
17+
valueFormatter?: (value: number) => string;
18+
onSelectionChange?: (selectedItems: string[]) => void;
19+
}
20+
21+
const defaultFormatter = (number: number) =>
22+
`${Intl.NumberFormat('us').format(number).toString()}`;
23+
24+
export default function BarList({
25+
data,
26+
valueFormatter = defaultFormatter,
27+
onSelectionChange
28+
}: BarListProps) {
29+
const [isOpen, setIsOpen] = useState(false);
30+
const [searchQuery, setSearchQuery] = useState('');
31+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
32+
const [selectedItems, setSelectedItems] = useState<string[]>([]);
33+
34+
const filteredItems = data.filter((item) =>
35+
item.name.toLowerCase().includes(searchQuery.toLowerCase()),
36+
);
37+
38+
// Calculate total value for header
39+
const totalValue = data.reduce((sum, item) => sum + item.value, 0);
40+
const hasMoreItems = data.length > 5;
41+
42+
const handleBarClick = (itemName: string) => {
43+
setSelectedItems(prev => {
44+
const newSelection = prev.includes(itemName)
45+
? prev.filter(name => name !== itemName)
46+
: [...prev, itemName];
47+
48+
onSelectionChange?.(newSelection);
49+
return newSelection;
50+
});
51+
};
52+
53+
const renderBarList = (items: BarListItem[]) => (
54+
<TremorBarList<BarListItem>
55+
data={items}
56+
valueFormatter={valueFormatter}
57+
className="mt-4"
58+
onValueChange={(item: BarListItem) => handleBarClick(item.name)}
59+
/>
60+
);
61+
62+
return (
63+
<>
64+
<Card className="h-full w-full rounded-none border-0" style={{ boxShadow: '-1px 0 0 0 rgb(55 65 81)' }}>
65+
<p className="text-tremor-default text-tremor-content dark:text-dark-tremor-content">
66+
Total
67+
</p>
68+
<p className="text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong">
69+
{valueFormatter(totalValue)}
70+
</p>
71+
<div className="mt-6 flex items-center justify-between">
72+
<p className="text-tremor-default font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong">
73+
Top {Math.min(5, data.length)}
74+
</p>
75+
<p className="text-tremor-label font-medium uppercase text-tremor-content dark:text-dark-tremor-content">
76+
Count
77+
</p>
78+
</div>
79+
{renderBarList(data.slice(0, 5))}
80+
{hasMoreItems && (
81+
<div className="absolute inset-x-0 bottom-0 flex justify-center rounded-b-tremor-default bg-gradient-to-t from-tremor-background to-transparent py-7 dark:from-dark-tremor-background">
82+
<button
83+
className="flex items-center justify-center rounded-tremor-small border border-tremor-border bg-tremor-background px-2.5 py-2 text-tremor-default font-medium text-tremor-content-strong shadow-tremor-input hover:bg-tremor-background-muted dark:border-dark-tremor-border dark:bg-dark-tremor-background dark:text-dark-tremor-content-strong dark:shadow-dark-tremor-input hover:dark:bg-dark-tremor-background-muted"
84+
onClick={() => setIsOpen(true)}
85+
>
86+
Show more
87+
</button>
88+
</div>
89+
)}
90+
<Dialog
91+
open={isOpen}
92+
onClose={() => setIsOpen(false)}
93+
static={true}
94+
className="z-[100]"
95+
>
96+
<DialogPanel className="overflow-hidden p-0">
97+
<div className="px-6 pb-4 pt-6">
98+
<TextInput
99+
icon={RiSearchLine}
100+
placeholder="Search..."
101+
className="rounded-tremor-small"
102+
value={searchQuery}
103+
onValueChange={setSearchQuery}
104+
/>
105+
<div className="flex items-center justify-between pt-4">
106+
<p className="text-tremor-default font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong">
107+
Name
108+
</p>
109+
<p className="text-tremor-label font-medium uppercase text-tremor-content dark:text-dark-tremor-content">
110+
Count
111+
</p>
112+
</div>
113+
</div>
114+
<div className="h-96 overflow-y-scroll px-6">
115+
{filteredItems.length > 0 ? (
116+
renderBarList(filteredItems)
117+
) : (
118+
<p className="flex h-full items-center justify-center text-tremor-default text-tremor-content-strong dark:text-dark-tremor-content-strong">
119+
No results.
120+
</p>
121+
)}
122+
</div>
123+
<div className="mt-4 border-t border-tremor-border bg-tremor-background-muted p-6 dark:border-dark-tremor-border dark:bg-dark-tremor-background">
124+
<button
125+
className="flex w-full items-center justify-center rounded-tremor-small border border-tremor-border bg-tremor-background py-2 text-tremor-default font-medium text-tremor-content-strong shadow-tremor-input hover:bg-tremor-background-muted dark:border-dark-tremor-border dark:bg-dark-tremor-background dark:text-dark-tremor-content-strong dark:shadow-dark-tremor-input hover:dark:bg-dark-tremor-background-muted"
126+
onClick={() => setIsOpen(false)}
127+
>
128+
Go back
129+
</button>
130+
</div>
131+
</DialogPanel>
132+
</Dialog>
133+
</Card>
134+
</>
135+
);
136+
}

0 commit comments

Comments
 (0)