@axzydev/axzy_ui_system 1.4.0 → 1.4.2

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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.4.0",
7
+ "version": "1.4.2",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
package/src/App.tsx CHANGED
@@ -14,6 +14,7 @@ import "./index.css";
14
14
  // Import Showcases
15
15
  import { HomeShowcase } from "./showcases/HomeShowcase";
16
16
  import { GettingStartedShowcase } from "./showcases/GettingStartedShowcase";
17
+ import { HelpShowcase } from "./showcases/HelpShowcase";
17
18
  import { SizesShowcase } from "./showcases/SizesShowcase";
18
19
  import {
19
20
  CardShowcase,
@@ -233,6 +234,8 @@ function App() {
233
234
  switch (itemId) {
234
235
  case "getting-started":
235
236
  return <GettingStartedShowcase />;
237
+ case "help":
238
+ return <HelpShowcase />;
236
239
  // Structure
237
240
  case "layout":
238
241
  return <LayoutShowcase />;
@@ -20,6 +20,7 @@ Vertical navigation sidebar with collapsible submenus, tooltips when collapsed,
20
20
  | `subitemConnector` | `"dot" \| "\|" \| "none"` | `"dot"` | Visual connector style for sub-items. |
21
21
  | `className` | `string` | — | Additional CSS classes on the sidebar `<aside>`. |
22
22
  | `header` | `ReactNode` | — | Optional content rendered above the navigation list (e.g. a wordmark). Hidden while the sidebar is collapsed, so it only appears when the hover-expanded rail is shown. |
23
+ | `notification` | `ITSidebarNotification` | — | Optional notification entry (icon + label + `ITBadget` counter) pinned above the navigation. Unlike `header`, it stays visible while collapsed, showing only the icon and counter. |
23
24
 
24
25
  ### ITNavigationItem
25
26
 
@@ -31,7 +32,18 @@ Vertical navigation sidebar with collapsible submenus, tooltips when collapsed,
31
32
  | `action` | `() => void` | Click handler for top-level items without submenus. |
32
33
  | `isActive` | `boolean` | Whether this item is active/highlighted. |
33
34
  | `subitems` | `ITNavigationSubItemEntry[]` | Array of entries: plain sub-items or titled groups (`ITNavigationSubItemGroup`). |
34
- | `badge` | `string` | Badge text (e.g. notification count). |
35
+ | `badge` | `number \| string` | Notification content rendered with `ITBadget` (e.g. an unread count). |
36
+ | `badgeProps` | `ITBadgetProps` | Styling forwarded to the item badge (`color`, `size`, `variant`, `className` for padding). |
37
+
38
+ ### ITSidebarNotification
39
+
40
+ | Prop | Type | Default | Description |
41
+ |------|------|---------|-------------|
42
+ | `count` | `number` | — | Count displayed inside the notification `ITBadget`. |
43
+ | `icon` | `ReactNode` | — | Icon rendered left of the label (e.g. a bell). Omit for a count-only pill. |
44
+ | `label` | `string` | `"Notificaciones"` | Accessible label and tooltip for the notification button. |
45
+ | `onClick` | `() => void` | — | Click handler for the notification button. |
46
+ | `badgeProps` | `ITBadgetProps` | `{ color: "danger", size: "sm" }` | Styling forwarded to the internal `ITBadget`; use `className` for padding. |
35
47
 
36
48
  ### ITNavigationSubItem
37
49
 
@@ -105,13 +117,46 @@ const items = [
105
117
 
106
118
  Discriminate entries with `"items" in entry`. The heading style (uppercase, `0.68rem`, weight 700, `0.08em` tracking, `var(--it-sidebar-icon-color, var(--color-secondary-400))`) is applied by the component and cannot be overridden per group.
107
119
 
120
+ ## Notifications and badges
121
+
122
+ Item badges and the notification row both render the `ITBadget` atom. Use `badge` for a per-item counter and `notification` for a top-level entry (e.g. a bell):
123
+
124
+ ```tsx
125
+ import { ITSidebar } from '@axzydev/axzy_ui_system';
126
+ import { FaBell } from 'react-icons/fa';
127
+
128
+ const items = [
129
+ { id: 'dashboard', label: 'Dashboard', isActive: true, action: () => {} },
130
+ { id: 'inbox', label: 'Inbox', badge: 12, action: () => {} },
131
+ {
132
+ id: 'alerts',
133
+ label: 'Alerts',
134
+ badge: 3,
135
+ badgeProps: { color: 'warning', variant: 'outlined' },
136
+ action: () => {},
137
+ },
138
+ ];
139
+
140
+ <ITSidebar
141
+ navigationItems={items}
142
+ notification={{
143
+ count: 5,
144
+ icon: <FaBell />,
145
+ label: 'Notifications',
146
+ onClick: () => {},
147
+ // Tweak the ITBadget, incl. padding via className.
148
+ badgeProps: { color: 'danger', className: 'px-2' },
149
+ }}
150
+ />
151
+ ```
152
+
108
153
  ## Notes
109
154
 
110
155
  - There is no collapse toggle button: the collapsed 88px rail expands and collapses automatically on pointer hover (300 ms leave delay).
111
156
  - Collapsed mode shrinks to icon-only; a glassmorphism panel shows the item's sub-items on hover. For an item whose `subitems` contain groups, that panel **flattens** every group into one plain list and renders **no** group headings.
112
157
  - Submenus expand/collapse with smooth transitions when a parent item is clicked; the expanded submenu is animated with a `max-h` transition (cap `1000px`).
113
158
  - Active item highlighting uses a colored left-border accent and subtle background tint.
114
- - Badge support renders a notification-style counter on the item.
159
+ - Badge support renders a notification-style counter on the item using the `ITBadget` atom; customize it with `badgeProps` (`color`, `size`, `variant`, `className`). The sidebar also accepts a `notification` entry (icon + label + `ITBadget` counter) pinned above the navigation, which stays visible while collapsed as icon + counter.
115
160
  - `subitemConnector` styles the visual link between parent and sub-items: `dot` (default), `|` (vertical bar), or `none`. Grouping does not change it: the `|` guide is a single `border-left` on the outer submenu `<ul>` only.
116
161
  - An active sub-item inside a group still auto-expands its parent, because auto-expand flattens groups the same way the collapsed flyout does.
117
162
  - `visibleOnMobile` forces the sidebar visible at every breakpoint and disables the collapsed rail, so it stays fully expanded.
@@ -1,3 +1,5 @@
1
+ import type { ITBadgetProps } from "@/components/atoms/badget/badget.props";
2
+
1
3
  /** A sub-navigation item nested under a parent menu. */
2
4
  export interface ITNavigationSubItem {
3
5
  /** Unique identifier for the sub-item. */
@@ -50,8 +52,38 @@ export interface ITNavigationItem {
50
52
  * The field `items` is reserved for {@link ITNavigationSubItemGroup}.
51
53
  */
52
54
  subitems?: ITNavigationSubItemEntry[];
53
- /** Badge text displayed on the item (e.g. notification count). */
54
- badge?: string;
55
+ /**
56
+ * Notification badge content rendered with the {@link ITBadget} atom
57
+ * (e.g. an unread count). Omit it to render no badge.
58
+ */
59
+ badge?: number | string;
60
+ /**
61
+ * Styling forwarded to the `ITBadget` rendered for {@link ITNavigationItem.badge}.
62
+ * `color`, `size`, `variant` and `className` are supported; use `className`
63
+ * to tweak the badge padding/positioning.
64
+ */
65
+ badgeProps?: ITBadgetProps;
66
+ }
67
+
68
+ /**
69
+ * Notification row pinned above the navigation in {@link ITSidebarProps}. It
70
+ * renders an optional icon, a label and an `ITBadget` counter — e.g. a bell with
71
+ * a number. When the rail is collapsed only the icon + counter are shown.
72
+ */
73
+ export interface ITSidebarNotification {
74
+ /** Count displayed inside the notification `ITBadget`. */
75
+ count: number;
76
+ /** Icon rendered left of the label (e.g. a bell). Omit for a count-only pill. */
77
+ icon?: React.ReactNode;
78
+ /** Accessible label and tooltip for the notification button. @default "Notificaciones" */
79
+ label?: string;
80
+ /** Click handler for the notification button. */
81
+ onClick?: () => void;
82
+ /**
83
+ * Styling forwarded to the internal `ITBadget`. Defaults to `color: "danger"`
84
+ * and `size: "sm"`; override `color`/`variant`/`size` or use `className` for padding.
85
+ */
86
+ badgeProps?: ITBadgetProps;
55
87
  }
56
88
 
57
89
  /** Props for the ITSidebar vertical navigation component. */
@@ -77,5 +109,11 @@ export interface ITSidebarProps {
77
109
  * Hidden while the sidebar is collapsed, so it only shows on hover.
78
110
  */
79
111
  header?: React.ReactNode;
112
+ /**
113
+ * Optional notification entry pinned above the navigation (icon + label +
114
+ * `ITBadget` counter). Unlike {@link ITSidebarProps.header} it stays visible
115
+ * while collapsed, showing just the icon and the counter.
116
+ */
117
+ notification?: ITSidebarNotification;
80
118
  }
81
119
 
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import ITSidebar from '@/components/organisms/sidebar/sidebar';
3
- import { FaHome, FaUsers, FaCog, FaChartBar, FaShieldAlt, FaShoppingCart } from 'react-icons/fa';
3
+ import { FaHome, FaUsers, FaCog, FaChartBar, FaShieldAlt, FaShoppingCart, FaRegBell } from 'react-icons/fa';
4
4
  import { expect, fn, userEvent, waitFor } from 'storybook/test';
5
5
 
6
6
  const meta: Meta<typeof ITSidebar> = {
@@ -174,3 +174,61 @@ export const Grouped: Story = {
174
174
  await expect(groupedSubItemActionSpy).toHaveBeenCalled();
175
175
  },
176
176
  };
177
+
178
+ export const WithNotificationBadges: Story = {
179
+ args: {
180
+ navigationItems: [
181
+ { id: 'dashboard', label: 'Dashboard', icon: <FaHome />, isActive: true },
182
+ { id: 'inbox', label: 'Bandeja', icon: <FaUsers />, badge: 12 },
183
+ {
184
+ id: 'alerts',
185
+ label: 'Alertas',
186
+ icon: <FaShieldAlt />,
187
+ badge: 3,
188
+ badgeProps: { color: 'warning', variant: 'outlined' },
189
+ },
190
+ { id: 'settings', label: 'Configuración', icon: <FaCog />, action: fn() },
191
+ ],
192
+ notification: {
193
+ count: 5,
194
+ icon: <FaRegBell />,
195
+ label: 'Notificaciones',
196
+ onClick: fn(),
197
+ },
198
+ isCollapsed: false,
199
+ visibleOnMobile: true,
200
+ },
201
+ render: (args) => (
202
+ <div className="h-screen bg-gray-50 flex">
203
+ <ITSidebar {...args} />
204
+ <div className="flex-1 p-8 text-zinc-500 font-medium">
205
+ Cada ítem puede llevar un ITBadget con número (`badge` + `badgeProps`) y el sidebar admite una fila de notificación con contador (`notification`).
206
+ </div>
207
+ </div>
208
+ ),
209
+ };
210
+
211
+ export const CollapsedNotifications: Story = {
212
+ args: {
213
+ navigationItems: [
214
+ { id: 'dashboard', label: 'Dashboard', icon: <FaHome />, isActive: true },
215
+ { id: 'inbox', label: 'Bandeja', icon: <FaUsers />, badge: 12 },
216
+ { id: 'settings', label: 'Configuración', icon: <FaCog /> },
217
+ ],
218
+ notification: {
219
+ count: 9,
220
+ icon: <FaRegBell />,
221
+ label: 'Notificaciones',
222
+ },
223
+ isCollapsed: true,
224
+ visibleOnMobile: true,
225
+ },
226
+ render: (args) => (
227
+ <div className="h-screen bg-gray-50 flex">
228
+ <ITSidebar {...args} />
229
+ <div className="flex-1 p-8 text-zinc-500 font-medium">
230
+ Al colapsar, la fila de notificación queda como icono + contador, y los badges de cada ítem siguen visibles como píldoras con número.
231
+ </div>
232
+ </div>
233
+ ),
234
+ };
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
+ import clsx from "clsx";
2
3
  import { FaChevronDown } from "react-icons/fa";
3
4
  import {
4
5
  ITNavigationItem,
@@ -7,6 +8,7 @@ import {
7
8
  ITNavigationSubItemGroup,
8
9
  ITSidebarProps,
9
10
  } from "./sidebar.props";
11
+ import ITBadget from "@/components/atoms/badget/badget";
10
12
  import ITText from "@/components/atoms/text/text";
11
13
 
12
14
  /** Narrows a submenu entry to a titled group. */
@@ -42,7 +44,14 @@ function flattenSubItems(
42
44
  * { id: "billing", label: "Billing", isActive: true },
43
45
  * ],
44
46
  * },
47
+ * { id: "inbox", label: "Inbox", icon: <FaEnvelope />, badge: 12 },
45
48
  * ]}
49
+ * notification={{
50
+ * count: 5,
51
+ * icon: <FaBell />,
52
+ * label: "Notificaciones",
53
+ * onClick: () => navigate("/notifications"),
54
+ * }}
46
55
  * isCollapsed
47
56
  * />
48
57
  * ```
@@ -56,6 +65,7 @@ export default function ITSidebar({
56
65
  onSubItemClick,
57
66
  subitemConnector = 'dot',
58
67
  header,
68
+ notification,
59
69
  }: ITSidebarProps) {
60
70
  const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
61
71
  const [isHovering, setIsHovering] = useState(false);
@@ -217,6 +227,88 @@ export default function ITSidebar({
217
227
  );
218
228
  };
219
229
 
230
+ const renderNotification = () => {
231
+ if (!notification) return null;
232
+
233
+ const {
234
+ count,
235
+ icon,
236
+ label = "Notificaciones",
237
+ onClick,
238
+ badgeProps,
239
+ } = notification;
240
+ const { className: badgeClassName, ...badgeRest } = badgeProps ?? {};
241
+
242
+ const badge = (
243
+ <ITBadget
244
+ color="danger"
245
+ size="sm"
246
+ {...badgeRest}
247
+ className={clsx("shadow-md", badgeClassName)}
248
+ >
249
+ {String(count)}
250
+ </ITBadget>
251
+ );
252
+
253
+ if (isSidebarCollapsed) {
254
+ return (
255
+ <button
256
+ type="button"
257
+ aria-label={label}
258
+ title={label}
259
+ onClick={onClick}
260
+ className="relative flex items-center justify-center w-full p-2 mb-1 rounded-xl transition-all duration-300"
261
+ style={{ color: "var(--it-sidebar-icon-color, var(--color-secondary-500))" }}
262
+ onMouseEnter={(e) => {
263
+ e.currentTarget.style.backgroundColor = "var(--it-sidebar-hover-bg, var(--color-secondary-100))";
264
+ }}
265
+ onMouseLeave={(e) => {
266
+ e.currentTarget.style.backgroundColor = "transparent";
267
+ }}
268
+ >
269
+ {icon && (
270
+ <span className="flex items-center justify-center text-[1.05rem]">{icon}</span>
271
+ )}
272
+ <span className="absolute top-0.5 right-0.5 pointer-events-none">{badge}</span>
273
+ </button>
274
+ );
275
+ }
276
+
277
+ return (
278
+ <button
279
+ type="button"
280
+ aria-label={label}
281
+ title={label}
282
+ onClick={onClick}
283
+ className="flex items-center gap-2.5 w-full text-left px-3 py-2 mb-1 rounded-xl transition-all duration-300"
284
+ style={{ color: "var(--it-sidebar-label-color, var(--color-secondary-600))" }}
285
+ onMouseEnter={(e) => {
286
+ e.currentTarget.style.backgroundColor = "var(--it-sidebar-hover-bg, var(--color-secondary-100))";
287
+ }}
288
+ onMouseLeave={(e) => {
289
+ e.currentTarget.style.backgroundColor = "transparent";
290
+ }}
291
+ >
292
+ {icon && (
293
+ <span
294
+ className="flex-shrink-0 flex items-center justify-center text-[1.05rem]"
295
+ style={{ color: "var(--it-sidebar-icon-color, #9ca3af)" }}
296
+ >
297
+ {icon}
298
+ </span>
299
+ )}
300
+ <ITText
301
+ as="span"
302
+ className="truncate tracking-wide"
303
+ style={{ fontSize: "0.8rem", fontWeight: 500 }}
304
+ >
305
+ {label}
306
+ </ITText>
307
+ <span className="ml-auto flex-shrink-0">{badge}</span>
308
+ </button>
309
+ );
310
+ };
311
+
220
312
  return (
221
313
  <aside
222
314
  ref={sidebarRef}
@@ -240,12 +332,17 @@ export default function ITSidebar({
240
332
  <div className="px-4 pt-4 pb-1 flex-shrink-0">{header}</div>
241
333
  )}
242
334
 
335
+ {notification && (
336
+ <div className="px-4 pt-3 flex-shrink-0">{renderNotification()}</div>
337
+ )}
338
+
243
339
  {/* Navigation Items */}
244
340
  <nav aria-label="Componentes de la librería" className="flex-1 py-4 overflow-y-auto overflow-x-hidden custom-scrollbar px-4">
245
341
  <ul className="space-y-1">
246
342
  {navigationItems.map((item) => {
247
343
  const hasSubmenu = !!item.subitems && item.subitems.length > 0;
248
344
  const submenuId = `it-sidebar-submenu-${item.id}`;
345
+ const { className: badgeClassName, ...badgeRest } = item.badgeProps ?? {};
249
346
  return (
250
347
  <li key={item.id} className="relative group/navitem">
251
348
  <div
@@ -321,22 +418,21 @@ export default function ITSidebar({
321
418
  </div>
322
419
  )}
323
420
 
324
- {item.badge && (
325
- <span
326
- className={`
327
- absolute flex items-center justify-center font-bold shadow-md
328
- ${isSidebarCollapsed
329
- ? "top-1 right-1 w-2.5 h-2.5 rounded-full ring-2 ring-white"
330
- : "right-3 top-1/2 transform -translate-y-1/2 px-1.5 py-0 text-[9px] rounded-full backdrop-blur-sm"}
331
- `}
332
- style={{
333
- backgroundColor: "var(--it-sidebar-badge-bg, var(--color-primary-500))",
334
- color: "var(--it-sidebar-badge-color, #ffffff)",
335
- boxShadow: isSidebarCollapsed ? "0 0 0 2px var(--it-sidebar-bg, var(--color-secondary-900))" : 'none'
336
- }}
421
+ {item.badge != null && item.badge !== "" && (
422
+ <ITBadget
423
+ color="primary"
424
+ size="sm"
425
+ {...badgeRest}
426
+ className={clsx(
427
+ "absolute shadow-md",
428
+ isSidebarCollapsed
429
+ ? "top-0.5 right-0.5"
430
+ : "right-3 top-1/2 -translate-y-1/2",
431
+ badgeClassName,
432
+ )}
337
433
  >
338
- {isSidebarCollapsed ? "" : item.badge}
339
- </span>
434
+ {String(item.badge)}
435
+ </ITBadget>
340
436
  )}
341
437
  </div>
342
438
 
package/src/index.ts CHANGED
@@ -103,7 +103,7 @@ import type { ITSearchSelectProps } from "./components/molecules/search-select/s
103
103
  import type { ITSelectProps } from "./components/molecules/select/select.props";
104
104
  import type { ITSearchTableProps } from "./components/organisms/searchTable/searchTable.props";
105
105
  import type { ITSegmentedControlProps } from "./components/atoms/segmented-control/segmented-control.props";
106
- import type { ITSidebarProps, ITNavigationItem, ITNavigationSubItem, ITNavigationSubItemEntry, ITNavigationSubItemGroup } from "./components/organisms/sidebar/sidebar.props";
106
+ import type { ITSidebarProps, ITSidebarNotification, ITNavigationItem, ITNavigationSubItem, ITNavigationSubItemEntry, ITNavigationSubItemGroup } from "./components/organisms/sidebar/sidebar.props";
107
107
  import type { ITSkeletonProps } from "./components/atoms/skeleton/skeleton.props";
108
108
  import type { ITSlideToggleProps } from "./components/atoms/slide/slide.props";
109
109
  import type { ITSliderProps } from "./components/atoms/slider/slider.props";
@@ -176,6 +176,7 @@ export {
176
176
  ITPageProps, ITPageHeaderProps,
177
177
  ITNavigationItem, ITNavigationSubItem,
178
178
  ITNavigationSubItemEntry, ITNavigationSubItemGroup,
179
+ ITSidebarNotification,
179
180
  ITPaginationProps, ITPopoverProps, ITProgressProps,
180
181
  ITRadioGroupProps, ITRadioOption, ITSelectProps, ITSearchSelectProps,
181
182
  ITSearchTableProps,
@@ -72,7 +72,7 @@ const GROUP_ACCENTS = [
72
72
  "from-primary-500 to-purple-500",
73
73
  ];
74
74
 
75
- const GITHUB_URL = "https://github.com/axzydev/axzy_ui_system";
75
+ const GITHUB_URL = "https://github.com/maag070208/AXZY_UI_SYSTEM";
76
76
  const INSTALL_CMD = "pnpm add @axzydev/axzy_ui_system";
77
77
 
78
78
  /**
@@ -30,6 +30,7 @@ export const SANDBOX_GROUPS: SandboxGroup[] = [
30
30
  label: "General",
31
31
  subitems: [
32
32
  { id: "getting-started", label: "Getting Started" },
33
+ { id: "help", label: "Ayuda · Solución de Bugs" },
33
34
  { id: "sizes", label: "Medidas · sm / md / lg" },
34
35
  ],
35
36
  },