impact-nova 2.2.9 → 2.3.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.
@@ -1,4 +1,4 @@
1
- import { DEFAULT_DATE_DISPLAY_FORMAT as O, isoToDisplay as S, getTodayStr as b, getYesterdayStr as W, getMonthRange as F, getYearRange as x } from "./date-utils.js";
1
+ import { DEFAULT_DATE_DISPLAY_FORMAT as O, isoToDisplay as C, getTodayStr as b, getYesterdayStr as W, getMonthRange as S, getYearRange as F } from "./date-utils.js";
2
2
  const v = {
3
3
  yes: !0,
4
4
  true: !0,
@@ -96,14 +96,14 @@ function E(i, n = !1, e = !1) {
96
96
  if (a === "today") return { operator: "equals", value: b() };
97
97
  if (a === "yesterday") return { operator: "equals", value: W() };
98
98
  if (/^\d{4}$/.test(t)) {
99
- const r = x(Number(t));
99
+ const r = F(Number(t));
100
100
  return { operator: "inRange", value: r.from, valueTo: r.to };
101
101
  }
102
102
  const o = t.match(/^([a-zA-Z]+)\s*(\d{4})$/);
103
103
  if (o) {
104
104
  const r = $[o[1].toLowerCase()];
105
105
  if (r) {
106
- const l = F(r, Number(o[2]));
106
+ const l = S(r, Number(o[2]));
107
107
  return { operator: "inRange", value: l.from, valueTo: l.to };
108
108
  }
109
109
  }
@@ -137,7 +137,7 @@ function m(i, n = O) {
137
137
  if (Array.isArray(i))
138
138
  return i.map((e) => m(e, n)).join(", ");
139
139
  if (/^\d{4}-\d{2}-\d{2}/.test(i)) {
140
- const e = S(i, n);
140
+ const e = C(i, n);
141
141
  if (e) return e;
142
142
  }
143
143
  return i;
@@ -175,7 +175,7 @@ function N(i, n = O) {
175
175
  return e;
176
176
  }
177
177
  }
178
- const C = {
178
+ const x = {
179
179
  contains: "contains",
180
180
  notContains: "notContains",
181
181
  equals: "equals",
@@ -211,7 +211,7 @@ function A(i, n, e = O) {
211
211
  }
212
212
  const t = n;
213
213
  if ("type" in t && t.type) {
214
- const s = t.type, c = C[s] || s, p = t.filter !== void 0 ? String(t.filter) : t.dateFrom !== void 0 ? String(t.dateFrom) : "", a = t.filterTo !== void 0 ? String(t.filterTo) : t.dateTo !== void 0 ? String(t.dateTo) : void 0;
214
+ const s = t.type, c = x[s] || s, p = t.filter !== void 0 ? String(t.filter) : t.dateFrom !== void 0 ? String(t.dateFrom) : "", a = t.filterTo !== void 0 ? String(t.filterTo) : t.dateTo !== void 0 ? String(t.dateTo) : void 0;
215
215
  return N({
216
216
  operator: c,
217
217
  value: p,
@@ -269,7 +269,7 @@ function h(i) {
269
269
  }
270
270
  }
271
271
  function M(i, n, e) {
272
- return e === "number" ? "number" : e === "date" ? "date" : e === "select" || e === "set" || Array.isArray(n) ? i === "notEqual" || i === "notContains" ? "text" : "set" : typeof n == "string" && /^\d{4}-\d{2}-\d{2}/.test(n) ? "date" : ["greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual", "inRange"].includes(i) && n && !I(n) && !isNaN(Number(n)) || typeof n == "string" && n && /^-?\d+(\.\d+)?$/.test(n.trim()) ? "number" : "text";
272
+ return e === "number" ? "number" : e === "date" ? "date" : e === "text" ? "text" : e === "select" || e === "set" || Array.isArray(n) ? i === "notEqual" || i === "notContains" ? "text" : "set" : typeof n == "string" && /^\d{4}-\d{2}-\d{2}/.test(n) ? "date" : ["greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual", "inRange"].includes(i) && n && !I(n) && !isNaN(Number(n)) || typeof n == "string" && n && /^-?\d+(\.\d+)?$/.test(n.trim()) ? "number" : "text";
273
273
  }
274
274
  function I(i) {
275
275
  return Array.isArray(i);
@@ -443,7 +443,7 @@ function V(i) {
443
443
  return o.conditions.flatMap((d) => p(d));
444
444
  if ("filterType" in o && o.filterType === "multi" && o.filterModels)
445
445
  return o.filterModels?.filter((d) => d != null).flatMap((d) => p(d)) ?? [];
446
- const r = o, l = "type" in r && r.type ? r.type : "equals", u = r.filterType === "set" ? "equals" : C[l] || "contains", f = r.values, g = r.filter !== void 0 ? String(r.filter) : r.dateFrom !== void 0 ? String(r.dateFrom) : f ? f.filter((d) => d != null) : "";
446
+ const r = o, l = "type" in r && r.type ? r.type : "equals", u = r.filterType === "set" ? "equals" : x[l] || "contains", f = r.values, g = r.filter !== void 0 ? String(r.filter) : r.dateFrom !== void 0 ? String(r.dateFrom) : f ? f.filter((d) => d != null) : "";
447
447
  return [{
448
448
  id: R(),
449
449
  columnId: t,
@@ -462,7 +462,7 @@ function V(i) {
462
462
  };
463
463
  }
464
464
  export {
465
- C as agToInternalOperator,
465
+ x as agToInternalOperator,
466
466
  V as convertFromAgGridFilterModel,
467
467
  k as convertToAgGridFilterModel,
468
468
  D as createEmptyRule,
@@ -1,52 +1,53 @@
1
- import { jsx as n, jsxs as k } from "react/jsx-runtime";
1
+ import { jsx as n, jsxs as R } from "react/jsx-runtime";
2
2
  import * as p from "react";
3
3
  import "react-day-picker";
4
4
  import { ChevronRight as C } from "impact-nova-icons";
5
- import { cn as d } from "../../../lib/utils.js";
5
+ import { cn as s } from "../../../lib/utils.js";
6
6
  import { Button as N } from "../../primitives/button/button.js";
7
7
  import { Select as D } from "../../forms/select/select.js";
8
- import { CalendarCaptionHeader as z } from "./calendar-caption-header.js";
9
- import { CalendarDayButton as O } from "./calendar-day-button.js";
8
+ import { CalendarCaptionHeader as Y } from "./calendar-caption-header.js";
9
+ import { CalendarDayButton as z } from "./calendar-day-button.js";
10
10
  import { PanelIndexContext as h } from "./calendar-panel-context.js";
11
- import { CalendarWeekNumberCell as W } from "./calendar-week-number-cell.js";
11
+ import { CalendarWeekNumberCell as O } from "./calendar-week-number-cell.js";
12
12
  import { getAvailableMonths as A } from "./calendar-month-utils.js";
13
- function X({
13
+ import { isCalendarPaddingWeek as F } from "./calendar-padding-week.utils.js";
14
+ function _({
14
15
  buttonVariant: u,
15
16
  pickerType: f,
16
- currentMonth: i,
17
+ currentMonth: d,
17
18
  monthsFull: w,
18
19
  resolvedStartMonth: b,
19
- resolvedEndMonth: I,
20
+ resolvedEndMonth: k,
20
21
  availableYears: v,
21
- weekStartsOn: M,
22
- previousMonthLabel: x,
23
- nextMonthLabel: g,
24
- monthPlaceholder: S,
25
- yearPlaceholder: y,
26
- weekNumberLabel: B,
27
- weekLabel: P,
22
+ weekStartsOn: I,
23
+ previousMonthLabel: g,
24
+ nextMonthLabel: x,
25
+ monthPlaceholder: M,
26
+ yearPlaceholder: P,
27
+ weekNumberLabel: S,
28
+ weekLabel: W,
28
29
  onMonthChange: m,
29
- onWeekSelect: R,
30
- components: Y
30
+ onWeekSelect: y,
31
+ components: B
31
32
  }) {
32
33
  return {
33
- Root: ({ className: e, rootRef: t, ...a }) => /* @__PURE__ */ n("div", { "data-slot": "calendar", ref: t, className: d(e), ...a }),
34
+ Root: ({ className: e, rootRef: t, ...a }) => /* @__PURE__ */ n("div", { "data-slot": "calendar", ref: t, className: s(e), ...a }),
34
35
  Chevron: ({ className: e, orientation: t, ...a }) => t === "left" ? /* @__PURE__ */ n(
35
36
  C,
36
37
  {
37
38
  size: 16,
38
- className: d("rotate-180 text-content", e),
39
+ className: s("rotate-180 text-content", e),
39
40
  ...a
40
41
  }
41
- ) : t === "right" ? /* @__PURE__ */ n(C, { size: 16, className: d("text-content", e), ...a }) : /* @__PURE__ */ n("span", {}),
42
+ ) : t === "right" ? /* @__PURE__ */ n(C, { size: 16, className: s("text-content", e), ...a }) : /* @__PURE__ */ n("span", {}),
42
43
  PreviousMonthButton: (e) => /* @__PURE__ */ n(
43
44
  N,
44
45
  {
45
46
  variant: u,
46
47
  size: "icon",
47
48
  className: "h-7 w-7",
48
- "aria-label": x,
49
- title: x,
49
+ "aria-label": g,
50
+ title: g,
50
51
  ...e
51
52
  }
52
53
  ),
@@ -56,35 +57,35 @@ function X({
56
57
  variant: u,
57
58
  size: "icon",
58
59
  className: "h-7 w-7",
59
- "aria-label": g,
60
- title: g,
60
+ "aria-label": x,
61
+ title: x,
61
62
  ...e
62
63
  }
63
64
  ),
64
65
  MonthCaption: ({ displayIndex: e, calendarMonth: t, ...a }) => /* @__PURE__ */ n(h.Provider, { value: e, children: /* @__PURE__ */ n("div", { ...a }) }),
65
- Month: ({ calendarMonth: e, displayIndex: t, className: a, children: r, ...l }) => {
66
- const o = p.Children.toArray(r), s = o[o.length - 1], c = o.slice(0, -1);
67
- return /* @__PURE__ */ k("div", { className: d(a), ...l, children: [
68
- c.length > 0 ? /* @__PURE__ */ n(z, { children: c }) : null,
69
- s
66
+ Month: ({ calendarMonth: e, displayIndex: t, className: a, children: l, ...r }) => {
67
+ const o = p.Children.toArray(l), i = o[o.length - 1], c = o.slice(0, -1);
68
+ return /* @__PURE__ */ R("div", { className: s(a), ...r, children: [
69
+ c.length > 0 ? /* @__PURE__ */ n(Y, { children: c }) : null,
70
+ i
70
71
  ] });
71
72
  },
72
- Months: ({ className: e, children: t, ...a }) => /* @__PURE__ */ n("div", { "data-slot": "day-months", className: d(e), ...a, children: t }),
73
+ Months: ({ className: e, children: t, ...a }) => /* @__PURE__ */ n("div", { "data-slot": "day-months", className: s(e), ...a, children: t }),
73
74
  MonthsDropdown: ({ value: e }) => {
74
- const t = p.useContext(h), a = t > 0, l = new Date(i.getFullYear(), i.getMonth() + t, 1).getFullYear(), o = A(
75
- l,
75
+ const t = p.useContext(h), a = t > 0, r = new Date(d.getFullYear(), d.getMonth() + t, 1).getFullYear(), o = A(
76
+ r,
76
77
  w,
77
78
  b,
78
- I
79
+ k
79
80
  );
80
81
  return /* @__PURE__ */ n(
81
82
  D,
82
83
  {
83
- value: o.find((s) => s.value === e?.toString()),
84
- onChange: (s) => {
85
- if (s && "value" in s) {
86
- const c = parseInt(s.value);
87
- m(a ? new Date(l, c - 1, 1) : new Date(l, c, 1));
84
+ value: o.find((i) => i.value === e?.toString()),
85
+ onChange: (i) => {
86
+ if (i && "value" in i) {
87
+ const c = parseInt(i.value);
88
+ m(a ? new Date(r, c - 1, 1) : new Date(r, c, 1));
88
89
  }
89
90
  },
90
91
  scrollToSelectedOnOpen: !0,
@@ -93,7 +94,7 @@ function X({
93
94
  menuWidth: "160px",
94
95
  isSearchable: !0,
95
96
  isClearable: !1,
96
- placeholder: S
97
+ placeholder: M
97
98
  }
98
99
  );
99
100
  },
@@ -102,19 +103,19 @@ function X({
102
103
  return /* @__PURE__ */ n(
103
104
  D,
104
105
  {
105
- value: v.find((r) => r.value === e?.toString()),
106
- onChange: (r) => {
107
- if (r && "value" in r) {
108
- const l = parseInt(r.value);
106
+ value: v.find((l) => l.value === e?.toString()),
107
+ onChange: (l) => {
108
+ if (l && "value" in l) {
109
+ const r = parseInt(l.value);
109
110
  if (a) {
110
111
  const o = new Date(
111
- i.getFullYear(),
112
- i.getMonth() + 1,
112
+ d.getFullYear(),
113
+ d.getMonth() + 1,
113
114
  1
114
115
  ).getMonth();
115
- m(new Date(l, o - 1, 1));
116
+ m(new Date(r, o - 1, 1));
116
117
  } else
117
- m(new Date(l, i.getMonth(), 1));
118
+ m(new Date(r, d.getMonth(), 1));
118
119
  }
119
120
  },
120
121
  scrollToSelectedOnOpen: !0,
@@ -123,26 +124,38 @@ function X({
123
124
  menuWidth: "140px",
124
125
  isSearchable: !0,
125
126
  isClearable: !1,
126
- placeholder: y
127
+ placeholder: P
128
+ }
129
+ );
130
+ },
131
+ DayButton: (e) => /* @__PURE__ */ n(z, { ...e, pickerType: f }),
132
+ Week: ({ week: e, className: t, children: a, ...l }) => {
133
+ const r = F(e);
134
+ return /* @__PURE__ */ n(
135
+ "tr",
136
+ {
137
+ className: s(t, r && "invisible"),
138
+ "aria-hidden": r || void 0,
139
+ ...l,
140
+ children: a
127
141
  }
128
142
  );
129
143
  },
130
- DayButton: (e) => /* @__PURE__ */ n(O, { ...e, pickerType: f }),
131
144
  WeekNumber: ({ week: e, ...t }) => /* @__PURE__ */ n(
132
- W,
145
+ O,
133
146
  {
134
147
  week: e,
135
- weekStartsOn: M,
148
+ weekStartsOn: I,
136
149
  pickerType: f,
137
- weekNumberLabel: B,
138
- weekLabel: P,
139
- onWeekSelect: R,
150
+ weekNumberLabel: S,
151
+ weekLabel: W,
152
+ onWeekSelect: y,
140
153
  ...t
141
154
  }
142
155
  ),
143
- ...Y
156
+ ...B
144
157
  };
145
158
  }
146
159
  export {
147
- X as buildCalendarDayPickerComponents
160
+ _ as buildCalendarDayPickerComponents
148
161
  };
@@ -0,0 +1,5 @@
1
+ export declare function isCalendarPaddingWeek(week: {
2
+ days: Array<{
3
+ outside?: boolean;
4
+ }>;
5
+ }): boolean;
@@ -0,0 +1,6 @@
1
+ function n(e) {
2
+ return e.days.length > 0 && e.days.every((d) => d.outside);
3
+ }
4
+ export {
5
+ n as isCalendarPaddingWeek
6
+ };
@@ -54,6 +54,6 @@ export interface CalendarProps extends Omit<React.ComponentProps<typeof DayPicke
54
54
  monthMode?: "single" | "multiple" | "range";
55
55
  selectedMonths?: MonthSelection | MonthSelection[] | MonthRange;
56
56
  onMonthSelect?: (months: MonthSelection | MonthSelection[] | MonthRange | undefined) => void;
57
- /** Always render 6 week rows to prevent layout shift when navigating months. Default true. */
57
+ /** Always render 6 week rows to prevent layout shift when navigating months. Default true. Padding weeks (all days outside the month) are visually hidden via the Week component. */
58
58
  fixedWeeks?: boolean;
59
59
  }
@@ -1,161 +1,170 @@
1
- import { jsx as a, jsxs as we } from "react/jsx-runtime";
2
- import { useId as J, useMemo as _, useState as C, useCallback as c, useRef as E, useEffect as ye } from "react";
3
- import * as O from "@radix-ui/react-popover";
4
- import { createComponent as Ce } from "../../../lib/primitives/create-component.js";
5
- import { cn as P } from "../../../lib/utils.js";
6
- import { useFieldChrome as Ie } from "../../../lib/primitives/use-field-chrome.js";
7
- import { useOverlayNestedLayerDismiss as De, useOverlayPortalContainer as Le } from "../../../lib/overlay/overlay-portal-context.js";
8
- import { OVERLAY_NESTED_FLOATING_INSTANT_HIDE_CLASS as ke } from "../../../lib/overlay/overlay-host.constants.js";
9
- import { comboboxSizeVariants as Ae } from "./combobox.variants.js";
10
- function Se(l) {
1
+ import { jsx as i, jsxs as $ } from "react/jsx-runtime";
2
+ import { useId as Z, useMemo as z, useState as k, useCallback as c, useRef as T, useEffect as ke } from "react";
3
+ import * as E from "@radix-ui/react-popover";
4
+ import { Cross as Le } from "impact-nova-icons";
5
+ import { Tooltip as Ae, TooltipTrigger as Ne, TooltipContent as Se } from "../../feedback/tooltip/tooltip.js";
6
+ import { useImpactNovaI18n as Pe } from "../../../i18n/use-impact-nova-i18n.js";
7
+ import { createComponent as Te } from "../../../lib/primitives/create-component.js";
8
+ import { cn as O } from "../../../lib/utils.js";
9
+ import { useFieldChrome as Ee } from "../../../lib/primitives/use-field-chrome.js";
10
+ import { useOverlayNestedLayerDismiss as Oe, useOverlayPortalContainer as He } from "../../../lib/overlay/overlay-portal-context.js";
11
+ import { OVERLAY_NESTED_FLOATING_INSTANT_HIDE_CLASS as Re } from "../../../lib/overlay/overlay-host.constants.js";
12
+ import { comboboxSizeVariants as Ve } from "./combobox.variants.js";
13
+ function Me(l) {
11
14
  if (l.options) return l.options;
12
- const { min: u, max: d, step: f } = l;
13
- if (u == null || d == null || f == null || f <= 0 || u > d) return [];
14
- const I = [];
15
- for (let b = u; b <= d; b += f) {
16
- const D = Math.round(b * 1e10) / 1e10;
17
- I.push(String(D));
15
+ const { min: u, max: f, step: p } = l;
16
+ if (u == null || f == null || p == null || p <= 0 || u > f) return [];
17
+ const L = [];
18
+ for (let x = u; x <= f; x += p) {
19
+ const v = Math.round(x * 1e10) / 1e10;
20
+ L.push(String(v));
18
21
  }
19
- return I;
22
+ return L;
20
23
  }
21
- function Ne(l, u) {
24
+ function je(l, u) {
22
25
  if (!u) return l;
23
- const d = u.toLowerCase();
24
- return l.filter((f) => f.toLowerCase().includes(d));
26
+ const f = u.toLowerCase();
27
+ return l.filter((p) => p.toLowerCase().includes(f));
25
28
  }
26
- const je = Ce(
29
+ const Je = Te(
27
30
  "Combobox",
28
31
  ({
29
32
  options: l,
30
33
  min: u,
31
- max: d,
32
- step: f,
33
- defaultValue: I = "",
34
- value: b,
35
- onChange: D,
36
- onBlur: F,
37
- placeholder: $,
38
- label: L,
39
- helperText: Q,
40
- helperTextPosition: X = "absolute",
41
- error: Z,
42
- isError: ee,
43
- disabled: m,
34
+ max: f,
35
+ step: p,
36
+ defaultValue: L = "",
37
+ value: x,
38
+ onChange: v,
39
+ onBlur: B,
40
+ placeholder: K,
41
+ label: A,
42
+ helperText: ee,
43
+ helperTextPosition: te = "absolute",
44
+ error: oe,
45
+ isError: ne,
46
+ disabled: d,
44
47
  isNumeric: H,
45
- size: te = "default",
46
- className: oe,
47
- emptyMessage: ne = "No options found",
48
- allowFreeText: k = !1,
49
- required: z,
50
- labelOrientation: re
51
- }, v) => {
52
- const se = J(), A = J(), R = typeof L == "string" ? L : $ ?? "Options", T = Ie({
53
- label: L,
54
- helperText: Q,
55
- helperTextPosition: X,
56
- labelOrientation: re,
57
- error: Z ?? ee,
58
- required: z,
59
- disabled: m,
60
- id: se,
61
- className: oe
62
- }), B = _(
63
- () => Se({ options: l, min: u, max: d, step: f }),
64
- [l, u, d, f]
65
- ), V = b !== void 0, [ie, ae] = C(I), S = V ? b : ie, [ce, K] = C(""), [o, x] = C(!1), le = c((e) => {
66
- x(e);
48
+ size: re = "default",
49
+ className: se,
50
+ emptyMessage: ie = "No options found",
51
+ allowFreeText: N = !1,
52
+ clearable: ae = !1,
53
+ required: U,
54
+ labelOrientation: ce
55
+ }, w) => {
56
+ const le = Z(), S = Z(), { t: W } = Pe(), R = typeof A == "string" ? A : K ?? "Options", V = Ee({
57
+ label: A,
58
+ helperText: ee,
59
+ helperTextPosition: te,
60
+ labelOrientation: ce,
61
+ error: oe ?? ne,
62
+ required: U,
63
+ disabled: d,
64
+ id: le,
65
+ className: se
66
+ }), q = z(
67
+ () => Me({ options: l, min: u, max: f, step: p }),
68
+ [l, u, f, p]
69
+ ), y = x !== void 0, [ue, G] = k(L), C = y ? x : ue, [de, M] = k(""), [o, h] = k(!1), fe = c((e) => {
70
+ h(e);
67
71
  }, []);
68
- De(o && !m, le);
69
- const ue = Le(), n = o ? ce : S, [M, p] = C(-1), U = E(-1), [de, w] = C("keyboard"), W = E(null), N = E(null), fe = c(
72
+ Oe(o && !d, fe);
73
+ const pe = He(), n = o ? de : C, [j, m] = k(-1), Y = T(-1), [me, I] = k("keyboard"), _ = T(null), P = T(null), he = c(
70
74
  (e) => {
71
- W.current = e, typeof v == "function" ? v(e) : v && (v.current = e);
75
+ _.current = e, typeof w == "function" ? w(e) : w && (w.current = e);
72
76
  },
73
- [v]
74
- ), r = _(
75
- () => Ne(B, n),
76
- [B, n]
77
- ), q = E(r), me = _(() => {
77
+ [w]
78
+ ), r = z(
79
+ () => je(q, n),
80
+ [q, n]
81
+ ), J = T(r), ge = z(() => {
78
82
  if (!o || !n) return -1;
79
83
  const e = r.findIndex(
80
84
  (t) => t.toLowerCase() === n.toLowerCase()
81
85
  );
82
86
  return e >= 0 ? e : -1;
83
- }, [o, n, r]), h = M >= 0 ? M : me, G = M >= 0 ? de : "mouse", pe = o && h >= 0 ? `${A}-option-${h}` : void 0;
84
- ye(() => {
85
- U.current = h, q.current = r, o && h >= 0 && N.current && N.current.querySelectorAll("[data-combobox-item]")[h]?.scrollIntoView({ block: "nearest" });
86
- }, [h, o, r]);
87
- const i = c(
87
+ }, [o, n, r]), g = j >= 0 ? j : ge, Q = j >= 0 ? me : "mouse", be = o && g >= 0 ? `${S}-option-${g}` : void 0;
88
+ ke(() => {
89
+ Y.current = g, J.current = r, o && g >= 0 && P.current && P.current.querySelectorAll("[data-combobox-item]")[g]?.scrollIntoView({ block: "nearest" });
90
+ }, [g, o, r]);
91
+ const a = c(
88
92
  (e) => {
89
- V || ae(e), D?.(e), x(!1), p(-1);
93
+ y || G(e), v?.(e), h(!1), m(-1);
90
94
  },
91
- [V, D]
92
- ), y = c(() => {
93
- x(!1), p(-1);
94
- }, []), he = c(
95
+ [y, v]
96
+ ), D = c(() => {
97
+ h(!1), m(-1);
98
+ }, []), xe = c(
99
+ (e) => {
100
+ e.preventDefault(), e.stopPropagation(), y || G(""), v?.(""), M(""), h(!1), m(-1), setTimeout(() => _.current?.focus(), 0);
101
+ },
102
+ [y, v]
103
+ ), ve = ae && !d && C.length > 0, we = c(
95
104
  (e) => {
96
105
  const t = e.target.value;
97
- K(t), p(-1), w("mouse"), o || x(!0);
106
+ M(t), m(-1), I("mouse"), o || h(!0);
98
107
  },
99
108
  [o]
100
- ), ge = c(() => {
101
- m || (K(S), x(!0));
102
- }, [m, S]), be = c(
109
+ ), ye = c(() => {
110
+ d || (M(C), h(!0));
111
+ }, [d, C]), Ce = c(
103
112
  (e) => {
104
113
  const t = e.relatedTarget;
105
- if (!N.current?.contains(t)) {
114
+ if (!P.current?.contains(t)) {
106
115
  if (o) {
107
116
  const s = r.find(
108
- (g) => g.toLowerCase() === n.toLowerCase()
117
+ (b) => b.toLowerCase() === n.toLowerCase()
109
118
  );
110
- s ? i(s) : r.length === 1 ? i(r[0]) : k && n ? i(n) : y();
119
+ s ? a(s) : r.length === 1 ? a(r[0]) : N && n ? a(n) : D();
111
120
  }
112
- F?.(e);
121
+ B?.(e);
113
122
  }
114
123
  },
115
- [o, r, n, i, y, k, F]
116
- ), xe = c(
124
+ [o, r, n, a, D, N, B]
125
+ ), Ie = c(
117
126
  (e) => {
118
127
  if (!o) {
119
- (e.key === "ArrowDown" || e.key === "ArrowUp") && (e.preventDefault(), x(!0));
128
+ (e.key === "ArrowDown" || e.key === "ArrowUp") && (e.preventDefault(), h(!0));
120
129
  return;
121
130
  }
122
131
  if (e.key === "Escape") {
123
- e.preventDefault(), e.stopPropagation(), y();
132
+ e.preventDefault(), e.stopPropagation(), D();
124
133
  return;
125
134
  }
126
135
  if (e.key === "ArrowDown") {
127
- e.preventDefault(), w("keyboard"), p(
136
+ e.preventDefault(), I("keyboard"), m(
128
137
  (t) => t < r.length - 1 ? t + 1 : 0
129
138
  );
130
139
  return;
131
140
  }
132
141
  if (e.key === "ArrowUp") {
133
- e.preventDefault(), w("keyboard"), p(
142
+ e.preventDefault(), I("keyboard"), m(
134
143
  (t) => t > 0 ? t - 1 : r.length - 1
135
144
  );
136
145
  return;
137
146
  }
138
147
  if (e.key === "Enter" || e.key === "Tab") {
139
148
  e.preventDefault();
140
- const t = U.current, s = q.current;
149
+ const t = Y.current, s = J.current;
141
150
  if (t >= 0 && t < s.length)
142
- i(s[t]);
151
+ a(s[t]);
143
152
  else {
144
- const g = s.find(
145
- (j) => j.toLowerCase() === n.toLowerCase()
153
+ const b = s.find(
154
+ (F) => F.toLowerCase() === n.toLowerCase()
146
155
  );
147
- g ? i(g) : s.length === 1 ? i(s[0]) : k && n ? i(n) : y();
156
+ b ? a(b) : s.length === 1 ? a(s[0]) : N && n ? a(n) : D();
148
157
  }
149
158
  return;
150
159
  }
151
160
  },
152
- [o, r, n, i, y, k]
153
- ), Y = c(
161
+ [o, r, n, a, D, N]
162
+ ), X = c(
154
163
  (e) => {
155
- i(e), setTimeout(() => W.current?.focus(), 0);
164
+ a(e), setTimeout(() => _.current?.focus(), 0);
156
165
  },
157
- [i]
158
- ), ve = c(
166
+ [a]
167
+ ), De = c(
159
168
  (e) => {
160
169
  const t = e.target.closest(
161
170
  "[data-combobox-item]"
@@ -164,66 +173,82 @@ const je = Ce(
164
173
  if (e.button !== 0) return;
165
174
  e.preventDefault();
166
175
  const s = t.getAttribute("data-combobox-value");
167
- s != null && Y(s);
176
+ s != null && X(s);
168
177
  return;
169
178
  }
170
179
  e.preventDefault();
171
180
  },
172
- [Y]
181
+ [X]
173
182
  );
174
- return /* @__PURE__ */ a("div", { "data-component": "combobox", "data-numeric": H || void 0, children: T.wrapControl(
175
- /* @__PURE__ */ we(O.Root, { open: o && !m, modal: !1, children: [
176
- /* @__PURE__ */ a(O.Anchor, { asChild: !0, children: /* @__PURE__ */ a(
183
+ return /* @__PURE__ */ i("div", { "data-component": "combobox", "data-numeric": H || void 0, children: V.wrapControl(
184
+ /* @__PURE__ */ $(E.Root, { open: o && !d, modal: !1, children: [
185
+ /* @__PURE__ */ i(E.Anchor, { asChild: !0, children: /* @__PURE__ */ $(
177
186
  "div",
178
187
  {
179
188
  "data-component": "combobox-input-border",
180
189
  tabIndex: -1,
181
- className: P(
190
+ className: O(
182
191
  "flex w-full items-center gap-1 rounded-md border bg-transparent px-3 text-sm transition-colors outline-none ring-0 focus:outline-none focus:ring-0",
183
192
  "focus-within:outline-none focus-within:ring-0 focus-within:border-brand hover:border-brand",
184
- T.hasError ? "border-destructive hover:border-destructive" : "border-field",
185
- m && "pointer-events-none opacity-100 bg-disabled-surface border-stroke text-disabled-foreground hover:border-stroke",
186
- Ae({ size: te }),
193
+ V.hasError ? "border-destructive hover:border-destructive" : "border-field",
194
+ d && "pointer-events-none opacity-100 bg-disabled-surface border-stroke text-disabled-foreground hover:border-stroke",
195
+ Ve({ size: re }),
187
196
  "bg-canvas"
188
197
  ),
189
- children: /* @__PURE__ */ a(
190
- "input",
191
- {
192
- ref: fe,
193
- type: "text",
194
- value: n,
195
- onChange: he,
196
- onFocus: ge,
197
- onBlur: be,
198
- onKeyDown: xe,
199
- placeholder: $,
200
- disabled: !!m,
201
- className: P(
202
- "flex h-full w-full bg-transparent p-0 text-sm font-medium",
203
- "outline-none ring-0 shadow-none border-none",
204
- "focus:outline-none focus:ring-0 focus:shadow-none focus:border-none",
205
- "focus-visible:outline-none focus-visible:ring-0 focus-visible:shadow-none focus-visible:border-none",
206
- "placeholder:font-medium placeholder:text-content-empty",
207
- m && "opacity-100 text-disabled-foreground placeholder:text-disabled-foreground cursor-not-allowed",
208
- H && "text-right"
209
- ),
210
- style: { boxShadow: "none", outline: "none" },
211
- autoComplete: "off",
212
- role: "combobox",
213
- "aria-expanded": o,
214
- "aria-autocomplete": "list",
215
- "aria-haspopup": "listbox",
216
- "aria-controls": o ? A : void 0,
217
- "aria-activedescendant": pe,
218
- "aria-required": z || void 0,
219
- "aria-label": L ? void 0 : R,
220
- ...T.controlAria
221
- }
222
- )
198
+ children: [
199
+ /* @__PURE__ */ i(
200
+ "input",
201
+ {
202
+ ref: he,
203
+ type: "text",
204
+ value: n,
205
+ onChange: we,
206
+ onFocus: ye,
207
+ onBlur: Ce,
208
+ onKeyDown: Ie,
209
+ placeholder: K,
210
+ disabled: !!d,
211
+ className: O(
212
+ "flex h-full w-full bg-transparent p-0 text-sm font-medium",
213
+ "outline-none ring-0 shadow-none border-none",
214
+ "focus:outline-none focus:ring-0 focus:shadow-none focus:border-none",
215
+ "focus-visible:outline-none focus-visible:ring-0 focus-visible:shadow-none focus-visible:border-none",
216
+ "placeholder:font-medium placeholder:text-content-empty",
217
+ d && "opacity-100 text-disabled-foreground placeholder:text-disabled-foreground cursor-not-allowed",
218
+ H && "text-right"
219
+ ),
220
+ style: { boxShadow: "none", outline: "none" },
221
+ autoComplete: "off",
222
+ role: "combobox",
223
+ "aria-expanded": o,
224
+ "aria-autocomplete": "list",
225
+ "aria-haspopup": "listbox",
226
+ "aria-controls": o ? S : void 0,
227
+ "aria-activedescendant": be,
228
+ "aria-required": U || void 0,
229
+ "aria-label": A ? void 0 : R,
230
+ ...V.controlAria
231
+ }
232
+ ),
233
+ ve ? /* @__PURE__ */ $(Ae, { children: [
234
+ /* @__PURE__ */ i(Ne, { asChild: !0, children: /* @__PURE__ */ i(
235
+ "button",
236
+ {
237
+ type: "button",
238
+ "data-clear-button": !0,
239
+ "aria-label": W("select.clear"),
240
+ className: "inline-flex shrink-0 cursor-pointer border-none bg-transparent p-0.5 text-content-icon hover:text-content-secondary",
241
+ onPointerDown: xe,
242
+ children: /* @__PURE__ */ i(Le, { size: "11px", className: "text-content-icon" })
243
+ }
244
+ ) }),
245
+ /* @__PURE__ */ i(Se, { variant: "tertiary", side: "top", children: W("select.clear") })
246
+ ] }) : null
247
+ ]
223
248
  }
224
249
  ) }),
225
- /* @__PURE__ */ a(O.Portal, { container: ue, children: /* @__PURE__ */ a(
226
- O.Content,
250
+ /* @__PURE__ */ i(E.Portal, { container: pe, children: /* @__PURE__ */ i(
251
+ E.Content,
227
252
  {
228
253
  align: "start",
229
254
  side: "bottom",
@@ -237,46 +262,46 @@ const je = Ce(
237
262
  onInteractOutside: () => {
238
263
  },
239
264
  onWheel: (e) => e.stopPropagation(),
240
- className: P(
265
+ className: O(
241
266
  "z-[9999] overflow-hidden rounded-[12px] bg-canvas text-base shadow-elevation-select focus:outline-none sm:text-sm flex flex-col border-none w-[var(--radix-popover-trigger-width)]",
242
- ke
267
+ Re
243
268
  ),
244
269
  style: {
245
270
  maxHeight: "var(--radix-popover-content-available-height)"
246
271
  },
247
- children: /* @__PURE__ */ a(
272
+ children: /* @__PURE__ */ i(
248
273
  "div",
249
274
  {
250
- ref: N,
251
- id: A,
275
+ ref: P,
276
+ id: S,
252
277
  className: "flex-1 overflow-auto py-1 px-[6px]",
253
278
  style: { maxHeight: 240, overscrollBehavior: "contain" },
254
279
  role: "listbox",
255
280
  "aria-label": R,
256
- onPointerDown: ve,
281
+ onPointerDown: De,
257
282
  onWheel: (e) => e.stopPropagation(),
258
283
  onMouseLeave: () => {
259
- w("mouse"), p(-1);
284
+ I("mouse"), m(-1);
260
285
  },
261
- children: r.length === 0 ? /* @__PURE__ */ a("div", { className: "flex items-center justify-center py-6 text-sm text-secondary-foreground", children: ne }) : r.map((e, t) => {
262
- const s = t === h, g = e.toLowerCase() === S.toLowerCase(), j = `${A}-option-${t}`;
263
- return /* @__PURE__ */ a("div", { className: "w-full pb-[2px]", children: /* @__PURE__ */ a(
286
+ children: r.length === 0 ? /* @__PURE__ */ i("div", { className: "flex items-center justify-center py-6 text-sm text-secondary-foreground", children: ie }) : r.map((e, t) => {
287
+ const s = t === g, b = e.toLowerCase() === C.toLowerCase(), F = `${S}-option-${t}`;
288
+ return /* @__PURE__ */ i("div", { className: "w-full pb-[2px]", children: /* @__PURE__ */ i(
264
289
  "div",
265
290
  {
266
291
  "data-combobox-item": !0,
267
292
  "data-combobox-value": e,
268
- id: j,
293
+ id: F,
269
294
  role: "option",
270
- "aria-selected": g,
271
- className: P(
295
+ "aria-selected": b,
296
+ className: O(
272
297
  "cursor-default select-none py-[6px] px-3 flex items-center transition-colors rounded-md",
273
298
  H ? "justify-end text-right" : "justify-between",
274
- s && G === "keyboard" ? "ring-2 ring-inset ring-primary bg-accent text-content" : s && G === "mouse" ? "bg-canvas-muted text-content" : g ? "bg-accent text-content" : "text-content hover:bg-canvas-muted cursor-pointer"
299
+ s && Q === "keyboard" ? "ring-2 ring-inset ring-primary bg-accent text-content" : s && Q === "mouse" ? "bg-canvas-muted text-content" : b ? "bg-accent text-content" : "text-content hover:bg-canvas-muted cursor-pointer"
275
300
  ),
276
301
  onMouseEnter: () => {
277
- w("mouse"), p(t);
302
+ I("mouse"), m(t);
278
303
  },
279
- children: /* @__PURE__ */ a("span", { className: "text-sm", children: e })
304
+ children: /* @__PURE__ */ i("span", { className: "text-sm", children: e })
280
305
  }
281
306
  ) }, e);
282
307
  })
@@ -289,5 +314,5 @@ const je = Ce(
289
314
  }
290
315
  );
291
316
  export {
292
- je as Combobox
317
+ Je as Combobox
293
318
  };
@@ -36,4 +36,6 @@ export interface ComboboxProps extends FieldControlProps {
36
36
  emptyMessage?: string;
37
37
  /** If true, typed text is committed even when it doesn't match any option */
38
38
  allowFreeText?: boolean;
39
+ /** When true, shows a clear button to reset the committed value */
40
+ clearable?: boolean;
39
41
  }
@@ -1 +1 @@
1
- export default "# Install and configure Impact Nova\n\nUse these steps to add Impact Nova to an existing project. Run the commands in your project root and apply the code changes to your entry file and layout.\n\n## 0. Scaffold a new app (recommended)\n\n**`create-impact-nova@^1.4.5`** is on npm. Scaffold auto-detects dependency mode:\n\n| Where you run | `impact-nova` dependency |\n|---------------|--------------------------|\n| Published CLI / MCP (outside monorepo) | `impact-nova@^2.2.9` from npm (`latest` tag) |\n| Inside impact-nova monorepo | `file:` link to repo root |\n\n```bash\nnpx create-impact-nova my-dashboard\ncd my-dashboard\nnpm run dev\n```\n\nMCP: **`scaffold_impact_nova_app`** with `projectName` and optional `modules[]` from the user prompt.\n\n**Overrides:** CLI `--from-npm` · `--link-monorepo` · MCP `usePublishedPackages: true | false`\n\n```bash\nnpx create-impact-nova my-dashboard --from-npm\n```\n\nBase template is shell-only (Home + shared primitives). Feature pages come from recipes — see `create-impact-nova/recipes/README.md`.\n\n**Local link in an existing app** (manual):\n\n```bash\nnpm install impact-nova@file:../path-to-impact-nova-repo\n```\n\nRun `npm run build` in the impact-nova repo after design-system changes.\n\n## 1. Install the package and peer dependencies\n\n```bash\nnpm install impact-nova impact-nova-icons\nnpm install react@^19 react-dom@^19\n```\n\n**Required peers:** `react@^19`, `react-dom@^19`, `impact-nova-icons@^0.1.1`\n\n**Optional peers** (install when you use these features):\n\n| Package | Version | When needed |\n|---------|---------|-------------|\n| `ag-grid-community` | `36.0.1` | AG Grid types/API (pin with react + enterprise) |\n| `ag-grid-react` | `36.0.1` | DataTable / AG Grid |\n| `ag-grid-enterprise` | `36.0.1` | Enterprise grid features |\n| `highcharts` | `^12` | Chart component |\n| `highcharts-react-official` | `^3` | Chart component |\n| `highcharts-border-radius` | `^0.0.4` | Rounded chart corners |\n\nIf you use AG Grid or Highcharts in your app, also install:\n\n```bash\nnpm install ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1\n# Optional, for charts:\nnpm install highcharts@^12 highcharts-react-official@^3 highcharts-border-radius@^0.0.4\n```\n\n## 2. Import styles in your root entry\n\nIn your app root (e.g. `main.tsx`, `App.tsx`, or `layout.tsx`), **import the CSS before your app component**:\n\n```tsx\nimport 'impact-nova/dist/impact-nova.css';\nimport App from './App';\n// ... rest of your bootstrap\n```\n\n## 3. Add Manrope font (recommended)\n\nIn your `index.html` or layout:\n\n```html\n<link href=\"https://fonts.googleapis.com/css2?family=Manrope:wght@200..800&display=swap\" rel=\"stylesheet\" />\n```\n\nIn your global CSS or Tailwind base:\n\n```css\n@layer base {\n body {\n font-family: 'Manrope', sans-serif;\n }\n}\n```\n\n## 4. Wrap your app with providers (recommended)\n\nMount **`ImpactNovaProviders`** once at the app root. It bundles i18n (`ImpactNovaI18nProvider`) and `TooltipProvider` (required for tooltips since v2.0.8):\n\n```tsx\nimport { ImpactNovaProviders } from 'impact-nova/form';\n\nfunction Root() {\n return (\n <ImpactNovaProviders locale=\"en\">\n <App />\n </ImpactNovaProviders>\n );\n}\n```\n\nFor another locale, pass a locale pack:\n\n```tsx\nimport { ImpactNovaProviders } from 'impact-nova/form';\nimport { de } from 'impact-nova/locale/de';\n\n<ImpactNovaProviders locale=\"de\" messages={de}>\n <App />\n</ImpactNovaProviders>\n```\n\n**Barrel exception:** locale packs may use `import { de } from 'impact-nova'`. All UI components still use subpaths (see §5).\n\n## 5. Subpath imports (recommended)\n\nPrefer subpath imports in feature code for tree-shaking:\n\n```tsx\nimport { Button } from 'impact-nova/button';\nimport { Card, CardContent } from 'impact-nova/card';\nimport { DataTable, DataTableContent, useDataTable } from 'impact-nova/data-table';\nimport { CheckCircle } from 'impact-nova-icons';\n```\n\nThe barrel `import { Button } from 'impact-nova'` still works but pulls a larger module graph. **Use subpaths for all UI components**; reserve the barrel for locale packs at app root only. Use `ImpactNovaProviders` from `impact-nova/form` for app setup.\n\n## 6. Using Impact Nova with CSS or SCSS (no Tailwind)\n\nIf your app uses **plain CSS or SCSS** and does **not** use Tailwind, you can still use Impact Nova. Tailwind is optional. Impact Nova ships a single built CSS file (no SCSS source).\n\n- **Import the library CSS** in your app root (e.g. `main.tsx`, `App.tsx`):\n ```tsx\n import 'impact-nova/dist/impact-nova.css';\n ```\n **CSS split exports** (optional, for layered theming): `impact-nova/impact-nova-tokens.scss`, `impact-nova/impact-nova-base.scss`, `impact-nova/impact-nova-components.css`\n Or from your main stylesheet: `@import 'impact-nova/dist/impact-nova.css';` in your main.css or main.scss.\n- **Component styling** comes from Impact Nova's CSS. Use each component's `variant` and `size` props; no Tailwind classes are required.\n- **Your layout and spacing** stay in your CSS or SCSS (margins, padding, flexbox, grid). Use your usual class names; the design system does not require Tailwind utilities.\n- **Manrope font:** Add the font link in `index.html` and in your CSS/SCSS set `body { font-family: 'Manrope', sans-serif; }` (see section 3).\n- **Summary:** No Tailwind, no Tailwind config. Load `impact-nova.css` once; use components with their props; use your existing CSS/SCSS for the rest.\n\n## 6b. Theming in consumer apps\n\n| Surface | What ships |\n|---------|------------|\n| **npm / consumer apps** | **Impact Light** only — `import 'impact-nova/dist/impact-nova.css'` (from `src/theme/tokens/light.json`) |\n| **Storybook / dev** | 10 curated **theme presets** (Impact Dark, Midnight, Ocean, …) — preview switcher only |\n\n**Do not** copy Storybook preset JSON or `theme-presets.ts` into consumer apps. Presets are not in the published package.\n\n**Custom branding:** override CSS variables on `:root` or a scoped class (e.g. `.my-brand`). Use Storybook **Theme Builder** / **Token Catalog** to explore tokens, then export CSS/JSON for your overrides.\n\n## 7. Using Impact Nova with Webpack\n\nIf your app is built with **Webpack** (including Create React App, which uses Webpack):\n\n- **CSS import:** Webpack will bundle the library CSS as long as you have a rule for `.css`. Ensure your config includes something like:\n ```js\n module: {\n rules: [\n {\n test: /\\.css$/,\n use: ['style-loader', 'css-loader'],\n // or with MiniCssExtractPlugin:\n // use: [MiniCssExtractPlugin.loader, 'css-loader'],\n },\n // ... other rules (e.g. for .tsx, .js)\n ],\n },\n ```\n Then in your root component or entry:\n ```tsx\n import 'impact-nova/dist/impact-nova.css';\n ```\n- **Create React App (CRA):** No extra config. Add `import 'impact-nova/dist/impact-nova.css';` in `index.tsx` or `App.tsx`.\n- **Dedupe React / AG Grid:** If you see “multiple copies of React” or AG Grid issues, use Webpack’s `resolve.alias` so the app uses a single version:\n ```js\n resolve: {\n alias: {\n react: path.resolve(__dirname, 'node_modules/react'),\n 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),\n // If using AG Grid:\n 'ag-grid-react': path.resolve(__dirname, 'node_modules/ag-grid-react'),\n 'ag-grid-community': path.resolve(__dirname, 'node_modules/ag-grid-community'),\n 'ag-grid-enterprise': path.resolve(__dirname, 'node_modules/ag-grid-enterprise'),\n },\n },\n ```\n- **Tailwind + Webpack:** If you use Tailwind, add the Impact Nova preset and include the package in `content` (see section 7 below).\n\n## 8. Optional: extend Tailwind with Impact Nova preset\n\nIf your project uses Tailwind and you want the same theme (colors, radius), extend your `tailwind.config.js` with the preset from the package:\n\n```js\n// tailwind.config.js\nimport impactNovaPreset from 'impact-nova/tailwind.config.js';\n// → dist/tailwind.config.js (bundled theme; no package src/ required)\n\nexport default {\n presets: [impactNovaPreset],\n content: [\n './index.html',\n './src/**/*.{js,ts,jsx,tsx}',\n './node_modules/impact-nova/dist/**/*.js',\n ],\n // ... rest of your config\n};\n```\n\n## Summary checklist\n\n- [ ] `npm install impact-nova impact-nova-icons` (and react, react-dom; optional: ag-grid, highcharts)\n- [ ] `import 'impact-nova/dist/impact-nova.css'` in root entry (or @import in main.css/main.scss if using CSS or SCSS without Tailwind; see §6)\n- [ ] Add Manrope font link and body font-family\n- [ ] Wrap app with `<ImpactNovaProviders locale=\"en\">` from `impact-nova/form`\n- [ ] Use subpath imports in feature code (see §5)\n- [ ] (Optional) Add Tailwind preset and content path for impact-nova\n- [ ] **Webpack:** Ensure a CSS rule (style-loader + css-loader) and, if needed, resolve.alias for React/AG Grid\n\nAfter this, import components via subpaths: `import { Button } from 'impact-nova/button';`\n\n## 9. Dashboard app shell (optional)\n\nTo scaffold a full dashboard (sidebar, header, breadcrumb, filters), read resource **`impact-nova://layout`** or call `get_real_world_patterns` with `topic: \"layout\"`. Use `Layout` from `impact-nova/layout` — not `DynamicLayout` (grid/flex utility only).\n";
1
+ export default "# Install and configure Impact Nova\n\nUse these steps to add Impact Nova to an existing project. Run the commands in your project root and apply the code changes to your entry file and layout.\n\n## 0. Scaffold a new app (recommended)\n\n**`create-impact-nova@^1.4.5`** is on npm. Scaffold auto-detects dependency mode:\n\n| Where you run | `impact-nova` dependency |\n|---------------|--------------------------|\n| Published CLI / MCP (outside monorepo) | `impact-nova@^2.3.0` from npm (`latest` tag) |\n| Inside impact-nova monorepo | `file:` link to repo root |\n\n```bash\nnpx create-impact-nova my-dashboard\ncd my-dashboard\nnpm run dev\n```\n\nMCP: **`scaffold_impact_nova_app`** with `projectName` and optional `modules[]` from the user prompt.\n\n**Overrides:** CLI `--from-npm` · `--link-monorepo` · MCP `usePublishedPackages: true | false`\n\n```bash\nnpx create-impact-nova my-dashboard --from-npm\n```\n\nBase template is shell-only (Home + shared primitives). Feature pages come from recipes — see `create-impact-nova/recipes/README.md`.\n\n**Local link in an existing app** (manual):\n\n```bash\nnpm install impact-nova@file:../path-to-impact-nova-repo\n```\n\nRun `npm run build` in the impact-nova repo after design-system changes.\n\n## 1. Install the package and peer dependencies\n\n```bash\nnpm install impact-nova impact-nova-icons\nnpm install react@^19 react-dom@^19\n```\n\n**Required peers:** `react@^19`, `react-dom@^19`, `impact-nova-icons@^0.1.1`\n\n**Optional peers** (install when you use these features):\n\n| Package | Version | When needed |\n|---------|---------|-------------|\n| `ag-grid-community` | `36.0.1` | AG Grid types/API (pin with react + enterprise) |\n| `ag-grid-react` | `36.0.1` | DataTable / AG Grid |\n| `ag-grid-enterprise` | `36.0.1` | Enterprise grid features |\n| `highcharts` | `^12` | Chart component |\n| `highcharts-react-official` | `^3` | Chart component |\n| `highcharts-border-radius` | `^0.0.4` | Rounded chart corners |\n\nIf you use AG Grid or Highcharts in your app, also install:\n\n```bash\nnpm install ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1\n# Optional, for charts:\nnpm install highcharts@^12 highcharts-react-official@^3 highcharts-border-radius@^0.0.4\n```\n\n## 2. Import styles in your root entry\n\nIn your app root (e.g. `main.tsx`, `App.tsx`, or `layout.tsx`), **import the CSS before your app component**:\n\n```tsx\nimport 'impact-nova/dist/impact-nova.css';\nimport App from './App';\n// ... rest of your bootstrap\n```\n\n## 3. Add Manrope font (recommended)\n\nIn your `index.html` or layout:\n\n```html\n<link href=\"https://fonts.googleapis.com/css2?family=Manrope:wght@200..800&display=swap\" rel=\"stylesheet\" />\n```\n\nIn your global CSS or Tailwind base:\n\n```css\n@layer base {\n body {\n font-family: 'Manrope', sans-serif;\n }\n}\n```\n\n## 4. Wrap your app with providers (recommended)\n\nMount **`ImpactNovaProviders`** once at the app root. It bundles i18n (`ImpactNovaI18nProvider`) and `TooltipProvider` (required for tooltips since v2.0.8):\n\n```tsx\nimport { ImpactNovaProviders } from 'impact-nova/form';\n\nfunction Root() {\n return (\n <ImpactNovaProviders locale=\"en\">\n <App />\n </ImpactNovaProviders>\n );\n}\n```\n\nFor another locale, pass a locale pack:\n\n```tsx\nimport { ImpactNovaProviders } from 'impact-nova/form';\nimport { de } from 'impact-nova/locale/de';\n\n<ImpactNovaProviders locale=\"de\" messages={de}>\n <App />\n</ImpactNovaProviders>\n```\n\n**Barrel exception:** locale packs may use `import { de } from 'impact-nova'`. All UI components still use subpaths (see §5).\n\n## 5. Subpath imports (recommended)\n\nPrefer subpath imports in feature code for tree-shaking:\n\n```tsx\nimport { Button } from 'impact-nova/button';\nimport { Card, CardContent } from 'impact-nova/card';\nimport { DataTable, DataTableContent, useDataTable } from 'impact-nova/data-table';\nimport { CheckCircle } from 'impact-nova-icons';\n```\n\nThe barrel `import { Button } from 'impact-nova'` still works but pulls a larger module graph. **Use subpaths for all UI components**; reserve the barrel for locale packs at app root only. Use `ImpactNovaProviders` from `impact-nova/form` for app setup.\n\n## 6. Using Impact Nova with CSS or SCSS (no Tailwind)\n\nIf your app uses **plain CSS or SCSS** and does **not** use Tailwind, you can still use Impact Nova. Tailwind is optional. Impact Nova ships a single built CSS file (no SCSS source).\n\n- **Import the library CSS** in your app root (e.g. `main.tsx`, `App.tsx`):\n ```tsx\n import 'impact-nova/dist/impact-nova.css';\n ```\n **CSS split exports** (optional, for layered theming): `impact-nova/impact-nova-tokens.scss`, `impact-nova/impact-nova-base.scss`, `impact-nova/impact-nova-components.css`\n Or from your main stylesheet: `@import 'impact-nova/dist/impact-nova.css';` in your main.css or main.scss.\n- **Component styling** comes from Impact Nova's CSS. Use each component's `variant` and `size` props; no Tailwind classes are required.\n- **Your layout and spacing** stay in your CSS or SCSS (margins, padding, flexbox, grid). Use your usual class names; the design system does not require Tailwind utilities.\n- **Manrope font:** Add the font link in `index.html` and in your CSS/SCSS set `body { font-family: 'Manrope', sans-serif; }` (see section 3).\n- **Summary:** No Tailwind, no Tailwind config. Load `impact-nova.css` once; use components with their props; use your existing CSS/SCSS for the rest.\n\n## 6b. Theming in consumer apps\n\n| Surface | What ships |\n|---------|------------|\n| **npm / consumer apps** | **Impact Light** only — `import 'impact-nova/dist/impact-nova.css'` (from `src/theme/tokens/light.json`) |\n| **Storybook / dev** | 10 curated **theme presets** (Impact Dark, Midnight, Ocean, …) — preview switcher only |\n\n**Do not** copy Storybook preset JSON or `theme-presets.ts` into consumer apps. Presets are not in the published package.\n\n**Custom branding:** override CSS variables on `:root` or a scoped class (e.g. `.my-brand`). Use Storybook **Theme Builder** / **Token Catalog** to explore tokens, then export CSS/JSON for your overrides.\n\n## 7. Using Impact Nova with Webpack\n\nIf your app is built with **Webpack** (including Create React App, which uses Webpack):\n\n- **CSS import:** Webpack will bundle the library CSS as long as you have a rule for `.css`. Ensure your config includes something like:\n ```js\n module: {\n rules: [\n {\n test: /\\.css$/,\n use: ['style-loader', 'css-loader'],\n // or with MiniCssExtractPlugin:\n // use: [MiniCssExtractPlugin.loader, 'css-loader'],\n },\n // ... other rules (e.g. for .tsx, .js)\n ],\n },\n ```\n Then in your root component or entry:\n ```tsx\n import 'impact-nova/dist/impact-nova.css';\n ```\n- **Create React App (CRA):** No extra config. Add `import 'impact-nova/dist/impact-nova.css';` in `index.tsx` or `App.tsx`.\n- **Dedupe React / AG Grid:** If you see “multiple copies of React” or AG Grid issues, use Webpack’s `resolve.alias` so the app uses a single version:\n ```js\n resolve: {\n alias: {\n react: path.resolve(__dirname, 'node_modules/react'),\n 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),\n // If using AG Grid:\n 'ag-grid-react': path.resolve(__dirname, 'node_modules/ag-grid-react'),\n 'ag-grid-community': path.resolve(__dirname, 'node_modules/ag-grid-community'),\n 'ag-grid-enterprise': path.resolve(__dirname, 'node_modules/ag-grid-enterprise'),\n },\n },\n ```\n- **Tailwind + Webpack:** If you use Tailwind, add the Impact Nova preset and include the package in `content` (see section 7 below).\n\n## 8. Optional: extend Tailwind with Impact Nova preset\n\nIf your project uses Tailwind and you want the same theme (colors, radius), extend your `tailwind.config.js` with the preset from the package:\n\n```js\n// tailwind.config.js\nimport impactNovaPreset from 'impact-nova/tailwind.config.js';\n// → dist/tailwind.config.js (bundled theme; no package src/ required)\n\nexport default {\n presets: [impactNovaPreset],\n content: [\n './index.html',\n './src/**/*.{js,ts,jsx,tsx}',\n './node_modules/impact-nova/dist/**/*.js',\n ],\n // ... rest of your config\n};\n```\n\n## Summary checklist\n\n- [ ] `npm install impact-nova impact-nova-icons` (and react, react-dom; optional: ag-grid, highcharts)\n- [ ] `import 'impact-nova/dist/impact-nova.css'` in root entry (or @import in main.css/main.scss if using CSS or SCSS without Tailwind; see §6)\n- [ ] Add Manrope font link and body font-family\n- [ ] Wrap app with `<ImpactNovaProviders locale=\"en\">` from `impact-nova/form`\n- [ ] Use subpath imports in feature code (see §5)\n- [ ] (Optional) Add Tailwind preset and content path for impact-nova\n- [ ] **Webpack:** Ensure a CSS rule (style-loader + css-loader) and, if needed, resolve.alias for React/AG Grid\n\nAfter this, import components via subpaths: `import { Button } from 'impact-nova/button';`\n\n## 9. Dashboard app shell (optional)\n\nTo scaffold a full dashboard (sidebar, header, breadcrumb, filters), read resource **`impact-nova://layout`** or call `get_real_world_patterns` with `topic: \"layout\"`. Use `Layout` from `impact-nova/layout` — not `DynamicLayout` (grid/flex utility only).\n";
@@ -1 +1 @@
1
- export default "# Impact Nova — baseline\n\nUse **Impact Nova** design-system components and tokens. Prefer subpath imports (e.g. `impact-nova/button`) when you only need a few modules. For live component lists, specs, and codegen, use the **impact-nova-mcp** MCP server (see npm `impact-nova-mcp`). Browse **Storybook**: https://impact-nova.iaproducts.ai/\n\n## Peer dependency summary\n\n```json\n{\n \"impactNova\": {\n \"package\": \"impact-nova\",\n \"recommendedVersion\": \"2.2.9\",\n \"recommendedDistTag\": \"latest\",\n \"description\": \"Design system package — ESM with 90+ subpath exports. Install: npm install impact-nova@latest\"\n },\n \"createImpactNova\": {\n \"package\": \"create-impact-nova\",\n \"recommendedVersion\": \"^1.4.5\",\n \"description\": \"Vite + React scaffold with recipe-based modules\"\n },\n \"localDevelopment\": {\n \"defaultScaffoldDependency\": \"auto\",\n \"description\": \"create-impact-nova auto-detects: file: link when run inside the impact-nova monorepo; npm (impact-nova@^2.2.9) otherwise. CLI: --from-npm or --link-monorepo to override. Rebuild impact-nova (npm run build) after DS changes when using file: link.\",\n \"cliFlagForNpm\": \"--from-npm\",\n \"cliFlagForMonorepo\": \"--link-monorepo\",\n \"mcpScaffoldFlag\": \"usePublishedPackages: true | false (omit for auto-detect)\"\n },\n \"mandatoryRules\": {\n \"componentsOnly\": \"Use Impact Nova components only — never create new custom UI components\",\n \"tables\": \"Any table/grid/spreadsheet → DataTable from impact-nova/data-table (not HTML table, not raw ag-grid-react)\",\n \"validateBeforeDone\": \"Call validate_snippet on generated UI code\",\n \"resource\": \"impact-nova://design-system-mandate\"\n },\n \"requiredPeers\": [\n {\n \"package\": \"react\",\n \"recommendedVersion\": \"^19.0.0\",\n \"description\": \"React 19 required\"\n },\n {\n \"package\": \"react-dom\",\n \"recommendedVersion\": \"^19.0.0\",\n \"description\": \"React DOM 19 required\"\n },\n {\n \"package\": \"impact-nova-icons\",\n \"recommendedVersion\": \">=0.1.0\",\n \"description\": \"Icons peer — not bundled in impact-nova; 0.1.x and 0.2.x supported\"\n }\n ],\n \"optionalPeers\": [\n {\n \"package\": \"ag-grid-community\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"AG Grid community types/API; pin to the same version as ag-grid-react\"\n },\n {\n \"package\": \"ag-grid-react\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"For DataTable and grid components; use exact version to match Impact Nova\"\n },\n {\n \"package\": \"ag-grid-enterprise\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"Required if using ag-grid-react with enterprise features\"\n },\n {\n \"package\": \"highcharts\",\n \"recommendedVersion\": \"^12.0.0\",\n \"description\": \"For Chart component\"\n },\n {\n \"package\": \"highcharts-react-official\",\n \"recommendedVersion\": \"^3.0.0\",\n \"description\": \"For Chart component\"\n },\n {\n \"package\": \"highcharts-border-radius\",\n \"recommendedVersion\": \"^0.0.4\",\n \"description\": \"Optional Highcharts plugin for rounded corners\"\n }\n ]\n}\n```\n";
1
+ export default "# Impact Nova — baseline\n\nUse **Impact Nova** design-system components and tokens. Prefer subpath imports (e.g. `impact-nova/button`) when you only need a few modules. For live component lists, specs, and codegen, use the **impact-nova-mcp** MCP server (see npm `impact-nova-mcp`). Browse **Storybook**: https://impact-nova.iaproducts.ai/\n\n## Peer dependency summary\n\n```json\n{\n \"impactNova\": {\n \"package\": \"impact-nova\",\n \"recommendedVersion\": \"2.3.0\",\n \"recommendedDistTag\": \"latest\",\n \"description\": \"Design system package — ESM with 90+ subpath exports. Install: npm install impact-nova@latest\"\n },\n \"createImpactNova\": {\n \"package\": \"create-impact-nova\",\n \"recommendedVersion\": \"^1.4.5\",\n \"description\": \"Vite + React scaffold with recipe-based modules\"\n },\n \"localDevelopment\": {\n \"defaultScaffoldDependency\": \"auto\",\n \"description\": \"create-impact-nova auto-detects: file: link when run inside the impact-nova monorepo; npm (impact-nova@^2.3.0) otherwise. CLI: --from-npm or --link-monorepo to override. Rebuild impact-nova (npm run build) after DS changes when using file: link.\",\n \"cliFlagForNpm\": \"--from-npm\",\n \"cliFlagForMonorepo\": \"--link-monorepo\",\n \"mcpScaffoldFlag\": \"usePublishedPackages: true | false (omit for auto-detect)\"\n },\n \"mandatoryRules\": {\n \"componentsOnly\": \"Use Impact Nova components only — never create new custom UI components\",\n \"tables\": \"Any table/grid/spreadsheet → DataTable from impact-nova/data-table (not HTML table, not raw ag-grid-react)\",\n \"validateBeforeDone\": \"Call validate_snippet on generated UI code\",\n \"resource\": \"impact-nova://design-system-mandate\"\n },\n \"requiredPeers\": [\n {\n \"package\": \"react\",\n \"recommendedVersion\": \"^19.0.0\",\n \"description\": \"React 19 required\"\n },\n {\n \"package\": \"react-dom\",\n \"recommendedVersion\": \"^19.0.0\",\n \"description\": \"React DOM 19 required\"\n },\n {\n \"package\": \"impact-nova-icons\",\n \"recommendedVersion\": \">=0.1.0\",\n \"description\": \"Icons peer — not bundled in impact-nova; 0.1.x and 0.2.x supported\"\n }\n ],\n \"optionalPeers\": [\n {\n \"package\": \"ag-grid-community\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"AG Grid community types/API; pin to the same version as ag-grid-react\"\n },\n {\n \"package\": \"ag-grid-react\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"For DataTable and grid components; use exact version to match Impact Nova\"\n },\n {\n \"package\": \"ag-grid-enterprise\",\n \"recommendedVersion\": \"36.0.1\",\n \"description\": \"Required if using ag-grid-react with enterprise features\"\n },\n {\n \"package\": \"highcharts\",\n \"recommendedVersion\": \"^12.0.0\",\n \"description\": \"For Chart component\"\n },\n {\n \"package\": \"highcharts-react-official\",\n \"recommendedVersion\": \"^3.0.0\",\n \"description\": \"For Chart component\"\n },\n {\n \"package\": \"highcharts-border-radius\",\n \"recommendedVersion\": \"^0.0.4\",\n \"description\": \"Optional Highcharts plugin for rounded corners\"\n }\n ]\n}\n```\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impact-nova",
3
- "version": "2.2.9",
3
+ "version": "2.3.0",
4
4
  "description": "Enterprise-grade React design system built with React 19, Vite, Tailwind CSS, and Radix UI. Built-in internationalization (i18n) and comprehensive UI component library for scalable, accessible, and performant applications.",
5
5
  "type": "module",
6
6
  "workspaces": [