@devalok/shilp-sutra 0.19.0 → 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.
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
  };
@@ -1 +1 @@
1
- {"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../src/ui/chip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQjE,QAAA,MAAM,YAAY;;;;8EA6DjB,CAAA;AAED,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEzJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,KAAK,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,YAAY,CAAC,EAAE,OAAO,CAAC,GAAG;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,QAAA,MAAM,IAAI;;;;;WATD,MAAM;YACL,SAAS;WACV,KAAK,CAAC,SAAS;cACZ,KAAK,CAAC,iBAAiB;gBACrB,MAAM,IAAI;eACX,OAAO;gBACN,MAAM;qCA6CnB,CAAA;AAGD,OAAO,EAAE,eAAe,IAAI,SAAS,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../src/ui/chip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQjE,QAAA,MAAM,YAAY;;;;8EA6DjB,CAAA;AAED,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEzJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,KAAK,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,YAAY,CAAC,EAAE,OAAO,CAAC,GAAG;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,QAAA,MAAM,IAAI;;;;;WATD,MAAM;YACL,SAAS;WACV,KAAK,CAAC,SAAS;cACZ,KAAK,CAAC,iBAAiB;gBACrB,MAAM,IAAI;eACX,OAAO;gBACN,MAAM;qCA8CnB,CAAA;AAGD,OAAO,EAAE,eAAe,IAAI,SAAS,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,CAAA"}
package/dist/ui/chip.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use client";
2
- import { jsxs as f, jsx as r } from "react/jsx-runtime";
2
+ import { jsxs as f, jsx as a } from "react/jsx-runtime";
3
3
  import * as v from "react";
4
4
  import { IconX as N } from "@tabler/icons-react";
5
5
  import { ag as h } from "../_chunks/vendor-utils.js";
6
6
  import { springs as w, motionProps as C } from "./lib/motion.js";
7
7
  import { cn as k } from "./lib/utils.js";
8
8
  import { m as n } from "../_chunks/framer.js";
9
- import { A as $ } from "../_chunks/framer.js";
9
+ import { A as X } from "../_chunks/framer.js";
10
10
  const j = h(
11
11
  "inline-flex items-center gap-ds-02 font-sans leading-ds-relaxed rounded-ds-full transition-colors duration-fast-01",
12
12
  {
@@ -69,8 +69,8 @@ const j = h(
69
69
  }
70
70
  }
71
71
  ), R = v.forwardRef(
72
- ({ label: t, variant: i, size: l, color: g, icon: o, onClick: s, onDismiss: c, disabled: a, className: d, ...m }, b) => {
73
- const e = !!s, u = e ? n.button : n.span, y = e && !a ? "cursor-pointer hover:bg-surface-4 active:scale-95" : "", x = a ? "opacity-action-disabled cursor-not-allowed" : "";
72
+ ({ label: t, variant: i, size: l, color: g, icon: o, onClick: s, onDismiss: c, disabled: e, className: d, ...m }, b) => {
73
+ const r = !!s, u = r ? n.button : n.span, y = r && !e ? "cursor-pointer hover:bg-surface-4" : "", x = e ? "opacity-action-disabled cursor-not-allowed" : "";
74
74
  return /* @__PURE__ */ f(
75
75
  u,
76
76
  {
@@ -78,16 +78,17 @@ const j = h(
78
78
  layout: !0,
79
79
  initial: { opacity: 1, scale: 1 },
80
80
  exit: { opacity: 0, scale: 0.8 },
81
+ whileTap: r && !e ? { scale: 0.95 } : void 0,
81
82
  transition: w.snappy,
82
83
  className: k(j({ variant: i, size: l, color: g }), y, x, d),
83
- onClick: e ? s : void 0,
84
- disabled: e ? a : void 0,
85
- type: e ? "button" : void 0,
84
+ onClick: r ? s : void 0,
85
+ disabled: r ? e : void 0,
86
+ type: r ? "button" : void 0,
86
87
  ...C(m),
87
88
  children: [
88
- o && /* @__PURE__ */ r("span", { className: "flex-shrink-0 [&>svg]:w-ico-sm [&>svg]:h-ico-sm", children: o }),
89
- /* @__PURE__ */ r("span", { children: t }),
90
- c && /* @__PURE__ */ r(
89
+ o && /* @__PURE__ */ a("span", { className: "flex-shrink-0 [&>svg]:w-ico-sm [&>svg]:h-ico-sm", children: o }),
90
+ /* @__PURE__ */ a("span", { children: t }),
91
+ c && /* @__PURE__ */ a(
91
92
  "button",
92
93
  {
93
94
  type: "button",
@@ -96,7 +97,7 @@ const j = h(
96
97
  onClick: (p) => {
97
98
  p.stopPropagation(), c();
98
99
  },
99
- children: /* @__PURE__ */ r(N, { className: "h-ico-sm w-ico-sm" })
100
+ children: /* @__PURE__ */ a(N, { className: "h-ico-sm w-ico-sm" })
100
101
  }
101
102
  )
102
103
  ]
@@ -107,6 +108,6 @@ const j = h(
107
108
  R.displayName = "Chip";
108
109
  export {
109
110
  R as Chip,
110
- $ as ChipGroup,
111
+ X as ChipGroup,
111
112
  j as chipVariants
112
113
  };
package/dist/ui/dialog.js CHANGED
@@ -56,12 +56,12 @@ const H = t.forwardRef(({ className: a, children: s, ...e }, r) => {
56
56
  children: /* @__PURE__ */ d(
57
57
  g.div,
58
58
  {
59
- initial: { opacity: 0, scale: 0.95 },
60
- animate: { opacity: 1, scale: 1 },
61
- exit: { opacity: 0, scale: 0.95 },
59
+ initial: { opacity: 0, scale: 0.95, x: "-50%", y: "-50%" },
60
+ animate: { opacity: 1, scale: 1, x: "-50%", y: "-50%" },
61
+ exit: { opacity: 0, scale: 0.95, x: "-50%", y: "-50%" },
62
62
  transition: { ...T.smooth, opacity: p.fade },
63
63
  className: i(
64
- "fixed left-[50%] top-[50%] z-modal grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-ds-05 border border-surface-border-strong bg-surface-1 p-ds-06 shadow-04 rounded-ds-xl",
64
+ "fixed left-[50%] top-[50%] z-modal grid w-full max-w-lg gap-ds-05 border border-surface-border-strong bg-surface-1 p-ds-06 shadow-04 rounded-ds-xl",
65
65
  a
66
66
  ),
67
67
  children: [
@@ -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
 
@@ -46,6 +46,9 @@
46
46
  - AlertDialogAction does NOT have color="error" styling — add it yourself via className or wrap a Button
47
47
 
48
48
  ## Changes
49
+ ### v0.19.1
50
+ - **Fixed** AlertDialog not centered after Framer Motion animation completes — same `transform: none` fix as Dialog.
51
+
49
52
  ### v0.18.0
50
53
  - **Changed** Overlay animations migrated to Framer Motion (physics-based springs)
51
54
  - **Added** `AlertDialogContentProps`, `AlertDialogActionProps`, `AlertDialogCancelProps` type exports
@@ -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
 
@@ -33,6 +33,9 @@
33
33
  - `color="primary"` will be renamed to `color="brand"` in v1.0 — use `color="primary"` for now
34
34
 
35
35
  ## Changes
36
+ ### v0.19.1
37
+ - **Fixed** `active:scale-95` tap feedback broken by Framer Motion transform override — replaced with `whileTap={{ scale: 0.95 }}`
38
+
36
39
  ### v0.4.2
37
40
  - **Changed** (BREAKING) `variant="filled"` renamed to `"subtle"`, `variant="outlined"` renamed to `"outline"`, `onDelete` renamed to `onDismiss`
38
41
 
@@ -39,6 +39,9 @@
39
39
  - DialogTitle is required for accessibility — screen readers announce it when the dialog opens
40
40
 
41
41
  ## Changes
42
+ ### v0.19.1
43
+ - **Fixed** Dialog not centered after Framer Motion animation completes — `transform: none` inline style overrode Tailwind `translate-x/y` classes. Centering now handled via Framer Motion `x`/`y` properties.
44
+
42
45
  ### v0.18.0
43
46
  - **Changed** Overlay animations migrated to Framer Motion (physics-based springs)
44
47
  - **Added** `DialogContentProps`, `DialogTitleProps` type exports
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.0
8
+ > Version: 0.20.0
9
9
 
10
10
  ---
11
11
 
@@ -231,6 +231,9 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
231
231
  - AlertDialogAction does NOT have color="error" styling — add it yourself via className or wrap a Button
232
232
 
233
233
  ## Changes
234
+ ### v0.19.1
235
+ - **Fixed** AlertDialog not centered after Framer Motion animation completes — same `transform: none` fix as Dialog.
236
+
234
237
  ### v0.18.0
235
238
  - **Changed** Overlay animations migrated to Framer Motion (physics-based springs)
236
239
  - **Added** `AlertDialogContentProps`, `AlertDialogActionProps`, `AlertDialogCancelProps` type exports
@@ -404,7 +407,8 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
404
407
 
405
408
  ## Props
406
409
  color: "info" | "success" | "warning" | "error" | "neutral"
407
- 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)
408
412
  onDismiss: () => void (optional, shows X button)
409
413
  children: ReactNode (message text)
410
414
 
@@ -424,6 +428,11 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
424
428
  - `onDismiss` fires after the exit animation completes, not immediately on dismiss button click
425
429
 
426
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
+
427
436
  ### v0.3.1
428
437
  - **Fixed** BannerProps uses `Omit<HTMLAttributes, 'color'>` to resolve TypeScript conflict with CVA `color` variant
429
438
 
@@ -713,6 +722,9 @@ import { BarChart } from '@devalok/shilp-sutra/ui/charts'
713
722
  - `color="primary"` will be renamed to `color="brand"` in v1.0 — use `color="primary"` for now
714
723
 
715
724
  ## Changes
725
+ ### v0.19.1
726
+ - **Fixed** `active:scale-95` tap feedback broken by Framer Motion transform override — replaced with `whileTap={{ scale: 0.95 }}`
727
+
716
728
  ### v0.4.2
717
729
  - **Changed** (BREAKING) `variant="filled"` renamed to `"subtle"`, `variant="outlined"` renamed to `"outline"`, `onDelete` renamed to `onDismiss`
718
730
 
@@ -1126,6 +1138,9 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1126
1138
  - DialogTitle is required for accessibility — screen readers announce it when the dialog opens
1127
1139
 
1128
1140
  ## Changes
1141
+ ### v0.19.1
1142
+ - **Fixed** Dialog not centered after Framer Motion animation completes — `transform: none` inline style overrode Tailwind `translate-x/y` classes. Centering now handled via Framer Motion `x`/`y` properties.
1143
+
1129
1144
  ### v0.18.0
1130
1145
  - **Changed** Overlay animations migrated to Framer Motion (physics-based springs)
1131
1146
  - **Added** `DialogContentProps`, `DialogTitleProps` type exports
@@ -2902,9 +2917,11 @@ export default function RootLayout({ children }) {
2902
2917
  emptyState?: ReactNode — empty state content
2903
2918
  compact: boolean — tighter spacing, no avatars, smaller text
2904
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? }
2905
2922
 
2906
2923
  ## Defaults
2907
- loading=false, compact=false, hasMore=false
2924
+ loading=false, compact=false, hasMore=false, groupBy='none'
2908
2925
 
2909
2926
  ## Example
2910
2927
  ```jsx
@@ -2919,13 +2936,23 @@ export default function RootLayout({ children }) {
2919
2936
  />
2920
2937
  ```
2921
2938
 
2939
+ ## Exported Utilities
2940
+ groupItemsByTime(items: ActivityItem[], labels?: GroupLabels) — pure function that buckets items into time groups; returns { label: string, items: ActivityItem[] }[]
2941
+
2922
2942
  ## Gotchas
2923
2943
  - `items` is required — passing an empty array renders the `emptyState` content
2924
2944
  - `color` on each item controls the timeline dot color
2925
2945
  - `actor.image` is optional — falls back to initials from `actor.name`
2926
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
2927
2949
 
2928
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
+
2929
2956
  ### v0.18.0
2930
2957
  - **Fixed** `bg-accent-9` changed to `bg-info-9` (info color, not accent)
2931
2958
 
package/llms.txt CHANGED
@@ -167,7 +167,7 @@ Components with two-axis system: Button, Badge, Alert, Chip, Banner, Progress, S
167
167
 
168
168
  ### Feedback & Notifications
169
169
  - Alert: variant(subtle|filled|outline) color(info|success|warning|error|neutral) + title, onDismiss
170
- - Banner: color(info|success|warning|error|neutral) + title, onDismiss
170
+ - Banner: color(info|success|warning|error|neutral) + actions?(ReactNode), onDismiss. `action` (singular) deprecated, use `actions`. Mobile flex-wrap for multiple buttons.
171
171
  - Toast: imperative API via toast.success/error/warning/info/loading/message/undo/promise/upload/custom — REQUIRES <Toaster> at layout root
172
172
  - Spinner: size(sm|md|lg) — renders with role="status"
173
173
  - Progress: track size(sm|md|lg), indicator color(default|success|warning|error)
@@ -238,7 +238,7 @@ NOTIFICATION SELECTION GUIDE:
238
238
  - (UploadProgress REMOVED — upload tracking is now built into toast.upload())
239
239
  - RichTextEditor: Tiptap editor — bold/italic/underline/strike/highlight, headings, blockquote, lists (bullet/ordered/task), code, links, images (paste/drop/upload), file attachments, @mentions, emoji picker + :shortcode:, text alignment, HR. Props: onImageUpload?, onFileUpload?, mentions?, onMentionSearch?, onMentionSelect?(item: MentionItem)
240
240
  - RichTextViewer: read-only renderer for RichTextEditor HTML content (renders all above content types)
241
- - ActivityFeed: items(ActivityItem[]), onLoadMore, loading, hasMore, emptyState, compact, maxInitialItems. Type: ActivityItem = { id, actor?, action, timestamp, icon?, color?, detail? }
241
+ - ActivityFeed: items(ActivityItem[]), onLoadMore, loading, hasMore, emptyState, compact, maxInitialItems, groupBy?('time'|'none'), groupLabels?({ today, yesterday, thisWeek, older }). Type: ActivityItem = { id, actor?, action, timestamp, icon?, color?, detail? }. Utility: groupItemsByTime(items, labels) exported.
242
242
  - CommandPalette, MemberPicker
243
243
  - ErrorDisplay, GlobalLoading
244
244
  - Loading skeletons: CardSkeleton, TableSkeleton, BoardSkeleton, ListSkeleton