@fylib/adapter-angular 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.
Files changed (75) hide show
  1. package/dist/base/base-component.d.ts +18 -0
  2. package/dist/base/base-component.js +36 -0
  3. package/dist/base/interaction.utils.d.ts +7 -0
  4. package/dist/base/interaction.utils.js +34 -0
  5. package/dist/base/interaction.utils.test.d.ts +1 -0
  6. package/dist/base/interaction.utils.test.js +25 -0
  7. package/dist/components/accordion.component.d.ts +32 -0
  8. package/dist/components/accordion.component.js +337 -0
  9. package/dist/components/badge.component.d.ts +10 -0
  10. package/dist/components/badge.component.js +112 -0
  11. package/dist/components/button.component.d.ts +33 -0
  12. package/dist/components/button.component.js +272 -0
  13. package/dist/components/card.component.d.ts +29 -0
  14. package/dist/components/card.component.js +236 -0
  15. package/dist/components/chart.component.d.ts +39 -0
  16. package/dist/components/chart.component.js +307 -0
  17. package/dist/components/icon.component.d.ts +18 -0
  18. package/dist/components/icon.component.js +144 -0
  19. package/dist/components/input.component.d.ts +50 -0
  20. package/dist/components/input.component.js +383 -0
  21. package/dist/components/modal.component.d.ts +46 -0
  22. package/dist/components/modal.component.js +404 -0
  23. package/dist/components/nav-link.component.d.ts +11 -0
  24. package/dist/components/nav-link.component.js +121 -0
  25. package/dist/components/notification-menu.component.d.ts +68 -0
  26. package/dist/components/notification-menu.component.js +695 -0
  27. package/dist/components/select.component.d.ts +52 -0
  28. package/dist/components/select.component.js +395 -0
  29. package/dist/components/table.component.d.ts +55 -0
  30. package/dist/components/table.component.js +643 -0
  31. package/dist/components/text.component.d.ts +8 -0
  32. package/dist/components/text.component.js +58 -0
  33. package/dist/components/toast.component.d.ts +27 -0
  34. package/dist/components/toast.component.js +260 -0
  35. package/dist/directives/animation.directive.d.ts +5 -0
  36. package/dist/directives/animation.directive.js +34 -0
  37. package/dist/directives/theme-vars.directive.d.ts +7 -0
  38. package/dist/directives/theme-vars.directive.js +70 -0
  39. package/dist/directives/wallpaper.directive.d.ts +28 -0
  40. package/dist/directives/wallpaper.directive.js +195 -0
  41. package/dist/effects/confetti.plugin.d.ts +1 -0
  42. package/dist/effects/confetti.plugin.js +151 -0
  43. package/dist/effects/extra-effects.plugin.d.ts +3 -0
  44. package/dist/effects/extra-effects.plugin.js +288 -0
  45. package/dist/effects/hearts.plugin.d.ts +1 -0
  46. package/dist/effects/hearts.plugin.js +172 -0
  47. package/dist/effects/register-all.d.ts +1 -0
  48. package/dist/effects/register-all.js +16 -0
  49. package/dist/effects/ui-effects.plugin.d.ts +1 -0
  50. package/dist/effects/ui-effects.plugin.js +134 -0
  51. package/dist/icons/providers/fontawesome.provider.d.ts +3 -0
  52. package/dist/icons/providers/fontawesome.provider.js +25 -0
  53. package/dist/icons/providers/mdi.provider.d.ts +3 -0
  54. package/dist/icons/providers/mdi.provider.js +13 -0
  55. package/dist/icons/providers/phosphor.provider.d.ts +3 -0
  56. package/dist/icons/providers/phosphor.provider.js +17 -0
  57. package/dist/icons/registry.d.ts +22 -0
  58. package/dist/icons/registry.js +12 -0
  59. package/dist/index.d.ts +29 -0
  60. package/dist/index.js +29 -0
  61. package/dist/layouts/layout.component.d.ts +24 -0
  62. package/dist/layouts/layout.component.js +255 -0
  63. package/dist/layouts/slot.component.d.ts +61 -0
  64. package/dist/layouts/slot.component.js +937 -0
  65. package/dist/providers.d.ts +12 -0
  66. package/dist/providers.js +18 -0
  67. package/dist/services/fylib.service.d.ts +31 -0
  68. package/dist/services/fylib.service.js +214 -0
  69. package/dist/services/notification.service.d.ts +27 -0
  70. package/dist/services/notification.service.js +118 -0
  71. package/dist/services/sse.service.d.ts +16 -0
  72. package/dist/services/sse.service.js +109 -0
  73. package/dist/services/webclient.service.d.ts +38 -0
  74. package/dist/services/webclient.service.js +144 -0
  75. package/package.json +43 -0
@@ -0,0 +1,695 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Component, Input, Output, EventEmitter, inject, ViewEncapsulation, signal, computed, effect } from '@angular/core';
11
+ import { CommonModule } from '@angular/common';
12
+ import { NotificationMenuDefinition } from '@fylib/catalog';
13
+ import { FyLibService } from '../services/fylib.service';
14
+ import { BaseFyComponent } from '../base/base-component';
15
+ import { FyIconComponent } from './icon.component';
16
+ import { FyAccordionComponent } from './accordion.component';
17
+ import { FyNotificationService } from '../services/notification.service';
18
+ import { FyWebClientService } from '../services/webclient.service';
19
+ let FyNotificationMenuComponent = class FyNotificationMenuComponent extends BaseFyComponent {
20
+ set notifications(value) {
21
+ this._notifications.set(value ?? []);
22
+ }
23
+ get notifications() {
24
+ return this._notifications();
25
+ }
26
+ constructor() {
27
+ super(inject(FyLibService), 'fy-notification-menu');
28
+ this.notify = inject(FyNotificationService);
29
+ this._notifications = signal([]);
30
+ this.unreadCount = 0;
31
+ this.activeAnimations = null;
32
+ this.activeEffects = null;
33
+ this.customStyles = null;
34
+ this.fyOpen = new EventEmitter();
35
+ this.fyClearAll = new EventEmitter();
36
+ this.fyViewAll = new EventEmitter();
37
+ this.fyNotificationClick = new EventEmitter();
38
+ this.isOpen = signal(false);
39
+ this.isClosing = signal(false);
40
+ this.webClient = inject(FyWebClientService, { optional: true });
41
+ this.themeTokens = computed(() => this.fylib.tokens().effects?.notificationMenu);
42
+ this.resolvedConfig = computed(() => {
43
+ const themeConfig = this.themeTokens()?.config;
44
+ const def = NotificationMenuDefinition.defaultProps;
45
+ return {
46
+ showAll: this.showAllNotifications ?? themeConfig?.showAll ?? def.showAllNotifications,
47
+ limit: this.maxNotifications ?? themeConfig?.limit ?? def.maxNotifications,
48
+ allowClear: this.enableClearAll ?? themeConfig?.allowClear ?? def.enableClearAll,
49
+ accordionMode: this.enableAccordion ?? themeConfig?.accordionMode ?? def.enableAccordion,
50
+ showViewAll: this.showViewAll ?? themeConfig?.showViewAll ?? def.showViewAll,
51
+ viewAllPosition: this.viewAllPosition ?? themeConfig?.viewAllPosition ?? def.viewAllPosition,
52
+ markAllAsReadOnOpen: this.markAllAsReadOnOpen ?? themeConfig?.markAllAsReadOnOpen ?? def.markAllAsReadOnOpen,
53
+ markAsReadOnClick: this.markAsReadOnClick ?? themeConfig?.markAsReadOnClick ?? def.markAsReadOnClick,
54
+ readApiEndpoint: this.readApiEndpoint ?? themeConfig?.readApiEndpoint,
55
+ readApiMethod: this.readApiMethod ?? themeConfig?.readApiMethod ?? def.readApiMethod,
56
+ readApiHeaders: this.readApiHeaders ?? themeConfig?.readApiHeaders
57
+ };
58
+ });
59
+ this.visibleLimit = signal(10);
60
+ this.displayNotifications = computed(() => {
61
+ const config = this.resolvedConfig();
62
+ const notifications = this._notifications();
63
+ if (config.showAll)
64
+ return notifications;
65
+ const limit = this.visibleLimit();
66
+ return notifications.slice(0, limit);
67
+ });
68
+ this.shouldScroll = computed(() => this._notifications().length > 5);
69
+ this.scrollHeight = computed(() => this.themeTokens()?.dropdown?.maxHeight || '320px');
70
+ this.accordionItems = computed(() => {
71
+ return this.displayNotifications().map(item => ({
72
+ id: item.id,
73
+ title: item.title,
74
+ subtitle: item.description,
75
+ content: item.details
76
+ }));
77
+ });
78
+ // Sincroniza com o FyNotificationService usando effect
79
+ effect(() => {
80
+ const current = this.notify.notifications();
81
+ this._notifications.set(current);
82
+ const limit = this.resolvedConfig().limit;
83
+ this.visibleLimit.set(Math.max(limit ?? 10, 10));
84
+ });
85
+ }
86
+ initialLimit() {
87
+ const limit = this.resolvedConfig().limit;
88
+ return Math.max(limit ?? 10, 10);
89
+ }
90
+ loadMore() {
91
+ const current = this.visibleLimit();
92
+ const total = this._notifications().length;
93
+ const next = Math.min(current + 10, total);
94
+ this.visibleLimit.set(next);
95
+ }
96
+ get unreadCountDisplay() {
97
+ return this._notifications().filter(n => !n.read).length;
98
+ }
99
+ toggleMenu() {
100
+ if (this.isOpen()) {
101
+ this.closeMenu();
102
+ }
103
+ else {
104
+ this.openMenu();
105
+ }
106
+ }
107
+ openMenu() {
108
+ this.isClosing.set(false);
109
+ this.visibleLimit.set(this.initialLimit());
110
+ this.isOpen.set(true);
111
+ this.fyOpen.emit();
112
+ this.triggerByEvent('fy-notification-menu.open', undefined, this.activeEffects);
113
+ // Marcar todas como lidas se configurado
114
+ if (this.resolvedConfig().markAllAsReadOnOpen) {
115
+ this.markAllAsRead();
116
+ }
117
+ }
118
+ closeMenu() {
119
+ if (!this.isOpen())
120
+ return;
121
+ this.isClosing.set(true);
122
+ this.isOpen.set(false);
123
+ this.visibleLimit.set(this.initialLimit());
124
+ // Aguarda a animação de saída (out) terminar antes de remover do DOM
125
+ setTimeout(() => {
126
+ this.isClosing.set(false);
127
+ }, 200); // tempo aproximado da animação dropdown-out
128
+ }
129
+ clearAll() {
130
+ this.fyClearAll.emit();
131
+ this.triggerByEvent('fy-notification-menu.clearAll', undefined, this.activeEffects);
132
+ this.notify.clearNotifications();
133
+ this._notifications.set([]);
134
+ }
135
+ viewAll() {
136
+ this.fyViewAll.emit();
137
+ this.closeMenu();
138
+ }
139
+ onItemClick(item) {
140
+ this.fyNotificationClick.emit(item);
141
+ if (this.resolvedConfig().markAsReadOnClick && !item.read) {
142
+ this.markAsRead(item);
143
+ }
144
+ }
145
+ onItemExpand(item) {
146
+ if (this.resolvedConfig().markAsReadOnClick && !item.read) {
147
+ this.markAsRead(item);
148
+ }
149
+ }
150
+ onMarkAsRead(event, item) {
151
+ event.stopPropagation();
152
+ this.markAsRead(item);
153
+ }
154
+ markAsRead(item) {
155
+ this.notify.markAsRead(item.id);
156
+ if (this.onRead)
157
+ this.onRead(item);
158
+ // Backend Call
159
+ const config = this.resolvedConfig();
160
+ if (config.readApiEndpoint && this.webClient) {
161
+ this.sendReadToBackend([item.id]);
162
+ }
163
+ }
164
+ markAllAsRead() {
165
+ const unread = this._notifications().filter(n => !n.read);
166
+ if (unread.length === 0)
167
+ return;
168
+ this.notify.markAllAsRead();
169
+ if (this.onReadAll)
170
+ this.onReadAll(unread);
171
+ // Backend Call
172
+ const config = this.resolvedConfig();
173
+ if (config.readApiEndpoint && this.webClient) {
174
+ this.sendReadToBackend(unread.map(n => n.id));
175
+ }
176
+ }
177
+ sendReadToBackend(ids) {
178
+ const config = this.resolvedConfig();
179
+ if (!this.webClient || !config.readApiEndpoint)
180
+ return;
181
+ const body = { ids };
182
+ const method = (config.readApiMethod || 'POST').toLowerCase();
183
+ // O FyWebClientService já lida com crypto e headers
184
+ const request = this.webClient[method](config.readApiEndpoint, body, {
185
+ headers: config.readApiHeaders,
186
+ cryptoEnabled: true // Habilita criptografia se configurada no motor global
187
+ });
188
+ if (request) {
189
+ request.subscribe({
190
+ error: (err) => console.error('[FyNotificationMenu] Failed to mark as read in backend', err)
191
+ });
192
+ }
193
+ }
194
+ get animationClassSuffix() {
195
+ if (this.isClosing()) {
196
+ const anim = this.resolveAnim('close', undefined, NotificationMenuDefinition.features?.animations?.close);
197
+ return anim ? ` fy-anim-${anim}` : '';
198
+ }
199
+ const anim = this.resolveAnim('open', undefined, NotificationMenuDefinition.features?.animations?.open);
200
+ return anim ? ` fy-anim-${anim}` : '';
201
+ }
202
+ };
203
+ __decorate([
204
+ Input(),
205
+ __metadata("design:type", Array),
206
+ __metadata("design:paramtypes", [Array])
207
+ ], FyNotificationMenuComponent.prototype, "notifications", null);
208
+ __decorate([
209
+ Input(),
210
+ __metadata("design:type", Number)
211
+ ], FyNotificationMenuComponent.prototype, "unreadCount", void 0);
212
+ __decorate([
213
+ Input(),
214
+ __metadata("design:type", Boolean)
215
+ ], FyNotificationMenuComponent.prototype, "showAllNotifications", void 0);
216
+ __decorate([
217
+ Input(),
218
+ __metadata("design:type", Number)
219
+ ], FyNotificationMenuComponent.prototype, "maxNotifications", void 0);
220
+ __decorate([
221
+ Input(),
222
+ __metadata("design:type", Boolean)
223
+ ], FyNotificationMenuComponent.prototype, "enableClearAll", void 0);
224
+ __decorate([
225
+ Input(),
226
+ __metadata("design:type", Boolean)
227
+ ], FyNotificationMenuComponent.prototype, "enableAccordion", void 0);
228
+ __decorate([
229
+ Input(),
230
+ __metadata("design:type", Boolean)
231
+ ], FyNotificationMenuComponent.prototype, "showViewAll", void 0);
232
+ __decorate([
233
+ Input(),
234
+ __metadata("design:type", Object)
235
+ ], FyNotificationMenuComponent.prototype, "viewAllPosition", void 0);
236
+ __decorate([
237
+ Input(),
238
+ __metadata("design:type", Boolean)
239
+ ], FyNotificationMenuComponent.prototype, "markAllAsReadOnOpen", void 0);
240
+ __decorate([
241
+ Input(),
242
+ __metadata("design:type", Boolean)
243
+ ], FyNotificationMenuComponent.prototype, "markAsReadOnClick", void 0);
244
+ __decorate([
245
+ Input(),
246
+ __metadata("design:type", String)
247
+ ], FyNotificationMenuComponent.prototype, "readApiEndpoint", void 0);
248
+ __decorate([
249
+ Input(),
250
+ __metadata("design:type", String)
251
+ ], FyNotificationMenuComponent.prototype, "readApiMethod", void 0);
252
+ __decorate([
253
+ Input(),
254
+ __metadata("design:type", Object)
255
+ ], FyNotificationMenuComponent.prototype, "readApiHeaders", void 0);
256
+ __decorate([
257
+ Input(),
258
+ __metadata("design:type", Function)
259
+ ], FyNotificationMenuComponent.prototype, "onRead", void 0);
260
+ __decorate([
261
+ Input(),
262
+ __metadata("design:type", Function)
263
+ ], FyNotificationMenuComponent.prototype, "onReadAll", void 0);
264
+ __decorate([
265
+ Input(),
266
+ __metadata("design:type", Object)
267
+ ], FyNotificationMenuComponent.prototype, "activeAnimations", void 0);
268
+ __decorate([
269
+ Input(),
270
+ __metadata("design:type", Object)
271
+ ], FyNotificationMenuComponent.prototype, "activeEffects", void 0);
272
+ __decorate([
273
+ Input(),
274
+ __metadata("design:type", Object)
275
+ ], FyNotificationMenuComponent.prototype, "customStyles", void 0);
276
+ __decorate([
277
+ Output(),
278
+ __metadata("design:type", Object)
279
+ ], FyNotificationMenuComponent.prototype, "fyOpen", void 0);
280
+ __decorate([
281
+ Output(),
282
+ __metadata("design:type", Object)
283
+ ], FyNotificationMenuComponent.prototype, "fyClearAll", void 0);
284
+ __decorate([
285
+ Output(),
286
+ __metadata("design:type", Object)
287
+ ], FyNotificationMenuComponent.prototype, "fyViewAll", void 0);
288
+ __decorate([
289
+ Output(),
290
+ __metadata("design:type", Object)
291
+ ], FyNotificationMenuComponent.prototype, "fyNotificationClick", void 0);
292
+ FyNotificationMenuComponent = __decorate([
293
+ Component({
294
+ selector: 'fy-notification-menu',
295
+ standalone: true,
296
+ imports: [
297
+ CommonModule,
298
+ FyIconComponent,
299
+ FyAccordionComponent
300
+ ],
301
+ template: `
302
+ <div class="fy-notification-menu">
303
+ <!-- Botão de Notificação -->
304
+ <button
305
+ class="fy-notification-menu__trigger"
306
+ (click)="toggleMenu()"
307
+ [style.color]="themeTokens()?.button?.textColor"
308
+ >
309
+ <fy-icon [name]="themeTokens()?.button?.icon || 'bell'"></fy-icon>
310
+ @if (unreadCountDisplay > 0) {
311
+ <span
312
+ class="fy-notification-menu__badge"
313
+ [style.background]="themeTokens()?.button?.badgeBackground"
314
+ [style.color]="themeTokens()?.button?.badgeTextColor"
315
+ >
316
+ {{ unreadCountDisplay > 99 ? '99+' : unreadCountDisplay }}
317
+ </span>
318
+ }
319
+ </button>
320
+
321
+ <!-- Dropdown -->
322
+ @if (isOpen() || isClosing()) {
323
+ <div
324
+ class="fy-notification-menu__dropdown"
325
+ [class]="composeAnimClasses(animationClassSuffix)"
326
+ [style.width]="themeTokens()?.dropdown?.width"
327
+ [style.background]="themeTokens()?.dropdown?.background"
328
+ [style.border-color]="themeTokens()?.dropdown?.borderColor"
329
+ [style.box-shadow]="themeTokens()?.dropdown?.shadow"
330
+ [style.border-radius]="themeTokens()?.dropdown?.borderRadius"
331
+ >
332
+ <!-- Header -->
333
+ <div class="fy-notification-menu__header">
334
+ <span class="fy-notification-menu__title">Notificações</span>
335
+
336
+ <div class="fy-notification-menu__header-actions">
337
+ @if (resolvedConfig().allowClear && notifications.length > 0) {
338
+ <button
339
+ class="fy-notification-menu__action-btn"
340
+ (click)="clearAll()"
341
+ >
342
+ Limpar tudo
343
+ </button>
344
+ }
345
+
346
+ @if (resolvedConfig().showViewAll && resolvedConfig().viewAllPosition.startsWith('header')) {
347
+ <button
348
+ class="fy-notification-menu__action-btn"
349
+ (click)="viewAll()"
350
+ >
351
+ Ver todas
352
+ </button>
353
+ }
354
+
355
+ <!-- Botão fechar (Mobile/Tablet) -->
356
+ <button class="fy-notification-menu__close-btn" (click)="closeMenu()">
357
+ <fy-icon name="x"></fy-icon>
358
+ </button>
359
+ </div>
360
+ </div>
361
+
362
+ <!-- List -->
363
+ <div
364
+ class="fy-notification-menu__body"
365
+ [class.fy-notification-menu__body--scroll]="shouldScroll()"
366
+ >
367
+ @if (_notifications().length === 0) {
368
+ <div class="fy-notification-menu__empty">
369
+ Nenhuma notificação por enquanto.
370
+ </div>
371
+ }
372
+
373
+ @if (resolvedConfig().accordionMode) {
374
+ @for (item of displayNotifications(); track item.id) {
375
+ <div class="fy-notification-menu__item-wrapper">
376
+ <fy-accordion
377
+ [items]="[{ id: item.id, title: item.title, subtitle: item.description, content: item.details }]"
378
+ [flush]="true"
379
+ class="fy-notification-menu__accordion-item"
380
+ [class.fy-notification-menu__item--unread]="!item.read"
381
+ [class.fy-notification-menu__item--read]="item.read"
382
+ (fyExpand)="onItemExpand(item)"
383
+ >
384
+ </fy-accordion>
385
+ @if (!item.read) {
386
+ <div class="fy-notification-menu__unread-dot"></div>
387
+ }
388
+ </div>
389
+ }
390
+ } @else {
391
+ @for (item of displayNotifications(); track item.id) {
392
+ <div
393
+ class="fy-notification-menu__item"
394
+ [class.fy-notification-menu__item--unread]="!item.read"
395
+ [class.fy-notification-menu__item--read]="item.read"
396
+ (click)="onItemClick(item)"
397
+ >
398
+ @if (item.icon) {
399
+ <div class="fy-notification-menu__item-icon">
400
+ <fy-icon [name]="item.icon"></fy-icon>
401
+ </div>
402
+ }
403
+ <div class="fy-notification-menu__item-content">
404
+ <div class="fy-notification-menu__item-title">{{ item.title }}</div>
405
+ <div class="fy-notification-menu__item-desc">{{ item.description }}</div>
406
+ <div class="fy-notification-menu__item-date">{{ item.date | date:'short' }}</div>
407
+ </div>
408
+ @if (!item.read) {
409
+ <div class="fy-notification-menu__item-actions">
410
+ <button class="fy-notification-menu__read-btn" (click)="onMarkAsRead($event, item)">
411
+ <fy-icon name="check"></fy-icon>
412
+ </button>
413
+ </div>
414
+ }
415
+ @if (!item.read) {
416
+ <div class="fy-notification-menu__unread-dot"></div>
417
+ }
418
+ </div>
419
+ }
420
+ }
421
+ </div>
422
+
423
+ <!-- Footer -->
424
+ @if (resolvedConfig().showViewAll && resolvedConfig().viewAllPosition.startsWith('footer')) {
425
+ <div
426
+ class="fy-notification-menu__footer"
427
+ [style.justify-content]="resolvedConfig().viewAllPosition.endsWith('right') ? 'flex-end' : 'flex-start'"
428
+ >
429
+ <button class="fy-notification-menu__action-btn" (click)="viewAll()">Ver todas</button>
430
+ @if (shouldScroll() && displayNotifications().length < _notifications().length) {
431
+ <button
432
+ class="fy-notification-menu__action-btn"
433
+ (click)="loadMore()"
434
+ >
435
+ Carregar mais
436
+ </button>
437
+ }
438
+ </div>
439
+ }
440
+ </div>
441
+ }
442
+ </div>
443
+ `,
444
+ styles: [`
445
+ .fy-notification-menu {
446
+ position: relative;
447
+ display: inline-block;
448
+ }
449
+
450
+ .fy-notification-menu__trigger {
451
+ background: transparent;
452
+ border: none;
453
+ padding: 8px;
454
+ cursor: pointer;
455
+ position: relative;
456
+ display: flex;
457
+ align-items: center;
458
+ justify-content: center;
459
+ font-size: 22px;
460
+ border-radius: 50%;
461
+ transition: background 0.2s;
462
+ }
463
+
464
+ .fy-notification-menu__trigger:hover {
465
+ background: rgba(0,0,0,0.05);
466
+ }
467
+
468
+ .fy-notification-menu__badge {
469
+ position: absolute;
470
+ top: 4px;
471
+ right: 4px;
472
+ min-width: 18px;
473
+ height: 18px;
474
+ padding: 0 4px;
475
+ border-radius: 9px;
476
+ font-size: 10px;
477
+ font-weight: bold;
478
+ display: flex;
479
+ align-items: center;
480
+ justify-content: center;
481
+ border: 2px solid var(--fy-colors-surface, #ffffff);
482
+ }
483
+
484
+ .fy-notification-menu__dropdown {
485
+ position: absolute;
486
+ top: 100%;
487
+ right: 0;
488
+ margin-top: 8px;
489
+ z-index: 1000;
490
+ display: flex;
491
+ flex-direction: column;
492
+ overflow: hidden;
493
+ box-shadow: var(--fy-notificationMenu-dropdown-shadow, 0 10px 40px rgba(0,0,0,0.1));
494
+ border: 1px solid var(--fy-notificationMenu-dropdown-borderColor, rgba(0,0,0,0.08));
495
+ background-color: var(--fy-notificationMenu-dropdown-background, #fff);
496
+ border-radius: var(--fy-notificationMenu-dropdown-borderRadius, 12px);
497
+ max-height: var(--fy-notificationMenu-dropdown-maxHeight, 450px);
498
+ }
499
+
500
+ @media (max-width: 768px) {
501
+ .fy-notification-menu__dropdown {
502
+ position: fixed;
503
+ top: calc(var(--fy-layout-header-height, 64px) + 8px);
504
+ left: 16px;
505
+ right: 16px;
506
+ width: calc(100% - 32px) !important;
507
+ max-height: calc(100vh - var(--fy-layout-header-height, 64px) - 32px) !important;
508
+ margin-top: 0;
509
+ z-index: 2000;
510
+ }
511
+ }
512
+
513
+ .fy-notification-menu__header {
514
+ padding: 12px 16px;
515
+ display: flex;
516
+ justify-content: space-between;
517
+ align-items: center;
518
+ border-bottom: 1px solid var(--fy-colors-border, rgba(0,0,0,0.05));
519
+ flex-shrink: 0;
520
+ }
521
+
522
+ .fy-notification-menu__header-actions {
523
+ display: flex;
524
+ align-items: center;
525
+ gap: 8px;
526
+ }
527
+
528
+ .fy-notification-menu__title {
529
+ font-weight: var(--fy-typography-fontWeight-bold, 600);
530
+ font-size: 14px;
531
+ }
532
+
533
+ .fy-notification-menu__action-btn {
534
+ background: transparent;
535
+ border: none;
536
+ color: var(--fy-colors-primary, #007aff);
537
+ font-size: 12px;
538
+ cursor: pointer;
539
+ padding: 4px 8px;
540
+ border-radius: 4px;
541
+ }
542
+
543
+ .fy-notification-menu__action-btn:hover {
544
+ background: rgba(var(--fy-colors-primary-rgb), 0.08);
545
+ }
546
+
547
+ .fy-notification-menu__close-btn {
548
+ display: none;
549
+ background: transparent;
550
+ border: none;
551
+ color: var(--fy-colors-text, inherit);
552
+ font-size: 20px;
553
+ cursor: pointer;
554
+ padding: 4px;
555
+ line-height: 1;
556
+ opacity: 0.6;
557
+ transition: opacity 0.2s;
558
+ }
559
+
560
+ .fy-notification-menu__close-btn:hover {
561
+ opacity: 1;
562
+ }
563
+
564
+ @media (max-width: 768px) {
565
+ .fy-notification-menu__close-btn {
566
+ display: inline-flex;
567
+ align-items: center;
568
+ justify-content: center;
569
+ }
570
+ }
571
+
572
+ .fy-notification-menu__body {
573
+ flex: 1;
574
+ min-height: 0;
575
+ overflow-y: auto;
576
+ }
577
+ .fy-notification-menu__body--scroll {
578
+ max-height: var(--fy-notificationMenu-dropdown-maxHeight, 320px);
579
+ }
580
+
581
+ .fy-notification-menu__empty {
582
+ padding: 32px 16px;
583
+ text-align: center;
584
+ color: var(--fy-colors-secondary, #86868b);
585
+ font-size: 13px;
586
+ }
587
+
588
+ .fy-notification-menu__item {
589
+ padding: 12px 16px;
590
+ display: flex;
591
+ gap: 12px;
592
+ cursor: pointer;
593
+ transition: background 0.2s;
594
+ border-bottom: 1px solid var(--fy-colors-border, rgba(0,0,0,0.03));
595
+ position: relative;
596
+ }
597
+
598
+ .fy-notification-menu__item:hover {
599
+ background: rgba(0,0,0,0.02);
600
+ }
601
+
602
+ .fy-notification-menu__item--unread {
603
+ background: rgba(var(--fy-colors-primary-rgb), 0.02);
604
+ }
605
+
606
+ .fy-notification-menu__item--read {
607
+ opacity: 0.6;
608
+ filter: grayscale(0.4);
609
+ transition: all 0.3s ease;
610
+ }
611
+
612
+ .fy-notification-menu__item--read:hover {
613
+ opacity: 0.8;
614
+ filter: grayscale(0.2);
615
+ }
616
+
617
+ .fy-notification-menu__accordion-item.fy-notification-menu__item--read ::ng-deep .fy-accordion__header {
618
+ opacity: 0.7;
619
+ }
620
+
621
+ .fy-notification-menu__unread-dot {
622
+ position: absolute;
623
+ right: 12px;
624
+ top: 12px;
625
+ width: 8px;
626
+ height: 8px;
627
+ background: var(--fy-colors-primary, #007aff);
628
+ border-radius: 50%;
629
+ }
630
+
631
+ .fy-notification-menu__item-content {
632
+ flex: 1;
633
+ }
634
+
635
+ .fy-notification-menu__item-title {
636
+ font-weight: 600;
637
+ font-size: 13px;
638
+ margin-bottom: 2px;
639
+ }
640
+
641
+ .fy-notification-menu__item-desc {
642
+ font-size: 12px;
643
+ color: var(--fy-colors-secondary, #86868b);
644
+ line-height: 1.4;
645
+ }
646
+
647
+ .fy-notification-menu__item-date {
648
+ font-size: 11px;
649
+ color: var(--fy-colors-secondary, #86868b);
650
+ margin-top: 4px;
651
+ opacity: 0.7;
652
+ }
653
+
654
+ .fy-notification-menu__item-actions {
655
+ display: flex;
656
+ align-items: center;
657
+ margin-right: 20px;
658
+ }
659
+
660
+ .fy-notification-menu__read-btn {
661
+ background: transparent;
662
+ border: none;
663
+ padding: 6px;
664
+ border-radius: 4px;
665
+ cursor: pointer;
666
+ color: var(--fy-colors-primary, #007aff);
667
+ opacity: 0.4;
668
+ transition: all 0.2s;
669
+ display: flex;
670
+ align-items: center;
671
+ justify-content: center;
672
+ }
673
+
674
+ .fy-notification-menu__read-btn:hover {
675
+ opacity: 1;
676
+ background: rgba(var(--fy-colors-primary-rgb), 0.1);
677
+ }
678
+
679
+ .fy-notification-menu__accordion-item {
680
+ display: block;
681
+ border-bottom: 1px solid var(--fy-colors-border, rgba(0,0,0,0.03));
682
+ }
683
+
684
+ .fy-notification-menu__footer {
685
+ padding: 8px 16px;
686
+ border-top: 1px solid var(--fy-colors-border, rgba(0,0,0,0.05));
687
+ display: flex;
688
+ flex-shrink: 0;
689
+ }
690
+ `],
691
+ encapsulation: ViewEncapsulation.None
692
+ }),
693
+ __metadata("design:paramtypes", [])
694
+ ], FyNotificationMenuComponent);
695
+ export { FyNotificationMenuComponent };