@apliteni/apliteni-ui 0.43.0 → 0.44.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/README.md +20 -36
- package/package.json +1 -1
- package/react/README.md +7 -1
- package/react/dist/index.d.ts +15 -1
- package/react/dist/index.js +78 -0
- package/src/assets/icons.js +1 -1
- package/src/components/pagination.js +2 -2
- package/src/components/tooltip.js +1 -1
- package/src/styles/base.css +1 -1
- package/src/styles/command-palette.css +1 -1
- package/src/styles/layout.css +4 -4
- package/src/styles/pagination.css +1 -1
- package/src/tokens/tokens.css +1 -1
package/README.md
CHANGED
|
@@ -217,42 +217,26 @@ react/ # React components — private workspace, built to reac
|
|
|
217
217
|
dist/ # tsup output; shipped as …/react and …/react/css
|
|
218
218
|
```
|
|
219
219
|
|
|
220
|
-
##
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
npm
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
Versioned publish runs from CI on a GitHub Release:
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
npm version patch # or minor / major — bumps package.json + tags
|
|
244
|
-
git push --follow-tags
|
|
245
|
-
gh release create v$(node -p "require('./package.json').version") --generate-notes
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
The **Release** workflow (`.github/workflows/release.yml`) then publishes to the public
|
|
249
|
-
npm registry over npm Trusted Publishing (OIDC) — there is no long-lived token. No
|
|
250
|
-
manual `npm publish` needed. It runs in two jobs: `build` installs and runs `npm pack`,
|
|
251
|
-
whose `prepare` rebuilds `react/dist` from the tagged commit, and `publish` — the only
|
|
252
|
-
job that can mint an OIDC credential — just publishes that tarball, so no dependency
|
|
253
|
-
or build script ever runs beside the credential. The packaging guard
|
|
254
|
-
(`scripts/packaging.test.js`) fails CI if the React subpath isn't in the tarball, and
|
|
255
|
-
the release itself re-checks the tarball before publishing it.
|
|
220
|
+
## Contribute
|
|
221
|
+
|
|
222
|
+
Use Node 20 or newer. Install [jq](https://jqlang.github.io/jq/), then run `npm ci` at
|
|
223
|
+
the repository root. Start Storybook with `npm run storybook`. For React, use `npm run
|
|
224
|
+
storybook -w react`. Before opening a PR, run `npm test`, `npm test -w react`, `npm run
|
|
225
|
+
build-storybook`, and `node site/build.mjs`.
|
|
226
|
+
|
|
227
|
+
To add a component, put its token-based CSS in `src/styles/`. Include it in
|
|
228
|
+
`src/index.css` and in both the `styles` map and `cssText` in `src/inline.js`. Add the
|
|
229
|
+
HTML factory under `src/components/`, export it from `src/index.js`, and add a
|
|
230
|
+
playground and state examples under `stories/components/`. Put React components in
|
|
231
|
+
`react/src/`, with a test and story that use the shared classes and tokens. Read the
|
|
232
|
+
[guidelines](guidelines/overview.md) for the design rules.
|
|
233
|
+
|
|
234
|
+
Open an issue, branch from `main`, and link the issue in your PR. Changes to published
|
|
235
|
+
files, including this README, require a version bump in `package.json` and
|
|
236
|
+
`package-lock.json`, plus an entry in `site/changelog.mjs`. Merging the bump starts the
|
|
237
|
+
release workflow. Do not push a tag or publish by hand.
|
|
238
|
+
|
|
239
|
+
[Agent rules](AGENTS.md) cover review, data handling, and release checks.
|
|
256
240
|
|
|
257
241
|
## Deploy (ui.apli.tech)
|
|
258
242
|
|
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -28,7 +28,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
|
|
|
28
28
|
import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Components: `DataTable`, `Pagination`, `StatBand`, `Modal`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Button`, `Badge`, `Card`, `Icon`.
|
|
31
|
+
Components: `DataTable`, `Pagination`, `StatBand`, `Modal`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Snippet`, `Tabs`, `Button`, `Badge`, `Card`, `Icon`.
|
|
32
32
|
|
|
33
33
|
`Snippet` accepts `label`, `code`, `reveal`, and `copyLabel` props. It treats `code`
|
|
34
34
|
as plain text and copies it exactly as provided. After a successful clipboard write,
|
|
@@ -332,3 +332,9 @@ import '@apliteni/apliteni-ui/react/css';
|
|
|
332
332
|
]} />
|
|
333
333
|
</DrawerSection>
|
|
334
334
|
```
|
|
335
|
+
|
|
336
|
+
## Tabs
|
|
337
|
+
|
|
338
|
+
Tabs is controlled through `value` and `onChange`. Pass items with unique values, labels, optional counts, and React panel content. Provide a label for the tablist and a value that matches one item. Inactive panel content is unmounted.
|
|
339
|
+
|
|
340
|
+
Arrow keys move between tabs and wrap from the last tab to the first. They also activate the selected tab. Home and End select the first and last tabs. Tab moves to the selected panel. New panels use the kit’s fade animation, which is shortened when reduced motion is enabled.
|
package/react/dist/index.d.ts
CHANGED
|
@@ -450,7 +450,21 @@ type SnippetProps = {
|
|
|
450
450
|
};
|
|
451
451
|
declare function Snippet({ label, code, reveal, copyLabel }: SnippetProps): react.JSX.Element;
|
|
452
452
|
|
|
453
|
+
type TabItem = {
|
|
454
|
+
value: string;
|
|
455
|
+
label: string;
|
|
456
|
+
count?: number;
|
|
457
|
+
panel: ReactNode;
|
|
458
|
+
};
|
|
459
|
+
type TabsProps = {
|
|
460
|
+
items: TabItem[];
|
|
461
|
+
value: string;
|
|
462
|
+
onChange: (value: string) => void;
|
|
463
|
+
label: string;
|
|
464
|
+
};
|
|
465
|
+
declare function Tabs({ items, value, onChange, label }: TabsProps): react.JSX.Element;
|
|
466
|
+
|
|
453
467
|
declare const PAGE_SIZES: readonly number[];
|
|
454
468
|
declare const DEFAULT_PAGE_SIZE: number;
|
|
455
469
|
|
|
456
|
-
export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TableSort, sortTableRows };
|
|
470
|
+
export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TabItem, type TableSort, Tabs, type TabsProps, sortTableRows };
|
package/react/dist/index.js
CHANGED
|
@@ -1748,6 +1748,83 @@ function Snippet({ label = "shell", code = "", reveal = false, copyLabel = "Copy
|
|
|
1748
1748
|
] });
|
|
1749
1749
|
}
|
|
1750
1750
|
|
|
1751
|
+
// src/Tabs.tsx
|
|
1752
|
+
import { useEffect as useEffect6, useId as useId6, useRef as useRef10 } from "react";
|
|
1753
|
+
import { playEntrance } from "@apliteni/apliteni-ui/motion";
|
|
1754
|
+
import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1755
|
+
function Tabs({ items, value, onChange, label }) {
|
|
1756
|
+
const id = useId6();
|
|
1757
|
+
const previous = useRef10(value);
|
|
1758
|
+
const activePanel = useRef10(null);
|
|
1759
|
+
const buttons = useRef10([]);
|
|
1760
|
+
useEffect6(() => {
|
|
1761
|
+
if (previous.current !== value) playEntrance(activePanel.current);
|
|
1762
|
+
previous.current = value;
|
|
1763
|
+
}, [value]);
|
|
1764
|
+
return /* @__PURE__ */ jsxs15("div", { className: "ui-tabs", children: [
|
|
1765
|
+
/* @__PURE__ */ jsx19("div", { className: "ui-tabs__list", role: "tablist", "aria-label": label, children: items.map((item, i) => /* @__PURE__ */ jsxs15(
|
|
1766
|
+
"button",
|
|
1767
|
+
{
|
|
1768
|
+
type: "button",
|
|
1769
|
+
role: "tab",
|
|
1770
|
+
ref: (el) => {
|
|
1771
|
+
buttons.current[i] = el;
|
|
1772
|
+
},
|
|
1773
|
+
className: `ui-tabs__tab${item.value === value ? " is-active" : ""}`,
|
|
1774
|
+
id: `${id}-tab-${i}`,
|
|
1775
|
+
"aria-controls": `${id}-panel-${i}`,
|
|
1776
|
+
"aria-selected": item.value === value,
|
|
1777
|
+
tabIndex: item.value === value ? 0 : -1,
|
|
1778
|
+
onClick: () => onChange(item.value),
|
|
1779
|
+
onKeyDown: (event) => {
|
|
1780
|
+
let next;
|
|
1781
|
+
switch (event.key) {
|
|
1782
|
+
case "ArrowRight":
|
|
1783
|
+
next = (i + 1) % items.length;
|
|
1784
|
+
break;
|
|
1785
|
+
case "ArrowLeft":
|
|
1786
|
+
next = (i - 1 + items.length) % items.length;
|
|
1787
|
+
break;
|
|
1788
|
+
case "Home":
|
|
1789
|
+
next = 0;
|
|
1790
|
+
break;
|
|
1791
|
+
case "End":
|
|
1792
|
+
next = items.length - 1;
|
|
1793
|
+
break;
|
|
1794
|
+
default:
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
event.preventDefault();
|
|
1798
|
+
buttons.current[next]?.focus();
|
|
1799
|
+
onChange(items[next].value);
|
|
1800
|
+
},
|
|
1801
|
+
children: [
|
|
1802
|
+
item.label,
|
|
1803
|
+
item.count != null && /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
1804
|
+
" ",
|
|
1805
|
+
item.count
|
|
1806
|
+
] })
|
|
1807
|
+
]
|
|
1808
|
+
},
|
|
1809
|
+
item.value
|
|
1810
|
+
)) }),
|
|
1811
|
+
/* @__PURE__ */ jsx19("div", { className: "ui-tabs__panels", children: items.map((item, i) => /* @__PURE__ */ jsx19(
|
|
1812
|
+
"div",
|
|
1813
|
+
{
|
|
1814
|
+
className: "ui-tabs__panel",
|
|
1815
|
+
role: "tabpanel",
|
|
1816
|
+
ref: item.value === value ? activePanel : void 0,
|
|
1817
|
+
id: `${id}-panel-${i}`,
|
|
1818
|
+
"aria-labelledby": `${id}-tab-${i}`,
|
|
1819
|
+
tabIndex: 0,
|
|
1820
|
+
hidden: item.value !== value,
|
|
1821
|
+
children: item.value === value ? item.panel : null
|
|
1822
|
+
},
|
|
1823
|
+
item.value
|
|
1824
|
+
)) })
|
|
1825
|
+
] });
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1751
1828
|
// src/index.ts
|
|
1752
1829
|
var PAGE_SIZES = KIT_PAGE_SIZES;
|
|
1753
1830
|
var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
|
|
@@ -1778,5 +1855,6 @@ export {
|
|
|
1778
1855
|
SkeletonTable,
|
|
1779
1856
|
Snippet,
|
|
1780
1857
|
StatBand,
|
|
1858
|
+
Tabs,
|
|
1781
1859
|
sortTableRows
|
|
1782
1860
|
};
|
package/src/assets/icons.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// iconCategories drives the Storybook grid.
|
|
8
8
|
//
|
|
9
9
|
// Naming, grouping and provenance, and the gates that hold them:
|
|
10
|
-
//
|
|
10
|
+
// Use Lucide paths and name each glyph for what it depicts.
|
|
11
11
|
import { esc } from '../html.js';
|
|
12
12
|
const NAV = {
|
|
13
13
|
chevronDown: '<path d="M6 9l6 6 6-6" stroke-linecap="round" stroke-linejoin="round"/>',
|
|
@@ -241,7 +241,7 @@ export function pagination({
|
|
|
241
241
|
*
|
|
242
242
|
* @returns {() => void} a function that removes the listeners.
|
|
243
243
|
*
|
|
244
|
-
*
|
|
244
|
+
* Delegate pager events from the root so handlers survive rerenders.
|
|
245
245
|
*/
|
|
246
246
|
export function wirePagination(root = document, { onPage, onPageSize } = {}) {
|
|
247
247
|
const scope = typeof root === 'string' ? document.querySelector(root) : root;
|
|
@@ -310,7 +310,7 @@ export function wirePagination(root = document, { onPage, onPageSize } = {}) {
|
|
|
310
310
|
* @returns {Element|null} the status element, or null when there is nothing to
|
|
311
311
|
* update — safe against a torn-down view.
|
|
312
312
|
*
|
|
313
|
-
*
|
|
313
|
+
* Update the existing live region so screen readers can announce it.
|
|
314
314
|
*/
|
|
315
315
|
export function setPagerStatus(root, text) {
|
|
316
316
|
const el = typeof root === 'string' ? document.querySelector(root) : root;
|
|
@@ -17,7 +17,7 @@ import { esc } from './index.js';
|
|
|
17
17
|
const cx = (...a) => a.filter(Boolean).join(' ');
|
|
18
18
|
/* classes: ui-tip__label ui-tip__value ui-tip__detail — built from `k` below, so
|
|
19
19
|
a sweep reading class names off the source cannot see them.
|
|
20
|
-
|
|
20
|
+
*/
|
|
21
21
|
const PARTS = ['label', 'value', 'detail'];
|
|
22
22
|
const px = (n) => (typeof n === 'number' && Number.isFinite(n) ? `${n}px` : null);
|
|
23
23
|
let seq = 0;
|
package/src/styles/base.css
CHANGED
|
@@ -128,7 +128,7 @@ strong {
|
|
|
128
128
|
so this weighs (0,0,1) and any component rule that sizes an icon outranks it.
|
|
129
129
|
The count of those rules is deliberately not written here — it moved once
|
|
130
130
|
already, and two gates hold it over the ground each actually sweeps.
|
|
131
|
-
|
|
131
|
+
*/
|
|
132
132
|
svg:where(:not([width]):not([height])) {
|
|
133
133
|
width: 1.1em;
|
|
134
134
|
height: 1.1em;
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
/* Literal px, and a line-height it does not inherit: the target-size gate
|
|
115
115
|
reads declarations rather than a laid-out box, and `var()` inside a padding
|
|
116
116
|
shorthand measures as nothing there.
|
|
117
|
-
|
|
117
|
+
*/
|
|
118
118
|
padding: 16px 0;
|
|
119
119
|
line-height: 1.5;
|
|
120
120
|
font-family: inherit;
|
package/src/styles/layout.css
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
/* The rail's hover WAS --surface-2, so moving the rail onto it would have made
|
|
63
63
|
hovering a row do nothing. The danger row and the current row are left out by
|
|
64
64
|
name, because this rule outranks the states nav.css writes for them.
|
|
65
|
-
|
|
65
|
+
*/
|
|
66
66
|
.ui-app__rail .ui-nav__item:not(.is-danger):not(.is-active):hover { background: var(--surface-3); /* ring-gap: inherit — control paint; its focus indicator uses the containing surface. */ }
|
|
67
67
|
.ui-app__brand {
|
|
68
68
|
display: flex;
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
/* A destructive row of the rail is a navigation row and rests like one; the danger
|
|
119
119
|
intent is the step nav.css writes for it. Both of nav.css's danger states are
|
|
120
120
|
named, not hover alone.
|
|
121
|
-
|
|
121
|
+
*/
|
|
122
122
|
.ui-app__rail .ui-nav__item.is-danger:not(:hover):not(:focus-visible) { color: var(--text); }
|
|
123
123
|
|
|
124
124
|
/* One rule closes the rail, and it is this one: the nav's footer slot is empty now
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
/* As tall as the mark inside it plus its own padding — 30 + 4 + 4, written out
|
|
177
177
|
rather than as the calc() it comes from, and held to that arithmetic by
|
|
178
178
|
stories/apps/shell-states.test.js.
|
|
179
|
-
|
|
179
|
+
*/
|
|
180
180
|
min-height: 38px;
|
|
181
181
|
padding: var(--space-1) 0;
|
|
182
182
|
border: 0;
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
min-width: 0;
|
|
348
348
|
padding: 7px 10px;
|
|
349
349
|
/* A family alone leaves the browser's leading behind: `font: inherit` first, then
|
|
350
|
-
the one step this row takes.
|
|
350
|
+
the one step this row takes. */
|
|
351
351
|
font: inherit;
|
|
352
352
|
font-size: var(--text-sm);
|
|
353
353
|
text-align: start;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* --single one page of content: the size control alone, no steps
|
|
5
5
|
* The controls are .ui-btn ghost/sm and the size control is a .ui-select, so
|
|
6
6
|
* everything here is layout and the two states those sheets do not own.
|
|
7
|
-
*
|
|
7
|
+
* Keep the status at the start and controls together at the far end.
|
|
8
8
|
* ========================================================================== */
|
|
9
9
|
|
|
10
10
|
.ui-pager {
|
package/src/tokens/tokens.css
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
* vocabulary; the fallbacks keep tokens.css self-sufficient when imported
|
|
87
87
|
* without brand.generated.css. Delays are the kit's own, and the reusable
|
|
88
88
|
* effect layer lives in styles/motion.css.
|
|
89
|
-
*
|
|
89
|
+
* Change generated brand tokens upstream in apliteni/design-system. */
|
|
90
90
|
--ease: var(--easing-ease-in-out, cubic-bezier(0.4, 0, 0.2, 1));
|
|
91
91
|
--ease-out: var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1));
|
|
92
92
|
--ease-in: var(--easing-ease-in, cubic-bezier(0.4, 0, 1, 1));
|