@devalok/shilp-sutra 0.19.1 → 0.20.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.
@@ -12,6 +12,12 @@ export interface ActivityItem {
12
12
  color?: 'default' | 'success' | 'warning' | 'error' | 'info';
13
13
  detail?: React.ReactNode;
14
14
  }
15
+ export interface GroupLabels {
16
+ today?: string;
17
+ yesterday?: string;
18
+ thisWeek?: string;
19
+ older?: string;
20
+ }
15
21
  export interface ActivityFeedProps extends React.HTMLAttributes<HTMLDivElement> {
16
22
  items: ActivityItem[];
17
23
  onLoadMore?: () => void;
@@ -20,7 +26,17 @@ export interface ActivityFeedProps extends React.HTMLAttributes<HTMLDivElement>
20
26
  emptyState?: React.ReactNode;
21
27
  compact?: boolean;
22
28
  maxInitialItems?: number;
29
+ groupBy?: 'time' | 'none';
30
+ groupLabels?: GroupLabels;
23
31
  }
32
+ /**
33
+ * Buckets items into time-based groups: today, yesterday, earlier this week (since Monday), older.
34
+ * Skips empty groups.
35
+ */
36
+ export declare function groupItemsByTime(items: ActivityItem[], labels?: GroupLabels): {
37
+ label: string;
38
+ items: ActivityItem[];
39
+ }[];
24
40
  declare const ActivityFeed: React.ForwardRefExoticComponent<ActivityFeedProps & React.RefAttributes<HTMLDivElement>>;
25
41
  export { ActivityFeed };
26
42
  //# sourceMappingURL=activity-feed.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"activity-feed.d.ts","sourceRoot":"","sources":["../../src/composed/activity-feed.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAA;IAChC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;IAC5D,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AAED,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AA4HD,QAAA,MAAM,YAAY,0FA4EjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"activity-feed.d.ts","sourceRoot":"","sources":["../../src/composed/activity-feed.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAA;IAChC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;IAC5D,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAiBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,YAAY,EAAE,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,EAAE,CA6C5C;AA8ID,QAAA,MAAM,YAAY,0FAiGjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -1,117 +1,168 @@
1
1
  "use client";
2
- import { jsx as a, jsxs as r } from "react/jsx-runtime";
3
- import * as g from "react";
4
- import { cn as t } from "../ui/lib/utils.js";
5
- import { IconChevronRight as b } from "@tabler/icons-react";
6
- import { Avatar as k, AvatarImage as y, AvatarFallback as A } from "../ui/avatar.js";
7
- import { Button as x } from "../ui/button.js";
8
- import { Skeleton as c } from "../ui/skeleton.js";
9
- import { getInitials as S } from "./lib/string-utils.js";
10
- import { formatRelativeTime as C } from "../ui/lib/date-utils.js";
11
- const j = {
2
+ import { jsx as t, jsxs as a } from "react/jsx-runtime";
3
+ import * as N from "react";
4
+ import { cn as r } from "../ui/lib/utils.js";
5
+ import { IconChevronRight as S } from "@tabler/icons-react";
6
+ import { Avatar as O, AvatarImage as C, AvatarFallback as F } from "../ui/avatar.js";
7
+ import { Button as D } from "../ui/button.js";
8
+ import { Skeleton as v } from "../ui/skeleton.js";
9
+ import { getInitials as W } from "./lib/string-utils.js";
10
+ import { formatRelativeTime as j } from "../ui/lib/date-utils.js";
11
+ import { tweens as E } from "../ui/lib/motion.js";
12
+ import { m as R } from "../_chunks/framer.js";
13
+ const L = {
12
14
  default: "bg-surface-fg-subtle",
13
15
  success: "bg-success-9",
14
16
  warning: "bg-warning-9",
15
17
  error: "bg-error-9",
16
18
  info: "bg-info-9"
19
+ }, z = {
20
+ today: "Today",
21
+ yesterday: "Yesterday",
22
+ thisWeek: "This Week",
23
+ older: "Older"
17
24
  };
18
- function D({ compact: e }) {
19
- return /* @__PURE__ */ a("div", { className: t("flex flex-col", e ? "gap-1" : "gap-3"), children: [0, 1, 2].map((s) => /* @__PURE__ */ r("div", { className: "flex items-center gap-ds-03", children: [
20
- /* @__PURE__ */ a(c, { className: "h-2 w-2 shrink-0 rounded-ds-full" }),
21
- !e && /* @__PURE__ */ a(c, { className: "h-4 w-4 shrink-0 rounded-ds-full" }),
22
- /* @__PURE__ */ a(c, { className: t("flex-1", e ? "h-3" : "h-4") }),
23
- /* @__PURE__ */ a(c, { className: "h-3 w-12" })
25
+ function B(e, s) {
26
+ const o = { ...z, ...s }, d = /* @__PURE__ */ new Date(), c = new Date(d.getFullYear(), d.getMonth(), d.getDate()), n = new Date(c.getTime() - 24 * 60 * 60 * 1e3), l = d.getDay(), b = l === 0 ? 6 : l - 1, y = new Date(
27
+ c.getTime() - b * 24 * 60 * 60 * 1e3
28
+ ), f = [], m = [], u = [], p = [];
29
+ for (const i of e) {
30
+ const x = (typeof i.timestamp == "string" ? new Date(i.timestamp) : i.timestamp).getTime();
31
+ x >= c.getTime() ? f.push(i) : x >= n.getTime() ? m.push(i) : x >= y.getTime() ? u.push(i) : p.push(i);
32
+ }
33
+ const h = [];
34
+ return f.length > 0 && h.push({ label: o.today, items: f }), m.length > 0 && h.push({ label: o.yesterday, items: m }), u.length > 0 && h.push({ label: o.thisWeek, items: u }), p.length > 0 && h.push({ label: o.older, items: p }), h;
35
+ }
36
+ function G({ compact: e }) {
37
+ return /* @__PURE__ */ t("div", { className: r("flex flex-col", e ? "gap-1" : "gap-3"), children: [0, 1, 2].map((s) => /* @__PURE__ */ a("div", { className: "flex items-center gap-ds-03", children: [
38
+ /* @__PURE__ */ t(v, { className: "h-2 w-2 shrink-0 rounded-ds-full" }),
39
+ !e && /* @__PURE__ */ t(v, { className: "h-4 w-4 shrink-0 rounded-ds-full" }),
40
+ /* @__PURE__ */ t(v, { className: r("flex-1", e ? "h-3" : "h-4") }),
41
+ /* @__PURE__ */ t(v, { className: "h-3 w-12" })
24
42
  ] }, s)) });
25
43
  }
26
- function R({
44
+ function T({
27
45
  item: e,
28
46
  compact: s
29
47
  }) {
30
- const [n, o] = g.useState(!1), d = e.color ?? "default", i = () => {
31
- e.detail && o((l) => !l);
48
+ const [o, d] = N.useState(!1), c = e.color ?? "default", n = () => {
49
+ e.detail && d((l) => !l);
32
50
  };
33
- return /* @__PURE__ */ r("div", { className: t("relative flex items-start", s ? "gap-ds-02" : "gap-ds-03"), children: [
34
- /* @__PURE__ */ a(
51
+ return /* @__PURE__ */ a("div", { className: r("relative flex items-start", s ? "gap-ds-02" : "gap-ds-03"), children: [
52
+ /* @__PURE__ */ t(
35
53
  "div",
36
54
  {
37
- className: t(
55
+ className: r(
38
56
  "relative z-10 mt-1.5 h-2 w-2 shrink-0 rounded-ds-full ring-2 ring-surface-1",
39
- j[d]
57
+ L[c]
40
58
  )
41
59
  }
42
60
  ),
43
- !s && e.actor && /* @__PURE__ */ r(k, { className: "h-5 w-5 shrink-0 text-[9px]", children: [
44
- e.actor.image && /* @__PURE__ */ a(y, { src: e.actor.image, alt: e.actor.name }),
45
- /* @__PURE__ */ a(A, { className: "text-[9px]", children: S(e.actor.name) })
61
+ !s && e.actor && /* @__PURE__ */ a(O, { className: "h-5 w-5 shrink-0 text-[9px]", children: [
62
+ e.actor.image && /* @__PURE__ */ t(C, { src: e.actor.image, alt: e.actor.name }),
63
+ /* @__PURE__ */ t(F, { className: "text-[9px]", children: W(e.actor.name) })
46
64
  ] }),
47
- /* @__PURE__ */ r("div", { className: "flex min-w-0 flex-1 flex-col", children: [
48
- /* @__PURE__ */ r("div", { className: "flex items-center justify-between gap-ds-02", children: [
49
- /* @__PURE__ */ r("div", { className: t("min-w-0 flex-1 flex items-center gap-ds-01 flex-wrap", s ? "text-ds-xs" : "text-ds-sm"), children: [
50
- e.actor && /* @__PURE__ */ r("span", { className: "font-medium text-surface-fg", children: [
65
+ /* @__PURE__ */ a("div", { className: "flex min-w-0 flex-1 flex-col", children: [
66
+ /* @__PURE__ */ a("div", { className: "flex items-center justify-between gap-ds-02", children: [
67
+ /* @__PURE__ */ a("div", { className: r("min-w-0 flex-1 flex items-center gap-ds-01 flex-wrap", s ? "text-ds-xs" : "text-ds-sm"), children: [
68
+ e.actor && /* @__PURE__ */ a("span", { className: "font-medium text-surface-fg", children: [
51
69
  e.actor.name,
52
70
  " "
53
71
  ] }),
54
- e.detail && /* @__PURE__ */ a(b, { className: t("h-3 w-3 shrink-0 text-surface-fg-subtle transition-transform", n && "rotate-90") }),
55
- /* @__PURE__ */ a(
72
+ e.detail && /* @__PURE__ */ t(S, { className: r("h-3 w-3 shrink-0 text-surface-fg-subtle transition-transform", o && "rotate-90") }),
73
+ /* @__PURE__ */ t(
56
74
  "span",
57
75
  {
58
- className: t(
76
+ className: r(
59
77
  "text-surface-fg-muted",
60
78
  e.detail && "cursor-pointer hover:underline"
61
79
  ),
62
- onClick: i,
80
+ onClick: n,
63
81
  role: e.detail ? "button" : void 0,
64
82
  tabIndex: e.detail ? 0 : void 0,
65
83
  onKeyDown: e.detail ? (l) => {
66
- (l.key === "Enter" || l.key === " ") && (l.preventDefault(), i());
84
+ (l.key === "Enter" || l.key === " ") && (l.preventDefault(), n());
67
85
  } : void 0,
68
86
  children: e.action
69
87
  }
70
88
  )
71
89
  ] }),
72
- /* @__PURE__ */ a(
90
+ /* @__PURE__ */ t(
73
91
  "time",
74
92
  {
75
- className: t(
93
+ className: r(
76
94
  "shrink-0 whitespace-nowrap text-surface-fg-subtle",
77
95
  s ? "text-ds-xs" : "text-ds-sm"
78
96
  ),
79
97
  dateTime: (typeof e.timestamp == "string" ? new Date(e.timestamp) : e.timestamp).toISOString(),
80
- children: C(e.timestamp)
98
+ children: j(e.timestamp)
81
99
  }
82
100
  )
83
101
  ] }),
84
- n && e.detail && /* @__PURE__ */ a("div", { className: "mt-ds-02 animate-in fade-in slide-in-from-top-1 text-ds-sm text-surface-fg-muted", children: e.detail })
102
+ o && e.detail && /* @__PURE__ */ t("div", { className: "mt-ds-02 animate-in fade-in slide-in-from-top-1 text-ds-sm text-surface-fg-muted", children: e.detail })
85
103
  ] })
86
104
  ] });
87
105
  }
88
- const z = g.forwardRef(
106
+ function I({
107
+ label: e,
108
+ isFirst: s
109
+ }) {
110
+ return /* @__PURE__ */ a(
111
+ R.div,
112
+ {
113
+ initial: { opacity: 0 },
114
+ animate: { opacity: 1 },
115
+ transition: E.fade,
116
+ className: r(
117
+ "flex items-center gap-ds-03",
118
+ s ? "" : "mt-ds-06",
119
+ "mb-ds-03"
120
+ ),
121
+ children: [
122
+ /* @__PURE__ */ t("hr", { className: "flex-1 border-surface-border" }),
123
+ /* @__PURE__ */ t("span", { className: "bg-surface-1 px-ds-03 text-ds-xs font-medium uppercase tracking-wider text-surface-fg-subtle", children: e }),
124
+ /* @__PURE__ */ t("hr", { className: "flex-1 border-surface-border" })
125
+ ]
126
+ }
127
+ );
128
+ }
129
+ const Y = N.forwardRef(
89
130
  ({
90
131
  items: e,
91
132
  onLoadMore: s,
92
- loading: n = !1,
93
- hasMore: o = !1,
94
- emptyState: d,
95
- compact: i = !1,
133
+ loading: o = !1,
134
+ hasMore: d = !1,
135
+ emptyState: c,
136
+ compact: n = !1,
96
137
  maxInitialItems: l,
138
+ groupBy: b = "none",
139
+ groupLabels: y,
97
140
  className: f,
98
141
  ...m
99
- }, h) => {
100
- const [p, N] = g.useState(!1);
101
- if (n)
102
- return /* @__PURE__ */ a("div", { ref: h, className: t("relative", f), ...m, children: /* @__PURE__ */ a(D, { compact: i }) });
142
+ }, u) => {
143
+ const [p, h] = N.useState(!1);
144
+ if (o)
145
+ return /* @__PURE__ */ t("div", { ref: u, className: r("relative", f), ...m, children: /* @__PURE__ */ t(G, { compact: n }) });
103
146
  if (e.length === 0)
104
- return d ? /* @__PURE__ */ a("div", { ref: h, className: f, ...m, children: d }) : null;
105
- const u = l != null && !p && e.length > l, w = u ? e.slice(0, l) : e;
106
- return /* @__PURE__ */ r("div", { ref: h, className: t("relative", f), ...m, children: [
107
- /* @__PURE__ */ a("div", { className: "absolute bottom-0 left-[3px] top-0 w-px bg-surface-border" }),
108
- /* @__PURE__ */ a("div", { className: t("relative flex flex-col", i ? "gap-1" : "gap-3"), children: w.map((v) => /* @__PURE__ */ a(R, { item: v, compact: i }, v.id)) }),
109
- u && /* @__PURE__ */ a("div", { className: "relative mt-ds-03 flex justify-center", children: /* @__PURE__ */ r(
110
- x,
147
+ return c ? /* @__PURE__ */ t("div", { ref: u, className: f, ...m, children: c }) : null;
148
+ const i = l != null && !p && e.length > l, w = i ? e.slice(0, l) : e, x = b === "time";
149
+ return /* @__PURE__ */ a("div", { ref: u, className: r("relative", f), ...m, children: [
150
+ x ? /* @__PURE__ */ t("div", { children: B(w, y).map((g, A) => /* @__PURE__ */ a("div", { children: [
151
+ /* @__PURE__ */ t(I, { label: g.label, isFirst: A === 0 }),
152
+ /* @__PURE__ */ a("div", { className: r("relative flex flex-col", n ? "gap-1" : "gap-3"), children: [
153
+ /* @__PURE__ */ t("div", { className: "absolute bottom-0 left-[3px] top-0 w-px bg-surface-border" }),
154
+ g.items.map((k) => /* @__PURE__ */ t(T, { item: k, compact: n }, k.id))
155
+ ] })
156
+ ] }, g.label)) }) : /* @__PURE__ */ a("div", { className: r("relative flex flex-col", n ? "gap-1" : "gap-3"), children: [
157
+ /* @__PURE__ */ t("div", { className: "absolute bottom-0 left-[3px] top-0 w-px bg-surface-border" }),
158
+ w.map((g) => /* @__PURE__ */ t(T, { item: g, compact: n }, g.id))
159
+ ] }),
160
+ i && /* @__PURE__ */ t("div", { className: "relative mt-ds-03 flex justify-center", children: /* @__PURE__ */ a(
161
+ D,
111
162
  {
112
163
  variant: "ghost",
113
164
  size: "sm",
114
- onClick: () => N(!0),
165
+ onClick: () => h(!0),
115
166
  children: [
116
167
  "Show all (",
117
168
  e.length,
@@ -119,11 +170,12 @@ const z = g.forwardRef(
119
170
  ]
120
171
  }
121
172
  ) }),
122
- o && s && !u && /* @__PURE__ */ a("div", { className: "relative mt-ds-03 flex justify-center", children: /* @__PURE__ */ a(x, { variant: "ghost", size: "sm", onClick: s, children: "Load more" }) })
173
+ d && s && !i && /* @__PURE__ */ t("div", { className: "relative mt-ds-03 flex justify-center", children: /* @__PURE__ */ t(D, { variant: "ghost", size: "sm", onClick: s, children: "Load more" }) })
123
174
  ] });
124
175
  }
125
176
  );
126
- z.displayName = "ActivityFeed";
177
+ Y.displayName = "ActivityFeed";
127
178
  export {
128
- z as ActivityFeed
179
+ Y as ActivityFeed,
180
+ B as groupItemsByTime
129
181
  };
@@ -46,7 +46,7 @@ export { ScheduleView } from './schedule-view';
46
46
  export type { ScheduleViewProps, ScheduleEvent } from './schedule-view';
47
47
  export { ConfirmDialog } from './confirm-dialog';
48
48
  export type { ConfirmDialogProps } from './confirm-dialog';
49
- export { ActivityFeed } from './activity-feed';
50
- export type { ActivityItem, ActivityFeedProps } from './activity-feed';
49
+ export { ActivityFeed, groupItemsByTime } from './activity-feed';
50
+ export type { ActivityItem, ActivityFeedProps, GroupLabels } from './activity-feed';
51
51
  export { getInitials } from './lib/string-utils';
52
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composed/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAEhE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,YAAY,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE5E,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAE/F,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AACpJ,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAE9M,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC7F,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEvF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAE5E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEvE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composed/index.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,9 @@ import { EmptyState as x } from "./empty-state.js";
6
6
  import { AvatarGroup as l } from "./avatar-group.js";
7
7
  import { PriorityIndicator as k } from "./priority-indicator.js";
8
8
  import { RichTextEditor as P, RichTextViewer as S } from "./rich-text-editor.js";
9
- import { C as D, D as T, a as g, b as u, M as b, P as h, T as y, Y as V, u as B } from "../_chunks/use-calendar.js";
10
- import { BoardSkeleton as G, CardSkeleton as L, ListSkeleton as M, TableSkeleton as R } from "./loading-skeleton.js";
11
- import { DashboardSkeleton as w, ProjectListSkeleton as A, TaskDetailSkeleton as I } from "./page-skeletons.js";
9
+ import { C as T, D as g, a as D, b as u, M as b, P as h, T as y, Y as B, u as V } from "../_chunks/use-calendar.js";
10
+ import { BoardSkeleton as G, CardSkeleton as I, ListSkeleton as L, TableSkeleton as M } from "./loading-skeleton.js";
11
+ import { DashboardSkeleton as v, ProjectListSkeleton as w, TaskDetailSkeleton as A } from "./page-skeletons.js";
12
12
  import { ErrorDisplay as j } from "./error-boundary.js";
13
13
  import { GlobalLoading as H } from "./global-loading.js";
14
14
  import { CommandPalette as z } from "./command-palette.js";
@@ -16,42 +16,43 @@ import { MemberPicker as K } from "./member-picker.js";
16
16
  import { SimpleTooltip as O } from "./simple-tooltip.js";
17
17
  import { ScheduleView as U } from "./schedule-view.js";
18
18
  import { ConfirmDialog as X } from "./confirm-dialog.js";
19
- import { ActivityFeed as _ } from "./activity-feed.js";
20
- import { getInitials as ee } from "./lib/string-utils.js";
19
+ import { ActivityFeed as _, groupItemsByTime as $ } from "./activity-feed.js";
20
+ import { getInitials as re } from "./lib/string-utils.js";
21
21
  export {
22
22
  _ as ActivityFeed,
23
23
  l as AvatarGroup,
24
24
  G as BoardSkeleton,
25
- D as CalendarGrid,
26
- L as CardSkeleton,
25
+ T as CalendarGrid,
26
+ I as CardSkeleton,
27
27
  z as CommandPalette,
28
28
  X as ConfirmDialog,
29
29
  a as ContentCard,
30
- w as DashboardSkeleton,
31
- T as DatePicker,
32
- g as DateRangePicker,
30
+ v as DashboardSkeleton,
31
+ g as DatePicker,
32
+ D as DateRangePicker,
33
33
  u as DateTimePicker,
34
34
  x as EmptyState,
35
35
  j as ErrorDisplay,
36
36
  H as GlobalLoading,
37
- M as ListSkeleton,
37
+ L as ListSkeleton,
38
38
  K as MemberPicker,
39
39
  b as MonthPicker,
40
40
  o as PageHeader,
41
41
  h as Presets,
42
42
  k as PriorityIndicator,
43
- A as ProjectListSkeleton,
43
+ w as ProjectListSkeleton,
44
44
  P as RichTextEditor,
45
45
  S as RichTextViewer,
46
46
  U as ScheduleView,
47
47
  O as SimpleTooltip,
48
48
  p as StatusBadge,
49
- R as TableSkeleton,
50
- I as TaskDetailSkeleton,
49
+ M as TableSkeleton,
50
+ A as TaskDetailSkeleton,
51
51
  y as TimePicker,
52
- V as YearPicker,
52
+ B as YearPicker,
53
53
  i as contentCardVariants,
54
- ee as getInitials,
54
+ re as getInitials,
55
+ $ as groupItemsByTime,
55
56
  s as statusBadgeVariants,
56
- B as useCalendar
57
+ V as useCalendar
57
58
  };
@@ -13,7 +13,8 @@ declare const bannerVariants: (props?: ({
13
13
  * **Banner vs Alert:** Banner spans the full width of its container (e.g., top of a page or section).
14
14
  * Alert is an inline block inside page content. Use Banner for system-level announcements.
15
15
  *
16
- * **`action` slot:** Accepts any React node — typically a `<Button variant="ghost" size="sm">` or a link.
16
+ * **`actions` slot:** Accepts any React node(s) — typically one or more `<Button variant="ghost" size="sm">`.
17
+ * Multiple actions wrap gracefully on narrow viewports. The singular `action` prop is deprecated.
17
18
  * **Dismissible:** Provide `onDismiss` to show an × button.
18
19
  *
19
20
  * @example
@@ -36,7 +37,10 @@ declare const bannerVariants: (props?: ({
36
37
  * // These are just a few ways — feel free to combine props creatively!
37
38
  */
38
39
  export interface BannerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>, VariantProps<typeof bannerVariants> {
40
+ /** @deprecated Use `actions` instead */
39
41
  action?: React.ReactNode;
42
+ /** Action slot — accepts any React node(s), typically ghost Buttons. Preferred over `action`. */
43
+ actions?: React.ReactNode;
40
44
  onDismiss?: () => void;
41
45
  }
42
46
  declare const Banner: React.ForwardRefExoticComponent<BannerProps & React.RefAttributes<HTMLDivElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAKjE,QAAA,MAAM,cAAc;;8EAmBnB,CAAA;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,EACzD,YAAY,CAAC,OAAO,cAAc,CAAC;IACrC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,QAAA,MAAM,MAAM,oFAuCX,CAAA;AAGD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAKjE,QAAA,MAAM,cAAc;;8EAmBnB,CAAA;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,EACzD,YAAY,CAAC,OAAO,cAAc,CAAC;IACrC,wCAAwC;IACxC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,iGAAiG;IACjG,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,QAAA,MAAM,MAAM,oFA0CX,CAAA;AAGD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
package/dist/ui/banner.js CHANGED
@@ -1,13 +1,13 @@
1
1
  "use client";
2
- import { jsx as e, jsxs as b } from "react/jsx-runtime";
3
- import { IconInfoCircle as o, IconAlertCircle as g, IconAlertTriangle as p, IconCircleCheck as h, IconX as x } from "@tabler/icons-react";
2
+ import { jsx as e, jsxs as h } from "react/jsx-runtime";
3
+ import { IconInfoCircle as n, IconAlertCircle as p, IconAlertTriangle as x, IconCircleCheck as v, IconX as w } from "@tabler/icons-react";
4
4
  import * as r from "react";
5
- import { ag as v } from "../_chunks/vendor-utils.js";
6
- import { cn as w } from "./lib/utils.js";
7
- import { springs as N, motionProps as I } from "./lib/motion.js";
8
- import { A as C, m as y } from "../_chunks/framer.js";
9
- const k = v(
10
- "flex items-center gap-ds-04 px-ds-06 py-ds-04 text-ds-md font-medium border-b",
5
+ import { ag as k } from "../_chunks/vendor-utils.js";
6
+ import { cn as N } from "./lib/utils.js";
7
+ import { springs as I, motionProps as C } from "./lib/motion.js";
8
+ import { A as y, m as A } from "../_chunks/framer.js";
9
+ const _ = k(
10
+ "flex flex-wrap items-center gap-ds-04 px-ds-06 py-ds-04 text-ds-md font-medium border-b",
11
11
  {
12
12
  variants: {
13
13
  color: {
@@ -20,39 +20,39 @@ const k = v(
20
20
  },
21
21
  defaultVariants: { color: "info" }
22
22
  }
23
- ), A = {
24
- info: o,
25
- success: h,
26
- warning: p,
27
- error: g,
28
- neutral: o
29
- }, V = r.forwardRef(
30
- ({ className: t, color: s = "info", action: n, onDismiss: i, children: a, ...c }, l) => {
31
- const m = A[s ?? "info"], [f, d] = r.useState(!0), u = r.useCallback(() => {
32
- d(!1);
23
+ ), V = {
24
+ info: n,
25
+ success: v,
26
+ warning: x,
27
+ error: p,
28
+ neutral: n
29
+ }, j = r.forwardRef(
30
+ ({ className: t, color: s = "info", action: a, actions: c, onDismiss: o, children: l, ...d }, m) => {
31
+ const i = c ?? a, f = V[s ?? "info"], [u, b] = r.useState(!0), g = r.useCallback(() => {
32
+ b(!1);
33
33
  }, []);
34
- return /* @__PURE__ */ e(C, { onExitComplete: i, children: f && /* @__PURE__ */ b(
35
- y.div,
34
+ return /* @__PURE__ */ e(y, { onExitComplete: o, children: u && /* @__PURE__ */ h(
35
+ A.div,
36
36
  {
37
- ref: l,
37
+ ref: m,
38
38
  initial: { height: "auto", opacity: 1 },
39
39
  exit: { height: 0, opacity: 0 },
40
- transition: N.snappy,
41
- className: w(k({ color: s }), "overflow-hidden", t),
40
+ transition: I.snappy,
41
+ className: N(_({ color: s }), "overflow-hidden", t),
42
42
  role: "alert",
43
- ...I(c),
43
+ ...C(d),
44
44
  children: [
45
- /* @__PURE__ */ e(m, { className: "h-ico-md w-ico-md shrink-0", "aria-hidden": "true" }),
46
- /* @__PURE__ */ e("span", { className: "flex-1", children: a }),
47
- n && /* @__PURE__ */ e("span", { className: "shrink-0", children: n }),
48
- i && /* @__PURE__ */ e(
45
+ /* @__PURE__ */ e(f, { className: "h-ico-md w-ico-md shrink-0", "aria-hidden": "true" }),
46
+ /* @__PURE__ */ e("div", { className: "min-w-0 flex-1", children: l }),
47
+ i && /* @__PURE__ */ e("div", { className: "flex shrink-0 items-center gap-ds-02 [&_button]:transition-colors [&_button]:duration-150 [&_button:hover]:bg-black/10 [&_button:hover]:dark:bg-white/10", children: i }),
48
+ o && /* @__PURE__ */ e(
49
49
  "button",
50
50
  {
51
51
  type: "button",
52
- onClick: u,
53
- className: "shrink-0 min-h-ds-xs min-w-ds-xs flex items-center justify-center rounded-ds-sm text-surface-fg-subtle transition-colors hover:text-surface-fg-muted hover:bg-surface-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-9",
52
+ onClick: g,
53
+ className: "shrink-0 min-h-ds-xs min-w-ds-xs flex items-center justify-center rounded-ds-sm transition-colors duration-150 hover:bg-black/10 dark:hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-9",
54
54
  "aria-label": "Dismiss",
55
- children: /* @__PURE__ */ e(x, { className: "h-ico-sm w-ico-sm" })
55
+ children: /* @__PURE__ */ e(w, { className: "h-ico-sm w-ico-sm" })
56
56
  }
57
57
  )
58
58
  ]
@@ -60,8 +60,8 @@ const k = v(
60
60
  ) });
61
61
  }
62
62
  );
63
- V.displayName = "Banner";
63
+ j.displayName = "Banner";
64
64
  export {
65
- V as Banner,
66
- k as bannerVariants
65
+ j as Banner,
66
+ _ as bannerVariants
67
67
  };
@@ -12,9 +12,11 @@
12
12
  emptyState?: ReactNode — empty state content
13
13
  compact: boolean — tighter spacing, no avatars, smaller text
14
14
  maxInitialItems: number — truncate with "Show all (N)" toggle
15
+ groupBy?: 'time' | 'none' — group items by time buckets (today, yesterday, this week, older)
16
+ groupLabels?: GroupLabels — custom labels for time groups: { today?, yesterday?, thisWeek?, older? }
15
17
 
16
18
  ## Defaults
17
- loading=false, compact=false, hasMore=false
19
+ loading=false, compact=false, hasMore=false, groupBy='none'
18
20
 
19
21
  ## Example
20
22
  ```jsx
@@ -29,13 +31,23 @@
29
31
  />
30
32
  ```
31
33
 
34
+ ## Exported Utilities
35
+ groupItemsByTime(items: ActivityItem[], labels?: GroupLabels) — pure function that buckets items into time groups; returns { label: string, items: ActivityItem[] }[]
36
+
32
37
  ## Gotchas
33
38
  - `items` is required — passing an empty array renders the `emptyState` content
34
39
  - `color` on each item controls the timeline dot color
35
40
  - `actor.image` is optional — falls back to initials from `actor.name`
36
41
  - `maxInitialItems` truncates with a "Show all (N)" toggle button
42
+ - `maxInitialItems` applies to the flat list BEFORE grouping — items are sliced first, then grouped
43
+ - Empty time groups are automatically skipped
37
44
 
38
45
  ## Changes
46
+ ### v0.20.0
47
+ - **Added** `groupBy="time"` prop — groups items into Today, Yesterday, This Week, Older with section headers
48
+ - **Added** `groupLabels` prop for custom group label text
49
+ - **Added** `groupItemsByTime()` exported pure utility function
50
+
39
51
  ### v0.18.0
40
52
  - **Fixed** `bg-accent-9` changed to `bg-info-9` (info color, not accent)
41
53
 
@@ -6,7 +6,8 @@
6
6
 
7
7
  ## Props
8
8
  color: "info" | "success" | "warning" | "error" | "neutral"
9
- action: ReactNode (optional action slot, typically a ghost Button)
9
+ actions: ReactNode (optional action slot for one or more buttons; wraps on narrow viewports)
10
+ action: ReactNode (DEPRECATED — use `actions` instead)
10
11
  onDismiss: () => void (optional, shows X button)
11
12
  children: ReactNode (message text)
12
13
 
@@ -26,6 +27,11 @@
26
27
  - `onDismiss` fires after the exit animation completes, not immediately on dismiss button click
27
28
 
28
29
  ## Changes
30
+ ### v0.20.0
31
+ - **Added** `actions` prop (plural) for multiple action buttons with mobile-friendly flex-wrap
32
+ - **Deprecated** `action` prop — use `actions` instead (both still work; `actions` takes priority)
33
+ - **Changed** Root layout uses `flex-wrap` and children wrapper uses `min-w-0` for better text truncation
34
+
29
35
  ### v0.3.1
30
36
  - **Fixed** BannerProps uses `Omit<HTMLAttributes, 'color'>` to resolve TypeScript conflict with CVA `color` variant
31
37
 
package/llms-full.txt CHANGED
@@ -5,7 +5,7 @@
5
5
  > All variant values and props verified from source CVA definitions.
6
6
  >
7
7
  > Package: @devalok/shilp-sutra
8
- > Version: 0.19.1
8
+ > Version: 0.20.0
9
9
 
10
10
  ---
11
11
 
@@ -407,7 +407,8 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
407
407
 
408
408
  ## Props
409
409
  color: "info" | "success" | "warning" | "error" | "neutral"
410
- action: ReactNode (optional action slot, typically a ghost Button)
410
+ actions: ReactNode (optional action slot for one or more buttons; wraps on narrow viewports)
411
+ action: ReactNode (DEPRECATED — use `actions` instead)
411
412
  onDismiss: () => void (optional, shows X button)
412
413
  children: ReactNode (message text)
413
414
 
@@ -427,6 +428,11 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
427
428
  - `onDismiss` fires after the exit animation completes, not immediately on dismiss button click
428
429
 
429
430
  ## Changes
431
+ ### v0.20.0
432
+ - **Added** `actions` prop (plural) for multiple action buttons with mobile-friendly flex-wrap
433
+ - **Deprecated** `action` prop — use `actions` instead (both still work; `actions` takes priority)
434
+ - **Changed** Root layout uses `flex-wrap` and children wrapper uses `min-w-0` for better text truncation
435
+
430
436
  ### v0.3.1
431
437
  - **Fixed** BannerProps uses `Omit<HTMLAttributes, 'color'>` to resolve TypeScript conflict with CVA `color` variant
432
438
 
@@ -2911,9 +2917,11 @@ export default function RootLayout({ children }) {
2911
2917
  emptyState?: ReactNode — empty state content
2912
2918
  compact: boolean — tighter spacing, no avatars, smaller text
2913
2919
  maxInitialItems: number — truncate with "Show all (N)" toggle
2920
+ groupBy?: 'time' | 'none' — group items by time buckets (today, yesterday, this week, older)
2921
+ groupLabels?: GroupLabels — custom labels for time groups: { today?, yesterday?, thisWeek?, older? }
2914
2922
 
2915
2923
  ## Defaults
2916
- loading=false, compact=false, hasMore=false
2924
+ loading=false, compact=false, hasMore=false, groupBy='none'
2917
2925
 
2918
2926
  ## Example
2919
2927
  ```jsx
@@ -2928,13 +2936,23 @@ export default function RootLayout({ children }) {
2928
2936
  />
2929
2937
  ```
2930
2938
 
2939
+ ## Exported Utilities
2940
+ groupItemsByTime(items: ActivityItem[], labels?: GroupLabels) — pure function that buckets items into time groups; returns { label: string, items: ActivityItem[] }[]
2941
+
2931
2942
  ## Gotchas
2932
2943
  - `items` is required — passing an empty array renders the `emptyState` content
2933
2944
  - `color` on each item controls the timeline dot color
2934
2945
  - `actor.image` is optional — falls back to initials from `actor.name`
2935
2946
  - `maxInitialItems` truncates with a "Show all (N)" toggle button
2947
+ - `maxInitialItems` applies to the flat list BEFORE grouping — items are sliced first, then grouped
2948
+ - Empty time groups are automatically skipped
2936
2949
 
2937
2950
  ## Changes
2951
+ ### v0.20.0
2952
+ - **Added** `groupBy="time"` prop — groups items into Today, Yesterday, This Week, Older with section headers
2953
+ - **Added** `groupLabels` prop for custom group label text
2954
+ - **Added** `groupItemsByTime()` exported pure utility function
2955
+
2938
2956
  ### v0.18.0
2939
2957
  - **Fixed** `bg-accent-9` changed to `bg-info-9` (info color, not accent)
2940
2958