@kanso-protocol/notification-center 0.1.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.
@@ -0,0 +1,325 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { KpAvatarComponent } from '@kanso-protocol/avatar';
4
+ import { KpIconComponent } from '@kanso-protocol/icon';
5
+ import { KpButtonComponent } from '@kanso-protocol/button';
6
+
7
+ class KpNotificationItemComponent {
8
+ read = false;
9
+ title = 'Notification title';
10
+ message = null;
11
+ time = null;
12
+ icon = 'bell';
13
+ appearance = 'neutral';
14
+ avatarInitials = null;
15
+ avatarSrc = null;
16
+ click$ = new EventEmitter();
17
+ /** Map notification appearance to Avatar's appearance tokens. User avatars stay default. */
18
+ get resolvedAppearance() {
19
+ if (this.avatarInitials || this.avatarSrc)
20
+ return 'default';
21
+ return this.appearance;
22
+ }
23
+ /** Unread indicator color — matches the appearance intent. Default: online (green). */
24
+ get statusColor() {
25
+ switch (this.appearance) {
26
+ case 'danger':
27
+ case 'warning': return 'busy';
28
+ case 'neutral': return 'offline';
29
+ default: return 'online';
30
+ }
31
+ }
32
+ get hostClasses() {
33
+ return `kp-notif-item ${this.read ? '' : 'kp-notif-item--unread'}`;
34
+ }
35
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpNotificationItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
36
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: KpNotificationItemComponent, isStandalone: true, selector: "kp-notification-item", inputs: { read: "read", title: "title", message: "message", time: "time", icon: "icon", appearance: "appearance", avatarInitials: "avatarInitials", avatarSrc: "avatarSrc" }, outputs: { click$: "click$" }, host: { attributes: { "role": "listitem" }, properties: { "class": "hostClasses" } }, ngImport: i0, template: `
37
+ <kp-avatar
38
+ size="md"
39
+ [appearance]="resolvedAppearance"
40
+ [initials]="avatarInitials || null"
41
+ [src]="avatarSrc || null"
42
+ [showStatus]="!read"
43
+ [status]="statusColor"
44
+ >
45
+ @if (!avatarInitials && !avatarSrc && icon) {
46
+ <kp-icon [name]="icon" />
47
+ }
48
+ </kp-avatar>
49
+ <div class="kp-notif-item__content">
50
+ <span class="kp-notif-item__title">{{ title }}</span>
51
+ @if (message) {
52
+ <span class="kp-notif-item__message">{{ message }}</span>
53
+ }
54
+ @if (time) {
55
+ <span class="kp-notif-item__time">{{ time }}</span>
56
+ }
57
+ </div>
58
+ `, isInline: true, styles: [":host{box-sizing:border-box;display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-100, var(--kp-color-gray-100));background:var(--kp-color-white, var(--kp-color-white));font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);cursor:pointer;transition:background var(--kp-motion-duration-fast) ease}:host(.kp-notif-item--unread){background:var(--kp-color-gray-50, var(--kp-color-gray-50))}:host(:hover){background:var(--kp-color-gray-100, var(--kp-color-gray-100))}kp-avatar{flex:0 0 auto}kp-avatar ::ng-deep .ti{font-size:18px;line-height:1}.kp-notif-item__content{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}.kp-notif-item__title{font-size:13px;font-weight:500;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-item__message{font-size:13px;color:var(--kp-color-gray-600, var(--kp-color-gray-600));line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.kp-notif-item__time{font-size:11px;color:var(--kp-color-gray-500, var(--kp-color-gray-500));margin-top:2px}\n"], dependencies: [{ kind: "component", type: KpAvatarComponent, selector: "kp-avatar", inputs: ["size", "shape", "appearance", "initials", "src", "alt", "showStatus", "status", "showRing", "ariaLabelOverride"] }, { kind: "component", type: KpIconComponent, selector: "kp-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
59
+ }
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpNotificationItemComponent, decorators: [{
61
+ type: Component,
62
+ args: [{ selector: 'kp-notification-item', imports: [KpAvatarComponent, KpIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses', role: 'listitem' }, template: `
63
+ <kp-avatar
64
+ size="md"
65
+ [appearance]="resolvedAppearance"
66
+ [initials]="avatarInitials || null"
67
+ [src]="avatarSrc || null"
68
+ [showStatus]="!read"
69
+ [status]="statusColor"
70
+ >
71
+ @if (!avatarInitials && !avatarSrc && icon) {
72
+ <kp-icon [name]="icon" />
73
+ }
74
+ </kp-avatar>
75
+ <div class="kp-notif-item__content">
76
+ <span class="kp-notif-item__title">{{ title }}</span>
77
+ @if (message) {
78
+ <span class="kp-notif-item__message">{{ message }}</span>
79
+ }
80
+ @if (time) {
81
+ <span class="kp-notif-item__time">{{ time }}</span>
82
+ }
83
+ </div>
84
+ `, styles: [":host{box-sizing:border-box;display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-100, var(--kp-color-gray-100));background:var(--kp-color-white, var(--kp-color-white));font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);cursor:pointer;transition:background var(--kp-motion-duration-fast) ease}:host(.kp-notif-item--unread){background:var(--kp-color-gray-50, var(--kp-color-gray-50))}:host(:hover){background:var(--kp-color-gray-100, var(--kp-color-gray-100))}kp-avatar{flex:0 0 auto}kp-avatar ::ng-deep .ti{font-size:18px;line-height:1}.kp-notif-item__content{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}.kp-notif-item__title{font-size:13px;font-weight:500;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-item__message{font-size:13px;color:var(--kp-color-gray-600, var(--kp-color-gray-600));line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.kp-notif-item__time{font-size:11px;color:var(--kp-color-gray-500, var(--kp-color-gray-500));margin-top:2px}\n"] }]
85
+ }], propDecorators: { read: [{
86
+ type: Input
87
+ }], title: [{
88
+ type: Input
89
+ }], message: [{
90
+ type: Input
91
+ }], time: [{
92
+ type: Input
93
+ }], icon: [{
94
+ type: Input
95
+ }], appearance: [{
96
+ type: Input
97
+ }], avatarInitials: [{
98
+ type: Input
99
+ }], avatarSrc: [{
100
+ type: Input
101
+ }], click$: [{
102
+ type: Output
103
+ }] } });
104
+
105
+ /**
106
+ * Kanso Protocol — NotificationCenter
107
+ *
108
+ * Panel with header + optional filters + list of notifications +
109
+ * optional footer ("View all"). Three states: with-items / empty /
110
+ * loading. Typically anchored to the bell icon in Header via
111
+ * DropdownMenu / Popover positioning.
112
+ *
113
+ * @example
114
+ * <kp-notification-center
115
+ * state="with-items"
116
+ * [notifications]="items"
117
+ * [showFilters]="true"
118
+ * unreadCount="3"
119
+ * />
120
+ */
121
+ class KpNotificationCenterComponent {
122
+ state = 'with-items';
123
+ notifications = [];
124
+ showFilters = false;
125
+ activeFilter = 'all';
126
+ filters = [
127
+ { id: 'all', label: 'All' },
128
+ { id: 'unread', label: 'Unread' },
129
+ { id: 'mentions', label: 'Mentions' },
130
+ ];
131
+ showFooter = true;
132
+ markAllRead = new EventEmitter();
133
+ settingsClick = new EventEmitter();
134
+ itemClick = new EventEmitter();
135
+ filterChange = new EventEmitter();
136
+ viewAll = new EventEmitter();
137
+ get hostClasses() {
138
+ return 'kp-notif-center';
139
+ }
140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpNotificationCenterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
141
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: KpNotificationCenterComponent, isStandalone: true, selector: "kp-notification-center", inputs: { state: "state", notifications: "notifications", showFilters: "showFilters", activeFilter: "activeFilter", filters: "filters", showFooter: "showFooter" }, outputs: { markAllRead: "markAllRead", settingsClick: "settingsClick", itemClick: "itemClick", filterChange: "filterChange", viewAll: "viewAll" }, host: { attributes: { "role": "region", "aria-label": "Notifications" }, properties: { "class": "hostClasses" } }, ngImport: i0, template: `
142
+ <div class="kp-notif-center__header">
143
+ <span class="kp-notif-center__title">Notifications</span>
144
+ <div class="kp-notif-center__header-actions">
145
+ <kp-button size="sm" variant="ghost" color="neutral" (click)="markAllRead.emit()">Mark all as read</kp-button>
146
+ <kp-button size="sm" variant="ghost" color="neutral" [iconOnly]="true" aria-label="Settings" (click)="settingsClick.emit()">
147
+ <kp-icon kpButtonIconLeft name="settings" />
148
+ </kp-button>
149
+ </div>
150
+ </div>
151
+
152
+ @if (showFilters) {
153
+ <div class="kp-notif-center__filters">
154
+ @for (f of filters; track f.id) {
155
+ <button
156
+ type="button"
157
+ class="kp-notif-center__filter"
158
+ [class.kp-notif-center__filter--active]="activeFilter === f.id"
159
+ (click)="filterChange.emit(f.id)"
160
+ >
161
+ {{ f.label }}
162
+ @if (f.count != null) { <span class="kp-notif-center__count">{{ f.count }}</span> }
163
+ </button>
164
+ }
165
+ </div>
166
+ }
167
+
168
+ <div class="kp-notif-center__body">
169
+ @switch (state) {
170
+ @case ('with-items') {
171
+ <div class="kp-notif-center__list" role="list">
172
+ @for (n of notifications; track n.id) {
173
+ <kp-notification-item
174
+ [title]="n.title"
175
+ [message]="n.message ?? null"
176
+ [time]="n.time ?? null"
177
+ [read]="!!n.read"
178
+ [icon]="n.avatarInitials || n.avatarSrc ? null : (n.icon ?? 'bell')"
179
+ [appearance]="n.appearance || 'neutral'"
180
+ [avatarInitials]="n.avatarInitials ?? null"
181
+ [avatarSrc]="n.avatarSrc ?? null"
182
+ (click$)="itemClick.emit(n)"
183
+ />
184
+ }
185
+ </div>
186
+ }
187
+ @case ('empty') {
188
+ <div class="kp-notif-center__empty">
189
+ <span class="kp-notif-center__empty-icon" aria-hidden="true"><kp-icon name="bell-off" /></span>
190
+ <span class="kp-notif-center__empty-title">You're all caught up</span>
191
+ <span class="kp-notif-center__empty-desc">No new notifications. We'll let you know when something arrives.</span>
192
+ </div>
193
+ }
194
+ @case ('loading') {
195
+ <div class="kp-notif-center__list">
196
+ @for (_ of [0,1,2,3]; track $index) {
197
+ <div class="kp-notif-center__skeleton">
198
+ <span class="kp-notif-center__skel-avatar"></span>
199
+ <span class="kp-notif-center__skel-lines">
200
+ <span class="kp-notif-center__skel-line" style="width:60%"></span>
201
+ <span class="kp-notif-center__skel-line" style="width:90%"></span>
202
+ <span class="kp-notif-center__skel-line" style="width:30%"></span>
203
+ </span>
204
+ </div>
205
+ }
206
+ </div>
207
+ }
208
+ }
209
+ </div>
210
+
211
+ @if (showFooter) {
212
+ <div class="kp-notif-center__footer">
213
+ <kp-button size="sm" variant="ghost" color="neutral" (click)="viewAll.emit()">View all notifications</kp-button>
214
+ </div>
215
+ }
216
+ `, isInline: true, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:400px;max-height:600px;border-radius:12px;background:var(--kp-color-white, var(--kp-color-white));border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));box-shadow:var(--kp-elevation-floating);overflow:hidden;font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}.kp-notif-center__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}.kp-notif-center__title{font-size:15px;font-weight:600;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__header-actions{display:flex;align-items:center;gap:4px}.kp-notif-center__filters{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}.kp-notif-center__filter{all:unset;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:6px;font-size:13px;font-weight:500;color:var(--kp-color-gray-600, var(--kp-color-gray-600));cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-notif-center__filter:hover{background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__filter--active{background:var(--kp-color-blue-50, var(--kp-color-blue-50));color:var(--kp-color-blue-700, var(--kp-color-blue-700))}.kp-notif-center__count{display:inline-flex;align-items:center;padding:1px 6px;border-radius:999px;background:var(--kp-color-gray-200, var(--kp-color-gray-200));color:var(--kp-color-gray-700, var(--kp-color-gray-700));font-size:11px;font-weight:500}.kp-notif-center__filter--active .kp-notif-center__count{background:var(--kp-color-blue-600, var(--kp-color-blue-600));color:var(--kp-color-white)}.kp-notif-center__body{flex:1 1 auto;min-height:0;overflow-y:auto}.kp-notif-center__list{display:flex;flex-direction:column}.kp-notif-center__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:48px 24px;text-align:center}.kp-notif-center__empty-icon{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:50%;background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-500, var(--kp-color-gray-500));margin-bottom:8px}.kp-notif-center__empty-icon .ti{font-size:24px}.kp-notif-center__empty-title{font-size:14px;font-weight:500;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__empty-desc{font-size:13px;color:var(--kp-color-gray-500, var(--kp-color-gray-500));max-width:260px;line-height:1.4}.kp-notif-center__skeleton{display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-100, var(--kp-color-gray-100))}.kp-notif-center__skel-avatar{width:40px;height:40px;border-radius:50%;background:var(--kp-color-gray-100, var(--kp-color-gray-100));flex:0 0 auto}.kp-notif-center__skel-lines{display:flex;flex-direction:column;gap:6px;flex:1 1 auto;padding-top:4px}.kp-notif-center__skel-line{height:10px;border-radius:4px;background:var(--kp-color-gray-100, var(--kp-color-gray-100))}.kp-notif-center__footer{display:flex;justify-content:center;padding:8px 16px;border-top:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}\n"], dependencies: [{ kind: "component", type: KpButtonComponent, selector: "kp-button", inputs: ["size", "variant", "color", "disabled", "loading", "iconOnly", "forceState"] }, { kind: "component", type: KpNotificationItemComponent, selector: "kp-notification-item", inputs: ["read", "title", "message", "time", "icon", "appearance", "avatarInitials", "avatarSrc"], outputs: ["click$"] }, { kind: "component", type: KpIconComponent, selector: "kp-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
217
+ }
218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpNotificationCenterComponent, decorators: [{
219
+ type: Component,
220
+ args: [{ selector: 'kp-notification-center', imports: [KpButtonComponent, KpNotificationItemComponent, KpIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses', role: 'region', 'aria-label': 'Notifications' }, template: `
221
+ <div class="kp-notif-center__header">
222
+ <span class="kp-notif-center__title">Notifications</span>
223
+ <div class="kp-notif-center__header-actions">
224
+ <kp-button size="sm" variant="ghost" color="neutral" (click)="markAllRead.emit()">Mark all as read</kp-button>
225
+ <kp-button size="sm" variant="ghost" color="neutral" [iconOnly]="true" aria-label="Settings" (click)="settingsClick.emit()">
226
+ <kp-icon kpButtonIconLeft name="settings" />
227
+ </kp-button>
228
+ </div>
229
+ </div>
230
+
231
+ @if (showFilters) {
232
+ <div class="kp-notif-center__filters">
233
+ @for (f of filters; track f.id) {
234
+ <button
235
+ type="button"
236
+ class="kp-notif-center__filter"
237
+ [class.kp-notif-center__filter--active]="activeFilter === f.id"
238
+ (click)="filterChange.emit(f.id)"
239
+ >
240
+ {{ f.label }}
241
+ @if (f.count != null) { <span class="kp-notif-center__count">{{ f.count }}</span> }
242
+ </button>
243
+ }
244
+ </div>
245
+ }
246
+
247
+ <div class="kp-notif-center__body">
248
+ @switch (state) {
249
+ @case ('with-items') {
250
+ <div class="kp-notif-center__list" role="list">
251
+ @for (n of notifications; track n.id) {
252
+ <kp-notification-item
253
+ [title]="n.title"
254
+ [message]="n.message ?? null"
255
+ [time]="n.time ?? null"
256
+ [read]="!!n.read"
257
+ [icon]="n.avatarInitials || n.avatarSrc ? null : (n.icon ?? 'bell')"
258
+ [appearance]="n.appearance || 'neutral'"
259
+ [avatarInitials]="n.avatarInitials ?? null"
260
+ [avatarSrc]="n.avatarSrc ?? null"
261
+ (click$)="itemClick.emit(n)"
262
+ />
263
+ }
264
+ </div>
265
+ }
266
+ @case ('empty') {
267
+ <div class="kp-notif-center__empty">
268
+ <span class="kp-notif-center__empty-icon" aria-hidden="true"><kp-icon name="bell-off" /></span>
269
+ <span class="kp-notif-center__empty-title">You're all caught up</span>
270
+ <span class="kp-notif-center__empty-desc">No new notifications. We'll let you know when something arrives.</span>
271
+ </div>
272
+ }
273
+ @case ('loading') {
274
+ <div class="kp-notif-center__list">
275
+ @for (_ of [0,1,2,3]; track $index) {
276
+ <div class="kp-notif-center__skeleton">
277
+ <span class="kp-notif-center__skel-avatar"></span>
278
+ <span class="kp-notif-center__skel-lines">
279
+ <span class="kp-notif-center__skel-line" style="width:60%"></span>
280
+ <span class="kp-notif-center__skel-line" style="width:90%"></span>
281
+ <span class="kp-notif-center__skel-line" style="width:30%"></span>
282
+ </span>
283
+ </div>
284
+ }
285
+ </div>
286
+ }
287
+ }
288
+ </div>
289
+
290
+ @if (showFooter) {
291
+ <div class="kp-notif-center__footer">
292
+ <kp-button size="sm" variant="ghost" color="neutral" (click)="viewAll.emit()">View all notifications</kp-button>
293
+ </div>
294
+ }
295
+ `, styles: [":host{box-sizing:border-box;display:flex;flex-direction:column;width:400px;max-height:600px;border-radius:12px;background:var(--kp-color-white, var(--kp-color-white));border:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));box-shadow:var(--kp-elevation-floating);overflow:hidden;font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}.kp-notif-center__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}.kp-notif-center__title{font-size:15px;font-weight:600;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__header-actions{display:flex;align-items:center;gap:4px}.kp-notif-center__filters{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}.kp-notif-center__filter{all:unset;display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:6px;font-size:13px;font-weight:500;color:var(--kp-color-gray-600, var(--kp-color-gray-600));cursor:pointer;transition:background var(--kp-motion-duration-fast) ease,color .12s ease}.kp-notif-center__filter:hover{background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__filter--active{background:var(--kp-color-blue-50, var(--kp-color-blue-50));color:var(--kp-color-blue-700, var(--kp-color-blue-700))}.kp-notif-center__count{display:inline-flex;align-items:center;padding:1px 6px;border-radius:999px;background:var(--kp-color-gray-200, var(--kp-color-gray-200));color:var(--kp-color-gray-700, var(--kp-color-gray-700));font-size:11px;font-weight:500}.kp-notif-center__filter--active .kp-notif-center__count{background:var(--kp-color-blue-600, var(--kp-color-blue-600));color:var(--kp-color-white)}.kp-notif-center__body{flex:1 1 auto;min-height:0;overflow-y:auto}.kp-notif-center__list{display:flex;flex-direction:column}.kp-notif-center__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:48px 24px;text-align:center}.kp-notif-center__empty-icon{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:50%;background:var(--kp-color-gray-100, var(--kp-color-gray-100));color:var(--kp-color-gray-500, var(--kp-color-gray-500));margin-bottom:8px}.kp-notif-center__empty-icon .ti{font-size:24px}.kp-notif-center__empty-title{font-size:14px;font-weight:500;color:var(--kp-color-gray-900, var(--kp-color-gray-900))}.kp-notif-center__empty-desc{font-size:13px;color:var(--kp-color-gray-500, var(--kp-color-gray-500));max-width:260px;line-height:1.4}.kp-notif-center__skeleton{display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid var(--kp-color-gray-100, var(--kp-color-gray-100))}.kp-notif-center__skel-avatar{width:40px;height:40px;border-radius:50%;background:var(--kp-color-gray-100, var(--kp-color-gray-100));flex:0 0 auto}.kp-notif-center__skel-lines{display:flex;flex-direction:column;gap:6px;flex:1 1 auto;padding-top:4px}.kp-notif-center__skel-line{height:10px;border-radius:4px;background:var(--kp-color-gray-100, var(--kp-color-gray-100))}.kp-notif-center__footer{display:flex;justify-content:center;padding:8px 16px;border-top:1px solid var(--kp-color-gray-200, var(--kp-color-gray-200))}\n"] }]
296
+ }], propDecorators: { state: [{
297
+ type: Input
298
+ }], notifications: [{
299
+ type: Input
300
+ }], showFilters: [{
301
+ type: Input
302
+ }], activeFilter: [{
303
+ type: Input
304
+ }], filters: [{
305
+ type: Input
306
+ }], showFooter: [{
307
+ type: Input
308
+ }], markAllRead: [{
309
+ type: Output
310
+ }], settingsClick: [{
311
+ type: Output
312
+ }], itemClick: [{
313
+ type: Output
314
+ }], filterChange: [{
315
+ type: Output
316
+ }], viewAll: [{
317
+ type: Output
318
+ }] } });
319
+
320
+ /**
321
+ * Generated bundle index. Do not edit.
322
+ */
323
+
324
+ export { KpNotificationCenterComponent, KpNotificationItemComponent };
325
+ //# sourceMappingURL=kanso-protocol-notification-center.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kanso-protocol-notification-center.mjs","sources":["../../../../../packages/patterns/notification-center/src/notification-item.component.ts","../../../../../packages/patterns/notification-center/src/notification-center.component.ts","../../../../../packages/patterns/notification-center/src/kanso-protocol-notification-center.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport {\n KpAvatarAppearance,\n KpAvatarComponent,\n KpAvatarStatus,\n} from '@kanso-protocol/avatar';\nimport { KpIconComponent } from '@kanso-protocol/icon';\n\nexport type KpNotificationAppearance =\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'info'\n | 'neutral';\n\n@Component({\n selector: 'kp-notification-item',\n imports: [KpAvatarComponent, KpIconComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses', role: 'listitem' },\n template: `\n <kp-avatar\n size=\"md\"\n [appearance]=\"resolvedAppearance\"\n [initials]=\"avatarInitials || null\"\n [src]=\"avatarSrc || null\"\n [showStatus]=\"!read\"\n [status]=\"statusColor\"\n >\n @if (!avatarInitials && !avatarSrc && icon) {\n <kp-icon [name]=\"icon\" />\n }\n </kp-avatar>\n <div class=\"kp-notif-item__content\">\n <span class=\"kp-notif-item__title\">{{ title }}</span>\n @if (message) {\n <span class=\"kp-notif-item__message\">{{ message }}</span>\n }\n @if (time) {\n <span class=\"kp-notif-item__time\">{{ time }}</span>\n }\n </div>\n `,\n styles: [`\n :host {\n box-sizing: border-box;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--kp-color-gray-100, var(--kp-color-gray-100));\n background: var(--kp-color-white, var(--kp-color-white));\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n cursor: pointer;\n transition: background var(--kp-motion-duration-fast) ease;\n }\n :host(.kp-notif-item--unread) { background: var(--kp-color-gray-50, var(--kp-color-gray-50)); }\n :host(:hover) { background: var(--kp-color-gray-100, var(--kp-color-gray-100)); }\n\n kp-avatar { flex: 0 0 auto; }\n kp-avatar ::ng-deep .ti { font-size: 18px; line-height: 1; }\n\n .kp-notif-item__content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 auto;\n min-width: 0;\n }\n .kp-notif-item__title {\n font-size: 13px;\n font-weight: 500;\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-notif-item__message {\n font-size: 13px;\n color: var(--kp-color-gray-600, var(--kp-color-gray-600));\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .kp-notif-item__time {\n font-size: 11px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n margin-top: 2px;\n }\n `],\n})\nexport class KpNotificationItemComponent {\n @Input() read = false;\n @Input() title = 'Notification title';\n @Input() message: string | null = null;\n @Input() time: string | null = null;\n\n @Input() icon: string | null = 'bell';\n @Input() appearance: KpNotificationAppearance = 'neutral';\n\n @Input() avatarInitials: string | null = null;\n @Input() avatarSrc: string | null = null;\n\n @Output() click$ = new EventEmitter<void>();\n\n /** Map notification appearance to Avatar's appearance tokens. User avatars stay default. */\n get resolvedAppearance(): KpAvatarAppearance {\n if (this.avatarInitials || this.avatarSrc) return 'default';\n return this.appearance as KpAvatarAppearance;\n }\n\n /** Unread indicator color — matches the appearance intent. Default: online (green). */\n get statusColor(): KpAvatarStatus {\n switch (this.appearance) {\n case 'danger':\n case 'warning': return 'busy';\n case 'neutral': return 'offline';\n default: return 'online';\n }\n }\n\n get hostClasses(): string {\n return `kp-notif-item ${this.read ? '' : 'kp-notif-item--unread'}`;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n} from '@angular/core';\nimport { KpButtonComponent } from '@kanso-protocol/button';\nimport { KpIconComponent } from '@kanso-protocol/icon';\nimport {\n KpNotificationAppearance,\n KpNotificationItemComponent,\n} from './notification-item.component';\n\nexport type KpNotificationCenterState = 'with-items' | 'empty' | 'loading';\n\nexport interface KpNotification {\n id: string;\n title: string;\n message?: string;\n time?: string;\n read?: boolean;\n icon?: string;\n appearance?: KpNotificationAppearance;\n avatarInitials?: string;\n avatarSrc?: string;\n}\n\n/**\n * Kanso Protocol — NotificationCenter\n *\n * Panel with header + optional filters + list of notifications +\n * optional footer (\"View all\"). Three states: with-items / empty /\n * loading. Typically anchored to the bell icon in Header via\n * DropdownMenu / Popover positioning.\n *\n * @example\n * <kp-notification-center\n * state=\"with-items\"\n * [notifications]=\"items\"\n * [showFilters]=\"true\"\n * unreadCount=\"3\"\n * />\n */\n@Component({\n selector: 'kp-notification-center',\n imports: [KpButtonComponent, KpNotificationItemComponent, KpIconComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses', role: 'region', 'aria-label': 'Notifications' },\n template: `\n <div class=\"kp-notif-center__header\">\n <span class=\"kp-notif-center__title\">Notifications</span>\n <div class=\"kp-notif-center__header-actions\">\n <kp-button size=\"sm\" variant=\"ghost\" color=\"neutral\" (click)=\"markAllRead.emit()\">Mark all as read</kp-button>\n <kp-button size=\"sm\" variant=\"ghost\" color=\"neutral\" [iconOnly]=\"true\" aria-label=\"Settings\" (click)=\"settingsClick.emit()\">\n <kp-icon kpButtonIconLeft name=\"settings\" />\n </kp-button>\n </div>\n </div>\n\n @if (showFilters) {\n <div class=\"kp-notif-center__filters\">\n @for (f of filters; track f.id) {\n <button\n type=\"button\"\n class=\"kp-notif-center__filter\"\n [class.kp-notif-center__filter--active]=\"activeFilter === f.id\"\n (click)=\"filterChange.emit(f.id)\"\n >\n {{ f.label }}\n @if (f.count != null) { <span class=\"kp-notif-center__count\">{{ f.count }}</span> }\n </button>\n }\n </div>\n }\n\n <div class=\"kp-notif-center__body\">\n @switch (state) {\n @case ('with-items') {\n <div class=\"kp-notif-center__list\" role=\"list\">\n @for (n of notifications; track n.id) {\n <kp-notification-item\n [title]=\"n.title\"\n [message]=\"n.message ?? null\"\n [time]=\"n.time ?? null\"\n [read]=\"!!n.read\"\n [icon]=\"n.avatarInitials || n.avatarSrc ? null : (n.icon ?? 'bell')\"\n [appearance]=\"n.appearance || 'neutral'\"\n [avatarInitials]=\"n.avatarInitials ?? null\"\n [avatarSrc]=\"n.avatarSrc ?? null\"\n (click$)=\"itemClick.emit(n)\"\n />\n }\n </div>\n }\n @case ('empty') {\n <div class=\"kp-notif-center__empty\">\n <span class=\"kp-notif-center__empty-icon\" aria-hidden=\"true\"><kp-icon name=\"bell-off\" /></span>\n <span class=\"kp-notif-center__empty-title\">You're all caught up</span>\n <span class=\"kp-notif-center__empty-desc\">No new notifications. We'll let you know when something arrives.</span>\n </div>\n }\n @case ('loading') {\n <div class=\"kp-notif-center__list\">\n @for (_ of [0,1,2,3]; track $index) {\n <div class=\"kp-notif-center__skeleton\">\n <span class=\"kp-notif-center__skel-avatar\"></span>\n <span class=\"kp-notif-center__skel-lines\">\n <span class=\"kp-notif-center__skel-line\" style=\"width:60%\"></span>\n <span class=\"kp-notif-center__skel-line\" style=\"width:90%\"></span>\n <span class=\"kp-notif-center__skel-line\" style=\"width:30%\"></span>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n @if (showFooter) {\n <div class=\"kp-notif-center__footer\">\n <kp-button size=\"sm\" variant=\"ghost\" color=\"neutral\" (click)=\"viewAll.emit()\">View all notifications</kp-button>\n </div>\n }\n `,\n styles: [`\n :host {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n width: 400px;\n max-height: 600px;\n border-radius: 12px;\n background: var(--kp-color-white, var(--kp-color-white));\n border: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n box-shadow: var(--kp-elevation-floating);\n overflow: hidden;\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n }\n\n .kp-notif-center__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n }\n .kp-notif-center__title {\n font-size: 15px;\n font-weight: 600;\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-notif-center__header-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .kp-notif-center__filters {\n display: flex;\n gap: 4px;\n padding: 8px;\n border-bottom: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n }\n .kp-notif-center__filter {\n all: unset;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--kp-color-gray-600, var(--kp-color-gray-600));\n cursor: pointer;\n transition: background var(--kp-motion-duration-fast) ease, color 120ms ease;\n }\n .kp-notif-center__filter:hover { background: var(--kp-color-gray-100, var(--kp-color-gray-100)); color: var(--kp-color-gray-900, var(--kp-color-gray-900)); }\n .kp-notif-center__filter--active { background: var(--kp-color-blue-50, var(--kp-color-blue-50)); color: var(--kp-color-blue-700, var(--kp-color-blue-700)); }\n .kp-notif-center__count {\n display: inline-flex;\n align-items: center;\n padding: 1px 6px;\n border-radius: 999px;\n background: var(--kp-color-gray-200, var(--kp-color-gray-200));\n color: var(--kp-color-gray-700, var(--kp-color-gray-700));\n font-size: 11px;\n font-weight: 500;\n }\n .kp-notif-center__filter--active .kp-notif-center__count {\n background: var(--kp-color-blue-600, var(--kp-color-blue-600));\n color: var(--kp-color-white);\n }\n\n .kp-notif-center__body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n }\n .kp-notif-center__list { display: flex; flex-direction: column; }\n\n .kp-notif-center__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n padding: 48px 24px;\n text-align: center;\n }\n .kp-notif-center__empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: var(--kp-color-gray-100, var(--kp-color-gray-100));\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n margin-bottom: 8px;\n }\n .kp-notif-center__empty-icon .ti { font-size: 24px; }\n .kp-notif-center__empty-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--kp-color-gray-900, var(--kp-color-gray-900));\n }\n .kp-notif-center__empty-desc {\n font-size: 13px;\n color: var(--kp-color-gray-500, var(--kp-color-gray-500));\n max-width: 260px;\n line-height: 1.4;\n }\n\n .kp-notif-center__skeleton {\n display: flex;\n gap: 12px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--kp-color-gray-100, var(--kp-color-gray-100));\n }\n .kp-notif-center__skel-avatar {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--kp-color-gray-100, var(--kp-color-gray-100));\n flex: 0 0 auto;\n }\n .kp-notif-center__skel-lines {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1 1 auto;\n padding-top: 4px;\n }\n .kp-notif-center__skel-line {\n height: 10px;\n border-radius: 4px;\n background: var(--kp-color-gray-100, var(--kp-color-gray-100));\n }\n\n .kp-notif-center__footer {\n display: flex;\n justify-content: center;\n padding: 8px 16px;\n border-top: 1px solid var(--kp-color-gray-200, var(--kp-color-gray-200));\n }\n `],\n})\nexport class KpNotificationCenterComponent {\n @Input() state: KpNotificationCenterState = 'with-items';\n @Input() notifications: KpNotification[] = [];\n\n @Input() showFilters = false;\n @Input() activeFilter = 'all';\n @Input() filters: { id: string; label: string; count?: number }[] = [\n { id: 'all', label: 'All' },\n { id: 'unread', label: 'Unread' },\n { id: 'mentions', label: 'Mentions' },\n ];\n\n @Input() showFooter = true;\n\n @Output() markAllRead = new EventEmitter<void>();\n @Output() settingsClick = new EventEmitter<void>();\n @Output() itemClick = new EventEmitter<KpNotification>();\n @Output() filterChange = new EventEmitter<string>();\n @Output() viewAll = new EventEmitter<void>();\n\n get hostClasses(): string {\n return 'kp-notif-center';\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@kanso-protocol/notification-center",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "peerDependencies": {
6
+ "@angular/core": "^18.0.0",
7
+ "@angular/common": "^18.0.0",
8
+ "@kanso-protocol/core": "^0.0.1",
9
+ "@kanso-protocol/avatar": ">=0.1.0",
10
+ "@kanso-protocol/button": ">=0.1.0"
11
+ },
12
+ "description": "Kanso Protocol — notification-center (pattern).",
13
+ "author": "GregNBlack",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/GregNBlack/kanso-protocol.git",
17
+ "directory": "packages/patterns/notification-center"
18
+ },
19
+ "homepage": "https://gregnblack.github.io/kanso-protocol/?path=/docs/patterns-notificationcenter--docs",
20
+ "bugs": "https://github.com/GregNBlack/kanso-protocol/issues",
21
+ "keywords": [
22
+ "design-system",
23
+ "angular",
24
+ "kanso",
25
+ "notification-center"
26
+ ],
27
+ "sideEffects": false,
28
+ "module": "fesm2022/kanso-protocol-notification-center.mjs",
29
+ "typings": "types/kanso-protocol-notification-center.d.ts",
30
+ "exports": {
31
+ "./package.json": {
32
+ "default": "./package.json"
33
+ },
34
+ ".": {
35
+ "types": "./types/kanso-protocol-notification-center.d.ts",
36
+ "default": "./fesm2022/kanso-protocol-notification-center.mjs"
37
+ }
38
+ },
39
+ "type": "module",
40
+ "dependencies": {
41
+ "tslib": "^2.3.0"
42
+ }
43
+ }
@@ -0,0 +1,75 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter } from '@angular/core';
3
+ import { KpAvatarAppearance, KpAvatarStatus } from '@kanso-protocol/avatar';
4
+
5
+ type KpNotificationAppearance = 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral';
6
+ declare class KpNotificationItemComponent {
7
+ read: boolean;
8
+ title: string;
9
+ message: string | null;
10
+ time: string | null;
11
+ icon: string | null;
12
+ appearance: KpNotificationAppearance;
13
+ avatarInitials: string | null;
14
+ avatarSrc: string | null;
15
+ click$: EventEmitter<void>;
16
+ /** Map notification appearance to Avatar's appearance tokens. User avatars stay default. */
17
+ get resolvedAppearance(): KpAvatarAppearance;
18
+ /** Unread indicator color — matches the appearance intent. Default: online (green). */
19
+ get statusColor(): KpAvatarStatus;
20
+ get hostClasses(): string;
21
+ static ɵfac: i0.ɵɵFactoryDeclaration<KpNotificationItemComponent, never>;
22
+ static ɵcmp: i0.ɵɵComponentDeclaration<KpNotificationItemComponent, "kp-notification-item", never, { "read": { "alias": "read"; "required": false; }; "title": { "alias": "title"; "required": false; }; "message": { "alias": "message"; "required": false; }; "time": { "alias": "time"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "appearance": { "alias": "appearance"; "required": false; }; "avatarInitials": { "alias": "avatarInitials"; "required": false; }; "avatarSrc": { "alias": "avatarSrc"; "required": false; }; }, { "click$": "click$"; }, never, never, true, never>;
23
+ }
24
+
25
+ type KpNotificationCenterState = 'with-items' | 'empty' | 'loading';
26
+ interface KpNotification {
27
+ id: string;
28
+ title: string;
29
+ message?: string;
30
+ time?: string;
31
+ read?: boolean;
32
+ icon?: string;
33
+ appearance?: KpNotificationAppearance;
34
+ avatarInitials?: string;
35
+ avatarSrc?: string;
36
+ }
37
+ /**
38
+ * Kanso Protocol — NotificationCenter
39
+ *
40
+ * Panel with header + optional filters + list of notifications +
41
+ * optional footer ("View all"). Three states: with-items / empty /
42
+ * loading. Typically anchored to the bell icon in Header via
43
+ * DropdownMenu / Popover positioning.
44
+ *
45
+ * @example
46
+ * <kp-notification-center
47
+ * state="with-items"
48
+ * [notifications]="items"
49
+ * [showFilters]="true"
50
+ * unreadCount="3"
51
+ * />
52
+ */
53
+ declare class KpNotificationCenterComponent {
54
+ state: KpNotificationCenterState;
55
+ notifications: KpNotification[];
56
+ showFilters: boolean;
57
+ activeFilter: string;
58
+ filters: {
59
+ id: string;
60
+ label: string;
61
+ count?: number;
62
+ }[];
63
+ showFooter: boolean;
64
+ markAllRead: EventEmitter<void>;
65
+ settingsClick: EventEmitter<void>;
66
+ itemClick: EventEmitter<KpNotification>;
67
+ filterChange: EventEmitter<string>;
68
+ viewAll: EventEmitter<void>;
69
+ get hostClasses(): string;
70
+ static ɵfac: i0.ɵɵFactoryDeclaration<KpNotificationCenterComponent, never>;
71
+ static ɵcmp: i0.ɵɵComponentDeclaration<KpNotificationCenterComponent, "kp-notification-center", never, { "state": { "alias": "state"; "required": false; }; "notifications": { "alias": "notifications"; "required": false; }; "showFilters": { "alias": "showFilters"; "required": false; }; "activeFilter": { "alias": "activeFilter"; "required": false; }; "filters": { "alias": "filters"; "required": false; }; "showFooter": { "alias": "showFooter"; "required": false; }; }, { "markAllRead": "markAllRead"; "settingsClick": "settingsClick"; "itemClick": "itemClick"; "filterChange": "filterChange"; "viewAll": "viewAll"; }, never, never, true, never>;
72
+ }
73
+
74
+ export { KpNotificationCenterComponent, KpNotificationItemComponent };
75
+ export type { KpNotification, KpNotificationAppearance, KpNotificationCenterState };