@ssa-ui-kit/core 3.22.1 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/source-map.md +97 -27
- package/dist/components/Carousel/Carousel.d.ts +62 -0
- package/dist/components/Carousel/index.d.ts +1 -0
- package/dist/components/Carousel/styles.d.ts +27 -0
- package/dist/components/Carousel/types.d.ts +141 -0
- package/dist/components/Charts/FunnelChart/FunnelChart.d.ts +24 -0
- package/dist/components/Charts/FunnelChart/index.d.ts +2 -0
- package/dist/components/Charts/FunnelChart/styles.d.ts +10 -0
- package/dist/components/Charts/FunnelChart/types.d.ts +62 -0
- package/dist/components/Charts/FunnelChart/utils.d.ts +25 -0
- package/dist/components/Charts/index.d.ts +1 -0
- package/dist/components/Drawer/DrawerActions.d.ts +44 -0
- package/dist/components/Drawer/DrawerFooter.d.ts +33 -0
- package/dist/components/Drawer/DrawerRoot.d.ts +10 -4
- package/dist/components/Drawer/constants.d.ts +17 -0
- package/dist/components/Drawer/index.parts.d.ts +2 -0
- package/dist/components/Drawer/useDrawer.d.ts +23 -0
- package/dist/components/NavBar/NavBarList.d.ts +5 -0
- package/dist/components/Table/ColumnResizeContext.d.ts +30 -0
- package/dist/components/Table/ColumnResizeHandle.d.ts +47 -0
- package/dist/components/Table/ColumnResizeHandle.types.d.ts +29 -0
- package/dist/components/Table/Table.d.ts +67 -5
- package/dist/components/Table/TableBase.d.ts +36 -0
- package/dist/components/Table/consts.d.ts +8 -0
- package/dist/components/Table/hooks/useColumnResize.d.ts +76 -0
- package/dist/components/Table/index.d.ts +5 -0
- package/dist/components/Table/types.d.ts +149 -0
- package/dist/components/TableCellHeader/TableCellHeader.d.ts +2 -1
- package/dist/components/TableCellHeader/types.d.ts +18 -1
- package/dist/components/Tooltip/types.d.ts +2 -2
- package/dist/components/Typeahead/Typeahead.context.d.ts +1 -0
- package/dist/components/Typeahead/Typeahead.d.ts +1 -1
- package/dist/components/Typeahead/singleLine.d.ts +52 -0
- package/dist/components/Typeahead/styles.d.ts +19 -0
- package/dist/components/Typeahead/types.d.ts +10 -1
- package/dist/components/Typeahead/useTypeahead.d.ts +2 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/index.js +2365 -325
- package/dist/index.mjs +2361 -326
- package/dist/tsbuildcache +1 -1
- package/dist/types/emotion.d.ts +9 -0
- package/package.json +3 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface DrawerFooterProps extends React.HTMLAttributes<HTMLElement> {
|
|
2
|
+
children?: React.ReactNode;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Drawer.Footer - A sticky action bar across the bottom of the panel.
|
|
6
|
+
*
|
|
7
|
+
* Holds the drawer's closing actions — cancel and confirm, clear and apply —
|
|
8
|
+
* above a full-width divider. It stays at the bottom whether the body is short
|
|
9
|
+
* or scrolling, and it only handles layout: the buttons are ordinary `Button`s,
|
|
10
|
+
* so their labels, variants and disabled states are yours to set.
|
|
11
|
+
*
|
|
12
|
+
* Place it as the last child of `Drawer.Content`. Children are laid out in a
|
|
13
|
+
* row, 20px apart, aligned to the trailing edge.
|
|
14
|
+
*
|
|
15
|
+
* @category Components
|
|
16
|
+
* @subcategory Overlay
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <Drawer.Content>
|
|
21
|
+
* <Drawer.Header>
|
|
22
|
+
* <Drawer.Title>Filters</Drawer.Title>
|
|
23
|
+
* <Drawer.CloseButton />
|
|
24
|
+
* </Drawer.Header>
|
|
25
|
+
* {fields}
|
|
26
|
+
* <Drawer.Footer>
|
|
27
|
+
* <Button variant="custom" text="Clear" onClick={handleClear} />
|
|
28
|
+
* <Button variant="primary" text="View leads" onClick={handleSubmit} />
|
|
29
|
+
* </Drawer.Footer>
|
|
30
|
+
* </Drawer.Content>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare const DrawerFooter: import("react").ForwardRefExoticComponent<DrawerFooterProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -18,7 +18,7 @@ export interface DrawerProps extends UseDrawerOptions {
|
|
|
18
18
|
* └── Drawer.Content — the panel itself
|
|
19
19
|
* ├── Drawer.Header
|
|
20
20
|
* │ ├── Drawer.Title
|
|
21
|
-
* │ └── Drawer.CloseButton
|
|
21
|
+
* │ └── Drawer.Actions — save/search + Drawer.CloseButton
|
|
22
22
|
* └── …your content
|
|
23
23
|
* ```
|
|
24
24
|
* Each layer reads context from the one above, so skipping a level (Content
|
|
@@ -37,7 +37,9 @@ export interface DrawerProps extends UseDrawerOptions {
|
|
|
37
37
|
* across open/close belongs above the drawer, not inside it.
|
|
38
38
|
*
|
|
39
39
|
* Note `opened`/`defaultOpened` are the legacy spelling of `open`/`defaultOpen`;
|
|
40
|
-
* prefer the latter.
|
|
40
|
+
* prefer the latter. `title`/`withCloseButton` are likewise deprecated — they
|
|
41
|
+
* build a header that cannot hold anything else; compose `Drawer.Header`
|
|
42
|
+
* yourself instead.
|
|
41
43
|
*
|
|
42
44
|
* @category Components
|
|
43
45
|
* @subcategory Overlay
|
|
@@ -45,14 +47,18 @@ export interface DrawerProps extends UseDrawerOptions {
|
|
|
45
47
|
* @example
|
|
46
48
|
* ```tsx
|
|
47
49
|
* // Controlled: the trigger lives outside the drawer
|
|
48
|
-
* const drawer = useDrawer(
|
|
50
|
+
* const drawer = useDrawer();
|
|
49
51
|
*
|
|
50
52
|
* <>
|
|
51
53
|
* <Button {...drawer.interactions.getReferenceProps()} text="Settings" />
|
|
52
54
|
* <Drawer.Root store={drawer}>
|
|
53
55
|
* <Drawer.Portal>
|
|
54
56
|
* <Drawer.Overlay>
|
|
55
|
-
* <Drawer.Content
|
|
57
|
+
* <Drawer.Content>
|
|
58
|
+
* <Drawer.Header>
|
|
59
|
+
* <Drawer.Title>Settings</Drawer.Title>
|
|
60
|
+
* <Drawer.CloseButton />
|
|
61
|
+
* </Drawer.Header>
|
|
56
62
|
* {form}
|
|
57
63
|
* </Drawer.Content>
|
|
58
64
|
* </Drawer.Overlay>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel padding, per the design: 24px above the header, 32px gutters.
|
|
3
|
+
*
|
|
4
|
+
* `DrawerHeader` and `DrawerFooter` are sticky bars that have to cover the full
|
|
5
|
+
* width of the panel as content scrolls under them, so both cancel these
|
|
6
|
+
* gutters with a negative margin and re-apply them as their own padding.
|
|
7
|
+
* Keeping the numbers here means the bleed cannot drift from the padding it is
|
|
8
|
+
* cancelling.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PANEL_PADDING_X = 32;
|
|
11
|
+
export declare const PANEL_PADDING_TOP = 24;
|
|
12
|
+
/**
|
|
13
|
+
* Gap between the header and the body, per the design. Applied as the header's
|
|
14
|
+
* bottom padding rather than a margin so it stays opaque while the header is
|
|
15
|
+
* stuck and the body scrolls beneath it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const HEADER_GAP = 24;
|
|
@@ -3,5 +3,7 @@ export { DrawerOverlay as Overlay } from './DrawerOverlay';
|
|
|
3
3
|
export { DrawerPortal as Portal } from './DrawerPortal';
|
|
4
4
|
export { DrawerContent as Content } from './DrawerContent';
|
|
5
5
|
export { DrawerHeader as Header } from './DrawerHeader';
|
|
6
|
+
export { DrawerActions as Actions } from './DrawerActions';
|
|
7
|
+
export { DrawerFooter as Footer } from './DrawerFooter';
|
|
6
8
|
export { DrawerTitle as Title } from './DrawerTitle';
|
|
7
9
|
export { DrawerCloseButton as CloseButton } from './DrawerCloseButton';
|
|
@@ -25,8 +25,30 @@ export type UseDrawerOptions = {
|
|
|
25
25
|
duration?: number;
|
|
26
26
|
dismissable?: boolean;
|
|
27
27
|
position?: Position;
|
|
28
|
+
/**
|
|
29
|
+
* Title for the header that `Drawer.Content` builds for you.
|
|
30
|
+
*
|
|
31
|
+
* @deprecated Compose the header instead — `Drawer.Header` with a
|
|
32
|
+
* `Drawer.Title` inside. The store-built header cannot hold anything else,
|
|
33
|
+
* so a drawer that needs a save button or a search field has to abandon it
|
|
34
|
+
* entirely. Removed in the next major release.
|
|
35
|
+
*/
|
|
28
36
|
title?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Adds a close button to the header that `Drawer.Content` builds for you.
|
|
39
|
+
*
|
|
40
|
+
* @deprecated Render `Drawer.CloseButton` yourself, inside a `Drawer.Header`
|
|
41
|
+
* — usually within a `Drawer.Actions` group. Removed in the next major
|
|
42
|
+
* release.
|
|
43
|
+
*/
|
|
29
44
|
withCloseButton?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Width of the panel for `left`/`right` drawers — a number of px, or any CSS
|
|
47
|
+
* length. `top`/`bottom` drawers span the full width and ignore it.
|
|
48
|
+
*
|
|
49
|
+
* @default 400
|
|
50
|
+
*/
|
|
51
|
+
width?: number | string;
|
|
30
52
|
onOpenChange?: (open: boolean, event?: Event, reason?: OpenChangeReason) => void;
|
|
31
53
|
};
|
|
32
54
|
export declare const useDrawer: (options?: UseDrawerOptions) => {
|
|
@@ -64,6 +86,7 @@ export declare const useDrawer: (options?: UseDrawerOptions) => {
|
|
|
64
86
|
};
|
|
65
87
|
title: string | undefined;
|
|
66
88
|
withCloseButton: boolean;
|
|
89
|
+
width: string | number;
|
|
67
90
|
portalNode: HTMLElement | null;
|
|
68
91
|
setFloating: ((node: HTMLElement | null) => void) & ((node: HTMLElement | null) => void);
|
|
69
92
|
setPortalNode: import("react").Dispatch<import("react").SetStateAction<HTMLElement | null>>;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Also used as the styling base for `CollapsibleNavBarList`, which resets the
|
|
3
|
+
* column layout below — keep the two in sync when changing direction, gap or
|
|
4
|
+
* wrapping here.
|
|
5
|
+
*/
|
|
1
6
|
declare const NavBarList: import("@emotion/styled").StyledComponent<{
|
|
2
7
|
theme?: import("@emotion/react").Theme;
|
|
3
8
|
as?: React.ElementType;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ColumnResizeApi } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Null outside a resizable `Table` — deliberately not a "safe" context.
|
|
4
|
+
* `TableCellHeader` reads it on every render to decide whether it owes the
|
|
5
|
+
* table a drag handle, and the answer is usually no.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ColumnResizeContext: import("react").Context<ColumnResizeApi | null>;
|
|
8
|
+
/**
|
|
9
|
+
* The enclosing table's resize API, or `null` when there isn't one.
|
|
10
|
+
*
|
|
11
|
+
* Lets a custom header cell decide whether it owes the table a drag handle,
|
|
12
|
+
* without knowing how its table was configured.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const MyHeaderCell = ({ children }) => {
|
|
17
|
+
* const columnResize = useColumnResizeContext();
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <TableCell as="th">
|
|
21
|
+
* {children}
|
|
22
|
+
* {columnResize && <ColumnResizeHandle />}
|
|
23
|
+
* </TableCell>
|
|
24
|
+
* );
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @see {@link useColumnResize} - creates the API this reads
|
|
29
|
+
*/
|
|
30
|
+
export declare const useColumnResizeContext: () => ColumnResizeApi | null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ColumnResizeHandleProps } from './ColumnResizeHandle.types';
|
|
2
|
+
/**
|
|
3
|
+
* ColumnResizeHandle - the grab area on a column's right edge
|
|
4
|
+
*
|
|
5
|
+
* Rendered automatically by `TableCellHeader` inside a `Table` with
|
|
6
|
+
* `resizableColumns`, so most tables never name it. Render it by hand when the
|
|
7
|
+
* header cells are something other than `TableCellHeader` but still sit inside
|
|
8
|
+
* a resizable `Table`.
|
|
9
|
+
*
|
|
10
|
+
* Outside a resizable table it renders nothing, which keeps it safe to drop
|
|
11
|
+
* into a shared header cell that is used in both kinds of table.
|
|
12
|
+
*
|
|
13
|
+
* ### Which column it resizes
|
|
14
|
+
*
|
|
15
|
+
* With no `columnIndex` it takes the `cellIndex` of the cell it is rendered in.
|
|
16
|
+
* That is the right answer for every table whose header is one cell per column,
|
|
17
|
+
* and the wrong one as soon as a header cell spans several columns — pass
|
|
18
|
+
* `columnIndex` explicitly there.
|
|
19
|
+
*
|
|
20
|
+
* @category Components
|
|
21
|
+
* @subcategory Data Display
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <Table resizableColumns>
|
|
26
|
+
* <TableHead>
|
|
27
|
+
* <TableRow>
|
|
28
|
+
* <th>
|
|
29
|
+
* Name
|
|
30
|
+
* <ColumnResizeHandle />
|
|
31
|
+
* </th>
|
|
32
|
+
* </TableRow>
|
|
33
|
+
* </TableHead>
|
|
34
|
+
* </Table>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @see {@link Table} - the resizableColumns prop that turns this on
|
|
38
|
+
* @see {@link useColumnResize} - the state and maths behind the handle
|
|
39
|
+
*
|
|
40
|
+
* @accessibility
|
|
41
|
+
* - Focusable `role="separator"` with `aria-orientation="vertical"`
|
|
42
|
+
* - Left/Right arrows resize in 8px steps, Shift + arrow in 40px steps
|
|
43
|
+
* - Enter, or a double click, restores the column's starting width
|
|
44
|
+
* - Reports the live width through `aria-valuenow`
|
|
45
|
+
*/
|
|
46
|
+
export declare const ColumnResizeHandle: ({ columnIndex, label, ...rest }: ColumnResizeHandleProps) => import("@emotion/react/jsx-runtime").JSX.Element | null;
|
|
47
|
+
export default ColumnResizeHandle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props for the ColumnResizeHandle component
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* // Inside a TableCellHeader, index taken from the cell
|
|
8
|
+
* <ColumnResizeHandle />
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* // Explicit column, for a header that does not map one cell per column
|
|
14
|
+
* <ColumnResizeHandle columnIndex={2} label="Created at" />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export interface ColumnResizeHandleProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'role' | 'tabIndex' | 'onPointerDown' | 'onPointerMove' | 'onPointerUp' | 'onPointerCancel' | 'onDoubleClick' | 'onKeyDown'> {
|
|
18
|
+
/**
|
|
19
|
+
* Which column this handle resizes. Defaults to the `cellIndex` of the cell
|
|
20
|
+
* the handle is rendered in, which is only wrong when a header cell spans
|
|
21
|
+
* more than one column.
|
|
22
|
+
*/
|
|
23
|
+
columnIndex?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Column name used in the handle's accessible label. Defaults to the text of
|
|
26
|
+
* the cell the handle sits in.
|
|
27
|
+
*/
|
|
28
|
+
label?: string;
|
|
29
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TableProps } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Table - Base table component for displaying tabular data
|
|
4
4
|
*
|
|
@@ -16,6 +16,26 @@ import { CommonProps } from '../../types/emotion';
|
|
|
16
16
|
* - TableRow (data rows)
|
|
17
17
|
* - TableCell (data cells)
|
|
18
18
|
*
|
|
19
|
+
* ### Resizable columns
|
|
20
|
+
*
|
|
21
|
+
* `resizableColumns` gives every header cell a drag handle on its right edge.
|
|
22
|
+
* The table measures its own columns on first layout, so it opens at the widths
|
|
23
|
+
* it would have had anyway, then switches to `table-layout: fixed` and
|
|
24
|
+
* truncates overflowing cell content with an ellipsis.
|
|
25
|
+
*
|
|
26
|
+
* By default a resize is a trade between two columns: the one to the right
|
|
27
|
+
* gives up exactly what the dragged column gains, so the table stays the width
|
|
28
|
+
* it was and nothing around it reflows. The last column has nothing to its
|
|
29
|
+
* right to trade with, so it has no handle.
|
|
30
|
+
*
|
|
31
|
+
* `columnResizeMode="expand"` opts into the other model — the table grows
|
|
32
|
+
* with the dragged column and the others keep their widths — for tables that
|
|
33
|
+
* have a horizontally scrollable parent to grow into.
|
|
34
|
+
*
|
|
35
|
+
* Widths are not persisted: read them from `onColumnWidthsChange`, store them
|
|
36
|
+
* wherever the app keeps user preferences, and feed them back through
|
|
37
|
+
* `defaultColumnWidths`.
|
|
38
|
+
*
|
|
19
39
|
* @category Components
|
|
20
40
|
* @subcategory Data Display
|
|
21
41
|
*
|
|
@@ -44,6 +64,48 @@ import { CommonProps } from '../../types/emotion';
|
|
|
44
64
|
*
|
|
45
65
|
* @example
|
|
46
66
|
* ```tsx
|
|
67
|
+
* // Resizable columns, widths measured from the content
|
|
68
|
+
* <Table resizableColumns>
|
|
69
|
+
* <TableHead>
|
|
70
|
+
* <TableRow>
|
|
71
|
+
* <TableCellHeader>Name</TableCellHeader>
|
|
72
|
+
* <TableCellHeader>Email</TableCellHeader>
|
|
73
|
+
* </TableRow>
|
|
74
|
+
* </TableHead>
|
|
75
|
+
* <TableBody>{rows}</TableBody>
|
|
76
|
+
* </Table>
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* // Columns keep their widths and the table grows, so it needs to be able to
|
|
82
|
+
* // scroll sideways
|
|
83
|
+
* <Wrapper css={{ display: 'block', overflowX: 'auto' }}>
|
|
84
|
+
* <Table resizableColumns columnResizeMode="expand">
|
|
85
|
+
* {children}
|
|
86
|
+
* </Table>
|
|
87
|
+
* </Wrapper>
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```tsx
|
|
92
|
+
* // Resizable columns, widths owned and persisted by the caller
|
|
93
|
+
* const [widths, setWidths] = useState(loadWidths);
|
|
94
|
+
*
|
|
95
|
+
* <Table
|
|
96
|
+
* resizableColumns
|
|
97
|
+
* columnWidths={widths}
|
|
98
|
+
* minColumnWidth={96}
|
|
99
|
+
* onColumnWidthsChange={(next) => {
|
|
100
|
+
* setWidths(next);
|
|
101
|
+
* saveWidths(next);
|
|
102
|
+
* }}>
|
|
103
|
+
* {children}
|
|
104
|
+
* </Table>
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```tsx
|
|
47
109
|
* // Table with custom styling
|
|
48
110
|
* <Table css={{ border: '1px solid #ccc' }}>
|
|
49
111
|
* <TableHead>
|
|
@@ -66,15 +128,15 @@ import { CommonProps } from '../../types/emotion';
|
|
|
66
128
|
* @see {@link TableRow} - Row component
|
|
67
129
|
* @see {@link TableCell} - Data cell component
|
|
68
130
|
* @see {@link TableCellHeader} - Header cell component
|
|
131
|
+
* @see {@link useColumnResize} - the resizing state, for custom header layouts
|
|
132
|
+
* @see {@link ColumnResizeHandle} - the drag handle header cells render
|
|
69
133
|
*
|
|
70
134
|
* @accessibility
|
|
71
135
|
* - Semantic HTML table element
|
|
72
136
|
* - Proper table structure for screen readers
|
|
73
137
|
* - Keyboard navigation support
|
|
74
138
|
* - ARIA attributes when needed
|
|
139
|
+
* - Resize handles are keyboard operable; see {@link ColumnResizeHandle}
|
|
75
140
|
*/
|
|
76
|
-
declare const Table: import("
|
|
77
|
-
theme?: import("@emotion/react").Theme;
|
|
78
|
-
as?: React.ElementType;
|
|
79
|
-
} & CommonProps, import("react").DetailedHTMLProps<import("react").TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, {}>;
|
|
141
|
+
declare const Table: import("react").ForwardRefExoticComponent<TableProps & import("react").RefAttributes<HTMLTableElement>>;
|
|
80
142
|
export default Table;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CommonProps } from '../../types/emotion';
|
|
2
|
+
import { ColumnResizeMode } from './types';
|
|
3
|
+
export interface TableBaseProps extends CommonProps {
|
|
4
|
+
/** Applies the truncation rules resizing depends on. */
|
|
5
|
+
isResizable?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Whether the columns have been measured yet. Fixed layout is what makes a
|
|
8
|
+
* dragged width stick, but it can only be switched on once there are widths
|
|
9
|
+
* to switch to.
|
|
10
|
+
*/
|
|
11
|
+
hasColumnWidths?: boolean;
|
|
12
|
+
/** How a resize distributes width. See TableProps.columnResizeMode. */
|
|
13
|
+
columnResizeMode?: ColumnResizeMode;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The styled element behind `Table`.
|
|
17
|
+
*
|
|
18
|
+
* Kept separate so `Table` can stay a component with state and a colgroup while
|
|
19
|
+
* everything that composes it — `NestedTable`, consumer `styled(Table)` calls —
|
|
20
|
+
* keeps getting a plain table element underneath.
|
|
21
|
+
*
|
|
22
|
+
* ### Nothing per-pixel belongs in here
|
|
23
|
+
*
|
|
24
|
+
* Every distinct value interpolated into this template mints another emotion
|
|
25
|
+
* class and injects another stylesheet rule, permanently. A width that changes
|
|
26
|
+
* with the pointer would add one rule per frame of every drag — a few hundred
|
|
27
|
+
* within seconds — and each insertion invalidates the document's style cache,
|
|
28
|
+
* so resizing gets progressively slower until the page is reloaded. The table's
|
|
29
|
+
* width in `expand` mode is therefore an inline style, set by `Table`, and the
|
|
30
|
+
* props here only ever take a handful of values.
|
|
31
|
+
*/
|
|
32
|
+
declare const TableBase: import("@emotion/styled").StyledComponent<{
|
|
33
|
+
theme?: import("@emotion/react").Theme;
|
|
34
|
+
as?: React.ElementType;
|
|
35
|
+
} & TableBaseProps, import("react").DetailedHTMLProps<import("react").TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, {}>;
|
|
36
|
+
export default TableBase;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Smallest width a column can be dragged to, in pixels. */
|
|
2
|
+
export declare const DEFAULT_MIN_COLUMN_WIDTH = 64;
|
|
3
|
+
/** Width change per arrow-key press on a resize handle, in pixels. */
|
|
4
|
+
export declare const KEYBOARD_RESIZE_STEP = 8;
|
|
5
|
+
/** Width change per Shift + arrow-key press on a resize handle, in pixels. */
|
|
6
|
+
export declare const KEYBOARD_RESIZE_STEP_LARGE = 40;
|
|
7
|
+
/** Hit area of the drag handle, inset into the right edge of the cell. */
|
|
8
|
+
export declare const RESIZE_HANDLE_WIDTH = 8;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ColumnResizeApi, UseColumnResizeOptions } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Column resizing for a `Table`, without any of the rendering.
|
|
4
|
+
*
|
|
5
|
+
* Owns the width of every column and the pointer/keyboard maths that moves it.
|
|
6
|
+
* `Table`'s `resizableColumns` prop is a thin wrapper over this hook — reach for
|
|
7
|
+
* the hook directly when the header is built from something other than
|
|
8
|
+
* `TableCellHeader`, or when the handles need to live somewhere unusual.
|
|
9
|
+
*
|
|
10
|
+
* ## Widths are measured, not invented
|
|
11
|
+
*
|
|
12
|
+
* With no `defaultColumnWidths`, the first layout pass reads the columns' real
|
|
13
|
+
* rendered widths off the DOM while the table is still in `table-layout: auto`,
|
|
14
|
+
* and seeds state from those. The table only switches to `fixed` once it has
|
|
15
|
+
* numbers to switch to, so an uncontrolled table starts out looking exactly as
|
|
16
|
+
* it did before resizing was turned on. Under jsdom every measurement comes back
|
|
17
|
+
* `0`; the hook then stays unseeded, so tests that care about widths should pass
|
|
18
|
+
* `defaultColumnWidths` explicitly.
|
|
19
|
+
*
|
|
20
|
+
* ## Two ways to spend the width
|
|
21
|
+
*
|
|
22
|
+
* In the default `fit` mode a resize is a trade: the column to the right gives
|
|
23
|
+
* up exactly what the dragged column gains, so the table's own width never
|
|
24
|
+
* changes and no surrounding layout moves. The last column has nothing to its
|
|
25
|
+
* right and so cannot be dragged.
|
|
26
|
+
*
|
|
27
|
+
* In `expand` mode the table grows instead, and the columns beside the dragged
|
|
28
|
+
* one keep their widths. That needs a horizontally scrollable container, or the
|
|
29
|
+
* table will overflow whatever is holding it.
|
|
30
|
+
*
|
|
31
|
+
* A column marked `data-column-pinned="true"` on its header cell never pays for
|
|
32
|
+
* the column before it in `fit` mode, so that handle is inert — pinning a
|
|
33
|
+
* column pins it against every route to changing its width, not just its own
|
|
34
|
+
* handle.
|
|
35
|
+
*
|
|
36
|
+
* ## Stored widths are not always rendered widths
|
|
37
|
+
*
|
|
38
|
+
* A table in `fit` mode is still `width: 100%`, and fixed layout spreads any
|
|
39
|
+
* difference between the widths it was given and the width it actually has
|
|
40
|
+
* across the columns in proportion. Widths of `[220, 140, 140, 96]` in a
|
|
41
|
+
* 1248px table therefore render at a little over twice their stated size.
|
|
42
|
+
*
|
|
43
|
+
* That makes two coordinate spaces: the caller's units, which the widths and
|
|
44
|
+
* the min/max bounds are in, and rendered pixels, which is what a pointer
|
|
45
|
+
* moves through. Every gesture is converted between them, so a column tracks
|
|
46
|
+
* the pointer exactly no matter how far apart the two spaces are — without
|
|
47
|
+
* quietly rewriting the numbers the caller chose.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* const { columnWidths, tableRef, getResizeHandleProps } = useColumnResize({
|
|
52
|
+
* defaultColumnWidths: [240, 160, 160],
|
|
53
|
+
* onColumnWidthsChange: (widths) => localStorage.setItem('cols', JSON.stringify(widths)),
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* <table ref={tableRef} style={{ tableLayout: 'fixed' }}>
|
|
57
|
+
* <colgroup>
|
|
58
|
+
* {columnWidths.map((width, index) => <col key={index} style={{ width }} />)}
|
|
59
|
+
* </colgroup>
|
|
60
|
+
* <thead>
|
|
61
|
+
* <tr>
|
|
62
|
+
* {headers.map((header, index) => (
|
|
63
|
+
* <th key={header}>
|
|
64
|
+
* {header}
|
|
65
|
+
* <span {...getResizeHandleProps(index)} />
|
|
66
|
+
* </th>
|
|
67
|
+
* ))}
|
|
68
|
+
* </tr>
|
|
69
|
+
* </thead>
|
|
70
|
+
* </table>
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @see {@link Table} - pass `resizableColumns` to get all of this wired up
|
|
74
|
+
* @see {@link ColumnResizeHandle} - the handle `TableCellHeader` renders for you
|
|
75
|
+
*/
|
|
76
|
+
export declare const useColumnResize: (options?: UseColumnResizeOptions) => ColumnResizeApi;
|
|
@@ -1 +1,6 @@
|
|
|
1
1
|
export { default } from './Table';
|
|
2
|
+
export { default as ColumnResizeHandle } from './ColumnResizeHandle';
|
|
3
|
+
export { useColumnResize } from './hooks/useColumnResize';
|
|
4
|
+
export { useColumnResizeContext } from './ColumnResizeContext';
|
|
5
|
+
export * from './types';
|
|
6
|
+
export * from './ColumnResizeHandle.types';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { AriaAttributes, KeyboardEvent, PointerEvent, RefObject, TableHTMLAttributes } from 'react';
|
|
2
|
+
import { CommonProps } from '../../types/emotion';
|
|
3
|
+
/**
|
|
4
|
+
* How a resize distributes width.
|
|
5
|
+
*
|
|
6
|
+
* - `fit` — the column to the right gives up what the dragged column gains, so
|
|
7
|
+
* the table's own width never changes. The last column cannot be dragged,
|
|
8
|
+
* having nothing to its right to trade with.
|
|
9
|
+
* - `expand` — the table grows and the other columns keep their widths, so the
|
|
10
|
+
* table needs a horizontally scrollable parent.
|
|
11
|
+
*/
|
|
12
|
+
export type ColumnResizeMode = 'fit' | 'expand';
|
|
13
|
+
/** Options shared by `useColumnResize` and `Table`'s resizable mode. */
|
|
14
|
+
export interface ColumnResizeOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Column widths in pixels, one per column. Passing this prop puts the table
|
|
17
|
+
* into controlled mode for the life of the component — hold the value in
|
|
18
|
+
* state and update it from `onColumnWidthsChange`, or the columns will not
|
|
19
|
+
* move when dragged.
|
|
20
|
+
*/
|
|
21
|
+
columnWidths?: number[];
|
|
22
|
+
/**
|
|
23
|
+
* Starting column widths in pixels, for the uncontrolled case. Omit it and
|
|
24
|
+
* the table measures its own columns on first layout, so it opens at whatever
|
|
25
|
+
* widths it would have had without resizing.
|
|
26
|
+
*/
|
|
27
|
+
defaultColumnWidths?: number[];
|
|
28
|
+
/**
|
|
29
|
+
* Called on every width change, including each frame of a drag. Persisting
|
|
30
|
+
* widths is the caller's job — write them to storage or user preferences here
|
|
31
|
+
* and feed them back through `defaultColumnWidths`.
|
|
32
|
+
*/
|
|
33
|
+
onColumnWidthsChange?: (columnWidths: number[]) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Narrowest a column can be dragged, in pixels.
|
|
36
|
+
* @default 64
|
|
37
|
+
*/
|
|
38
|
+
minColumnWidth?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Widest a column can be dragged, in pixels. Unbounded when omitted.
|
|
41
|
+
*/
|
|
42
|
+
maxColumnWidth?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a resize is paid for by the next column or by the table's own
|
|
45
|
+
* width. Defaults to `fit`, which keeps the table exactly as wide as it was
|
|
46
|
+
* — surrounding layout never moves because a user dragged a column.
|
|
47
|
+
*
|
|
48
|
+
* Switch to `expand` when the table lives in its own scroll container and
|
|
49
|
+
* columns should keep the widths the user gave them.
|
|
50
|
+
*
|
|
51
|
+
* @default 'fit'
|
|
52
|
+
*/
|
|
53
|
+
columnResizeMode?: ColumnResizeMode;
|
|
54
|
+
}
|
|
55
|
+
/** Props for the element that starts a drag. Spread onto any element. */
|
|
56
|
+
export interface ResizeHandleProps extends Pick<AriaAttributes, 'aria-orientation' | 'aria-valuenow' | 'aria-valuemin' | 'aria-valuemax'> {
|
|
57
|
+
role: 'separator';
|
|
58
|
+
tabIndex: number;
|
|
59
|
+
'data-resizing'?: 'true';
|
|
60
|
+
onPointerDown: (event: PointerEvent<HTMLElement>) => void;
|
|
61
|
+
onPointerMove: (event: PointerEvent<HTMLElement>) => void;
|
|
62
|
+
onPointerUp: (event: PointerEvent<HTMLElement>) => void;
|
|
63
|
+
onPointerCancel: (event: PointerEvent<HTMLElement>) => void;
|
|
64
|
+
onDoubleClick: () => void;
|
|
65
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Options for {@link useColumnResize}.
|
|
69
|
+
*
|
|
70
|
+
* Everything `Table` accepts for resizing, plus the two escape hatches `Table`
|
|
71
|
+
* itself needs — `enabled` and `controlled`.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* useColumnResize({
|
|
76
|
+
* defaultColumnWidths: [240, 160, 160],
|
|
77
|
+
* minColumnWidth: 96,
|
|
78
|
+
* onColumnWidthsChange: saveWidths,
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export interface UseColumnResizeOptions extends ColumnResizeOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Whether to measure and track widths at all. `Table` passes its
|
|
85
|
+
* `resizableColumns` prop through so the hook can be called unconditionally.
|
|
86
|
+
* @default true
|
|
87
|
+
*/
|
|
88
|
+
enabled?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Overrides the controlled/uncontrolled decision, which is otherwise taken
|
|
91
|
+
* from whether `columnWidths` was passed. `Table` sets it explicitly because
|
|
92
|
+
* it rebuilds the options object rather than forwarding its own props.
|
|
93
|
+
*/
|
|
94
|
+
controlled?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/** What `useColumnResize` hands back. */
|
|
97
|
+
export interface ColumnResizeApi {
|
|
98
|
+
/** Current width of each column, in pixels. Empty until measured. */
|
|
99
|
+
columnWidths: number[];
|
|
100
|
+
/** Attach to the `table` element so the hook can measure its columns. */
|
|
101
|
+
tableRef: RefObject<HTMLTableElement | null>;
|
|
102
|
+
/** Whether a drag is in progress. */
|
|
103
|
+
isResizing: boolean;
|
|
104
|
+
/** The column being dragged, or `null`. */
|
|
105
|
+
resizingColumnIndex: number | null;
|
|
106
|
+
minColumnWidth: number;
|
|
107
|
+
maxColumnWidth?: number;
|
|
108
|
+
columnResizeMode: ColumnResizeMode;
|
|
109
|
+
/**
|
|
110
|
+
* Sets one column's width, clamped to the min/max. In `fit` mode the width is
|
|
111
|
+
* taken from, or given back to, the column on the right, and the request is
|
|
112
|
+
* clamped by that column's limits as well as this one's.
|
|
113
|
+
*/
|
|
114
|
+
setColumnWidth: (index: number, width: number) => void;
|
|
115
|
+
/** Adds to one column's width, clamped to the min/max. */
|
|
116
|
+
resizeColumnBy: (index: number, delta: number) => void;
|
|
117
|
+
/** Restores a column to the width it started at. */
|
|
118
|
+
resetColumn: (index: number) => void;
|
|
119
|
+
/** Builds the props for the drag handle of a given column. */
|
|
120
|
+
getResizeHandleProps: (index: number) => ResizeHandleProps;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Props for the {@link Table} component.
|
|
124
|
+
*
|
|
125
|
+
* Every attribute of a `table` element, plus the column-resizing options that
|
|
126
|
+
* `resizableColumns` turns on.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```tsx
|
|
130
|
+
* <Table resizableColumns minColumnWidth={96}>
|
|
131
|
+
* {children}
|
|
132
|
+
* </Table>
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export interface TableProps extends CommonProps, ColumnResizeOptions, TableHTMLAttributes<HTMLTableElement> {
|
|
136
|
+
/**
|
|
137
|
+
* Lets columns be resized by dragging the right edge of each header cell.
|
|
138
|
+
*
|
|
139
|
+
* Turning this on switches the table to `table-layout: fixed` once it has
|
|
140
|
+
* measured its columns, truncates overflowing cell content with an ellipsis,
|
|
141
|
+
* and makes every `TableCellHeader` render its own drag handle.
|
|
142
|
+
*
|
|
143
|
+
* By default the table's own width does not change — see
|
|
144
|
+
* `columnResizeMode`.
|
|
145
|
+
*
|
|
146
|
+
* @default false
|
|
147
|
+
*/
|
|
148
|
+
resizableColumns?: boolean;
|
|
149
|
+
}
|
|
@@ -53,6 +53,7 @@ import { TableCellHeaderProps } from './types';
|
|
|
53
53
|
* @see {@link TableRow} - Row component (use in TableHead)
|
|
54
54
|
* @see {@link TableCell} - Data cell component (use in TableBody)
|
|
55
55
|
* @see {@link TableBody} - Body section component
|
|
56
|
+
* @see {@link ColumnResizeHandle} - the handle rendered inside resizable headers
|
|
56
57
|
*
|
|
57
58
|
* @accessibility
|
|
58
59
|
* - Semantic HTML th element (renders as th)
|
|
@@ -60,5 +61,5 @@ import { TableCellHeaderProps } from './types';
|
|
|
60
61
|
* - Supports ARIA attributes via TableCellProps
|
|
61
62
|
* - Better accessibility than using TableCell in headers
|
|
62
63
|
*/
|
|
63
|
-
declare const TableCellHeader: ({ children, ...props }: React.PropsWithChildren<TableCellHeaderProps>) => import("@emotion/react/jsx-runtime").JSX.Element;
|
|
64
|
+
declare const TableCellHeader: ({ children, resizable, ...props }: React.PropsWithChildren<TableCellHeaderProps>) => import("@emotion/react/jsx-runtime").JSX.Element;
|
|
64
65
|
export default TableCellHeader;
|