@estiva-app/ui 0.13.1 → 0.15.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 +28 -0
- package/dist/Avatar.d.ts +1 -1
- package/dist/Avatar.d.ts.map +1 -1
- package/dist/AvatarGroup.d.ts.map +1 -1
- package/dist/ChipInput.d.ts +14 -1
- package/dist/ChipInput.d.ts.map +1 -1
- package/dist/Divider.d.ts +5 -2
- package/dist/Divider.d.ts.map +1 -1
- package/dist/EmptyState.d.ts +3 -0
- package/dist/EmptyState.d.ts.map +1 -1
- package/dist/Property.d.ts +7 -0
- package/dist/Property.d.ts.map +1 -1
- package/dist/Reaction.d.ts +12 -6
- package/dist/Reaction.d.ts.map +1 -1
- package/dist/TextInput.d.ts +10 -1
- package/dist/TextInput.d.ts.map +1 -1
- package/dist/Toast.d.ts +19 -6
- package/dist/Toast.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +1 -1
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/eslint/escape.d.ts +61 -0
- package/dist/eslint/escape.d.ts.map +1 -0
- package/dist/eslint/index.d.ts +45 -0
- package/dist/eslint/index.d.ts.map +1 -0
- package/dist/eslint/index.js +118 -0
- package/dist/eslint/index.js.map +7 -0
- package/dist/eslint/no-raw-button.d.ts +11 -0
- package/dist/eslint/no-raw-button.d.ts.map +1 -0
- package/dist/index.js +276 -249
- package/dist/index.js.map +3 -3
- package/package.json +5 -1
- package/src/AttachmentCard.tsx +14 -14
- package/src/Avatar.mdx +7 -2
- package/src/Avatar.picture.test.tsx +60 -0
- package/src/Avatar.stories.tsx +1 -1
- package/src/Avatar.tsx +50 -14
- package/src/AvatarGroup.tsx +1 -0
- package/src/Breadcrumb.tsx +2 -2
- package/src/Checkbox.tsx +1 -1
- package/src/Chip.tsx +1 -1
- package/src/ChipInput.mdx +9 -0
- package/src/ChipInput.stories.tsx +18 -0
- package/src/ChipInput.test.tsx +18 -0
- package/src/ChipInput.tsx +18 -4
- package/src/DialogShell.stories.tsx +1 -1
- package/src/Divider.mdx +3 -2
- package/src/Divider.test.tsx +15 -0
- package/src/Divider.tsx +11 -9
- package/src/EditableText.stories.tsx +2 -2
- package/src/EmptyState.mdx +3 -2
- package/src/EmptyState.stories.tsx +7 -3
- package/src/EmptyState.test.tsx +15 -0
- package/src/EmptyState.tsx +4 -1
- package/src/IconButton.stories.tsx +1 -1
- package/src/Kbd.tsx +3 -3
- package/src/Menu.stories.tsx +2 -2
- package/src/Menu.tsx +3 -3
- package/src/Popover.stories.tsx +1 -1
- package/src/PreviewCard.stories.tsx +5 -5
- package/src/Property.mdx +8 -0
- package/src/Property.stories.tsx +4 -4
- package/src/Property.test.tsx +44 -0
- package/src/Property.tsx +23 -8
- package/src/RailItem.tsx +1 -1
- package/src/Reaction.mdx +14 -3
- package/src/Reaction.test.tsx +62 -0
- package/src/Reaction.tsx +21 -12
- package/src/ReactionPicker.tsx +1 -1
- package/src/SectionLabel.stories.tsx +1 -1
- package/src/SectionLabel.tsx +1 -1
- package/src/Select.test.tsx +20 -3
- package/src/Select.tsx +3 -3
- package/src/TextInput.mdx +6 -0
- package/src/TextInput.stories.tsx +45 -0
- package/src/TextInput.test.tsx +38 -0
- package/src/TextInput.tsx +16 -4
- package/src/Textarea.tsx +1 -1
- package/src/Toast.mdx +28 -4
- package/src/Toast.stories.tsx +12 -1
- package/src/Toast.test.tsx +137 -0
- package/src/Toast.tsx +127 -83
- package/src/Toolbar.tsx +4 -2
- package/src/cn.ts +1 -1
- package/src/eslint/escape.ts +112 -0
- package/src/eslint/index.test.ts +82 -0
- package/src/eslint/index.ts +100 -0
- package/src/eslint/no-raw-button.test.ts +118 -0
- package/src/eslint/no-raw-button.ts +39 -0
- package/stories/Choosing.mdx +1 -0
- package/stories/TokensPage.tsx +6 -1
- package/tailwind-preset.js +7 -0
package/README.md
CHANGED
|
@@ -63,6 +63,34 @@ export default {
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
### The lint rules — `@estiva-app/ui/eslint`
|
|
67
|
+
|
|
68
|
+
The UI Guardrails' rules ship with the package, as an ESLint plugin, so an app
|
|
69
|
+
gets a new rule with a version bump. Each rule names the component to use
|
|
70
|
+
instead. Today there is one: `estiva/no-raw-button`.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// eslint.config.js — alongside your own rules
|
|
74
|
+
import estiva from '@estiva-app/ui/eslint'
|
|
75
|
+
export default [
|
|
76
|
+
// …your config, with a parser that reads TSX
|
|
77
|
+
{ files: ['src/**/*.tsx'], ignores: ['**/*.test.tsx'], ...estiva.configs.recommended },
|
|
78
|
+
]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A place that keeps a raw element on purpose says why, on the line above:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
// @estiva-escape: the reason, at least ten characters
|
|
85
|
+
<button …>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
or, as a JSX child, `{/* @estiva-escape: the reason */}`. Not `eslint-disable`:
|
|
89
|
+
it switches the rule off without saying why, and the gate counts it as a
|
|
90
|
+
failure. `countGates(results)` counts errors and escapes for
|
|
91
|
+
`.gates-count.json`; lint with `settings: { estiva: { reportEscapes: true } }`
|
|
92
|
+
for the escapes to be counted.
|
|
93
|
+
|
|
66
94
|
## The rules
|
|
67
95
|
|
|
68
96
|
### Forking is allowed
|
package/dist/Avatar.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const hueFor: (key: string) => string;
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const initialsFor: (name: string) => string;
|
|
9
9
|
export interface AvatarProps {
|
|
10
|
-
/** The picture URL, resolved by the caller.
|
|
10
|
+
/** The picture URL, resolved by the caller. Until it arrives, and if it fails, the initials show. */
|
|
11
11
|
src?: string;
|
|
12
12
|
/** The person's name — where the initials and the colour come from. */
|
|
13
13
|
name?: string;
|
package/dist/Avatar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../src/Avatar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../src/Avatar.tsx"],"names":[],"mappings":"AA+BA,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,WAIjC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,WAIvC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,qGAAqG;IACrG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uGAAuG;IACvG,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAQ,EAAE,IAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,WAAW,+BAiF/F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../src/AvatarGroup.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,IAAS,EAAE,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../src/AvatarGroup.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,IAAS,EAAE,EAAE,gBAAgB,+BAkCnE"}
|
package/dist/ChipInput.d.ts
CHANGED
|
@@ -16,9 +16,22 @@ export interface InputChipProps {
|
|
|
16
16
|
leading?: ReactNode;
|
|
17
17
|
/** Draws the ✕; absent, the chip is display-only. */
|
|
18
18
|
onRemove?: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* The ✕'s name for a screen reader. `Remove <label>` when not given. A chip
|
|
21
|
+
* whose ✕ does something other than remove it names what it does — a
|
|
22
|
+
* launcher's scope chip leaves the scope ("Leave Ship").
|
|
23
|
+
*/
|
|
24
|
+
removeLabel?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Cut a long label with an ellipsis once the chip is capped, by a
|
|
27
|
+
* `max-w-*` on `className`. Off unless asked: cutting clips up to 4px of a
|
|
28
|
+
* letter's soft edge at 1x even when the label fits (measured 2026-09-15),
|
|
29
|
+
* so a chip that is never capped keeps every pixel.
|
|
30
|
+
*/
|
|
31
|
+
truncate?: boolean;
|
|
19
32
|
className?: string;
|
|
20
33
|
}
|
|
21
|
-
export declare function InputChip({ label, leading, onRemove, className }: InputChipProps): import("react").JSX.Element;
|
|
34
|
+
export declare function InputChip({ label, leading, onRemove, removeLabel, truncate, className }: InputChipProps): import("react").JSX.Element;
|
|
22
35
|
/**
|
|
23
36
|
* A multi-select input: chips for the chosen, a typeahead for the rest —
|
|
24
37
|
* Peek's PersonChipInput (2026-09-01), generalised on the way in, and on Base
|
package/dist/ChipInput.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChipInput.d.ts","sourceRoot":"","sources":["../src/ChipInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAuB7E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,+
|
|
1
|
+
{"version":3,"file":"ChipInput.d.ts","sourceRoot":"","sources":["../src/ChipInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAuB7E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,cAAc,+BAyBvG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACzE,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IAC7B,4CAA4C;IAC5C,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,qDAAqD;IACrD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACtC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAA;IACrC,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,EACrE,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAuB,EACvB,SAAS,EACT,UAAe,EACf,WAAW,EACX,UAAU,EACV,GAAG,IAAI,EACR,EAAE,cAAc,CAAC,CAAC,CAAC,+BAkJnB"}
|
package/dist/Divider.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Peek's Divider (2026-08-28): a hairline in `border-subtle`, inset 12px each
|
|
3
3
|
* side. Plus what Ship added: `orientation="vertical"` — the same hairline
|
|
4
|
-
* standing up, stretching to its row's height, no inset
|
|
5
|
-
*
|
|
4
|
+
* standing up, stretching to its row's height, no inset.
|
|
5
|
+
*
|
|
6
|
+
* On Base UI's `Separator` since stage 6 of the migration (2026-09-14). It
|
|
7
|
+
* writes the `separator` role and `aria-orientation` this component used to
|
|
8
|
+
* write by hand, and adds `data-orientation`. Nothing it draws changed.
|
|
6
9
|
*/
|
|
7
10
|
export interface DividerProps {
|
|
8
11
|
orientation?: 'horizontal' | 'vertical';
|
package/dist/Divider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../src/Divider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../src/Divider.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,WAA0B,EAAE,KAAK,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,YAAY,+BAyBvG"}
|
package/dist/EmptyState.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ import type { ReactNode } from 'react';
|
|
|
12
12
|
* - `section` — one section of a page is empty, and the page has other
|
|
13
13
|
* things on it: the line alone, left-aligned, no icon. A section's
|
|
14
14
|
* emptiness is a line among the page's content, not a stage of its own.
|
|
15
|
+
* It is the quiet line — caption size in the muted colour, as a Field's
|
|
16
|
+
* helper line is — so beside rows it reads as "nothing here", not as one
|
|
17
|
+
* more row (Katerina, 2026-09-15: "not that intense", "smaller font").
|
|
15
18
|
*
|
|
16
19
|
* The message is the caller's — a shared component has no words of its own
|
|
17
20
|
* for what is missing.
|
package/dist/EmptyState.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../src/EmptyState.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC
|
|
1
|
+
{"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../src/EmptyState.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAc,EAAE,SAAS,EAAE,EAAE,eAAe,+BAUvF"}
|
package/dist/Property.d.ts
CHANGED
|
@@ -13,6 +13,13 @@ import type { ReactNode } from 'react';
|
|
|
13
13
|
* is the `menu` token (9px / 115% / 500), uppercase and letter-spaced —
|
|
14
14
|
* its own voice, deliberately not SectionLabel's (unmerged, Katerina
|
|
15
15
|
* 2026-09-01). A literal string, not merged, so the token size survives.
|
|
16
|
+
*
|
|
17
|
+
* **A name and its value, in the markup made for it** (stage 6 of the
|
|
18
|
+
* migration, 2026-09-14). Base UI has no part for this, so it is HTML's own:
|
|
19
|
+
* a `<dl>` holding one `<dt>` (the label) and one `<dd>` (the value), so a
|
|
20
|
+
* screen reader hears the label as the name of what follows rather than as a
|
|
21
|
+
* loose word beside it. Each property is a list of one, which needs no
|
|
22
|
+
* wrapper from the caller.
|
|
16
23
|
*/
|
|
17
24
|
export interface PropertyProps {
|
|
18
25
|
label: string;
|
package/dist/Property.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Property.d.ts","sourceRoot":"","sources":["../src/Property.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC
|
|
1
|
+
{"version":3,"file":"Property.d.ts","sourceRoot":"","sources":["../src/Property.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAUD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAkBrF"}
|
package/dist/Reaction.d.ts
CHANGED
|
@@ -20,8 +20,10 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
|
20
20
|
* 1px hairline against no border at all — which is legible and is not the
|
|
21
21
|
* signal Peek's accent fill gives.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* On Base UI's `Toggle` since stage 6 of the migration (2026-09-14): a native
|
|
24
|
+
* `<button>` with `aria-pressed` and `data-pressed`, so the state reaches
|
|
25
|
+
* assistive tech as a toggle rather than as a colour. Base UI writes both;
|
|
26
|
+
* this component used to write the first by hand.
|
|
25
27
|
*
|
|
26
28
|
* ## `aria-label` is required, and the reason is specific
|
|
27
29
|
*
|
|
@@ -37,12 +39,16 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
|
37
39
|
* `small`, the same 8px horizontal padding, the `chip` type token for the
|
|
38
40
|
* count so it sits at 11px/500 like every other count in the system.
|
|
39
41
|
*/
|
|
40
|
-
export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
|
42
|
+
export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type' | 'value'> {
|
|
41
43
|
/** The emoji, drawn decoratively — name the control with `aria-label`. */
|
|
42
44
|
emoji: ReactNode;
|
|
43
|
-
/** How many people reacted. Drawn as
|
|
45
|
+
/** How many people reacted. Drawn as it is. A count of `0` is not a reaction: do not render one. */
|
|
44
46
|
count: number;
|
|
45
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* You are one of them: the accent tint and edge, and `aria-pressed`. The
|
|
49
|
+
* caller owns it — a press calls `onClick` and the pill keeps showing this
|
|
50
|
+
* value until the caller changes it.
|
|
51
|
+
*/
|
|
46
52
|
pressed?: boolean;
|
|
47
53
|
/**
|
|
48
54
|
* Names the control. **Required** — the emoji is decorative and the count is
|
|
@@ -50,5 +56,5 @@ export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonEleme
|
|
|
50
56
|
*/
|
|
51
57
|
'aria-label': string;
|
|
52
58
|
}
|
|
53
|
-
export declare function Reaction({ emoji, count, pressed, className,
|
|
59
|
+
export declare function Reaction({ emoji, count, pressed, className, ...props }: ReactionProps): import("react").JSX.Element;
|
|
54
60
|
//# sourceMappingURL=Reaction.d.ts.map
|
package/dist/Reaction.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reaction.d.ts","sourceRoot":"","sources":["../src/Reaction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Reaction.d.ts","sourceRoot":"","sources":["../src/Reaction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACjH,0EAA0E;IAC1E,KAAK,EAAE,SAAS,CAAA;IAChB,oGAAoG;IACpG,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAe,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BAuC7F"}
|
package/dist/TextInput.d.ts
CHANGED
|
@@ -12,6 +12,15 @@ import { type InputHTMLAttributes } from 'react';
|
|
|
12
12
|
* context behind it — is gone. Outside a `Field` it behaves as before: the
|
|
13
13
|
* field context has a default, so nothing has to be wrapped.
|
|
14
14
|
*/
|
|
15
|
-
export
|
|
15
|
+
export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
16
|
+
/**
|
|
17
|
+
* `small` is 24px tall with 12px text — the small `Select`'s size, so a
|
|
18
|
+
* field can share a dense row with small selects (D67, 2026-09-13: Peek's
|
|
19
|
+
* reference widget hand-built one because this was 38px). Replaces the
|
|
20
|
+
* native `size` attribute, which counts characters and which no caller
|
|
21
|
+
* used; set a width with the layout instead.
|
|
22
|
+
*/
|
|
23
|
+
size?: 'default' | 'small';
|
|
24
|
+
}
|
|
16
25
|
export declare const TextInput: import("react").ForwardRefExoticComponent<TextInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
17
26
|
//# sourceMappingURL=TextInput.d.ts.map
|
package/dist/TextInput.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;GAYG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"TextInput.d.ts","sourceRoot":"","sources":["../src/TextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACzF;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAC3B;AAED,eAAO,MAAM,SAAS,6GAmBpB,CAAA"}
|
package/dist/Toast.d.ts
CHANGED
|
@@ -8,9 +8,12 @@ import { type ReactNode } from 'react';
|
|
|
8
8
|
* the same dark overlay pill; the type lives in the icon's colour and glow,
|
|
9
9
|
* not the surface.
|
|
10
10
|
*
|
|
11
|
-
* The provider
|
|
12
|
-
* (
|
|
13
|
-
*
|
|
11
|
+
* The provider sits on Base UI's `Toast` since stage 6 of the migration
|
|
12
|
+
* (2026-09-14). Base UI owns the portal, the timers, the stack and what a
|
|
13
|
+
* screen reader hears; this file owns how a toast looks and where the stack
|
|
14
|
+
* stands (bottom-left). **Up to three show at once** (D7): the newest nearest
|
|
15
|
+
* the corner, a fourth hides the oldest until one of the three closes. Before
|
|
16
|
+
* stage 6 a new toast replaced the standing one, and nothing announced either.
|
|
14
17
|
*/
|
|
15
18
|
export type ToastType = 'success' | 'brand' | 'neutral' | 'warning' | 'error';
|
|
16
19
|
export interface ToastProps {
|
|
@@ -33,12 +36,20 @@ export interface ToastProps {
|
|
|
33
36
|
onAction?: () => void;
|
|
34
37
|
className?: string;
|
|
35
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* One toast, drawn in place. What the provider shows is this pill; draw it
|
|
41
|
+
* yourself only where a page needs a toast's look without its timing — a
|
|
42
|
+
* story, a picture of the set.
|
|
43
|
+
*/
|
|
36
44
|
export declare function Toast({ label, type, leadingIcon, actionLabel, onAction, className }: ToastProps): import("react").JSX.Element;
|
|
37
45
|
export interface ToastOptions {
|
|
38
46
|
label: string;
|
|
39
47
|
/** Which of the five this is — it decides the surface and the leading icon. Defaults to 'neutral'. */
|
|
40
48
|
type?: ToastType;
|
|
41
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Renders an action on the right side. Pressing it runs `onAction`, if
|
|
51
|
+
* given, and closes this toast — so a Dismiss needs only its label.
|
|
52
|
+
*/
|
|
42
53
|
actionLabel?: string;
|
|
43
54
|
onAction?: () => void;
|
|
44
55
|
/** Show the leading icon — a check, a `!` or an `×`, per `type`. Defaults to true. */
|
|
@@ -47,8 +58,10 @@ export interface ToastOptions {
|
|
|
47
58
|
durationMs?: number;
|
|
48
59
|
}
|
|
49
60
|
interface ToastValue {
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
/** Shows a toast and returns its id, for `dismissToast`. */
|
|
62
|
+
showToast: (opts: ToastOptions) => string;
|
|
63
|
+
/** Closes the toast with this id; with no id, closes every toast on screen. */
|
|
64
|
+
dismissToast: (id?: string) => void;
|
|
52
65
|
}
|
|
53
66
|
export declare function ToastProvider({ children }: {
|
|
54
67
|
children: ReactNode;
|
package/dist/Toast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAK1E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAgB,EAAE,WAAkB,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,UAAU,+BAelH;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,sGAAsG;IACtG,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,UAAU,UAAU;IAClB,4DAA4D;IAC5D,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,CAAA;IACzC,+EAA+E;IAC/E,YAAY,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAcD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAgClE;AAsCD,wBAAgB,QAAQ,IAAI,UAAU,CAIrC"}
|
package/dist/Toolbar.d.ts
CHANGED
|
@@ -83,7 +83,7 @@ export declare function ToolbarButton({ ref, disabled, disabledReason, ...props
|
|
|
83
83
|
* a `TextInput` dropped in the row.
|
|
84
84
|
*/
|
|
85
85
|
export type ToolbarInputProps = TextInputProps;
|
|
86
|
-
export declare function ToolbarInput(props: ToolbarInputProps): import("react").JSX.Element;
|
|
86
|
+
export declare function ToolbarInput({ size, ...props }: ToolbarInputProps): import("react").JSX.Element;
|
|
87
87
|
/**
|
|
88
88
|
* The hairline between two groups of controls. It is `Divider`'s rule and
|
|
89
89
|
* `Toolbar.Separator`'s role, so a screen reader hears the grouping a sighted
|
package/dist/Toolbar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../src/Toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAE/D,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,SAAS,CAAA;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,WAA0B,EAAE,SAAgB,EAAE,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,+BAwBnJ;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BAqB5F;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../src/Toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAE/D,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,SAAS,CAAA;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,WAA0B,EAAE,SAAgB,EAAE,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,+BAwBnJ;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BAqB5F;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,+BAIjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BASrE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AST, Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* The escape marker (UIG-3, seam S4 in docs/GATES.md §16): one written reason
|
|
4
|
+
* why one element stays as it is.
|
|
5
|
+
*
|
|
6
|
+
* A line comment directly above the element,
|
|
7
|
+
*
|
|
8
|
+
* // @estiva-escape: <reason>
|
|
9
|
+
*
|
|
10
|
+
* or, where the element is a JSX child, the JSX comment on the line above,
|
|
11
|
+
*
|
|
12
|
+
* {/* @estiva-escape: <reason> *\/}
|
|
13
|
+
*
|
|
14
|
+
* with at least ten characters of reason, spaces not counted.
|
|
15
|
+
*
|
|
16
|
+
* Not `eslint-disable`. A directive switches a rule off without saying it was
|
|
17
|
+
* meant, and the count of escapes (`.gates-count.json`) could not see it. A
|
|
18
|
+
* marker written inside a directive that switches one of these rules off is
|
|
19
|
+
* reported for that reason.
|
|
20
|
+
*
|
|
21
|
+
* The token lint's older notes (`eslint-disable-next-line <rule> -- @estiva-escape:
|
|
22
|
+
* <reason>`, Katerina's ruling A2 of 15 September) are not read here: they
|
|
23
|
+
* escape the token lint's rules, which are not this plugin's, and they stay as
|
|
24
|
+
* they are (Katerina, 15 September).
|
|
25
|
+
*/
|
|
26
|
+
export declare const ESCAPE_MARKER = "@estiva-escape";
|
|
27
|
+
/** Characters of reason, spaces not counted. */
|
|
28
|
+
export declare const MIN_REASON = 10;
|
|
29
|
+
/** The key under ESLint's `settings` this plugin reads. */
|
|
30
|
+
export declare const SETTINGS_KEY = "estiva";
|
|
31
|
+
/**
|
|
32
|
+
* Every rule spreads these into its `meta.messages`, because `isEscaped`
|
|
33
|
+
* reports through the rule that called it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ESCAPE_MESSAGES: {
|
|
36
|
+
readonly escapeWithoutReason: "An escape needs its reason, at least 10 characters: `// @estiva-escape: <why this stays>`. An escape with no reason hides nothing.";
|
|
37
|
+
readonly escapeInDirective: "Write the escape as its own comment on the line above: `// @estiva-escape: <reason>`. Inside an eslint-disable comment it switches the rule off instead of recording why.";
|
|
38
|
+
readonly escaped: "Escaped: {{reason}}";
|
|
39
|
+
};
|
|
40
|
+
export interface EstivaSettings {
|
|
41
|
+
/**
|
|
42
|
+
* Report every sanctioned escape as a message with the id `escaped`, so a
|
|
43
|
+
* count can read them. Off in the lint anyone runs; on only in the count.
|
|
44
|
+
*/
|
|
45
|
+
reportEscapes?: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface Located {
|
|
48
|
+
loc?: AST.SourceLocation | null;
|
|
49
|
+
range?: [number, number];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Whether the element at `node` carries a valid escape on the line above.
|
|
53
|
+
*
|
|
54
|
+
* Every rule of this plugin calls it before it reports. It reports, through
|
|
55
|
+
* that rule, a marker with too short a reason and a marker inside an
|
|
56
|
+
* eslint-disable directive for this rule; either way the element is not
|
|
57
|
+
* escaped, so the rule reports it too.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isEscaped(context: Rule.RuleContext, node: Located): boolean;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=escape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/eslint/escape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,mBAAmB,CAAA;AAE7C,gDAAgD;AAChD,eAAO,MAAM,UAAU,KAAK,CAAA;AAE5B,2DAA2D;AAC3D,eAAO,MAAM,YAAY,WAAW,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;CAIlB,CAAA;AAEV,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,UAAU,OAAO;IACf,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,GAAG,IAAI,CAAA;IAC/B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzB;AAaD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAmC3E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ESLint, Linter } from 'eslint';
|
|
2
|
+
export { ESCAPE_MARKER, MIN_REASON, SETTINGS_KEY, isEscaped, type EstivaSettings } from './escape';
|
|
3
|
+
/** The name the configs register the plugin under, so every rule id is `estiva/<rule>`. */
|
|
4
|
+
export declare const PLUGIN_KEY = "estiva";
|
|
5
|
+
declare const plugin: {
|
|
6
|
+
meta: {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
};
|
|
10
|
+
rules: {
|
|
11
|
+
'no-raw-button': import("eslint").Rule.RuleModule;
|
|
12
|
+
};
|
|
13
|
+
configs: {
|
|
14
|
+
recommended: Linter.Config;
|
|
15
|
+
strict: Linter.Config;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export default plugin;
|
|
19
|
+
export interface GateRuleCount {
|
|
20
|
+
errors: number;
|
|
21
|
+
warnings: number;
|
|
22
|
+
escapes: number;
|
|
23
|
+
}
|
|
24
|
+
export interface GateCount {
|
|
25
|
+
/** Per rule id, including rules with nothing to report. */
|
|
26
|
+
rules: Record<string, GateRuleCount>;
|
|
27
|
+
/**
|
|
28
|
+
* Reports of these rules that an `eslint-disable` directive silenced. Not
|
|
29
|
+
* an escape: a gate fails on any of these (docs/GATES.md §16, S4).
|
|
30
|
+
*/
|
|
31
|
+
disabled: {
|
|
32
|
+
filePath: string;
|
|
33
|
+
line: number;
|
|
34
|
+
ruleId: string;
|
|
35
|
+
}[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Count what a lint of these rules found, for `.gates-count.json` (seam S3).
|
|
39
|
+
*
|
|
40
|
+
* Run the lint with `settings: { estiva: { reportEscapes: true } }` for the
|
|
41
|
+
* escapes to be counted; without it they are silent and count 0. A marker
|
|
42
|
+
* with no reason, or inside a directive, counts as an error of its rule.
|
|
43
|
+
*/
|
|
44
|
+
export declare function countGates(results: ESLint.LintResult[]): GateCount;
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAG5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAIlG,2FAA2F;AAC3F,eAAO,MAAM,UAAU,WAAW,CAAA;AAMlC,QAAA,MAAM,MAAM;;;;;;;;aAGK;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAA;KAAE;CAC7C,CAAA;AAqBzB,eAAe,MAAM,CAAA;AAErB,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACpC;;;OAGG;IACH,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC/D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,SAAS,CAgBlE"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// src/eslint/index.ts
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
|
|
4
|
+
// src/eslint/escape.ts
|
|
5
|
+
var ESCAPE_MARKER = "@estiva-escape";
|
|
6
|
+
var MIN_REASON = 10;
|
|
7
|
+
var SETTINGS_KEY = "estiva";
|
|
8
|
+
var ESCAPE_MESSAGES = {
|
|
9
|
+
escapeWithoutReason: `An escape needs its reason, at least ${MIN_REASON} characters: \`// ${ESCAPE_MARKER}: <why this stays>\`. An escape with no reason hides nothing.`,
|
|
10
|
+
escapeInDirective: `Write the escape as its own comment on the line above: \`// ${ESCAPE_MARKER}: <reason>\`. Inside an eslint-disable comment it switches the rule off instead of recording why.`,
|
|
11
|
+
escaped: "Escaped: {{reason}}"
|
|
12
|
+
};
|
|
13
|
+
var DIRECTIVE = /^\s*eslint-disable(?:-next-line|-line)?(?=\s|$)([^]*)$/;
|
|
14
|
+
function directiveRules(rest) {
|
|
15
|
+
const dashes = rest.search(/(?:^|\s)--(?:\s|$)/);
|
|
16
|
+
return (dashes === -1 ? rest : rest.slice(0, dashes)).split(/[\s,]+/).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
var BETWEEN = /^[\s}]*$/;
|
|
19
|
+
function isEscaped(context, node) {
|
|
20
|
+
const { sourceCode } = context;
|
|
21
|
+
if (!node.loc || !node.range) return false;
|
|
22
|
+
const line = node.loc.start.line;
|
|
23
|
+
const nodeStart = node.range[0];
|
|
24
|
+
const comment = sourceCode.getAllComments().find((c) => c.loc && c.range && c.loc.end.line === line - 1 && BETWEEN.test(sourceCode.text.slice(c.range[1], nodeStart)));
|
|
25
|
+
if (!comment?.loc) return false;
|
|
26
|
+
const at = comment.value.indexOf(ESCAPE_MARKER);
|
|
27
|
+
if (at === -1) return false;
|
|
28
|
+
const directive = DIRECTIVE.exec(comment.value);
|
|
29
|
+
if (directive) {
|
|
30
|
+
const rules2 = directiveRules(directive[1]);
|
|
31
|
+
if (rules2.length > 0 && !rules2.includes(context.id)) return false;
|
|
32
|
+
context.report({ loc: comment.loc, messageId: "escapeInDirective" });
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const reason = comment.value.slice(at + ESCAPE_MARKER.length).replace(/^:/, "").trim();
|
|
36
|
+
if (reason.replace(/\s/g, "").length < MIN_REASON) {
|
|
37
|
+
context.report({ loc: comment.loc, messageId: "escapeWithoutReason" });
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const settings = context.settings[SETTINGS_KEY];
|
|
41
|
+
if (settings?.reportEscapes) context.report({ loc: comment.loc, messageId: "escaped", data: { reason } });
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/eslint/no-raw-button.ts
|
|
46
|
+
var noRawButton = {
|
|
47
|
+
meta: {
|
|
48
|
+
type: "problem",
|
|
49
|
+
docs: { description: "A raw <button> where @estiva-app/ui has one" },
|
|
50
|
+
schema: [],
|
|
51
|
+
messages: {
|
|
52
|
+
raw: "Use `Button` from @estiva-app/ui instead of a raw <button>.",
|
|
53
|
+
...ESCAPE_MESSAGES
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
create(context) {
|
|
57
|
+
return {
|
|
58
|
+
// ESLint's types know ESTree's nodes, not JSX's; the parser hands this one over.
|
|
59
|
+
JSXOpeningElement(node) {
|
|
60
|
+
const element = node;
|
|
61
|
+
if (element.name.type !== "JSXIdentifier" || element.name.name !== "button") return;
|
|
62
|
+
if (isEscaped(context, element)) return;
|
|
63
|
+
context.report({ loc: element.loc, messageId: "raw" });
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/eslint/index.ts
|
|
70
|
+
var { version } = createRequire(import.meta.url)("../../package.json");
|
|
71
|
+
var PLUGIN_KEY = "estiva";
|
|
72
|
+
var rules = {
|
|
73
|
+
"no-raw-button": noRawButton
|
|
74
|
+
};
|
|
75
|
+
var plugin = {
|
|
76
|
+
meta: { name: "@estiva-app/ui/eslint", version },
|
|
77
|
+
rules,
|
|
78
|
+
configs: {}
|
|
79
|
+
};
|
|
80
|
+
var ruleIds = Object.keys(rules).map((name) => `${PLUGIN_KEY}/${name}`);
|
|
81
|
+
plugin.configs.recommended = {
|
|
82
|
+
name: "@estiva-app/ui/recommended",
|
|
83
|
+
plugins: { [PLUGIN_KEY]: plugin },
|
|
84
|
+
rules: { [`${PLUGIN_KEY}/no-raw-button`]: "error" }
|
|
85
|
+
};
|
|
86
|
+
plugin.configs.strict = {
|
|
87
|
+
name: "@estiva-app/ui/strict",
|
|
88
|
+
plugins: { [PLUGIN_KEY]: plugin },
|
|
89
|
+
rules: Object.fromEntries(ruleIds.map((id) => [id, "error"]))
|
|
90
|
+
};
|
|
91
|
+
var index_default = plugin;
|
|
92
|
+
function countGates(results) {
|
|
93
|
+
const counts = Object.fromEntries(ruleIds.map((id) => [id, { errors: 0, warnings: 0, escapes: 0 }]));
|
|
94
|
+
const disabled = [];
|
|
95
|
+
for (const result of results) {
|
|
96
|
+
for (const message of result.messages) {
|
|
97
|
+
const count = message.ruleId ? counts[message.ruleId] : void 0;
|
|
98
|
+
if (!count) continue;
|
|
99
|
+
if (message.messageId === "escaped") count.escapes += 1;
|
|
100
|
+
else if (message.severity === 2) count.errors += 1;
|
|
101
|
+
else count.warnings += 1;
|
|
102
|
+
}
|
|
103
|
+
for (const message of result.suppressedMessages ?? []) {
|
|
104
|
+
if (message.ruleId && counts[message.ruleId]) disabled.push({ filePath: result.filePath, line: message.line, ruleId: message.ruleId });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { rules: counts, disabled };
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
ESCAPE_MARKER,
|
|
111
|
+
MIN_REASON,
|
|
112
|
+
PLUGIN_KEY,
|
|
113
|
+
SETTINGS_KEY,
|
|
114
|
+
countGates,
|
|
115
|
+
index_default as default,
|
|
116
|
+
isEscaped
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/eslint/index.ts", "../../src/eslint/escape.ts", "../../src/eslint/no-raw-button.ts"],
|
|
4
|
+
"sourcesContent": ["/// <reference types=\"node\" />\n/**\n * `@estiva-app/ui/eslint` \u2014 the UI Guardrails' lint rules, as a plugin (UIG-3,\n * seam S1 in docs/GATES.md \u00A716).\n *\n * A plugin rather than config, so every app \u2014 Peek, Ship, Leaf \u2014 gets a new\n * rule through an ordinary version bump, and an app's editor hook runs the\n * very rule code its CI runs.\n *\n * import estiva from '@estiva-app/ui/eslint'\n * export default [{ files: ['src/**\\/*.tsx'], ...estiva.configs.recommended }]\n *\n * Register it under `estiva` (the configs do): the rule ids are\n * `estiva/<rule>`, and `countGates` counts those ids.\n *\n * Built on its own by build.mjs, for Node, into `dist/eslint/`; nothing here\n * reaches the components' browser bundle.\n */\nimport { createRequire } from 'node:module'\nimport type { ESLint, Linter } from 'eslint'\nimport { noRawButton } from './no-raw-button'\n\nexport { ESCAPE_MARKER, MIN_REASON, SETTINGS_KEY, isEscaped, type EstivaSettings } from './escape'\n\nconst { version } = createRequire(import.meta.url)('../../package.json') as { version: string }\n\n/** The name the configs register the plugin under, so every rule id is `estiva/<rule>`. */\nexport const PLUGIN_KEY = 'estiva'\n\nconst rules = {\n 'no-raw-button': noRawButton,\n}\n\nconst plugin = {\n meta: { name: '@estiva-app/ui/eslint', version },\n rules,\n configs: {} as { recommended: Linter.Config; strict: Linter.Config },\n} satisfies ESLint.Plugin\n\nconst ruleIds = Object.keys(rules).map((name) => `${PLUGIN_KEY}/${name}`)\n\n/**\n * `recommended` switches every rule on at the level it was ruled at: an error\n * blocks, a warning is reported and never blocks. `strict` makes every rule an\n * error. With one rule, an error, the two are the same today; they part when\n * the first warning-level rule arrives (UIG-25).\n */\nplugin.configs.recommended = {\n name: '@estiva-app/ui/recommended',\n plugins: { [PLUGIN_KEY]: plugin },\n rules: { [`${PLUGIN_KEY}/no-raw-button`]: 'error' },\n}\nplugin.configs.strict = {\n name: '@estiva-app/ui/strict',\n plugins: { [PLUGIN_KEY]: plugin },\n rules: Object.fromEntries(ruleIds.map((id) => [id, 'error'])),\n}\n\nexport default plugin\n\nexport interface GateRuleCount {\n errors: number\n warnings: number\n escapes: number\n}\n\nexport interface GateCount {\n /** Per rule id, including rules with nothing to report. */\n rules: Record<string, GateRuleCount>\n /**\n * Reports of these rules that an `eslint-disable` directive silenced. Not\n * an escape: a gate fails on any of these (docs/GATES.md \u00A716, S4).\n */\n disabled: { filePath: string; line: number; ruleId: string }[]\n}\n\n/**\n * Count what a lint of these rules found, for `.gates-count.json` (seam S3).\n *\n * Run the lint with `settings: { estiva: { reportEscapes: true } }` for the\n * escapes to be counted; without it they are silent and count 0. A marker\n * with no reason, or inside a directive, counts as an error of its rule.\n */\nexport function countGates(results: ESLint.LintResult[]): GateCount {\n const counts: Record<string, GateRuleCount> = Object.fromEntries(ruleIds.map((id) => [id, { errors: 0, warnings: 0, escapes: 0 }]))\n const disabled: GateCount['disabled'] = []\n for (const result of results) {\n for (const message of result.messages) {\n const count = message.ruleId ? counts[message.ruleId] : undefined\n if (!count) continue\n if (message.messageId === 'escaped') count.escapes += 1\n else if (message.severity === 2) count.errors += 1\n else count.warnings += 1\n }\n for (const message of result.suppressedMessages ?? []) {\n if (message.ruleId && counts[message.ruleId]) disabled.push({ filePath: result.filePath, line: message.line, ruleId: message.ruleId })\n }\n }\n return { rules: counts, disabled }\n}\n", "import type { AST, Rule } from 'eslint'\n\n/**\n * The escape marker (UIG-3, seam S4 in docs/GATES.md \u00A716): one written reason\n * why one element stays as it is.\n *\n * A line comment directly above the element,\n *\n * // @estiva-escape: <reason>\n *\n * or, where the element is a JSX child, the JSX comment on the line above,\n *\n * {/* @estiva-escape: <reason> *\\/}\n *\n * with at least ten characters of reason, spaces not counted.\n *\n * Not `eslint-disable`. A directive switches a rule off without saying it was\n * meant, and the count of escapes (`.gates-count.json`) could not see it. A\n * marker written inside a directive that switches one of these rules off is\n * reported for that reason.\n *\n * The token lint's older notes (`eslint-disable-next-line <rule> -- @estiva-escape:\n * <reason>`, Katerina's ruling A2 of 15 September) are not read here: they\n * escape the token lint's rules, which are not this plugin's, and they stay as\n * they are (Katerina, 15 September).\n */\nexport const ESCAPE_MARKER = '@estiva-escape'\n\n/** Characters of reason, spaces not counted. */\nexport const MIN_REASON = 10\n\n/** The key under ESLint's `settings` this plugin reads. */\nexport const SETTINGS_KEY = 'estiva'\n\n/**\n * Every rule spreads these into its `meta.messages`, because `isEscaped`\n * reports through the rule that called it.\n */\nexport const ESCAPE_MESSAGES = {\n escapeWithoutReason: `An escape needs its reason, at least ${MIN_REASON} characters: \\`// ${ESCAPE_MARKER}: <why this stays>\\`. An escape with no reason hides nothing.`,\n escapeInDirective: `Write the escape as its own comment on the line above: \\`// ${ESCAPE_MARKER}: <reason>\\`. Inside an eslint-disable comment it switches the rule off instead of recording why.`,\n escaped: 'Escaped: {{reason}}',\n} as const\n\nexport interface EstivaSettings {\n /**\n * Report every sanctioned escape as a message with the id `escaped`, so a\n * count can read them. Off in the lint anyone runs; on only in the count.\n */\n reportEscapes?: boolean\n}\n\ninterface Located {\n loc?: AST.SourceLocation | null\n range?: [number, number]\n}\n\nconst DIRECTIVE = /^\\s*eslint-disable(?:-next-line|-line)?(?=\\s|$)([^]*)$/\n\n/** The rules a directive names: what comes before its ` -- ` description. None means every rule. */\nfunction directiveRules(rest: string): string[] {\n const dashes = rest.search(/(?:^|\\s)--(?:\\s|$)/)\n return (dashes === -1 ? rest : rest.slice(0, dashes)).split(/[\\s,]+/).filter(Boolean)\n}\n\n/** Only spaces and a JSX expression's closing brace between the marker and the element. */\nconst BETWEEN = /^[\\s}]*$/\n\n/**\n * Whether the element at `node` carries a valid escape on the line above.\n *\n * Every rule of this plugin calls it before it reports. It reports, through\n * that rule, a marker with too short a reason and a marker inside an\n * eslint-disable directive for this rule; either way the element is not\n * escaped, so the rule reports it too.\n */\nexport function isEscaped(context: Rule.RuleContext, node: Located): boolean {\n const { sourceCode } = context\n if (!node.loc || !node.range) return false\n const line = node.loc.start.line\n const nodeStart = node.range[0]\n\n const comment = sourceCode\n .getAllComments()\n .find((c) => c.loc && c.range && c.loc.end.line === line - 1 && BETWEEN.test(sourceCode.text.slice(c.range[1], nodeStart)))\n if (!comment?.loc) return false\n\n const at = comment.value.indexOf(ESCAPE_MARKER)\n if (at === -1) return false\n\n const directive = DIRECTIVE.exec(comment.value)\n if (directive) {\n const rules = directiveRules(directive[1])\n // A directive for other rules (the token lint's notes) is not an escape of this one.\n if (rules.length > 0 && !rules.includes(context.id)) return false\n context.report({ loc: comment.loc, messageId: 'escapeInDirective' })\n return false\n }\n\n const reason = comment.value\n .slice(at + ESCAPE_MARKER.length)\n .replace(/^:/, '')\n .trim()\n if (reason.replace(/\\s/g, '').length < MIN_REASON) {\n context.report({ loc: comment.loc, messageId: 'escapeWithoutReason' })\n return false\n }\n\n const settings = context.settings[SETTINGS_KEY] as EstivaSettings | undefined\n if (settings?.reportEscapes) context.report({ loc: comment.loc, messageId: 'escaped', data: { reason } })\n return true\n}\n", "import type { Rule } from 'eslint'\nimport { ESCAPE_MESSAGES, isEscaped } from './escape'\n\ninterface JSXOpeningElement {\n name: { type: string; name?: string }\n loc: NonNullable<Rule.Node['loc']>\n range: [number, number]\n}\n\n/**\n * A raw `<button>` in an app (UIG-3, the tracer). The package's `Button`,\n * `IconButton`, `MenuItem` and chips are buttons already; an app that writes\n * its own has a look and a behaviour nobody else keeps in step with.\n *\n * Only the JSX element named `button`. `<Button>`, `<Foo.button>` and\n * `createElement('button')` are not this rule's business.\n */\nexport const noRawButton: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: { description: 'A raw <button> where @estiva-app/ui has one' },\n schema: [],\n messages: {\n raw: 'Use `Button` from @estiva-app/ui instead of a raw <button>.',\n ...ESCAPE_MESSAGES,\n },\n },\n create(context) {\n return {\n // ESLint's types know ESTree's nodes, not JSX's; the parser hands this one over.\n JSXOpeningElement(node: Rule.Node) {\n const element = node as unknown as JSXOpeningElement\n if (element.name.type !== 'JSXIdentifier' || element.name.name !== 'button') return\n if (isEscaped(context, element)) return\n context.report({ loc: element.loc, messageId: 'raw' })\n },\n }\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["rules"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* A raw `<button>` in an app (UIG-3, the tracer). The package's `Button`,
|
|
4
|
+
* `IconButton`, `MenuItem` and chips are buttons already; an app that writes
|
|
5
|
+
* its own has a look and a behaviour nobody else keeps in step with.
|
|
6
|
+
*
|
|
7
|
+
* Only the JSX element named `button`. `<Button>`, `<Foo.button>` and
|
|
8
|
+
* `createElement('button')` are not this rule's business.
|
|
9
|
+
*/
|
|
10
|
+
export declare const noRawButton: Rule.RuleModule;
|
|
11
|
+
//# sourceMappingURL=no-raw-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-raw-button.d.ts","sourceRoot":"","sources":["../../src/eslint/no-raw-button.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AASlC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,IAAI,CAAC,UAqB9B,CAAA"}
|