@mesgflow/ui-kit 0.6.0 → 0.7.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.
- package/README.md +56 -5
- package/fesm2022/mesgflow-ui-kit-accordion.mjs +3 -2
- package/fesm2022/mesgflow-ui-kit-accordion.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-agenda.mjs +208 -0
- package/fesm2022/mesgflow-ui-kit-agenda.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-aspect-ratio.mjs +37 -0
- package/fesm2022/mesgflow-ui-kit-aspect-ratio.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-blocks.mjs +207 -0
- package/fesm2022/mesgflow-ui-kit-blocks.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-box-input.mjs +2 -2
- package/fesm2022/mesgflow-ui-kit-box-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-button.mjs +188 -242
- package/fesm2022/mesgflow-ui-kit-button.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-card.mjs +37 -16
- package/fesm2022/mesgflow-ui-kit-card.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-carousel.mjs +308 -0
- package/fesm2022/mesgflow-ui-kit-carousel.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-checkbox.mjs +16 -21
- package/fesm2022/mesgflow-ui-kit-checkbox.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-chip.mjs +2 -2
- package/fesm2022/mesgflow-ui-kit-chip.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-code-block.mjs +9 -5
- package/fesm2022/mesgflow-ui-kit-code-block.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-color-picker.mjs +6 -4
- package/fesm2022/mesgflow-ui-kit-color-picker.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-combobox.mjs +145 -65
- package/fesm2022/mesgflow-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-context-menu.mjs +4 -6
- package/fesm2022/mesgflow-ui-kit-context-menu.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-core.mjs +441 -128
- package/fesm2022/mesgflow-ui-kit-core.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-countdown.mjs +149 -0
- package/fesm2022/mesgflow-ui-kit-countdown.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-date-picker.mjs +131 -10
- package/fesm2022/mesgflow-ui-kit-date-picker.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-dialog.mjs +117 -34
- package/fesm2022/mesgflow-ui-kit-dialog.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-drawer.mjs +109 -0
- package/fesm2022/mesgflow-ui-kit-drawer.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-dropzone.mjs +3 -3
- package/fesm2022/mesgflow-ui-kit-dropzone.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-field.mjs +53 -12
- package/fesm2022/mesgflow-ui-kit-field.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-file-upload.mjs +356 -0
- package/fesm2022/mesgflow-ui-kit-file-upload.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs +131 -0
- package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-icon.mjs +6 -1
- package/fesm2022/mesgflow-ui-kit-icon.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-icons-lucide.mjs +371 -0
- package/fesm2022/mesgflow-ui-kit-icons-lucide.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-inline-edit.mjs +279 -0
- package/fesm2022/mesgflow-ui-kit-inline-edit.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-input.mjs +147 -4
- package/fesm2022/mesgflow-ui-kit-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-lightbox.mjs +299 -0
- package/fesm2022/mesgflow-ui-kit-lightbox.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-link.mjs +2 -2
- package/fesm2022/mesgflow-ui-kit-link.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-listbox.mjs +151 -0
- package/fesm2022/mesgflow-ui-kit-listbox.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-mention.mjs +283 -0
- package/fesm2022/mesgflow-ui-kit-mention.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-menu.mjs +169 -5
- package/fesm2022/mesgflow-ui-kit-menu.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-nav-menu.mjs +276 -0
- package/fesm2022/mesgflow-ui-kit-nav-menu.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-notification-center.mjs +281 -0
- package/fesm2022/mesgflow-ui-kit-notification-center.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-number-input.mjs +7 -4
- package/fesm2022/mesgflow-ui-kit-number-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-pagination.mjs +4 -3
- package/fesm2022/mesgflow-ui-kit-pagination.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-password-input.mjs +7 -2
- package/fesm2022/mesgflow-ui-kit-password-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-popover.mjs +9 -49
- package/fesm2022/mesgflow-ui-kit-popover.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-progress.mjs +162 -21
- package/fesm2022/mesgflow-ui-kit-progress.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-radio.mjs +4 -6
- package/fesm2022/mesgflow-ui-kit-radio.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-rating.mjs +2 -2
- package/fesm2022/mesgflow-ui-kit-rating.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-search-input.mjs +9 -5
- package/fesm2022/mesgflow-ui-kit-search-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-segmented-control.mjs +12 -8
- package/fesm2022/mesgflow-ui-kit-segmented-control.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-select.mjs +3 -2
- package/fesm2022/mesgflow-ui-kit-select.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-sidebar.mjs +16 -13
- package/fesm2022/mesgflow-ui-kit-sidebar.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-skeleton.mjs +4 -3
- package/fesm2022/mesgflow-ui-kit-skeleton.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-slider.mjs +250 -3
- package/fesm2022/mesgflow-ui-kit-slider.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-sortable.mjs +526 -0
- package/fesm2022/mesgflow-ui-kit-sortable.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-spinner.mjs +42 -13
- package/fesm2022/mesgflow-ui-kit-spinner.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-split-button.mjs +205 -0
- package/fesm2022/mesgflow-ui-kit-split-button.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-splitter.mjs +210 -3
- package/fesm2022/mesgflow-ui-kit-splitter.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-stat-tile.mjs +11 -4
- package/fesm2022/mesgflow-ui-kit-stat-tile.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-stepper.mjs +167 -60
- package/fesm2022/mesgflow-ui-kit-stepper.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-switch.mjs +2 -2
- package/fesm2022/mesgflow-ui-kit-switch.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-table.mjs +16 -10
- package/fesm2022/mesgflow-ui-kit-table.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-tabs.mjs +410 -169
- package/fesm2022/mesgflow-ui-kit-tabs.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-tag-input.mjs +7 -2
- package/fesm2022/mesgflow-ui-kit-tag-input.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-theming.mjs +4 -4
- package/fesm2022/mesgflow-ui-kit-theming.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-time-picker.mjs +3 -3
- package/fesm2022/mesgflow-ui-kit-time-picker.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-toast.mjs +113 -30
- package/fesm2022/mesgflow-ui-kit-toast.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-toggle.mjs +3 -2
- package/fesm2022/mesgflow-ui-kit-toggle.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-toolbar.mjs +4 -2
- package/fesm2022/mesgflow-ui-kit-toolbar.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-tooltip.mjs +73 -50
- package/fesm2022/mesgflow-ui-kit-tooltip.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-tour.mjs +8 -20
- package/fesm2022/mesgflow-ui-kit-tour.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-transfer-list.mjs +288 -0
- package/fesm2022/mesgflow-ui-kit-transfer-list.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit-tree.mjs +1 -1
- package/fesm2022/mesgflow-ui-kit-tree.mjs.map +1 -1
- package/fesm2022/mesgflow-ui-kit-virtual-list.mjs +109 -0
- package/fesm2022/mesgflow-ui-kit-virtual-list.mjs.map +1 -0
- package/fesm2022/mesgflow-ui-kit.mjs +92 -12836
- package/fesm2022/mesgflow-ui-kit.mjs.map +1 -1
- package/package.json +87 -3
- package/schematics/collection.json +10 -0
- package/schematics/migration.json +10 -0
- package/schematics/migrations/v0-7.cjs +70 -0
- package/schematics/ng-add/index.cjs +234 -0
- package/schematics/ng-add/schema.json +43 -0
- package/tokens/color.css +96 -0
- package/tokens/controls.css +145 -0
- package/tokens/elevation.css +62 -6
- package/tokens/motion.css +304 -66
- package/tokens/radius.css +14 -0
- package/tokens.css +1 -0
- package/tokens.json +1583 -0
- package/types/mesgflow-ui-kit-agenda.d.ts +49 -0
- package/types/mesgflow-ui-kit-aspect-ratio.d.ts +18 -0
- package/types/mesgflow-ui-kit-blocks.d.ts +128 -0
- package/types/mesgflow-ui-kit-button.d.ts +23 -38
- package/types/mesgflow-ui-kit-card.d.ts +26 -16
- package/types/mesgflow-ui-kit-carousel.d.ts +51 -0
- package/types/mesgflow-ui-kit-checkbox.d.ts +8 -5
- package/types/mesgflow-ui-kit-code-block.d.ts +4 -1
- package/types/mesgflow-ui-kit-combobox.d.ts +47 -22
- package/types/mesgflow-ui-kit-core.d.ts +271 -21
- package/types/mesgflow-ui-kit-countdown.d.ts +47 -0
- package/types/mesgflow-ui-kit-date-picker.d.ts +31 -1
- package/types/mesgflow-ui-kit-dialog.d.ts +29 -6
- package/types/mesgflow-ui-kit-drawer.d.ts +45 -0
- package/types/mesgflow-ui-kit-field.d.ts +17 -8
- package/types/mesgflow-ui-kit-file-upload.d.ts +64 -0
- package/types/mesgflow-ui-kit-hover-card.d.ts +2 -2
- package/types/mesgflow-ui-kit-icons-lucide.d.ts +27 -0
- package/types/mesgflow-ui-kit-inline-edit.d.ts +42 -0
- package/types/mesgflow-ui-kit-input.d.ts +64 -2
- package/types/mesgflow-ui-kit-lightbox.d.ts +54 -0
- package/types/mesgflow-ui-kit-listbox.d.ts +30 -0
- package/types/mesgflow-ui-kit-mention.d.ts +59 -0
- package/types/mesgflow-ui-kit-menu.d.ts +41 -2
- package/types/mesgflow-ui-kit-nav-menu.d.ts +58 -0
- package/types/mesgflow-ui-kit-notification-center.d.ts +41 -0
- package/types/mesgflow-ui-kit-number-input.d.ts +3 -1
- package/types/mesgflow-ui-kit-password-input.d.ts +3 -1
- package/types/mesgflow-ui-kit-popover.d.ts +13 -5
- package/types/mesgflow-ui-kit-progress.d.ts +33 -12
- package/types/mesgflow-ui-kit-radio.d.ts +2 -2
- package/types/mesgflow-ui-kit-search-input.d.ts +3 -1
- package/types/mesgflow-ui-kit-segmented-control.d.ts +4 -0
- package/types/mesgflow-ui-kit-sidebar.d.ts +1 -1
- package/types/mesgflow-ui-kit-slider.d.ts +60 -1
- package/types/mesgflow-ui-kit-sortable.d.ts +103 -0
- package/types/mesgflow-ui-kit-spinner.d.ts +5 -8
- package/types/mesgflow-ui-kit-split-button.d.ts +47 -0
- package/types/mesgflow-ui-kit-splitter.d.ts +64 -1
- package/types/mesgflow-ui-kit-stat-tile.d.ts +3 -0
- package/types/mesgflow-ui-kit-stepper.d.ts +54 -32
- package/types/mesgflow-ui-kit-table.d.ts +2 -0
- package/types/mesgflow-ui-kit-tabs.d.ts +193 -93
- package/types/mesgflow-ui-kit-tag-input.d.ts +3 -1
- package/types/mesgflow-ui-kit-toast.d.ts +48 -22
- package/types/mesgflow-ui-kit-tooltip.d.ts +15 -12
- package/types/mesgflow-ui-kit-transfer-list.d.ts +34 -0
- package/types/mesgflow-ui-kit-virtual-list.d.ts +35 -0
- package/types/mesgflow-ui-kit.d.ts +87 -5444
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, output, signal, inject, ElementRef, computed, DestroyRef, Component } from '@angular/core';
|
|
3
|
+
import { nextId } from '@mesgflow/ui-kit/core';
|
|
4
|
+
import { Icon } from '@mesgflow/ui-kit/icon';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<mf-nav-menu [items]="nav" ariaLabel="Ana gezinme" (linkClick)="go($event)" />`
|
|
8
|
+
*
|
|
9
|
+
* Pazarlama/site başlığı gezinmesi — WAI-ARIA APG "Disclosure Navigation
|
|
10
|
+
* Menu" (bilerek `role="menu"` DEĞİL: site gezinmesi bağlantılardır, menü
|
|
11
|
+
* uygulama komutlarıdır). Üst düğmeler `aria-expanded` + `aria-controls`;
|
|
12
|
+
* paneller ızgara bağlantılar + isteğe bağlı vurgulu kart.
|
|
13
|
+
*
|
|
14
|
+
* - Fare: üzerine gelince kısa gecikmeyle açılır, ayrılınca kapanır.
|
|
15
|
+
* - Klavye: Enter/Boşluk aç/kapa, Escape kapatır ve odağı düğmeye döndürür,
|
|
16
|
+
* odak gezinmeden çıkınca panel kapanır.
|
|
17
|
+
* - `linkClick`: yönlendirici kullanan uygulamalar için (orta tık/yeni sekme
|
|
18
|
+
* için `href` korunur; `event.preventDefault()` tüketicidedir).
|
|
19
|
+
*/
|
|
20
|
+
class NavMenu {
|
|
21
|
+
items = input.required(/* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
23
|
+
ariaLabel = input('', /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** ms — fare ile açılma gecikmesi. */
|
|
26
|
+
hoverDelay = input(120, /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "hoverDelay" }] : /* istanbul ignore next */ []));
|
|
28
|
+
linkClick = output();
|
|
29
|
+
baseId = nextId('mf-nav-menu');
|
|
30
|
+
open = signal(null, /* @ts-ignore */
|
|
31
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
32
|
+
host = inject(ElementRef);
|
|
33
|
+
timer;
|
|
34
|
+
anyOpen = computed(() => this.open() !== null, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "anyOpen" }] : /* istanbul ignore next */ []));
|
|
36
|
+
constructor() {
|
|
37
|
+
inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));
|
|
38
|
+
}
|
|
39
|
+
panelWidth(item) {
|
|
40
|
+
const cols = (item.links?.length ?? 0) > 3 ? 2 : 1;
|
|
41
|
+
return `min(${cols * 18 + (item.featured ? 15 : 0)}rem, calc(100vw - 2rem))`;
|
|
42
|
+
}
|
|
43
|
+
toggle(i) {
|
|
44
|
+
clearTimeout(this.timer);
|
|
45
|
+
this.open.set(this.open() === i ? null : i);
|
|
46
|
+
}
|
|
47
|
+
hoverOpen(i) {
|
|
48
|
+
if (!this.items()[i]?.links?.length)
|
|
49
|
+
return this.hoverClose();
|
|
50
|
+
clearTimeout(this.timer);
|
|
51
|
+
this.timer = setTimeout(() => this.open.set(i), this.anyOpen() ? 0 : this.hoverDelay());
|
|
52
|
+
}
|
|
53
|
+
hoverClose() {
|
|
54
|
+
clearTimeout(this.timer);
|
|
55
|
+
this.timer = setTimeout(() => this.open.set(null), 200);
|
|
56
|
+
}
|
|
57
|
+
onFocusOut(event) {
|
|
58
|
+
const next = event.relatedTarget;
|
|
59
|
+
if (!next || !this.host.nativeElement.contains(next))
|
|
60
|
+
this.open.set(null);
|
|
61
|
+
}
|
|
62
|
+
closeAndFocus() {
|
|
63
|
+
const i = this.open();
|
|
64
|
+
if (i === null)
|
|
65
|
+
return;
|
|
66
|
+
this.open.set(null);
|
|
67
|
+
this.host.nativeElement.querySelector(`#${this.baseId}-trigger-${i}`)?.focus();
|
|
68
|
+
}
|
|
69
|
+
onLink(event, link) {
|
|
70
|
+
this.linkClick.emit({ link, event });
|
|
71
|
+
this.open.set(null);
|
|
72
|
+
}
|
|
73
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: NavMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
74
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: NavMenu, isStandalone: true, selector: "mf-nav-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hoverDelay: { classPropertyName: "hoverDelay", publicName: "hoverDelay", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkClick: "linkClick" }, host: { listeners: { "focusout": "onFocusOut($event)", "keydown.escape": "closeAndFocus()" }, classAttribute: "relative block" }, exportAs: ["mfNavMenu"], ngImport: i0, template: `
|
|
75
|
+
<nav [attr.aria-label]="ariaLabel() || null">
|
|
76
|
+
<ul class="m-0 flex list-none items-center gap-0.5 p-0">
|
|
77
|
+
@for (item of items(); track item.label; let i = $index) {
|
|
78
|
+
<li (pointerenter)="hoverOpen(i)" (pointerleave)="hoverClose()">
|
|
79
|
+
@if (item.links?.length) {
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
[id]="baseId + '-trigger-' + i"
|
|
83
|
+
[attr.aria-expanded]="open() === i"
|
|
84
|
+
[attr.aria-controls]="baseId + '-panel-' + i"
|
|
85
|
+
class="inline-flex h-9 items-center gap-1 rounded-md px-3 text-sm font-medium text-fg-muted transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring aria-expanded:text-fg"
|
|
86
|
+
(click)="toggle(i)"
|
|
87
|
+
>
|
|
88
|
+
{{ item.label }}
|
|
89
|
+
<span
|
|
90
|
+
mfIcon
|
|
91
|
+
[size]="14"
|
|
92
|
+
class="transition-transform duration-base ease-emphasis"
|
|
93
|
+
[class.rotate-180]="open() === i"
|
|
94
|
+
>expand_more</span
|
|
95
|
+
>
|
|
96
|
+
</button>
|
|
97
|
+
<div
|
|
98
|
+
[id]="baseId + '-panel-' + i"
|
|
99
|
+
[hidden]="open() !== i"
|
|
100
|
+
class="absolute start-0 top-full z-overlay pt-2"
|
|
101
|
+
>
|
|
102
|
+
<div
|
|
103
|
+
class="flex gap-2 rounded-xl border border-hairline bg-overlay p-2 shadow-3 animate-overlay-in"
|
|
104
|
+
[style.width]="panelWidth(item)"
|
|
105
|
+
>
|
|
106
|
+
<ul
|
|
107
|
+
class="m-0 grid flex-1 list-none gap-1 p-0"
|
|
108
|
+
[class.grid-cols-2]="item.links.length > 3"
|
|
109
|
+
>
|
|
110
|
+
@for (link of item.links; track link.href) {
|
|
111
|
+
<li>
|
|
112
|
+
<a
|
|
113
|
+
[href]="link.href"
|
|
114
|
+
class="flex items-start gap-3 rounded-lg p-2.5 no-underline transition-colors duration-fast hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring"
|
|
115
|
+
(click)="onLink($event, link)"
|
|
116
|
+
>
|
|
117
|
+
@if (link.icon) {
|
|
118
|
+
<span
|
|
119
|
+
mfIcon
|
|
120
|
+
[size]="18"
|
|
121
|
+
class="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary-tint text-primary-tint-fg"
|
|
122
|
+
>{{ link.icon }}</span
|
|
123
|
+
>
|
|
124
|
+
}
|
|
125
|
+
<span class="min-w-0">
|
|
126
|
+
<span class="block text-sm font-medium text-fg">{{ link.label }}</span>
|
|
127
|
+
@if (link.description) {
|
|
128
|
+
<span class="mt-0.5 block text-xs text-fg-muted">{{
|
|
129
|
+
link.description
|
|
130
|
+
}}</span>
|
|
131
|
+
}
|
|
132
|
+
</span>
|
|
133
|
+
</a>
|
|
134
|
+
</li>
|
|
135
|
+
}
|
|
136
|
+
</ul>
|
|
137
|
+
@if (item.featured; as f) {
|
|
138
|
+
<a
|
|
139
|
+
[href]="f.href"
|
|
140
|
+
class="relative flex w-56 shrink-0 flex-col justify-end overflow-hidden rounded-lg bg-brand-deep p-4 text-white no-underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring"
|
|
141
|
+
(click)="onLink($event, f)"
|
|
142
|
+
>
|
|
143
|
+
<span class="pointer-events-none absolute inset-0 bg-grid opacity-40"></span>
|
|
144
|
+
<span class="relative font-display font-semibold">{{ f.label }}</span>
|
|
145
|
+
@if (f.description) {
|
|
146
|
+
<span class="relative mt-1 text-xs text-white/80">{{ f.description }}</span>
|
|
147
|
+
}
|
|
148
|
+
</a>
|
|
149
|
+
}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
} @else {
|
|
153
|
+
<a
|
|
154
|
+
[href]="item.href ?? '#'"
|
|
155
|
+
class="inline-flex h-9 items-center rounded-md px-3 text-sm font-medium text-fg-muted no-underline transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring"
|
|
156
|
+
(click)="onLink($event, { label: item.label, href: item.href ?? '#' })"
|
|
157
|
+
>{{ item.label }}</a
|
|
158
|
+
>
|
|
159
|
+
}
|
|
160
|
+
</li>
|
|
161
|
+
}
|
|
162
|
+
</ul>
|
|
163
|
+
</nav>
|
|
164
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: Icon, selector: "span[mfIcon]", inputs: ["size", "filled"] }] });
|
|
165
|
+
}
|
|
166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: NavMenu, decorators: [{
|
|
167
|
+
type: Component,
|
|
168
|
+
args: [{
|
|
169
|
+
selector: 'mf-nav-menu',
|
|
170
|
+
exportAs: 'mfNavMenu',
|
|
171
|
+
imports: [Icon],
|
|
172
|
+
host: {
|
|
173
|
+
class: 'relative block',
|
|
174
|
+
'(focusout)': 'onFocusOut($event)',
|
|
175
|
+
'(keydown.escape)': 'closeAndFocus()',
|
|
176
|
+
},
|
|
177
|
+
template: `
|
|
178
|
+
<nav [attr.aria-label]="ariaLabel() || null">
|
|
179
|
+
<ul class="m-0 flex list-none items-center gap-0.5 p-0">
|
|
180
|
+
@for (item of items(); track item.label; let i = $index) {
|
|
181
|
+
<li (pointerenter)="hoverOpen(i)" (pointerleave)="hoverClose()">
|
|
182
|
+
@if (item.links?.length) {
|
|
183
|
+
<button
|
|
184
|
+
type="button"
|
|
185
|
+
[id]="baseId + '-trigger-' + i"
|
|
186
|
+
[attr.aria-expanded]="open() === i"
|
|
187
|
+
[attr.aria-controls]="baseId + '-panel-' + i"
|
|
188
|
+
class="inline-flex h-9 items-center gap-1 rounded-md px-3 text-sm font-medium text-fg-muted transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring aria-expanded:text-fg"
|
|
189
|
+
(click)="toggle(i)"
|
|
190
|
+
>
|
|
191
|
+
{{ item.label }}
|
|
192
|
+
<span
|
|
193
|
+
mfIcon
|
|
194
|
+
[size]="14"
|
|
195
|
+
class="transition-transform duration-base ease-emphasis"
|
|
196
|
+
[class.rotate-180]="open() === i"
|
|
197
|
+
>expand_more</span
|
|
198
|
+
>
|
|
199
|
+
</button>
|
|
200
|
+
<div
|
|
201
|
+
[id]="baseId + '-panel-' + i"
|
|
202
|
+
[hidden]="open() !== i"
|
|
203
|
+
class="absolute start-0 top-full z-overlay pt-2"
|
|
204
|
+
>
|
|
205
|
+
<div
|
|
206
|
+
class="flex gap-2 rounded-xl border border-hairline bg-overlay p-2 shadow-3 animate-overlay-in"
|
|
207
|
+
[style.width]="panelWidth(item)"
|
|
208
|
+
>
|
|
209
|
+
<ul
|
|
210
|
+
class="m-0 grid flex-1 list-none gap-1 p-0"
|
|
211
|
+
[class.grid-cols-2]="item.links.length > 3"
|
|
212
|
+
>
|
|
213
|
+
@for (link of item.links; track link.href) {
|
|
214
|
+
<li>
|
|
215
|
+
<a
|
|
216
|
+
[href]="link.href"
|
|
217
|
+
class="flex items-start gap-3 rounded-lg p-2.5 no-underline transition-colors duration-fast hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring"
|
|
218
|
+
(click)="onLink($event, link)"
|
|
219
|
+
>
|
|
220
|
+
@if (link.icon) {
|
|
221
|
+
<span
|
|
222
|
+
mfIcon
|
|
223
|
+
[size]="18"
|
|
224
|
+
class="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary-tint text-primary-tint-fg"
|
|
225
|
+
>{{ link.icon }}</span
|
|
226
|
+
>
|
|
227
|
+
}
|
|
228
|
+
<span class="min-w-0">
|
|
229
|
+
<span class="block text-sm font-medium text-fg">{{ link.label }}</span>
|
|
230
|
+
@if (link.description) {
|
|
231
|
+
<span class="mt-0.5 block text-xs text-fg-muted">{{
|
|
232
|
+
link.description
|
|
233
|
+
}}</span>
|
|
234
|
+
}
|
|
235
|
+
</span>
|
|
236
|
+
</a>
|
|
237
|
+
</li>
|
|
238
|
+
}
|
|
239
|
+
</ul>
|
|
240
|
+
@if (item.featured; as f) {
|
|
241
|
+
<a
|
|
242
|
+
[href]="f.href"
|
|
243
|
+
class="relative flex w-56 shrink-0 flex-col justify-end overflow-hidden rounded-lg bg-brand-deep p-4 text-white no-underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring"
|
|
244
|
+
(click)="onLink($event, f)"
|
|
245
|
+
>
|
|
246
|
+
<span class="pointer-events-none absolute inset-0 bg-grid opacity-40"></span>
|
|
247
|
+
<span class="relative font-display font-semibold">{{ f.label }}</span>
|
|
248
|
+
@if (f.description) {
|
|
249
|
+
<span class="relative mt-1 text-xs text-white/80">{{ f.description }}</span>
|
|
250
|
+
}
|
|
251
|
+
</a>
|
|
252
|
+
}
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
} @else {
|
|
256
|
+
<a
|
|
257
|
+
[href]="item.href ?? '#'"
|
|
258
|
+
class="inline-flex h-9 items-center rounded-md px-3 text-sm font-medium text-fg-muted no-underline transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring"
|
|
259
|
+
(click)="onLink($event, { label: item.label, href: item.href ?? '#' })"
|
|
260
|
+
>{{ item.label }}</a
|
|
261
|
+
>
|
|
262
|
+
}
|
|
263
|
+
</li>
|
|
264
|
+
}
|
|
265
|
+
</ul>
|
|
266
|
+
</nav>
|
|
267
|
+
`,
|
|
268
|
+
}]
|
|
269
|
+
}], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hoverDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverDelay", required: false }] }], linkClick: [{ type: i0.Output, args: ["linkClick"] }] } });
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generated bundle index. Do not edit.
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
export { NavMenu };
|
|
276
|
+
//# sourceMappingURL=mesgflow-ui-kit-nav-menu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesgflow-ui-kit-nav-menu.mjs","sources":["../../../projects/ui-kit/nav-menu/nav-menu.ts","../../../projects/ui-kit/nav-menu/mesgflow-ui-kit-nav-menu.ts"],"sourcesContent":["import {\r\n Component,\r\n DestroyRef,\r\n ElementRef,\r\n computed,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n} from '@angular/core';\r\nimport { nextId } from '@mesgflow/ui-kit/core';\r\nimport { Icon } from '@mesgflow/ui-kit/icon';\r\n\r\nexport interface NavMenuLink {\r\n label: string;\r\n href: string;\r\n description?: string;\r\n icon?: string;\r\n}\r\n\r\nexport interface NavMenuItem {\r\n label: string;\r\n /** Alt panel yoksa doğrudan bağlantı. */\r\n href?: string;\r\n links?: NavMenuLink[];\r\n /** Panelin yan tarafındaki vurgulu kart (yeni özellik, duyuru). */\r\n featured?: NavMenuLink;\r\n}\r\n\r\n/**\r\n * `<mf-nav-menu [items]=\"nav\" ariaLabel=\"Ana gezinme\" (linkClick)=\"go($event)\" />`\r\n *\r\n * Pazarlama/site başlığı gezinmesi — WAI-ARIA APG \"Disclosure Navigation\r\n * Menu\" (bilerek `role=\"menu\"` DEĞİL: site gezinmesi bağlantılardır, menü\r\n * uygulama komutlarıdır). Üst düğmeler `aria-expanded` + `aria-controls`;\r\n * paneller ızgara bağlantılar + isteğe bağlı vurgulu kart.\r\n *\r\n * - Fare: üzerine gelince kısa gecikmeyle açılır, ayrılınca kapanır.\r\n * - Klavye: Enter/Boşluk aç/kapa, Escape kapatır ve odağı düğmeye döndürür,\r\n * odak gezinmeden çıkınca panel kapanır.\r\n * - `linkClick`: yönlendirici kullanan uygulamalar için (orta tık/yeni sekme\r\n * için `href` korunur; `event.preventDefault()` tüketicidedir).\r\n */\r\n@Component({\r\n selector: 'mf-nav-menu',\r\n exportAs: 'mfNavMenu',\r\n imports: [Icon],\r\n host: {\r\n class: 'relative block',\r\n '(focusout)': 'onFocusOut($event)',\r\n '(keydown.escape)': 'closeAndFocus()',\r\n },\r\n template: `\r\n <nav [attr.aria-label]=\"ariaLabel() || null\">\r\n <ul class=\"m-0 flex list-none items-center gap-0.5 p-0\">\r\n @for (item of items(); track item.label; let i = $index) {\r\n <li (pointerenter)=\"hoverOpen(i)\" (pointerleave)=\"hoverClose()\">\r\n @if (item.links?.length) {\r\n <button\r\n type=\"button\"\r\n [id]=\"baseId + '-trigger-' + i\"\r\n [attr.aria-expanded]=\"open() === i\"\r\n [attr.aria-controls]=\"baseId + '-panel-' + i\"\r\n class=\"inline-flex h-9 items-center gap-1 rounded-md px-3 text-sm font-medium text-fg-muted transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring aria-expanded:text-fg\"\r\n (click)=\"toggle(i)\"\r\n >\r\n {{ item.label }}\r\n <span\r\n mfIcon\r\n [size]=\"14\"\r\n class=\"transition-transform duration-base ease-emphasis\"\r\n [class.rotate-180]=\"open() === i\"\r\n >expand_more</span\r\n >\r\n </button>\r\n <div\r\n [id]=\"baseId + '-panel-' + i\"\r\n [hidden]=\"open() !== i\"\r\n class=\"absolute start-0 top-full z-overlay pt-2\"\r\n >\r\n <div\r\n class=\"flex gap-2 rounded-xl border border-hairline bg-overlay p-2 shadow-3 animate-overlay-in\"\r\n [style.width]=\"panelWidth(item)\"\r\n >\r\n <ul\r\n class=\"m-0 grid flex-1 list-none gap-1 p-0\"\r\n [class.grid-cols-2]=\"item.links.length > 3\"\r\n >\r\n @for (link of item.links; track link.href) {\r\n <li>\r\n <a\r\n [href]=\"link.href\"\r\n class=\"flex items-start gap-3 rounded-lg p-2.5 no-underline transition-colors duration-fast hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring\"\r\n (click)=\"onLink($event, link)\"\r\n >\r\n @if (link.icon) {\r\n <span\r\n mfIcon\r\n [size]=\"18\"\r\n class=\"mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary-tint text-primary-tint-fg\"\r\n >{{ link.icon }}</span\r\n >\r\n }\r\n <span class=\"min-w-0\">\r\n <span class=\"block text-sm font-medium text-fg\">{{ link.label }}</span>\r\n @if (link.description) {\r\n <span class=\"mt-0.5 block text-xs text-fg-muted\">{{\r\n link.description\r\n }}</span>\r\n }\r\n </span>\r\n </a>\r\n </li>\r\n }\r\n </ul>\r\n @if (item.featured; as f) {\r\n <a\r\n [href]=\"f.href\"\r\n class=\"relative flex w-56 shrink-0 flex-col justify-end overflow-hidden rounded-lg bg-brand-deep p-4 text-white no-underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring\"\r\n (click)=\"onLink($event, f)\"\r\n >\r\n <span class=\"pointer-events-none absolute inset-0 bg-grid opacity-40\"></span>\r\n <span class=\"relative font-display font-semibold\">{{ f.label }}</span>\r\n @if (f.description) {\r\n <span class=\"relative mt-1 text-xs text-white/80\">{{ f.description }}</span>\r\n }\r\n </a>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <a\r\n [href]=\"item.href ?? '#'\"\r\n class=\"inline-flex h-9 items-center rounded-md px-3 text-sm font-medium text-fg-muted no-underline transition-colors duration-fast ease-emphasis hover:bg-state-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring\"\r\n (click)=\"onLink($event, { label: item.label, href: item.href ?? '#' })\"\r\n >{{ item.label }}</a\r\n >\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </nav>\r\n `,\r\n})\r\nexport class NavMenu {\r\n readonly items = input.required<NavMenuItem[]>();\r\n readonly ariaLabel = input('');\r\n /** ms — fare ile açılma gecikmesi. */\r\n readonly hoverDelay = input(120);\r\n readonly linkClick = output<{ link: NavMenuLink; event: MouseEvent }>();\r\n\r\n protected readonly baseId = nextId('mf-nav-menu');\r\n protected readonly open = signal<number | null>(null);\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n private timer?: ReturnType<typeof setTimeout>;\r\n\r\n protected readonly anyOpen = computed(() => this.open() !== null);\r\n\r\n constructor() {\r\n inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));\r\n }\r\n\r\n protected panelWidth(item: NavMenuItem): string {\r\n const cols = (item.links?.length ?? 0) > 3 ? 2 : 1;\r\n return `min(${cols * 18 + (item.featured ? 15 : 0)}rem, calc(100vw - 2rem))`;\r\n }\r\n\r\n protected toggle(i: number): void {\r\n clearTimeout(this.timer);\r\n this.open.set(this.open() === i ? null : i);\r\n }\r\n\r\n protected hoverOpen(i: number): void {\r\n if (!this.items()[i]?.links?.length) return this.hoverClose();\r\n clearTimeout(this.timer);\r\n this.timer = setTimeout(() => this.open.set(i), this.anyOpen() ? 0 : this.hoverDelay());\r\n }\r\n\r\n protected hoverClose(): void {\r\n clearTimeout(this.timer);\r\n this.timer = setTimeout(() => this.open.set(null), 200);\r\n }\r\n\r\n protected onFocusOut(event: FocusEvent): void {\r\n const next = event.relatedTarget as Node | null;\r\n if (!next || !this.host.nativeElement.contains(next)) this.open.set(null);\r\n }\r\n\r\n protected closeAndFocus(): void {\r\n const i = this.open();\r\n if (i === null) return;\r\n this.open.set(null);\r\n this.host.nativeElement.querySelector<HTMLElement>(`#${this.baseId}-trigger-${i}`)?.focus();\r\n }\r\n\r\n protected onLink(event: MouseEvent, link: NavMenuLink): void {\r\n this.linkClick.emit({ link, event });\r\n this.open.set(null);\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import * as i1 from '@angular/cdk/overlay';
|
|
2
|
+
import { OverlayModule } from '@angular/cdk/overlay';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { model, output, inject, signal, computed, Component } from '@angular/core';
|
|
5
|
+
import { Avatar } from '@mesgflow/ui-kit/avatar';
|
|
6
|
+
import { Button } from '@mesgflow/ui-kit/button';
|
|
7
|
+
import { MF_NOTIFICATION_CENTER_LABELS } from '@mesgflow/ui-kit/core';
|
|
8
|
+
import { Icon } from '@mesgflow/ui-kit/icon';
|
|
9
|
+
import { Popover } from '@mesgflow/ui-kit/popover';
|
|
10
|
+
|
|
11
|
+
const TONE_BG = {
|
|
12
|
+
neutral: 'bg-surface-sunken text-fg-muted',
|
|
13
|
+
primary: 'bg-primary-tint text-primary-tint-fg',
|
|
14
|
+
success: 'bg-success-tint text-success-tint-fg',
|
|
15
|
+
warning: 'bg-warning-tint text-warning-tint-fg',
|
|
16
|
+
danger: 'bg-danger-tint text-danger-tint-fg',
|
|
17
|
+
info: 'bg-info-tint text-info-tint-fg',
|
|
18
|
+
help: 'bg-help-tint text-help-tint-fg',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* `<mf-notification-center [(notifications)]="inbox" (itemClick)="open($event)" (viewAll)="go()" />`
|
|
22
|
+
*
|
|
23
|
+
* Başlıktaki zil + açılır gelen kutusu: okunmamış sayacı (99+), "tümünü
|
|
24
|
+
* okundu işaretle", okunmamış/okunmuş sekmesi, boş durum. Bir öğeye tıklamak
|
|
25
|
+
* onu okundu yapar ve `itemClick` yayar. Tetikleyicinin erişilebilir adı
|
|
26
|
+
* okunmamış sayısını içerir; liste `role="list"`.
|
|
27
|
+
*/
|
|
28
|
+
class NotificationCenter {
|
|
29
|
+
notifications = model([], /* @ts-ignore */
|
|
30
|
+
...(ngDevMode ? [{ debugName: "notifications" }] : /* istanbul ignore next */ []));
|
|
31
|
+
itemClick = output();
|
|
32
|
+
viewAll = output();
|
|
33
|
+
labels = inject(MF_NOTIFICATION_CENTER_LABELS);
|
|
34
|
+
open = signal(false, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
36
|
+
unread = computed(() => this.notifications().filter((n) => !n.read).length, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "unread" }] : /* istanbul ignore next */ []));
|
|
38
|
+
triggerLabel = computed(() => this.labels().trigger.replace('{count}', String(this.unread())), /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "triggerLabel" }] : /* istanbul ignore next */ []));
|
|
40
|
+
toneBg(n) {
|
|
41
|
+
return TONE_BG[n.tone ?? 'neutral'];
|
|
42
|
+
}
|
|
43
|
+
markAllRead() {
|
|
44
|
+
this.notifications.update((list) => list.map((n) => ({ ...n, read: true })));
|
|
45
|
+
}
|
|
46
|
+
select(n) {
|
|
47
|
+
this.notifications.update((list) => list.map((x) => (x.id === n.id ? { ...x, read: true } : x)));
|
|
48
|
+
this.itemClick.emit(n);
|
|
49
|
+
}
|
|
50
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: NotificationCenter, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
51
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: NotificationCenter, isStandalone: true, selector: "mf-notification-center", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notifications: "notificationsChange", itemClick: "itemClick", viewAll: "viewAll" }, host: { classAttribute: "inline-flex" }, exportAs: ["mfNotificationCenter"], ngImport: i0, template: `
|
|
52
|
+
<button
|
|
53
|
+
mfButton
|
|
54
|
+
appearance="ghost"
|
|
55
|
+
tone="neutral"
|
|
56
|
+
size="icon"
|
|
57
|
+
shape="square"
|
|
58
|
+
cdkOverlayOrigin
|
|
59
|
+
#origin="cdkOverlayOrigin"
|
|
60
|
+
[attr.aria-label]="triggerLabel()"
|
|
61
|
+
[attr.aria-expanded]="open()"
|
|
62
|
+
aria-haspopup="dialog"
|
|
63
|
+
(click)="open.set(!open())"
|
|
64
|
+
>
|
|
65
|
+
<span mfIcon [size]="20" [class.animate-pop]="unread() > 0">notifications</span>
|
|
66
|
+
@if (unread() > 0) {
|
|
67
|
+
<span
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
class="absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-danger-fill px-1 text-[0.625rem] font-bold text-danger-fill-fg tabular-nums ring-2 ring-canvas"
|
|
70
|
+
>{{ unread() > 99 ? '99+' : unread() }}</span
|
|
71
|
+
>
|
|
72
|
+
}
|
|
73
|
+
</button>
|
|
74
|
+
|
|
75
|
+
<mf-popover [origin]="origin" [(open)]="open" placement="bottom-end" padding="none">
|
|
76
|
+
<section
|
|
77
|
+
role="dialog"
|
|
78
|
+
[attr.aria-label]="labels().title"
|
|
79
|
+
class="flex w-[min(24rem,calc(100vw-2rem))] flex-col"
|
|
80
|
+
>
|
|
81
|
+
<header class="flex items-center justify-between gap-3 border-b border-hairline px-4 py-3">
|
|
82
|
+
<h2 class="font-display text-title-sm text-fg">{{ labels().title }}</h2>
|
|
83
|
+
<button
|
|
84
|
+
mfButton
|
|
85
|
+
size="xs"
|
|
86
|
+
appearance="link"
|
|
87
|
+
[disabled]="!unread()"
|
|
88
|
+
(click)="markAllRead()"
|
|
89
|
+
>
|
|
90
|
+
{{ labels().markAllRead }}
|
|
91
|
+
</button>
|
|
92
|
+
</header>
|
|
93
|
+
|
|
94
|
+
@if (notifications().length) {
|
|
95
|
+
<ul role="list" class="m-0 max-h-96 list-none overflow-y-auto p-1.5">
|
|
96
|
+
@for (n of notifications(); track n.id) {
|
|
97
|
+
<li>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
class="relative flex w-full items-start gap-3 rounded-lg p-2.5 text-start transition-colors duration-fast ease-emphasis hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring"
|
|
101
|
+
(click)="select(n)"
|
|
102
|
+
>
|
|
103
|
+
@if (n.actor) {
|
|
104
|
+
<mf-avatar [name]="n.actor" size="sm" [colorful]="true" />
|
|
105
|
+
} @else {
|
|
106
|
+
<span
|
|
107
|
+
mfIcon
|
|
108
|
+
[size]="16"
|
|
109
|
+
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full"
|
|
110
|
+
[class]="toneBg(n)"
|
|
111
|
+
>{{ n.icon ?? 'notifications' }}</span
|
|
112
|
+
>
|
|
113
|
+
}
|
|
114
|
+
<span class="min-w-0 flex-1">
|
|
115
|
+
<span class="block text-sm text-fg" [class.font-semibold]="!n.read">{{
|
|
116
|
+
n.title
|
|
117
|
+
}}</span>
|
|
118
|
+
@if (n.body) {
|
|
119
|
+
<span class="mt-0.5 line-clamp-2 block text-sm text-fg-muted">{{
|
|
120
|
+
n.body
|
|
121
|
+
}}</span>
|
|
122
|
+
}
|
|
123
|
+
<span class="mt-1 block text-xs text-fg-subtle">{{ n.time }}</span>
|
|
124
|
+
</span>
|
|
125
|
+
@if (!n.read) {
|
|
126
|
+
<span
|
|
127
|
+
class="mt-1.5 h-2 w-2 shrink-0 rounded-full bg-accent"
|
|
128
|
+
aria-hidden="true"
|
|
129
|
+
></span>
|
|
130
|
+
<span class="sr-only">{{ labels().unread }}</span>
|
|
131
|
+
}
|
|
132
|
+
</button>
|
|
133
|
+
</li>
|
|
134
|
+
}
|
|
135
|
+
</ul>
|
|
136
|
+
} @else {
|
|
137
|
+
<div class="flex flex-col items-center gap-2 px-6 py-10 text-center">
|
|
138
|
+
<span mfIcon [size]="28" class="text-fg-subtle">notifications_off</span>
|
|
139
|
+
<p class="font-medium text-fg">{{ labels().empty }}</p>
|
|
140
|
+
<p class="text-sm text-fg-muted">{{ labels().emptyDescription }}</p>
|
|
141
|
+
</div>
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
<footer class="border-t border-hairline p-1.5">
|
|
145
|
+
<button
|
|
146
|
+
mfButton
|
|
147
|
+
appearance="ghost"
|
|
148
|
+
tone="neutral"
|
|
149
|
+
class="w-full"
|
|
150
|
+
(click)="viewAll.emit(); open.set(false)"
|
|
151
|
+
>
|
|
152
|
+
{{ labels().viewAll }}
|
|
153
|
+
</button>
|
|
154
|
+
</footer>
|
|
155
|
+
</section>
|
|
156
|
+
</mf-popover>
|
|
157
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: Popover, selector: "mf-popover", inputs: ["origin", "open", "placement", "padding", "hasBackdrop", "matchTriggerWidth", "panelClass"], outputs: ["openChange", "closed"], exportAs: ["mfPopover"] }, { kind: "component", type: Button, selector: "button[mfButton], a[mfButton]", inputs: ["tone", "appearance", "size", "shape", "type", "loading", "iconStart", "iconEnd"], exportAs: ["mfButton"] }, { kind: "directive", type: Icon, selector: "span[mfIcon]", inputs: ["size", "filled"] }, { kind: "component", type: Avatar, selector: "mf-avatar", inputs: ["name", "src", "size", "shape", "colorful", "tone"] }] });
|
|
158
|
+
}
|
|
159
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: NotificationCenter, decorators: [{
|
|
160
|
+
type: Component,
|
|
161
|
+
args: [{
|
|
162
|
+
selector: 'mf-notification-center',
|
|
163
|
+
exportAs: 'mfNotificationCenter',
|
|
164
|
+
imports: [OverlayModule, Popover, Button, Icon, Avatar],
|
|
165
|
+
host: { class: 'inline-flex' },
|
|
166
|
+
template: `
|
|
167
|
+
<button
|
|
168
|
+
mfButton
|
|
169
|
+
appearance="ghost"
|
|
170
|
+
tone="neutral"
|
|
171
|
+
size="icon"
|
|
172
|
+
shape="square"
|
|
173
|
+
cdkOverlayOrigin
|
|
174
|
+
#origin="cdkOverlayOrigin"
|
|
175
|
+
[attr.aria-label]="triggerLabel()"
|
|
176
|
+
[attr.aria-expanded]="open()"
|
|
177
|
+
aria-haspopup="dialog"
|
|
178
|
+
(click)="open.set(!open())"
|
|
179
|
+
>
|
|
180
|
+
<span mfIcon [size]="20" [class.animate-pop]="unread() > 0">notifications</span>
|
|
181
|
+
@if (unread() > 0) {
|
|
182
|
+
<span
|
|
183
|
+
aria-hidden="true"
|
|
184
|
+
class="absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-danger-fill px-1 text-[0.625rem] font-bold text-danger-fill-fg tabular-nums ring-2 ring-canvas"
|
|
185
|
+
>{{ unread() > 99 ? '99+' : unread() }}</span
|
|
186
|
+
>
|
|
187
|
+
}
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<mf-popover [origin]="origin" [(open)]="open" placement="bottom-end" padding="none">
|
|
191
|
+
<section
|
|
192
|
+
role="dialog"
|
|
193
|
+
[attr.aria-label]="labels().title"
|
|
194
|
+
class="flex w-[min(24rem,calc(100vw-2rem))] flex-col"
|
|
195
|
+
>
|
|
196
|
+
<header class="flex items-center justify-between gap-3 border-b border-hairline px-4 py-3">
|
|
197
|
+
<h2 class="font-display text-title-sm text-fg">{{ labels().title }}</h2>
|
|
198
|
+
<button
|
|
199
|
+
mfButton
|
|
200
|
+
size="xs"
|
|
201
|
+
appearance="link"
|
|
202
|
+
[disabled]="!unread()"
|
|
203
|
+
(click)="markAllRead()"
|
|
204
|
+
>
|
|
205
|
+
{{ labels().markAllRead }}
|
|
206
|
+
</button>
|
|
207
|
+
</header>
|
|
208
|
+
|
|
209
|
+
@if (notifications().length) {
|
|
210
|
+
<ul role="list" class="m-0 max-h-96 list-none overflow-y-auto p-1.5">
|
|
211
|
+
@for (n of notifications(); track n.id) {
|
|
212
|
+
<li>
|
|
213
|
+
<button
|
|
214
|
+
type="button"
|
|
215
|
+
class="relative flex w-full items-start gap-3 rounded-lg p-2.5 text-start transition-colors duration-fast ease-emphasis hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring"
|
|
216
|
+
(click)="select(n)"
|
|
217
|
+
>
|
|
218
|
+
@if (n.actor) {
|
|
219
|
+
<mf-avatar [name]="n.actor" size="sm" [colorful]="true" />
|
|
220
|
+
} @else {
|
|
221
|
+
<span
|
|
222
|
+
mfIcon
|
|
223
|
+
[size]="16"
|
|
224
|
+
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full"
|
|
225
|
+
[class]="toneBg(n)"
|
|
226
|
+
>{{ n.icon ?? 'notifications' }}</span
|
|
227
|
+
>
|
|
228
|
+
}
|
|
229
|
+
<span class="min-w-0 flex-1">
|
|
230
|
+
<span class="block text-sm text-fg" [class.font-semibold]="!n.read">{{
|
|
231
|
+
n.title
|
|
232
|
+
}}</span>
|
|
233
|
+
@if (n.body) {
|
|
234
|
+
<span class="mt-0.5 line-clamp-2 block text-sm text-fg-muted">{{
|
|
235
|
+
n.body
|
|
236
|
+
}}</span>
|
|
237
|
+
}
|
|
238
|
+
<span class="mt-1 block text-xs text-fg-subtle">{{ n.time }}</span>
|
|
239
|
+
</span>
|
|
240
|
+
@if (!n.read) {
|
|
241
|
+
<span
|
|
242
|
+
class="mt-1.5 h-2 w-2 shrink-0 rounded-full bg-accent"
|
|
243
|
+
aria-hidden="true"
|
|
244
|
+
></span>
|
|
245
|
+
<span class="sr-only">{{ labels().unread }}</span>
|
|
246
|
+
}
|
|
247
|
+
</button>
|
|
248
|
+
</li>
|
|
249
|
+
}
|
|
250
|
+
</ul>
|
|
251
|
+
} @else {
|
|
252
|
+
<div class="flex flex-col items-center gap-2 px-6 py-10 text-center">
|
|
253
|
+
<span mfIcon [size]="28" class="text-fg-subtle">notifications_off</span>
|
|
254
|
+
<p class="font-medium text-fg">{{ labels().empty }}</p>
|
|
255
|
+
<p class="text-sm text-fg-muted">{{ labels().emptyDescription }}</p>
|
|
256
|
+
</div>
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
<footer class="border-t border-hairline p-1.5">
|
|
260
|
+
<button
|
|
261
|
+
mfButton
|
|
262
|
+
appearance="ghost"
|
|
263
|
+
tone="neutral"
|
|
264
|
+
class="w-full"
|
|
265
|
+
(click)="viewAll.emit(); open.set(false)"
|
|
266
|
+
>
|
|
267
|
+
{{ labels().viewAll }}
|
|
268
|
+
</button>
|
|
269
|
+
</footer>
|
|
270
|
+
</section>
|
|
271
|
+
</mf-popover>
|
|
272
|
+
`,
|
|
273
|
+
}]
|
|
274
|
+
}], propDecorators: { notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }, { type: i0.Output, args: ["notificationsChange"] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], viewAll: [{ type: i0.Output, args: ["viewAll"] }] } });
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Generated bundle index. Do not edit.
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
export { NotificationCenter };
|
|
281
|
+
//# sourceMappingURL=mesgflow-ui-kit-notification-center.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesgflow-ui-kit-notification-center.mjs","sources":["../../../projects/ui-kit/notification-center/notification-center.ts","../../../projects/ui-kit/notification-center/mesgflow-ui-kit-notification-center.ts"],"sourcesContent":["import { OverlayModule } from '@angular/cdk/overlay';\r\nimport { Component, computed, inject, model, output, signal } from '@angular/core';\r\nimport { Avatar } from '@mesgflow/ui-kit/avatar';\r\nimport { Button } from '@mesgflow/ui-kit/button';\r\nimport { MF_NOTIFICATION_CENTER_LABELS, type MfTone } from '@mesgflow/ui-kit/core';\r\nimport { Icon } from '@mesgflow/ui-kit/icon';\r\nimport { Popover } from '@mesgflow/ui-kit/popover';\r\n\r\nexport interface NotificationItem {\r\n id: string;\r\n title: string;\r\n body?: string;\r\n /** Görünen zaman metni (\"2 dk önce\") — biçimleme tüketicide. */\r\n time: string;\r\n read?: boolean;\r\n /** Avatar için ad; yoksa `icon` gösterilir. */\r\n actor?: string;\r\n icon?: string;\r\n tone?: MfTone;\r\n}\r\n\r\nconst TONE_BG: Record<MfTone, string> = {\r\n neutral: 'bg-surface-sunken text-fg-muted',\r\n primary: 'bg-primary-tint text-primary-tint-fg',\r\n success: 'bg-success-tint text-success-tint-fg',\r\n warning: 'bg-warning-tint text-warning-tint-fg',\r\n danger: 'bg-danger-tint text-danger-tint-fg',\r\n info: 'bg-info-tint text-info-tint-fg',\r\n help: 'bg-help-tint text-help-tint-fg',\r\n};\r\n\r\n/**\r\n * `<mf-notification-center [(notifications)]=\"inbox\" (itemClick)=\"open($event)\" (viewAll)=\"go()\" />`\r\n *\r\n * Başlıktaki zil + açılır gelen kutusu: okunmamış sayacı (99+), \"tümünü\r\n * okundu işaretle\", okunmamış/okunmuş sekmesi, boş durum. Bir öğeye tıklamak\r\n * onu okundu yapar ve `itemClick` yayar. Tetikleyicinin erişilebilir adı\r\n * okunmamış sayısını içerir; liste `role=\"list\"`.\r\n */\r\n@Component({\r\n selector: 'mf-notification-center',\r\n exportAs: 'mfNotificationCenter',\r\n imports: [OverlayModule, Popover, Button, Icon, Avatar],\r\n host: { class: 'inline-flex' },\r\n template: `\r\n <button\r\n mfButton\r\n appearance=\"ghost\"\r\n tone=\"neutral\"\r\n size=\"icon\"\r\n shape=\"square\"\r\n cdkOverlayOrigin\r\n #origin=\"cdkOverlayOrigin\"\r\n [attr.aria-label]=\"triggerLabel()\"\r\n [attr.aria-expanded]=\"open()\"\r\n aria-haspopup=\"dialog\"\r\n (click)=\"open.set(!open())\"\r\n >\r\n <span mfIcon [size]=\"20\" [class.animate-pop]=\"unread() > 0\">notifications</span>\r\n @if (unread() > 0) {\r\n <span\r\n aria-hidden=\"true\"\r\n class=\"absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-danger-fill px-1 text-[0.625rem] font-bold text-danger-fill-fg tabular-nums ring-2 ring-canvas\"\r\n >{{ unread() > 99 ? '99+' : unread() }}</span\r\n >\r\n }\r\n </button>\r\n\r\n <mf-popover [origin]=\"origin\" [(open)]=\"open\" placement=\"bottom-end\" padding=\"none\">\r\n <section\r\n role=\"dialog\"\r\n [attr.aria-label]=\"labels().title\"\r\n class=\"flex w-[min(24rem,calc(100vw-2rem))] flex-col\"\r\n >\r\n <header class=\"flex items-center justify-between gap-3 border-b border-hairline px-4 py-3\">\r\n <h2 class=\"font-display text-title-sm text-fg\">{{ labels().title }}</h2>\r\n <button\r\n mfButton\r\n size=\"xs\"\r\n appearance=\"link\"\r\n [disabled]=\"!unread()\"\r\n (click)=\"markAllRead()\"\r\n >\r\n {{ labels().markAllRead }}\r\n </button>\r\n </header>\r\n\r\n @if (notifications().length) {\r\n <ul role=\"list\" class=\"m-0 max-h-96 list-none overflow-y-auto p-1.5\">\r\n @for (n of notifications(); track n.id) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"relative flex w-full items-start gap-3 rounded-lg p-2.5 text-start transition-colors duration-fast ease-emphasis hover:bg-state-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring\"\r\n (click)=\"select(n)\"\r\n >\r\n @if (n.actor) {\r\n <mf-avatar [name]=\"n.actor\" size=\"sm\" [colorful]=\"true\" />\r\n } @else {\r\n <span\r\n mfIcon\r\n [size]=\"16\"\r\n class=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full\"\r\n [class]=\"toneBg(n)\"\r\n >{{ n.icon ?? 'notifications' }}</span\r\n >\r\n }\r\n <span class=\"min-w-0 flex-1\">\r\n <span class=\"block text-sm text-fg\" [class.font-semibold]=\"!n.read\">{{\r\n n.title\r\n }}</span>\r\n @if (n.body) {\r\n <span class=\"mt-0.5 line-clamp-2 block text-sm text-fg-muted\">{{\r\n n.body\r\n }}</span>\r\n }\r\n <span class=\"mt-1 block text-xs text-fg-subtle\">{{ n.time }}</span>\r\n </span>\r\n @if (!n.read) {\r\n <span\r\n class=\"mt-1.5 h-2 w-2 shrink-0 rounded-full bg-accent\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n <span class=\"sr-only\">{{ labels().unread }}</span>\r\n }\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <div class=\"flex flex-col items-center gap-2 px-6 py-10 text-center\">\r\n <span mfIcon [size]=\"28\" class=\"text-fg-subtle\">notifications_off</span>\r\n <p class=\"font-medium text-fg\">{{ labels().empty }}</p>\r\n <p class=\"text-sm text-fg-muted\">{{ labels().emptyDescription }}</p>\r\n </div>\r\n }\r\n\r\n <footer class=\"border-t border-hairline p-1.5\">\r\n <button\r\n mfButton\r\n appearance=\"ghost\"\r\n tone=\"neutral\"\r\n class=\"w-full\"\r\n (click)=\"viewAll.emit(); open.set(false)\"\r\n >\r\n {{ labels().viewAll }}\r\n </button>\r\n </footer>\r\n </section>\r\n </mf-popover>\r\n `,\r\n})\r\nexport class NotificationCenter {\r\n readonly notifications = model<NotificationItem[]>([]);\r\n readonly itemClick = output<NotificationItem>();\r\n readonly viewAll = output<void>();\r\n\r\n protected readonly labels = inject(MF_NOTIFICATION_CENTER_LABELS);\r\n protected readonly open = signal(false);\r\n protected readonly unread = computed(() => this.notifications().filter((n) => !n.read).length);\r\n protected readonly triggerLabel = computed(() =>\r\n this.labels().trigger.replace('{count}', String(this.unread())),\r\n );\r\n\r\n protected toneBg(n: NotificationItem): string {\r\n return TONE_BG[n.tone ?? 'neutral'];\r\n }\r\n\r\n markAllRead(): void {\r\n this.notifications.update((list) => list.map((n) => ({ ...n, read: true })));\r\n }\r\n\r\n protected select(n: NotificationItem): void {\r\n this.notifications.update((list) =>\r\n list.map((x) => (x.id === n.id ? { ...x, read: true } : x)),\r\n );\r\n this.itemClick.emit(n);\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|