@hideyukimori/nene2-ui 0.16.1 → 0.17.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 +13 -6
- package/dist/data/DataTable.d.ts +19 -13
- package/dist/data/DataTable.js +11 -2
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/DetailList.d.ts +17 -5
- package/dist/data/DetailList.js +8 -2
- package/dist/data/DetailList.js.map +1 -1
- package/dist/data/Pagination.d.ts +24 -0
- package/dist/data/Pagination.js +14 -3
- package/dist/data/Pagination.js.map +1 -1
- package/dist/feedback/Badge.d.ts +13 -3
- package/dist/feedback/Badge.js +5 -2
- package/dist/feedback/Badge.js.map +1 -1
- package/dist/forms/FormField.d.ts +3 -1
- package/dist/forms/FormField.js +3 -2
- package/dist/forms/FormField.js.map +1 -1
- package/package.json +1 -1
- package/themes/default.css +27 -0
package/README.md
CHANGED
|
@@ -258,11 +258,17 @@ wrap every choice in a `<div>` to say them.
|
|
|
258
258
|
label, so the kit carries it). That was right and it left the reason in place. **Fixing what
|
|
259
259
|
a report names is not the same as fixing what it is about.**
|
|
260
260
|
|
|
261
|
+
Until 0.17.0 seven components accepted no `className` at all. Four of them now do — `Badge`,
|
|
262
|
+
`FormField`, `DataTable`, `Pagination` — because a caller could not add a margin or a width
|
|
263
|
+
without wrapping them. **Three still do not, on purpose**: `Modal`, `ConfirmDialog` and
|
|
264
|
+
`ToastProvider` are overlays and a provider; a class landed on a `<dialog>` from outside can
|
|
265
|
+
undo the margin and top-layer assumptions the kit relies on (0.16.1 is the case in point).
|
|
266
|
+
|
|
261
267
|
### 🔴 A palette that is missing a meaning
|
|
262
268
|
|
|
263
|
-
The kit's theme defines **
|
|
269
|
+
The kit's theme defines **14 of the 28 colours** in the frozen Core Token Contract v1. That
|
|
264
270
|
is a legal theme — the contract says what a name means, not that every name must be used —
|
|
265
|
-
but it stops being harmless the moment a component needs one of the
|
|
271
|
+
but it stops being harmless the moment a component needs one of the fourteen.
|
|
266
272
|
|
|
267
273
|
`warn` was one. Six ships define `--color-warn`; nene-payout, the product this theme was
|
|
268
274
|
promoted from, does not. So `--color-x-slot-alert-warn-*` pointed at `--color-danger` and a
|
|
@@ -273,10 +279,11 @@ listening and no one looking**.
|
|
|
273
279
|
resolve to one value cannot express the distinction it is named for.** Both are now checked
|
|
274
280
|
— radius by step count, colour by whether two meanings resolve to the same value.
|
|
275
281
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
`
|
|
279
|
-
`
|
|
282
|
+
`success` / `success-soft` / `on-success` and `info` / `info-soft` / `on-info` were added in
|
|
283
|
+
0.17.0 because `Badge` needed them (#422) — defined when a component needs them, not all at
|
|
284
|
+
once. Still undefined: `surface-overlay`, `surface-sunken`, `text-faint`, `text-inverse`,
|
|
285
|
+
`border-strong`, `accent-hover`, `accent-soft`, `on-danger`, `focus-ring`, `scrim`. Two of
|
|
286
|
+
those — `focus-ring` and `scrim` — already have component slots pointing at substitutes.
|
|
280
287
|
|
|
281
288
|
### 🔴 Two validity states, and only one of them is invalid
|
|
282
289
|
|
package/dist/data/DataTable.d.ts
CHANGED
|
@@ -16,17 +16,23 @@ export interface DataTableProps<Row> {
|
|
|
16
16
|
rowKey: (row: Row) => string;
|
|
17
17
|
/** Localized description of what the table contains. Becomes the table's caption. */
|
|
18
18
|
caption: string;
|
|
19
|
+
/** Composed after the kit's own classes (design principle 2). Lands on the `<table>`. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Below `sm`, draw each row as a card: every cell becomes a block headed by its column's
|
|
23
|
+
* name. Off by default — a table that changes shape at a breakpoint is a decision, the
|
|
24
|
+
* same rule as `Modal`'s `sheetOnMobile`.
|
|
25
|
+
*
|
|
26
|
+
* 🔴 Four ships already do this by hand (`<td data-label>` + `content: attr(data-label)`;
|
|
27
|
+
* invoice / profile / records / vault, measured 2026-08-25), which is why it is here and
|
|
28
|
+
* not left to them (#423). The kit does it with utilities, every one carrying the
|
|
29
|
+
* `max-sm:` prefix, so a wide viewport is untouched.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ The visual header row is hidden (`sr-only`), not removed: the `<th scope="col">` stay
|
|
32
|
+
* in the document, so assistive tech still pairs a cell with its column. The `::before`
|
|
33
|
+
* label is presentational and is not read on its own. That the two do not double up is a
|
|
34
|
+
* live-lane check — jsdom does not lay out, and it is not asserted here.
|
|
35
|
+
*/
|
|
36
|
+
collapse?: 'sm';
|
|
19
37
|
}
|
|
20
|
-
|
|
21
|
-
* A tabular list.
|
|
22
|
-
*
|
|
23
|
-
* 🔴 Every heading carries `scope="col"`. Without it a screen reader cannot pair a cell with
|
|
24
|
-
* its column, so each cell is read as a bare value — and nothing about the rendered table
|
|
25
|
-
* looks any different, which is why three ships each shipped a table and the `scope` is the
|
|
26
|
-
* detail most likely to differ between them.
|
|
27
|
-
*
|
|
28
|
-
* 🔴 The caption is required, not optional. A visually obvious table ("it's clearly the
|
|
29
|
-
* invoice list") is not obvious to someone arriving at it by keyboard from elsewhere on the
|
|
30
|
-
* page. It is visually hidden, not absent.
|
|
31
|
-
*/
|
|
32
|
-
export declare function DataTable<Row>({ columns, rows, rowKey, caption }: DataTableProps<Row>): import("react").JSX.Element;
|
|
38
|
+
export declare function DataTable<Row>({ columns, rows, rowKey, caption, className, collapse, }: DataTableProps<Row>): import("react").JSX.Element;
|
package/dist/data/DataTable.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../lib/cx.js';
|
|
2
3
|
/**
|
|
3
4
|
* A tabular list.
|
|
4
5
|
*
|
|
@@ -11,7 +12,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
12
|
* invoice list") is not obvious to someone arriving at it by keyboard from elsewhere on the
|
|
12
13
|
* page. It is visually hidden, not absent.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
// Every class here carries `max-sm:`. Only structure — the label's weight and colour are
|
|
16
|
+
// the header's own slots, read through `before:`.
|
|
17
|
+
const CARD_TABLE = 'max-sm:block';
|
|
18
|
+
const CARD_THEAD = 'max-sm:sr-only';
|
|
19
|
+
const CARD_TBODY = 'max-sm:block';
|
|
20
|
+
const CARD_ROW = 'max-sm:block max-sm:border-b max-sm:border-x-slot-table-border';
|
|
21
|
+
const CARD_CELL = 'max-sm:block max-sm:border-b-0 max-sm:text-left max-sm:before:block max-sm:before:content-[attr(data-label)] max-sm:before:font-x-slot-table-header max-sm:before:text-x-slot-table-header-fg';
|
|
22
|
+
export function DataTable({ columns, rows, rowKey, caption, className, collapse, }) {
|
|
23
|
+
const cards = collapse === 'sm';
|
|
24
|
+
return (_jsxs("table", { className: cx('w-full border-collapse font-sans text-x-slot-table-fg', cards && CARD_TABLE, className), children: [_jsx("caption", { className: "sr-only", children: caption }), _jsx("thead", { className: cards ? CARD_THEAD : undefined, children: _jsx("tr", { children: columns.map((col) => (_jsx("th", { scope: "col", className: `border-b border-x-slot-table-border px-x-slot-table-cell-pad-x py-x-slot-table-cell-pad-y font-x-slot-table-header text-x-slot-table-header-fg ${col.align === 'end' ? 'text-right' : 'text-left'}`, children: col.header }, col.key))) }) }), _jsx("tbody", { className: cards ? CARD_TBODY : undefined, children: rows.map((row) => (_jsx("tr", { className: cards ? CARD_ROW : undefined, children: columns.map((col) => (_jsx("td", { ...(cards ? { 'data-label': col.header } : {}), className: cx('border-b border-x-slot-table-border px-x-slot-table-cell-pad-x py-x-slot-table-cell-pad-y', col.align === 'end' ? 'text-right' : 'text-left', cards && CARD_CELL), children: col.cell(row) }, col.key))) }, rowKey(row)))) })] }));
|
|
16
25
|
}
|
|
17
26
|
//# sourceMappingURL=DataTable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","sourceRoot":"","sources":["../../src/data/DataTable.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"DataTable.js","sourceRoot":"","sources":["../../src/data/DataTable.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAwClC;;;;;;;;;;;GAWG;AACH,yFAAyF;AACzF,kDAAkD;AAClD,MAAM,UAAU,GAAG,cAAc,CAAC;AAClC,MAAM,UAAU,GAAG,gBAAgB,CAAC;AACpC,MAAM,UAAU,GAAG,cAAc,CAAC;AAClC,MAAM,QAAQ,GAAG,gEAAgE,CAAC;AAClF,MAAM,SAAS,GACb,+LAA+L,CAAC;AAElM,MAAM,UAAU,SAAS,CAAM,EAC7B,OAAO,EACP,IAAI,EACJ,MAAM,EACN,OAAO,EACP,SAAS,EACT,QAAQ,GACY;IACpB,MAAM,KAAK,GAAG,QAAQ,KAAK,IAAI,CAAC;IAChC,OAAO,CACL,iBACE,SAAS,EAAE,EAAE,CACX,uDAAuD,EACvD,KAAK,IAAI,UAAU,EACnB,SAAS,CACV,aAED,kBAAS,SAAS,EAAC,SAAS,YAAE,OAAO,GAAW,EAChD,gBAAO,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,YAC9C,uBACG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,aAEE,KAAK,EAAC,KAAK,EACX,SAAS,EAAE,kJACT,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WACvC,EAAE,YAED,GAAG,CAAC,MAAM,IANN,GAAG,CAAC,GAAG,CAOT,CACN,CAAC,GACC,GACC,EACR,gBAAO,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,YAC7C,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACjB,aAAsB,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAC1D,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,gBAEM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAC/C,SAAS,EAAE,EAAE,CACX,2FAA2F,EAC3F,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAChD,KAAK,IAAI,SAAS,CACnB,YAEA,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IART,GAAG,CAAC,GAAG,CAST,CACN,CAAC,IAbK,MAAM,CAAC,GAAG,CAAC,CAcf,CACN,CAAC,GACI,IACF,CACT,CAAC;AACJ,CAAC"}
|
|
@@ -8,9 +8,21 @@ export interface DetailListProps {
|
|
|
8
8
|
/** Composed after the kit's own classes (design principle 2). */
|
|
9
9
|
className?: string;
|
|
10
10
|
rows: DetailRow[];
|
|
11
|
+
/**
|
|
12
|
+
* `stack` (default) is one row per line — what the component always drew. `columns` lays
|
|
13
|
+
* the rows out in two columns on a wide viewport and **falls back to one column below
|
|
14
|
+
* `md` on its own**, so a caller never has to say so (nene-vault's own `<dl>` carries
|
|
15
|
+
* `max-md:grid-cols-1`, and so do the other four ships' — measured 2026-08-25, #391).
|
|
16
|
+
*
|
|
17
|
+
* 🔴 A choice between two arrangements, not a column count. Five ships write a two-column
|
|
18
|
+
* detail block and none writes three; a `columns: number` would be a value the kit
|
|
19
|
+
* invented (design principle 3). `rows[].span` was considered and rejected for the same
|
|
20
|
+
* reason: it brings a column count into every row (#424).
|
|
21
|
+
*
|
|
22
|
+
* Only structure lives here. The term's weight and colour are slots
|
|
23
|
+
* (`--font-weight-x-slot-detail-term`, `--color-x-slot-detail-term-fg`) and reach a
|
|
24
|
+
* product through the theme, not through a prop.
|
|
25
|
+
*/
|
|
26
|
+
layout?: 'stack' | 'columns';
|
|
11
27
|
}
|
|
12
|
-
|
|
13
|
-
* Read-only key/value display for detail screens. Uses a description list so the
|
|
14
|
-
* label/value relationship is conveyed to assistive technology.
|
|
15
|
-
*/
|
|
16
|
-
export declare function DetailList({ rows, className }: DetailListProps): import("react").JSX.Element;
|
|
28
|
+
export declare function DetailList({ rows, className, layout }: DetailListProps): import("react").JSX.Element;
|
package/dist/data/DetailList.js
CHANGED
|
@@ -4,7 +4,13 @@ import { cx } from '../lib/cx.js';
|
|
|
4
4
|
* Read-only key/value display for detail screens. Uses a description list so the
|
|
5
5
|
* label/value relationship is conveyed to assistive technology.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const LAYOUT_CLASS = {
|
|
8
|
+
stack: 'flex flex-col',
|
|
9
|
+
// One gap slot for both axes — the fleet's two-column blocks use one value, and a second
|
|
10
|
+
// slot would be a step nobody asked for.
|
|
11
|
+
columns: 'grid grid-cols-2 max-md:grid-cols-1',
|
|
12
|
+
};
|
|
13
|
+
export function DetailList({ rows, className, layout = 'stack' }) {
|
|
14
|
+
return (_jsx("dl", { className: cx(LAYOUT_CLASS[layout], 'gap-x-slot-detail-gap', className), children: rows.map((row) => (_jsxs("div", { className: "flex flex-col gap-x-slot-detail-row-gap border-b border-x-slot-detail-border py-x-slot-detail-row-pad-y", children: [_jsx("dt", { className: "font-sans font-x-slot-detail-term text-x-slot-detail-term-fg", children: row.label }), _jsx("dd", { className: "font-sans text-x-slot-detail-description-fg", children: row.value })] }, row.label))) }));
|
|
9
15
|
}
|
|
10
16
|
//# sourceMappingURL=DetailList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DetailList.js","sourceRoot":"","sources":["../../src/data/DetailList.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"DetailList.js","sourceRoot":"","sources":["../../src/data/DetailList.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AA8BlC;;;GAGG;AACH,MAAM,YAAY,GAA2D;IAC3E,KAAK,EAAE,eAAe;IACtB,yFAAyF;IACzF,yCAAyC;IACzC,OAAO,EAAE,qCAAqC;CAC/C,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,EAAmB;IAC/E,OAAO,CACL,aAAI,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,SAAS,CAAC,YACxE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACjB,eAEE,SAAS,EAAC,yGAAyG,aAEnH,aAAI,SAAS,EAAC,8DAA8D,YACzE,GAAG,CAAC,KAAK,GACP,EACL,aAAI,SAAS,EAAC,6CAA6C,YAAE,GAAG,CAAC,KAAK,GAAM,KANvE,GAAG,CAAC,KAAK,CAOV,CACP,CAAC,GACC,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -12,6 +12,30 @@ interface PaginationBase {
|
|
|
12
12
|
* component that only knew about pages could not produce the sentence the product needs.
|
|
13
13
|
*/
|
|
14
14
|
status: string;
|
|
15
|
+
/** Composed after the kit's own classes (design principle 2). Lands on the `<nav>`. */
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Passed to both buttons. `sm` for a dense list footer. Omitted, the buttons keep the
|
|
19
|
+
* size they had — which is what every caller got before 0.17.0.
|
|
20
|
+
*/
|
|
21
|
+
size?: 'md' | 'sm';
|
|
22
|
+
/**
|
|
23
|
+
* On a narrow viewport, stack the three parts vertically. Off by default: the same rule as
|
|
24
|
+
* `Modal`'s `sheetOnMobile` — a component that changes shape at a breakpoint is a decision.
|
|
25
|
+
*/
|
|
26
|
+
stackOnMobile?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Where the status sentence sits. `center` (default) is between the two buttons — what the
|
|
29
|
+
* component always drew. `start` puts it before both, `end` after both.
|
|
30
|
+
*
|
|
31
|
+
* 🔴 Not a free layout. Three positions is the whole space a row of two buttons and one
|
|
32
|
+
* sentence has, which makes it structure (design principle 3), not a value.
|
|
33
|
+
*
|
|
34
|
+
* There is deliberately no "hide when empty" prop: the kit cannot know a list is empty (the
|
|
35
|
+
* offset model carries no total), and `{total > 0 && <Pagination … />}` says it in the one
|
|
36
|
+
* place that does know. A prop would only move that line and pretend the kit decided.
|
|
37
|
+
*/
|
|
38
|
+
statusPlacement?: 'start' | 'center' | 'end';
|
|
15
39
|
}
|
|
16
40
|
interface PagePagination extends PaginationBase {
|
|
17
41
|
/** 1-based. */
|
package/dist/data/Pagination.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from '../primitives/Button.js';
|
|
3
3
|
import { Stack } from '../layout/Stack.js';
|
|
4
|
+
import { cx } from '../lib/cx.js';
|
|
4
5
|
/**
|
|
5
6
|
* Page-by-page navigation for a list.
|
|
6
7
|
*
|
|
@@ -22,11 +23,21 @@ import { Stack } from '../layout/Stack.js';
|
|
|
22
23
|
* the row jump, and moves the next-page button under the cursor that just clicked it.
|
|
23
24
|
*/
|
|
24
25
|
export function Pagination(props) {
|
|
25
|
-
const { label, previousLabel, nextLabel, status } = props;
|
|
26
|
+
const { label, previousLabel, nextLabel, status, className, size = 'md', stackOnMobile, statusPlacement = 'center', } = props;
|
|
26
27
|
const atStart = props.page === undefined ? !props.canPrev : props.page <= 1;
|
|
27
28
|
const atEnd = props.page === undefined ? !props.canNext : props.page >= props.pageCount;
|
|
28
29
|
const goPrev = () => props.page === undefined ? props.onPrev() : props.onPageChange(props.page - 1);
|
|
29
30
|
const goNext = () => props.page === undefined ? props.onNext() : props.onPageChange(props.page + 1);
|
|
30
|
-
|
|
31
|
+
const prev = (_jsx(Button, { variant: "secondary", size: size, disabled: atStart, onClick: goPrev, "aria-label": previousLabel, children: previousLabel }, "prev"));
|
|
32
|
+
const current = (_jsx("span", { "aria-current": "page", className: "font-sans text-x-slot-pagination-fg", children: status }, "status"));
|
|
33
|
+
const next = (_jsx(Button, { variant: "secondary", size: size, disabled: atEnd, onClick: goNext, "aria-label": nextLabel, children: nextLabel }, "next"));
|
|
34
|
+
const order = statusPlacement === 'start'
|
|
35
|
+
? [current, prev, next]
|
|
36
|
+
: statusPlacement === 'end'
|
|
37
|
+
? [prev, next, current]
|
|
38
|
+
: [prev, current, next];
|
|
39
|
+
return (_jsx("nav", { "aria-label": label, className: className, children: _jsx(Stack, { direction: "horizontal", gap: "2xs", align: "center",
|
|
40
|
+
// Every class carries the `max-sm:` prefix, so a wide viewport is untouched.
|
|
41
|
+
className: cx(stackOnMobile === true && 'max-sm:flex-col max-sm:items-stretch'), children: order }) }));
|
|
31
42
|
}
|
|
32
43
|
//# sourceMappingURL=Pagination.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.js","sourceRoot":"","sources":["../../src/data/Pagination.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Pagination.js","sourceRoot":"","sources":["../../src/data/Pagination.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAiElC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,EACJ,KAAK,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,SAAS,EACT,IAAI,GAAG,IAAI,EACX,aAAa,EACb,eAAe,GAAG,QAAQ,GAC3B,GAAG,KAAK,CAAC;IAEV,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC;IAExF,MAAM,MAAM,GAAG,GAAG,EAAE,CAClB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,GAAG,EAAE,CAClB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAEjF,MAAM,IAAI,GAAG,CACX,KAAC,MAAM,IAEL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,MAAM,gBACH,aAAa,YAExB,aAAa,IAPV,MAAM,CAQH,CACV,CAAC;IACF,MAAM,OAAO,GAAG,CACd,+BAAgC,MAAM,EAAC,SAAS,EAAC,qCAAqC,YACnF,MAAM,IADC,QAAQ,CAEX,CACR,CAAC;IACF,MAAM,IAAI,GAAG,CACX,KAAC,MAAM,IAEL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,KAAK,EACf,OAAO,EAAE,MAAM,gBACH,SAAS,YAEpB,SAAS,IAPN,MAAM,CAQH,CACV,CAAC;IACF,MAAM,KAAK,GACT,eAAe,KAAK,OAAO;QACzB,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;QACvB,CAAC,CAAC,eAAe,KAAK,KAAK;YACzB,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC;YACvB,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAE9B,OAAO,CACL,4BAAiB,KAAK,EAAE,SAAS,EAAE,SAAS,YAC1C,KAAC,KAAK,IACJ,SAAS,EAAC,YAAY,EACtB,GAAG,EAAC,KAAK,EACT,KAAK,EAAC,QAAQ;YACd,6EAA6E;YAC7E,SAAS,EAAE,EAAE,CAAC,aAAa,KAAK,IAAI,IAAI,sCAAsC,CAAC,YAE9E,KAAK,GACA,GACJ,CACP,CAAC;AACJ,CAAC"}
|
package/dist/feedback/Badge.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
export interface BadgeProps {
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* What the badge means, not what colour it is.
|
|
5
|
+
*
|
|
6
|
+
* 🔴 `success` / `warn` / `info` — not `ok` / `warning`. Five ships carry a success and a
|
|
7
|
+
* warning tone in their own badge and they do not agree on the words (clear and invoice
|
|
8
|
+
* say `ok`, origin says `warning`); the contract bans both synonyms, and `InlineAlert`
|
|
9
|
+
* already says `warn`. One vocabulary across the kit, so a tone means the same thing in a
|
|
10
|
+
* badge as it does in an alert (0.17.0, #422).
|
|
11
|
+
*/
|
|
12
|
+
tone?: 'neutral' | 'accent' | 'danger' | 'success' | 'warn' | 'info';
|
|
13
|
+
/** Composed after the kit's own classes (design principle 2). */
|
|
14
|
+
className?: string;
|
|
5
15
|
children: ReactNode;
|
|
6
16
|
}
|
|
7
17
|
/**
|
|
@@ -11,4 +21,4 @@ export interface BadgeProps {
|
|
|
11
21
|
* `<Badge color="red">` becomes a lie the moment the theme changes, and there is no way to
|
|
12
22
|
* find every such lie afterwards except by reading every screen.
|
|
13
23
|
*/
|
|
14
|
-
export declare function Badge({ tone, children }: BadgeProps): import("react").JSX.Element;
|
|
24
|
+
export declare function Badge({ tone, className, children }: BadgeProps): import("react").JSX.Element;
|
package/dist/feedback/Badge.js
CHANGED
|
@@ -4,6 +4,9 @@ const TONE_CLASS = {
|
|
|
4
4
|
neutral: 'bg-x-slot-badge-neutral-bg text-x-slot-badge-neutral-fg border-x-slot-badge-neutral-border',
|
|
5
5
|
accent: 'bg-x-slot-badge-accent-bg text-x-slot-badge-accent-fg border-x-slot-badge-accent-border',
|
|
6
6
|
danger: 'bg-x-slot-badge-danger-bg text-x-slot-badge-danger-fg border-x-slot-badge-danger-border',
|
|
7
|
+
success: 'bg-x-slot-badge-success-bg text-x-slot-badge-success-fg border-x-slot-badge-success-border',
|
|
8
|
+
warn: 'bg-x-slot-badge-warn-bg text-x-slot-badge-warn-fg border-x-slot-badge-warn-border',
|
|
9
|
+
info: 'bg-x-slot-badge-info-bg text-x-slot-badge-info-fg border-x-slot-badge-info-border',
|
|
7
10
|
};
|
|
8
11
|
/**
|
|
9
12
|
* A small status marker.
|
|
@@ -12,7 +15,7 @@ const TONE_CLASS = {
|
|
|
12
15
|
* `<Badge color="red">` becomes a lie the moment the theme changes, and there is no way to
|
|
13
16
|
* find every such lie afterwards except by reading every screen.
|
|
14
17
|
*/
|
|
15
|
-
export function Badge({ tone = 'neutral', children }) {
|
|
16
|
-
return (_jsx("span", { className: cx('inline-flex items-center rounded-x-slot-badge border px-x-slot-badge-pad-x py-x-slot-badge-pad-y font-sans', TONE_CLASS[tone]), children: children }));
|
|
18
|
+
export function Badge({ tone = 'neutral', className, children }) {
|
|
19
|
+
return (_jsx("span", { className: cx('inline-flex items-center rounded-x-slot-badge border px-x-slot-badge-pad-x py-x-slot-badge-pad-y font-sans', TONE_CLASS[tone], className), children: children }));
|
|
17
20
|
}
|
|
18
21
|
//# sourceMappingURL=Badge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../src/feedback/Badge.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../src/feedback/Badge.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAkBlC,MAAM,UAAU,GAAoD;IAClE,OAAO,EACL,4FAA4F;IAC9F,MAAM,EAAE,yFAAyF;IACjG,MAAM,EAAE,yFAAyF;IACjG,OAAO,EACL,4FAA4F;IAC9F,IAAI,EAAE,mFAAmF;IACzF,IAAI,EAAE,mFAAmF;CAC1F,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAc;IACzE,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,4GAA4G,EAC5G,UAAU,CAAC,IAAI,CAAC,EAChB,SAAS,CACV,YAEA,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -37,6 +37,8 @@ export interface FormFieldProps {
|
|
|
37
37
|
* Folding the two together would mean the kit picking "*" for every locale.
|
|
38
38
|
*/
|
|
39
39
|
requiredMarker?: ReactNode;
|
|
40
|
+
/** Composed after the kit's own classes (design principle 2). Lands on the outer `<div>`. */
|
|
41
|
+
className?: string;
|
|
40
42
|
children: ReactNode;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
@@ -54,4 +56,4 @@ export interface FormFieldProps {
|
|
|
54
56
|
* 🔴 The hint stays visible when there is an error. nene-profile hides it in that case, and
|
|
55
57
|
* that is the moment the user most needs it; the error is read first instead.
|
|
56
58
|
*/
|
|
57
|
-
export declare function FormField({ id, label, error, warning, hint, labelAdornment, required, requiredMarker, children, }: FormFieldProps): import("react").JSX.Element;
|
|
59
|
+
export declare function FormField({ id, label, error, warning, hint, labelAdornment, required, requiredMarker, className, children, }: FormFieldProps): import("react").JSX.Element;
|
package/dist/forms/FormField.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { FieldContext } from './field-context.js';
|
|
4
|
+
import { cx } from '../lib/cx.js';
|
|
4
5
|
/**
|
|
5
6
|
* Labelled form field wrapper: associates a `<label>` with its control, and links the
|
|
6
7
|
* control to its help text and its validation error.
|
|
@@ -16,12 +17,12 @@ import { FieldContext } from './field-context.js';
|
|
|
16
17
|
* 🔴 The hint stays visible when there is an error. nene-profile hides it in that case, and
|
|
17
18
|
* that is the moment the user most needs it; the error is read first instead.
|
|
18
19
|
*/
|
|
19
|
-
export function FormField({ id, label, error = null, warning = null, hint, labelAdornment, required = false, requiredMarker, children, }) {
|
|
20
|
+
export function FormField({ id, label, error = null, warning = null, hint, labelAdornment, required = false, requiredMarker, className, children, }) {
|
|
20
21
|
const errorId = error === null ? null : `${id}-error`;
|
|
21
22
|
const warningId = warning === null ? null : `${id}-warning`;
|
|
22
23
|
const hintId = hint === undefined || hint === null ? null : `${id}-hint`;
|
|
23
24
|
const value = useMemo(() => ({ id, errorId, warningId, hintId, required }), [id, errorId, warningId, hintId, required]);
|
|
24
|
-
return (_jsx(FieldContext.Provider, { value: value, children: _jsxs("div", { className:
|
|
25
|
+
return (_jsx(FieldContext.Provider, { value: value, children: _jsxs("div", { className: cx('flex flex-col gap-x-slot-field-gap', className), children: [_jsxs("label", { htmlFor: id, className: "font-sans font-x-slot-field-label text-x-slot-field-label-size text-x-slot-field-label", children: [label, required && requiredMarker !== undefined && requiredMarker !== null ? (_jsx("span", { className: "text-x-slot-field-required-marker", children: requiredMarker })) : null, labelAdornment] }), children, hintId === null ? null : (_jsx("span", { id: hintId, className: "font-sans text-x-slot-field-hint-size text-x-slot-field-hint", children: hint })), warningId === null ? null : (
|
|
25
26
|
// role="status" ではなく role="alert" にすると、入力の途中で読み上げを割り込む。
|
|
26
27
|
// 警告は「見直す価値がある」であって「止まれ」ではないので、次の切れ目で読ませる。
|
|
27
28
|
_jsx("p", { id: warningId, role: "status", className: "font-sans text-x-slot-field-error-size text-x-slot-field-warning-fg", children: warning })), errorId === null ? null : (_jsx("p", { id: errorId, role: "alert", className: "font-sans text-x-slot-field-error-size text-x-slot-field-error", children: error }))] }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.js","sourceRoot":"","sources":["../../src/forms/FormField.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"FormField.js","sourceRoot":"","sources":["../../src/forms/FormField.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AA6ClC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,EAAE,EACF,KAAK,EACL,KAAK,GAAG,IAAI,EACZ,OAAO,GAAG,IAAI,EACd,IAAI,EACJ,cAAc,EACd,QAAQ,GAAG,KAAK,EAChB,cAAc,EACd,SAAS,EACT,QAAQ,GACO;IACf,MAAM,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC;IACtD,MAAM,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC;IAC5D,MAAM,MAAM,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC;IACzE,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EACpD,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAC3C,CAAC;IAEF,OAAO,CACL,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACjC,eAAK,SAAS,EAAE,EAAE,CAAC,oCAAoC,EAAE,SAAS,CAAC,aACjE,iBACE,OAAO,EAAE,EAAE,EACX,SAAS,EAAC,wFAAwF,aAEjG,KAAK,EACL,QAAQ,IAAI,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,CACrE,eAAM,SAAS,EAAC,mCAAmC,YAAE,cAAc,GAAQ,CAC5E,CAAC,CAAC,CAAC,IAAI,EACP,cAAc,IACT,EACP,QAAQ,EACR,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,eACE,EAAE,EAAE,MAAM,EACV,SAAS,EAAC,8DAA8D,YAEvE,IAAI,GACA,CACR,EACA,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC3B,wDAAwD;gBACxD,2CAA2C;gBAC3C,YACE,EAAE,EAAE,SAAS,EACb,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,qEAAqE,YAE9E,OAAO,GACN,CACL,EACA,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACzB,YACE,EAAE,EAAE,OAAO,EACX,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,gEAAgE,YAEzE,KAAK,GACJ,CACL,IACG,GACgB,CACzB,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hideyukimori/nene2-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "NeNe fleet shared React UI kit — token-driven primitives on Tailwind v4 @theme. Components carry no design of their own; themes do.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/themes/default.css
CHANGED
|
@@ -43,6 +43,22 @@
|
|
|
43
43
|
--color-on-warn: oklch(21% 0.04 75);
|
|
44
44
|
--color-danger-soft: oklch(95% 0.03 30);
|
|
45
45
|
|
|
46
|
+
/* 🔴 Six more contract colours, added in 0.17.0 for `Badge` (#422). Five ships carry a
|
|
47
|
+
* success tone and a warning tone in their own badge (clear / field / invoice / origin /
|
|
48
|
+
* vault) and four carry `info`, so a kit badge with three tones could not say what their
|
|
49
|
+
* screens already say. Hues follow the contract's reference theme; lightness is measured:
|
|
50
|
+
* white on this `success` is 5.35:1 and on this `info` 5.78:1 (both clear 4.5:1 for text),
|
|
51
|
+
* the fills are 5.51:1 / 5.95:1 against `surface-raised`, and `text-primary` on either
|
|
52
|
+
* soft is ≥ 13.8:1 — computed with the same OKLCH→sRGB pipeline that reproduces the
|
|
53
|
+
* `warn` figures above (3.48 / 3.58 / 4.97), so the calculator has a positive control.
|
|
54
|
+
* Fourteen contract colours are still undefined — see the README. */
|
|
55
|
+
--color-success: oklch(50% 0.14 155);
|
|
56
|
+
--color-success-soft: oklch(95% 0.03 155);
|
|
57
|
+
--color-on-success: oklch(99% 0 0);
|
|
58
|
+
--color-info: oklch(50% 0.12 245);
|
|
59
|
+
--color-info-soft: oklch(95% 0.03 245);
|
|
60
|
+
--color-on-info: oklch(99% 0 0);
|
|
61
|
+
|
|
46
62
|
/* The shadow scale. Three steps, chosen the way the spacing and radius scales were —
|
|
47
63
|
* measured from what the fleet already ships, not invented here (#386).
|
|
48
64
|
*
|
|
@@ -303,6 +319,17 @@
|
|
|
303
319
|
--color-x-slot-badge-danger-bg: var(--color-danger);
|
|
304
320
|
--color-x-slot-badge-danger-fg: var(--color-on-accent);
|
|
305
321
|
--color-x-slot-badge-danger-border: var(--color-danger);
|
|
322
|
+
/* 0.17.0 (#422). Filled, like `accent` and `danger`: a status badge is read at a glance
|
|
323
|
+
* and an outlined success next to a filled danger would weigh the two unequally. */
|
|
324
|
+
--color-x-slot-badge-success-bg: var(--color-success);
|
|
325
|
+
--color-x-slot-badge-success-fg: var(--color-on-success);
|
|
326
|
+
--color-x-slot-badge-success-border: var(--color-success);
|
|
327
|
+
--color-x-slot-badge-warn-bg: var(--color-warn);
|
|
328
|
+
--color-x-slot-badge-warn-fg: var(--color-on-warn);
|
|
329
|
+
--color-x-slot-badge-warn-border: var(--color-warn);
|
|
330
|
+
--color-x-slot-badge-info-bg: var(--color-info);
|
|
331
|
+
--color-x-slot-badge-info-fg: var(--color-on-info);
|
|
332
|
+
--color-x-slot-badge-info-border: var(--color-info);
|
|
306
333
|
|
|
307
334
|
--color-x-slot-toast-bg: var(--color-surface-raised);
|
|
308
335
|
--color-x-slot-toast-fg: var(--color-text-primary);
|