Skip to main content

tablo_ui/components/primitives/
table.rs

1// SYNC: topcoat-ui-registry@0.11.0 sha256:1bb770c77662b17b71a1a2c574917b177dcaab9e5d73dae274d59405e9f02ae4 — do not hand-edit. Sync via `cargo xtask sync-topcoat-ui` (ADR-0007).
2use topcoat::{
3    Result,
4    view::{Attributes, Child, View, class, component, view},
5};
6
7/// A table inside a horizontally scrollable container.
8///
9/// Extra padding on the left and right aligns the cells with a card's content.
10/// Top and bottom borders separate the table from neighboring elements. A border
11/// is omitted when the table is the first or last child on that side.
12///
13/// Pass table sections as children. `attrs` are forwarded to the `<table>`, with extra
14/// classes added to its classes.
15///
16/// ```ignore
17/// view! {
18///     table(
19///         table_header(
20///             table_row(
21///                 table_head("Environment")
22///                 table_head("Status")
23///             )
24///         )
25///         table_body(
26///             table_row(
27///                 table_cell("production")
28///                 table_cell("Live")
29///             )
30///         )
31///     )
32/// }
33/// ```
34#[component]
35pub async fn table(
36    #[default] mut attrs: Attributes,
37    #[default] child: Child<'_>,
38) -> Result<impl View> {
39    Ok(view! {
40        <div data-slot="table" class="w-full overflow-x-auto">
41            // Put borders on the table so the caption stays outside them.
42            <table
43                class=(class!(
44                    "w-full caption-bottom border-collapse border-border text-sm",
45                    "[:not(:first-child)>&]:border-t [:not(:last-child)>&]:border-b",
46                    attrs.remove("class"),
47                ))
48                (attrs)
49            >
50                (child)
51            </table>
52        </div>
53    })
54}
55
56/// The heading section of a [`table`], holding the row of column headers.
57#[component]
58pub async fn table_header(
59    #[default] mut attrs: Attributes,
60    #[default] child: Child<'_>,
61) -> Result<impl View> {
62    Ok(view! {
63        <thead
64            class=(class!("bg-foreground/5 [&_tr]:border-b", attrs.remove("class")))
65            (attrs)
66        >
67            (child)
68        </thead>
69    })
70}
71
72/// The body of a table, containing data rows.
73#[component]
74pub async fn table_body(
75    #[default] mut attrs: Attributes,
76    #[default] child: Child<'_>,
77) -> Result<impl View> {
78    Ok(view! {
79        <tbody
80            class=(class!("[&_tr:last-child]:border-0", attrs.remove("class")))
81            (attrs)
82        >
83            (child)
84        </tbody>
85    })
86}
87
88/// The closing section of a [`table`], for totals and other summaries.
89#[component]
90pub async fn table_footer(
91    #[default] mut attrs: Attributes,
92    #[default] child: Child<'_>,
93) -> Result<impl View> {
94    Ok(view! {
95        <tfoot
96            class=(class!(
97                "border-t border-border bg-foreground/5 font-medium [&>tr]:last:border-b-0",
98                attrs.remove("class"),
99            ))
100            (attrs)
101        >
102            (child)
103        </tfoot>
104    })
105}
106
107/// A table row with a separator and hover styling.
108#[component]
109pub async fn table_row(
110    #[default] mut attrs: Attributes,
111    #[default] child: Child<'_>,
112) -> Result<impl View> {
113    Ok(view! {
114        <tr
115            class=(class!(
116                "border-b border-border transition-colors hover:bg-foreground/5",
117                attrs.remove("class"),
118            ))
119            (attrs)
120        >
121            (child)
122        </tr>
123    })
124}
125
126/// A column header in a [`table_header`]'s row.
127#[component]
128pub async fn table_head(
129    #[default] mut attrs: Attributes,
130    #[default] child: Child<'_>,
131) -> Result<impl View> {
132    Ok(view! {
133        <th
134            class=(class!(
135                "p-3 text-left align-middle font-medium whitespace-nowrap",
136                "text-muted-foreground first:pl-6 last:pr-6",
137                attrs.remove("class"),
138            ))
139            (attrs)
140        >
141            (child)
142        </th>
143    })
144}
145
146/// One cell of a [`table_row`].
147#[component]
148pub async fn table_cell(
149    #[default] mut attrs: Attributes,
150    #[default] child: Child<'_>,
151) -> Result<impl View> {
152    Ok(view! {
153        <td
154            class=(class!(
155                "p-3 align-middle whitespace-nowrap first:pl-6 last:pr-6",
156                attrs.remove("class"),
157            ))
158            (attrs)
159        >
160            (child)
161        </td>
162    })
163}
164
165/// A caption describing the table.
166#[component]
167pub async fn table_caption(
168    #[default] mut attrs: Attributes,
169    #[default] child: Child<'_>,
170) -> Result<impl View> {
171    Ok(view! {
172        <caption
173            class=(class!("mt-4 text-sm text-muted-foreground", attrs.remove("class")))
174            (attrs)
175        >
176            (child)
177        </caption>
178    })
179}