@evinvest/uikit 0.17.0 → 0.18.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 +16 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/tokens.css +23 -5
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ your Tailwind v4 entrypoint — this is the load-bearing part of the kit:
|
|
|
51
51
|
| surfaces | `background` `card` `popover` `muted` `hover` |
|
|
52
52
|
| ink | `ink` `ink-mid` `ink-soft` — hierarchy, loudest first |
|
|
53
53
|
| lines | `border` `input` `ring` |
|
|
54
|
-
| roles | `brand` `primary` `secondary` `positive` `accent-trace` `accent-debug` `accent-info` `accent-warn` `accent-error`, each with `on-*` where it gets filled |
|
|
54
|
+
| roles | `brand` `primary` `secondary` `positive` `accent-trace` `accent-debug` `accent-info` `accent-warn` `accent-error`, each with `on-*` where it gets filled; `primary-ink` — the primary role as ink on a surface (`#128377` fill under a white `on-primary`, `#2a9d8f` ink) |
|
|
55
55
|
| scalars | `radius` `control-radius` `control-py` `display-scale` `band-py` `page-max` `page-px` `shadow-*` `font-*` |
|
|
56
56
|
| charts | `chart-1` … `chart-5` |
|
|
57
57
|
|
|
@@ -63,6 +63,17 @@ A surface takes its ink from the scope, so it carries no `-foreground`. A
|
|
|
63
63
|
**filled role** does not — gold wants black and navy wants white regardless of
|
|
64
64
|
polarity — so `on-{role}` exists exactly there.
|
|
65
65
|
|
|
66
|
+
A fill and an ink are different jobs, and `primary` is the one role that
|
|
67
|
+
names them separately: `primary` is the fill (`bg-primary text-on-primary` on
|
|
68
|
+
a button, a checked switch or checkbox — `#128377`, white reads on it at
|
|
69
|
+
4.63:1 and it clears 3:1 against `background`, `secondary`, `card` and
|
|
70
|
+
`popover`) and `primary-ink` is the same hue as ink (`text-primary-ink` on a
|
|
71
|
+
link or eyebrow, a radio dot, a slider range, `border-primary-ink` on a checked
|
|
72
|
+
outline, the focus `ring` — `#2a9d8f`, 5.85:1 on `background`, 4.72:1 on
|
|
73
|
+
`card`). Every other role holds its own label and reads as ink with one value.
|
|
74
|
+
A consumer sheet must define `--primary-ink`; see
|
|
75
|
+
[`docs/spec/accents.md`](../../docs/spec/accents.md).
|
|
76
|
+
|
|
66
77
|
Accents are decorative and **ordered by significance**, quiet to loud. Pick a
|
|
67
78
|
rung by how loud the thing should be, never by what it means; see
|
|
68
79
|
[`docs/spec/accents.md`](../../docs/spec/accents.md). Every consumer defines all
|
|
@@ -78,11 +89,6 @@ state, never its appearance (`Red`) and never its selection (`Default`); see
|
|
|
78
89
|
and carries EV's values: a single dark palette on `:root, .dark`. A consumer
|
|
79
90
|
with two polarities writes its own sheet against the same names.
|
|
80
91
|
|
|
81
|
-
`text-main-mist`, `bg-sidebar`, `text-muted-foreground` and the rest keep
|
|
82
|
-
working, and the current tokens are re-pointed at the old values so rendered
|
|
83
|
-
colour is unchanged. Import it instead while migrating; it goes away once every
|
|
84
|
-
consumer has moved.
|
|
85
|
-
|
|
86
92
|
## Usage
|
|
87
93
|
|
|
88
94
|
```tsx
|
|
@@ -188,8 +194,10 @@ measuring needs host-only `web-sys`). Known gaps:
|
|
|
188
194
|
at day granularity (days outside render `disabled` + `data-disabled="true"`);
|
|
189
195
|
`disabled` freezes the whole grid and the nav the same way; the nav buttons
|
|
190
196
|
take label overrides. TS-only `locale` renders the caption and weekday headers through
|
|
191
|
-
`Intl` (Monday-first)
|
|
192
|
-
|
|
197
|
+
`Intl` (Monday-first): `weekday: "short"`, falling back to `"narrow"` for a
|
|
198
|
+
locale whose short form contains whitespace (vi's "Thứ 2" → "T2", he's
|
|
199
|
+
"יום ב׳" → "ב׳") as long as narrow still yields seven distinct labels. Rust has no `Intl`, so its captions
|
|
200
|
+
stay English. Rust does manual date math; TS uses the built-in `Date`.
|
|
193
201
|
- **date-time-picker:** the kit's own bricks only — an outline trigger, a
|
|
194
202
|
`Popover` with the `Calendar` and two numeric 24-hour hours/minutes fields (no
|
|
195
203
|
native `datetime-local` / `time` input, so the browser's locale popup never
|
package/dist/index.d.ts
CHANGED
|
@@ -254,7 +254,7 @@ declare const buttonVariantClasses: {
|
|
|
254
254
|
readonly outline: "border bg-transparent shadow-xs hover:bg-hover hover:text-ink";
|
|
255
255
|
readonly ghost: "hover:bg-hover hover:text-ink";
|
|
256
256
|
readonly destructive: "bg-accent-error text-on-accent-error hover:bg-accent-error/90 focus-visible:ring-accent-error/20";
|
|
257
|
-
readonly link: "text-primary underline-offset-4 hover:underline";
|
|
257
|
+
readonly link: "text-primary-ink underline-offset-4 hover:underline";
|
|
258
258
|
};
|
|
259
259
|
type ButtonVariant = keyof typeof buttonVariantClasses;
|
|
260
260
|
declare const buttonSizeClasses: {
|
package/dist/index.js
CHANGED
|
@@ -234,9 +234,19 @@ function useFloating(opts) {
|
|
|
234
234
|
|
|
235
235
|
// src/primitives/focus-scope.tsx
|
|
236
236
|
import * as React6 from "react";
|
|
237
|
-
var FOCUSABLE =
|
|
237
|
+
var FOCUSABLE = [
|
|
238
|
+
"a[href]",
|
|
239
|
+
"button:not([disabled])",
|
|
240
|
+
"textarea:not([disabled])",
|
|
241
|
+
"input:not([disabled])",
|
|
242
|
+
"select:not([disabled])",
|
|
243
|
+
"[tabindex]"
|
|
244
|
+
].map((sel) => `${sel}:not([tabindex="-1"])`).join(",");
|
|
245
|
+
function focusCandidates(root) {
|
|
246
|
+
return Array.from(root.querySelectorAll(FOCUSABLE));
|
|
247
|
+
}
|
|
238
248
|
function focusable(root) {
|
|
239
|
-
return
|
|
249
|
+
return focusCandidates(root).filter(
|
|
240
250
|
(el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement
|
|
241
251
|
);
|
|
242
252
|
}
|
|
@@ -708,7 +718,7 @@ var buttonVariantClasses = {
|
|
|
708
718
|
"outline": "border bg-transparent shadow-xs hover:bg-hover hover:text-ink",
|
|
709
719
|
"ghost": "hover:bg-hover hover:text-ink",
|
|
710
720
|
"destructive": "bg-accent-error text-on-accent-error hover:bg-accent-error/90 focus-visible:ring-accent-error/20",
|
|
711
|
-
"link": "text-primary underline-offset-4 hover:underline"
|
|
721
|
+
"link": "text-primary-ink underline-offset-4 hover:underline"
|
|
712
722
|
};
|
|
713
723
|
var buttonSizeClasses = {
|
|
714
724
|
"xs": "h-7 gap-1 px-2 text-xs has-[>svg]:px-1.5",
|
|
@@ -930,7 +940,7 @@ var emptyMediaVariants = {
|
|
|
930
940
|
"icon": "bg-muted text-ink flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6"
|
|
931
941
|
};
|
|
932
942
|
var EMPTY_TITLE = "text-lg font-medium tracking-tight";
|
|
933
|
-
var EMPTY_DESCRIPTION = "text-ink-soft [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4";
|
|
943
|
+
var EMPTY_DESCRIPTION = "text-ink-soft [&>a:hover]:text-primary-ink text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4";
|
|
934
944
|
var EMPTY_CONTENT = "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance";
|
|
935
945
|
|
|
936
946
|
// src/components/empty.tsx
|
|
@@ -1032,9 +1042,9 @@ var FIELD_SET = "flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has
|
|
|
1032
1042
|
var FIELD_LEGEND = "mb-3 font-medium data-[variant=legend]:text-base data-[variant=label]:text-sm";
|
|
1033
1043
|
var FIELD_GROUP = "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4";
|
|
1034
1044
|
var FIELD_CONTENT = "group/field-content flex flex-1 flex-col gap-1.5 leading-snug";
|
|
1035
|
-
var FIELD_LABEL = "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary";
|
|
1045
|
+
var FIELD_LABEL = "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 has-data-[state=checked]:bg-primary-ink/5 has-data-[state=checked]:border-primary-ink";
|
|
1036
1046
|
var FIELD_TITLE = "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50";
|
|
1037
|
-
var FIELD_DESCRIPTION = "text-ink-soft text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5 [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4";
|
|
1047
|
+
var FIELD_DESCRIPTION = "text-ink-soft text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5 [&>a:hover]:text-primary-ink [&>a]:underline [&>a]:underline-offset-4";
|
|
1038
1048
|
var FIELD_SEPARATOR = "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2";
|
|
1039
1049
|
var FIELD_SEPARATOR_LINE = "absolute inset-0 top-1/2 shrink-0 bg-border h-px w-full";
|
|
1040
1050
|
var FIELD_SEPARATOR_CONTENT = "bg-background text-ink-soft relative mx-auto block w-fit px-2";
|
|
@@ -1643,7 +1653,7 @@ var itemMediaVariants = {
|
|
|
1643
1653
|
};
|
|
1644
1654
|
var ITEM_CONTENT = "flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none";
|
|
1645
1655
|
var ITEM_TITLE = "flex w-fit items-center gap-2 text-sm leading-snug font-medium";
|
|
1646
|
-
var ITEM_DESCRIPTION = "text-ink-soft line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4";
|
|
1656
|
+
var ITEM_DESCRIPTION = "text-ink-soft line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary-ink [&>a]:underline [&>a]:underline-offset-4";
|
|
1647
1657
|
var ITEM_ACTIONS = "flex items-center gap-2";
|
|
1648
1658
|
var ITEM_HEADER = "flex basis-full items-center justify-between gap-2";
|
|
1649
1659
|
var ITEM_FOOTER = "flex basis-full items-center justify-between gap-2";
|
|
@@ -1947,8 +1957,8 @@ function PaginationEllipsis({
|
|
|
1947
1957
|
import "react";
|
|
1948
1958
|
|
|
1949
1959
|
// src/generated/progress.ts
|
|
1950
|
-
var PROGRESS_TRACK = "bg-primary/20 relative h-2 w-full overflow-hidden rounded-full";
|
|
1951
|
-
var PROGRESS_INDICATOR = "bg-primary h-full w-full flex-1 transition-all";
|
|
1960
|
+
var PROGRESS_TRACK = "bg-primary-ink/20 relative h-2 w-full overflow-hidden rounded-full";
|
|
1961
|
+
var PROGRESS_INDICATOR = "bg-primary-ink h-full w-full flex-1 transition-all";
|
|
1952
1962
|
|
|
1953
1963
|
// src/components/progress.tsx
|
|
1954
1964
|
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
@@ -2466,7 +2476,7 @@ var SECTION_BASE = "w-full px-[var(--page-px)]";
|
|
|
2466
2476
|
var SECTION_PY = "py-[var(--band-py)]";
|
|
2467
2477
|
var SECTION_PY_TIGHT = "py-[var(--band-py-tight)]";
|
|
2468
2478
|
var SECTION_HEAD = "flex flex-col gap-2 md:gap-3.5";
|
|
2469
|
-
var EYEBROW = "font-medium text-[10px] md:text-[11.5px] tracking-[0.15em] md:tracking-[0.16em] text-primary";
|
|
2479
|
+
var EYEBROW = "font-medium text-[10px] md:text-[11.5px] tracking-[0.15em] md:tracking-[0.16em] text-primary-ink [.bg-primary_&]:text-on-primary";
|
|
2470
2480
|
var DISPLAY_BASE = "font-display [font-weight:700] max-w-[54rem] tracking-[-0.005em] leading-[1.16] md:leading-[1.14] text-[calc(1.5625rem*var(--display-scale))] md:text-[calc(2.625rem*var(--display-scale))]";
|
|
2471
2481
|
var LEDE = "max-w-[52rem] text-[15px] md:text-[17px] leading-[1.58] text-ink-soft";
|
|
2472
2482
|
var PROSE = "text-[14px] md:text-[15px] leading-[1.62] text-ink-soft";
|
|
@@ -2924,7 +2934,7 @@ import * as React45 from "react";
|
|
|
2924
2934
|
|
|
2925
2935
|
// src/generated/radio-group.ts
|
|
2926
2936
|
var RADIO_GROUP_ROOT = "grid gap-3";
|
|
2927
|
-
var RADIO_GROUP_ITEM = "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50";
|
|
2937
|
+
var RADIO_GROUP_ITEM = "border-input text-primary-ink focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50";
|
|
2928
2938
|
|
|
2929
2939
|
// src/components/radio-group.tsx
|
|
2930
2940
|
import { jsx as jsx35 } from "react/jsx-runtime";
|
|
@@ -3018,7 +3028,7 @@ function RadioGroupItem({
|
|
|
3018
3028
|
children: /* @__PURE__ */ jsx35(
|
|
3019
3029
|
"svg",
|
|
3020
3030
|
{
|
|
3021
|
-
className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2",
|
|
3031
|
+
className: "fill-primary-ink absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2",
|
|
3022
3032
|
viewBox: "0 0 24 24",
|
|
3023
3033
|
"aria-hidden": "true",
|
|
3024
3034
|
children: /* @__PURE__ */ jsx35("circle", { cx: "12", cy: "12", r: "10" })
|
|
@@ -3036,8 +3046,8 @@ import * as React46 from "react";
|
|
|
3036
3046
|
// src/generated/slider.ts
|
|
3037
3047
|
var SLIDER_ROOT = "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col";
|
|
3038
3048
|
var SLIDER_TRACK = "bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5";
|
|
3039
|
-
var SLIDER_RANGE = "bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full";
|
|
3040
|
-
var SLIDER_THUMB = "border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-ink shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50";
|
|
3049
|
+
var SLIDER_RANGE = "bg-primary-ink absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full";
|
|
3050
|
+
var SLIDER_THUMB = "border-primary-ink ring-ring/50 block size-4 shrink-0 rounded-full border bg-ink shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50";
|
|
3041
3051
|
|
|
3042
3052
|
// src/components/slider.tsx
|
|
3043
3053
|
import { jsx as jsx36, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -6970,7 +6980,7 @@ var CALENDAR_NAV = "flex items-center justify-between gap-1 w-full px-1";
|
|
|
6970
6980
|
var CALENDAR_CAPTION = "text-sm font-medium select-none";
|
|
6971
6981
|
var CALENDAR_GRID = "w-full border-collapse mt-4";
|
|
6972
6982
|
var CALENDAR_WEEKDAY_ROW = "flex";
|
|
6973
|
-
var CALENDAR_WEEKDAY = "text-ink-soft rounded-md w-9 shrink-0 font-normal text-[0.8rem] select-none";
|
|
6983
|
+
var CALENDAR_WEEKDAY = "text-ink-soft rounded-md w-9 shrink-0 font-normal text-[0.8rem] select-none whitespace-nowrap";
|
|
6974
6984
|
var CALENDAR_WEEK = "flex w-full mt-2";
|
|
6975
6985
|
var CALENDAR_DAY_EMPTY = "relative size-9 shrink-0 p-0 select-none";
|
|
6976
6986
|
var CALENDAR_DAY_CELL = "relative size-9 shrink-0 p-0 text-center select-none";
|
|
@@ -7033,13 +7043,19 @@ function outOfRange(day, min, max) {
|
|
|
7033
7043
|
return !!min && t < startOfDay(min).getTime() || !!max && t > startOfDay(max).getTime();
|
|
7034
7044
|
}
|
|
7035
7045
|
var MONDAY = new Date(2024, 0, 1);
|
|
7036
|
-
function
|
|
7037
|
-
const fmt = new Intl.DateTimeFormat(locale, { weekday
|
|
7046
|
+
function formatWeekdays(locale, weekday) {
|
|
7047
|
+
const fmt = new Intl.DateTimeFormat(locale, { weekday });
|
|
7038
7048
|
return Array.from(
|
|
7039
7049
|
{ length: 7 },
|
|
7040
7050
|
(_, i) => fmt.format(new Date(MONDAY.getFullYear(), MONDAY.getMonth(), MONDAY.getDate() + i))
|
|
7041
7051
|
);
|
|
7042
7052
|
}
|
|
7053
|
+
function localisedWeekdays(locale) {
|
|
7054
|
+
const short = formatWeekdays(locale, "short");
|
|
7055
|
+
if (!short.some((label) => /\s/.test(label))) return short;
|
|
7056
|
+
const narrow = formatWeekdays(locale, "narrow");
|
|
7057
|
+
return new Set(narrow).size === narrow.length ? narrow : short;
|
|
7058
|
+
}
|
|
7043
7059
|
function Calendar({
|
|
7044
7060
|
selected,
|
|
7045
7061
|
onSelect,
|
|
@@ -7289,6 +7305,7 @@ function DateTimePicker({
|
|
|
7289
7305
|
});
|
|
7290
7306
|
const rootRef = React67.useRef(null);
|
|
7291
7307
|
const hoursRef = React67.useRef(null);
|
|
7308
|
+
const focusTrigger = () => rootRef.current?.querySelector("[data-slot=date-time-picker-trigger]")?.focus();
|
|
7292
7309
|
React67.useEffect(() => {
|
|
7293
7310
|
if (!isOpen) return;
|
|
7294
7311
|
hoursRef.current?.focus();
|
|
@@ -7296,9 +7313,7 @@ function DateTimePicker({
|
|
|
7296
7313
|
const active = document.activeElement;
|
|
7297
7314
|
const content = hoursRef.current?.closest("[data-slot=date-time-picker-content]");
|
|
7298
7315
|
const lost = !active || active === document.body || !!content?.contains(active);
|
|
7299
|
-
if (lost)
|
|
7300
|
-
rootRef.current?.querySelector("[data-slot=date-time-picker-trigger]")?.focus();
|
|
7301
|
-
}
|
|
7316
|
+
if (lost) focusTrigger();
|
|
7302
7317
|
};
|
|
7303
7318
|
}, [isOpen]);
|
|
7304
7319
|
const emit = (next) => setValue(clampMoment(atMinute(next), min, max));
|
|
@@ -7330,6 +7345,15 @@ function DateTimePicker({
|
|
|
7330
7345
|
setValue(null);
|
|
7331
7346
|
setOpen(false);
|
|
7332
7347
|
};
|
|
7348
|
+
const onContentKeyDown = (e) => {
|
|
7349
|
+
if (e.key !== "Tab") return;
|
|
7350
|
+
const items = focusCandidates(e.currentTarget);
|
|
7351
|
+
const edge = e.shiftKey ? items[0] : items[items.length - 1];
|
|
7352
|
+
if (!edge || e.target !== edge) return;
|
|
7353
|
+
e.preventDefault();
|
|
7354
|
+
setOpen(false);
|
|
7355
|
+
focusTrigger();
|
|
7356
|
+
};
|
|
7333
7357
|
const onContentBlur = (e) => {
|
|
7334
7358
|
const next = e.relatedTarget;
|
|
7335
7359
|
if (!(next instanceof Node)) return;
|
|
@@ -7383,6 +7407,7 @@ function DateTimePicker({
|
|
|
7383
7407
|
"aria-label": labels.dialog ?? "Choose date and time",
|
|
7384
7408
|
align,
|
|
7385
7409
|
onBlur: onContentBlur,
|
|
7410
|
+
onKeyDown: onContentKeyDown,
|
|
7386
7411
|
className: DATE_TIME_PICKER_CONTENT,
|
|
7387
7412
|
children: [
|
|
7388
7413
|
/* @__PURE__ */ jsx57(
|