@omkarux/vela 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/components/Button/Button.d.ts +32 -0
- package/dist/components/Button/Button.js +51 -0
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/ContextualAlert/ContextualAlert.d.ts +18 -0
- package/dist/components/ContextualAlert/ContextualAlert.js +49 -0
- package/dist/components/ContextualAlert/index.d.ts +2 -0
- package/dist/components/Input/Input.d.ts +12 -0
- package/dist/components/Input/Input.js +31 -0
- package/dist/components/Input/index.d.ts +2 -0
- package/dist/components/StatusIndicator/StatusIndicator.d.ts +14 -0
- package/dist/components/StatusIndicator/StatusIndicator.js +31 -0
- package/dist/components/StatusIndicator/index.d.ts +2 -0
- package/dist/components/Tabs/Tabs.d.ts +44 -0
- package/dist/components/Tabs/Tabs.js +94 -0
- package/dist/components/Tabs/index.d.ts +2 -0
- package/dist/components/Toggle/Toggle.d.ts +23 -0
- package/dist/components/Toggle/Toggle.js +55 -0
- package/dist/components/Toggle/index.d.ts +2 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +16 -0
- package/dist/lib/cn.d.ts +5 -0
- package/dist/lib/cn.js +6 -0
- package/dist/lib/icons.d.ts +7 -0
- package/dist/lib/icons.js +32 -0
- package/dist/tokens.css +610 -0
- package/dist/vela.css +897 -0
- package/guidelines/components/button.md +104 -0
- package/guidelines/components/contextual-alert.md +59 -0
- package/guidelines/components/input.md +65 -0
- package/guidelines/components/status-indicator.md +48 -0
- package/guidelines/components/tabs.md +71 -0
- package/guidelines/components/toggle.md +53 -0
- package/guidelines/foundations/color.md +67 -0
- package/guidelines/foundations/radius.md +17 -0
- package/guidelines/foundations/sizing.md +35 -0
- package/guidelines/foundations/spacing.md +28 -0
- package/guidelines/foundations/typography.md +34 -0
- package/guidelines/llms.txt +33 -0
- package/guidelines/overview.md +48 -0
- package/guidelines/setup.md +64 -0
- package/guidelines/tokens.md +69 -0
- package/package.json +84 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.2.0] — 2026-09-09
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **Re-hued the entire primitive palette** — 52 raw values replaced. Vela now runs a deep
|
|
10
|
+
sea-teal brand (`--vela-primary-500: #0a6c7d`) on a cool slate neutral ramp.
|
|
11
|
+
|
|
12
|
+
Nothing else moved. Not one semantic mapping, not one component file, not one test. That is
|
|
13
|
+
the property the two-layer architecture exists to provide, and this release is the proof:
|
|
14
|
+
a complete visual re-skin is a diff confined to the primitive block.
|
|
15
|
+
|
|
16
|
+
All 76 contrast pairs passed on the first run of the new palette in both themes.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Corrected the asserted-pair count in the docs from 78 to 76 (38 pairs × 2 themes).
|
|
20
|
+
|
|
21
|
+
### Note on versioning
|
|
22
|
+
Every colour in the system changed, so this is a minor bump rather than a patch even though no
|
|
23
|
+
API changed — under 0.x, a visual break is still a break. Consumers pinned to `0.1.x` keep the
|
|
24
|
+
old palette.
|
|
25
|
+
|
|
26
|
+
## [0.1.0] — 2026-09-09
|
|
27
|
+
|
|
28
|
+
Initial release.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **Components:** Button, Toggle, Input, ContextualAlert, StatusIndicator, Tabs.
|
|
32
|
+
- **Tokens:** 239 CSS custom properties in two layers (primitives → semantics), covering
|
|
33
|
+
colour, spacing, sizing, radius and typography.
|
|
34
|
+
- **Themes:** light and dark. The OS preference applies by default; `data-theme="light"` or
|
|
35
|
+
`data-theme="dark"` on the root element overrides it in either direction.
|
|
36
|
+
- **Taxonomies:** severity (6 levels), risk (3 levels) and status (5 states) as separate,
|
|
37
|
+
non-interchangeable token families, each with a complete role row.
|
|
38
|
+
- **Type-enforced constraints:** `ButtonProps` is a discriminated union; `Toggle` requires a
|
|
39
|
+
`label`; `Tabs.List` requires an `aria-label`; every variant/size/severity/status set is
|
|
40
|
+
closed.
|
|
41
|
+
- **`guidelines/`** shipped inside the package — per-component specifications with closed
|
|
42
|
+
prop tables, token bindings, hard constraints and anti-patterns, plus `llms.txt` as an
|
|
43
|
+
agent entry point.
|
|
44
|
+
- **Contrast audit** (`npm run contrast`) covering 76 pairs across both themes, backed by
|
|
45
|
+
tests so a regression fails CI.
|
|
46
|
+
- **Publish gate** (`npm run verify`) that packs the tarball, installs it into a throwaway
|
|
47
|
+
app, and asserts both that valid usage typechecks and that eight documented rule
|
|
48
|
+
violations are rejected by the compiler.
|
|
49
|
+
|
|
50
|
+
### Accessibility decisions worth recording
|
|
51
|
+
- `--vela-btn-destructive-bg` binds `red-700`, not `red-500`: red-500 behind a white label is
|
|
52
|
+
3.93:1 and fails AA.
|
|
53
|
+
- `--vela-text-de-emphasized` binds `grey-600`, not `grey-500` (3.04:1 on white).
|
|
54
|
+
- `--vela-text-severity-success` and `--vela-text-risk-low` bind `green-800`, not `green-700`.
|
|
55
|
+
- Status dots bind `yellow-700` and `orange-600` rather than the 500s to clear 3:1 on white.
|
|
56
|
+
- `Button` keeps its label and stays focusable while `loading`, setting `aria-busy` and
|
|
57
|
+
ignoring clicks, rather than swapping the label out or disabling itself.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Omkar Khadamkar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Vela
|
|
2
|
+
|
|
3
|
+
A token-first React component kit. Six components, two themes, zero runtime dependencies —
|
|
4
|
+
and every design rule in the documentation is enforced by the TypeScript compiler.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @omkarux/vela
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import '@omkarux/vela/styles.css'
|
|
12
|
+
import { Button, ContextualAlert, Tabs } from '@omkarux/vela'
|
|
13
|
+
|
|
14
|
+
<div className="vela-root">
|
|
15
|
+
<ContextualAlert severity="major" title="Scan failed">
|
|
16
|
+
Three assets could not be reached.
|
|
17
|
+
</ContextualAlert>
|
|
18
|
+
<Button variant="primary" appearance="filled">Retry Scan</Button>
|
|
19
|
+
</div>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What is actually in here
|
|
23
|
+
|
|
24
|
+
| | |
|
|
25
|
+
|---|---|
|
|
26
|
+
| **Components** | Button · Toggle · Input · ContextualAlert · StatusIndicator · Tabs |
|
|
27
|
+
| **Tokens** | 239 CSS custom properties, two layers (primitives → semantics) |
|
|
28
|
+
| **Themes** | Light + dark. OS preference by default, `data-theme` to override |
|
|
29
|
+
| **Dependencies** | None. `react` / `react-dom` are peers (18.3 or 19) |
|
|
30
|
+
| **Format** | ESM, `preserveModules` for tree-shaking, full `.d.ts` |
|
|
31
|
+
| **Tests** | 116, including 76 asserted contrast pairs across both themes |
|
|
32
|
+
| **Packed size** | 26.7 kB, 43 files |
|
|
33
|
+
|
|
34
|
+
## The three ideas worth stealing
|
|
35
|
+
|
|
36
|
+
**1. Dark mode redefines no primitive.** The entire dark theme is semantic tokens re-pointed
|
|
37
|
+
onto a different part of the same scale. A test fails the build if a raw hex ever appears in
|
|
38
|
+
the dark block. That property is what makes a third theme cheap instead of a rewrite.
|
|
39
|
+
|
|
40
|
+
**2. Severity, Risk and Status are separate taxonomies.** Six levels, three levels, five
|
|
41
|
+
states — never interchangeable. There is no "high severity" and no "critical risk". The
|
|
42
|
+
types reject both. Most design systems collapse these into one `status` prop and then spend
|
|
43
|
+
years untangling it.
|
|
44
|
+
|
|
45
|
+
**3. Design constraints are compiler errors, not review comments.** `ButtonProps` is a
|
|
46
|
+
discriminated union, so the rule "text-link is primary-only, small sizes only, never with an
|
|
47
|
+
icon" is not a paragraph someone has to remember:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<Button variant="primary" appearance="text-link" size="tiny">Learn More</Button> // ✅
|
|
51
|
+
<Button variant="destructive" appearance="text-link">Delete</Button> // ✗ won't compile
|
|
52
|
+
<Button variant="primary" appearance="text-link" size="huge">Go</Button> // ✗ won't compile
|
|
53
|
+
<Button variant="primary" appearance="text-link" icon={<Icon />}>Go</Button> // ✗ won't compile
|
|
54
|
+
<ContextualAlert severity="high">…</ContextualAlert> // ✗ severity has no "high"
|
|
55
|
+
<StatusIndicator status="critical" /> // ✗ that's severity, not status
|
|
56
|
+
<Tabs.List>…</Tabs.List> // ✗ aria-label is required
|
|
57
|
+
<Toggle /> // ✗ label is required
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`npm run verify` asserts every one of those rejections against the *published tarball*.
|
|
61
|
+
|
|
62
|
+
## Accessibility is asserted, not claimed
|
|
63
|
+
|
|
64
|
+
`npm run contrast` prints every foreground/background pair in both themes against WCAG 2.1
|
|
65
|
+
AA — 4.5:1 for text, 3:1 for UI boundaries and non-text signals. The same data backs a test,
|
|
66
|
+
so a token change that breaks contrast fails CI.
|
|
67
|
+
|
|
68
|
+
The first run of that audit found 9 failures in the inherited light palette. All 9 are fixed,
|
|
69
|
+
and the fixes are documented in `guidelines/foundations/color.md` so nobody "corrects" them
|
|
70
|
+
back — most notably `--vela-btn-destructive-bg`, which binds `red-700` rather than `red-500`
|
|
71
|
+
because red-500 behind a white label is 3.93:1.
|
|
72
|
+
|
|
73
|
+
## Built for agents as well as people
|
|
74
|
+
|
|
75
|
+
`guidelines/` ships **inside the package**. It is the specification, not a rendered copy of
|
|
76
|
+
the source: closed prop tables, token-binding tables, hard constraints, and an anti-patterns
|
|
77
|
+
block per component. Point your coding agent at `guidelines/llms.txt`, or import a single
|
|
78
|
+
file:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import buttonSpec from '@omkarux/vela/guidelines/components/button.md?raw'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`AGENTS.md` at the repo root carries the same rules for anyone working *on* the system.
|
|
85
|
+
|
|
86
|
+
## Theming
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
document.documentElement.setAttribute('data-theme', 'dark') // force dark
|
|
90
|
+
document.documentElement.setAttribute('data-theme', 'light') // force light
|
|
91
|
+
document.documentElement.removeAttribute('data-theme') // follow the OS
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Scripts
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run dev # demo showcase at localhost:5174
|
|
98
|
+
npm test # 116 tests
|
|
99
|
+
npm run contrast # WCAG report for both themes
|
|
100
|
+
npm run verify # typecheck + tests + build + pack-and-consume gate
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Scope
|
|
104
|
+
|
|
105
|
+
Six components, chosen to exercise every foundation and both status taxonomies. Not yet
|
|
106
|
+
specced: Checkbox, Radio, Dropdown List, Select List, Token Pill, Number Stepper, Text Area,
|
|
107
|
+
Modal, Tooltip, Icon Button, Actions Dropdown, View Switcher. The guidelines name these
|
|
108
|
+
explicitly so an agent flags the gap instead of inventing one.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type ButtonVariant = 'primary' | 'standard' | 'destructive';
|
|
3
|
+
export type ButtonSize = 'tiny' | 'regular' | 'large' | 'huge';
|
|
4
|
+
interface ButtonCommon extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'children'> {
|
|
5
|
+
/** Label. Verb-first, Title Case: "Save Changes", not "Information". */
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Shows an indicator and sets aria-busy. Clicks are ignored while true. */
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
type?: 'button' | 'submit';
|
|
11
|
+
}
|
|
12
|
+
export interface SolidButtonProps extends ButtonCommon {
|
|
13
|
+
variant?: ButtonVariant;
|
|
14
|
+
appearance?: 'filled' | 'hollow';
|
|
15
|
+
size?: ButtonSize;
|
|
16
|
+
/** Optional icon, always rendered LEFT of the label. There is no trailing slot. */
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The text-link appearance is deliberately narrower than the others.
|
|
21
|
+
* `icon?: never` and the closed `size` union mean the compiler — not a code
|
|
22
|
+
* review — rejects `<Button appearance="text-link" variant="destructive" />`.
|
|
23
|
+
*/
|
|
24
|
+
export interface TextLinkButtonProps extends ButtonCommon {
|
|
25
|
+
variant: 'primary';
|
|
26
|
+
appearance: 'text-link';
|
|
27
|
+
size?: 'tiny' | 'regular';
|
|
28
|
+
icon?: never;
|
|
29
|
+
}
|
|
30
|
+
export type ButtonProps = SolidButtonProps | TextLinkButtonProps;
|
|
31
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsxs as m, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import * as v from "react";
|
|
3
|
+
import { cn as h } from "../../lib/cn.js";
|
|
4
|
+
const g = v.forwardRef(
|
|
5
|
+
function(r, s) {
|
|
6
|
+
const {
|
|
7
|
+
variant: i = "standard",
|
|
8
|
+
appearance: o = "filled",
|
|
9
|
+
size: c = "regular",
|
|
10
|
+
disabled: d = !1,
|
|
11
|
+
loading: a = !1,
|
|
12
|
+
type: u = "button",
|
|
13
|
+
icon: t,
|
|
14
|
+
children: b,
|
|
15
|
+
className: p,
|
|
16
|
+
onClick: e,
|
|
17
|
+
...f
|
|
18
|
+
} = r;
|
|
19
|
+
return /* @__PURE__ */ m(
|
|
20
|
+
"button",
|
|
21
|
+
{
|
|
22
|
+
ref: s,
|
|
23
|
+
type: u,
|
|
24
|
+
disabled: d,
|
|
25
|
+
"aria-busy": a || void 0,
|
|
26
|
+
onClick: (l) => {
|
|
27
|
+
if (a) {
|
|
28
|
+
l.preventDefault();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
e == null || e(l);
|
|
32
|
+
},
|
|
33
|
+
className: h(
|
|
34
|
+
"vela-btn",
|
|
35
|
+
`vela-btn--${i}`,
|
|
36
|
+
`vela-btn--${o}`,
|
|
37
|
+
`vela-btn--${c}`,
|
|
38
|
+
p
|
|
39
|
+
),
|
|
40
|
+
...f,
|
|
41
|
+
children: [
|
|
42
|
+
a ? /* @__PURE__ */ n("span", { className: "vela-btn__spinner", "aria-hidden": "true" }) : t ? /* @__PURE__ */ n("span", { className: "vela-btn__icon", "aria-hidden": "true", children: t }) : null,
|
|
43
|
+
/* @__PURE__ */ n("span", { className: "vela-btn__label", children: b })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
export {
|
|
50
|
+
g as Button
|
|
51
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The six severity levels. Describes events and alerts.
|
|
4
|
+
* There is no "high severity" — that is the Risk taxonomy, and the two are
|
|
5
|
+
* kept apart on purpose. See guidelines/foundations/color.md.
|
|
6
|
+
*/
|
|
7
|
+
export type Severity = 'success' | 'info' | 'warning' | 'minor' | 'major' | 'critical';
|
|
8
|
+
export interface ContextualAlertProps {
|
|
9
|
+
severity: Severity;
|
|
10
|
+
/** Optional bold lead-in. The body is always required. */
|
|
11
|
+
title?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
/** Renders a dismiss control. Omit for alerts the user must not clear. */
|
|
14
|
+
onDismiss?: () => void;
|
|
15
|
+
dismissLabel?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function ContextualAlert({ severity, title, children, onDismiss, dismissLabel, className, }: ContextualAlertProps): React.JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsxs as n, jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import { cn as v } from "../../lib/cn.js";
|
|
3
|
+
import { CriticalOctagonIcon as I, WarningTriangleIcon as l, InfoCircleIcon as d, CheckCircleIcon as C, CloseIcon as _ } from "../../lib/icons.js";
|
|
4
|
+
const u = {
|
|
5
|
+
success: C,
|
|
6
|
+
info: d,
|
|
7
|
+
warning: l,
|
|
8
|
+
minor: l,
|
|
9
|
+
major: l,
|
|
10
|
+
critical: I
|
|
11
|
+
}, f = /* @__PURE__ */ new Set(["major", "critical"]);
|
|
12
|
+
function g({
|
|
13
|
+
severity: e,
|
|
14
|
+
title: r,
|
|
15
|
+
children: o,
|
|
16
|
+
onDismiss: c,
|
|
17
|
+
dismissLabel: i = "Dismiss",
|
|
18
|
+
className: s
|
|
19
|
+
}) {
|
|
20
|
+
const m = u[e], t = f.has(e);
|
|
21
|
+
return /* @__PURE__ */ n(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: v("vela-alert", `vela-alert--${e}`, s),
|
|
25
|
+
role: t ? "alert" : "status",
|
|
26
|
+
"aria-live": t ? "assertive" : "polite",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ a(m, { className: "vela-alert__icon" }),
|
|
29
|
+
/* @__PURE__ */ n("div", { className: "vela-alert__body", children: [
|
|
30
|
+
r && /* @__PURE__ */ a("strong", { className: "vela-alert__title", children: r }),
|
|
31
|
+
o
|
|
32
|
+
] }),
|
|
33
|
+
c && /* @__PURE__ */ a(
|
|
34
|
+
"button",
|
|
35
|
+
{
|
|
36
|
+
type: "button",
|
|
37
|
+
className: "vela-alert__dismiss",
|
|
38
|
+
onClick: c,
|
|
39
|
+
"aria-label": i,
|
|
40
|
+
children: /* @__PURE__ */ a(_, {})
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
g as ContextualAlert
|
|
49
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
3
|
+
/** Required — an input without a visible label is a support ticket. */
|
|
4
|
+
label: string;
|
|
5
|
+
/** Helper text below the field. Announced via aria-describedby. */
|
|
6
|
+
hint?: string;
|
|
7
|
+
/** Presence puts the field in the error state; the string is the message. */
|
|
8
|
+
error?: string;
|
|
9
|
+
size?: 'tiny' | 'regular' | 'large';
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsxs as s, jsx as l } from "react/jsx-runtime";
|
|
2
|
+
import * as t from "react";
|
|
3
|
+
import { cn as c } from "../../lib/cn.js";
|
|
4
|
+
const j = t.forwardRef(
|
|
5
|
+
function({ label: o, hint: i, error: e, size: u = "regular", id: m, required: n, className: p, ...f }, v) {
|
|
6
|
+
const h = t.useId(), a = m ?? h, r = `${a}-hint`, d = `${a}-error`, _ = [i ? r : null, e ? d : null].filter(Boolean).join(" ") || void 0;
|
|
7
|
+
return /* @__PURE__ */ s("div", { className: c("vela-field", p), children: [
|
|
8
|
+
/* @__PURE__ */ s("label", { className: "vela-field__label", htmlFor: a, children: [
|
|
9
|
+
o,
|
|
10
|
+
n && /* @__PURE__ */ l("span", { className: "vela-field__required", "aria-hidden": "true", children: "*" })
|
|
11
|
+
] }),
|
|
12
|
+
/* @__PURE__ */ l(
|
|
13
|
+
"input",
|
|
14
|
+
{
|
|
15
|
+
ref: v,
|
|
16
|
+
id: a,
|
|
17
|
+
required: n,
|
|
18
|
+
"aria-invalid": e ? !0 : void 0,
|
|
19
|
+
"aria-describedby": _,
|
|
20
|
+
className: c("vela-input", `vela-input--${u}`),
|
|
21
|
+
...f
|
|
22
|
+
}
|
|
23
|
+
),
|
|
24
|
+
i && /* @__PURE__ */ l("span", { id: r, className: "vela-field__hint", children: i }),
|
|
25
|
+
e && /* @__PURE__ */ l("span", { id: d, className: "vela-field__error", role: "alert", children: e })
|
|
26
|
+
] });
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
export {
|
|
30
|
+
j as Input
|
|
31
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five health states. This is NOT severity and NOT risk — those are
|
|
3
|
+
* separate taxonomies with their own tokens. There is no "critical" status
|
|
4
|
+
* and no "high" status; if you reach for one, you want a different component.
|
|
5
|
+
*/
|
|
6
|
+
export type Status = 'unknown' | 'healthy' | 'warning' | 'medium' | 'unhealthy';
|
|
7
|
+
export interface StatusIndicatorProps {
|
|
8
|
+
status: Status;
|
|
9
|
+
/** Visible text. Strongly preferred — see the aria-label fallback below. */
|
|
10
|
+
label?: string;
|
|
11
|
+
size?: 'small' | 'regular';
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function StatusIndicator({ status, label, size, className, }: StatusIndicatorProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsxs as r, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import { cn as l } from "../../lib/cn.js";
|
|
3
|
+
const i = {
|
|
4
|
+
unknown: "Status unknown",
|
|
5
|
+
healthy: "Healthy",
|
|
6
|
+
warning: "Warning",
|
|
7
|
+
medium: "Degraded",
|
|
8
|
+
unhealthy: "Unhealthy"
|
|
9
|
+
};
|
|
10
|
+
function d({
|
|
11
|
+
status: t,
|
|
12
|
+
label: a,
|
|
13
|
+
size: e = "regular",
|
|
14
|
+
className: s
|
|
15
|
+
}) {
|
|
16
|
+
return /* @__PURE__ */ r(
|
|
17
|
+
"span",
|
|
18
|
+
{
|
|
19
|
+
className: l("vela-status", `vela-status--${t}`, `vela-status--${e}`, s),
|
|
20
|
+
role: "img",
|
|
21
|
+
"aria-label": a ? void 0 : i[t],
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ n("span", { className: "vela-status__dot", "aria-hidden": "true" }),
|
|
24
|
+
a && /* @__PURE__ */ n("span", { className: "vela-status__label", children: a })
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
d as StatusIndicator
|
|
31
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TabsProps {
|
|
3
|
+
/** Controlled active value. Omit for uncontrolled. */
|
|
4
|
+
value?: string;
|
|
5
|
+
defaultValue: string;
|
|
6
|
+
onValueChange?: (value: string) => void;
|
|
7
|
+
size?: 'regular' | 'large';
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Navigate between DIFFERENT content panels.
|
|
13
|
+
* If the panels show the SAME dataset rendered differently (grid vs. chart),
|
|
14
|
+
* that is a View Switcher, not Tabs.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Tabs({ value, defaultValue, onValueChange, size, children, className, }: TabsProps): React.JSX.Element;
|
|
17
|
+
export declare namespace Tabs {
|
|
18
|
+
var List: typeof TabList;
|
|
19
|
+
var Trigger: typeof TabTrigger;
|
|
20
|
+
var Panel: typeof TabPanel;
|
|
21
|
+
}
|
|
22
|
+
export interface TabListProps {
|
|
23
|
+
/** Required: a tablist needs a name to be navigable by screen reader. */
|
|
24
|
+
'aria-label': string;
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
declare function TabList({ children, className, ...rest }: TabListProps): React.JSX.Element;
|
|
29
|
+
export interface TabTriggerProps {
|
|
30
|
+
value: string;
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
/** Optional numeric badge, e.g. an alert count. */
|
|
33
|
+
count?: number;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
declare function TabTrigger({ value, children, count, disabled, className }: TabTriggerProps): React.JSX.Element;
|
|
38
|
+
export interface TabPanelProps {
|
|
39
|
+
value: string;
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
declare function TabPanel({ value, children, className }: TabPanelProps): React.JSX.Element | null;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as u, jsxs as k } from "react/jsx-runtime";
|
|
2
|
+
import * as c from "react";
|
|
3
|
+
import { cn as x } from "../../lib/cn.js";
|
|
4
|
+
const p = c.createContext(null);
|
|
5
|
+
function T(e) {
|
|
6
|
+
const s = c.useContext(p);
|
|
7
|
+
if (!s)
|
|
8
|
+
throw new Error(`<Tabs.${e}> must be rendered inside <Tabs>.`);
|
|
9
|
+
return s;
|
|
10
|
+
}
|
|
11
|
+
function y({
|
|
12
|
+
value: e,
|
|
13
|
+
defaultValue: s,
|
|
14
|
+
onValueChange: r,
|
|
15
|
+
size: a = "regular",
|
|
16
|
+
children: o,
|
|
17
|
+
className: n
|
|
18
|
+
}) {
|
|
19
|
+
const d = e !== void 0, [t, l] = c.useState(s), i = d ? e : t, b = c.useId(), f = c.useCallback(
|
|
20
|
+
(g) => {
|
|
21
|
+
d || l(g), r == null || r(g);
|
|
22
|
+
},
|
|
23
|
+
[d, r]
|
|
24
|
+
), m = c.useMemo(
|
|
25
|
+
() => ({ value: i, setValue: f, idPrefix: b }),
|
|
26
|
+
[i, f, b]
|
|
27
|
+
);
|
|
28
|
+
return /* @__PURE__ */ u(p.Provider, { value: m, children: /* @__PURE__ */ u("div", { className: x("vela-tabs", `vela-tabs--${a}`, n), children: o }) });
|
|
29
|
+
}
|
|
30
|
+
function v({ children: e, className: s, ...r }) {
|
|
31
|
+
const a = c.useRef(null);
|
|
32
|
+
return /* @__PURE__ */ u(
|
|
33
|
+
"div",
|
|
34
|
+
{
|
|
35
|
+
ref: a,
|
|
36
|
+
role: "tablist",
|
|
37
|
+
onKeyDown: (n) => {
|
|
38
|
+
var b, f, m;
|
|
39
|
+
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(n.key)) return;
|
|
40
|
+
const t = Array.from(
|
|
41
|
+
((b = a.current) == null ? void 0 : b.querySelectorAll('[role="tab"]:not(:disabled)')) ?? []
|
|
42
|
+
);
|
|
43
|
+
if (t.length === 0) return;
|
|
44
|
+
const l = t.indexOf(document.activeElement);
|
|
45
|
+
let i = l;
|
|
46
|
+
n.key === "ArrowRight" ? i = (l + 1) % t.length : n.key === "ArrowLeft" ? i = (l - 1 + t.length) % t.length : n.key === "Home" ? i = 0 : n.key === "End" && (i = t.length - 1), n.preventDefault(), (f = t[i]) == null || f.focus(), (m = t[i]) == null || m.click();
|
|
47
|
+
},
|
|
48
|
+
className: x("vela-tabs__list", s),
|
|
49
|
+
...r,
|
|
50
|
+
children: e
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function $({ value: e, children: s, count: r, disabled: a, className: o }) {
|
|
55
|
+
const { value: n, setValue: d, idPrefix: t } = T("Trigger"), l = n === e;
|
|
56
|
+
return /* @__PURE__ */ k(
|
|
57
|
+
"button",
|
|
58
|
+
{
|
|
59
|
+
type: "button",
|
|
60
|
+
role: "tab",
|
|
61
|
+
id: `${t}-tab-${e}`,
|
|
62
|
+
"aria-selected": l,
|
|
63
|
+
"aria-controls": `${t}-panel-${e}`,
|
|
64
|
+
tabIndex: l ? 0 : -1,
|
|
65
|
+
disabled: a,
|
|
66
|
+
onClick: () => d(e),
|
|
67
|
+
className: x("vela-tabs__trigger", o),
|
|
68
|
+
children: [
|
|
69
|
+
s,
|
|
70
|
+
r !== void 0 && /* @__PURE__ */ u("span", { className: "vela-tabs__count", children: r })
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function w({ value: e, children: s, className: r }) {
|
|
76
|
+
const { value: a, idPrefix: o } = T("Panel");
|
|
77
|
+
return a !== e ? null : /* @__PURE__ */ u(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
role: "tabpanel",
|
|
81
|
+
id: `${o}-panel-${e}`,
|
|
82
|
+
"aria-labelledby": `${o}-tab-${e}`,
|
|
83
|
+
tabIndex: 0,
|
|
84
|
+
className: x("vela-tabs__panel", r),
|
|
85
|
+
children: s
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
y.List = v;
|
|
90
|
+
y.Trigger = $;
|
|
91
|
+
y.Panel = w;
|
|
92
|
+
export {
|
|
93
|
+
y as Tabs
|
|
94
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface ToggleProps {
|
|
3
|
+
/** Controlled state. Omit for uncontrolled. */
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
defaultChecked?: boolean;
|
|
6
|
+
onChange?: (checked: boolean) => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Required. A switch with no accessible name is unusable with a screen
|
|
10
|
+
* reader, so the API does not allow one to exist.
|
|
11
|
+
*/
|
|
12
|
+
label: string;
|
|
13
|
+
labelPosition?: 'right' | 'left';
|
|
14
|
+
size?: 'tiny' | 'regular';
|
|
15
|
+
id?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A binary setting that takes effect IMMEDIATELY.
|
|
21
|
+
* If the change only takes effect after a Save click, use Checkbox instead.
|
|
22
|
+
*/
|
|
23
|
+
export declare const Toggle: React.ForwardRefExoticComponent<ToggleProps & React.RefAttributes<HTMLButtonElement>>;
|