feat(data-table): allow custom column widths (#1265)

* feat(data-table): allow header column `width`, `minWidth` values

* Run "yarn build:docs"

* test(data-table): assert width, minWidth properties

* docs(data-table): add "Custom column widths" example
This commit is contained in:
metonym 2022-05-14 09:24:24 -07:00 committed by GitHub
commit c6f210899b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 14 deletions

View file

@ -927,6 +927,8 @@ export interface DataTableEmptyHeader {
display?: (item: Value) => DataTableValue; display?: (item: Value) => DataTableValue;
sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1); sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1);
columnMenu?: boolean; columnMenu?: boolean;
width?: string;
minWidth?: string;
} }
export interface DataTableNonEmptyHeader { export interface DataTableNonEmptyHeader {
@ -935,6 +937,8 @@ export interface DataTableNonEmptyHeader {
display?: (item: Value) => DataTableValue; display?: (item: Value) => DataTableValue;
sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1); sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1);
columnMenu?: boolean; columnMenu?: boolean;
width?: string;
minWidth?: string;
} }
export type DataTableHeader = DataTableNonEmptyHeader | DataTableEmptyHeader; export type DataTableHeader = DataTableNonEmptyHeader | DataTableEmptyHeader;
@ -3748,13 +3752,14 @@ None.
### Props ### Props
| Prop name | Kind | Reactive | Type | Default value | Description | | Prop name | Kind | Reactive | Type | Default value | Description |
| :------------- | :--------------- | :------- | :------------------------------------------------------------------ | ---------------------- | --------------------------------------- | | :------------- | :--------------- | :------- | :------------------------------------------------------------------ | ---------------------- | ---------------------------------------------- |
| size | <code>let</code> | No | <code>"compact" &#124; "short" &#124; "medium" &#124; "tall"</code> | <code>undefined</code> | Set the size of the table | | size | <code>let</code> | No | <code>"compact" &#124; "short" &#124; "medium" &#124; "tall"</code> | <code>undefined</code> | Set the size of the table |
| zebra | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use zebra styles | | zebra | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use zebra styles |
| useStaticWidth | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use static width | | useStaticWidth | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to use static width |
| sortable | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` for the sortable variant | | sortable | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` for the sortable variant |
| stickyHeader | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable a sticky header | | stickyHeader | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to enable a sticky header |
| tableStyle | <code>let</code> | No | <code>string</code> | <code>undefined</code> | Set the style attribute on the `table` element |
### Slots ### Slots

View file

@ -2495,14 +2495,14 @@
"ts": "type DataTableValue = any" "ts": "type DataTableValue = any"
}, },
{ {
"type": "{ key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }", "type": "{ key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }",
"name": "DataTableEmptyHeader", "name": "DataTableEmptyHeader",
"ts": "interface DataTableEmptyHeader { key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }" "ts": "interface DataTableEmptyHeader { key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }"
}, },
{ {
"type": "{ key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }", "type": "{ key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }",
"name": "DataTableNonEmptyHeader", "name": "DataTableNonEmptyHeader",
"ts": "interface DataTableNonEmptyHeader { key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }" "ts": "interface DataTableNonEmptyHeader { key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }"
}, },
{ {
"type": "DataTableNonEmptyHeader | DataTableEmptyHeader", "type": "DataTableNonEmptyHeader | DataTableEmptyHeader",
@ -10863,6 +10863,16 @@
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"constant": false, "constant": false,
"reactive": false "reactive": false
},
{
"name": "tableStyle",
"kind": "let",
"description": "Set the style attribute on the `table` element",
"type": "string",
"isFunction": false,
"isFunctionDeclaration": false,
"constant": false,
"reactive": false
} }
], ],
"moduleExports": [], "moduleExports": [],

View file

@ -328,6 +328,14 @@ title="Load balancers" description="Your organization's active load balancers."
]}" ]}"
/> />
### Custom column widths
Specify a `width` or `minWidth` property in the `headers` object to customize the width of each column.
A [table-layout: fixed](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout#values) rule will be applied to the `table` element when using custom widths.
<FileSource src="/framed/DataTable/DataTableHeaderWidth" />
### Sticky header ### Sticky header
Set `stickyHeader` to `true` for the header to be fixed in place. Set `stickyHeader` to `true` for the header to be fixed in place.

View file

@ -0,0 +1,19 @@
<script>
import { DataTable } from "carbon-components-svelte";
</script>
<DataTable
headers="{[
{ key: 'name', value: 'Name', width: '50%', minWidth: '200px' },
{ key: 'protocol', value: 'Protocol', width: '60px' },
{ key: 'port', value: 'Port', width: '60px' },
{ key: 'rule', value: 'Rule', width: '10rem' },
]}"
rows="{Array.from({ length: 6 }).map((_, i) => ({
id: i,
name: 'Load Balancer ' + (i + 1),
protocol: 'HTTP',
port: i % 3 ? (i % 2 ? 3000 : 80) : 443,
rule: i % 3 ? 'Round robin' : 'DNS delegation',
}))}"
/>

View file

@ -2,8 +2,8 @@
/** /**
* @typedef {string} DataTableKey * @typedef {string} DataTableKey
* @typedef {any} DataTableValue * @typedef {any} DataTableValue
* @typedef {{ key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }} DataTableEmptyHeader * @typedef {{ key: DataTableKey; empty: boolean; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }} DataTableEmptyHeader
* @typedef {{ key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; }} DataTableNonEmptyHeader * @typedef {{ key: DataTableKey; value: DataTableValue; display?: (item: Value) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => (0 | -1 | 1)); columnMenu?: boolean; width?: string; minWidth?: string; }} DataTableNonEmptyHeader
* @typedef {DataTableNonEmptyHeader | DataTableEmptyHeader} DataTableHeader * @typedef {DataTableNonEmptyHeader | DataTableEmptyHeader} DataTableHeader
* @typedef {{ id: any; [key: string]: DataTableValue; }} DataTableRow * @typedef {{ id: any; [key: string]: DataTableValue; }} DataTableRow
* @typedef {any} DataTableRowId * @typedef {any} DataTableRowId
@ -240,6 +240,20 @@
: rows; : rows;
$: displayedRows = getDisplayedRows($tableRows, page, pageSize); $: displayedRows = getDisplayedRows($tableRows, page, pageSize);
$: displayedSortedRows = getDisplayedRows(sortedRows, page, pageSize); $: displayedSortedRows = getDisplayedRows(sortedRows, page, pageSize);
$: hasCustomHeaderWidth = headers.some(
(header) => header.width || header.minWidth
);
/** @type {(header: DataTableHeader) => undefined | string} */
const formatHeaderWidth = (header) => {
const styles = [
header.width && `width: ${header.width}`,
header.minWidth && `min-width: ${header.minWidth}`,
].filter(Boolean);
if (styles.length === 0) return undefined;
return styles.join(";");
};
</script> </script>
<TableContainer useStaticWidth="{useStaticWidth}" {...$$restProps}> <TableContainer useStaticWidth="{useStaticWidth}" {...$$restProps}>
@ -264,6 +278,7 @@
stickyHeader="{stickyHeader}" stickyHeader="{stickyHeader}"
sortable="{sortable}" sortable="{sortable}"
useStaticWidth="{useStaticWidth}" useStaticWidth="{useStaticWidth}"
tableStyle="{hasCustomHeaderWidth && 'table-layout: fixed'}"
> >
<TableHead> <TableHead>
<TableRow> <TableRow>
@ -322,6 +337,7 @@
{:else} {:else}
<TableHeader <TableHeader
id="{header.key}" id="{header.key}"
style="{formatHeaderWidth(header)}"
disableSorting="{header.sort === false}" disableSorting="{header.sort === false}"
on:click="{() => { on:click="{() => {
dispatch('click', { header }); dispatch('click', { header });

View file

@ -16,6 +16,12 @@
/** Set to `true` to enable a sticky header */ /** Set to `true` to enable a sticky header */
export let stickyHeader = false; export let stickyHeader = false;
/**
* Set the style attribute on the `table` element
* @type {string}
*/
export let tableStyle = undefined;
</script> </script>
{#if stickyHeader} {#if stickyHeader}
@ -30,6 +36,7 @@
class:bx--data-table--zebra="{zebra}" class:bx--data-table--zebra="{zebra}"
class:bx--data-table--static="{useStaticWidth}" class:bx--data-table--static="{useStaticWidth}"
class:bx--data-table--sticky-header="{stickyHeader}" class:bx--data-table--sticky-header="{stickyHeader}"
style="{tableStyle}"
> >
<slot /> <slot />
</table> </table>
@ -46,6 +53,7 @@
class:bx--data-table--static="{useStaticWidth}" class:bx--data-table--static="{useStaticWidth}"
class:bx--data-table--sticky-header="{stickyHeader}" class:bx--data-table--sticky-header="{stickyHeader}"
{...$$restProps} {...$$restProps}
style="{tableStyle}"
> >
<slot /> <slot />
</table> </table>

View file

@ -15,7 +15,7 @@
const headers: DataTableHeader[] = [ const headers: DataTableHeader[] = [
{ key: "name", value: "Name" }, { key: "name", value: "Name" },
{ key: "protocol", value: "Protocol" }, { key: "protocol", value: "Protocol", width: "400px", minWidth: "40%" },
{ key: "port", value: "Port" }, { key: "port", value: "Port" },
{ key: "rule", value: "Rule", sort: false }, { key: "rule", value: "Rule", sort: false },
]; ];

View file

@ -11,6 +11,8 @@ export interface DataTableEmptyHeader {
display?: (item: Value) => DataTableValue; display?: (item: Value) => DataTableValue;
sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1); sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1);
columnMenu?: boolean; columnMenu?: boolean;
width?: string;
minWidth?: string;
} }
export interface DataTableNonEmptyHeader { export interface DataTableNonEmptyHeader {
@ -19,6 +21,8 @@ export interface DataTableNonEmptyHeader {
display?: (item: Value) => DataTableValue; display?: (item: Value) => DataTableValue;
sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1); sort?: false | ((a: DataTableValue, b: DataTableValue) => 0 | -1 | 1);
columnMenu?: boolean; columnMenu?: boolean;
width?: string;
minWidth?: string;
} }
export type DataTableHeader = DataTableNonEmptyHeader | DataTableEmptyHeader; export type DataTableHeader = DataTableNonEmptyHeader | DataTableEmptyHeader;

View file

@ -32,6 +32,12 @@ export interface TableProps
* @default false * @default false
*/ */
stickyHeader?: boolean; stickyHeader?: boolean;
/**
* Set the style attribute on the `table` element
* @default undefined
*/
tableStyle?: string;
} }
export default class Table extends SvelteComponentTyped< export default class Table extends SvelteComponentTyped<