Restructure raw content + css
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
+143
-149
@@ -9517,155 +9517,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bui-DataTableRoot {
|
||||
gap: var(--bui-space-3);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-1);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--unsorted {
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease-in-out, transform .1s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort:hover .bui-DataTableRoot--unsorted, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--unsorted {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-asc, .bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-asc, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-asc {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-desc, .bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-desc, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-desc {
|
||||
opacity: 1;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.bui-TableRoot {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
|
||||
.bui-TableCellText {
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextIcon, .bui-TableCellTextIcon svg {
|
||||
color: var(--bui-fg-primary);
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextContent {
|
||||
gap: var(--bui-space-0_5);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfile {
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatar {
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--bui-fg-primary);
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
border-radius: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarImage {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarFallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--bui-font-size-2);
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
box-shadow: inset 0 0 0 1px var(--bui-border);
|
||||
border-radius: var(--bui-radius-full);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination {
|
||||
padding-top: var(--bui-space-5);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--left {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--right {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--select {
|
||||
min-width: 10.5rem;
|
||||
}
|
||||
|
||||
.bui-FieldError {
|
||||
color: var(--bui-fg-danger);
|
||||
font-size: var(--bui-font-size-2);
|
||||
@@ -10166,6 +10017,149 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bui-Table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableHeadSortButton {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-1);
|
||||
display: inline-flex;
|
||||
|
||||
&:hover svg {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
& svg {
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease-in-out, transform .1s ease-in-out;
|
||||
}
|
||||
|
||||
&[data-sort-order="asc"] svg {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
&[data-sort-order="desc"] svg {
|
||||
opacity: 1;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
|
||||
.bui-TableCellText {
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextIcon, .bui-TableCellTextIcon svg {
|
||||
color: var(--bui-fg-primary);
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextContent {
|
||||
gap: var(--bui-space-0_5);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfile {
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatar {
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--bui-fg-primary);
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
border-radius: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarImage {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarFallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--bui-font-size-2);
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
box-shadow: inset 0 0 0 1px var(--bui-border);
|
||||
border-radius: var(--bui-radius-full);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination {
|
||||
padding-top: var(--bui-space-5);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--left {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--right {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination--select {
|
||||
min-width: 10.5rem;
|
||||
}
|
||||
|
||||
.bui-Tabs {
|
||||
--active-tab-left: 0px;
|
||||
--active-tab-right: 0px;
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import {
|
||||
RawTable,
|
||||
RawTableBody,
|
||||
RawTableHead,
|
||||
RawTableHeader,
|
||||
RawTableRow,
|
||||
RawTableCell,
|
||||
} from '.';
|
||||
|
||||
const invoices = [
|
||||
{
|
||||
invoice: 'INV001',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$250.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV002',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$150.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV003',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$350.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV004',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$450.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV005',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$550.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV006',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$200.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV007',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$300.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
];
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/RawTable',
|
||||
component: RawTable,
|
||||
subcomponents: {
|
||||
Body: RawTableBody as ComponentType<unknown>,
|
||||
Cell: RawTableCell as ComponentType<unknown>,
|
||||
Head: RawTableHead as ComponentType<unknown>,
|
||||
Header: RawTableHeader as ComponentType<unknown>,
|
||||
Row: RawTableRow as ComponentType<unknown>,
|
||||
},
|
||||
} satisfies Meta<typeof RawTable>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<RawTable>
|
||||
<RawTableHeader>
|
||||
<RawTableRow>
|
||||
<RawTableHead className="w-[100px]">Invoice</RawTableHead>
|
||||
<RawTableHead>Status</RawTableHead>
|
||||
<RawTableHead>Method</RawTableHead>
|
||||
<RawTableHead className="text-right">Amount</RawTableHead>
|
||||
</RawTableRow>
|
||||
</RawTableHeader>
|
||||
<RawTableBody>
|
||||
{invoices.map(invoice => (
|
||||
<RawTableRow key={invoice.invoice}>
|
||||
<RawTableCell className="font-medium">
|
||||
{invoice.invoice}
|
||||
</RawTableCell>
|
||||
<RawTableCell>{invoice.paymentStatus}</RawTableCell>
|
||||
<RawTableCell>{invoice.paymentMethod}</RawTableCell>
|
||||
<RawTableCell className="text-right">
|
||||
{invoice.totalAmount}
|
||||
</RawTableCell>
|
||||
</RawTableRow>
|
||||
))}
|
||||
</RawTableBody>
|
||||
</RawTable>
|
||||
),
|
||||
};
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.bui-TableRoot {
|
||||
.bui-Table {
|
||||
width: 100%;
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
@@ -27,6 +27,33 @@
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableHeadSortButton {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-1);
|
||||
|
||||
&:hover svg {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
& svg {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
&[data-sort-order='asc'] svg {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
&[data-sort-order='desc'] svg {
|
||||
opacity: 1;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
@@ -26,95 +26,8 @@ export const RawTable = forwardRef<
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<table ref={ref} className={clsx(classNames.root, className)} {...props} />
|
||||
<table ref={ref} className={clsx(classNames.table, className)} {...props} />
|
||||
);
|
||||
});
|
||||
|
||||
RawTable.displayName = 'RawTable';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableHeader = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={clsx(classNames.header, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableHeader.displayName = 'RawTableHeader';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableBody = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tbody ref={ref} className={clsx(classNames.body, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableBody.displayName = 'RawTableBody';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tr ref={ref} className={clsx(classNames.row, className)} {...props}>
|
||||
{props.children}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
RawTableRow.displayName = 'RawTableRow';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableHead = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<th ref={ref} className={clsx(classNames.head, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableHead.displayName = 'RawTableHead';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCaption = forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={clsx(classNames.caption, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableCaption.displayName = 'RawTableCaption';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCell = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<td ref={ref} className={clsx(classNames.cell, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableCell.displayName = 'RawTableCell';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableBody = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tbody ref={ref} className={clsx(classNames.body, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableBody.displayName = 'RawTableBody';
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCaption = forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={clsx(classNames.caption, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableCaption.displayName = 'RawTableCaption';
|
||||
+16
-2
@@ -14,5 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { RawHeadContent } from './RawHeadContent';
|
||||
export type { RawHeadContentProps } from './types';
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCell = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<td ref={ref} className={clsx(classNames.cell, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableCell.displayName = 'RawTableCell';
|
||||
+21
-27
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { Icon } from '../../Icon';
|
||||
import { Hidden } from '../../Hidden';
|
||||
import { RawHeadContentProps } from './types';
|
||||
import { RawTableHeadProps } from './types';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
function getSortTitle(nextOrder: string | false) {
|
||||
if (nextOrder === 'asc') {
|
||||
@@ -32,7 +32,8 @@ function getSortTitle(nextOrder: string | false) {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function RawHeadContent<TData>({ header }: RawHeadContentProps<TData>) {
|
||||
export function RawTableHead<TData>({ header }: RawTableHeadProps<TData>) {
|
||||
const { classNames } = useStyles('Table');
|
||||
const headerContent = useMemo(
|
||||
() => flexRender(header.column.columnDef.header, header.getContext()),
|
||||
[header],
|
||||
@@ -50,32 +51,25 @@ export function RawHeadContent<TData>({ header }: RawHeadContentProps<TData>) {
|
||||
);
|
||||
|
||||
if (!header.column.getCanSort()) {
|
||||
return headerContent;
|
||||
return <th className={classNames.head}>{headerContent}</th>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="bui-DataTableRoot--sort"
|
||||
onClick={handleSort}
|
||||
onKeyDown={handleSort}
|
||||
>
|
||||
{headerContent}
|
||||
<Hidden>, {getSortTitle(header.column.getNextSortingOrder())}</Hidden>
|
||||
<Icon
|
||||
aria-hidden
|
||||
tab-index={-1}
|
||||
name="arrow-down"
|
||||
className={clsx(
|
||||
'bui-DataTableRoot--unsorted',
|
||||
{
|
||||
asc: 'bui-DataTableRoot--sorted-asc',
|
||||
desc: 'bui-DataTableRoot--sorted-desc',
|
||||
}[header.column.getIsSorted() as string],
|
||||
)}
|
||||
data-testid="datatable--sort-icon"
|
||||
/>
|
||||
</span>
|
||||
<th className={classNames.head}>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={classNames.headSortButton}
|
||||
onClick={handleSort}
|
||||
onKeyDown={handleSort}
|
||||
{...(header.column.getIsSorted() && {
|
||||
'data-sort-order': header.column.getIsSorted(),
|
||||
})}
|
||||
>
|
||||
{headerContent}
|
||||
<Hidden>, {getSortTitle(header.column.getNextSortingOrder())}</Hidden>
|
||||
<Icon aria-hidden tab-index={-1} name="arrow-down" />
|
||||
</span>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableHeader = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={clsx(classNames.header, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableHeader.displayName = 'RawTableHeader';
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tr ref={ref} className={clsx(classNames.row, className)} {...props}>
|
||||
{props.children}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
RawTableRow.displayName = 'RawTableRow';
|
||||
@@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
RawTable,
|
||||
RawTableBody,
|
||||
RawTableHead,
|
||||
RawTableHeader,
|
||||
RawTableRow,
|
||||
RawTableCell,
|
||||
} from './RawTable';
|
||||
export { RawTable } from './RawTable';
|
||||
export { RawTableBody } from './RawTableBody';
|
||||
export { RawTableCaption } from './RawTableCaption';
|
||||
export { RawTableCell } from './RawTableCell';
|
||||
export { RawTableHead } from './RawTableHead';
|
||||
export { RawTableHeader } from './RawTableHeader';
|
||||
export { RawTableRow } from './RawTableRow';
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
import { Header } from '@tanstack/react-table';
|
||||
|
||||
/** @internal */
|
||||
export interface RawHeadContentProps<TData> {
|
||||
export interface RawTableHeadProps<TData>
|
||||
extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
||||
header: Header<TData, unknown>;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { columns } from './mocked-columns';
|
||||
import {
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
PaginationState,
|
||||
} from '@tanstack/react-table';
|
||||
@@ -48,8 +49,11 @@ export const Uncontrolled: Story = {
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
|
||||
console.log(table.getState().sorting);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table table={table} />
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
.bui-DataTableRoot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--bui-space-3);
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-1);
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--unsorted {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort:hover .bui-DataTableRoot--unsorted,
|
||||
.bui-DataTableRoot--sort:focus .bui-DataTableRoot--unsorted {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-asc,
|
||||
.bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-asc,
|
||||
.bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-asc {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-desc,
|
||||
.bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-desc,
|
||||
.bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-desc {
|
||||
opacity: 1;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
RawTableBody,
|
||||
RawTableCell,
|
||||
} from './RawTable';
|
||||
import { RawHeadContent } from './RawHeadContent';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
|
||||
function getAriaSort(sortDirection: string | false) {
|
||||
@@ -58,16 +57,17 @@ export function Table<TData>(
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<RawTableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => {
|
||||
if (header.isPlaceholder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<RawTableHead
|
||||
key={header.id}
|
||||
style={{ width: header.getSize() }}
|
||||
aria-sort={getAriaSort(header.column.getIsSorted())}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<RawHeadContent header={header} />
|
||||
)}
|
||||
</RawTableHead>
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</RawTableRow>
|
||||
|
||||
@@ -33,13 +33,11 @@ export const columns: ColumnDef<DataProps>[] = [
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<td>
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onChange={(checked: boolean) => row.toggleSelected(checked)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
</td>
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onChange={(checked: boolean) => row.toggleSelected(checked)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
@@ -56,6 +54,7 @@ export const columns: ColumnDef<DataProps>[] = [
|
||||
/>
|
||||
),
|
||||
size: 450,
|
||||
enableSorting: false,
|
||||
},
|
||||
{
|
||||
accessorKey: 'owner',
|
||||
|
||||
@@ -22,13 +22,6 @@
|
||||
@import '../components/Checkbox/styles.css';
|
||||
@import '../components/Collapsible/Collapsible.styles.css';
|
||||
@import '../components/Container/styles.css';
|
||||
|
||||
@import '../components/Table/Table.styles.css';
|
||||
@import '../components/Table/RawTable/RawTable.styles.css';
|
||||
@import '../components/Table/TableCellText/TableCellText.styles.css';
|
||||
@import '../components/Table/TableCellProfile/TableCellProfile.styles.css';
|
||||
@import '../components/Table/TablePagination/TablePagination.styles.css';
|
||||
|
||||
@import '../components/FieldError/FieldError.styles.css';
|
||||
@import '../components/FieldLabel/FieldLabel.styles.css';
|
||||
@import '../components/Flex/styles.css';
|
||||
@@ -41,6 +34,12 @@
|
||||
@import '../components/Menu/Menu.styles.css';
|
||||
@import '../components/Popover/Popover.styles.css';
|
||||
@import '../components/RadioGroup/RadioGroup.styles.css';
|
||||
|
||||
@import '../components/Table/RawTable/RawTable.styles.css';
|
||||
@import '../components/Table/TableCellText/TableCellText.styles.css';
|
||||
@import '../components/Table/TableCellProfile/TableCellProfile.styles.css';
|
||||
@import '../components/Table/TablePagination/TablePagination.styles.css';
|
||||
|
||||
@import '../components/Tabs/Tabs.styles.css';
|
||||
@import '../components/Text/styles.css';
|
||||
@import '../components/TextField/TextField.styles.css';
|
||||
|
||||
@@ -231,11 +231,12 @@ export const componentDefinitions = {
|
||||
},
|
||||
Table: {
|
||||
classNames: {
|
||||
root: 'bui-TableRoot',
|
||||
table: 'bui-Table',
|
||||
header: 'bui-TableHeader',
|
||||
body: 'bui-TableBody',
|
||||
row: 'bui-TableRow',
|
||||
head: 'bui-TableHead',
|
||||
headSortButton: 'bui-TableHeadSortButton',
|
||||
caption: 'bui-TableCaption',
|
||||
cell: 'bui-TableCell',
|
||||
cellText: 'bui-TableCellText',
|
||||
|
||||
Reference in New Issue
Block a user