@repraze/kernelkit 0.1.0-alpha.4 → 0.1.0-alpha.5

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.
Files changed (115) hide show
  1. package/dist/components/alert-dialog.d.ts.map +1 -1
  2. package/dist/components/alert-dialog.js +4 -1
  3. package/dist/components/alert-dialog.js.map +1 -1
  4. package/dist/components/alert.js +3 -3
  5. package/dist/components/alert.js.map +1 -1
  6. package/dist/components/badge.js +1 -1
  7. package/dist/components/badge.js.map +1 -1
  8. package/dist/components/button.js +1 -1
  9. package/dist/components/button.js.map +1 -1
  10. package/dist/components/calendar.d.ts +8 -1
  11. package/dist/components/calendar.d.ts.map +1 -1
  12. package/dist/components/calendar.js +33 -11
  13. package/dist/components/calendar.js.map +1 -1
  14. package/dist/components/callout.js +2 -2
  15. package/dist/components/callout.js.map +1 -1
  16. package/dist/components/combobox.js +1 -1
  17. package/dist/components/combobox.js.map +1 -1
  18. package/dist/components/date-picker.d.ts +11 -5
  19. package/dist/components/date-picker.d.ts.map +1 -1
  20. package/dist/components/date-picker.js +29 -7
  21. package/dist/components/date-picker.js.map +1 -1
  22. package/dist/components/description-list.d.ts +26 -0
  23. package/dist/components/description-list.d.ts.map +1 -0
  24. package/dist/components/description-list.js +59 -0
  25. package/dist/components/description-list.js.map +1 -0
  26. package/dist/components/locale.d.ts +19 -0
  27. package/dist/components/locale.d.ts.map +1 -0
  28. package/dist/components/locale.js +54 -0
  29. package/dist/components/locale.js.map +1 -0
  30. package/dist/components/popover.d.ts +1 -1
  31. package/dist/components/popover.d.ts.map +1 -1
  32. package/dist/components/popover.js +2 -2
  33. package/dist/components/popover.js.map +1 -1
  34. package/dist/components/progress.d.ts +19 -6
  35. package/dist/components/progress.d.ts.map +1 -1
  36. package/dist/components/progress.js +18 -3
  37. package/dist/components/progress.js.map +1 -1
  38. package/dist/components/search-trigger.d.ts +4 -1
  39. package/dist/components/search-trigger.d.ts.map +1 -1
  40. package/dist/components/search-trigger.js +5 -2
  41. package/dist/components/search-trigger.js.map +1 -1
  42. package/dist/components/shimmer.d.ts +13 -0
  43. package/dist/components/shimmer.d.ts.map +1 -0
  44. package/dist/components/shimmer.js +21 -0
  45. package/dist/components/shimmer.js.map +1 -0
  46. package/dist/components/spinner.d.ts +28 -6
  47. package/dist/components/spinner.d.ts.map +1 -1
  48. package/dist/components/spinner.js +66 -14
  49. package/dist/components/spinner.js.map +1 -1
  50. package/dist/components/stepper.d.ts +64 -0
  51. package/dist/components/stepper.d.ts.map +1 -0
  52. package/dist/components/stepper.js +167 -0
  53. package/dist/components/stepper.js.map +1 -0
  54. package/dist/components/tag-input.d.ts +47 -0
  55. package/dist/components/tag-input.d.ts.map +1 -0
  56. package/dist/components/tag-input.js +211 -0
  57. package/dist/components/tag-input.js.map +1 -0
  58. package/dist/components/time-input.d.ts +75 -0
  59. package/dist/components/time-input.d.ts.map +1 -0
  60. package/dist/components/time-input.js +341 -0
  61. package/dist/components/time-input.js.map +1 -0
  62. package/dist/components/time-select.d.ts +49 -0
  63. package/dist/components/time-select.d.ts.map +1 -0
  64. package/dist/components/time-select.js +178 -0
  65. package/dist/components/time-select.js.map +1 -0
  66. package/dist/components/timeline.d.ts +41 -0
  67. package/dist/components/timeline.d.ts.map +1 -0
  68. package/dist/components/timeline.js +83 -0
  69. package/dist/components/timeline.js.map +1 -0
  70. package/dist/components/toast.d.ts.map +1 -1
  71. package/dist/components/toast.js +4 -2
  72. package/dist/components/toast.js.map +1 -1
  73. package/dist/components/tree.d.ts +87 -0
  74. package/dist/components/tree.d.ts.map +1 -0
  75. package/dist/components/tree.js +516 -0
  76. package/dist/components/tree.js.map +1 -0
  77. package/dist/hooks/use-time-format.d.ts +23 -0
  78. package/dist/hooks/use-time-format.d.ts.map +1 -0
  79. package/dist/hooks/use-time-format.js +52 -0
  80. package/dist/hooks/use-time-format.js.map +1 -0
  81. package/dist/lib/color.d.ts +5 -1
  82. package/dist/lib/color.d.ts.map +1 -1
  83. package/dist/lib/color.js +14 -2
  84. package/dist/lib/color.js.map +1 -1
  85. package/dist/lib/recipes.d.ts +5 -4
  86. package/dist/lib/recipes.d.ts.map +1 -1
  87. package/dist/lib/recipes.js +7 -5
  88. package/dist/lib/recipes.js.map +1 -1
  89. package/package.json +1 -1
  90. package/src/components/alert-dialog.tsx +4 -1
  91. package/src/components/alert.tsx +3 -3
  92. package/src/components/badge.tsx +1 -1
  93. package/src/components/button.tsx +1 -1
  94. package/src/components/calendar.tsx +48 -4
  95. package/src/components/callout.tsx +2 -2
  96. package/src/components/combobox.tsx +1 -1
  97. package/src/components/date-picker.tsx +43 -9
  98. package/src/components/description-list.tsx +107 -0
  99. package/src/components/locale.tsx +67 -0
  100. package/src/components/popover.tsx +3 -1
  101. package/src/components/progress.tsx +70 -4
  102. package/src/components/search-trigger.tsx +8 -2
  103. package/src/components/shimmer.tsx +36 -0
  104. package/src/components/spinner.tsx +126 -23
  105. package/src/components/stepper.tsx +346 -0
  106. package/src/components/tag-input.tsx +345 -0
  107. package/src/components/time-input.tsx +578 -0
  108. package/src/components/time-select.tsx +291 -0
  109. package/src/components/timeline.tsx +190 -0
  110. package/src/components/toast.tsx +4 -4
  111. package/src/components/tree.tsx +832 -0
  112. package/src/hooks/use-time-format.ts +72 -0
  113. package/src/lib/color.ts +19 -2
  114. package/src/lib/recipes.ts +7 -5
  115. package/src/styles/kernelkit.css +56 -0
@@ -0,0 +1,72 @@
1
+ "use client";
2
+
3
+ import {useLocale} from "@repraze/kernelkit/components/locale";
4
+ import * as React from "react";
5
+
6
+ /** `"auto"` follows the locale: 12 in en-US, 24 in fr-FR, en-GB or ja-JP. */
7
+ type HourCyclePreference = 12 | 24 | "auto";
8
+
9
+ interface TimeFormat {
10
+ locale: string;
11
+ hourCycle: 12 | 24;
12
+ /** Localized day periods: "AM" / "PM" in en-US, "a.m." / "p.m." in en-CA, "오전" / "오후" in ko. */
13
+ am: string;
14
+ pm: string;
15
+ /** The day period comes before the hour (ko, zh). */
16
+ periodFirst: boolean;
17
+ /** "09:30" (24h) or "9:30 AM" / "오전 9:30" (12h), in the locale's own pattern. */
18
+ format: (minutes: number) => string;
19
+ /** A two-digit segment in the locale's digits: "05", "٠٥" in ar-EG. */
20
+ digits: (n: number) => string;
21
+ }
22
+
23
+ const pad = (n: number) => String(n).padStart(2, "0");
24
+
25
+ /**
26
+ * Hour cycle, day-period labels and a formatter, from `Intl` for the resolved locale (see `useLocale`:
27
+ * prop › LocaleProvider › browser, hydration-safe). An explicit 12 / 24 overrides the locale's cycle.
28
+ */
29
+ function useTimeFormat(preference: HourCyclePreference = "auto", explicitLocale?: string): TimeFormat {
30
+ const locale = useLocale(explicitLocale);
31
+ return React.useMemo(() => {
32
+ let hourCycle: 12 | 24 = preference === "auto" ? 24 : preference;
33
+ let am = "AM";
34
+ let pm = "PM";
35
+ let periodFirst = false;
36
+ let formatter: Intl.DateTimeFormat | null = null;
37
+ let numbers: Intl.NumberFormat | null = null;
38
+ try {
39
+ numbers = new Intl.NumberFormat(locale, {minimumIntegerDigits: 2, useGrouping: false});
40
+ if (preference === "auto") {
41
+ const hc = new Intl.DateTimeFormat(locale, {hour: "numeric"}).resolvedOptions().hourCycle;
42
+ hourCycle = hc === "h11" || hc === "h12" ? 12 : 24;
43
+ }
44
+ const f = new Intl.DateTimeFormat(locale, {hour: "numeric", hourCycle: "h12"});
45
+ const parts = (h: number) => f.formatToParts(new Date(2000, 0, 1, h));
46
+ am = parts(9).find((p) => p.type === "dayPeriod")?.value ?? am;
47
+ pm = parts(21).find((p) => p.type === "dayPeriod")?.value ?? pm;
48
+ const types = parts(9).map((p) => p.type);
49
+ periodFirst = types.includes("dayPeriod") && types.indexOf("dayPeriod") < types.indexOf("hour");
50
+ formatter = new Intl.DateTimeFormat(locale, {
51
+ hour: hourCycle === 24 ? "2-digit" : "numeric",
52
+ minute: "2-digit",
53
+ hourCycle: hourCycle === 24 ? "h23" : "h12",
54
+ });
55
+ } catch {
56
+ formatter = null;
57
+ }
58
+ const format = (minutes: number) => {
59
+ const h = Math.floor(minutes / 60);
60
+ const m = minutes % 60;
61
+ if (formatter) {
62
+ return formatter.format(new Date(2000, 0, 1, h, m));
63
+ }
64
+ return hourCycle === 24 ? `${pad(h)}:${pad(m)}` : `${h % 12 || 12}:${pad(m)} ${h < 12 ? am : pm}`;
65
+ };
66
+ const digits = (n: number) => (numbers ? numbers.format(n) : pad(n));
67
+ return {locale, hourCycle, am, pm, periodFirst, format, digits};
68
+ }, [preference, locale]);
69
+ }
70
+
71
+ export {useTimeFormat};
72
+ export type {HourCyclePreference, TimeFormat};
package/src/lib/color.ts CHANGED
@@ -6,9 +6,13 @@
6
6
  * - `--c-tint` opaque wash behind foreground text
7
7
  * - `--c-fill` / `--c-fill-fg` vivid solid surface and its text (TermiColor "fills are the brand":
8
8
  * in light mode the text tier is darkened for contrast, the fill tier stays vivid)
9
+ * - `--c-soft-text` text on a wash of its own color (soft variants, soft status surfaces): `--c` mixed 25%
10
+ * toward the foreground with its chroma restored, which keeps hue and saturation and passes WCAG AA
11
+ * 4.5:1 for every color, in light, dark and both hc modes, on background, card and popover (plain `--c`
12
+ * on its own wash fell to ~3.6)
9
13
  *
10
14
  * Spread `colorVariants` into a cva `variants.color` map, then write variants against the vars
11
- * (`bg-(--c) text-(--c-fg)`, `bg-(--c)/12 text-(--c)`, …).
15
+ * (`bg-(--c) text-(--c-fg)`, `bg-(--c)/12 text-(--c-soft-text)`, …).
12
16
  */
13
17
 
14
18
  export const STATUS_COLORS = ["danger", "warning", "success", "info"] as const;
@@ -17,8 +21,17 @@ export const COLORS = ["neutral", "primary", ...STATUS_COLORS, ...HUES] as const
17
21
 
18
22
  export type Color = (typeof COLORS)[number];
19
23
 
24
+ /**
25
+ * Derived from `--c` on the same element, so it is set alongside every color (a :root rule would see no `--c`).
26
+ * The mix toward the (neutral) foreground sets the lightness for contrast but also scales chroma by 0.75, so
27
+ * where relative color syntax is supported the chroma is multiplied back by 1/0.75: same lightness and hue,
28
+ * the original saturation (≥ 5.08:1 everywhere, vs ≥ 5.68 for the plain mix kept as the fallback).
29
+ */
30
+ const SOFT_TEXT =
31
+ "[--c-soft-text:color-mix(in_oklab,var(--c)_75%,var(--foreground))] supports-[color:oklch(from_red_l_c_h)]:[--c-soft-text:oklch(from_color-mix(in_oklab,var(--c)_75%,var(--foreground))_l_calc(c*1.333)_h)]";
32
+
20
33
  // Literal strings on purpose: Tailwind only generates classes it can read in the source.
21
- export const colorVariants: Record<Color, string> = {
34
+ const baseColorVariants: Record<Color, string> = {
22
35
  neutral:
23
36
  "[--c:var(--foreground)] [--c-fg:var(--background)] [--c-tint:var(--secondary)] [--c-fill:var(--foreground)] [--c-fill-fg:var(--background)]",
24
37
  primary:
@@ -39,6 +52,10 @@ export const colorVariants: Record<Color, string> = {
39
52
  pink: "[--c:var(--tc-pink)] [--c-fg:var(--tc-pink-on)] [--c-tint:var(--tc-pink-tint)] [--c-fill:var(--tc-pink-fill)] [--c-fill-fg:var(--tc-pink-on-fill)]",
40
53
  };
41
54
 
55
+ export const colorVariants = Object.fromEntries(
56
+ Object.entries(baseColorVariants).map(([color, vars]) => [color, `${vars} ${SOFT_TEXT}`]),
57
+ ) as Record<Color, string>;
58
+
42
59
  /** Every color except neutral: the ones whose outline/ghost variants are tinted. */
43
60
  export const TINTED_COLORS = COLORS.filter((c) => c !== "neutral");
44
61
 
@@ -55,14 +55,16 @@ export const selectedRow = "bg-accent text-accent-foreground shadow-[inset_2px_0
55
55
 
56
56
  /**
57
57
  * Status surface shared by Alert and Callout (§2): one `soft` / `outline` axis, same values in both.
58
- * Expects a color variant (`--c`, `--c-tint`) on the same element. Neutral swaps the hue border for the
58
+ * Expects a color variant (`--c`, `--c-tint`) on the same element. Text, icons and links on it use
59
+ * `--c-text`, which each variant sets to the color that passes 4.5:1 on that surface. Neutral swaps the hue border for the
59
60
  * plain border on a card surface (`statusSurfaceNeutral`).
60
61
  */
61
62
  export const statusSurface = {
62
- // status tint behind foreground text; the hue lives in the border, icon and accents (TermiColor rule)
63
- soft: "border-(--c)/30 bg-(--c-tint) hc:border-(--c)",
63
+ // status tint behind foreground text; the hue lives in the border, icon and accents (TermiColor rule).
64
+ // `--c-text` is the hue for text, icons and links on it: on the tint it is the soft text (4.5:1)
65
+ soft: "border-(--c)/30 bg-(--c-tint) hc:border-(--c) [--c-text:var(--c-soft-text)]",
64
66
  // no wash: card surface with a hue border, for dense layouts
65
- outline: "border-(--c)/50 bg-card hc:border-(--c)",
67
+ outline: "border-(--c)/50 bg-card hc:border-(--c) [--c-text:var(--c)]",
66
68
  } as const;
67
69
 
68
- export const statusSurfaceNeutral = "border-border bg-card hc:border-border";
70
+ export const statusSurfaceNeutral = "border-border bg-card hc:border-border [--c-text:var(--c)]";
@@ -187,6 +187,62 @@
187
187
  translate: 250% 0;
188
188
  }
189
189
  }
190
+ /* Spinner variants (spinner.tsx) and segmented Progress: cells light up and fade, a glyph strip steps (braille), a cursor
191
+ blinks, equalizer bars bounce, a scan line sweeps. Delays are set per cell. */
192
+ --animate-loader-cell: kk-loader-cell 1.2s ease-in-out infinite;
193
+ @keyframes kk-loader-cell {
194
+ 0%,
195
+ 100% {
196
+ opacity: 0.2;
197
+ }
198
+ 30% {
199
+ opacity: 1;
200
+ }
201
+ 60% {
202
+ opacity: 0.2;
203
+ }
204
+ }
205
+ --animate-loader-braille: kk-loader-braille 0.8s steps(10) infinite;
206
+ @keyframes kk-loader-braille {
207
+ to {
208
+ translate: 0 -100%;
209
+ }
210
+ }
211
+ --animate-loader-blink: kk-loader-blink 1s step-end infinite;
212
+ @keyframes kk-loader-blink {
213
+ 50% {
214
+ opacity: 0;
215
+ }
216
+ }
217
+ --animate-loader-bars: kk-loader-bars 0.9s ease-in-out infinite;
218
+ @keyframes kk-loader-bars {
219
+ 0%,
220
+ 100% {
221
+ scale: 1 0.3;
222
+ }
223
+ 50% {
224
+ scale: 1 1;
225
+ }
226
+ }
227
+ --animate-loader-scan: kk-loader-scan 1.4s linear infinite;
228
+ @keyframes kk-loader-scan {
229
+ from {
230
+ translate: -100% 0;
231
+ }
232
+ to {
233
+ translate: 100% 0;
234
+ }
235
+ }
236
+ /* Shimmer: a highlight band sweeping across clipped text. */
237
+ --animate-shimmer: kk-shimmer 2s linear infinite;
238
+ @keyframes kk-shimmer {
239
+ from {
240
+ background-position: 100% 0;
241
+ }
242
+ to {
243
+ background-position: 0% 0;
244
+ }
245
+ }
190
246
  /* Boxes under 20px (checkbox, kbd keys, tiny chips): capped so they never turn into circles at xl. */
191
247
  --radius-box: min(calc(var(--kk-radius) * 0.5), 0.25rem);
192
248