@tangible/ui 0.0.1
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/README.md +100 -0
- package/components/Accordion/Accordion.d.ts +22 -0
- package/components/Accordion/Accordion.js +192 -0
- package/components/Accordion/AccordionContext.d.ts +5 -0
- package/components/Accordion/AccordionContext.js +23 -0
- package/components/Accordion/index.d.ts +2 -0
- package/components/Accordion/index.js +1 -0
- package/components/Accordion/types.d.ts +61 -0
- package/components/Accordion/types.js +1 -0
- package/components/Avatar/Avatar.d.ts +11 -0
- package/components/Avatar/Avatar.js +67 -0
- package/components/Avatar/AvatarGroup.d.ts +11 -0
- package/components/Avatar/AvatarGroup.js +45 -0
- package/components/Avatar/index.d.ts +9 -0
- package/components/Avatar/index.js +7 -0
- package/components/Avatar/types.d.ts +44 -0
- package/components/Avatar/types.js +12 -0
- package/components/Button/Button.d.ts +4 -0
- package/components/Button/Button.js +33 -0
- package/components/Button/index.d.ts +2 -0
- package/components/Button/index.js +1 -0
- package/components/Button/types.d.ts +127 -0
- package/components/Button/types.js +1 -0
- package/components/Card/Card.d.ts +29 -0
- package/components/Card/Card.js +47 -0
- package/components/Card/index.d.ts +2 -0
- package/components/Card/index.js +1 -0
- package/components/Chip/Chip.d.ts +24 -0
- package/components/Chip/Chip.js +37 -0
- package/components/Chip/index.d.ts +2 -0
- package/components/Chip/index.js +1 -0
- package/components/Chips/Chips.d.ts +31 -0
- package/components/Chips/Chips.js +21 -0
- package/components/Chips/index.d.ts +2 -0
- package/components/Chips/index.js +1 -0
- package/components/ContentIndicator/ContentIndicator.d.ts +2 -0
- package/components/ContentIndicator/ContentIndicator.js +21 -0
- package/components/ContentIndicator/index.d.ts +2 -0
- package/components/ContentIndicator/index.js +1 -0
- package/components/ContentIndicator/types.d.ts +57 -0
- package/components/ContentIndicator/types.js +1 -0
- package/components/Dropdown/Dropdown.d.ts +31 -0
- package/components/Dropdown/Dropdown.js +219 -0
- package/components/Dropdown/DropdownContext.d.ts +3 -0
- package/components/Dropdown/DropdownContext.js +9 -0
- package/components/Dropdown/index.d.ts +2 -0
- package/components/Dropdown/index.js +1 -0
- package/components/Dropdown/types.d.ts +102 -0
- package/components/Dropdown/types.js +8 -0
- package/components/Icon/Icon.d.ts +22 -0
- package/components/Icon/Icon.js +24 -0
- package/components/Icon/index.d.ts +2 -0
- package/components/Icon/index.js +1 -0
- package/components/IconButton/IconButton.d.ts +2 -0
- package/components/IconButton/IconButton.js +50 -0
- package/components/IconButton/index.d.ts +2 -0
- package/components/IconButton/index.js +1 -0
- package/components/IconButton/types.d.ts +79 -0
- package/components/IconButton/types.js +1 -0
- package/components/Modal/Modal.d.ts +52 -0
- package/components/Modal/Modal.js +133 -0
- package/components/Modal/context.d.ts +6 -0
- package/components/Modal/context.js +9 -0
- package/components/Modal/index.d.ts +2 -0
- package/components/Modal/index.js +1 -0
- package/components/Notice/Notice.d.ts +93 -0
- package/components/Notice/Notice.js +144 -0
- package/components/Notice/index.d.ts +2 -0
- package/components/Notice/index.js +1 -0
- package/components/OverlapStack/OverlapStack.d.ts +44 -0
- package/components/OverlapStack/OverlapStack.js +41 -0
- package/components/OverlapStack/index.d.ts +2 -0
- package/components/OverlapStack/index.js +1 -0
- package/components/Pager/Pager.d.ts +26 -0
- package/components/Pager/Pager.js +151 -0
- package/components/Pager/index.d.ts +2 -0
- package/components/Pager/index.js +1 -0
- package/components/Progress/Progress.d.ts +2 -0
- package/components/Progress/Progress.js +100 -0
- package/components/Progress/index.d.ts +4 -0
- package/components/Progress/index.js +2 -0
- package/components/Progress/types.d.ts +251 -0
- package/components/Progress/types.js +1 -0
- package/components/Progress/useProgressSegments.d.ts +40 -0
- package/components/Progress/useProgressSegments.js +42 -0
- package/components/Rating/Rating.d.ts +32 -0
- package/components/Rating/Rating.js +74 -0
- package/components/Rating/index.d.ts +2 -0
- package/components/Rating/index.js +1 -0
- package/components/SegmentedControl/SegmentedControl.d.ts +10 -0
- package/components/SegmentedControl/SegmentedControl.js +183 -0
- package/components/SegmentedControl/SegmentedControlContext.d.ts +3 -0
- package/components/SegmentedControl/SegmentedControlContext.js +9 -0
- package/components/SegmentedControl/index.d.ts +2 -0
- package/components/SegmentedControl/index.js +1 -0
- package/components/SegmentedControl/types.d.ts +63 -0
- package/components/SegmentedControl/types.js +1 -0
- package/components/Sidebar/Sidebar.d.ts +17 -0
- package/components/Sidebar/Sidebar.js +107 -0
- package/components/Sidebar/index.d.ts +2 -0
- package/components/Sidebar/index.js +1 -0
- package/components/Sidebar/types.d.ts +65 -0
- package/components/Sidebar/types.js +4 -0
- package/components/StepIndicator/StepIndicator.d.ts +2 -0
- package/components/StepIndicator/StepIndicator.js +64 -0
- package/components/StepIndicator/index.d.ts +2 -0
- package/components/StepIndicator/index.js +1 -0
- package/components/StepIndicator/types.d.ts +68 -0
- package/components/StepIndicator/types.js +1 -0
- package/components/StepList/StepList.d.ts +12 -0
- package/components/StepList/StepList.js +59 -0
- package/components/StepList/StepListContext.d.ts +3 -0
- package/components/StepList/StepListContext.js +9 -0
- package/components/StepList/index.d.ts +2 -0
- package/components/StepList/index.js +1 -0
- package/components/StepList/types.d.ts +91 -0
- package/components/StepList/types.js +4 -0
- package/components/Table/BulkActionsBar.d.ts +12 -0
- package/components/Table/BulkActionsBar.js +9 -0
- package/components/Table/DataTable.d.ts +35 -0
- package/components/Table/DataTable.js +184 -0
- package/components/Table/Pagination.d.ts +13 -0
- package/components/Table/Pagination.js +13 -0
- package/components/Table/index.d.ts +2 -0
- package/components/Table/index.js +1 -0
- package/components/Tabs/Tabs.d.ts +23 -0
- package/components/Tabs/Tabs.js +309 -0
- package/components/Tabs/TabsContext.d.ts +3 -0
- package/components/Tabs/TabsContext.js +12 -0
- package/components/Tabs/index.d.ts +2 -0
- package/components/Tabs/index.js +1 -0
- package/components/Tabs/types.d.ts +75 -0
- package/components/Tabs/types.js +1 -0
- package/components/Toolbar/Toolbar.d.ts +18 -0
- package/components/Toolbar/Toolbar.js +241 -0
- package/components/Toolbar/index.d.ts +2 -0
- package/components/Toolbar/index.js +1 -0
- package/components/Toolbar/types.d.ts +28 -0
- package/components/Toolbar/types.js +1 -0
- package/components/Tooltip/Tooltip.d.ts +15 -0
- package/components/Tooltip/Tooltip.js +166 -0
- package/components/Tooltip/TooltipContext.d.ts +15 -0
- package/components/Tooltip/TooltipContext.js +25 -0
- package/components/Tooltip/index.d.ts +2 -0
- package/components/Tooltip/index.js +1 -0
- package/components/Tooltip/types.d.ts +85 -0
- package/components/Tooltip/types.js +8 -0
- package/components/index.d.ts +52 -0
- package/components/index.js +26 -0
- package/constants.d.ts +16 -0
- package/constants.js +16 -0
- package/icons/cred/index.d.ts +31 -0
- package/icons/cred/index.js +136 -0
- package/icons/icons.svg +155 -0
- package/icons/lms/index.d.ts +21 -0
- package/icons/lms/index.js +81 -0
- package/icons/manifest.json +1226 -0
- package/icons/player/index.d.ts +55 -0
- package/icons/player/index.js +268 -0
- package/icons/reaction/index.d.ts +79 -0
- package/icons/reaction/index.js +400 -0
- package/icons/registry.d.ts +316 -0
- package/icons/registry.js +163 -0
- package/icons/system/index.d.ts +155 -0
- package/icons/system/index.js +818 -0
- package/package.json +121 -0
- package/styles/all.css +1 -0
- package/styles/all.expanded.css +4137 -0
- package/styles/all.expanded.unlayered.css +4137 -0
- package/styles/all.unlayered.css +1 -0
- package/styles/components/_bundle.scss +51 -0
- package/styles/components/index.scss +1 -0
- package/styles/components/input/index.scss +248 -0
- package/styles/index.scss +71 -0
- package/styles/system/_constants.scss +12 -0
- package/styles/system/_motion.scss +47 -0
- package/styles/system/_palette-fns.scss +10 -0
- package/styles/system/_palettes.scss +80 -0
- package/styles/system/_tokens.scss +249 -0
- package/styles/system/index.scss +4 -0
- package/styles/utilities/_index.scss +373 -0
- package/tui-manifest.json +1858 -0
- package/types/index.d.ts +2 -0
- package/types/index.js +1 -0
- package/types/index.ts +2 -0
- package/types/sizes.d.ts +17 -0
- package/types/sizes.js +10 -0
- package/types/sizes.ts +21 -0
- package/types/svg.d.ts +5 -0
- package/types/themes.d.ts +14 -0
- package/types/themes.js +9 -0
- package/types/themes.ts +17 -0
- package/utils/color/contrast.d.ts +33 -0
- package/utils/color/contrast.js +88 -0
- package/utils/color-scheme.d.ts +25 -0
- package/utils/color-scheme.js +55 -0
- package/utils/compose-refs.d.ts +17 -0
- package/utils/compose-refs.js +38 -0
- package/utils/cx.d.ts +12 -0
- package/utils/cx.js +14 -0
- package/utils/focus-trap.d.ts +40 -0
- package/utils/focus-trap.js +93 -0
- package/utils/index.d.ts +10 -0
- package/utils/index.js +16 -0
- package/utils/math.d.ts +4 -0
- package/utils/math.js +19 -0
- package/utils/merge-props.d.ts +25 -0
- package/utils/merge-props.js +60 -0
- package/utils/polymorphic.d.ts +28 -0
- package/utils/polymorphic.js +44 -0
- package/utils/portal.d.ts +11 -0
- package/utils/portal.js +105 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cx } from '../../utils/cx.js';
|
|
4
|
+
import { Button } from '../Button/index.js';
|
|
5
|
+
import { IconButton } from '../IconButton/index.js';
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Pager Component
|
|
8
|
+
// =============================================================================
|
|
9
|
+
//
|
|
10
|
+
// Generic pagination control for any paginated content. Not coupled to any
|
|
11
|
+
// specific data source — just needs current page, total pages, and a callback.
|
|
12
|
+
//
|
|
13
|
+
// Use this for card grids, search results, galleries, or any paginated list.
|
|
14
|
+
// For TanStack Table integration, use TablePagination which wraps this.
|
|
15
|
+
//
|
|
16
|
+
// =============================================================================
|
|
17
|
+
const DEFAULT_MAX_SLOTS = 7;
|
|
18
|
+
/**
|
|
19
|
+
* Build a stable-width list of page tokens (numbers + ellipses).
|
|
20
|
+
*
|
|
21
|
+
* Always shows: first, last, current, current±1
|
|
22
|
+
* Single-page gaps are filled in (no "1 ... 3" silliness)
|
|
23
|
+
* Expands symmetrically from current, biased toward center
|
|
24
|
+
*/
|
|
25
|
+
function buildNumberTokens(total, current, maxSlots = DEFAULT_MAX_SLOTS) {
|
|
26
|
+
if (total <= 1)
|
|
27
|
+
return [1];
|
|
28
|
+
if (total <= maxSlots)
|
|
29
|
+
return Array.from({ length: total }, (_, i) => i + 1);
|
|
30
|
+
const inRange = (n) => n >= 1 && n <= total;
|
|
31
|
+
// Start with anchors: first, last, current and neighbors
|
|
32
|
+
const chosen = new Set([1, total]);
|
|
33
|
+
if (inRange(current))
|
|
34
|
+
chosen.add(current);
|
|
35
|
+
if (inRange(current - 1))
|
|
36
|
+
chosen.add(current - 1);
|
|
37
|
+
if (inRange(current + 1))
|
|
38
|
+
chosen.add(current + 1);
|
|
39
|
+
/**
|
|
40
|
+
* Convert chosen pages to tokens, inserting ellipses for gaps.
|
|
41
|
+
* Single-page gaps get filled in rather than showing an ellipsis.
|
|
42
|
+
*/
|
|
43
|
+
const materialize = () => {
|
|
44
|
+
const pages = [...chosen].sort((a, b) => a - b);
|
|
45
|
+
const tokens = [];
|
|
46
|
+
for (let i = 0; i < pages.length; i++) {
|
|
47
|
+
tokens.push(pages[i]);
|
|
48
|
+
const next = pages[i + 1];
|
|
49
|
+
if (next == null)
|
|
50
|
+
break;
|
|
51
|
+
const gap = next - pages[i];
|
|
52
|
+
if (gap === 2)
|
|
53
|
+
tokens.push(pages[i] + 1);
|
|
54
|
+
else if (gap > 2)
|
|
55
|
+
tokens.push('...');
|
|
56
|
+
}
|
|
57
|
+
return tokens;
|
|
58
|
+
};
|
|
59
|
+
/** Try adding a page. Returns true if it fit within maxSlots. */
|
|
60
|
+
const tryAdd = (p) => {
|
|
61
|
+
if (!inRange(p) || chosen.has(p))
|
|
62
|
+
return false;
|
|
63
|
+
chosen.add(p);
|
|
64
|
+
if (materialize().length > maxSlots) {
|
|
65
|
+
chosen.delete(p);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
};
|
|
70
|
+
// Expand symmetrically from current until we fill available slots
|
|
71
|
+
let offset = 2;
|
|
72
|
+
while (materialize().length < maxSlots) {
|
|
73
|
+
const left = current - offset;
|
|
74
|
+
const right = current + offset;
|
|
75
|
+
// Bias toward center: if past halfway, try right first
|
|
76
|
+
const preferRight = current > total / 2;
|
|
77
|
+
const [first, second] = preferRight ? [right, left] : [left, right];
|
|
78
|
+
// Try preferred side, then other side
|
|
79
|
+
const added = tryAdd(first) || tryAdd(second);
|
|
80
|
+
// If neither side worked, try both explicitly (handles edge cases)
|
|
81
|
+
if (!added && !tryAdd(left) && !tryAdd(right))
|
|
82
|
+
break;
|
|
83
|
+
offset++;
|
|
84
|
+
}
|
|
85
|
+
return materialize();
|
|
86
|
+
}
|
|
87
|
+
function buildItems(total, current, mode, maxNumbers) {
|
|
88
|
+
const items = [];
|
|
89
|
+
items.push({
|
|
90
|
+
kind: 'prev',
|
|
91
|
+
disabled: current <= 1,
|
|
92
|
+
page: Math.max(1, current - 1),
|
|
93
|
+
});
|
|
94
|
+
if (mode === 'full') {
|
|
95
|
+
for (let p = 1; p <= total; p++) {
|
|
96
|
+
items.push({ kind: 'page', page: p, current: p === current });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (mode === 'ends') {
|
|
100
|
+
const set = new Set([1, current - 1, current, current + 1, total].filter((n) => n >= 1 && n <= total));
|
|
101
|
+
const pages = Array.from(set).sort((a, b) => a - b);
|
|
102
|
+
pages.forEach((p) => items.push({ kind: 'page', page: p, current: p === current }));
|
|
103
|
+
}
|
|
104
|
+
else if (mode === 'smart') {
|
|
105
|
+
const tokens = buildNumberTokens(total, current, maxNumbers);
|
|
106
|
+
tokens.forEach((tok) => {
|
|
107
|
+
if (tok === '...')
|
|
108
|
+
items.push({ kind: 'ellipsis' });
|
|
109
|
+
else
|
|
110
|
+
items.push({ kind: 'page', page: tok, current: tok === current });
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// mode === 'simple' => no page numbers
|
|
114
|
+
items.push({
|
|
115
|
+
kind: 'next',
|
|
116
|
+
disabled: current >= total,
|
|
117
|
+
page: Math.min(total, current + 1),
|
|
118
|
+
});
|
|
119
|
+
return items;
|
|
120
|
+
}
|
|
121
|
+
// -----------------------------------------------------------------------------
|
|
122
|
+
// Component
|
|
123
|
+
// -----------------------------------------------------------------------------
|
|
124
|
+
export function Pager({ currentPage, totalPages, onPageChange, pageSize, pageSizeOptions, onPageSizeChange, mode = 'smart', maxNumbers = DEFAULT_MAX_SLOTS, navStyle = 'text', hidden = false, className, }) {
|
|
125
|
+
// Normalise inputs
|
|
126
|
+
const total = Math.max(1, totalPages);
|
|
127
|
+
const current = Math.min(total, Math.max(1, currentPage));
|
|
128
|
+
const items = React.useMemo(() => buildItems(total, current, mode, maxNumbers), [total, current, mode, maxNumbers]);
|
|
129
|
+
const go = React.useCallback((page) => onPageChange(page), [onPageChange]);
|
|
130
|
+
if (hidden)
|
|
131
|
+
return null;
|
|
132
|
+
const showPageSizeSelector = pageSizeOptions && pageSizeOptions.length > 0 && onPageSizeChange;
|
|
133
|
+
return (_jsxs("div", { className: cx('tui-pager', className), children: [_jsx("nav", { className: "tui-pager__nav", "aria-label": "Pagination", children: items.map((it, i) => {
|
|
134
|
+
if (it.kind === 'ellipsis') {
|
|
135
|
+
return (_jsx("span", { className: "tui-pager__ellipsis", "aria-hidden": true, children: "..." }, `e${i}`));
|
|
136
|
+
}
|
|
137
|
+
if (it.kind === 'prev' || it.kind === 'next') {
|
|
138
|
+
const isPrev = it.kind === 'prev';
|
|
139
|
+
const label = isPrev ? 'Previous page' : 'Next page';
|
|
140
|
+
if (navStyle === 'icon') {
|
|
141
|
+
return (_jsx(IconButton, { icon: isPrev ? 'system/chevron-left' : 'system/chevron-right', label: label, showTooltip: true, size: "sm", theme: "secondary", variant: "outline", disabled: it.disabled, onClick: () => go(it.page), className: "tui-pager__item" }, it.kind));
|
|
142
|
+
}
|
|
143
|
+
return (_jsx(Button, { size: "sm", theme: "secondary", variant: "outline", disabled: it.disabled, onClick: () => go(it.page), "aria-label": label, className: "tui-pager__item", children: isPrev ? 'Prev' : 'Next' }, it.kind));
|
|
144
|
+
}
|
|
145
|
+
// Page number
|
|
146
|
+
if (it.kind === 'page') {
|
|
147
|
+
return (_jsx(Button, { size: "sm", theme: it.current ? 'primary' : 'secondary', variant: it.current ? 'solid' : 'outline', "aria-current": it.current ? 'page' : undefined, "aria-label": `Page ${it.page}`, onClick: () => go(it.page), className: "tui-pager__item", children: it.page }, it.page));
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}) }), _jsxs("div", { className: "tui-pager__info", children: [_jsxs("span", { children: ["Page ", _jsx("strong", { children: current }), " of ", total] }), showPageSizeSelector && (_jsxs("label", { className: "tui-pager__page-size-label", children: [_jsx("span", { className: "tui-visually-hidden", children: "Items per page" }), _jsx("select", { className: "tui-input", value: pageSize, onChange: (e) => onPageSizeChange(Number(e.target.value)), children: pageSizeOptions.map((s) => (_jsxs("option", { value: s, children: [s, " / page"] }, s))) })] }))] })] }));
|
|
151
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Pager } from './Pager.js';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useProgressSegments } from './useProgressSegments.js';
|
|
4
|
+
import { cx } from '../../utils/cx.js';
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// COMPONENT
|
|
7
|
+
// =============================================================================
|
|
8
|
+
export function Progress(props) {
|
|
9
|
+
const { children, mode = 'line', size = 'md', max = 100, showLabels = true, labelledBy, ariaLabel, defaultLabel, className, } = props;
|
|
10
|
+
// Determine mode
|
|
11
|
+
const isSegmented = 'segments' in props && Array.isArray(props.segments);
|
|
12
|
+
// Standard mode props
|
|
13
|
+
const value = !isSegmented ? (props.value ?? 0) : 0;
|
|
14
|
+
const indeterminate = !isSegmented ? (props.indeterminate ?? false) : false;
|
|
15
|
+
const labelPosition = !isSegmented ? (props.labelPosition ?? 'inline') : 'inline';
|
|
16
|
+
const labelStart = !isSegmented ? props.labelStart : undefined;
|
|
17
|
+
const labelEnd = !isSegmented ? props.labelEnd : undefined;
|
|
18
|
+
const variant = !isSegmented && 'variant' in props ? (props.variant ?? 'ring') : 'ring';
|
|
19
|
+
// Segments (for segmented mode) — fallback ensures type safety
|
|
20
|
+
const segments = ('segments' in props && props.segments) || [];
|
|
21
|
+
// Process segments via hook
|
|
22
|
+
const { processedSegments, ariaValueText, primaryValue, primaryPct, } = useProgressSegments({ segments, max });
|
|
23
|
+
// Calculate percentages for standard mode
|
|
24
|
+
const pct = Math.max(0, Math.min(100, (value / max) * 100));
|
|
25
|
+
// Dev warning: inside position only supports labelStart (or children)
|
|
26
|
+
if (import.meta.env.DEV && labelPosition === 'inside' && labelStart && labelEnd) {
|
|
27
|
+
console.warn('Progress: labelPosition="inside" only supports a single label. ' +
|
|
28
|
+
'labelEnd will be ignored. Use labelStart or children for inside content.');
|
|
29
|
+
}
|
|
30
|
+
// Check if we have any label content to show
|
|
31
|
+
const hasLabels = !!(labelStart || labelEnd || children);
|
|
32
|
+
// Determine accessible name props for progressbar
|
|
33
|
+
const ariaProps = {};
|
|
34
|
+
if (labelledBy) {
|
|
35
|
+
ariaProps['aria-labelledby'] = labelledBy;
|
|
36
|
+
}
|
|
37
|
+
else if (ariaLabel) {
|
|
38
|
+
ariaProps['aria-label'] = ariaLabel;
|
|
39
|
+
}
|
|
40
|
+
else if (defaultLabel) {
|
|
41
|
+
ariaProps['aria-label'] = defaultLabel;
|
|
42
|
+
}
|
|
43
|
+
// Add valuetext for segmented mode
|
|
44
|
+
if (isSegmented && ariaValueText) {
|
|
45
|
+
ariaProps['aria-valuetext'] = ariaValueText;
|
|
46
|
+
}
|
|
47
|
+
// aria-valuenow, aria-valuemin, aria-valuemax
|
|
48
|
+
const ariaValueProps = indeterminate
|
|
49
|
+
? {
|
|
50
|
+
'aria-valuenow': undefined,
|
|
51
|
+
'aria-valuemin': undefined,
|
|
52
|
+
'aria-valuemax': undefined,
|
|
53
|
+
'aria-busy': true,
|
|
54
|
+
}
|
|
55
|
+
: {
|
|
56
|
+
'aria-valuenow': Math.round(isSegmented ? primaryValue : value),
|
|
57
|
+
'aria-valuemin': 0,
|
|
58
|
+
'aria-valuemax': max,
|
|
59
|
+
'aria-busy': undefined,
|
|
60
|
+
};
|
|
61
|
+
// Build CSS custom properties
|
|
62
|
+
const styleVars = {
|
|
63
|
+
'--tui-progress-value': String(isSegmented ? primaryValue : value),
|
|
64
|
+
'--tui-progress-max': String(max),
|
|
65
|
+
'--tui-progress-pct': String(isSegmented ? primaryPct : pct),
|
|
66
|
+
};
|
|
67
|
+
// Add segment-specific vars
|
|
68
|
+
processedSegments.forEach((seg, i) => {
|
|
69
|
+
styleVars[`--tui-progress-z${i + 1}-pct`] = String(seg.pct);
|
|
70
|
+
styleVars[`--tui-progress-z${i + 1}-width`] = String(seg.width);
|
|
71
|
+
});
|
|
72
|
+
const rootProps = {
|
|
73
|
+
role: 'progressbar',
|
|
74
|
+
...ariaProps,
|
|
75
|
+
...ariaValueProps,
|
|
76
|
+
className: cx('tui-progress', `is-mode-${mode}`, `is-size-${size}`, !isSegmented && showLabels && hasLabels && `is-label-${labelPosition}`, indeterminate && 'is-indeterminate', isSegmented && 'is-segmented', mode === 'circle' && `is-variant-${variant}`, className),
|
|
77
|
+
style: styleVars,
|
|
78
|
+
};
|
|
79
|
+
// Inside label content (for line integrated or circle center)
|
|
80
|
+
const insideContent = children ?? labelStart;
|
|
81
|
+
// Content for line variant
|
|
82
|
+
const lineContent = isSegmented ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "tui-progress__track", children: processedSegments.map((seg, i) => (_jsx("div", { className: cx('tui-progress__bar', `is-z${seg.zIndex}`), style: { '--tui-segment-width': `${seg.width}%` } }, i))) }), showLabels && (children ? (_jsx("div", { className: "tui-progress__segment-labels", children: children })) : (_jsx("div", { className: "tui-progress__segment-labels", children: processedSegments.map((seg, i) => (seg.visible && (_jsxs("span", { className: cx('tui-progress__segment-label', `is-z${seg.zIndex}`), children: [seg.label && _jsx("span", { className: "tui-progress__segment-label-text", children: seg.label }), _jsxs("span", { className: "tui-progress__segment-label-value", children: [seg.roundedPct, "%"] })] }, i)))) })))] })) : (_jsxs("div", { className: "tui-progress__track", children: [_jsx("div", { className: "tui-progress__bar" }), !indeterminate && showLabels && labelPosition === 'inside' && insideContent && (_jsx("div", { className: "tui-progress__label-wrapper", children: _jsx("div", { className: "tui-progress__label is-inside", children: insideContent }) }))] }));
|
|
83
|
+
// Content for circle variant (segmented not supported — enforced by TypeScript)
|
|
84
|
+
const circleContent = (_jsxs("div", { className: "tui-progress__circle", children: [_jsx("div", { className: "tui-progress__circle__bar" }), !indeterminate && showLabels && labelPosition === 'inside' && insideContent && (_jsx("div", { className: "tui-progress__label is-inside", children: insideContent }))] }));
|
|
85
|
+
const trackContent = mode === 'line' ? lineContent : circleContent;
|
|
86
|
+
// ==========================================================================
|
|
87
|
+
// Render based on label position
|
|
88
|
+
// ==========================================================================
|
|
89
|
+
// No labels to show
|
|
90
|
+
if (!showLabels || !hasLabels || isSegmented) {
|
|
91
|
+
return (_jsxs("div", { ...rootProps, children: [trackContent, !labelledBy && !ariaLabel && defaultLabel && (_jsx("span", { className: "visually-hidden", children: defaultLabel }))] }));
|
|
92
|
+
}
|
|
93
|
+
// Inside position: labels are rendered within trackContent above
|
|
94
|
+
if (labelPosition === 'inside') {
|
|
95
|
+
return (_jsxs("div", { ...rootProps, children: [trackContent, !labelledBy && !ariaLabel && defaultLabel && (_jsx("span", { className: "visually-hidden", children: defaultLabel }))] }));
|
|
96
|
+
}
|
|
97
|
+
// Above/below/inline positions: render labels outside the track
|
|
98
|
+
const labelRow = (_jsxs("div", { className: "tui-progress__labels", children: [labelStart && _jsx("span", { className: "tui-progress__label is-start", children: labelStart }), labelEnd && _jsx("span", { className: "tui-progress__label is-end", children: labelEnd })] }));
|
|
99
|
+
return (_jsxs("div", { ...rootProps, children: [labelPosition === 'above' && labelRow, labelPosition === 'inline' ? (_jsxs("div", { className: "tui-progress__inline", children: [labelStart && _jsx("span", { className: "tui-progress__label is-start", children: labelStart }), trackContent, labelEnd && _jsx("span", { className: "tui-progress__label is-end", children: labelEnd })] })) : (trackContent), labelPosition === 'below' && labelRow, !labelledBy && !ariaLabel && defaultLabel && (_jsx("span", { className: "visually-hidden", children: defaultLabel }))] }));
|
|
100
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Progress } from './Progress';
|
|
2
|
+
export { useProgressSegments } from './useProgressSegments';
|
|
3
|
+
export type { ProgressProps, ProgressSegment, SegmentedProgressProps, StandardProgressProps, Mode, Size, LabelPos, } from './types';
|
|
4
|
+
export type { ProcessedSegment } from './useProgressSegments';
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { Size } from '../../types';
|
|
3
|
+
export type { Size };
|
|
4
|
+
export type Mode = 'line' | 'circle';
|
|
5
|
+
export type LabelPos = 'above' | 'inline' | 'below' | 'inside';
|
|
6
|
+
/**
|
|
7
|
+
* A single segment in segmented progress mode.
|
|
8
|
+
*
|
|
9
|
+
* Segments are rendered front-to-back (z1, z2, z3...) with each segment
|
|
10
|
+
* showing the portion from the previous segment's value to its own value.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Student is at 24%, should be at 38%
|
|
14
|
+
* { value: 24, label: 'Progress' } // z1: 0-24%
|
|
15
|
+
* { value: 38, label: 'Expected' } // z2: 24-38%
|
|
16
|
+
*/
|
|
17
|
+
export type ProgressSegment = {
|
|
18
|
+
/**
|
|
19
|
+
* Cumulative value for this segment (0–100, or use with `max` prop).
|
|
20
|
+
* Each segment's visual width = its value minus the previous segment's value.
|
|
21
|
+
*/
|
|
22
|
+
value: number;
|
|
23
|
+
/**
|
|
24
|
+
* Label text shown before the percentage in the label below the bar.
|
|
25
|
+
* If omitted, only the percentage is shown.
|
|
26
|
+
*/
|
|
27
|
+
label?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Base props shared by all Progress variants.
|
|
31
|
+
*/
|
|
32
|
+
export type BaseProgressProps = {
|
|
33
|
+
/**
|
|
34
|
+
* Custom content to render inside the progress indicator.
|
|
35
|
+
* - Line mode with `inside`: Replaces default content inside the bar
|
|
36
|
+
* - Circle mode with `inside`: Replaces centered content
|
|
37
|
+
* - Segmented mode: Replaces the segment labels area below the bar
|
|
38
|
+
*/
|
|
39
|
+
children?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* Size of the progress bar, affecting height and font size.
|
|
42
|
+
* - `'xs'`: 12px font, thinnest bar (for dense step indicators)
|
|
43
|
+
* - `'sm'`: 14px font, thinner bar
|
|
44
|
+
* - `'md'`: 16px font, standard bar (default)
|
|
45
|
+
* - `'lg'`: 20px font, thicker bar
|
|
46
|
+
* @default 'md'
|
|
47
|
+
*/
|
|
48
|
+
size?: Size;
|
|
49
|
+
/**
|
|
50
|
+
* Maximum value for the progress calculation.
|
|
51
|
+
* Progress percentage = (value / max) × 100
|
|
52
|
+
* @default 100
|
|
53
|
+
*/
|
|
54
|
+
max?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Whether to display labels.
|
|
57
|
+
* When false, hides all label content regardless of other props.
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
showLabels?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* ARIA: ID of an element that labels this progress bar.
|
|
63
|
+
* Takes precedence over `ariaLabel` and `defaultLabel`.
|
|
64
|
+
*/
|
|
65
|
+
labelledBy?: string;
|
|
66
|
+
/**
|
|
67
|
+
* ARIA: Accessible name for the progress bar.
|
|
68
|
+
* Use when no visible label exists. Takes precedence over `defaultLabel`.
|
|
69
|
+
*/
|
|
70
|
+
ariaLabel?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Fallback accessible name, rendered as visually-hidden text.
|
|
73
|
+
* Only used if neither `labelledBy` nor `ariaLabel` is provided.
|
|
74
|
+
*/
|
|
75
|
+
defaultLabel?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Additional CSS class names to apply to the root element.
|
|
78
|
+
*/
|
|
79
|
+
className?: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Props for segmented mode — displays multiple stacked segments (z1, z2, z3...).
|
|
83
|
+
*
|
|
84
|
+
* Each segment shows the portion from the previous segment's value to its value.
|
|
85
|
+
* Useful for showing actual vs expected progress, or multi-phase completion.
|
|
86
|
+
*
|
|
87
|
+
* **Restricted in segmented mode:**
|
|
88
|
+
* - `variant` — only `'line'` is supported (enforced by TypeScript)
|
|
89
|
+
* - `labelPosition`, `labelStart`, `labelEnd` — use segment-level labels instead
|
|
90
|
+
* - `indeterminate` — not supported
|
|
91
|
+
* - `value` — derived from segments array
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* <Progress
|
|
96
|
+
* segments={[
|
|
97
|
+
* { value: 24, label: 'Progress' },
|
|
98
|
+
* { value: 38, label: 'Expected' },
|
|
99
|
+
* ]}
|
|
100
|
+
* ariaLabel="Student progress"
|
|
101
|
+
* />
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export type SegmentedProgressProps = BaseProgressProps & {
|
|
105
|
+
/**
|
|
106
|
+
* Display mode. Segmented progress only supports line mode.
|
|
107
|
+
* @default 'line'
|
|
108
|
+
*/
|
|
109
|
+
mode?: 'line';
|
|
110
|
+
/**
|
|
111
|
+
* Segments to display, ordered front-to-back (z1, z2, z3...).
|
|
112
|
+
* Maximum 5 segments have default colors; beyond that, override via CSS.
|
|
113
|
+
*/
|
|
114
|
+
segments: ProgressSegment[];
|
|
115
|
+
/** Not available in segmented mode. */
|
|
116
|
+
value?: never;
|
|
117
|
+
/** Not supported in segmented mode. */
|
|
118
|
+
indeterminate?: never;
|
|
119
|
+
/** Not available in segmented mode. Labels appear below the bar. */
|
|
120
|
+
labelPosition?: never;
|
|
121
|
+
/** Not available in segmented mode. */
|
|
122
|
+
labelStart?: never;
|
|
123
|
+
/** Not available in segmented mode. */
|
|
124
|
+
labelEnd?: never;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Props for standard single-value progress mode.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```tsx
|
|
131
|
+
* // Basic bar with no label
|
|
132
|
+
* <Progress value={42} ariaLabel="Upload progress" />
|
|
133
|
+
*
|
|
134
|
+
* // With labels above the bar (split left/right)
|
|
135
|
+
* <Progress
|
|
136
|
+
* value={33}
|
|
137
|
+
* labelPosition="above"
|
|
138
|
+
* labelStart="33% Complete"
|
|
139
|
+
* labelEnd="3/9 Steps"
|
|
140
|
+
* />
|
|
141
|
+
*
|
|
142
|
+
* // Indeterminate (loading)
|
|
143
|
+
* <Progress indeterminate ariaLabel="Loading" />
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export type StandardProgressProps = BaseProgressProps & {
|
|
147
|
+
/**
|
|
148
|
+
* Display mode of the progress indicator.
|
|
149
|
+
* - `'line'`: Horizontal bar (default)
|
|
150
|
+
* - `'circle'`: Circular/ring indicator
|
|
151
|
+
* @default 'line'
|
|
152
|
+
*/
|
|
153
|
+
mode?: Mode;
|
|
154
|
+
/**
|
|
155
|
+
* Visual variant for circle mode.
|
|
156
|
+
* - `'ring'`: Hollow centre (default) — shows progress as a ring/donut
|
|
157
|
+
* - `'solid'`: Filled centre — shows as a solid filled circle
|
|
158
|
+
*
|
|
159
|
+
* Only applies when `mode="circle"`. Ignored in line mode.
|
|
160
|
+
* @default 'ring'
|
|
161
|
+
*/
|
|
162
|
+
variant?: 'ring' | 'solid';
|
|
163
|
+
/**
|
|
164
|
+
* Current progress value (0–100, or use with `max` prop).
|
|
165
|
+
* @default 0
|
|
166
|
+
*/
|
|
167
|
+
value?: number;
|
|
168
|
+
/**
|
|
169
|
+
* When true, shows an animated indeterminate state.
|
|
170
|
+
* Use for operations where progress percentage is unknown.
|
|
171
|
+
* Ignores `value` when enabled.
|
|
172
|
+
* @default false
|
|
173
|
+
*/
|
|
174
|
+
indeterminate?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Where to display the labels relative to the progress bar.
|
|
177
|
+
* - `'above'`: Labels in a row above the bar
|
|
178
|
+
* - `'inline'`: Labels beside the bar horizontally (default)
|
|
179
|
+
* - `'below'`: Labels in a row below the bar
|
|
180
|
+
* - `'inside'`: Label content inside/overlapping the bar (or centered in circle)
|
|
181
|
+
*
|
|
182
|
+
* When both `labelStart` and `labelEnd` are provided with `above`, `inline`,
|
|
183
|
+
* or `below`, they appear on opposite sides (space-between).
|
|
184
|
+
*
|
|
185
|
+
* @default 'inline'
|
|
186
|
+
*/
|
|
187
|
+
labelPosition?: LabelPos;
|
|
188
|
+
/**
|
|
189
|
+
* Label content for the start (left) position.
|
|
190
|
+
* Use with `labelPosition` to control placement.
|
|
191
|
+
* @example labelStart="33% Complete"
|
|
192
|
+
*/
|
|
193
|
+
labelStart?: React.ReactNode;
|
|
194
|
+
/**
|
|
195
|
+
* Label content for the end (right) position.
|
|
196
|
+
* Use with `labelPosition` to control placement.
|
|
197
|
+
* @example labelEnd="3/9 Steps"
|
|
198
|
+
*/
|
|
199
|
+
labelEnd?: React.ReactNode;
|
|
200
|
+
/** Not available in standard mode. Use segmented mode instead. */
|
|
201
|
+
segments?: never;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Progress bar component with support for single-value and segmented modes.
|
|
205
|
+
*
|
|
206
|
+
* ## Modes
|
|
207
|
+
*
|
|
208
|
+
* **Standard mode** — Single progress value with optional labels:
|
|
209
|
+
* ```tsx
|
|
210
|
+
* <Progress
|
|
211
|
+
* value={42}
|
|
212
|
+
* labelPosition="above"
|
|
213
|
+
* labelStart="42%"
|
|
214
|
+
* labelEnd="Progress"
|
|
215
|
+
* />
|
|
216
|
+
* ```
|
|
217
|
+
*
|
|
218
|
+
* **Segmented mode** — Multiple stacked segments for comparing values:
|
|
219
|
+
* ```tsx
|
|
220
|
+
* <Progress
|
|
221
|
+
* segments={[
|
|
222
|
+
* { value: 24, label: 'Actual' },
|
|
223
|
+
* { value: 38, label: 'Expected' },
|
|
224
|
+
* ]}
|
|
225
|
+
* />
|
|
226
|
+
* ```
|
|
227
|
+
*
|
|
228
|
+
* ## Styling
|
|
229
|
+
*
|
|
230
|
+
* Customise via CSS custom properties on the component or a parent:
|
|
231
|
+
*
|
|
232
|
+
* | Property | Description | Default |
|
|
233
|
+
* |----------|-------------|---------|
|
|
234
|
+
* | `--tui-progress-fill` | Bar fill color (standard mode) | Primary theme color |
|
|
235
|
+
* | `--tui-progress-track` | Track background color | Border color |
|
|
236
|
+
* | `--tui-progress-radius` | Border radius | Medium radius |
|
|
237
|
+
* | `--tui-progress-z1-fill` | Segment 1 color | Primary |
|
|
238
|
+
* | `--tui-progress-z2-fill` | Segment 2 color | Warning (amber) |
|
|
239
|
+
* | `--tui-progress-z3-fill` | Segment 3 color | Success (green) |
|
|
240
|
+
* | `--tui-progress-z4-fill` | Segment 4 color | Danger (red) |
|
|
241
|
+
* | `--tui-progress-z5-fill` | Segment 5 color | Info (blue) |
|
|
242
|
+
*
|
|
243
|
+
* ## Accessibility
|
|
244
|
+
*
|
|
245
|
+
* - Uses `role="progressbar"` with proper ARIA attributes
|
|
246
|
+
* - `aria-valuenow`, `aria-valuemin`, `aria-valuemax` for screen readers
|
|
247
|
+
* - `aria-valuetext` in segmented mode announces all segment values
|
|
248
|
+
* - `aria-busy` set during indeterminate state
|
|
249
|
+
* - Respects `prefers-reduced-motion` for animations
|
|
250
|
+
*/
|
|
251
|
+
export type ProgressProps = SegmentedProgressProps | StandardProgressProps;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProgressSegment } from './types';
|
|
2
|
+
export type { ProgressSegment };
|
|
3
|
+
export type ProcessedSegment = ProgressSegment & {
|
|
4
|
+
/** Percentage value (0-100) */
|
|
5
|
+
pct: number;
|
|
6
|
+
/** Rounded percentage for display */
|
|
7
|
+
roundedPct: number;
|
|
8
|
+
/** Visual width as percentage (this segment's portion) */
|
|
9
|
+
width: number;
|
|
10
|
+
/** Whether segment has visible width (width > 0) */
|
|
11
|
+
visible: boolean;
|
|
12
|
+
/** Z-index identifier (1, 2, 3...) */
|
|
13
|
+
zIndex: number;
|
|
14
|
+
};
|
|
15
|
+
type UseProgressSegmentsOptions = {
|
|
16
|
+
segments: ProgressSegment[];
|
|
17
|
+
max: number;
|
|
18
|
+
};
|
|
19
|
+
type UseProgressSegmentsResult = {
|
|
20
|
+
/** Processed segments with calculated widths and visibility */
|
|
21
|
+
processedSegments: ProcessedSegment[];
|
|
22
|
+
/** Accessible description of all segments for screen readers */
|
|
23
|
+
ariaValueText: string;
|
|
24
|
+
/** Primary value (first segment) for aria-valuenow */
|
|
25
|
+
primaryValue: number;
|
|
26
|
+
/** Primary percentage (first segment) */
|
|
27
|
+
primaryPct: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Hook to process progress segments into renderable data.
|
|
31
|
+
*
|
|
32
|
+
* Calculates each segment's visual width based on cumulative values:
|
|
33
|
+
* - Segment 1 (z1): width = segment[0].value
|
|
34
|
+
* - Segment 2 (z2): width = segment[1].value - segment[0].value
|
|
35
|
+
* - etc.
|
|
36
|
+
*
|
|
37
|
+
* Segments with zero or negative width (when a later segment has lower value)
|
|
38
|
+
* are marked as not visible.
|
|
39
|
+
*/
|
|
40
|
+
export declare function useProgressSegments({ segments, max, }: UseProgressSegmentsOptions): UseProgressSegmentsResult;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to process progress segments into renderable data.
|
|
4
|
+
*
|
|
5
|
+
* Calculates each segment's visual width based on cumulative values:
|
|
6
|
+
* - Segment 1 (z1): width = segment[0].value
|
|
7
|
+
* - Segment 2 (z2): width = segment[1].value - segment[0].value
|
|
8
|
+
* - etc.
|
|
9
|
+
*
|
|
10
|
+
* Segments with zero or negative width (when a later segment has lower value)
|
|
11
|
+
* are marked as not visible.
|
|
12
|
+
*/
|
|
13
|
+
export function useProgressSegments({ segments, max, }) {
|
|
14
|
+
return useMemo(() => {
|
|
15
|
+
const processedSegments = segments.map((seg, i) => {
|
|
16
|
+
const segPct = Math.max(0, Math.min(100, (seg.value / max) * 100));
|
|
17
|
+
const prevPct = i === 0
|
|
18
|
+
? 0
|
|
19
|
+
: Math.max(0, Math.min(100, (segments[i - 1].value / max) * 100));
|
|
20
|
+
const width = Math.max(0, segPct - prevPct);
|
|
21
|
+
return {
|
|
22
|
+
...seg,
|
|
23
|
+
pct: segPct,
|
|
24
|
+
roundedPct: Math.round(segPct),
|
|
25
|
+
width,
|
|
26
|
+
visible: width > 0,
|
|
27
|
+
zIndex: i + 1,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
const ariaValueText = processedSegments
|
|
31
|
+
.map(seg => `${seg.label ?? `Segment ${seg.zIndex}`} ${seg.roundedPct}%`)
|
|
32
|
+
.join(', ');
|
|
33
|
+
const primaryValue = segments[0]?.value ?? 0;
|
|
34
|
+
const primaryPct = processedSegments[0]?.pct ?? 0;
|
|
35
|
+
return {
|
|
36
|
+
processedSegments,
|
|
37
|
+
ariaValueText,
|
|
38
|
+
primaryValue,
|
|
39
|
+
primaryPct,
|
|
40
|
+
};
|
|
41
|
+
}, [segments, max]);
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SizeStandard, Theme as ThemeFull } from '../../types';
|
|
2
|
+
type Size = SizeStandard;
|
|
3
|
+
type Theme = ThemeFull;
|
|
4
|
+
export type RatingProps = {
|
|
5
|
+
/** Controlled value (1..max). Use with onChange */
|
|
6
|
+
value?: number;
|
|
7
|
+
/** Uncontrolled initial value */
|
|
8
|
+
defaultValue?: number;
|
|
9
|
+
/** Maximum icons shown */
|
|
10
|
+
max?: number;
|
|
11
|
+
/** Disable interaction (keeps semantics) */
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
/** Presentational readOnly (no form semantics) */
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
/** Name for the radio group (if you care about form posts) */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Size maps to icon + spacing */
|
|
18
|
+
size?: Size;
|
|
19
|
+
/** Theme feeds foreground color tokens */
|
|
20
|
+
theme?: Theme;
|
|
21
|
+
/** Called on change (controlled or uncontrolled) */
|
|
22
|
+
onChange?: (value: number) => void;
|
|
23
|
+
/** Allow clicking the current selection to clear back to 0 */
|
|
24
|
+
allowClear?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
/** Gap override (e.g. '0.25rem') – otherwise uses density utilities */
|
|
27
|
+
gap?: string;
|
|
28
|
+
/** Accessible label for the rating group. Defaults to "Rating: X of Y" */
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function Rating({ value, defaultValue, max, disabled, readOnly, name, size, theme, onChange, allowClear, className, gap, ariaLabel, }: RatingProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export {};
|