@kennycorrea/ui 1.6.1 → 1.8.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 +15 -0
- package/dist/molecules/NavItem/NavItem.d.ts +5 -2
- package/dist/molecules/NavItem/NavItem.js +22 -22
- package/dist/molecules/NavItem/context.d.ts +5 -0
- package/dist/organisms/Accordion/Accordion.d.ts +34 -0
- package/dist/organisms/Accordion/Accordion.js +63 -0
- package/dist/organisms/Accordion/index.d.ts +1 -0
- package/dist/organisms/Accordion/index.js +1 -0
- package/dist/organisms/DataTable/DataTable.js +2 -1
- package/dist/organisms/Sidebar/Sidebar.js +15 -3
- package/dist/organisms/index.d.ts +1 -0
- package/dist/organisms/index.js +1 -0
- package/package.json +1 -1
- package/tokens.css +31 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0 — 2026-10-27
|
|
4
|
+
|
|
5
|
+
An `Accordion` organism.
|
|
6
|
+
|
|
7
|
+
- **`Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`.** Built on native `<details>`/`<summary>`, so keyboard and screen-reader support come from the browser. `type="multiple"` (default) or `"single"`; controlled with `value`/`onValueChange` or uncontrolled with `defaultValue`. The trigger takes any content and wraps; a chevron turns when open and stays still under reduced motion. Content mounts only while its item is open, unless `keepMounted` is set on the Accordion or the item. No built-in strings. Example in `examples/Accordions.tsx`; axe-checked in light and dark in `test/Accordion.test.tsx`.
|
|
8
|
+
|
|
9
|
+
## 1.7.0 — 2026-10-23
|
|
10
|
+
|
|
11
|
+
Sidebar groups as an accordion, and select-all for stacked tables.
|
|
12
|
+
|
|
13
|
+
- **`Sidebar` groups are an accordion.** Opening one group closes any other, in the persistent column and the off-canvas panel. The group open at first is the first with `defaultOpen`, or else the first containing the active item. A `NavItem` outside a `Sidebar` still opens on its own.
|
|
14
|
+
- **Choosing a group from the icon rail expands the sidebar with that group open**, instead of showing its children in a menu beside the rail. This is a behaviour change: the rail no longer has a dropdown.
|
|
15
|
+
- **Groups open and close with a height animation**, and without one under reduced motion. A closed group stays mounted, so its list is `inert` rather than `hidden`.
|
|
16
|
+
- **`DataTable` select-all in the stacked layout.** When rows stack into cards and the header is hidden, a "Select all rows" checkbox appears above them. It shows the same indeterminate state and acts on the current page. Closes the known gap noted in 1.6.1.
|
|
17
|
+
|
|
3
18
|
## 1.6.1 — 2026-10-23
|
|
4
19
|
|
|
5
20
|
Fixes found by running the example app in a real browser at phone and desktop widths, in light and dark. Each has a test in `test/browser-fixes.test.tsx`; the cascade problems are pinned by computing the selectors' specificity.
|
|
@@ -21,11 +21,14 @@ export interface NavItemProps {
|
|
|
21
21
|
/** Whether a group starts open. Default: open when one of its children is active. */
|
|
22
22
|
defaultOpen?: boolean;
|
|
23
23
|
}
|
|
24
|
+
/** The key a group is tracked by in the Sidebar's accordion. */
|
|
25
|
+
export declare function navKey(item: NavItemProps): string;
|
|
24
26
|
/**
|
|
25
27
|
* One entry in a navigation list: a link, a button, or a group of child links.
|
|
26
28
|
*
|
|
27
29
|
* Renders an `<li>`, so put it inside a `<ul>` (Sidebar does). Inside a collapsed Sidebar the
|
|
28
|
-
* label stays in the accessible name and is also shown as a tooltip
|
|
29
|
-
*
|
|
30
|
+
* label stays in the accessible name and is also shown as a tooltip. Inside a Sidebar groups are
|
|
31
|
+
* an accordion: opening one closes the others, and choosing one from the icon rail expands the
|
|
32
|
+
* sidebar with that group open. Outside one, each group opens on its own.
|
|
30
33
|
*/
|
|
31
34
|
export declare function NavItem(item: NavItemProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
|
3
|
-
import { DropdownMenu as ThemedDropdownMenu } from '@radix-ui/themes';
|
|
4
3
|
import { useId, useState } from 'react';
|
|
5
4
|
import { Tooltip } from '../../atoms/Tooltip/Tooltip.js';
|
|
6
5
|
import { useNavContext } from './context.js';
|
|
@@ -35,38 +34,39 @@ function ItemControl({ item, onNavigate }) {
|
|
|
35
34
|
}
|
|
36
35
|
return (_jsx("button", { type: "button", className: "ui-nav-item", "aria-current": item.active ? 'page' : undefined, onClick: onClick, children: children }));
|
|
37
36
|
}
|
|
37
|
+
/** The key a group is tracked by in the Sidebar's accordion. */
|
|
38
|
+
export function navKey(item) {
|
|
39
|
+
return item.id ?? item.label;
|
|
40
|
+
}
|
|
38
41
|
/**
|
|
39
42
|
* One entry in a navigation list: a link, a button, or a group of child links.
|
|
40
43
|
*
|
|
41
44
|
* Renders an `<li>`, so put it inside a `<ul>` (Sidebar does). Inside a collapsed Sidebar the
|
|
42
|
-
* label stays in the accessible name and is also shown as a tooltip
|
|
43
|
-
*
|
|
45
|
+
* label stays in the accessible name and is also shown as a tooltip. Inside a Sidebar groups are
|
|
46
|
+
* an accordion: opening one closes the others, and choosing one from the icon rail expands the
|
|
47
|
+
* sidebar with that group open. Outside one, each group opens on its own.
|
|
44
48
|
*/
|
|
45
49
|
export function NavItem(item) {
|
|
46
|
-
const { collapsed, onNavigate } = useNavContext();
|
|
50
|
+
const { collapsed, onNavigate, openGroup, setOpenGroup, expand } = useNavContext();
|
|
47
51
|
const childActive = item.items?.some((c) => c.active) ?? false;
|
|
48
|
-
const [
|
|
52
|
+
const [ownOpen, setOwnOpen] = useState(item.defaultOpen ?? childActive);
|
|
49
53
|
const listId = useId();
|
|
54
|
+
const key = navKey(item);
|
|
55
|
+
const open = setOpenGroup ? openGroup === key : ownOpen;
|
|
56
|
+
const setOpen = (next) => {
|
|
57
|
+
if (setOpenGroup)
|
|
58
|
+
setOpenGroup(next ? key : null);
|
|
59
|
+
else
|
|
60
|
+
setOwnOpen(next);
|
|
61
|
+
};
|
|
50
62
|
if (!item.items) {
|
|
51
63
|
return (_jsx("li", { children: _jsx(Tooltip, { label: collapsed ? item.label : undefined, children: _jsx(ItemControl, { item: item, onNavigate: onNavigate }) }) }));
|
|
52
64
|
}
|
|
53
65
|
if (collapsed) {
|
|
54
|
-
return (_jsx("li", { children:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
}, children: child.href !== undefined
|
|
60
|
-
? (child.render ?? ((p) => _jsx("a", { ...p })))({
|
|
61
|
-
href: child.href,
|
|
62
|
-
'aria-current': child.active ? 'page' : undefined,
|
|
63
|
-
onClick: () => {
|
|
64
|
-
child.onClick?.();
|
|
65
|
-
onNavigate?.();
|
|
66
|
-
},
|
|
67
|
-
children: child.label,
|
|
68
|
-
})
|
|
69
|
-
: child.label }, child.id ?? child.label)))] })] }) }));
|
|
66
|
+
return (_jsx("li", { children: _jsx(Tooltip, { label: item.label, children: _jsx("button", { type: "button", className: "ui-nav-item", "aria-current": childActive ? 'page' : undefined, "aria-expanded": false, onClick: () => {
|
|
67
|
+
setOpen(true);
|
|
68
|
+
expand?.();
|
|
69
|
+
}, children: _jsx(ItemContent, { item: item }) }) }) }));
|
|
70
70
|
}
|
|
71
|
-
return (_jsxs("li", { children: [_jsx("button", { type: "button", className: "ui-nav-item", "aria-expanded": open, "aria-controls": listId, onClick: () => setOpen(
|
|
71
|
+
return (_jsxs("li", { children: [_jsx("button", { type: "button", className: "ui-nav-item", "aria-expanded": open, "aria-controls": listId, onClick: () => setOpen(!open), children: _jsx(ItemContent, { item: item, trailing: _jsx("span", { className: "ui-nav-item__chevron", "aria-hidden": "true", children: _jsx(ChevronDownIcon, {}) }) }) }), _jsx("div", { className: "ui-nav-group", "data-open": open ? '' : undefined, children: _jsx("ul", { id: listId, className: "ui-nav-group__items", inert: !open, children: item.items.map((child) => (_jsx("li", { children: _jsx(ItemControl, { item: child, onNavigate: onNavigate }) }, child.id ?? child.label))) }) })] }));
|
|
72
72
|
}
|
|
@@ -9,6 +9,11 @@ export interface NavContextValue {
|
|
|
9
9
|
collapsed: boolean;
|
|
10
10
|
/** Called after any item is activated, so an off-canvas panel can close. */
|
|
11
11
|
onNavigate?: () => void;
|
|
12
|
+
/** The one group that is open, by key. Sidebar keeps it so groups behave as an accordion. */
|
|
13
|
+
openGroup?: string | null;
|
|
14
|
+
setOpenGroup?: (key: string | null) => void;
|
|
15
|
+
/** Leaves the icon rail, so a group chosen there can open in place. */
|
|
16
|
+
expand?: () => void;
|
|
12
17
|
}
|
|
13
18
|
export declare const NavContext: import("react").Context<NavContextValue>;
|
|
14
19
|
export declare function useNavContext(): NavContextValue;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
export interface AccordionProps {
|
|
3
|
+
/** `multiple` lets any number be open; `single` closes the others when one opens. */
|
|
4
|
+
type?: 'multiple' | 'single';
|
|
5
|
+
/** The open items. Controlled when given, with `onValueChange`. */
|
|
6
|
+
value?: string[];
|
|
7
|
+
defaultValue?: string[];
|
|
8
|
+
onValueChange?: (value: string[]) => void;
|
|
9
|
+
/** Keep every item's content mounted while closed. Each item can override it. */
|
|
10
|
+
keepMounted?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A stack of collapsible sections, each a native `<details>`/`<summary>`: the summary is the
|
|
16
|
+
* button, so keyboard (Enter/Space), focus and the expanded state reach assistive technology
|
|
17
|
+
* without any ARIA of our own. Open state lives in React, so `value` can be controlled.
|
|
18
|
+
* Content mounts only while open unless `keepMounted` is set.
|
|
19
|
+
*/
|
|
20
|
+
export declare function Accordion({ type, value, defaultValue, onValueChange, keepMounted, className, children, }: AccordionProps): import("react").JSX.Element;
|
|
21
|
+
export interface AccordionItemProps extends Omit<ComponentPropsWithoutRef<'details'>, 'open' | 'onToggle'> {
|
|
22
|
+
value: string;
|
|
23
|
+
/** Overrides the Accordion's `keepMounted` for this item. */
|
|
24
|
+
keepMounted?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function AccordionItem({ value, keepMounted, className, children, ...rest }: AccordionItemProps): import("react").JSX.Element;
|
|
27
|
+
export interface AccordionTriggerProps extends ComponentPropsWithoutRef<'summary'> {
|
|
28
|
+
/** Hide the chevron, e.g. when the trigger draws its own. */
|
|
29
|
+
hideChevron?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** The row that opens and closes the item. Takes any content: a logo, a name, badges, meta. */
|
|
32
|
+
export declare function AccordionTrigger({ className, children, onClick, hideChevron, ...rest }: AccordionTriggerProps): import("react").JSX.Element;
|
|
33
|
+
export type AccordionContentProps = ComponentPropsWithoutRef<'div'>;
|
|
34
|
+
export declare function AccordionContent({ className, ...rest }: AccordionContentProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
|
3
|
+
import { createContext, useCallback, useContext, useState, } from 'react';
|
|
4
|
+
import { cn } from '../../utils/index.js';
|
|
5
|
+
const AccordionContext = createContext(null);
|
|
6
|
+
const ItemContext = createContext(null);
|
|
7
|
+
function useAccordion() {
|
|
8
|
+
const ctx = useContext(AccordionContext);
|
|
9
|
+
if (!ctx)
|
|
10
|
+
throw new Error('AccordionItem must be used inside an Accordion');
|
|
11
|
+
return ctx;
|
|
12
|
+
}
|
|
13
|
+
function useItem() {
|
|
14
|
+
const ctx = useContext(ItemContext);
|
|
15
|
+
if (!ctx)
|
|
16
|
+
throw new Error('AccordionTrigger and AccordionContent must be inside an AccordionItem');
|
|
17
|
+
return ctx;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A stack of collapsible sections, each a native `<details>`/`<summary>`: the summary is the
|
|
21
|
+
* button, so keyboard (Enter/Space), focus and the expanded state reach assistive technology
|
|
22
|
+
* without any ARIA of our own. Open state lives in React, so `value` can be controlled.
|
|
23
|
+
* Content mounts only while open unless `keepMounted` is set.
|
|
24
|
+
*/
|
|
25
|
+
export function Accordion({ type = 'multiple', value, defaultValue, onValueChange, keepMounted = false, className, children, }) {
|
|
26
|
+
const [inner, setInner] = useState(defaultValue ?? []);
|
|
27
|
+
const open = value ?? inner;
|
|
28
|
+
const toggle = useCallback((item) => {
|
|
29
|
+
const next = open.includes(item)
|
|
30
|
+
? open.filter((v) => v !== item)
|
|
31
|
+
: type === 'single'
|
|
32
|
+
? [item]
|
|
33
|
+
: [...open, item];
|
|
34
|
+
if (value === undefined)
|
|
35
|
+
setInner(next);
|
|
36
|
+
onValueChange?.(next);
|
|
37
|
+
}, [open, type, value, onValueChange]);
|
|
38
|
+
const isOpen = useCallback((item) => open.includes(item), [open]);
|
|
39
|
+
return (_jsx(AccordionContext.Provider, { value: { isOpen, toggle, keepMounted }, children: _jsx("div", { className: cn('ui-accordion', className), children: children }) }));
|
|
40
|
+
}
|
|
41
|
+
export function AccordionItem({ value, keepMounted, className, children, ...rest }) {
|
|
42
|
+
const acc = useAccordion();
|
|
43
|
+
const open = acc.isOpen(value);
|
|
44
|
+
return (_jsx(ItemContext.Provider, { value: { value, open, keepMounted: keepMounted ?? acc.keepMounted }, children: _jsx("details", { ...rest, open: open, className: cn('ui-accordion__item', className), children: children }) }));
|
|
45
|
+
}
|
|
46
|
+
/** The row that opens and closes the item. Takes any content: a logo, a name, badges, meta. */
|
|
47
|
+
export function AccordionTrigger({ className, children, onClick, hideChevron = false, ...rest }) {
|
|
48
|
+
const acc = useAccordion();
|
|
49
|
+
const item = useItem();
|
|
50
|
+
const handleClick = (e) => {
|
|
51
|
+
onClick?.(e);
|
|
52
|
+
// The browser would flip `open` itself; React owns it instead so `value` stays the truth.
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
acc.toggle(item.value);
|
|
55
|
+
};
|
|
56
|
+
return (_jsxs("summary", { ...rest, onClick: handleClick, className: cn('ui-accordion__trigger', className), children: [children, hideChevron ? null : (_jsx("span", { "aria-hidden": "true", className: "ui-accordion__chevron", children: _jsx(ChevronDownIcon, {}) }))] }));
|
|
57
|
+
}
|
|
58
|
+
export function AccordionContent({ className, ...rest }) {
|
|
59
|
+
const item = useItem();
|
|
60
|
+
if (!item.open && !item.keepMounted)
|
|
61
|
+
return null;
|
|
62
|
+
return _jsx("div", { ...rest, className: cn('ui-accordion__content', className) });
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Accordion.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Accordion.js';
|
|
@@ -23,6 +23,7 @@ const INTERACTIVE = 'a, button, input, select, textarea, [role="checkbox"], [rol
|
|
|
23
23
|
export function DataTable({ columns, rows, getRowId, caption, showCaption = false, getRowLabel, sort = null, onSortChange, selection, onSelectionChange, rowActions, onRowAction, onRowClick, state = 'ready', empty, error, loadingRows = 5, pagination, density = 'comfortable', stackBelow = 'md', stickyFirstColumn = false, maxHeight, className, }) {
|
|
24
24
|
const strings = useUiStrings();
|
|
25
25
|
const captionId = useId();
|
|
26
|
+
const selectAllId = useId();
|
|
26
27
|
const selectable = !!selection && !!onSelectionChange;
|
|
27
28
|
const hasActions = !!rowActions;
|
|
28
29
|
const span = columns.length + (selectable ? 1 : 0) + (hasActions ? 1 : 0);
|
|
@@ -113,7 +114,7 @@ export function DataTable({ columns, rows, getRowId, caption, showCaption = fals
|
|
|
113
114
|
const regionStyle = maxHeight
|
|
114
115
|
? { maxBlockSize: maxHeight }
|
|
115
116
|
: undefined;
|
|
116
|
-
return (_jsxs("div", { className: ['ui-table', className].filter(Boolean).join(' '), "data-density": density, "data-stack": stackBelow || undefined, "data-sticky-first": stickyFirstColumn ? '' : undefined, children: [effective === 'loading' ? (_jsx("span", { role: "status", className: "ui-sr-only", children: strings.loading })) : null, _jsx("section", { className: "ui-table__region", "aria-labelledby": captionId,
|
|
117
|
+
return (_jsxs("div", { className: ['ui-table', className].filter(Boolean).join(' '), "data-density": density, "data-stack": stackBelow || undefined, "data-sticky-first": stickyFirstColumn ? '' : undefined, children: [effective === 'loading' ? (_jsx("span", { role: "status", className: "ui-sr-only", children: strings.loading })) : null, selectable && effective === 'ready' ? (_jsxs("label", { className: "ui-table__selectall", htmlFor: selectAllId, children: [_jsx(Checkbox, { id: selectAllId, checked: allChecked, onCheckedChange: toggleAll }), strings.selectAll] })) : null, _jsx("section", { className: "ui-table__region", "aria-labelledby": captionId,
|
|
117
118
|
// biome-ignore lint/a11y/noNoninteractiveTabindex: a scrolling region must be reachable by keyboard to be scrolled.
|
|
118
119
|
tabIndex: 0, style: regionStyle, children: _jsxs("table", { className: "ui-table__table", role: "table", "aria-busy": effective === 'loading' || undefined, children: [_jsx("caption", { id: captionId, className: showCaption ? 'ui-table__caption' : 'ui-sr-only', children: caption }), header, _jsx("tbody", { role: "rowgroup", children: body })] }) }), pagination && effective !== 'loading' ? _jsx(Pagination, { ...pagination }) : null] }));
|
|
119
120
|
}
|
|
@@ -8,7 +8,7 @@ import { minWidth } from '../../hooks/breakpoints.js';
|
|
|
8
8
|
import { useControllableState } from '../../hooks/useControllableState.js';
|
|
9
9
|
import { useMediaQuery } from '../../hooks/useMediaQuery.js';
|
|
10
10
|
import { NavContext } from '../../molecules/NavItem/context.js';
|
|
11
|
-
import { NavItem } from '../../molecules/NavItem/NavItem.js';
|
|
11
|
+
import { NavItem, navKey } from '../../molecules/NavItem/NavItem.js';
|
|
12
12
|
import { useUiStrings } from '../../strings.js';
|
|
13
13
|
import { useThemePortalTarget } from '../../utils/themePortal.js';
|
|
14
14
|
import { useShell } from '../AppShell/context.js';
|
|
@@ -38,6 +38,13 @@ export function Sidebar({ header, collapsedHeader, sections, footer, collapsed:
|
|
|
38
38
|
});
|
|
39
39
|
const { anchorRef, target, resolved } = useThemePortalTarget(container);
|
|
40
40
|
const navId = useId();
|
|
41
|
+
const [openGroup, setOpenGroup] = useState(() => {
|
|
42
|
+
for (const item of sections.flatMap((section) => section.items)) {
|
|
43
|
+
if (item.items && (item.defaultOpen ?? item.items.some((c) => c.active)))
|
|
44
|
+
return navKey(item);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
41
48
|
const wide = shell ? shell.wide : ownWide;
|
|
42
49
|
const panelOpen = shell ? shell.navOpen : (open ?? ownOpen);
|
|
43
50
|
const setPanelOpen = (next) => {
|
|
@@ -51,9 +58,14 @@ export function Sidebar({ header, collapsedHeader, sections, footer, collapsed:
|
|
|
51
58
|
if (wide) {
|
|
52
59
|
const toggleLabel = collapsed ? strings.expandSidebar : strings.collapseSidebar;
|
|
53
60
|
const railHeader = collapsed ? (collapsedHeader ?? header) : header;
|
|
54
|
-
return (_jsxs("div", { className: "ui-sidebar", "data-collapsed": collapsed ? '' : undefined, children: [railHeader ? _jsx("div", { className: "ui-sidebar__header", children: railHeader }) : null, _jsx(NavContext.Provider, { value: { collapsed }, children: _jsx(Sections, { sections: sections, label: label, id: navId }) }), footer || collapsible ? (_jsxs("div", { className: "ui-sidebar__footer", children: [footer, collapsible ? (_jsx(Tooltip, { label: collapsed ? toggleLabel : undefined, children: _jsxs("button", { type: "button", className: "ui-nav-item", "aria-expanded": !collapsed, "aria-controls": navId, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { className: "ui-nav-item__icon", "aria-hidden": "true", children: collapsed ? _jsx(DoubleArrowRightIcon, {}) : _jsx(DoubleArrowLeftIcon, {}) }), _jsx("span", { className: "ui-nav-item__label", children: toggleLabel })] }) })) : null] })) : null] }));
|
|
61
|
+
return (_jsxs("div", { className: "ui-sidebar", "data-collapsed": collapsed ? '' : undefined, children: [railHeader ? _jsx("div", { className: "ui-sidebar__header", children: railHeader }) : null, _jsx(NavContext.Provider, { value: { collapsed, openGroup, setOpenGroup, expand: () => setCollapsed(false) }, children: _jsx(Sections, { sections: sections, label: label, id: navId }) }), footer || collapsible ? (_jsxs("div", { className: "ui-sidebar__footer", children: [footer, collapsible ? (_jsx(Tooltip, { label: collapsed ? toggleLabel : undefined, children: _jsxs("button", { type: "button", className: "ui-nav-item", "aria-expanded": !collapsed, "aria-controls": navId, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { className: "ui-nav-item__icon", "aria-hidden": "true", children: collapsed ? _jsx(DoubleArrowRightIcon, {}) : _jsx(DoubleArrowLeftIcon, {}) }), _jsx("span", { className: "ui-nav-item__label", children: toggleLabel })] }) })) : null] })) : null] }));
|
|
55
62
|
}
|
|
56
|
-
const panel = (_jsxs(_Fragment, { children: [_jsx(RadixDialog.Overlay, { className: "ui-sidebar-overlay" }), _jsxs(RadixDialog.Content, { className: "ui-sidebar-panel", "aria-describedby": undefined, children: [_jsx(RadixDialog.Title, { className: "ui-sr-only", children: label }), _jsxs("div", { className: "ui-sidebar__header", children: [_jsx("div", { className: "min-w-0 flex-1", children: header }), _jsxs(RadixDialog.Close, { className: "ui-nav-item ui-sidebar__close", children: [_jsx("span", { className: "ui-nav-item__icon", "aria-hidden": "true", children: _jsx(Cross1Icon, {}) }), _jsx("span", { className: "ui-sr-only", children: strings.closeNavigation })] })] }), _jsx(NavContext.Provider, { value: {
|
|
63
|
+
const panel = (_jsxs(_Fragment, { children: [_jsx(RadixDialog.Overlay, { className: "ui-sidebar-overlay" }), _jsxs(RadixDialog.Content, { className: "ui-sidebar-panel", "aria-describedby": undefined, children: [_jsx(RadixDialog.Title, { className: "ui-sr-only", children: label }), _jsxs("div", { className: "ui-sidebar__header", children: [_jsx("div", { className: "min-w-0 flex-1", children: header }), _jsxs(RadixDialog.Close, { className: "ui-nav-item ui-sidebar__close", children: [_jsx("span", { className: "ui-nav-item__icon", "aria-hidden": "true", children: _jsx(Cross1Icon, {}) }), _jsx("span", { className: "ui-sr-only", children: strings.closeNavigation })] })] }), _jsx(NavContext.Provider, { value: {
|
|
64
|
+
collapsed: false,
|
|
65
|
+
onNavigate: () => setPanelOpen(false),
|
|
66
|
+
openGroup,
|
|
67
|
+
setOpenGroup,
|
|
68
|
+
}, children: _jsx(Sections, { sections: sections, label: label, id: navId }) }), footer ? _jsx("div", { className: "ui-sidebar__footer", children: footer }) : null] })] }));
|
|
57
69
|
const anchor = _jsx("span", { ref: anchorRef, hidden: true });
|
|
58
70
|
if (!resolved)
|
|
59
71
|
return anchor;
|
package/dist/organisms/index.js
CHANGED
package/package.json
CHANGED
package/tokens.css
CHANGED
|
@@ -1022,8 +1022,10 @@
|
|
|
1022
1022
|
.ui-nav-item__badge[data-dot] { min-inline-size: 8px; block-size: 8px; padding: 0; }
|
|
1023
1023
|
.ui-nav-item__chevron { flex: none; display: inline-flex; transition: transform 150ms ease-out; }
|
|
1024
1024
|
.ui-nav-item[aria-expanded="true"] .ui-nav-item__chevron { transform: rotate(180deg); }
|
|
1025
|
-
.ui-nav-
|
|
1026
|
-
|
|
1025
|
+
.ui-nav-group { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows 200ms ease-out, visibility 200ms; }
|
|
1026
|
+
.ui-nav-group[data-open] { grid-template-rows: 1fr; visibility: visible; }
|
|
1027
|
+
.ui-nav-group__items { min-block-size: 0; overflow: hidden; margin: 0; padding: 2px 0 0 32px; list-style: none; display: flex; flex-direction: column; gap: 2px; }
|
|
1028
|
+
@media (prefers-reduced-motion: reduce) { .ui-nav-item__chevron, .ui-nav-group { transition: none; } }
|
|
1027
1029
|
|
|
1028
1030
|
.ui-topbar { position: sticky; inset-block-start: 0; z-index: 20; display: grid; box-sizing: border-box;
|
|
1029
1031
|
grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "lead title actions" "search search search";
|
|
@@ -1187,6 +1189,7 @@
|
|
|
1187
1189
|
.ui-table__sort:focus-visible, .ui-table__rowbutton:focus-visible { outline: 2px solid var(--focus-8); outline-offset: 2px; border-radius: var(--radius-1); }
|
|
1188
1190
|
.ui-table__rowbutton { padding: 0; border: 0; background: none; font: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
1189
1191
|
@media (hover: hover) { .ui-table__rowbutton:hover { text-decoration: underline; } }
|
|
1192
|
+
.ui-table__selectall { display: none; align-items: center; gap: 8px; padding-inline: 12px; font-size: 14px; font-weight: 500; cursor: pointer; }
|
|
1190
1193
|
.ui-table__empty { padding: 0; }
|
|
1191
1194
|
.ui-table__table .ui-table__empty { block-size: auto; }
|
|
1192
1195
|
.ui-table__empty > * { flex: 1; }
|
|
@@ -1197,6 +1200,7 @@
|
|
|
1197
1200
|
@container (width < 480px) {
|
|
1198
1201
|
.ui-table[data-stack="sm"] :is(.ui-table__table, tbody, tr, th, td) { display: block; }
|
|
1199
1202
|
.ui-table[data-stack="sm"] thead { display: none; }
|
|
1203
|
+
.ui-table[data-stack="sm"] .ui-table__selectall { display: flex; }
|
|
1200
1204
|
.ui-table[data-stack="sm"] .ui-table__region { border: 0; background: none; overflow: visible; }
|
|
1201
1205
|
.ui-table[data-stack="sm"] tbody tr { position: relative; margin-block-end: 12px; padding: 8px 12px; border: 1px solid var(--color-hairline); border-radius: var(--radius-4); background: var(--color-panel-solid); }
|
|
1202
1206
|
.ui-table[data-stack="sm"] .ui-table__table tbody :is(th, td) { display: flex; justify-content: space-between; gap: 12px; block-size: auto; min-block-size: 32px; align-items: center; padding: 4px 0; border: 0; text-align: end; }
|
|
@@ -1208,6 +1212,7 @@
|
|
|
1208
1212
|
@container (width < 640px) {
|
|
1209
1213
|
.ui-table[data-stack="md"] :is(.ui-table__table, tbody, tr, th, td) { display: block; }
|
|
1210
1214
|
.ui-table[data-stack="md"] thead { display: none; }
|
|
1215
|
+
.ui-table[data-stack="md"] .ui-table__selectall { display: flex; }
|
|
1211
1216
|
.ui-table[data-stack="md"] .ui-table__region { border: 0; background: none; overflow: visible; }
|
|
1212
1217
|
.ui-table[data-stack="md"] tbody tr { position: relative; margin-block-end: 12px; padding: 8px 12px; border: 1px solid var(--color-hairline); border-radius: var(--radius-4); background: var(--color-panel-solid); }
|
|
1213
1218
|
.ui-table[data-stack="md"] .ui-table__table tbody :is(th, td) { display: flex; justify-content: space-between; gap: 12px; block-size: auto; min-block-size: 32px; align-items: center; padding: 4px 0; border: 0; text-align: end; }
|
|
@@ -1219,6 +1224,7 @@
|
|
|
1219
1224
|
@container (width < 800px) {
|
|
1220
1225
|
.ui-table[data-stack="lg"] :is(.ui-table__table, tbody, tr, th, td) { display: block; }
|
|
1221
1226
|
.ui-table[data-stack="lg"] thead { display: none; }
|
|
1227
|
+
.ui-table[data-stack="lg"] .ui-table__selectall { display: flex; }
|
|
1222
1228
|
.ui-table[data-stack="lg"] .ui-table__region { border: 0; background: none; overflow: visible; }
|
|
1223
1229
|
.ui-table[data-stack="lg"] tbody tr { position: relative; margin-block-end: 12px; padding: 8px 12px; border: 1px solid var(--color-hairline); border-radius: var(--radius-4); background: var(--color-panel-solid); }
|
|
1224
1230
|
.ui-table[data-stack="lg"] .ui-table__table tbody :is(th, td) { display: flex; justify-content: space-between; gap: 12px; block-size: auto; min-block-size: 32px; align-items: center; padding: 4px 0; border: 0; text-align: end; }
|
|
@@ -1409,3 +1415,26 @@
|
|
|
1409
1415
|
.rt-TextAreaRoot:has(> textarea[aria-invalid="true"]),
|
|
1410
1416
|
.rt-SelectTrigger[aria-invalid="true"],
|
|
1411
1417
|
.ui-date-input.rt-Button[aria-invalid="true"] { box-shadow: inset 0 0 0 1px var(--red-8); }
|
|
1418
|
+
|
|
1419
|
+
/* Accordion: native <details>, its marker replaced by a chevron that turns when open. */
|
|
1420
|
+
.ui-accordion { display: flex; flex-direction: column; gap: 8px; }
|
|
1421
|
+
.ui-accordion__item {
|
|
1422
|
+
border-radius: var(--radius-3, 8px);
|
|
1423
|
+
border: 1px solid var(--color-hairline);
|
|
1424
|
+
background: var(--color-panel-solid);
|
|
1425
|
+
color: var(--color-ink, inherit);
|
|
1426
|
+
}
|
|
1427
|
+
.ui-accordion__trigger {
|
|
1428
|
+
display: flex; flex-wrap: wrap; align-items: center; column-gap: 12px; row-gap: 4px;
|
|
1429
|
+
padding: 16px; cursor: pointer; list-style: none; border-radius: inherit;
|
|
1430
|
+
}
|
|
1431
|
+
.ui-accordion__trigger::-webkit-details-marker { display: none; }
|
|
1432
|
+
.ui-accordion__trigger::marker { content: ""; }
|
|
1433
|
+
.ui-accordion__trigger:focus-visible { outline: 2px solid var(--focus-8, var(--accent-8)); outline-offset: 2px; }
|
|
1434
|
+
.ui-accordion__chevron { display: inline-flex; margin-inline-start: auto; transition: transform 150ms ease; }
|
|
1435
|
+
.ui-accordion__item[open] > .ui-accordion__trigger .ui-accordion__chevron { transform: rotate(180deg); }
|
|
1436
|
+
.ui-accordion__content { border-top: 1px solid var(--color-hairline); padding: 16px; }
|
|
1437
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1438
|
+
.ui-accordion__chevron { transition: none; }
|
|
1439
|
+
.ui-accordion__item[open] > .ui-accordion__trigger .ui-accordion__chevron { transform: none; }
|
|
1440
|
+
}
|