dbeaver/pro#5553 Add Sorting functionality to base grid (#3456)

* dbeaver/pro#5553 feat: add column sorting functionality to DataGrid

* dbeaver/pro#5553 feat: enhance column sorting functionality to support multiple selection

* dbeaver/pro#5553 feat: add keyboard navigation to order button

* dbeaver/pro#5553 feat: add constraints handling logic for column sorting in DataGrid

* Revert "dbeaver/pro#5553 feat: add constraints handling logic for column sorting in DataGrid"

This reverts commit 6028a48b13.

* dbeaver/pro#5553 feat: add icon generation script to UIKit, add sample icons

* dbeaver/pro#5553 refactor: use inner react-data-grid API for column sorting

* dbeaver/pro#5553 feat: add IconCross component and update icon-related files

* dbeaver/pro#5553 feat: replace Button with IconButton in OrderButton component

* dbeaver/pro#5553 style: update foreground color variables for IconButton component

* dbeaver/pro#5553 refactor: add normalization rules to layers, use IconButton in Common DialogHeader

checked if new IconButton works well in light and dark mode by using in CommonDialogHeader

* dbeaver/pro#5553 style: format code and update dependencies in CommonDialogHeader and DataGridTable components

* dbeaver/pro#5553 style: move onColumnSort prop to correct position in DataGridTable component

* dbeaver/pro#5553 refactor: remove icons component generation, use svg sprite

* dbeaver/pro#5553 refactor: update OrderButton component and inline icons sprite in Body

we decided to go with this solution for now just adding icons to a sprite and use them by id. Imports by ?url  don't work in VSCode if try to use them in <use> href, so we need to find another solution or stick to that approach.

* dbeaver/pro#5553 refactor: update column sorting and sortable logic in DataGrid components

* dbeaver/pro#5553 style: format whitespace in Body component

* dbeaver/pro#5553 feat: add new sorting icons for ascending, descending, and unsorted states

* dbeaver/pro#5553 refactor:styling for header cell and order button components

* dbeaver/pro#5553 refactor: update sorting logic in DataGrid and OrderButton components

* dbeaver/pro#5553 refactor: value deps

* dbeaver/pro#5553 feat: update sorting icons for ascending, descending, and unsorted states

* dbeaver/pro#5553 style: update aria-label and title for OrderButton component

* dbeaver/pro#5553 fix: double focus

* dbeaver/pro#5553 fix: after merge

* dbeaver/pro#5553 refactor: removed outline offset for sort button

* dbeaver/pro#5553 refactor: update Tailwind CSS import to use layer directive

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Alexey <wrouds@gmail.com>
This commit is contained in:
Sychev Andrey
2025-05-24 00:21:37 +08:00
committed by GitHub
co-authored by Evgenia Alexey
parent 116b1a3be8
commit 52f567fded
39 changed files with 357 additions and 165 deletions
@@ -8,6 +8,10 @@
}
@layer components.rdg.Cell {
.rdg {
--dbv-kit-icon-btn-outline-width: 1px;
}
@media (prefers-color-scheme: dark) {
.rdg-cell-frozen {
&:nth-last-child(1 of &) {
@@ -49,8 +49,10 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
headerText,
getHeaderOrder,
getHeaderResizable,
columnSortable,
getHeaderHeight,
getHeaderPinned,
columnSortingState,
getHeaderDnD,
cell,
cellText,
@@ -67,6 +69,7 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
onScrollToBottom,
onFocus,
onCellChange,
onColumnSort,
children,
className,
onCellKeyDown,
@@ -141,7 +144,7 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
<HeaderDnDContext value={dndHeaderContext}>
<DataGridRowContext value={{ rowCount, onScrollToBottom }}>
<DataGridCellContext value={{ cell, cellText, cellElement, cellTooltip, onCellChange }}>
<DataGridCellHeaderContext value={{ headerElement, headerText, getHeaderDnD }}>
<DataGridCellHeaderContext value={{ headerElement, headerText, getHeaderDnD, columnSortable, onColumnSort, columnSortingState }}>
<DataGridBase
key={gridKey.current}
ref={innerGridRef}
@@ -11,6 +11,9 @@ export interface IDataGridHeaderCellContext {
getHeaderPinned?: (colIdx: number) => boolean;
getHeaderDnD?: (colIdx: number) => boolean;
onHeaderReorder?: (from: number, to: number) => void;
columnSortingState?: IGridReactiveValue<'asc' | 'desc' | undefined | null, [colIdx: number]>;
columnSortable?: IGridReactiveValue<boolean, [colIdx: number]>;
onColumnSort?: (colIdx: number, order: 'asc' | 'desc' | null, isMultiple: boolean) => void;
}
export const DataGridCellHeaderContext = createContext<IDataGridHeaderCellContext | null>(null);
@@ -3,7 +3,7 @@ import type { IInnerRow } from './IInnerRow.js';
import { HeaderCellContentRenderer } from './renderers/HeaderCellContentRenderer.js';
export function mapRenderHeaderCell(colIdx: number) {
return function RenderHeaderCell({}: RenderHeaderCellProps<IInnerRow, unknown>) {
return <HeaderCellContentRenderer colIdx={colIdx} />;
return function RenderHeaderCell({ tabIndex }: RenderHeaderCellProps<IInnerRow, unknown>) {
return <HeaderCellContentRenderer colIdx={colIdx} tabIndex={tabIndex} />;
};
}
@@ -1,17 +1,25 @@
import { memo, use } from 'react';
import { memo, use, useRef } from 'react';
import { useDrag, useDrop, type DnDStoreProvider } from '@dbeaver/react-dnd';
import { DataGridCellHeaderContext } from '../DataGridHeaderCellContext.js';
import { useGridReactiveValue } from '../useGridReactiveValue.js';
import { HeaderDnDContext } from '../useHeaderDnD.js';
import { OrderButton } from './OrderButton.js';
interface Props {
colIdx: number;
tabIndex?: number;
}
export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer({ colIdx }: Props) {
export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer({ colIdx, tabIndex }: Props) {
const dndHeaderContext = use(HeaderDnDContext);
const cellHeaderContext = use(DataGridCellHeaderContext);
const headerElement = useGridReactiveValue(cellHeaderContext?.headerElement, colIdx);
const getHeaderText = useGridReactiveValue(headerElement ? undefined : cellHeaderContext?.headerText, colIdx);
const isColumnSortable = useGridReactiveValue(cellHeaderContext?.columnSortable, colIdx);
const onColumnSort = cellHeaderContext?.onColumnSort;
const sortingState = useGridReactiveValue(cellHeaderContext?.columnSortingState, colIdx);
const orderButtonRef = useRef<HTMLButtonElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const draggable = dndHeaderContext?.getCanDrag?.(colIdx) ?? false;
const drag = useDrag({
@@ -47,9 +55,26 @@ export const HeaderCellContentRenderer = memo(function HeaderCellContentRenderer
},
});
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Tab' && !e.shiftKey && isColumnSortable && onColumnSort && orderButtonRef.current !== document.activeElement) {
e.preventDefault();
e.stopPropagation();
containerRef.current?.parentElement?.setAttribute('aria-selected', 'false');
orderButtonRef.current?.focus();
}
};
return (
<div className="tw:h-full tw:content-center" {...drag.props} {...drop.props}>
{headerElement ?? getHeaderText ?? ''}
<div
ref={containerRef}
tabIndex={tabIndex}
onKeyDown={handleKeyDown}
className="tw:w-full tw:h-full tw:content-center tw:flex tw:items-center tw:justify-between tw:gap-1 tw:outline-none tw:group"
{...drag.props}
{...drop.props}
>
<span className="tw:overflow-hidden tw:text-ellipsis">{headerElement ?? getHeaderText ?? ''}</span>
{isColumnSortable && onColumnSort && <OrderButton ref={orderButtonRef} colIdx={colIdx} sortState={sortingState} onSort={onColumnSort} />}
</div>
);
});
@@ -0,0 +1,42 @@
import { clsx, IconButton } from '@dbeaver/ui-kit';
interface OrderButtonProps {
colIdx: number;
sortState?: 'asc' | 'desc' | null;
onSort: (attributePosition: number, order: 'asc' | 'desc' | null, isMultiple: boolean) => void;
tabIndex?: number;
ref: React.Ref<HTMLButtonElement>;
}
export function OrderButton({ colIdx, sortState, onSort, tabIndex, ref }: OrderButtonProps) {
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
const nextSortState = sortState === 'asc' ? 'desc' : sortState === 'desc' ? null : 'asc';
onSort(colIdx, nextSortState, e.ctrlKey || e.metaKey);
}
const svgSortAsc = '#icon-sort-asc';
const svgSortDesc = '#icon-sort-desc';
const svgSortUnknown = '#icon-sort-unknown';
const iconSrc = sortState === 'asc' ? svgSortAsc : sortState === 'desc' ? svgSortDesc : svgSortUnknown;
return (
<IconButton
variant="secondary"
size="small"
onClick={handleClick}
tabIndex={tabIndex}
ref={ref}
title="Sort by column"
aria-label="Sort by column"
className={clsx(
'tw:opacity-0 tw:group-focus:opacity-100 tw:focus:opacity-100 tw:group-hover:opacity-100 tw:hover:opacity-100 tw:outline-offset-0',
sortState && 'tw:opacity-100',
)}
>
<svg width="16" height="16" viewBox="0 0 16 16">
<use href={iconSrc} />
</svg>
</IconButton>
);
}
@@ -1,4 +1,4 @@
@import 'tailwindcss' prefix(tw);
@import 'tailwindcss' layer(base) prefix(tw);
@layer theme {
main {
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor">
<polygon points="2 10 12 10 7 4 2 10" />
<rect x="6" y="6" width="2" height="22" />
<rect x="14" y="6" width="16" height="2" />
<rect x="14" y="15" width="12" height="2" />
<rect x="14" y="24" width="8" height="2" />
</svg>

After

Width:  |  Height:  |  Size: 353 B

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<polygon points="2 8.14 9.14 8.14 5.57 3.86 2 8.14" />
<rect x="4.86" y="5.29" width="1.43" height="15.71" />
<rect x="10.57" y="5.29" width="11.43" height="1.43" />
<rect x="10.57" y="11.71" width="8.57" height="1.43" />
<rect x="10.57" y="18.14" width="5.71" height="1.43" />
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
<polygon points="0 4 5 4 2.5 1 0 4" />
<rect x="2" y="4" width="1" height="11" />
<rect x="6" y="3" width="10" height="1" />
<rect x="6" y="8" width="7" height="1" />
<rect x="6" y="13" width="4" height="1" />
</svg>

After

Width:  |  Height:  |  Size: 346 B

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor">
<polygon points="12 22 2 22 7 28 12 22" />
<rect x="6" y="4" width="2" height="22" />
<rect x="14" y="6" width="16" height="2" />
<rect x="14" y="15" width="12" height="2" />
<rect x="14" y="24" width="8" height="2" />
</svg>

After

Width:  |  Height:  |  Size: 355 B

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<polygon points="9.14 16.71 2 16.71 5.57 21 9.14 16.71" />
<rect x="4.86" y="3.86" width="1.43" height="15.71" />
<rect x="10.57" y="5.29" width="11.43" height="1.43" />
<rect x="10.57" y="11.71" width="8.57" height="1.43" />
<rect x="10.57" y="18.14" width="5.71" height="1.43" />
</svg>

After

Width:  |  Height:  |  Size: 418 B

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
<polygon points="5 12 0 12 2.5 15 5 12" />
<rect x="2" y="1" width="1" height="12" />
<rect x="6" y="3" width="10" height="1" />
<rect x="6" y="8" width="7" height="1" />
<rect x="6" y="13" width="4" height="1" />
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="currentColor">
<polygon points="4 10 14 10 9 4 4 10" />
<rect x="8" y="6" width="2" height="22" />
<polygon points="28 22 18 22 23 28 28 22" />
<rect x="22" y="4" width="2" height="22" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<polygon points="3.4 8.29 10.55 8.29 6.98 4 3.4 8.29" />
<rect x="6.26" y="5.43" width="1.43" height="15.71" />
<polygon points="20.55 16.86 13.4 16.86 16.98 21.14 20.55 16.86" />
<rect x="16.26" y="4" width="1.43" height="15.71" />
</svg>

After

Width:  |  Height:  |  Size: 367 B

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
<polygon points="2 4 7 4 4.5 1 2 4" />
<rect x="4" y="4" width="1" height="11" />
<polygon points="14 12 9 12 11.5 15 14 12" />
<rect x="11" y="1" width="1" height="11" />
</svg>

After

Width:  |  Height:  |  Size: 306 B

@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-sort-unknown" viewBox="0 0 24 24" fill="currentColor">
<polygon points="3.4 8.29 10.55 8.29 6.98 4 3.4 8.29" />
<rect x="6.26" y="5.43" width="1.43" height="15.71" />
<polygon points="20.55 16.86 13.4 16.86 16.98 21.14 20.55 16.86" />
<rect x="16.26" y="4" width="1.43" height="15.71" />
</symbol>
<symbol id="icon-sort-desc" viewBox="0 0 24 24"
fill="currentColor">
<polygon points="9.14 16.71 2 16.71 5.57 21 9.14 16.71" />
<rect x="4.86" y="3.86" width="1.43" height="15.71" />
<rect x="10.57" y="5.29" width="11.43" height="1.43" />
<rect x="10.57" y="11.71" width="8.57" height="1.43" />
<rect x="10.57" y="18.14" width="5.71" height="1.43" />
</symbol>
<symbol id="icon-sort-asc" viewBox="0 0 24 24" fill="currentColor">
<polygon points="2 8.14 9.14 8.14 5.57 3.86 2 8.14" />
<rect x="4.86" y="5.29" width="1.43" height="15.71" />
<rect x="10.57" y="5.29" width="11.43" height="1.43" />
<rect x="10.57" y="11.71" width="8.57" height="1.43" />
<rect x="10.57" y="18.14" width="5.71" height="1.43" />
</symbol>
<symbol id="icon-cross" viewBox="0 0 24 24">
<path xmlns="http://www.w3.org/2000/svg" fill="currentColor" fill-rule="evenodd"
clip-rule="evenodd"
d="M3.31579 3.31579C2.89474 3.73684 2.89474 4.42105 3.31579 4.84211L10.4737 12L3.31579 19.1579C2.89474 19.5789 2.89474 20.2632 3.31579 20.6842C3.73684 21.1053 4.42105 21.1053 4.84211 20.6842L12 13.5263L19.1579 20.6842C19.5789 21.1053 20.2632 21.1053 20.6842 20.6842C21.1053 20.2632 21.1053 19.5789 20.6842 19.1579L13.5263 12L20.6842 4.84211C21.1053 4.42105 21.1053 3.73684 20.6842 3.31579C20.2632 2.89474 19.5789 2.89474 19.1579 3.31579L12 10.4737L4.84211 3.31579C4.42105 2.89474 3.73684 2.89474 3.31579 3.31579Z" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+2 -1
View File
@@ -7,7 +7,8 @@
"exports": {
".": "./lib/index.js",
"./*.css": "./src/*.css",
"./*": "./lib/*.js"
"./*": "./lib/*.js",
"./assets/icons/*.svg": "./assets/icons/*.svg"
},
"scripts": {
"build": "tsc -b",
@@ -0,0 +1,16 @@
export default function template(files) {
const exportLines = files.map((file) => {
const { path } = file;
const fileName = path.split('/').pop();
const componentNameWithExt = fileName.replace(/\.tsx$/, '.js');
const componentName = fileName.replace(/\.tsx$/, '');
return `export { default as ${componentName} } from './${componentNameWithExt}';`;
});
const header = '/* This file is automatically generated by the build:icons script. Do not modify it manually.*/\n\n ';
return header + exportLines.join('\n');
};
@@ -7,6 +7,7 @@
min-width: var(--dbv-kit-icon-btn-size);
width: var(--dbv-kit-icon-btn-size);
height: var(--dbv-kit-icon-btn-size);
font-size: calc(var(--dbv-kit-icon-btn-size) * 0.75);
border-radius: var(--dbv-kit-icon-btn-border-radius);
border-color: var(--dbv-kit-icon-btn-border-color);
@@ -56,14 +57,6 @@
}
}
.dbv-kit-icon-button__icon {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
/* Size variants */
.dbv-kit-icon-button--small {
--dbv-kit-icon-btn-size: var(--dbv-kit-icon-btn-small-size);
@@ -24,7 +24,7 @@ export function IconButtonBase({ className, variant = 'primary', size = 'medium'
return (
<ButtonRoot className={classToApply} {...props}>
<span className="dbv-kit-icon-button__icon">{children}</span>
{children}
</ButtonRoot>
);
}
@@ -17,8 +17,8 @@
/* Default colors */
--dbv-kit-icon-btn-foreground: var(--tw-color-gray-600);
--dbv-kit-icon-btn-foreground-hover: var(--dbv-kit-color-600);
--dbv-kit-icon-btn-foreground-active: var(--dbv-kit-color-700);
--dbv-kit-icon-btn-foreground-hover: var(--tw-color-gray-600);
--dbv-kit-icon-btn-foreground-active: var(--tw-color-gray-700);
--dbv-kit-icon-btn-background: transparent;
--dbv-kit-icon-btn-background-hover: var(--tw-color-gray-100);
--dbv-kit-icon-btn-background-active: var(--tw-color-gray-200);
@@ -91,23 +91,23 @@ export const IconButtons = () => {
<h2>Primary</h2>
<div className="tw:flex tw:gap-4 tw:my-4 tw:items-center">
<IconButton variant="primary" size="small" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="primary" size="medium" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="primary" size="large" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="primary" size="xlarge" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
</div>
@@ -116,23 +116,23 @@ export const IconButtons = () => {
<h2>Secondary</h2>
<div className="tw:flex tw:gap-4 tw:my-4 tw:items-center">
<IconButton variant="secondary" size="small" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="secondary" size="medium" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="secondary" size="large" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="secondary" size="xlarge" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
</div>
@@ -141,23 +141,23 @@ export const IconButtons = () => {
<h2>Danger</h2>
<div className="tw:flex tw:gap-4 tw:my-4 tw:items-center">
<IconButton variant="danger" size="small" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="danger" size="medium" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="danger" size="large" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="danger" size="xlarge" aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
</div>
@@ -166,41 +166,41 @@ export const IconButtons = () => {
<h2>Disabled</h2>
<div className="tw:flex tw:gap-4 tw:my-4 tw:items-center">
<IconButton variant="primary" size="small" disabled aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="secondary" size="medium" disabled aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="danger" size="large" disabled aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
</div>
<h2>Loading</h2>
<div className="tw:flex tw:gap-4 tw:my-4 tw:items-center">
<IconButton variant="primary" size="small" loading aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="secondary" loading={isLoading} onClick={handleClick} aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton size="large" loading={isLoading} onClick={handleClick} aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
<IconButton variant="danger" size="xlarge" loading={isLoading} onClick={handleClick} aria-label="Add item">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="currentColor" />
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">
<circle cx="12" cy="12" r="12" fill="currentColor" />
</svg>
</IconButton>
</div>