@scalewing/react 1.20.0 → 1.22.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/CHANGELOG.md +29 -0
- package/README.md +47 -5
- package/dist/components/ActionMenu.d.ts +13 -1
- package/dist/components/ActionMenu.js +2 -2
- package/dist/components/BarChart.d.ts +28 -0
- package/dist/components/BarChart.js +5 -4
- package/dist/components/CalendarButton.d.ts +16 -0
- package/dist/components/CalendarButton.js +5 -3
- package/dist/components/DateField.d.ts +8 -1
- package/dist/components/DateField.js +2 -1
- package/dist/components/DenominationGrid.d.ts +2 -2
- package/dist/components/DenominationGrid.js +5 -2
- package/dist/components/Field.d.ts +9 -0
- package/dist/components/Field.js +3 -2
- package/dist/components/SectionNav.d.ts +44 -0
- package/dist/components/SectionNav.js +48 -0
- package/dist/components/Table.d.ts +38 -13
- package/dist/components/Table.js +7 -12
- package/dist/components/Tabs.d.ts +3 -1
- package/dist/components/Tabs.js +8 -2
- package/dist/components/Tooltip.js +19 -3
- package/dist/components/action-menu/ActionMenuList.d.ts +3 -2
- package/dist/components/action-menu/ActionMenuList.js +16 -4
- package/dist/components/date-field/CalendarDialog.d.ts +4 -1
- package/dist/components/date-field/CalendarDialog.js +2 -2
- package/dist/components/date-field/CalendarGrid.d.ts +4 -1
- package/dist/components/date-field/CalendarGrid.js +11 -3
- package/dist/components/date-field/use-calendar-popup.d.ts +6 -1
- package/dist/components/date-field/use-calendar-popup.js +5 -3
- package/dist/components/denomination-grid/DenominationStrip.d.ts +2 -2
- package/dist/components/denomination-grid/DenominationStrip.js +17 -6
- package/dist/components/denomination-grid/types.d.ts +15 -2
- package/dist/components/table/TableCell.d.ts +42 -0
- package/dist/components/table/TableCell.js +27 -0
- package/dist/components/table/table-layout.d.ts +10 -0
- package/dist/components/table/table-layout.js +14 -0
- package/dist/components/tooltip/tooltip-open.d.ts +8 -0
- package/dist/components/tooltip/tooltip-open.js +60 -0
- package/dist/components/tooltip/use-tooltip-placement.d.ts +11 -0
- package/dist/components/tooltip/use-tooltip-placement.js +46 -0
- package/dist/components/use-anchored-popover.d.ts +2 -0
- package/dist/components/use-anchored-popover.js +2 -1
- package/dist/css/css-action-menu.js +24 -0
- package/dist/css/css-chart.js +45 -1
- package/dist/css/css-components.js +3 -0
- package/dist/css/css-date-field.js +47 -1
- package/dist/css/css-denomination-aligned.d.ts +7 -0
- package/dist/css/css-denomination-aligned.js +68 -0
- package/dist/css/css-denomination-grid.js +7 -1
- package/dist/css/css-field.js +14 -0
- package/dist/css/css-scroll-region.d.ts +8 -0
- package/dist/css/css-scroll-region.js +9 -3
- package/dist/css/css-section-nav.d.ts +4 -0
- package/dist/css/css-section-nav.js +120 -0
- package/dist/css/css-table-layout.d.ts +20 -0
- package/dist/css/css-table-layout.js +59 -0
- package/dist/css/css-table-sort.d.ts +2 -0
- package/dist/css/css-table-sort.js +105 -0
- package/dist/css/css-table.js +8 -0
- package/dist/css/css-tabs.d.ts +6 -1
- package/dist/css/css-tabs.js +9 -1
- package/dist/css/css-tooltip.js +7 -4
- package/dist/css/stylesheet.js +2 -0
- package/dist/date-only.d.ts +12 -0
- package/dist/date-only.js +24 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -0
- package/dist/styles.css +363 -6
- package/package.json +1 -1
|
@@ -1,13 +1,50 @@
|
|
|
1
|
-
import { type HTMLAttributes
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import { type TableLayout, type TableVerticalAlign } from './table/table-layout.js';
|
|
3
|
+
export { TableCell, type TableCellProps, type TableColumnWidth, type TableSort, } from './table/TableCell.js';
|
|
4
|
+
export type { TableLayout, TableVerticalAlign };
|
|
2
5
|
export type TableDensity = 'comfortable' | 'compact';
|
|
3
6
|
export type TableProps = HTMLAttributes<HTMLTableElement> & {
|
|
4
7
|
density?: TableDensity;
|
|
5
8
|
stickyHeader?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* `auto` (default) sizes each column by its content. `fixed` sizes the
|
|
11
|
+
* columns from the header row's `width`s alone (`table-layout: fixed`),
|
|
12
|
+
* so no column moves when the rows change, such as when a filter hides
|
|
13
|
+
* one. Columns without a width share what is left equally; when every
|
|
14
|
+
* column has one, spare width is shared in proportion to them. When the
|
|
15
|
+
* widths add up to more than the container, the table scrolls in its
|
|
16
|
+
* region and a column without a width gets no room at all, so on a table
|
|
17
|
+
* that can be narrower than its widths give every column one.
|
|
18
|
+
*/
|
|
19
|
+
layout?: TableLayout;
|
|
20
|
+
/**
|
|
21
|
+
* Where a cell's content sits in a row taller than it: `middle` (default)
|
|
22
|
+
* or `top`, for rows where one cell runs to several lines and the others
|
|
23
|
+
* should start on its first line.
|
|
24
|
+
*/
|
|
25
|
+
verticalAlign?: TableVerticalAlign;
|
|
6
26
|
};
|
|
7
27
|
/** A wide table scrolls inside its own `ScrollRegion`, named after the table. */
|
|
8
28
|
export declare const Table: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLTableElement> & {
|
|
9
29
|
density?: TableDensity;
|
|
10
30
|
stickyHeader?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* `auto` (default) sizes each column by its content. `fixed` sizes the
|
|
33
|
+
* columns from the header row's `width`s alone (`table-layout: fixed`),
|
|
34
|
+
* so no column moves when the rows change, such as when a filter hides
|
|
35
|
+
* one. Columns without a width share what is left equally; when every
|
|
36
|
+
* column has one, spare width is shared in proportion to them. When the
|
|
37
|
+
* widths add up to more than the container, the table scrolls in its
|
|
38
|
+
* region and a column without a width gets no room at all, so on a table
|
|
39
|
+
* that can be narrower than its widths give every column one.
|
|
40
|
+
*/
|
|
41
|
+
layout?: TableLayout;
|
|
42
|
+
/**
|
|
43
|
+
* Where a cell's content sits in a row taller than it: `middle` (default)
|
|
44
|
+
* or `top`, for rows where one cell runs to several lines and the others
|
|
45
|
+
* should start on its first line.
|
|
46
|
+
*/
|
|
47
|
+
verticalAlign?: TableVerticalAlign;
|
|
11
48
|
} & import("react").RefAttributes<HTMLTableElement>>;
|
|
12
49
|
export type TableHeaderProps = HTMLAttributes<HTMLTableSectionElement>;
|
|
13
50
|
export declare const TableHeader: import("react").ForwardRefExoticComponent<TableHeaderProps & import("react").RefAttributes<HTMLTableSectionElement>>;
|
|
@@ -19,15 +56,3 @@ export type TableRowProps = HTMLAttributes<HTMLTableRowElement> & {
|
|
|
19
56
|
export declare const TableRow: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLTableRowElement> & {
|
|
20
57
|
selected?: boolean;
|
|
21
58
|
} & import("react").RefAttributes<HTMLTableRowElement>>;
|
|
22
|
-
type TableCellAlign = 'start' | 'end';
|
|
23
|
-
export type TableCellProps = ((TdHTMLAttributes<HTMLTableCellElement> & {
|
|
24
|
-
as?: 'td';
|
|
25
|
-
}) | (ThHTMLAttributes<HTMLTableCellElement> & {
|
|
26
|
-
as: 'th';
|
|
27
|
-
})) & {
|
|
28
|
-
align?: TableCellAlign;
|
|
29
|
-
numeric?: boolean;
|
|
30
|
-
truncate?: boolean;
|
|
31
|
-
};
|
|
32
|
-
export declare const TableCell: import("react").ForwardRefExoticComponent<TableCellProps & import("react").RefAttributes<HTMLTableCellElement>>;
|
|
33
|
-
export {};
|
package/dist/components/Table.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
3
|
import { cx } from '../class-names.js';
|
|
4
4
|
import { ScrollRegion } from './ScrollRegion.js';
|
|
5
|
+
import { assertTableLayout, assertTableVerticalAlign, } from './table/table-layout.js';
|
|
6
|
+
export { TableCell, } from './table/TableCell.js';
|
|
5
7
|
/** A wide table scrolls inside its own `ScrollRegion`, named after the table. */
|
|
6
|
-
export const Table = forwardRef(function Table({ children, className, density = 'comfortable', stickyHeader = true, ...rest }, ref) {
|
|
7
|
-
|
|
8
|
+
export const Table = forwardRef(function Table({ children, className, density = 'comfortable', layout = 'auto', stickyHeader = true, verticalAlign = 'middle', ...rest }, ref) {
|
|
9
|
+
assertTableLayout(layout);
|
|
10
|
+
assertTableVerticalAlign(verticalAlign);
|
|
11
|
+
return (_jsx(ScrollRegion, { "aria-label": rest['aria-label'], "aria-labelledby": rest['aria-labelledby'], className: "sw-table-wrap", children: _jsx("table", { ref: ref, className: cx('sw-table', density === 'compact' && 'sw-table-compact', layout === 'fixed' && 'sw-table-fixed', stickyHeader && 'sw-table-sticky', verticalAlign === 'top' && 'sw-table-top', className), ...rest, children: children }) }));
|
|
8
12
|
});
|
|
9
13
|
export const TableHeader = forwardRef(function TableHeader({ className, ...rest }, ref) {
|
|
10
14
|
return _jsx("thead", { ref: ref, className: className, ...rest });
|
|
@@ -15,12 +19,3 @@ export const TableBody = forwardRef(function TableBody({ className, ...rest }, r
|
|
|
15
19
|
export const TableRow = forwardRef(function TableRow({ className, selected = false, ...rest }, ref) {
|
|
16
20
|
return (_jsx("tr", { ref: ref, "aria-selected": selected || undefined, className: cx(selected && 'sw-table-row-selected', className), ...rest }));
|
|
17
21
|
});
|
|
18
|
-
export const TableCell = forwardRef(function TableCell({ align = 'start', as = 'td', className, numeric = false, truncate = false, ...rest }, ref) {
|
|
19
|
-
const Component = as;
|
|
20
|
-
const alignmentClass = numeric
|
|
21
|
-
? 'sw-table-numeric'
|
|
22
|
-
: align === 'end'
|
|
23
|
-
? 'sw-table-end'
|
|
24
|
-
: undefined;
|
|
25
|
-
return (_jsx(Component, { ref: ref, className: cx(alignmentClass, truncate && 'sw-table-clip', className), ...rest }));
|
|
26
|
-
});
|
|
@@ -41,7 +41,9 @@ export declare function tabPanelId(tabsId: string, id: string): string;
|
|
|
41
41
|
/**
|
|
42
42
|
* A tab strip with `tablist` semantics for the sections of one page. Arrow
|
|
43
43
|
* keys, Home and End move the focus and select at once; the strip scrolls
|
|
44
|
-
* sideways when it overflows
|
|
44
|
+
* sideways when it overflows, and an edge with labels past it draws a shade
|
|
45
|
+
* (`sw-scroll-more-start`, `sw-scroll-more-end`), as a wide `Table` does.
|
|
46
|
+
* Pair each item with a `TabPanel`. `sticky`
|
|
45
47
|
* keeps it at the top of the viewport over a long panel.
|
|
46
48
|
*/
|
|
47
49
|
export declare function Tabs({ id, items, onChange, sticky, value, ...labelProps }: TabsProps): import("react").JSX.Element;
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { useRef, } from 'react';
|
|
3
4
|
import { cx } from '../class-names.js';
|
|
5
|
+
import { useScrollOverflow } from './use-scroll-overflow.js';
|
|
4
6
|
export function tabId(tabsId, id) {
|
|
5
7
|
return `${tabsId}-tab-${id}`;
|
|
6
8
|
}
|
|
@@ -10,11 +12,15 @@ export function tabPanelId(tabsId, id) {
|
|
|
10
12
|
/**
|
|
11
13
|
* A tab strip with `tablist` semantics for the sections of one page. Arrow
|
|
12
14
|
* keys, Home and End move the focus and select at once; the strip scrolls
|
|
13
|
-
* sideways when it overflows
|
|
15
|
+
* sideways when it overflows, and an edge with labels past it draws a shade
|
|
16
|
+
* (`sw-scroll-more-start`, `sw-scroll-more-end`), as a wide `Table` does.
|
|
17
|
+
* Pair each item with a `TabPanel`. `sticky`
|
|
14
18
|
* keeps it at the top of the viewport over a long panel.
|
|
15
19
|
*/
|
|
16
20
|
export function Tabs({ id, items, onChange, sticky = false, value, ...labelProps }) {
|
|
17
21
|
const tabs = useRef(new Map());
|
|
22
|
+
const strip = useRef(null);
|
|
23
|
+
const overflow = useScrollOverflow(strip);
|
|
18
24
|
function select(next) {
|
|
19
25
|
if (!next || next.id === value) {
|
|
20
26
|
return;
|
|
@@ -43,7 +49,7 @@ export function Tabs({ id, items, onChange, sticky = false, value, ...labelProps
|
|
|
43
49
|
event.preventDefault();
|
|
44
50
|
select(target);
|
|
45
51
|
}
|
|
46
|
-
return (_jsx("div", { className: cx('sw-tabs', sticky && 'sw-tabs-sticky'), onKeyDown: onKeyDown, role: "tablist", ...labelProps, children: items.map((item) => {
|
|
52
|
+
return (_jsx("div", { className: cx('sw-tabs', sticky && 'sw-tabs-sticky', overflow.start && 'sw-scroll-more-start', overflow.end && 'sw-scroll-more-end'), onKeyDown: onKeyDown, ref: strip, role: "tablist", ...labelProps, children: items.map((item) => {
|
|
47
53
|
const selected = item.id === value;
|
|
48
54
|
return (_jsx("button", { "aria-controls": tabPanelId(id, item.id), "aria-selected": selected, className: cx('sw-tab', selected && 'sw-tab-selected'), id: tabId(id, item.id), onClick: () => select(item), ref: (node) => {
|
|
49
55
|
if (node) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { cloneElement, useEffect, useId, useRef, useState, } from 'react';
|
|
4
|
+
import { focusIsVisible, tapActs } from './tooltip/tooltip-open.js';
|
|
5
|
+
import { useTooltipPlacement } from './tooltip/use-tooltip-placement.js';
|
|
4
6
|
import { tooltipTriggerAria, } from './tooltip-trigger-aria.js';
|
|
5
7
|
export function Tooltip({ content, disabled = false, relationship = 'description', trigger, }) {
|
|
6
8
|
if (!content.trim())
|
|
@@ -11,6 +13,8 @@ export function Tooltip({ content, disabled = false, relationship = 'description
|
|
|
11
13
|
const shown = open && !disabled;
|
|
12
14
|
const id = useId();
|
|
13
15
|
const wrapperRef = useRef(null);
|
|
16
|
+
const tooltipRef = useRef(null);
|
|
17
|
+
useTooltipPlacement(shown, tooltipRef, wrapperRef);
|
|
14
18
|
const triggerAria = tooltipTriggerAria(trigger.props, disabled ? undefined : id, relationship);
|
|
15
19
|
useEffect(() => {
|
|
16
20
|
if (!open)
|
|
@@ -26,7 +30,13 @@ export function Tooltip({ content, disabled = false, relationship = 'description
|
|
|
26
30
|
return (_jsxs("span", { className: "sw-tooltip-anchor", onBlurCapture: (event) => {
|
|
27
31
|
if (!event.currentTarget.contains(event.relatedTarget))
|
|
28
32
|
setOpen(false);
|
|
29
|
-
}, onFocusCapture: (
|
|
33
|
+
}, onFocusCapture: (event) => {
|
|
34
|
+
// A click or a tap focuses the trigger too; only a visible focus
|
|
35
|
+
// (the keyboard) opens the tooltip, so a clicked control does not
|
|
36
|
+
// keep its help shown until the focus moves.
|
|
37
|
+
if (focusIsVisible(event.target))
|
|
38
|
+
setOpen(true);
|
|
39
|
+
}, onKeyDownCapture: (event) => {
|
|
30
40
|
// Escape hides a visible tooltip and nothing else, so a surrounding
|
|
31
41
|
// Dialog does not also close. A hidden tooltip leaves Escape alone.
|
|
32
42
|
if (event.key !== 'Escape' || !shown)
|
|
@@ -40,7 +50,13 @@ export function Tooltip({ content, disabled = false, relationship = 'description
|
|
|
40
50
|
if (event.pointerType === 'mouse' || event.pointerType === 'pen')
|
|
41
51
|
setOpen(false);
|
|
42
52
|
}, onPointerDownCapture: (event) => {
|
|
43
|
-
if (event.pointerType
|
|
53
|
+
if (event.pointerType !== 'touch')
|
|
54
|
+
return;
|
|
55
|
+
// A tap on a control does what the control does; only a trigger
|
|
56
|
+
// that does nothing else on a tap toggles its help.
|
|
57
|
+
if (tapActs(event.target, event.currentTarget))
|
|
58
|
+
setOpen(false);
|
|
59
|
+
else
|
|
44
60
|
setOpen((current) => !current);
|
|
45
|
-
}, ref: wrapperRef, children: [cloneElement(trigger, triggerAria), disabled ? null : (_jsx("span", { className: "sw-tooltip", hidden: !shown, id: id, role: "tooltip", children: content }))] }));
|
|
61
|
+
}, ref: wrapperRef, children: [cloneElement(trigger, triggerAria), disabled ? null : (_jsx("span", { className: "sw-tooltip", hidden: !shown, id: id, ref: tooltipRef, role: "tooltip", children: content }))] }));
|
|
46
62
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type KeyboardEvent, type RefObject } from 'react';
|
|
1
|
+
import { type KeyboardEvent, type ReactNode, type RefObject } from 'react';
|
|
2
2
|
import { type InlineAlign } from '../../anchored-position.js';
|
|
3
3
|
import type { ActionMenuItem } from '../ActionMenu.js';
|
|
4
4
|
type ActionMenuListProps = {
|
|
5
5
|
id: string;
|
|
6
6
|
align: InlineAlign;
|
|
7
|
+
header?: ReactNode;
|
|
7
8
|
label: string;
|
|
8
9
|
items: readonly ActionMenuItem[];
|
|
9
10
|
focusIndex: number;
|
|
@@ -19,5 +20,5 @@ type ActionMenuListProps = {
|
|
|
19
20
|
* or, with `align="end"`, its end, and with the other edge when that one
|
|
20
21
|
* would not fit), with the focused command holding DOM focus.
|
|
21
22
|
*/
|
|
22
|
-
export declare function ActionMenuList({ id, align, label, items, focusIndex, triggerRef, onDismiss, onFocusIndex, onKeyDown, onSelect, }: ActionMenuListProps): import("react").JSX.Element;
|
|
23
|
+
export declare function ActionMenuList({ id, align, header, label, items, focusIndex, triggerRef, onDismiss, onFocusIndex, onKeyDown, onSelect, }: ActionMenuListProps): import("react").JSX.Element;
|
|
23
24
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useLayoutEffect, useRef, } from 'react';
|
|
3
|
+
import { useId, useLayoutEffect, useRef, } from 'react';
|
|
4
4
|
import { cx } from '../../class-names.js';
|
|
5
5
|
import { useAnchoredPopover } from '../use-anchored-popover.js';
|
|
6
6
|
/**
|
|
@@ -9,7 +9,7 @@ import { useAnchoredPopover } from '../use-anchored-popover.js';
|
|
|
9
9
|
* or, with `align="end"`, its end, and with the other edge when that one
|
|
10
10
|
* would not fit), with the focused command holding DOM focus.
|
|
11
11
|
*/
|
|
12
|
-
export function ActionMenuList({ id, align, label, items, focusIndex, triggerRef, onDismiss, onFocusIndex, onKeyDown, onSelect, }) {
|
|
12
|
+
export function ActionMenuList({ id, align, header, label, items, focusIndex, triggerRef, onDismiss, onFocusIndex, onKeyDown, onSelect, }) {
|
|
13
13
|
const menuRef = useRef(null);
|
|
14
14
|
useAnchoredPopover(menuRef, triggerRef, triggerRef, onDismiss, {
|
|
15
15
|
align,
|
|
@@ -19,10 +19,22 @@ export function ActionMenuList({ id, align, label, items, focusIndex, triggerRef
|
|
|
19
19
|
const commands = menuRef.current?.querySelectorAll('[role="menuitem"]');
|
|
20
20
|
commands?.[focusIndex]?.focus();
|
|
21
21
|
}, [focusIndex]);
|
|
22
|
-
|
|
22
|
+
const headerId = useId();
|
|
23
|
+
// An empty header (nothing, false or '') is no header, so the menu is never
|
|
24
|
+
// described by an empty element.
|
|
25
|
+
const hasHeader = header !== undefined &&
|
|
26
|
+
header !== null &&
|
|
27
|
+
header !== false &&
|
|
28
|
+
header !== '';
|
|
29
|
+
const menu = (_jsx("div", { "aria-describedby": hasHeader ? headerId : undefined, "aria-label": label, className: hasHeader ? 'sw-action-menu-items' : 'sw-action-menu-list', id: hasHeader ? undefined : id, onKeyDown: onKeyDown, ref: hasHeader ? undefined : menuRef, role: "menu", children: items.map((item, index) => (_jsxs("button", { className: cx('sw-action-menu-item', item.destructive && 'sw-action-menu-item-danger'), disabled: item.disabled, onClick: () => onSelect(item), onFocus: () => onFocusIndex(index), onKeyDown: (event) => {
|
|
23
30
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
24
31
|
event.preventDefault();
|
|
25
32
|
onSelect(item);
|
|
26
33
|
}
|
|
27
|
-
}, role: "menuitem", tabIndex: index === focusIndex ? 0 : -1, type: "button", children: [item.icon ? _jsx("span", { "aria-hidden": "true", children: item.icon }) : null, _jsx("span", { children: item.label })] }, item.id))) }));
|
|
34
|
+
}, role: "menuitem", tabIndex: index === focusIndex ? 0 : -1, type: "button", children: [item.icon ? _jsx("span", { "aria-hidden": "true", children: item.icon }) : null, _jsx("span", { lang: item.lang, children: item.label })] }, item.id))) }));
|
|
35
|
+
if (!hasHeader)
|
|
36
|
+
return menu;
|
|
37
|
+
// The header is not a menu item: it sits above the menu in the same
|
|
38
|
+
// popover, outside the arrow-key order, and describes the menu.
|
|
39
|
+
return (_jsxs("div", { className: "sw-action-menu-list", id: id, ref: menuRef, children: [_jsx("div", { className: "sw-action-menu-header", id: headerId, children: header }), menu] }));
|
|
28
40
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type RefObject } from 'react';
|
|
2
2
|
import { type WeekStart } from '../../calendar-month.js';
|
|
3
3
|
import { type DateFieldLabels } from '../../date-field-labels.js';
|
|
4
|
+
import { type DateOnlyRange } from '../../date-only.js';
|
|
4
5
|
export type CalendarDialogProps = {
|
|
5
6
|
anchorRef: RefObject<HTMLElement | null>;
|
|
6
7
|
buttonRef: RefObject<HTMLElement | null>;
|
|
@@ -13,6 +14,8 @@ export type CalendarDialogProps = {
|
|
|
13
14
|
/** `true` returns focus to the calendar button. */
|
|
14
15
|
onClose: (restoreFocus: boolean) => void;
|
|
15
16
|
onSelect: (value: string) => void;
|
|
17
|
+
/** Days to tint as a span; the selection stays `value`. */
|
|
18
|
+
range?: DateOnlyRange;
|
|
16
19
|
required: boolean;
|
|
17
20
|
today: string;
|
|
18
21
|
value: string;
|
|
@@ -23,4 +26,4 @@ export type CalendarDialogProps = {
|
|
|
23
26
|
* following the WAI-ARIA date picker dialog pattern. It opens on the
|
|
24
27
|
* selected day, or today, kept inside `[min, max]`.
|
|
25
28
|
*/
|
|
26
|
-
export declare function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, locale, max, min, onClose, onSelect, required, today, value, weekStartsOn, }: CalendarDialogProps): import("react").JSX.Element;
|
|
29
|
+
export declare function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, locale, max, min, onClose, onSelect, range, required, today, value, weekStartsOn, }: CalendarDialogProps): import("react").JSX.Element;
|
|
@@ -43,7 +43,7 @@ function sameDayIn(month, focused) {
|
|
|
43
43
|
* following the WAI-ARIA date picker dialog pattern. It opens on the
|
|
44
44
|
* selected day, or today, kept inside `[min, max]`.
|
|
45
45
|
*/
|
|
46
|
-
export function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, locale, max, min, onClose, onSelect, required, today, value, weekStartsOn, }) {
|
|
46
|
+
export function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, locale, max, min, onClose, onSelect, range, required, today, value, weekStartsOn, }) {
|
|
47
47
|
const titleId = useId();
|
|
48
48
|
const dialogRef = useRef(null);
|
|
49
49
|
const [rawFocusDate, setFocusDate] = useState(value || today);
|
|
@@ -89,5 +89,5 @@ export function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, loca
|
|
|
89
89
|
}, onKeyDown: onKeyDown, ref: dialogRef, role: "dialog", children: [_jsx(CalendarHeader, { labels: labels, locale: locale, max: max, min: min, month: month, onMonthChange: (next) => setFocusDate(sameDayIn(next, focusDate)), titleId: titleId, todayYear: monthOf(today).year }), _jsx(CalendarGrid, { focusDate: focusDate, locale: locale, max: max, min: min, month: month, onFocusDate: setFocusDate, onMoveFocus: (next) => {
|
|
90
90
|
focusGrid.current = true;
|
|
91
91
|
setFocusDate(next);
|
|
92
|
-
}, onSelect: onSelect, selected: value, titleId: titleId, today: today, weekStartsOn: weekStartsOn }), _jsxs("div", { className: "sw-date-field-footer", children: [required ? null : (_jsx(Button, { disabled: value === '', onPress: () => onSelect(''), size: "sm", variant: "ghost", children: labels.clear })), _jsx(Button, { disabled: todayUnavailable, onPress: () => onSelect(today), size: "sm", variant: "ghost", children: labels.today })] })] }));
|
|
92
|
+
}, onSelect: onSelect, range: range, selected: value, titleId: titleId, today: today, weekStartsOn: weekStartsOn }), _jsxs("div", { className: "sw-date-field-footer", children: [required ? null : (_jsx(Button, { disabled: value === '', onPress: () => onSelect(''), size: "sm", variant: "ghost", children: labels.clear })), _jsx(Button, { disabled: todayUnavailable, onPress: () => onSelect(today), size: "sm", variant: "ghost", children: labels.today })] })] }));
|
|
93
93
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CalendarMonth, type WeekStart } from '../../calendar-month.js';
|
|
2
|
+
import { type DateOnlyRange } from '../../date-only.js';
|
|
2
3
|
export type CalendarGridProps = {
|
|
3
4
|
focusDate: string;
|
|
4
5
|
locale: string;
|
|
@@ -10,6 +11,8 @@ export type CalendarGridProps = {
|
|
|
10
11
|
/** A grid key chose a day: focus it. */
|
|
11
12
|
onMoveFocus: (value: string) => void;
|
|
12
13
|
onSelect: (value: string) => void;
|
|
14
|
+
/** Days to tint as a span, such as the period a page shows. */
|
|
15
|
+
range?: DateOnlyRange;
|
|
13
16
|
selected: string;
|
|
14
17
|
titleId: string;
|
|
15
18
|
today: string;
|
|
@@ -20,4 +23,4 @@ export type CalendarGridProps = {
|
|
|
20
23
|
* `[min, max]` stay focusable for orientation but are `aria-disabled`;
|
|
21
24
|
* padding past the four-digit years is blank and inert.
|
|
22
25
|
*/
|
|
23
|
-
export declare function CalendarGrid({ focusDate, locale, max, min, month, onFocusDate, onMoveFocus, onSelect, selected, titleId, today, weekStartsOn, }: CalendarGridProps): import("react").JSX.Element;
|
|
26
|
+
export declare function CalendarGrid({ focusDate, locale, max, min, month, onFocusDate, onMoveFocus, onSelect, range, selected, titleId, today, weekStartsOn, }: CalendarGridProps): import("react").JSX.Element;
|
|
@@ -3,13 +3,21 @@ import { cx } from '../../class-names.js';
|
|
|
3
3
|
import { calendarKeyTarget } from '../../calendar-keys.js';
|
|
4
4
|
import { formatDateLabel, weekdayLabels } from '../../calendar-labels.js';
|
|
5
5
|
import { monthGrid, } from '../../calendar-month.js';
|
|
6
|
-
import { isOutsideDateRange } from '../../date-only.js';
|
|
6
|
+
import { dayInRange, isOutsideDateRange, } from '../../date-only.js';
|
|
7
|
+
/** A day's range classes: in the span, and its start or end. */
|
|
8
|
+
function rangeClassNames(place) {
|
|
9
|
+
return [
|
|
10
|
+
place !== null && 'sw-date-field-day-in-range',
|
|
11
|
+
(place === 'start' || place === 'only') && 'sw-date-field-day-range-start',
|
|
12
|
+
(place === 'end' || place === 'only') && 'sw-date-field-day-range-end',
|
|
13
|
+
];
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* The month as a WAI-ARIA grid with one roving tab stop. Days outside
|
|
9
17
|
* `[min, max]` stay focusable for orientation but are `aria-disabled`;
|
|
10
18
|
* padding past the four-digit years is blank and inert.
|
|
11
19
|
*/
|
|
12
|
-
export function CalendarGrid({ focusDate, locale, max, min, month, onFocusDate, onMoveFocus, onSelect, selected, titleId, today, weekStartsOn, }) {
|
|
20
|
+
export function CalendarGrid({ focusDate, locale, max, min, month, onFocusDate, onMoveFocus, onSelect, range, selected, titleId, today, weekStartsOn, }) {
|
|
13
21
|
function choose(value) {
|
|
14
22
|
if (!isOutsideDateRange(value, min, max))
|
|
15
23
|
onSelect(value);
|
|
@@ -27,7 +35,7 @@ export function CalendarGrid({ focusDate, locale, max, min, month, onFocusDate,
|
|
|
27
35
|
event.preventDefault();
|
|
28
36
|
onMoveFocus(target);
|
|
29
37
|
}
|
|
30
|
-
return (_jsxs("table", { "aria-labelledby": titleId, className: "sw-date-field-grid", onKeyDown: onKeyDown, role: "grid", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels(locale, weekStartsOn).map((weekday) => (_jsx("th", { abbr: weekday.long, scope: "col", children: weekday.short }, weekday.long))) }) }), _jsx("tbody", { children: monthGrid(month, weekStartsOn).map((row, rowIndex) => (_jsx("tr", { children: row.map((cell, column) => cell === null ? (_jsx("td", { "aria-disabled": "true", className: "sw-date-field-day sw-date-field-day-outside", role: "gridcell" }, `blank-${column}`)) : (_jsx("td", { "aria-current": cell.value === today ? 'date' : undefined, "aria-disabled": isOutsideDateRange(cell.value, min, max) || undefined, "aria-label": formatDateLabel(cell.value, locale), "aria-selected": cell.value === selected, className: cx('sw-date-field-day', !cell.inMonth && 'sw-date-field-day-outside'), "data-date": cell.value, onClick: () => choose(cell.value), onFocus: () => {
|
|
38
|
+
return (_jsxs("table", { "aria-labelledby": titleId, className: "sw-date-field-grid", onKeyDown: onKeyDown, role: "grid", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels(locale, weekStartsOn).map((weekday) => (_jsx("th", { abbr: weekday.long, scope: "col", children: weekday.short }, weekday.long))) }) }), _jsx("tbody", { children: monthGrid(month, weekStartsOn).map((row, rowIndex) => (_jsx("tr", { children: row.map((cell, column) => cell === null ? (_jsx("td", { "aria-disabled": "true", className: "sw-date-field-day sw-date-field-day-outside", role: "gridcell" }, `blank-${column}`)) : (_jsx("td", { "aria-current": cell.value === today ? 'date' : undefined, "aria-disabled": isOutsideDateRange(cell.value, min, max) || undefined, "aria-label": formatDateLabel(cell.value, locale), "aria-selected": cell.value === selected, className: cx('sw-date-field-day', !cell.inMonth && 'sw-date-field-day-outside', ...rangeClassNames(dayInRange(cell.value, range))), "data-date": cell.value, onClick: () => choose(cell.value), onFocus: () => {
|
|
31
39
|
// A press on a neighboring month's day selects it; moving
|
|
32
40
|
// the grid first would pull the day out from under it.
|
|
33
41
|
if (cell.inMonth && cell.value !== focusDate)
|
|
@@ -3,6 +3,11 @@ export type CalendarPopupOptions = {
|
|
|
3
3
|
value: string;
|
|
4
4
|
disabled: boolean;
|
|
5
5
|
onChange: (value: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* The calendar's today (`YYYY-MM-DD`); the device's local date when left
|
|
8
|
+
* out. An empty or malformed date throws a RangeError.
|
|
9
|
+
*/
|
|
10
|
+
today?: string;
|
|
6
11
|
/** Runs before a chosen date closes the calendar, such as a text reset. */
|
|
7
12
|
beforeSelect?: () => void;
|
|
8
13
|
};
|
|
@@ -33,4 +38,4 @@ export type CalendarPopup = {
|
|
|
33
38
|
* DateField and CalendarButton. Disabling closes it for good; choosing a
|
|
34
39
|
* date closes it, returns focus to the button, and reports a changed date.
|
|
35
40
|
*/
|
|
36
|
-
export declare function useCalendarPopup({ value, disabled, onChange, beforeSelect, }: CalendarPopupOptions): CalendarPopup;
|
|
41
|
+
export declare function useCalendarPopup({ value, disabled, onChange, today, beforeSelect, }: CalendarPopupOptions): CalendarPopup;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useId, useRef, useState } from 'react';
|
|
2
|
-
import { todayDateOnly } from '../../date-only.js';
|
|
2
|
+
import { assertDateOnly, todayDateOnly } from '../../date-only.js';
|
|
3
3
|
/**
|
|
4
4
|
* The open state of a calendar dialog toggled by one button, shared by
|
|
5
5
|
* DateField and CalendarButton. Disabling closes it for good; choosing a
|
|
6
6
|
* date closes it, returns focus to the button, and reports a changed date.
|
|
7
7
|
*/
|
|
8
|
-
export function useCalendarPopup({ value, disabled, onChange, beforeSelect, }) {
|
|
8
|
+
export function useCalendarPopup({ value, disabled, onChange, today, beforeSelect, }) {
|
|
9
|
+
if (today !== undefined)
|
|
10
|
+
assertDateOnly('today', today);
|
|
9
11
|
const buttonRef = useRef(null);
|
|
10
12
|
const [open, setOpen] = useState(false);
|
|
11
13
|
// Disabling closes the calendar for good, not just while disabled.
|
|
@@ -37,7 +39,7 @@ export function useCalendarPopup({ value, disabled, onChange, beforeSelect, }) {
|
|
|
37
39
|
id,
|
|
38
40
|
onClose: close,
|
|
39
41
|
onSelect: select,
|
|
40
|
-
today: todayDateOnly(),
|
|
42
|
+
today: today ?? todayDateOnly(),
|
|
41
43
|
},
|
|
42
44
|
};
|
|
43
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type DenominationGridProps } from './types.js';
|
|
2
|
-
type StripProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>> & Pick<DenominationGridProps, 'totalLabel'>;
|
|
3
|
-
export declare function DenominationStrip({ label, columns, rows, zeroLabel, totalLabel, }: StripProps): import("react").JSX.Element;
|
|
2
|
+
type StripProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>> & Pick<DenominationGridProps, 'totalLabel' | 'labelWidth'>;
|
|
3
|
+
export declare function DenominationStrip({ label, columns, rows, zeroLabel, totalLabel, labelWidth, }: StripProps): import("react").JSX.Element;
|
|
4
4
|
export {};
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../../class-names.js';
|
|
3
|
+
import { denominationLabelWidthClass } from '../../css/css-denomination-aligned.js';
|
|
3
4
|
import { denominationCellToneClassNames } from '../../css/css-denomination-cell-tones.js';
|
|
4
5
|
import { denominationCellView } from '../../denomination-cells.js';
|
|
5
6
|
import { ScrollRegion } from '../ScrollRegion.js';
|
|
6
7
|
import { RowLabelLine } from './RowLabelLine.js';
|
|
7
8
|
/**
|
|
8
|
-
* The total column's header:
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
9
|
+
* The total column's header: the consumer's name for the column, shown over
|
|
10
|
+
* the totals from md up in the column heads' style, and visually hidden
|
|
11
|
+
* below md, where each total sits under its row's label and the column
|
|
12
|
+
* takes no width. Without a name it is the empty corner it has always been.
|
|
13
|
+
* Both carry `sw-denomination-total-head`.
|
|
11
14
|
*/
|
|
12
15
|
function TotalHead({ totalLabel }) {
|
|
13
16
|
if (totalLabel === undefined)
|
|
14
17
|
return _jsx("td", { className: "sw-denomination-corner sw-denomination-total-head" });
|
|
15
|
-
return (_jsx("th", { className: "sw-denomination-total-head", scope: "col", children: _jsx("span", { className: "sw-
|
|
18
|
+
return (_jsx("th", { className: "sw-denomination-head sw-denomination-total-head", scope: "col", children: _jsx("span", { className: "sw-denomination-total-label", children: totalLabel }) }));
|
|
16
19
|
}
|
|
17
|
-
export function DenominationStrip({ label, columns, rows, zeroLabel, totalLabel, }) {
|
|
20
|
+
export function DenominationStrip({ label, columns, rows, zeroLabel, totalLabel, labelWidth, }) {
|
|
18
21
|
const hasTotals = rows.some((row) => row.total !== undefined);
|
|
19
|
-
|
|
22
|
+
// An aligned strip sizes its columns from the region's width; it needs
|
|
23
|
+
// the column count and whether a total column takes a share.
|
|
24
|
+
const aligned = labelWidth === undefined
|
|
25
|
+
? undefined
|
|
26
|
+
: {
|
|
27
|
+
'--sw-denomination-columns': columns.length,
|
|
28
|
+
'--sw-denomination-total': hasTotals ? 1 : 0,
|
|
29
|
+
};
|
|
30
|
+
return (_jsx(ScrollRegion, { "aria-label": label, className: cx('sw-denomination-scroll', aligned && 'sw-denomination-scroll-aligned'), children: _jsxs("table", { className: cx('sw-denomination-grid sw-denomination-strip', labelWidth && 'sw-denomination-strip-aligned', labelWidth && denominationLabelWidthClass(labelWidth)), style: aligned, children: [_jsx("caption", { className: "sw-sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "sw-denomination-corner" }), columns.map((column) => (_jsx("th", { className: "sw-denomination-head", scope: "col", children: column.label }, column.key))), hasTotals ? _jsx(TotalHead, { totalLabel: totalLabel }) : null] }) }), _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { className: cx('sw-denomination-row', `sw-denomination-row-${row.tone ?? 'neutral'}`, row.signed && 'sw-denomination-row-signed'), children: [_jsx("th", { className: "sw-denomination-label", scope: "row", children: _jsxs("span", { className: "sw-denomination-label-body", children: [_jsx(RowLabelLine, { row: row }), row.total !== undefined ? (_jsx("span", { "aria-hidden": "true", className: "sw-denomination-total-inline", children: row.total })) : null] }) }), columns.map((column, index) => {
|
|
20
31
|
const view = denominationCellView(row.cells[index] ?? null, row.signed ?? false, zeroLabel);
|
|
21
32
|
return (_jsx("td", { className: cx('sw-denomination-cell', `sw-denomination-cell-${view.state}`, ...denominationCellToneClassNames(row.cellTones?.[index])), children: view.text }, column.key));
|
|
22
33
|
}), hasTotals ? (_jsx("td", { className: "sw-denomination-total", children: _jsx("span", { className: "sw-denomination-total-value", children: row.total ?? '' }) })) : null] }, row.id))) })] }) }));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type BadgeTone } from '../../css/css-data.js';
|
|
3
|
+
import { type DenominationLabelWidth } from '../../css/css-denomination-aligned.js';
|
|
4
|
+
export type { DenominationLabelWidth };
|
|
3
5
|
export type DenominationGridTone = BadgeTone;
|
|
4
6
|
export type DenominationGridLayout = 'strip' | 'tiles';
|
|
5
7
|
/**
|
|
@@ -35,8 +37,9 @@ export type DenominationGridProps = {
|
|
|
35
37
|
subtotal?: (count: number, column: DenominationGridColumn) => string;
|
|
36
38
|
zeroLabel?: string;
|
|
37
39
|
/**
|
|
38
|
-
* Names the strip's total column
|
|
39
|
-
*
|
|
40
|
+
* Names the strip's total column, such as "Total": a column header over
|
|
41
|
+
* the totals from md up, visually hidden below md, where each total sits
|
|
42
|
+
* under its row's label. Only the strip has that column.
|
|
40
43
|
*/
|
|
41
44
|
totalLabel?: string;
|
|
42
45
|
/**
|
|
@@ -46,4 +49,14 @@ export type DenominationGridProps = {
|
|
|
46
49
|
* landmark list. Only the tiles layout has row containers.
|
|
47
50
|
*/
|
|
48
51
|
rowRole?: DenominationGridRowRole;
|
|
52
|
+
/**
|
|
53
|
+
* Lines the strip's columns up with every other strip of the same columns
|
|
54
|
+
* in a container of the same width, such as the cards of a feed: the row
|
|
55
|
+
* labels take this width (`xs` to `xl`, 4 to 16 rem, the `Table` column
|
|
56
|
+
* sizes; pick the narrowest that holds the longest label's longest word)
|
|
57
|
+
* and the count columns share the rest equally, whatever the counts are.
|
|
58
|
+
* Without it each strip sizes its columns by its own content. Only the
|
|
59
|
+
* strip has a label column.
|
|
60
|
+
*/
|
|
61
|
+
labelWidth?: DenominationLabelWidth;
|
|
49
62
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type TdHTMLAttributes, type ThHTMLAttributes } from 'react';
|
|
2
|
+
import { type TableColumnWidth } from './table-layout.js';
|
|
3
|
+
export type { TableColumnWidth };
|
|
4
|
+
type TableCellAlign = 'start' | 'end';
|
|
5
|
+
/**
|
|
6
|
+
* A sortable column's state: sorted `ascending` or `descending`, or `none`
|
|
7
|
+
* (sortable, not the column the table is sorted by).
|
|
8
|
+
*/
|
|
9
|
+
export type TableSort = 'ascending' | 'descending' | 'none';
|
|
10
|
+
type TableHeaderSort = {
|
|
11
|
+
/**
|
|
12
|
+
* Makes a header cell (`as="th"`) a sort control: its children become a
|
|
13
|
+
* button in the header's own text style, with a sort glyph after them (an
|
|
14
|
+
* up chevron when `ascending`, a down chevron when `descending`, a muted
|
|
15
|
+
* pair when `none`), and the cell carries `aria-sort` while it is the
|
|
16
|
+
* sorted column. Pass it with `onSort`, on `th` cells only.
|
|
17
|
+
*/
|
|
18
|
+
sort?: TableSort;
|
|
19
|
+
/** Called when the header's sort button is pressed; the consumer sorts. */
|
|
20
|
+
onSort?: () => void;
|
|
21
|
+
};
|
|
22
|
+
export type TableCellProps = ((Omit<TdHTMLAttributes<HTMLTableCellElement>, 'width'> & {
|
|
23
|
+
as?: 'td';
|
|
24
|
+
sort?: never;
|
|
25
|
+
onSort?: never;
|
|
26
|
+
}) | (Omit<ThHTMLAttributes<HTMLTableCellElement>, 'width'> & {
|
|
27
|
+
as: 'th';
|
|
28
|
+
} & TableHeaderSort)) & {
|
|
29
|
+
align?: TableCellAlign;
|
|
30
|
+
numeric?: boolean;
|
|
31
|
+
truncate?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The column's width, on its header cell: `xs` to `xl` are widths in rem
|
|
34
|
+
* (4, 6, 8, 12 and 16), which a `fixed` table keeps whatever the rows
|
|
35
|
+
* hold. `min` gives an `auto` table's column the least room its content
|
|
36
|
+
* allows and keeps this cell's text on one line, so set it on every cell
|
|
37
|
+
* of that column; a `fixed` table, which never measures content, treats
|
|
38
|
+
* it as no width.
|
|
39
|
+
*/
|
|
40
|
+
width?: TableColumnWidth;
|
|
41
|
+
};
|
|
42
|
+
export declare const TableCell: import("react").ForwardRefExoticComponent<TableCellProps & import("react").RefAttributes<HTMLTableCellElement>>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, } from 'react';
|
|
3
|
+
import { cx } from '../../class-names.js';
|
|
4
|
+
import { assertTableColumnWidth, tableColumnWidthClass, } from './table-layout.js';
|
|
5
|
+
const tableSorts = ['ascending', 'descending', 'none'];
|
|
6
|
+
/** `sort` and `onSort` come together, on a header cell, with a known value. */
|
|
7
|
+
function assertSort(as, sort, onSort) {
|
|
8
|
+
if (sort === undefined && onSort === undefined)
|
|
9
|
+
return;
|
|
10
|
+
if (as !== 'th')
|
|
11
|
+
throw new TypeError('TableCell sort is for a header cell (as="th")');
|
|
12
|
+
if (sort === undefined || typeof onSort !== 'function')
|
|
13
|
+
throw new TypeError('TableCell sort and onSort are passed together');
|
|
14
|
+
if (!tableSorts.includes(sort))
|
|
15
|
+
throw new RangeError(`Unknown TableCell sort: ${String(sort)}`);
|
|
16
|
+
}
|
|
17
|
+
export const TableCell = forwardRef(function TableCell({ align = 'start', as = 'td', children, className, numeric = false, onSort, sort, truncate = false, width, ...rest }, ref) {
|
|
18
|
+
assertSort(as, sort, onSort);
|
|
19
|
+
assertTableColumnWidth(width);
|
|
20
|
+
const Component = as;
|
|
21
|
+
const alignmentClass = numeric
|
|
22
|
+
? 'sw-table-numeric'
|
|
23
|
+
: align === 'end'
|
|
24
|
+
? 'sw-table-end'
|
|
25
|
+
: undefined;
|
|
26
|
+
return (_jsx(Component, { ref: ref, "aria-sort": sort === undefined || sort === 'none' ? undefined : sort, className: cx(alignmentClass, truncate && 'sw-table-clip', width && tableColumnWidthClass(width), className), ...rest, children: sort !== undefined && onSort ? (_jsxs("button", { className: "sw-table-sort", onClick: onSort, type: "button", children: [children, _jsx("span", { "aria-hidden": "true", className: cx('sw-table-sort-glyph', `sw-table-sort-${sort}`) })] })) : (children) }));
|
|
27
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TableColumnWidth } from '../../css/css-table-layout.js';
|
|
2
|
+
export { tableColumnWidthClass } from '../../css/css-table-layout.js';
|
|
3
|
+
export type { TableColumnWidth };
|
|
4
|
+
/** How a table sizes its columns: by content, or from the header row only. */
|
|
5
|
+
export type TableLayout = 'auto' | 'fixed';
|
|
6
|
+
/** Where a cell's content sits in a row taller than it. */
|
|
7
|
+
export type TableVerticalAlign = 'middle' | 'top';
|
|
8
|
+
export declare function assertTableLayout(layout: TableLayout): void;
|
|
9
|
+
export declare function assertTableVerticalAlign(align: TableVerticalAlign): void;
|
|
10
|
+
export declare function assertTableColumnWidth(width: TableColumnWidth | undefined): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { tableColumnWidths, } from '../../css/css-table-layout.js';
|
|
2
|
+
export { tableColumnWidthClass } from '../../css/css-table-layout.js';
|
|
3
|
+
export function assertTableLayout(layout) {
|
|
4
|
+
if (layout !== 'auto' && layout !== 'fixed')
|
|
5
|
+
throw new RangeError(`Unknown Table layout: ${String(layout)}`);
|
|
6
|
+
}
|
|
7
|
+
export function assertTableVerticalAlign(align) {
|
|
8
|
+
if (align !== 'middle' && align !== 'top')
|
|
9
|
+
throw new RangeError(`Unknown Table verticalAlign: ${String(align)}`);
|
|
10
|
+
}
|
|
11
|
+
export function assertTableColumnWidth(width) {
|
|
12
|
+
if (width !== undefined && !tableColumnWidths.includes(width))
|
|
13
|
+
throw new RangeError(`Unknown TableCell width: ${String(width)}`);
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Whether a tap at `target` lands on a control inside the tooltip's anchor. */
|
|
2
|
+
export declare function tapActs(target: EventTarget | null, anchor: Element): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Whether the focus that just landed on `target` is visible: from the
|
|
5
|
+
* keyboard or a script, not a click or a tap, which hover and the tap's own
|
|
6
|
+
* rule already cover. A browser without `:focus-visible` counts it visible.
|
|
7
|
+
*/
|
|
8
|
+
export declare function focusIsVisible(target: EventTarget | null): boolean;
|