@caratlogic/design-system 0.1.11 → 0.1.12
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/components/layouts/AppShell/AppShell.types.d.ts +14 -1
- package/dist/components/layouts/AppShell/AppShell.types.d.ts.map +1 -1
- package/dist/components/layouts/AppShell/DashboardShell.d.ts.map +1 -1
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.d.ts +8 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.types.d.ts +22 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.types.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.utils.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.utils.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/index.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/index.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.d.ts +9 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.d.ts +9 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.d.ts +12 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.d.ts +41 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.d.ts +4 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/index.d.ts +5 -0
- package/dist/components/organisms/InAppNotificationsPanel/index.d.ts.map +1 -0
- package/dist/components/organisms/Navbar/Navbar.d.ts +1 -1
- package/dist/components/organisms/Navbar/Navbar.d.ts.map +1 -1
- package/dist/index.cjs.js +159 -16
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +155 -17
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/relativeTime.d.ts +6 -0
- package/dist/utils/relativeTime.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -52,7 +52,12 @@ export interface AppShellMenuAction {
|
|
|
52
52
|
onClick: () => void;
|
|
53
53
|
destructive?: boolean;
|
|
54
54
|
}
|
|
55
|
-
export
|
|
55
|
+
export interface AppShellNotificationsBadge {
|
|
56
|
+
tone?: 'destructive' | 'success' | 'warning';
|
|
57
|
+
/** Renders a numeric badge on the bell instead of a plain dot. */
|
|
58
|
+
count?: ReactNode;
|
|
59
|
+
}
|
|
60
|
+
export type AppShellNotificationsIndicator = 'destructive' | 'success' | 'warning' | false | AppShellNotificationsBadge;
|
|
56
61
|
export interface DashboardShellProps {
|
|
57
62
|
brand: AppShellBrand;
|
|
58
63
|
/** Pre-gated, pre-grouped navigation. The app resolves `visible` + sections. */
|
|
@@ -74,6 +79,14 @@ export interface DashboardShellProps {
|
|
|
74
79
|
showNotifications?: boolean;
|
|
75
80
|
notificationsIndicator?: AppShellNotificationsIndicator;
|
|
76
81
|
onNotificationsClick?: () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Rendered in a popover anchored to the bell. When provided, the bell becomes
|
|
84
|
+
* the popover trigger; without it the bell keeps its plain click behaviour.
|
|
85
|
+
*/
|
|
86
|
+
notificationsPanel?: ReactNode;
|
|
87
|
+
/** Controlled open state for `notificationsPanel`. */
|
|
88
|
+
notificationsOpen?: boolean;
|
|
89
|
+
onNotificationsOpenChange?: (open: boolean) => void;
|
|
77
90
|
/** App-specific header actions (e.g. a barcode scanner), rendered leftmost. */
|
|
78
91
|
headerActionsSlot?: ReactNode;
|
|
79
92
|
/** Sidebar width CSS value. @default '248px' */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.types.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/AppShell/AppShell.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;yDACyD;AACzD,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,IAAI,EAAE,SAAS,CAAC;IAChB,iEAAiE;IACjE,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,8BAA8B,GACtC,aAAa,GACb,SAAS,GACT,SAAS,GACT,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"AppShell.types.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/AppShell/AppShell.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardShell.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/AppShell/DashboardShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DashboardShell.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/AppShell/DashboardShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,EAAE,EAAE,MAAM,OAAO,CAAC;AAqD/C,OAAO,KAAK,EAMV,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AA+Z1B,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CA4DlD,CAAC"}
|
package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationCard.accessibility.d.ts","sourceRoot":"","sources":["../../../../src/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB;;CAE5B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { InAppNotificationCardProps } from './InAppNotificationCard.types';
|
|
3
|
+
/**
|
|
4
|
+
* One row in a notification list. Unread state is signalled by a dot, a tinted
|
|
5
|
+
* surface and a heavier title, so it never relies on colour alone.
|
|
6
|
+
*/
|
|
7
|
+
export declare const InAppNotificationCard: FC<InAppNotificationCardProps>;
|
|
8
|
+
//# sourceMappingURL=InAppNotificationCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationCard.d.ts","sourceRoot":"","sources":["../../../../src/components/molecules/InAppNotificationCard/InAppNotificationCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAkBhC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAEhF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CAsDhE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** A single entry in a notification list. Transport-agnostic by design. */
|
|
3
|
+
export interface InAppNotificationItem {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
message: string;
|
|
7
|
+
/** ISO timestamp or Date — rendered as a relative label. */
|
|
8
|
+
createdAt: string | Date;
|
|
9
|
+
isRead: boolean;
|
|
10
|
+
/** Optional secondary line, e.g. a company or contact name. */
|
|
11
|
+
subject?: string;
|
|
12
|
+
/** Overrides the default leading icon for this entry. */
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export interface InAppNotificationCardProps {
|
|
16
|
+
notification: InAppNotificationItem;
|
|
17
|
+
onSelect: (notification: InAppNotificationItem) => void;
|
|
18
|
+
/** Leading icon used when the item does not supply its own. */
|
|
19
|
+
defaultIcon?: ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=InAppNotificationCard.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationCard.types.d.ts","sourceRoot":"","sources":["../../../../src/components/molecules/InAppNotificationCard/InAppNotificationCard.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,qBAAqB,CAAC;IACpC,QAAQ,EAAE,CAAC,YAAY,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACxD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Read entries sit flat on the surface; unread ones get a primary wash. */
|
|
2
|
+
export declare function getInAppNotificationCardClasses(isRead: boolean, className?: string): string;
|
|
3
|
+
export declare function getInAppNotificationIconClasses(isRead: boolean): string;
|
|
4
|
+
//# sourceMappingURL=InAppNotificationCard.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationCard.utils.d.ts","sourceRoot":"","sources":["../../../../src/components/molecules/InAppNotificationCard/InAppNotificationCard.utils.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,OAAO,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CASR;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAKvE"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { InAppNotificationCard } from './InAppNotificationCard';
|
|
2
|
+
export type { InAppNotificationCardProps, InAppNotificationItem, } from './InAppNotificationCard.types';
|
|
3
|
+
export { inAppNotificationCardA11y } from './InAppNotificationCard.accessibility';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/molecules/InAppNotificationCard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC"}
|
package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const inAppNotificationsPanelA11y: {
|
|
2
|
+
readonly panelLabel: "Notifications";
|
|
3
|
+
readonly listLabel: "Notification list";
|
|
4
|
+
readonly markAllReadLabel: "Mark all notifications as read";
|
|
5
|
+
readonly retryLabel: "Retry loading notifications";
|
|
6
|
+
};
|
|
7
|
+
/** Non-modal dropdown, so `dialog` rather than `alertdialog`. */
|
|
8
|
+
export declare const IN_APP_NOTIFICATIONS_PANEL_ROLE: "dialog";
|
|
9
|
+
//# sourceMappingURL=InAppNotificationsPanel.accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationsPanel.accessibility.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B;;;;;CAK9B,CAAC;AAEX,iEAAiE;AACjE,eAAO,MAAM,+BAA+B,EAAG,QAAiB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { InAppNotificationsPanelProps } from './InAppNotificationsPanel.types';
|
|
3
|
+
/**
|
|
4
|
+
* Notification list with header actions and loading / error / empty states.
|
|
5
|
+
* Positioning is the caller's concern — render it inside a popover anchored to
|
|
6
|
+
* whatever opens it (see `DashboardShell`'s `notificationsPanel` slot).
|
|
7
|
+
*/
|
|
8
|
+
export declare const InAppNotificationsPanel: FC<InAppNotificationsPanelProps>;
|
|
9
|
+
//# sourceMappingURL=InAppNotificationsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationsPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAuBhC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,EAAE,CAAC,4BAA4B,CAgJpE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { InAppNotificationItem } from '@/components/molecules/InAppNotificationCard';
|
|
3
|
+
import type { InAppNotificationsPanelState } from './InAppNotificationsPanel.types';
|
|
4
|
+
/** Derived display state for the panel header and body. */
|
|
5
|
+
export declare function useInAppNotificationsPanel(notifications: InAppNotificationItem[], unreadCount?: number): InAppNotificationsPanelState;
|
|
6
|
+
/**
|
|
7
|
+
* Fires `onLoadMore` when the sentinel scrolls into view inside `rootRef`.
|
|
8
|
+
* No-ops where IntersectionObserver is unavailable (jsdom, older browsers), so
|
|
9
|
+
* the list simply stops auto-loading rather than breaking.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useInfiniteScrollSentinel(rootRef: RefObject<HTMLElement | null>, onLoadMore: (() => void) | undefined, enabled: boolean): RefObject<HTMLDivElement | null>;
|
|
12
|
+
//# sourceMappingURL=InAppNotificationsPanel.hooks.d.ts.map
|
package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationsPanel.hooks.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AAC1F,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAEpF,2DAA2D;AAC3D,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,qBAAqB,EAAE,EACtC,WAAW,CAAC,EAAE,MAAM,GACnB,4BAA4B,CAS9B;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACtC,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,EACpC,OAAO,EAAE,OAAO,GACf,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAqBlC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { InAppNotificationItem } from '@/components/molecules/InAppNotificationCard';
|
|
3
|
+
export interface InAppNotificationsPanelProps {
|
|
4
|
+
/** Rendered in the order given — the caller owns sorting. */
|
|
5
|
+
notifications: InAppNotificationItem[];
|
|
6
|
+
onSelect: (notification: InAppNotificationItem) => void;
|
|
7
|
+
/** Total unread, which may exceed the fetched page. Defaults to counting `notifications`. */
|
|
8
|
+
unreadCount?: number;
|
|
9
|
+
/** Total notifications in the inbox — shown as the header count. */
|
|
10
|
+
totalCount?: number;
|
|
11
|
+
isLoading?: boolean;
|
|
12
|
+
/** More pages available — reveals the infinite-scroll sentinel. */
|
|
13
|
+
hasNextPage?: boolean;
|
|
14
|
+
/** True while the next page is in flight; shows an inline loader. */
|
|
15
|
+
isFetchingNextPage?: boolean;
|
|
16
|
+
/** Called once when the sentinel scrolls into view. */
|
|
17
|
+
onLoadMore?: () => void;
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
onRetry?: () => void;
|
|
20
|
+
onMarkAllRead?: () => void;
|
|
21
|
+
isMarkingAllRead?: boolean;
|
|
22
|
+
title?: string;
|
|
23
|
+
/** Empty-state heading. @default 'No notifications yet' */
|
|
24
|
+
emptyTitle?: string;
|
|
25
|
+
/** Optional second line explaining what will appear here. */
|
|
26
|
+
emptyDescription?: string;
|
|
27
|
+
/** Replaces the default bell glyph in the empty state. */
|
|
28
|
+
emptyIcon?: ReactNode;
|
|
29
|
+
/** Full escape hatch — replaces the whole empty state. */
|
|
30
|
+
emptyState?: ReactNode;
|
|
31
|
+
errorMessage?: string;
|
|
32
|
+
/** Leading icon for entries that do not supply their own. */
|
|
33
|
+
defaultIcon?: ReactNode;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface InAppNotificationsPanelState {
|
|
37
|
+
unreadCount: number;
|
|
38
|
+
hasUnread: boolean;
|
|
39
|
+
isEmpty: boolean;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=InAppNotificationsPanel.types.d.ts.map
|
package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationsPanel.types.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AAE1F,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,QAAQ,EAAE,CAAC,YAAY,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACxD,6FAA6F;IAC7F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { InAppNotificationItem } from '@/components/molecules/InAppNotificationCard';
|
|
2
|
+
/** Falls back to counting the current page when the caller has no total. */
|
|
3
|
+
export declare function resolveUnreadCount(notifications: InAppNotificationItem[], unreadCount?: number): number;
|
|
4
|
+
//# sourceMappingURL=InAppNotificationsPanel.utils.d.ts.map
|
package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InAppNotificationsPanel.utils.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AAE1F,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,qBAAqB,EAAE,EACtC,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAGR"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { InAppNotificationsPanel } from './InAppNotificationsPanel';
|
|
2
|
+
export { useInAppNotificationsPanel } from './InAppNotificationsPanel.hooks';
|
|
3
|
+
export type { InAppNotificationsPanelProps, InAppNotificationsPanelState, } from './InAppNotificationsPanel.types';
|
|
4
|
+
export { inAppNotificationsPanelA11y } from './InAppNotificationsPanel.accessibility';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/InAppNotificationsPanel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EACV,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yCAAyC,CAAC"}
|
|
@@ -3,7 +3,7 @@ import type { NavbarActionsProps, NavbarAvatarProps, NavbarIconButtonProps, Navb
|
|
|
3
3
|
export declare const Navbar: import("react").ForwardRefExoticComponent<NavbarProps & import("react").RefAttributes<HTMLElement>>;
|
|
4
4
|
export declare const NavbarTitle: FC<NavbarTitleProps>;
|
|
5
5
|
export declare const NavbarActions: FC<NavbarActionsProps>;
|
|
6
|
-
export declare const NavbarIconButton:
|
|
6
|
+
export declare const NavbarIconButton: import("react").ForwardRefExoticComponent<NavbarIconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
7
7
|
export declare const NavbarSearch: FC<NavbarSearchProps>;
|
|
8
8
|
export declare const NavbarAvatar: FC<NavbarAvatarProps>;
|
|
9
9
|
//# sourceMappingURL=Navbar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Navbar.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/Navbar/Navbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAqB3C,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAEjB,qBAAqB,EAGrB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAwCxB,eAAO,MAAM,MAAM,qGAiBlB,CAAC;AAKF,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAmE5C,CAAC;AAIF,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAShD,CAAC;AAIF,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"Navbar.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/Navbar/Navbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAqB3C,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAEjB,qBAAqB,EAGrB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAwCxB,eAAO,MAAM,MAAM,qGAiBlB,CAAC;AAKF,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAmE5C,CAAC;AAIF,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAShD,CAAC;AAIF,eAAO,MAAM,gBAAgB,qHAmD5B,CAAC;AAMF,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CA+C9C,CAAC;AAIF,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CA4B9C,CAAC"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -2698,12 +2698,12 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
2698
2698
|
*/
|
|
2699
2699
|
|
|
2700
2700
|
|
|
2701
|
-
const __iconNode$
|
|
2701
|
+
const __iconNode$18 = [
|
|
2702
2702
|
["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
|
|
2703
2703
|
["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
|
|
2704
2704
|
["path", { d: "M10 12h4", key: "a56b0p" }]
|
|
2705
2705
|
];
|
|
2706
|
-
const Archive = createLucideIcon("archive", __iconNode$
|
|
2706
|
+
const Archive = createLucideIcon("archive", __iconNode$18);
|
|
2707
2707
|
|
|
2708
2708
|
/**
|
|
2709
2709
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2713,11 +2713,11 @@ const Archive = createLucideIcon("archive", __iconNode$17);
|
|
|
2713
2713
|
*/
|
|
2714
2714
|
|
|
2715
2715
|
|
|
2716
|
-
const __iconNode$
|
|
2716
|
+
const __iconNode$17 = [
|
|
2717
2717
|
["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
|
|
2718
2718
|
["path", { d: "M19 12H5", key: "x3x0zl" }]
|
|
2719
2719
|
];
|
|
2720
|
-
const ArrowLeft = createLucideIcon("arrow-left", __iconNode$
|
|
2720
|
+
const ArrowLeft = createLucideIcon("arrow-left", __iconNode$17);
|
|
2721
2721
|
|
|
2722
2722
|
/**
|
|
2723
2723
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2727,11 +2727,11 @@ const ArrowLeft = createLucideIcon("arrow-left", __iconNode$16);
|
|
|
2727
2727
|
*/
|
|
2728
2728
|
|
|
2729
2729
|
|
|
2730
|
-
const __iconNode$
|
|
2730
|
+
const __iconNode$16 = [
|
|
2731
2731
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
2732
2732
|
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
2733
2733
|
];
|
|
2734
|
-
const ArrowRight = createLucideIcon("arrow-right", __iconNode$
|
|
2734
|
+
const ArrowRight = createLucideIcon("arrow-right", __iconNode$16);
|
|
2735
2735
|
|
|
2736
2736
|
/**
|
|
2737
2737
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2741,11 +2741,32 @@ const ArrowRight = createLucideIcon("arrow-right", __iconNode$15);
|
|
|
2741
2741
|
*/
|
|
2742
2742
|
|
|
2743
2743
|
|
|
2744
|
-
const __iconNode$
|
|
2744
|
+
const __iconNode$15 = [
|
|
2745
2745
|
["path", { d: "M4.929 4.929 19.07 19.071", key: "196cmz" }],
|
|
2746
2746
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
|
|
2747
2747
|
];
|
|
2748
|
-
const Ban = createLucideIcon("ban", __iconNode$
|
|
2748
|
+
const Ban = createLucideIcon("ban", __iconNode$15);
|
|
2749
|
+
|
|
2750
|
+
/**
|
|
2751
|
+
* @license lucide-react v0.539.0 - ISC
|
|
2752
|
+
*
|
|
2753
|
+
* This source code is licensed under the ISC license.
|
|
2754
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
2755
|
+
*/
|
|
2756
|
+
|
|
2757
|
+
|
|
2758
|
+
const __iconNode$14 = [
|
|
2759
|
+
["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
|
|
2760
|
+
[
|
|
2761
|
+
"path",
|
|
2762
|
+
{
|
|
2763
|
+
d: "M13.916 2.314A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.74 7.327A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673 9 9 0 0 1-.585-.665",
|
|
2764
|
+
key: "1tip0g"
|
|
2765
|
+
}
|
|
2766
|
+
],
|
|
2767
|
+
["circle", { cx: "18", cy: "8", r: "3", key: "1g0gzu" }]
|
|
2768
|
+
];
|
|
2769
|
+
const BellDot = createLucideIcon("bell-dot", __iconNode$14);
|
|
2749
2770
|
|
|
2750
2771
|
/**
|
|
2751
2772
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -28257,6 +28278,53 @@ const FilterSelect = ({ label, value, onValueChange, options, resetValue = 'all'
|
|
|
28257
28278
|
return (jsxRuntime.jsxs(Select, { value: value, onValueChange: onValueChange, keepOpenOnSelect: true, children: [jsxRuntime.jsxs(SelectTrigger, { className: cn(TRIGGER_BASE, active && TRIGGER_ACTIVE, triggerClassName), children: [jsxRuntime.jsx(Funnel, { className: cn('h-4 w-4 shrink-0', active ? 'text-primary' : 'text-muted-foreground'), "aria-hidden": "true" }), jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(SelectValue, { placeholder: label }), active && (jsxRuntime.jsx("span", { className: "ml-1 inline-flex h-4 min-w-4 items-center justify-center rounded bg-primary/10 px-1 text-[10px] font-semibold leading-none text-primary", children: activeCount }))] })] }), jsxRuntime.jsxs(SelectContent, { className: cn(CONTENT_BASE, contentClassName), searchable: searchable, children: [jsxRuntime.jsx(SelectItem, { value: resetValue, withCheckbox: true, children: label }), options.map((opt) => (jsxRuntime.jsx(SelectItem, { value: opt.value, searchValue: opt.searchValue, withCheckbox: true, children: opt.label }, opt.value)))] })] }));
|
|
28258
28279
|
};
|
|
28259
28280
|
|
|
28281
|
+
/**
|
|
28282
|
+
* Compact "time ago" label, e.g. "5m ago". Falls back to a locale date past a
|
|
28283
|
+
* week so older entries stay scannable.
|
|
28284
|
+
*/
|
|
28285
|
+
function formatRelativeTime(value) {
|
|
28286
|
+
if (!value)
|
|
28287
|
+
return '';
|
|
28288
|
+
const then = value instanceof Date ? value.getTime() : new Date(value).getTime();
|
|
28289
|
+
if (Number.isNaN(then))
|
|
28290
|
+
return '';
|
|
28291
|
+
const minutes = Math.floor((Date.now() - then) / 60000);
|
|
28292
|
+
if (minutes < 1)
|
|
28293
|
+
return 'Just now';
|
|
28294
|
+
if (minutes < 60)
|
|
28295
|
+
return `${minutes}m ago`;
|
|
28296
|
+
const hours = Math.floor(minutes / 60);
|
|
28297
|
+
if (hours < 24)
|
|
28298
|
+
return `${hours}h ago`;
|
|
28299
|
+
const days = Math.floor(hours / 24);
|
|
28300
|
+
if (days < 7)
|
|
28301
|
+
return `${days}d ago`;
|
|
28302
|
+
return new Date(then).toLocaleDateString();
|
|
28303
|
+
}
|
|
28304
|
+
|
|
28305
|
+
/** Read entries sit flat on the surface; unread ones get a primary wash. */
|
|
28306
|
+
function getInAppNotificationCardClasses(isRead, className) {
|
|
28307
|
+
return cn('flex h-[5.125rem] w-full cursor-pointer gap-2.5 rounded-lg border px-3 py-3 text-left transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50', isRead
|
|
28308
|
+
? 'border-border bg-card hover:bg-muted/40'
|
|
28309
|
+
: 'border-primary/20 bg-primary/5 hover:bg-primary/10', className);
|
|
28310
|
+
}
|
|
28311
|
+
function getInAppNotificationIconClasses(isRead) {
|
|
28312
|
+
return cn('mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full', isRead ? 'bg-muted text-muted-foreground' : 'bg-primary/10 text-primary');
|
|
28313
|
+
}
|
|
28314
|
+
|
|
28315
|
+
const inAppNotificationCardA11y = {
|
|
28316
|
+
unreadLabel: 'Unread',
|
|
28317
|
+
};
|
|
28318
|
+
|
|
28319
|
+
/**
|
|
28320
|
+
* One row in a notification list. Unread state is signalled by a dot, a tinted
|
|
28321
|
+
* surface and a heavier title, so it never relies on colour alone.
|
|
28322
|
+
*/
|
|
28323
|
+
const InAppNotificationCard = ({ notification, onSelect, defaultIcon, className, }) => {
|
|
28324
|
+
const { title, message, createdAt, isRead, subject, icon } = notification;
|
|
28325
|
+
return (jsxRuntime.jsxs("button", { type: "button", onClick: () => onSelect(notification), className: getInAppNotificationCardClasses(isRead, className), children: [jsxRuntime.jsx("span", { className: getInAppNotificationIconClasses(isRead), children: icon ?? defaultIcon ?? (jsxRuntime.jsx(BellDot, { className: "size-4", "aria-hidden": "true" })) }), jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [jsxRuntime.jsxs("span", { className: "flex items-baseline gap-2", children: [!isRead && (jsxRuntime.jsx("span", { role: "img", "aria-label": inAppNotificationCardA11y.unreadLabel, className: "size-1.5 shrink-0 rounded-full bg-primary" })), jsxRuntime.jsx("span", { className: cn('min-w-0 flex-1 truncate text-sm text-foreground', isRead ? 'font-medium' : 'font-semibold'), children: title }), jsxRuntime.jsx("time", { className: "shrink-0 text-[0.6875rem] text-muted-foreground tabular-nums", children: formatRelativeTime(createdAt) })] }), jsxRuntime.jsx("span", { className: "mt-1 line-clamp-1 text-xs text-secondary-foreground", children: message }), subject && (jsxRuntime.jsx("span", { className: "mt-0.5 block truncate text-[0.6875rem] font-medium text-muted-foreground", children: subject }))] })] }));
|
|
28326
|
+
};
|
|
28327
|
+
|
|
28260
28328
|
const HEADER_TONE_CLASSES = {
|
|
28261
28329
|
neutral: 'bg-[var(--section-header)]',
|
|
28262
28330
|
gem: 'bg-primary/5 dark:bg-[#173626]',
|
|
@@ -65655,6 +65723,71 @@ const FileUploader = ({ accept, multiple = true, maxSize = 50, maxFiles = 5, lab
|
|
|
65655
65723
|
return (jsxRuntime.jsxs("div", { className: cn('flex flex-col gap-4', className), children: [label && (jsxRuntime.jsx("label", { className: "text-sm font-medium text-foreground", children: label })), jsxRuntime.jsx(DropZone, { uploadSubject: uploadSubject, supportedFormats: supportedFormats, maxSize: maxSize, maxFiles: maxFiles, accept: accept, multiple: multiple, disabled: disabled, onFiles: handlePickerFiles }), files.length > 0 && (jsxRuntime.jsxs("div", { className: "space-y-2", children: [jsxRuntime.jsx("p", { className: "text-[13px] text-label", children: "Uploaded File" }), jsxRuntime.jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: onDragEnd, children: jsxRuntime.jsx(SortableContext, { items: files.map((f) => f.id), strategy: rectSortingStrategy, children: jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3", children: files.map((file) => (jsxRuntime.jsx(FileCard, { file: file, sortable: sortable, disabled: disabled, onRemove: handleRemove, onPreview: previewFile }, file.id))) }) }) })] })), enableUrlInput && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm font-normal text-label", children: [jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" }), jsxRuntime.jsx("span", { children: "or" }), jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })] }), jsxRuntime.jsx(UrlImportRow, { disabled: disabled, onAdd: handleAddUrl })] })), cropImages && currentCropFile && cropPreviewUrl && (jsxRuntime.jsx(ImageCropModal, { open: true, imageUrl: cropPreviewUrl, fileName: currentCropFile.name, fileType: currentCropFile.type, onCancel: handleCropCancel, onConfirm: handleCropConfirm }))] }));
|
|
65656
65724
|
};
|
|
65657
65725
|
|
|
65726
|
+
/** Falls back to counting the current page when the caller has no total. */
|
|
65727
|
+
function resolveUnreadCount(notifications, unreadCount) {
|
|
65728
|
+
if (typeof unreadCount === 'number')
|
|
65729
|
+
return Math.max(0, unreadCount);
|
|
65730
|
+
return notifications.filter((notification) => !notification.isRead).length;
|
|
65731
|
+
}
|
|
65732
|
+
|
|
65733
|
+
// 1. React
|
|
65734
|
+
/** Derived display state for the panel header and body. */
|
|
65735
|
+
function useInAppNotificationsPanel(notifications, unreadCount) {
|
|
65736
|
+
return React.useMemo(() => {
|
|
65737
|
+
const resolved = resolveUnreadCount(notifications, unreadCount);
|
|
65738
|
+
return {
|
|
65739
|
+
unreadCount: resolved,
|
|
65740
|
+
hasUnread: resolved > 0,
|
|
65741
|
+
isEmpty: notifications.length === 0,
|
|
65742
|
+
};
|
|
65743
|
+
}, [notifications, unreadCount]);
|
|
65744
|
+
}
|
|
65745
|
+
/**
|
|
65746
|
+
* Fires `onLoadMore` when the sentinel scrolls into view inside `rootRef`.
|
|
65747
|
+
* No-ops where IntersectionObserver is unavailable (jsdom, older browsers), so
|
|
65748
|
+
* the list simply stops auto-loading rather than breaking.
|
|
65749
|
+
*/
|
|
65750
|
+
function useInfiniteScrollSentinel(rootRef, onLoadMore, enabled) {
|
|
65751
|
+
const sentinelRef = React.useRef(null);
|
|
65752
|
+
React.useEffect(() => {
|
|
65753
|
+
const sentinel = sentinelRef.current;
|
|
65754
|
+
if (!sentinel || !enabled || !onLoadMore)
|
|
65755
|
+
return;
|
|
65756
|
+
if (typeof IntersectionObserver === 'undefined')
|
|
65757
|
+
return;
|
|
65758
|
+
const observer = new IntersectionObserver((entries) => {
|
|
65759
|
+
if (entries.some((entry) => entry.isIntersecting))
|
|
65760
|
+
onLoadMore();
|
|
65761
|
+
},
|
|
65762
|
+
// Start fetching slightly before the sentinel is actually visible.
|
|
65763
|
+
{ root: rootRef.current, rootMargin: '80px' });
|
|
65764
|
+
observer.observe(sentinel);
|
|
65765
|
+
return () => observer.disconnect();
|
|
65766
|
+
}, [rootRef, onLoadMore, enabled]);
|
|
65767
|
+
return sentinelRef;
|
|
65768
|
+
}
|
|
65769
|
+
|
|
65770
|
+
const inAppNotificationsPanelA11y = {
|
|
65771
|
+
panelLabel: 'Notifications',
|
|
65772
|
+
listLabel: 'Notification list',
|
|
65773
|
+
markAllReadLabel: 'Mark all notifications as read',
|
|
65774
|
+
retryLabel: 'Retry loading notifications',
|
|
65775
|
+
};
|
|
65776
|
+
/** Non-modal dropdown, so `dialog` rather than `alertdialog`. */
|
|
65777
|
+
const IN_APP_NOTIFICATIONS_PANEL_ROLE = 'dialog';
|
|
65778
|
+
|
|
65779
|
+
/**
|
|
65780
|
+
* Notification list with header actions and loading / error / empty states.
|
|
65781
|
+
* Positioning is the caller's concern — render it inside a popover anchored to
|
|
65782
|
+
* whatever opens it (see `DashboardShell`'s `notificationsPanel` slot).
|
|
65783
|
+
*/
|
|
65784
|
+
const InAppNotificationsPanel = ({ notifications, onSelect, unreadCount, totalCount, isLoading = false, hasNextPage = false, isFetchingNextPage = false, onLoadMore, isError = false, onRetry, onMarkAllRead, isMarkingAllRead = false, title = inAppNotificationsPanelA11y.panelLabel, emptyTitle = 'No notifications yet', emptyDescription, emptyIcon, emptyState, errorMessage = "Couldn't load notifications.", defaultIcon, className, }) => {
|
|
65785
|
+
const { unreadCount: resolvedUnread, hasUnread, isEmpty, } = useInAppNotificationsPanel(notifications, unreadCount);
|
|
65786
|
+
const listRef = React.useRef(null);
|
|
65787
|
+
const sentinelRef = useInfiniteScrollSentinel(listRef, onLoadMore, hasNextPage && !isLoading);
|
|
65788
|
+
return (jsxRuntime.jsxs("div", { role: IN_APP_NOTIFICATIONS_PANEL_ROLE, "aria-label": inAppNotificationsPanelA11y.panelLabel, className: cn('flex w-full flex-col overflow-hidden', className), children: [jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border px-2 py-3", children: [jsxRuntime.jsx("div", { className: "min-w-0", children: jsxRuntime.jsxs("h2", { className: "flex min-w-0 items-center gap-2 text-sm font-semibold text-foreground", children: [jsxRuntime.jsx("span", { className: "truncate", children: title }), typeof totalCount === 'number' && (jsxRuntime.jsx("span", { className: "shrink-0 rounded-full bg-muted px-2 py-0.5 text-[0.6875rem] font-semibold text-muted-foreground tabular-nums", children: totalCount }))] }) }), jsxRuntime.jsx("span", { className: "flex shrink-0 items-center gap-2", children: hasUnread && onMarkAllRead && (jsxRuntime.jsx(Button$1, { variant: "ghost", size: "sm", onClick: onMarkAllRead, disabled: isMarkingAllRead, "aria-label": inAppNotificationsPanelA11y.markAllReadLabel, className: "h-7 px-2 text-xs font-medium text-primary hover:text-primary", children: "Mark all as read" })) })] }), jsxRuntime.jsx("div", { ref: listRef, className: "min-h-0 max-h-[16.75rem] flex-1 overflow-y-auto p-2", children: isLoading ? (jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 py-10 text-sm text-muted-foreground", children: [jsxRuntime.jsx(LoaderCircle, { className: "size-4 animate-spin", "aria-hidden": "true" }), "Loading notifications..."] })) : isError ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 py-10 text-center", children: [jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: errorMessage }), onRetry && (jsxRuntime.jsx(Button$1, { variant: "outline", size: "sm", onClick: onRetry, "aria-label": inAppNotificationsPanelA11y.retryLabel, className: "h-7 px-3 text-xs", children: "Try again" }))] })) : isEmpty ? ((emptyState ?? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 px-6 py-10 text-center", children: [jsxRuntime.jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-muted", children: emptyIcon ?? (jsxRuntime.jsx(Bell, { className: "size-5 text-muted-foreground", "aria-hidden": "true" })) }), jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: emptyTitle }), emptyDescription && (jsxRuntime.jsx("p", { className: "max-w-[16rem] text-xs text-muted-foreground", children: emptyDescription }))] })))) : (jsxRuntime.jsxs("div", { role: "list", "aria-label": inAppNotificationsPanelA11y.listLabel, className: "flex flex-col gap-1.5", children: [notifications.map((notification) => (jsxRuntime.jsx(InAppNotificationCard, { notification: notification, onSelect: onSelect, defaultIcon: defaultIcon }, notification.id))), hasNextPage && (jsxRuntime.jsx("div", { ref: sentinelRef, "aria-hidden": "true", className: "h-px" })), isFetchingNextPage && (jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 py-3 text-xs text-muted-foreground", children: [jsxRuntime.jsx(LoaderCircle, { className: "size-3.5 animate-spin", "aria-hidden": "true" }), "Loading more..."] }))] })) })] }));
|
|
65789
|
+
};
|
|
65790
|
+
|
|
65658
65791
|
const stop = (fn) => (e) => {
|
|
65659
65792
|
e.stopPropagation();
|
|
65660
65793
|
fn?.();
|
|
@@ -65918,7 +66051,7 @@ const navbarIconButtonVariants = cva([
|
|
|
65918
66051
|
});
|
|
65919
66052
|
/** Tiny solid-pill badge ("8") absolutely positioned on the icon button. */
|
|
65920
66053
|
const navbarIndicatorCountVariants = cva([
|
|
65921
|
-
'absolute -top-
|
|
66054
|
+
'absolute -top-2.5 -right-2.5 inline-flex items-center justify-center',
|
|
65922
66055
|
'min-w-4 h-4 px-1 rounded-full ring-2 ring-card',
|
|
65923
66056
|
'text-[10px] font-medium leading-none tabular-nums text-white',
|
|
65924
66057
|
].join(' '), {
|
|
@@ -66005,10 +66138,11 @@ const NavbarTitle = ({ title, items, level = 1, className, ...props }) => {
|
|
|
66005
66138
|
// ─── Right-side actions container ────────────────────────────────────────────
|
|
66006
66139
|
const NavbarActions = ({ gap = 'md', className, children, ...props }) => (jsxRuntime.jsx("div", { className: cn(navbarActionsVariants({ gap }), className), ...props, children: children }));
|
|
66007
66140
|
// ─── Icon button (with optional dot or count badge) ──────────────────────────
|
|
66008
|
-
const NavbarIconButton = ({ appearance = 'plain', size = 'md', indicator = false, className, children, type = 'button', ...props }) => {
|
|
66141
|
+
const NavbarIconButton = React.forwardRef(({ appearance = 'plain', size = 'md', indicator = false, className, children, type = 'button', ...props }, ref) => {
|
|
66009
66142
|
const resolved = resolveIndicator(indicator);
|
|
66010
|
-
return (jsxRuntime.jsxs("button", { type: type, className: cn(navbarIconButtonVariants({ appearance, size }), className), ...props, children: [jsxRuntime.jsx("span", { "aria-hidden": "true", className: "inline-flex", children: children }), resolved && resolved.count !== null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorCountVariants({ tone: resolved.tone })), children: resolved.count })), resolved && resolved.count === null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorDotVariants({ tone: resolved.tone })) }))] }));
|
|
66011
|
-
};
|
|
66143
|
+
return (jsxRuntime.jsxs("button", { ref: ref, type: type, className: cn(navbarIconButtonVariants({ appearance, size }), className), ...props, children: [jsxRuntime.jsx("span", { "aria-hidden": "true", className: "inline-flex", children: children }), resolved && resolved.count !== null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorCountVariants({ tone: resolved.tone })), children: resolved.count })), resolved && resolved.count === null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorDotVariants({ tone: resolved.tone })) }))] }));
|
|
66144
|
+
});
|
|
66145
|
+
NavbarIconButton.displayName = 'NavbarIconButton';
|
|
66012
66146
|
// ─── Search input (matches Figma 288×36 muted pill) ──────────────────────────
|
|
66013
66147
|
const NavbarSearch = ({ placeholder = 'Search', value, onChange, onClear, className, containerClassName, ...rest }) => {
|
|
66014
66148
|
const isControlled = value !== undefined;
|
|
@@ -66092,7 +66226,8 @@ const NavLeaf = ({ item, currentPath, onNavigate }) => {
|
|
|
66092
66226
|
const Icon = item.icon;
|
|
66093
66227
|
const active = isPathActive(currentPath, item.path);
|
|
66094
66228
|
const { collapsed, expandSidebar } = useCollapsedSidebar();
|
|
66095
|
-
return (jsxRuntime.jsxs(SidebarMenuItem, { className: cn(active &&
|
|
66229
|
+
return (jsxRuntime.jsxs(SidebarMenuItem, { className: cn(active &&
|
|
66230
|
+
'flex items-center gap-1.5 group-data-[collapsible=icon]:block'), children: [active && jsxRuntime.jsx(ActiveRail, {}), jsxRuntime.jsxs(SidebarMenuButton, { isActive: active, tooltip: item.label, onClick: () => {
|
|
66096
66231
|
if (collapsed)
|
|
66097
66232
|
expandSidebar();
|
|
66098
66233
|
if (item.path)
|
|
@@ -66142,12 +66277,15 @@ function resolveBreadcrumb(pageMeta, leafOverride) {
|
|
|
66142
66277
|
return crumbs;
|
|
66143
66278
|
return crumbs.map((crumb, index) => index === crumbs.length - 1 ? { ...crumb, label: leafOverride } : crumb);
|
|
66144
66279
|
}
|
|
66145
|
-
const ShellHeader = ({ pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, headerActionsSlot, }) => {
|
|
66280
|
+
const ShellHeader = ({ pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, notificationsPanel, notificationsOpen, onNotificationsOpenChange, headerActionsSlot, }) => {
|
|
66146
66281
|
const { isMobile } = useSidebar();
|
|
66147
66282
|
const breadcrumb = resolveBreadcrumb(pageMeta, breadcrumbLeafOverride);
|
|
66148
|
-
return (jsxRuntime.jsxs(Navbar, { "aria-label": "Top navigation", children: [jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [isMobile && jsxRuntime.jsx(SidebarTrigger, {}), breadcrumb ? (jsxRuntime.jsx(NavbarTitle, { items: breadcrumb })) : (pageMeta?.title && jsxRuntime.jsx(NavbarTitle, { title: pageMeta.title }))] }), jsxRuntime.jsxs(NavbarActions, { gap: "lg", children: [headerActionsSlot, showThemeToggle && (jsxRuntime.jsx(NavbarIconButton, { "aria-label": isDark ? 'Switch to light theme' : 'Switch to dark theme', size: "md", onClick: onToggleTheme, children: isDark ? jsxRuntime.jsx(Sun, { size: 20 }) : jsxRuntime.jsx(Moon, { size: 20 }) })), showNotifications &&
|
|
66283
|
+
return (jsxRuntime.jsxs(Navbar, { "aria-label": "Top navigation", children: [jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [isMobile && jsxRuntime.jsx(SidebarTrigger, {}), breadcrumb ? (jsxRuntime.jsx(NavbarTitle, { items: breadcrumb })) : (pageMeta?.title && jsxRuntime.jsx(NavbarTitle, { title: pageMeta.title }))] }), jsxRuntime.jsxs(NavbarActions, { gap: "lg", children: [headerActionsSlot, showThemeToggle && (jsxRuntime.jsx(NavbarIconButton, { "aria-label": isDark ? 'Switch to light theme' : 'Switch to dark theme', size: "md", onClick: onToggleTheme, children: isDark ? jsxRuntime.jsx(Sun, { size: 20 }) : jsxRuntime.jsx(Moon, { size: 20 }) })), showNotifications &&
|
|
66284
|
+
(notificationsPanel ? (jsxRuntime.jsxs(Popover, { open: notificationsOpen, onOpenChange: onNotificationsOpenChange, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsx(NavbarIconButton, { "aria-label": "Notifications", size: "md", indicator: notificationsIndicator || undefined, onClick: onNotificationsClick, children: jsxRuntime.jsx(Bell, { size: 20 }) }) }), jsxRuntime.jsx(PopoverContent, { align: "end", sideOffset: 8, className: "w-[min(24rem,calc(100vw-2rem))] p-0", children: notificationsPanel })] })) : (jsxRuntime.jsx(NavbarIconButton, { "aria-label": "Notifications", size: "md", indicator: notificationsIndicator || undefined, onClick: onNotificationsClick, children: jsxRuntime.jsx(Bell, { size: 20 }) }))), jsxRuntime.jsx(ProfileMenu, { user: user, profileMenuItems: profileMenuItems, onLogout: onLogout })] })] }));
|
|
66149
66285
|
};
|
|
66150
|
-
const DashboardShell = ({ brand, navGroups, currentPath, onNavigate, pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, headerActionsSlot, sidebarWidth, children, }) => (jsxRuntime.jsxs(SidebarProvider, { className: "h-screen min-h-0", style: {
|
|
66286
|
+
const DashboardShell = ({ brand, navGroups, currentPath, onNavigate, pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, notificationsPanel, notificationsOpen, onNotificationsOpenChange, headerActionsSlot, sidebarWidth, children, }) => (jsxRuntime.jsxs(SidebarProvider, { className: "h-screen min-h-0", style: {
|
|
66287
|
+
'--sidebar-width': sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH,
|
|
66288
|
+
}, children: [jsxRuntime.jsx(ShellSidebar, { brand: brand, navGroups: navGroups, currentPath: currentPath, onNavigate: onNavigate }), jsxRuntime.jsxs(SidebarInset, { className: "flex h-screen flex-col overflow-hidden bg-background", children: [jsxRuntime.jsx(ShellHeader, { pageMeta: pageMeta, breadcrumbLeafOverride: breadcrumbLeafOverride, user: user, profileMenuItems: profileMenuItems, onLogout: onLogout, showThemeToggle: showThemeToggle, isDark: isDark, onToggleTheme: onToggleTheme, showNotifications: showNotifications, notificationsIndicator: notificationsIndicator, onNotificationsClick: onNotificationsClick, notificationsPanel: notificationsPanel, notificationsOpen: notificationsOpen, onNotificationsOpenChange: onNotificationsOpenChange, headerActionsSlot: headerActionsSlot }), jsxRuntime.jsx("main", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto", children: children })] })] }));
|
|
66151
66289
|
|
|
66152
66290
|
function CreateFormPageHeader({ title, onBack, backLabel = 'Go back', }) {
|
|
66153
66291
|
return (jsxRuntime.jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [jsxRuntime.jsx("h1", { className: "min-w-0 text-xl font-semibold leading-7 text-foreground", children: title }), jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-2", children: jsxRuntime.jsxs(Button$1, { type: "button", variant: "ghost", onClick: onBack, className: "h-8 shrink-0 gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-secondary-foreground hover:text-secondary-foreground", children: [jsxRuntime.jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": true }), backLabel] }) })] }));
|
|
@@ -68228,6 +68366,8 @@ exports.HistoryTimelineShimmer = HistoryTimelineShimmer;
|
|
|
68228
68366
|
exports.INVOICE_AUDIT_CONFIG = INVOICE_AUDIT_CONFIG;
|
|
68229
68367
|
exports.IconButton = IconButton;
|
|
68230
68368
|
exports.IdHyperlinkText = IdHyperlinkText;
|
|
68369
|
+
exports.InAppNotificationCard = InAppNotificationCard;
|
|
68370
|
+
exports.InAppNotificationsPanel = InAppNotificationsPanel;
|
|
68231
68371
|
exports.InfoBanner = InfoBanner;
|
|
68232
68372
|
exports.InventoryComboBox = InventoryComboBox;
|
|
68233
68373
|
exports.InventoryItemCard = InventoryItemCard;
|
|
@@ -68349,6 +68489,8 @@ exports.getBadgeTypeForStatus = getBadgeTypeForStatus;
|
|
|
68349
68489
|
exports.getChangePills = getChangePills;
|
|
68350
68490
|
exports.getInitials = getInitials;
|
|
68351
68491
|
exports.iconButtonVariants = iconButtonVariants;
|
|
68492
|
+
exports.inAppNotificationCardA11y = inAppNotificationCardA11y;
|
|
68493
|
+
exports.inAppNotificationsPanelA11y = inAppNotificationsPanelA11y;
|
|
68352
68494
|
exports.isPathActive = isPathActive;
|
|
68353
68495
|
exports.notify = notify;
|
|
68354
68496
|
exports.releaseStaleOverlayBodyLock = releaseStaleOverlayBodyLock;
|
|
@@ -68357,6 +68499,7 @@ exports.useAuditShowMore = useAuditShowMore;
|
|
|
68357
68499
|
exports.useBadge = useBadge;
|
|
68358
68500
|
exports.useButton = useButton;
|
|
68359
68501
|
exports.useDropdownMenu = useDropdownMenu;
|
|
68502
|
+
exports.useInAppNotificationsPanel = useInAppNotificationsPanel;
|
|
68360
68503
|
exports.useNavbar = useNavbar;
|
|
68361
68504
|
exports.useSidebar = useSidebar;
|
|
68362
68505
|
exports.videoThumbnailSrc = videoThumbnailSrc;
|