@apliteni/apliteni-ui 0.91.0 → 0.93.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 +5 -7
- package/package.json +1 -1
- package/react/README.md +2 -2
- package/react/dist/index.d.ts +3 -3
- package/react/dist/index.js +79 -111
- package/src/assets/icons.js +1 -1
- package/src/components/account-nav.js +2 -2
- package/src/components/back.js +2 -2
- package/src/components/command-palette.js +2 -2
- package/src/components/drawer.js +1 -1
- package/src/components/dropdown.js +12 -12
- package/src/components/filter-bar.js +2 -2
- package/src/components/footer.js +1 -1
- package/src/components/index.js +1 -1
- package/src/components/loading.js +1 -1
- package/src/components/shell.js +19 -19
- package/src/components/stat.js +3 -3
- package/src/components/success.js +3 -3
- package/src/components/tooltip.js +1 -1
- package/src/components/topbar.js +1 -1
- package/src/html.js +1 -1
- package/src/index.css +1 -1
- package/src/index.d.ts +1 -1
- package/src/logic/accents.js +1 -1
- package/src/logic/filter-bar.js +4 -4
- package/src/styles/back.css +3 -3
- package/src/styles/base.css +3 -3
- package/src/styles/button.css +3 -3
- package/src/styles/callout.css +2 -2
- package/src/styles/card.css +8 -8
- package/src/styles/command-palette.css +14 -14
- package/src/styles/confirm.css +7 -7
- package/src/styles/drawer.css +8 -8
- package/src/styles/dropdown.css +21 -21
- package/src/styles/feedback.css +2 -2
- package/src/styles/field-zoom.css +1 -1
- package/src/styles/file-drop.css +1 -1
- package/src/styles/filter-bar.css +6 -6
- package/src/styles/footer.css +1 -1
- package/src/styles/input.css +6 -6
- package/src/styles/layout.css +32 -32
- package/src/styles/loading.css +1 -1
- package/src/styles/motion.css +1 -1
- package/src/styles/nav.css +4 -4
- package/src/styles/pagination.css +2 -2
- package/src/styles/reduced-motion.css +2 -2
- package/src/styles/segmented.css +3 -3
- package/src/styles/stat.css +3 -3
- package/src/styles/success.css +1 -1
- package/src/styles/table.css +6 -6
- package/src/styles/tap-zone.css +1 -1
- package/src/styles/tooltip.css +3 -3
- package/src/styles/topbar.css +8 -8
- package/src/tokens/accents.css +1 -1
- package/src/tokens/tokens.css +25 -25
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ import { DataTable, Modal } from '@apliteni/apliteni-ui/react';
|
|
|
61
61
|
|
|
62
62
|
The `appShell()` factory provides the complete product layout: the sticky rail, the page body
|
|
63
63
|
and a topbar over it when you ask for one, so no product rebuilds it. The
|
|
64
|
-
[component catalog](docs/
|
|
64
|
+
[component catalog](docs/components.md#component-catalog) lists this factory and every other one.
|
|
65
65
|
Account and personal settings belong in a modal over the product rather than on a page of
|
|
66
66
|
their own — see [Guidelines / Account and settings](guidelines/account-and-settings.md).
|
|
67
67
|
|
|
@@ -100,7 +100,7 @@ only `data-accent` is present, the kit uses that accent with the dark theme. An
|
|
|
100
100
|
`data-theme` does *not* mean “follow the system”: the kit has no `prefers-color-scheme`
|
|
101
101
|
rule. If the host wants to follow the operating system preference, its JavaScript must
|
|
102
102
|
choose the theme and add the attribute. See
|
|
103
|
-
[an absent attribute means dark](docs/
|
|
103
|
+
[an absent attribute means dark](docs/components.md#an-absent-attribute-means-dark).
|
|
104
104
|
|
|
105
105
|
You can change the settings at runtime with `applyTheme('light')` and
|
|
106
106
|
`applyAccent('phoenix')`. These functions save the choices to `localStorage`. You can also
|
|
@@ -121,12 +121,10 @@ section.
|
|
|
121
121
|
|
|
122
122
|
| Page | What it answers |
|
|
123
123
|
| --- | --- |
|
|
124
|
-
| [
|
|
125
|
-
| [
|
|
124
|
+
| [foundations.md](docs/foundations.md) | The tokens and the floors: widths, breakpoints, spacing, type, colour, elevation, the focus ring, the tap floor. |
|
|
125
|
+
| [components.md](docs/components.md) | The package: what to import, theming, the component catalog, and what each component guarantees. |
|
|
126
126
|
| [react/README.md](react/README.md) | The React components, their props, and the Storybook on port 6007. |
|
|
127
|
-
| [
|
|
128
|
-
| [landing-page.md](docs/landing-page.md) | ui.apli.tech: the chrome, the static build, the hosting. |
|
|
129
|
-
| [changelog.md](docs/changelog.md) | The changelog's data model and its Storybook deeplinks. |
|
|
127
|
+
| [contributing.md](docs/contributing.md) | Working on the kit: the repository's layout, Storybook, the guidelines collection, the site, the changelog and releases. |
|
|
130
128
|
|
|
131
129
|
## Contribute
|
|
132
130
|
|
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -246,7 +246,7 @@ this component and compares what is left.
|
|
|
246
246
|
Two things the factory has that this does not, both deliberate. It emits no `data-dropdown`
|
|
247
247
|
on the container, so a page that calls `wireDropdown(document)` cannot adopt a dropdown React
|
|
248
248
|
owns — the same decision `Drawer` makes about `data-drawer`; the row and panel hooks stay,
|
|
249
|
-
because they are the row contract `docs/
|
|
249
|
+
because they are the row contract `docs/components.md` publishes. And it has no `portal: true`: the
|
|
250
250
|
panel is a child of the trigger's container, so a dropdown inside `.ui-app__rail`
|
|
251
251
|
(`position: sticky` with `overflow-y: auto`) still wants the vanilla factory.
|
|
252
252
|
|
|
@@ -541,7 +541,7 @@ An optional `id` lets the caller choose the control ID; it must be unique.
|
|
|
541
541
|
`type="search"` input, and no visible label, so the row keeps the height of the
|
|
542
542
|
unlabelled controls beside it. `ariaLabel` is required and is the control's only
|
|
543
543
|
name. It forwards its ref and the native input props, and `className` joins
|
|
544
|
-
`.ui-input`. There is no clear button — see
|
|
544
|
+
`.ui-input`. There is no clear button — see docs/components.md#react-search-field for why.
|
|
545
545
|
|
|
546
546
|
```tsx
|
|
547
547
|
<div className="ui-toolbar">
|
package/react/dist/index.d.ts
CHANGED
|
@@ -730,7 +730,7 @@ type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'ar
|
|
|
730
730
|
* No clear button. The browser paints its own near-black on the light field and
|
|
731
731
|
* white on the dark one, beside the kit's `--muted` magnifier — two glyphs, and
|
|
732
732
|
* only one answers to a token — so `input.css` suppresses it.
|
|
733
|
-
* why: docs/
|
|
733
|
+
* why: docs/components.md#react-search-field
|
|
734
734
|
*/
|
|
735
735
|
declare const SearchField: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "aria-label"> & {
|
|
736
736
|
/** The control's name. A toolbar search box shows no label, so this is the
|
|
@@ -850,7 +850,7 @@ type SuccessProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'> &
|
|
|
850
850
|
check?: SuccessMark;
|
|
851
851
|
title?: string;
|
|
852
852
|
/** One short line under the title, or nothing. There is no eyebrow tier.
|
|
853
|
-
* why: docs/
|
|
853
|
+
* why: docs/components.md#success-confirmations */
|
|
854
854
|
body?: string;
|
|
855
855
|
/** Removed: a confirmation carries one title and at most one line. Typed as
|
|
856
856
|
* `never` so a caller still passing it is told where it went rather than
|
|
@@ -869,7 +869,7 @@ declare const Success: react.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLD
|
|
|
869
869
|
check?: SuccessMark;
|
|
870
870
|
title?: string;
|
|
871
871
|
/** One short line under the title, or nothing. There is no eyebrow tier.
|
|
872
|
-
* why: docs/
|
|
872
|
+
* why: docs/components.md#success-confirmations */
|
|
873
873
|
body?: string;
|
|
874
874
|
/** Removed: a confirmation carries one title and at most one line. Typed as
|
|
875
875
|
* `never` so a caller still passing it is told where it went rather than
|
package/react/dist/index.js
CHANGED
|
@@ -935,7 +935,7 @@ function rowId(uid, groups, item) {
|
|
|
935
935
|
}
|
|
936
936
|
|
|
937
937
|
// src/DataTable.tsx
|
|
938
|
-
import {
|
|
938
|
+
import { useMemo as useMemo2, useState as useState5 } from "react";
|
|
939
939
|
import { DEFAULT_PAGE_SIZE as DEFAULT_PAGE_SIZE2 } from "@apliteni/apliteni-ui";
|
|
940
940
|
|
|
941
941
|
// src/Pagination.tsx
|
|
@@ -1199,31 +1199,6 @@ function DataTable({
|
|
|
1199
1199
|
hasMore = false
|
|
1200
1200
|
}) {
|
|
1201
1201
|
const scrollable = stickyHeader || pinnedIdentity;
|
|
1202
|
-
const scrollId = useId7();
|
|
1203
|
-
const scrollRegion = useRef9(null);
|
|
1204
|
-
const [columnScroll, setColumnScroll] = useState5({ overflow: false, start: true, end: false });
|
|
1205
|
-
const measureColumns = () => {
|
|
1206
|
-
const region = scrollRegion.current;
|
|
1207
|
-
if (region) setColumnScroll({
|
|
1208
|
-
overflow: region.scrollWidth > region.clientWidth + 1,
|
|
1209
|
-
start: region.scrollLeft <= 1,
|
|
1210
|
-
end: region.scrollLeft + region.clientWidth >= region.scrollWidth - 1
|
|
1211
|
-
});
|
|
1212
|
-
};
|
|
1213
|
-
useEffect5(() => {
|
|
1214
|
-
if (!scrollable) return;
|
|
1215
|
-
const region = scrollRegion.current;
|
|
1216
|
-
if (!region) return;
|
|
1217
|
-
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measureColumns);
|
|
1218
|
-
observer?.observe(region);
|
|
1219
|
-
if (region.firstElementChild) observer?.observe(region.firstElementChild);
|
|
1220
|
-
measureColumns();
|
|
1221
|
-
return () => observer?.disconnect();
|
|
1222
|
-
}, [scrollable, columns, rows]);
|
|
1223
|
-
const scrollColumns = (direction) => {
|
|
1224
|
-
const region = scrollRegion.current;
|
|
1225
|
-
if (region) region.scrollBy({ left: direction * region.clientWidth / 2, behavior: "instant" });
|
|
1226
|
-
};
|
|
1227
1202
|
const [localSort, setLocalSort] = useState5(
|
|
1228
1203
|
{ key: columns.find((c) => c.sortable)?.key, dir: -1 }
|
|
1229
1204
|
);
|
|
@@ -1276,20 +1251,13 @@ function DataTable({
|
|
|
1276
1251
|
);
|
|
1277
1252
|
const pageAllOn = selectable && slice.length > 0 && slice.every((r) => selected.has(r.name));
|
|
1278
1253
|
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
1279
|
-
scrollable && columnScroll.overflow && /* @__PURE__ */ jsxs13("div", { className: "ui-card__row", role: "group", "aria-label": `${scrollLabel} columns`, children: [
|
|
1280
|
-
/* @__PURE__ */ jsx17(Button, { size: "sm", icon: "arrowLeft", "aria-controls": scrollId, disabled: columnScroll.start, onClick: () => scrollColumns(-1), children: "Previous columns" }),
|
|
1281
|
-
/* @__PURE__ */ jsx17(Button, { size: "sm", iconRight: "arrowRight", "aria-controls": scrollId, disabled: columnScroll.end, onClick: () => scrollColumns(1), children: "More columns" })
|
|
1282
|
-
] }),
|
|
1283
1254
|
/* @__PURE__ */ jsx17(
|
|
1284
1255
|
"div",
|
|
1285
1256
|
{
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
role: stickyHeader || pinnedIdentity ? "region" : void 0,
|
|
1291
|
-
"aria-label": stickyHeader || pinnedIdentity ? scrollLabel : void 0,
|
|
1292
|
-
tabIndex: stickyHeader || pinnedIdentity ? 0 : void 0,
|
|
1257
|
+
className: scrollable ? "ui-table-scroll" : void 0,
|
|
1258
|
+
role: scrollable ? "region" : void 0,
|
|
1259
|
+
"aria-label": scrollable ? scrollLabel : void 0,
|
|
1260
|
+
tabIndex: scrollable ? 0 : void 0,
|
|
1293
1261
|
children: /* @__PURE__ */ jsxs13(
|
|
1294
1262
|
"table",
|
|
1295
1263
|
{
|
|
@@ -1442,9 +1410,9 @@ function Denied({
|
|
|
1442
1410
|
// src/DatePicker.tsx
|
|
1443
1411
|
import {
|
|
1444
1412
|
useCallback as useCallback2,
|
|
1445
|
-
useEffect as
|
|
1413
|
+
useEffect as useEffect5,
|
|
1446
1414
|
useMemo as useMemo3,
|
|
1447
|
-
useRef as
|
|
1415
|
+
useRef as useRef9,
|
|
1448
1416
|
useState as useState6
|
|
1449
1417
|
} from "react";
|
|
1450
1418
|
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
@@ -1457,7 +1425,7 @@ var DAY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
|
1457
1425
|
var PHONE_MAX = 560;
|
|
1458
1426
|
function usePhone() {
|
|
1459
1427
|
const [phone, setPhone] = useState6(false);
|
|
1460
|
-
|
|
1428
|
+
useEffect5(() => {
|
|
1461
1429
|
const query = typeof window === "undefined" ? null : window.matchMedia?.(`(max-width: ${PHONE_MAX}px)`);
|
|
1462
1430
|
if (!query) return;
|
|
1463
1431
|
const read = () => setPhone(query.matches);
|
|
@@ -1552,9 +1520,9 @@ function DatePicker({
|
|
|
1552
1520
|
const picksRange = mode === "range" || mode === "day-range";
|
|
1553
1521
|
const phone = usePhone();
|
|
1554
1522
|
const asSheet = sheet ?? phone;
|
|
1555
|
-
const root =
|
|
1556
|
-
const trigger =
|
|
1557
|
-
const grid =
|
|
1523
|
+
const root = useRef9(null);
|
|
1524
|
+
const trigger = useRef9(null);
|
|
1525
|
+
const grid = useRef9(null);
|
|
1558
1526
|
const [selfOpen, setSelfOpen] = useState6(defaultOpen);
|
|
1559
1527
|
const open = openProp ?? selfOpen;
|
|
1560
1528
|
const [selfValue, setSelfValue] = useState6(defaultValue);
|
|
@@ -1591,19 +1559,19 @@ function DatePicker({
|
|
|
1591
1559
|
}, [today, grain]);
|
|
1592
1560
|
const anchor = toIndex(picked, grain) ?? toIndex(picksRange ? span.start ?? span.end : null, grain) ?? todayIndex;
|
|
1593
1561
|
const [cursor, setCursor] = useState6(() => clamp(anchor, lo, hi));
|
|
1594
|
-
const land =
|
|
1562
|
+
const land = useRef9(false);
|
|
1595
1563
|
const setOpen = useCallback2((next) => {
|
|
1596
1564
|
if (openProp === void 0) setSelfOpen(next);
|
|
1597
1565
|
onOpenChange?.(next);
|
|
1598
1566
|
}, [openProp, onOpenChange]);
|
|
1599
|
-
const close =
|
|
1567
|
+
const close = useRef9(setOpen);
|
|
1600
1568
|
useIsoLayoutEffect(() => {
|
|
1601
1569
|
close.current = setOpen;
|
|
1602
1570
|
});
|
|
1603
|
-
|
|
1571
|
+
useEffect5(() => {
|
|
1604
1572
|
if (open) setCursor(clamp(anchor, lo, hi));
|
|
1605
1573
|
}, [open]);
|
|
1606
|
-
|
|
1574
|
+
useEffect5(() => {
|
|
1607
1575
|
if (!open || asSheet) return;
|
|
1608
1576
|
const doc = root.current?.ownerDocument ?? document;
|
|
1609
1577
|
const onClick = (e) => {
|
|
@@ -1913,9 +1881,9 @@ function DatePicker({
|
|
|
1913
1881
|
import {
|
|
1914
1882
|
Fragment as Fragment4,
|
|
1915
1883
|
useCallback as useCallback3,
|
|
1916
|
-
useEffect as
|
|
1917
|
-
useId as
|
|
1918
|
-
useRef as
|
|
1884
|
+
useEffect as useEffect6,
|
|
1885
|
+
useId as useId7,
|
|
1886
|
+
useRef as useRef10,
|
|
1919
1887
|
useState as useState7
|
|
1920
1888
|
} from "react";
|
|
1921
1889
|
import {
|
|
@@ -1987,19 +1955,19 @@ function Dropdown({
|
|
|
1987
1955
|
const [pick, setPick] = useState7(null);
|
|
1988
1956
|
const [query, setQuery] = useState7(() => search && search !== true ? search.query || "" : "");
|
|
1989
1957
|
const [activeKey, setActiveKey] = useState7(null);
|
|
1990
|
-
const root =
|
|
1991
|
-
const panel =
|
|
1992
|
-
const trigger =
|
|
1993
|
-
const field =
|
|
1994
|
-
const landOn =
|
|
1995
|
-
const at =
|
|
1996
|
-
const auto =
|
|
1958
|
+
const root = useRef10(null);
|
|
1959
|
+
const panel = useRef10(null);
|
|
1960
|
+
const trigger = useRef10(null);
|
|
1961
|
+
const field = useRef10(null);
|
|
1962
|
+
const landOn = useRef10(null);
|
|
1963
|
+
const at = useRef10("");
|
|
1964
|
+
const auto = useId7().replace(/:/g, "");
|
|
1997
1965
|
const uid = id ?? auto;
|
|
1998
1966
|
const setOpen = useCallback3((next) => {
|
|
1999
1967
|
if (openProp === void 0) setSelfOpen(next);
|
|
2000
1968
|
onOpenChange?.(next);
|
|
2001
1969
|
}, [openProp, onOpenChange]);
|
|
2002
|
-
const close =
|
|
1970
|
+
const close = useRef10(setOpen);
|
|
2003
1971
|
useIsoLayoutEffect(() => {
|
|
2004
1972
|
close.current = setOpen;
|
|
2005
1973
|
});
|
|
@@ -2052,7 +2020,7 @@ function Dropdown({
|
|
|
2052
2020
|
stop.forEach((off) => off());
|
|
2053
2021
|
};
|
|
2054
2022
|
}, [open]);
|
|
2055
|
-
const opened =
|
|
2023
|
+
const opened = useRef10(false);
|
|
2056
2024
|
useIsoLayoutEffect(() => {
|
|
2057
2025
|
const el = panel.current;
|
|
2058
2026
|
if (open) {
|
|
@@ -2091,7 +2059,7 @@ function Dropdown({
|
|
|
2091
2059
|
const given = rows.find((it) => it.selected);
|
|
2092
2060
|
const givenKey = given ? keyOf(given) : null;
|
|
2093
2061
|
const pickedKey = pick && pick.against === givenKey ? pick.key : null;
|
|
2094
|
-
|
|
2062
|
+
useEffect6(() => {
|
|
2095
2063
|
if (pick && pick.against !== givenKey) setPick(null);
|
|
2096
2064
|
}, [givenKey, pick]);
|
|
2097
2065
|
const picked = pickedKey != null ? rows.find((it) => keyOf(it) === pickedKey) : void 0;
|
|
@@ -2105,7 +2073,7 @@ function Dropdown({
|
|
|
2105
2073
|
const rowIds = /* @__PURE__ */ new Map();
|
|
2106
2074
|
rows.forEach((it, i) => rowIds.set(it, `${uid}-opt-${i}`));
|
|
2107
2075
|
const listId = `${uid}-list`;
|
|
2108
|
-
|
|
2076
|
+
useEffect6(() => {
|
|
2109
2077
|
if (!open) return;
|
|
2110
2078
|
const mine = () => close.current(false);
|
|
2111
2079
|
for (const other of [...openNow]) other();
|
|
@@ -2114,7 +2082,7 @@ function Dropdown({
|
|
|
2114
2082
|
openNow.delete(mine);
|
|
2115
2083
|
};
|
|
2116
2084
|
}, [open]);
|
|
2117
|
-
|
|
2085
|
+
useEffect6(() => {
|
|
2118
2086
|
if (!open) return;
|
|
2119
2087
|
const doc = root.current?.ownerDocument ?? document;
|
|
2120
2088
|
const onClick = (e) => {
|
|
@@ -2495,12 +2463,12 @@ function Segmented({ options, value, onChange, label, appearance = "pill", disab
|
|
|
2495
2463
|
}
|
|
2496
2464
|
|
|
2497
2465
|
// src/FilterBar.tsx
|
|
2498
|
-
import { useRef as
|
|
2466
|
+
import { useRef as useRef11, useState as useState8 } from "react";
|
|
2499
2467
|
import { filterChipText, filterChipName, filterChipUnset, filterChipItems, focusNextStop } from "@apliteni/apliteni-ui";
|
|
2500
2468
|
import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2501
2469
|
function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters", disabled = false, busy = false, onRemove, onClear, onChange }) {
|
|
2502
|
-
const bar =
|
|
2503
|
-
const focused =
|
|
2470
|
+
const bar = useRef11(null);
|
|
2471
|
+
const focused = useRef11(null);
|
|
2504
2472
|
const [opened, setOpened] = useState8({});
|
|
2505
2473
|
const blocked = disabled || busy;
|
|
2506
2474
|
useIsoLayoutEffect(() => {
|
|
@@ -2606,13 +2574,13 @@ function DrawerSection({ title, headingLevel = 3, className, children, ...rest }
|
|
|
2606
2574
|
}
|
|
2607
2575
|
|
|
2608
2576
|
// src/Snippet.tsx
|
|
2609
|
-
import { useEffect as
|
|
2577
|
+
import { useEffect as useEffect7, useRef as useRef12, useState as useState9 } from "react";
|
|
2610
2578
|
import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2611
2579
|
function Snippet({ label = "shell", code = "", children, copy = true, reveal = false, copyLabel = "Copy code" }) {
|
|
2612
2580
|
const [status, setStatus] = useState9("idle");
|
|
2613
|
-
const request =
|
|
2614
|
-
const timer =
|
|
2615
|
-
|
|
2581
|
+
const request = useRef12(0);
|
|
2582
|
+
const timer = useRef12(void 0);
|
|
2583
|
+
useEffect7(() => {
|
|
2616
2584
|
setStatus("idle");
|
|
2617
2585
|
return () => {
|
|
2618
2586
|
request.current++;
|
|
@@ -2655,15 +2623,15 @@ function Snippet({ label = "shell", code = "", children, copy = true, reveal = f
|
|
|
2655
2623
|
}
|
|
2656
2624
|
|
|
2657
2625
|
// src/Tabs.tsx
|
|
2658
|
-
import { useEffect as
|
|
2626
|
+
import { useEffect as useEffect8, useId as useId8, useRef as useRef13 } from "react";
|
|
2659
2627
|
import { playEntrance } from "@apliteni/apliteni-ui/motion";
|
|
2660
2628
|
import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2661
2629
|
function Tabs({ items, value, onChange, label }) {
|
|
2662
|
-
const id =
|
|
2663
|
-
const previous =
|
|
2664
|
-
const activePanel =
|
|
2665
|
-
const buttons =
|
|
2666
|
-
|
|
2630
|
+
const id = useId8();
|
|
2631
|
+
const previous = useRef13(value);
|
|
2632
|
+
const activePanel = useRef13(null);
|
|
2633
|
+
const buttons = useRef13([]);
|
|
2634
|
+
useEffect8(() => {
|
|
2667
2635
|
if (previous.current !== value) playEntrance(activePanel.current);
|
|
2668
2636
|
previous.current = value;
|
|
2669
2637
|
}, [value]);
|
|
@@ -2732,14 +2700,14 @@ function Tabs({ items, value, onChange, label }) {
|
|
|
2732
2700
|
}
|
|
2733
2701
|
|
|
2734
2702
|
// src/Timeline.tsx
|
|
2735
|
-
import { useEffect as
|
|
2703
|
+
import { useEffect as useEffect9, useRef as useRef14 } from "react";
|
|
2736
2704
|
import { playEntrance as playEntrance2, prefersReducedMotion } from "@apliteni/apliteni-ui/motion";
|
|
2737
2705
|
import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2738
2706
|
var kindIcons = { person: "user", rule: "bolt", reversal: "refresh" };
|
|
2739
2707
|
function Timeline({ events, className, ...rest }) {
|
|
2740
|
-
const previousIds =
|
|
2741
|
-
const rows =
|
|
2742
|
-
|
|
2708
|
+
const previousIds = useRef14(new Set(events.map((event) => event.id)));
|
|
2709
|
+
const rows = useRef14(/* @__PURE__ */ new Map());
|
|
2710
|
+
useEffect9(() => {
|
|
2743
2711
|
if (previousIds.current.size > 0 && !prefersReducedMotion()) {
|
|
2744
2712
|
for (const event of events) {
|
|
2745
2713
|
if (previousIds.current.has(event.id)) continue;
|
|
@@ -2782,7 +2750,7 @@ function Timeline({ events, className, ...rest }) {
|
|
|
2782
2750
|
}
|
|
2783
2751
|
|
|
2784
2752
|
// src/Field.tsx
|
|
2785
|
-
import { forwardRef as forwardRef6, useEffect as
|
|
2753
|
+
import { forwardRef as forwardRef6, useEffect as useEffect10, useId as useId9, useRef as useRef15, useState as useState10 } from "react";
|
|
2786
2754
|
import { esc, icon as icon4 } from "@apliteni/apliteni-ui";
|
|
2787
2755
|
import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2788
2756
|
function messages(id, hint, error) {
|
|
@@ -2811,12 +2779,12 @@ function wiring(id, hint, error) {
|
|
|
2811
2779
|
return { id, "aria-describedby": messages(id, hint, error), "aria-invalid": error ? true : void 0 };
|
|
2812
2780
|
}
|
|
2813
2781
|
function Field({ id: suppliedId, label, hint, error, required, children }) {
|
|
2814
|
-
const generatedId =
|
|
2782
|
+
const generatedId = useId9();
|
|
2815
2783
|
const id = suppliedId ?? generatedId;
|
|
2816
2784
|
return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: children({ ...wiring(id, hint, error), required }) });
|
|
2817
2785
|
}
|
|
2818
2786
|
var TextField = forwardRef6(function TextField2({ label, hint, error, required, type = "text", unit, icon: glyph, className = "", ...props }, ref) {
|
|
2819
|
-
const id =
|
|
2787
|
+
const id = useId9();
|
|
2820
2788
|
const hasUnit = type === "number" && !!unit;
|
|
2821
2789
|
const describedBy = [messages(id, hint, error), hasUnit ? `${id}-unit` : ""].filter(Boolean).join(" ") || void 0;
|
|
2822
2790
|
const control = /* @__PURE__ */ jsx29(
|
|
@@ -2843,7 +2811,7 @@ var TextField = forwardRef6(function TextField2({ label, hint, error, required,
|
|
|
2843
2811
|
] }) : control });
|
|
2844
2812
|
});
|
|
2845
2813
|
function TextArea({ label, hint, error, required, className = "", ...props }) {
|
|
2846
|
-
const id =
|
|
2814
|
+
const id = useId9();
|
|
2847
2815
|
return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: /* @__PURE__ */ jsx29(
|
|
2848
2816
|
"textarea",
|
|
2849
2817
|
{
|
|
@@ -2856,7 +2824,7 @@ function TextArea({ label, hint, error, required, className = "", ...props }) {
|
|
|
2856
2824
|
) });
|
|
2857
2825
|
}
|
|
2858
2826
|
function SelectField({ label, hint, error, required, className = "", children, ...props }) {
|
|
2859
|
-
const id =
|
|
2827
|
+
const id = useId9();
|
|
2860
2828
|
return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: /* @__PURE__ */ jsx29(
|
|
2861
2829
|
"select",
|
|
2862
2830
|
{
|
|
@@ -2869,11 +2837,11 @@ function SelectField({ label, hint, error, required, className = "", children, .
|
|
|
2869
2837
|
) });
|
|
2870
2838
|
}
|
|
2871
2839
|
function FileField({ label, hint, error, required, disabled, onFileChange, className = "", ...props }) {
|
|
2872
|
-
const id =
|
|
2873
|
-
const input =
|
|
2840
|
+
const id = useId9();
|
|
2841
|
+
const input = useRef15(null);
|
|
2874
2842
|
const [file, setFile] = useState10(null);
|
|
2875
2843
|
const [dragging, setDragging] = useState10(false);
|
|
2876
|
-
|
|
2844
|
+
useEffect10(() => {
|
|
2877
2845
|
const form = input.current?.form;
|
|
2878
2846
|
const reset = () => {
|
|
2879
2847
|
setFile(null);
|
|
@@ -2932,7 +2900,7 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
|
|
|
2932
2900
|
}
|
|
2933
2901
|
|
|
2934
2902
|
// src/FileDrop.tsx
|
|
2935
|
-
import { useRef as
|
|
2903
|
+
import { useRef as useRef16, useState as useState11 } from "react";
|
|
2936
2904
|
import { Fragment as Fragment8, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2937
2905
|
var MARKS = {
|
|
2938
2906
|
uploading: "clock",
|
|
@@ -2966,7 +2934,7 @@ function FileDrop({
|
|
|
2966
2934
|
children,
|
|
2967
2935
|
className
|
|
2968
2936
|
}) {
|
|
2969
|
-
const input =
|
|
2937
|
+
const input = useRef16(null);
|
|
2970
2938
|
const [over, setOver] = useState11(false);
|
|
2971
2939
|
const status = file?.status ?? "uploading";
|
|
2972
2940
|
const word = status === "error" ? file?.error ?? WORDS.error : file?.state ?? WORDS[status];
|
|
@@ -3101,7 +3069,7 @@ var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...p
|
|
|
3101
3069
|
});
|
|
3102
3070
|
|
|
3103
3071
|
// src/Toast.tsx
|
|
3104
|
-
import { createContext as createContext2, useCallback as useCallback4, useContext as useContext2, useEffect as
|
|
3072
|
+
import { createContext as createContext2, useCallback as useCallback4, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as useRef17, useState as useState12 } from "react";
|
|
3105
3073
|
import { createPortal as createPortal4 } from "react-dom";
|
|
3106
3074
|
import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3107
3075
|
var Context = createContext2(null);
|
|
@@ -3112,10 +3080,10 @@ function useToast() {
|
|
|
3112
3080
|
return push;
|
|
3113
3081
|
}
|
|
3114
3082
|
function Notice({ notice, remove }) {
|
|
3115
|
-
const root =
|
|
3083
|
+
const root = useRef17(null);
|
|
3116
3084
|
const [leaving2, setLeaving] = useState12(false);
|
|
3117
|
-
const dismissed =
|
|
3118
|
-
const remaining =
|
|
3085
|
+
const dismissed = useRef17(false);
|
|
3086
|
+
const remaining = useRef17(5e3);
|
|
3119
3087
|
const [hovered, setHovered] = useState12(false);
|
|
3120
3088
|
const [focused, setFocused] = useState12(false);
|
|
3121
3089
|
const paused = hovered || focused;
|
|
@@ -3126,7 +3094,7 @@ function Notice({ notice, remove }) {
|
|
|
3126
3094
|
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) remove();
|
|
3127
3095
|
else setLeaving(true);
|
|
3128
3096
|
}, [remove]);
|
|
3129
|
-
|
|
3097
|
+
useEffect11(() => {
|
|
3130
3098
|
if (leaving2) {
|
|
3131
3099
|
const element = root.current;
|
|
3132
3100
|
const finish = (event) => {
|
|
@@ -3140,7 +3108,7 @@ function Notice({ notice, remove }) {
|
|
|
3140
3108
|
};
|
|
3141
3109
|
}
|
|
3142
3110
|
}, [leaving2, remove]);
|
|
3143
|
-
|
|
3111
|
+
useEffect11(() => {
|
|
3144
3112
|
if (leaving2 || notice.action || paused) return;
|
|
3145
3113
|
const started = Date.now();
|
|
3146
3114
|
const timer = setTimeout(dismiss, remaining.current);
|
|
@@ -3218,8 +3186,8 @@ function usePublishedReach(stack2, count) {
|
|
|
3218
3186
|
}
|
|
3219
3187
|
function Toast({ children }) {
|
|
3220
3188
|
const [entries, setEntries] = useState12([]);
|
|
3221
|
-
const nextId =
|
|
3222
|
-
const stack2 =
|
|
3189
|
+
const nextId = useRef17(0);
|
|
3190
|
+
const stack2 = useRef17(null);
|
|
3223
3191
|
const push = useCallback4((notice) => {
|
|
3224
3192
|
const id = nextId.current++;
|
|
3225
3193
|
const remove = () => setEntries((current) => current.filter((entry) => entry.id !== id));
|
|
@@ -3236,22 +3204,22 @@ function Toast({ children }) {
|
|
|
3236
3204
|
}
|
|
3237
3205
|
|
|
3238
3206
|
// src/AppShell.tsx
|
|
3239
|
-
import { useEffect as
|
|
3207
|
+
import { useEffect as useEffect13, useId as useId11, useState as useState14 } from "react";
|
|
3240
3208
|
import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
|
|
3241
3209
|
|
|
3242
3210
|
// src/SidebarNav.tsx
|
|
3243
|
-
import { forwardRef as forwardRef8, useEffect as
|
|
3211
|
+
import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId10, useState as useState13 } from "react";
|
|
3244
3212
|
import { ENTRANCE_FALLBACK_MS } from "@apliteni/apliteni-ui/motion";
|
|
3245
3213
|
import { Fragment as Fragment9, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3246
3214
|
var badgeText = (badge) => typeof badge === "object" ? badge.text : badge;
|
|
3247
3215
|
function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
|
|
3248
|
-
const uid =
|
|
3216
|
+
const uid = useId10();
|
|
3249
3217
|
const group = !sub && !!item.items?.length;
|
|
3250
3218
|
const childActive = group && item.items.some((child) => child.id === active);
|
|
3251
3219
|
const [expanded, setExpanded] = useState13();
|
|
3252
3220
|
const open = expanded ?? item.defaultOpen ?? childActive;
|
|
3253
3221
|
const [entering, setEntering] = useState13(false);
|
|
3254
|
-
|
|
3222
|
+
useEffect12(() => {
|
|
3255
3223
|
if (!entering) return;
|
|
3256
3224
|
const timer = setTimeout(() => setEntering(false), ENTRANCE_FALLBACK_MS);
|
|
3257
3225
|
return () => clearTimeout(timer);
|
|
@@ -3302,7 +3270,7 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
|
|
|
3302
3270
|
return /* @__PURE__ */ jsx33("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx33("a", { ...props }) });
|
|
3303
3271
|
}
|
|
3304
3272
|
function Section({ section, ...props }) {
|
|
3305
|
-
const uid =
|
|
3273
|
+
const uid = useId10();
|
|
3306
3274
|
return /* @__PURE__ */ jsxs28("div", { className: "ui-nav__section", children: [
|
|
3307
3275
|
section.label && /* @__PURE__ */ jsx33("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
|
|
3308
3276
|
/* @__PURE__ */ jsx33("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx33(Row, { item, ...props }, item.id)) })
|
|
@@ -3358,21 +3326,21 @@ function AppShell({
|
|
|
3358
3326
|
palette,
|
|
3359
3327
|
renderLink
|
|
3360
3328
|
}) {
|
|
3361
|
-
const uid =
|
|
3329
|
+
const uid = useId11();
|
|
3362
3330
|
const [collapsed, setCollapsed] = useState14(defaultCollapsed);
|
|
3363
3331
|
const [search, setSearch] = useState14(false);
|
|
3364
3332
|
const [more, setMore] = useState14(false);
|
|
3365
3333
|
const [theme, setTheme] = useState14("dark");
|
|
3366
3334
|
const current = path(pathname);
|
|
3367
3335
|
const active = sections.filter((section) => current === path(section.href) || path(section.href) !== "/" && current.startsWith(`${path(section.href)}/`)).sort((a, b) => path(b.href).length - path(a.href).length)[0];
|
|
3368
|
-
|
|
3336
|
+
useEffect13(() => {
|
|
3369
3337
|
setCollapsed(railCollapsed() ?? defaultCollapsed);
|
|
3370
3338
|
}, [defaultCollapsed]);
|
|
3371
|
-
|
|
3339
|
+
useEffect13(() => {
|
|
3372
3340
|
setMore(false);
|
|
3373
3341
|
setSearch(false);
|
|
3374
3342
|
}, [pathname]);
|
|
3375
|
-
|
|
3343
|
+
useEffect13(() => {
|
|
3376
3344
|
const root = document.documentElement;
|
|
3377
3345
|
const sync2 = () => setTheme(root.getAttribute("data-theme") === "light" ? "light" : "dark");
|
|
3378
3346
|
sync2();
|
|
@@ -3380,7 +3348,7 @@ function AppShell({
|
|
|
3380
3348
|
observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
|
|
3381
3349
|
return () => observer.disconnect();
|
|
3382
3350
|
}, []);
|
|
3383
|
-
|
|
3351
|
+
useEffect13(() => {
|
|
3384
3352
|
const hotkey = (event) => {
|
|
3385
3353
|
if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== "k") return;
|
|
3386
3354
|
const target = event.target;
|
|
@@ -3607,7 +3575,7 @@ function ThemeToggle({ labelled = false }) {
|
|
|
3607
3575
|
}
|
|
3608
3576
|
|
|
3609
3577
|
// src/Success.tsx
|
|
3610
|
-
import { forwardRef as forwardRef9, useEffect as
|
|
3578
|
+
import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as useRef18, useState as useState15 } from "react";
|
|
3611
3579
|
import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3612
3580
|
var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
|
|
3613
3581
|
const circled = variant === "circled";
|
|
@@ -3655,11 +3623,11 @@ var pieces = [
|
|
|
3655
3623
|
];
|
|
3656
3624
|
function Countdown({ seconds, label = "Redirecting", onDone }) {
|
|
3657
3625
|
const [remaining, setRemaining] = useState15(seconds);
|
|
3658
|
-
const callback =
|
|
3659
|
-
|
|
3626
|
+
const callback = useRef18(onDone);
|
|
3627
|
+
useEffect14(() => {
|
|
3660
3628
|
callback.current = onDone;
|
|
3661
3629
|
}, [onDone]);
|
|
3662
|
-
|
|
3630
|
+
useEffect14(() => {
|
|
3663
3631
|
let count = seconds;
|
|
3664
3632
|
const timer = setInterval(() => {
|
|
3665
3633
|
count -= 1;
|
package/src/assets/icons.js
CHANGED
|
@@ -166,7 +166,7 @@ export const iconNames = Object.keys(ICONS);
|
|
|
166
166
|
// `filter` — Lucide's funnel — fails the first test. Depicting a thing is not the
|
|
167
167
|
// fault; `copy` is on this list and depicts one too. The kit reads the funnel alone
|
|
168
168
|
// as not saying whether the control filters, sorts or exports — its own inference
|
|
169
|
-
// from the sources in docs/
|
|
169
|
+
// from the sources in docs/foundations.md, not a measured recognition finding.
|
|
170
170
|
// See #565.
|
|
171
171
|
export const iconOnlyAllowed = {
|
|
172
172
|
x: 'close or dismiss',
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// that interpolates raw. It lives here rather than in shell.js because both
|
|
3
3
|
// shell.js and topbar.js need it and shell.js already imports topbar.js — the
|
|
4
4
|
// other direction would be a cycle. shell.js re-exports ACCOUNT_NAV, which is
|
|
5
|
-
// the published name docs/
|
|
6
|
-
// why: docs/
|
|
5
|
+
// the published name docs/components.md documents.
|
|
6
|
+
// why: docs/components.md#the-page-shell
|
|
7
7
|
import { esc } from './index.js';
|
|
8
8
|
|
|
9
9
|
// nav.js item objects. Labels are raw text — every nav primitive escapes, so a
|
package/src/components/back.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// docs/reviews/270-back-control.html and the owner chose this one, so the other
|
|
12
12
|
// three — a bordered button above the title, an icon-only arrow beside it, and
|
|
13
13
|
// the breadcrumb trail alone — are not built here.
|
|
14
|
-
// why: docs/
|
|
14
|
+
// why: docs/components.md#the-back-link
|
|
15
15
|
import { esc, icon } from './index.js';
|
|
16
16
|
|
|
17
17
|
// "Back" names a direction rather than a place. It is what a caller who names no
|
|
@@ -29,7 +29,7 @@ const TAB_OR_NEWLINE = /[\t\n\r]/g;
|
|
|
29
29
|
|
|
30
30
|
// A label that already says "Back to Invoices" names the place after those words, or the
|
|
31
31
|
// link would be read as "Back to Back to Invoices". The whole phrase, since "Backups" and
|
|
32
|
-
// "Back office" are places too. why: docs/
|
|
32
|
+
// "Back office" are places too. why: docs/components.md#the-back-link
|
|
33
33
|
const SAID = /^back\s+to(?:\s+|$)/i;
|
|
34
34
|
|
|
35
35
|
const text = (v) => (typeof v === 'string' || typeof v === 'number' ? String(v).trim() : '');
|
|
@@ -9,7 +9,7 @@ export { SCORE, scoreCommand, rankCommands, rankGroups, paletteHotkey } from '..
|
|
|
9
9
|
// The kit ships the shell and the ranking and names no result kinds; a row goes
|
|
10
10
|
// somewhere, runs something, or asks a confirm first. Inertness, Escape and the
|
|
11
11
|
// focus trap come from ./overlay.js, the stack the drawer and the confirm share.
|
|
12
|
-
// why: docs/
|
|
12
|
+
// why: docs/components.md#the-command-palette
|
|
13
13
|
import { esc, icon } from './index.js';
|
|
14
14
|
import { OVERLAY_LAYER, adoptOverlay, popOverlay, pushOverlay, returnFocus, syncOverlays } from './overlay.js';
|
|
15
15
|
import { safeUrl } from '../html.js';
|
|
@@ -61,7 +61,7 @@ function keysFor(shortcut) {
|
|
|
61
61
|
* the wiring, which is also what makes Cmd+Enter open it in a tab.
|
|
62
62
|
*
|
|
63
63
|
* A destructive item that names no confirm is rendered DISABLED. The kit's rule
|
|
64
|
-
* is that a delete asks first (docs/
|
|
64
|
+
* is that a delete asks first (docs/components.md#the-command-palette), and a
|
|
65
65
|
* palette is the one surface where a reader is typing fast and choosing from a
|
|
66
66
|
* list that reorders under them.
|
|
67
67
|
*/
|
package/src/components/drawer.js
CHANGED
|
@@ -75,7 +75,7 @@ export function drawer({
|
|
|
75
75
|
*
|
|
76
76
|
* drawer({ title, body: drawerSection({ title: 'Source', rows: [['Statement', '#4102']] }) })
|
|
77
77
|
*
|
|
78
|
-
* why: docs/
|
|
78
|
+
* why: docs/components.md#the-drawer
|
|
79
79
|
*
|
|
80
80
|
* @param {object} [o]
|
|
81
81
|
* @param {string} [o.title] group heading (escaped)
|