@papernote/ui 1.8.2 → 1.9.1

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/index.js CHANGED
@@ -9701,7 +9701,7 @@ function UserProfileButton({ userName, userEmail, userTitle, initials, isOnline
9701
9701
  */
9702
9702
  const Layout = ({ sidebar, children, statusBar, className = '', sections }) => {
9703
9703
  console.log('🏗️ Layout render with sections:', sections);
9704
- return (jsxRuntime.jsxs("div", { className: `h-screen flex flex-col bg-paper-100 ${className}`, children: [jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden relative", children: [sidebar, jsxRuntime.jsx("div", { className: "w-8 h-full bg-paper-100 flex-shrink-0 relative flex items-center justify-center", children: jsxRuntime.jsx(PageNavigation, { sections: sections }) }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: children })] }), statusBar] }));
9704
+ return (jsxRuntime.jsxs("div", { className: `h-screen flex flex-col bg-paper-100 ${className}`, children: [jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden relative", children: [sidebar, jsxRuntime.jsx("div", { className: "w-8 h-full bg-paper-100 flex-shrink-0 relative z-10 flex items-start justify-center pt-32", children: jsxRuntime.jsx(PageNavigation, { sections: sections }) }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: children })] }), statusBar] }));
9705
9705
  };
9706
9706
 
9707
9707
  /**
@@ -10076,6 +10076,203 @@ function NotificationIndicator({ count = 0, onClick, className = '', maxCount =
10076
10076
  return (jsxRuntime.jsxs("button", { onClick: onClick, className: `relative bg-white p-2.5 rounded-lg text-ink-400 hover:text-ink-600 hover:bg-paper-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent-400 transition-all shadow-xs dark:bg-slate-800 dark:text-slate-400 dark:hover:text-slate-100 dark:hover:bg-slate-700 ${className}`, "aria-label": "View notifications", children: [jsxRuntime.jsx(lucideReact.Bell, { className: "h-5 w-5" }), showBadge && (jsxRuntime.jsx("span", { className: `absolute -top-1 -right-1 ${variantClasses[variant]} text-white text-xs font-semibold rounded-full h-5 min-w-5 px-1 flex items-center justify-center`, children: displayCount }))] }));
10077
10077
  }
10078
10078
 
10079
+ /**
10080
+ * Format a date to a relative time string
10081
+ */
10082
+ function formatTimeAgo(date) {
10083
+ const now = new Date();
10084
+ const then = new Date(date);
10085
+ const diffMs = now.getTime() - then.getTime();
10086
+ const diffSeconds = Math.floor(diffMs / 1000);
10087
+ const diffMinutes = Math.floor(diffSeconds / 60);
10088
+ const diffHours = Math.floor(diffMinutes / 60);
10089
+ const diffDays = Math.floor(diffHours / 24);
10090
+ if (diffSeconds < 60) {
10091
+ return 'Just now';
10092
+ }
10093
+ else if (diffMinutes < 60) {
10094
+ return `${diffMinutes}m ago`;
10095
+ }
10096
+ else if (diffHours < 24) {
10097
+ return `${diffHours}h ago`;
10098
+ }
10099
+ else if (diffDays < 7) {
10100
+ return `${diffDays}d ago`;
10101
+ }
10102
+ else {
10103
+ return then.toLocaleDateString();
10104
+ }
10105
+ }
10106
+ /**
10107
+ * Map notification type to Badge variant
10108
+ */
10109
+ const typeToBadgeVariant = {
10110
+ info: 'info',
10111
+ success: 'success',
10112
+ warning: 'warning',
10113
+ error: 'error',
10114
+ };
10115
+ /**
10116
+ * Default labels for notification types
10117
+ */
10118
+ const defaultTypeLabels = {
10119
+ info: 'Info',
10120
+ success: 'Success',
10121
+ warning: 'Warning',
10122
+ error: 'Alert',
10123
+ };
10124
+ /**
10125
+ * Map dropdown position to Popover placement
10126
+ */
10127
+ function getPopoverPlacement(position) {
10128
+ switch (position) {
10129
+ case 'bottom-right':
10130
+ case 'right':
10131
+ return 'bottom-start';
10132
+ case 'bottom-left':
10133
+ case 'left':
10134
+ return 'bottom-end';
10135
+ case 'top-right':
10136
+ return 'top-start';
10137
+ case 'top-left':
10138
+ return 'top-end';
10139
+ default:
10140
+ return 'bottom-start';
10141
+ }
10142
+ }
10143
+ /**
10144
+ * NotificationBell - A bell icon with badge and dropdown for displaying notifications
10145
+ *
10146
+ * Displays a bell icon with an optional unread count badge. When clicked, shows a
10147
+ * dropdown panel with recent notifications, mark as read actions, and a link to
10148
+ * view all notifications.
10149
+ *
10150
+ * @example Basic usage (compact variant)
10151
+ * ```tsx
10152
+ * <NotificationBell
10153
+ * notifications={notifications}
10154
+ * onMarkAsRead={(id) => markRead(id)}
10155
+ * onMarkAllRead={() => markAllRead()}
10156
+ * onNotificationClick={(n) => navigate(n.actionUrl)}
10157
+ * onViewAll={() => navigate('/notifications')}
10158
+ * />
10159
+ * ```
10160
+ *
10161
+ * @example Detailed variant with labeled badges
10162
+ * ```tsx
10163
+ * <NotificationBell
10164
+ * notifications={notifications}
10165
+ * variant="detailed"
10166
+ * showUnreadInHeader
10167
+ * dropdownPosition="bottom-left"
10168
+ * />
10169
+ * ```
10170
+ */
10171
+ function NotificationBell({ notifications, unreadCount: providedUnreadCount, onMarkAsRead, onMarkAllRead, onNotificationClick, onViewAll, loading = false, dropdownPosition = 'bottom-right', maxHeight = '400px', size = 'md', emptyMessage = 'No notifications', viewAllText = 'View all notifications', disabled = false, className = '', variant = 'compact', showUnreadInHeader = false, bellStyle = 'ghost', }) {
10172
+ const [isOpen, setIsOpen] = React.useState(false);
10173
+ // Calculate unread count if not provided
10174
+ const unreadCount = React.useMemo(() => {
10175
+ if (providedUnreadCount !== undefined) {
10176
+ return providedUnreadCount;
10177
+ }
10178
+ return notifications.filter((n) => !n.isRead).length;
10179
+ }, [providedUnreadCount, notifications]);
10180
+ // Handle notification click
10181
+ const handleNotificationClick = React.useCallback((notification) => {
10182
+ onNotificationClick?.(notification);
10183
+ }, [onNotificationClick]);
10184
+ // Handle mark as read
10185
+ const handleMarkAsRead = React.useCallback((e, id) => {
10186
+ e.stopPropagation();
10187
+ onMarkAsRead?.(id);
10188
+ }, [onMarkAsRead]);
10189
+ // Handle mark all as read
10190
+ const handleMarkAllRead = React.useCallback(() => {
10191
+ onMarkAllRead?.();
10192
+ }, [onMarkAllRead]);
10193
+ // Handle view all
10194
+ const handleViewAll = React.useCallback(() => {
10195
+ onViewAll?.();
10196
+ setIsOpen(false);
10197
+ }, [onViewAll]);
10198
+ // Icon sizes based on button size
10199
+ const iconSizes = {
10200
+ sm: 'h-4 w-4',
10201
+ md: 'h-5 w-5',
10202
+ lg: 'h-6 w-6',
10203
+ };
10204
+ // Dropdown width based on size
10205
+ const dropdownWidths = {
10206
+ sm: 'w-72',
10207
+ md: 'w-80',
10208
+ lg: 'w-96',
10209
+ };
10210
+ // Outlined bell style classes
10211
+ const outlinedSizeClasses = {
10212
+ sm: 'p-2',
10213
+ md: 'p-3',
10214
+ lg: 'p-4',
10215
+ };
10216
+ // Trigger button
10217
+ const triggerButton = bellStyle === 'outlined' ? (jsxRuntime.jsxs("div", { className: "relative inline-block", children: [jsxRuntime.jsx("button", { className: `
10218
+ ${outlinedSizeClasses[size]}
10219
+ bg-white border-2 border-paper-300 rounded-xl
10220
+ hover:bg-paper-50 hover:border-paper-400
10221
+ focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-accent-400
10222
+ transition-all duration-200
10223
+ disabled:opacity-40 disabled:cursor-not-allowed
10224
+ ${className}
10225
+ `, disabled: disabled, "aria-label": unreadCount > 0
10226
+ ? `Notifications - ${unreadCount} unread`
10227
+ : 'Notifications', children: jsxRuntime.jsx(lucideReact.Bell, { className: `${iconSizes[size]} text-ink-600` }) }), unreadCount > 0 && (jsxRuntime.jsx("span", { className: `
10228
+ absolute -top-1 -right-1
10229
+ flex items-center justify-center
10230
+ min-w-[18px] h-[18px] px-1.5
10231
+ rounded-full text-white font-semibold text-[11px]
10232
+ bg-error-500 shadow-sm
10233
+ pointer-events-none
10234
+ `, "aria-label": `${unreadCount > 99 ? '99+' : unreadCount} notifications`, children: unreadCount > 99 ? '99+' : unreadCount }))] })) : (jsxRuntime.jsx(Button, { variant: "ghost", iconOnly: true, size: size, disabled: disabled, badge: unreadCount > 0 ? unreadCount : undefined, badgeVariant: "error", "aria-label": unreadCount > 0
10235
+ ? `Notifications - ${unreadCount} unread`
10236
+ : 'Notifications', className: className, children: jsxRuntime.jsx(lucideReact.Bell, { className: iconSizes[size] }) }));
10237
+ // Header title with optional unread count
10238
+ const headerTitle = showUnreadInHeader && unreadCount > 0
10239
+ ? `Notifications (${unreadCount} unread)`
10240
+ : 'Notifications';
10241
+ // Render compact notification item
10242
+ const renderCompactItem = (notification) => (jsxRuntime.jsxs("div", { className: "flex gap-3", children: [jsxRuntime.jsx("div", { className: "flex-shrink-0 pt-1", children: jsxRuntime.jsx(Badge, { dot: true, variant: typeToBadgeVariant[notification.type], size: "sm" }) }), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsx(Text, { size: "sm", weight: notification.priority === 'high' ||
10243
+ notification.priority === 'urgent' ||
10244
+ !notification.isRead
10245
+ ? 'medium'
10246
+ : 'normal', truncate: true, children: notification.title }), jsxRuntime.jsx(Text, { size: "xs", color: "muted", lineClamp: 2, className: "mt-0.5", children: notification.message }), jsxRuntime.jsx(Text, { size: "xs", color: "muted", className: "mt-1", children: formatTimeAgo(notification.createdAt) })] }), !notification.isRead && onMarkAsRead && (jsxRuntime.jsx("button", { className: "flex-shrink-0 p-1 text-ink-400 hover:text-ink-600 hover:bg-paper-100 rounded transition-colors", onClick: (e) => handleMarkAsRead(e, notification.id), "aria-label": "Mark as read", title: "Mark as read", children: jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }))] }));
10247
+ // Render detailed notification item
10248
+ const renderDetailedItem = (notification) => (jsxRuntime.jsxs("div", { className: "flex gap-3", children: [jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [jsxRuntime.jsx(Text, { size: "sm", weight: notification.priority === 'high' ||
10249
+ notification.priority === 'urgent' ||
10250
+ !notification.isRead
10251
+ ? 'semibold'
10252
+ : 'medium', className: "flex-1", children: notification.title }), jsxRuntime.jsx(Text, { size: "xs", color: "muted", className: "flex-shrink-0 whitespace-nowrap", children: formatTimeAgo(notification.createdAt) })] }), jsxRuntime.jsx(Text, { size: "xs", color: "muted", lineClamp: 2, className: "mt-1", children: notification.message }), jsxRuntime.jsx("div", { className: "mt-2", children: jsxRuntime.jsx(Badge, { variant: typeToBadgeVariant[notification.type], size: "sm", children: notification.typeLabel || defaultTypeLabels[notification.type] }) })] }), !notification.isRead && onMarkAsRead && (jsxRuntime.jsx("button", { className: "flex-shrink-0 p-1 text-ink-400 hover:text-ink-600 hover:bg-paper-100 rounded transition-colors self-center", onClick: (e) => handleMarkAsRead(e, notification.id), "aria-label": "Mark as read", title: "Mark as read", children: jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }))] }));
10253
+ // Dropdown content
10254
+ const dropdownContent = (jsxRuntime.jsxs("div", { className: `${dropdownWidths[size]} bg-white`, children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-paper-200", children: [jsxRuntime.jsx(Text, { weight: "semibold", size: "sm", children: headerTitle }), unreadCount > 0 && onMarkAllRead && (jsxRuntime.jsxs("button", { className: "flex items-center gap-1.5 text-xs text-ink-600 hover:text-ink-800 transition-colors", onClick: handleMarkAllRead, children: [jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5" }), "Mark all read"] }))] }), jsxRuntime.jsx("div", { className: "overflow-y-auto", style: { maxHeight }, role: "list", "aria-label": "Notifications", children: loading ? (
10255
+ // Loading state
10256
+ jsxRuntime.jsx("div", { className: "p-4", children: jsxRuntime.jsx(Stack, { spacing: "sm", children: [1, 2, 3].map((i) => (jsxRuntime.jsxs("div", { className: "flex gap-3", children: [variant === 'compact' && (jsxRuntime.jsx(Skeleton, { className: "h-4 w-4 rounded-full flex-shrink-0" })), jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsxs("div", { className: "flex justify-between", children: [jsxRuntime.jsx(Skeleton, { className: "h-4 w-3/4 mb-2" }), variant === 'detailed' && (jsxRuntime.jsx(Skeleton, { className: "h-3 w-12" }))] }), jsxRuntime.jsx(Skeleton, { className: "h-3 w-full mb-1" }), variant === 'compact' ? (jsxRuntime.jsx(Skeleton, { className: "h-3 w-1/4" })) : (jsxRuntime.jsx(Skeleton, { className: "h-5 w-16 mt-2" }))] })] }, i))) }) })) : notifications.length === 0 ? (
10257
+ // Empty state
10258
+ jsxRuntime.jsxs("div", { className: "py-8 px-4 text-center", children: [jsxRuntime.jsx(lucideReact.Bell, { className: "h-10 w-10 text-ink-300 mx-auto mb-3" }), jsxRuntime.jsx(Text, { color: "muted", size: "sm", children: emptyMessage })] })) : (
10259
+ // Notification items
10260
+ notifications.map((notification) => (jsxRuntime.jsx("div", { role: "listitem", className: `
10261
+ px-4 py-3 border-b border-paper-100 last:border-b-0
10262
+ hover:bg-paper-50 transition-colors cursor-pointer
10263
+ ${!notification.isRead ? 'bg-primary-50/30' : ''}
10264
+ ${notification.priority === 'urgent' ? 'border-l-2 border-l-error-500' : ''}
10265
+ `, onClick: () => handleNotificationClick(notification), onKeyDown: (e) => {
10266
+ if (e.key === 'Enter' || e.key === ' ') {
10267
+ e.preventDefault();
10268
+ handleNotificationClick(notification);
10269
+ }
10270
+ }, tabIndex: 0, children: variant === 'compact'
10271
+ ? renderCompactItem(notification)
10272
+ : renderDetailedItem(notification) }, notification.id)))) }), onViewAll && notifications.length > 0 && (jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-paper-200", children: jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", fullWidth: true, onClick: handleViewAll, icon: jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3.5 w-3.5" }), iconPosition: "right", children: viewAllText }) }))] }));
10273
+ return (jsxRuntime.jsx(Popover, { trigger: triggerButton, placement: getPopoverPlacement(dropdownPosition), triggerMode: "click", showArrow: false, offset: 4, open: isOpen, onOpenChange: setIsOpen, closeOnClickOutside: true, closeOnEscape: true, disabled: disabled, className: "p-0 overflow-hidden", children: dropdownContent }));
10274
+ }
10275
+
10079
10276
  /**
10080
10277
  * Get value from item by key path (supports nested keys like 'user.name')
10081
10278
  */
@@ -11092,52 +11289,44 @@ function getAugmentedNamespace(n) {
11092
11289
  * (A1, A1:C5, ...)
11093
11290
  */
11094
11291
 
11095
- var collection;
11096
- var hasRequiredCollection;
11097
-
11098
- function requireCollection () {
11099
- if (hasRequiredCollection) return collection;
11100
- hasRequiredCollection = 1;
11101
- class Collection {
11292
+ let Collection$3 = class Collection {
11102
11293
 
11103
- constructor(data, refs) {
11104
- if (data == null && refs == null) {
11105
- this._data = [];
11106
- this._refs = [];
11107
- } else {
11108
- if (data.length !== refs.length)
11109
- throw Error('Collection: data length should match references length.');
11110
- this._data = data;
11111
- this._refs = refs;
11112
- }
11113
- }
11294
+ constructor(data, refs) {
11295
+ if (data == null && refs == null) {
11296
+ this._data = [];
11297
+ this._refs = [];
11298
+ } else {
11299
+ if (data.length !== refs.length)
11300
+ throw Error('Collection: data length should match references length.');
11301
+ this._data = data;
11302
+ this._refs = refs;
11303
+ }
11304
+ }
11114
11305
 
11115
- get data() {
11116
- return this._data;
11117
- }
11306
+ get data() {
11307
+ return this._data;
11308
+ }
11118
11309
 
11119
- get refs() {
11120
- return this._refs;
11121
- }
11310
+ get refs() {
11311
+ return this._refs;
11312
+ }
11122
11313
 
11123
- get length() {
11124
- return this._data.length;
11125
- }
11314
+ get length() {
11315
+ return this._data.length;
11316
+ }
11126
11317
 
11127
- /**
11128
- * Add data and references to this collection.
11129
- * @param {{}} obj - data
11130
- * @param {{}} ref - reference
11131
- */
11132
- add(obj, ref) {
11133
- this._data.push(obj);
11134
- this._refs.push(ref);
11135
- }
11136
- }
11318
+ /**
11319
+ * Add data and references to this collection.
11320
+ * @param {{}} obj - data
11321
+ * @param {{}} ref - reference
11322
+ */
11323
+ add(obj, ref) {
11324
+ this._data.push(obj);
11325
+ this._refs.push(ref);
11326
+ }
11327
+ };
11137
11328
 
11138
- collection = Collection;
11139
- return collection;
11140
- }
11329
+ var collection = Collection$3;
11141
11330
 
11142
11331
  var helpers;
11143
11332
  var hasRequiredHelpers;
@@ -11146,7 +11335,7 @@ function requireHelpers () {
11146
11335
  if (hasRequiredHelpers) return helpers;
11147
11336
  hasRequiredHelpers = 1;
11148
11337
  const FormulaError = requireError();
11149
- const Collection = requireCollection();
11338
+ const Collection = collection;
11150
11339
 
11151
11340
  const Types = {
11152
11341
  NUMBER: 0,
@@ -20800,7 +20989,7 @@ var engineering = EngineeringFunctions;
20800
20989
 
20801
20990
  const FormulaError$b = requireError();
20802
20991
  const {FormulaHelpers: FormulaHelpers$8, Types: Types$6, WildCard, Address: Address$3} = requireHelpers();
20803
- const Collection$2 = requireCollection();
20992
+ const Collection$2 = collection;
20804
20993
  const H$5 = FormulaHelpers$8;
20805
20994
 
20806
20995
  const ReferenceFunctions$1 = {
@@ -32428,7 +32617,7 @@ var parsing = {
32428
32617
  const FormulaError$4 = requireError();
32429
32618
  const {Address: Address$1} = requireHelpers();
32430
32619
  const {Prefix: Prefix$1, Postfix: Postfix$1, Infix: Infix$1, Operators: Operators$1} = operators;
32431
- const Collection$1 = requireCollection();
32620
+ const Collection$1 = collection;
32432
32621
  const MAX_ROW$1 = 1048576, MAX_COLUMN$1 = 16384;
32433
32622
  const {NotAllInputParsedException} = require$$4;
32434
32623
 
@@ -33190,7 +33379,7 @@ var hooks$1 = {
33190
33379
  const FormulaError$2 = requireError();
33191
33380
  const {FormulaHelpers: FormulaHelpers$1, Types, Address} = requireHelpers();
33192
33381
  const {Prefix, Postfix, Infix, Operators} = operators;
33193
- const Collection = requireCollection();
33382
+ const Collection = collection;
33194
33383
  const MAX_ROW = 1048576, MAX_COLUMN = 16384;
33195
33384
 
33196
33385
  let Utils$1 = class Utils {
@@ -57807,6 +57996,7 @@ exports.ModalFooter = ModalFooter;
57807
57996
  exports.MultiSelect = MultiSelect;
57808
57997
  exports.NotificationBanner = NotificationBanner;
57809
57998
  exports.NotificationBar = NotificationBar;
57999
+ exports.NotificationBell = NotificationBell;
57810
58000
  exports.NotificationIndicator = NotificationIndicator;
57811
58001
  exports.NumberInput = NumberInput;
57812
58002
  exports.Page = Page;