@masterteam/components 0.0.137 → 0.0.139

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.
@@ -1,14 +1,25 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, input, model, contentChild, output, linkedSignal, computed, effect, signal, Component } from '@angular/core';
3
- import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
2
+ import { inject, PLATFORM_ID, NgZone, computed, input, model, booleanAttribute, contentChild, output, viewChild, linkedSignal, signal, effect, Component } from '@angular/core';
3
+ import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
4
+ import { Directionality } from '@angular/cdk/bidi';
4
5
  import { Avatar } from '@masterteam/components/avatar';
5
6
  import { Card } from '@masterteam/components/card';
6
7
  import { ClientPageMenu } from '@masterteam/components/client-page-menu';
7
8
  import { Icon } from '@masterteam/icons';
8
9
 
10
+ const DEFAULT_SIDEBAR_WIDTH = 280;
11
+ const DEFAULT_MIN_SIDEBAR_WIDTH = 220;
12
+ const DEFAULT_MAX_SIDEBAR_WIDTH = 420;
9
13
  class ClientPage {
10
14
  platformId = inject(PLATFORM_ID);
15
+ document = inject(DOCUMENT);
16
+ dir = inject(Directionality, { optional: true });
17
+ zone = inject(NgZone);
11
18
  mediaQueryList = null;
19
+ resizeMoveListener = null;
20
+ resizeUpListener = null;
21
+ /** Whether the current direction is RTL */
22
+ isRtl = computed(() => this.dir?.value === 'rtl', ...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
12
23
  /** Icon for the left sidebar header */
13
24
  menuIcon = input(...(ngDevMode ? [undefined, { debugName: "menuIcon" }] : /* istanbul ignore next */ []));
14
25
  /** Title for the left sidebar header */
@@ -21,10 +32,23 @@ class ClientPage {
21
32
  activeItem = input(...(ngDevMode ? [undefined, { debugName: "activeItem" }] : /* istanbul ignore next */ []));
22
33
  /** Whether the sidebar menu is collapsed (two-way bindable) */
23
34
  collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
35
+ /** Enables desktop sidebar resizing from the divider */
36
+ resizable = input(true, { ...(ngDevMode ? { debugName: "resizable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
37
+ /** Unique storage seed used to persist the sidebar width */
38
+ storageKey = input(null, ...(ngDevMode ? [{ debugName: "storageKey" }] : /* istanbul ignore next */ []));
39
+ /** Storage location used for persisted sidebar width */
40
+ storageMode = input('local', ...(ngDevMode ? [{ debugName: "storageMode" }] : /* istanbul ignore next */ []));
41
+ /** Minimum desktop sidebar width in pixels */
42
+ minSidebarWidth = input(DEFAULT_MIN_SIDEBAR_WIDTH, ...(ngDevMode ? [{ debugName: "minSidebarWidth" }] : /* istanbul ignore next */ []));
43
+ /** Maximum desktop sidebar width in pixels */
44
+ maxSidebarWidth = input(DEFAULT_MAX_SIDEBAR_WIDTH, ...(ngDevMode ? [{ debugName: "maxSidebarWidth" }] : /* istanbul ignore next */ []));
45
+ /** Optional default desktop sidebar width in pixels */
46
+ defaultSidebarWidth = input(null, ...(ngDevMode ? [{ debugName: "defaultSidebarWidth" }] : /* istanbul ignore next */ []));
24
47
  /** Template projected into the left sidebar header end */
25
48
  headerEnd = contentChild('headerClientPageEnd', ...(ngDevMode ? [{ debugName: "headerEnd" }] : /* istanbul ignore next */ []));
26
49
  /** Emitted when a menu item is clicked */
27
50
  menuItemClick = output();
51
+ layoutRoot = viewChild('layoutRoot', ...(ngDevMode ? [{ debugName: "layoutRoot" }] : /* istanbul ignore next */ []));
28
52
  /** Tracks activeItem input, overridable by user clicks */
29
53
  selectedKey = linkedSignal(() => this.activeItem(), ...(ngDevMode ? [{ debugName: "selectedKey" }] : /* istanbul ignore next */ []));
30
54
  /** Currently selected menu item */
@@ -38,42 +62,48 @@ class ClientPage {
38
62
  }, ...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
39
63
  /** Whether the initial default selection has been emitted */
40
64
  initialEmitted = false;
41
- constructor() {
42
- effect(() => {
43
- const item = this.selectedItem();
44
- if (item && !this.initialEmitted) {
45
- this.initialEmitted = true;
46
- this.menuItemClick.emit(item);
47
- }
48
- });
49
- this.setupResponsiveBehavior();
50
- }
51
- /** Whether the divider/toggle area is being hovered */
52
- dividerHovered = signal(false, ...(ngDevMode ? [{ debugName: "dividerHovered" }] : /* istanbul ignore next */ []));
65
+ /** Whether the sidebar area is being hovered */
66
+ sidebarHovered = signal(false, ...(ngDevMode ? [{ debugName: "sidebarHovered" }] : /* istanbul ignore next */ []));
67
+ /** Whether the desktop divider is currently resizing */
68
+ isResizing = signal(false, ...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
53
69
  /** Whether the viewport is at or below the responsive breakpoint */
54
70
  isResponsive = signal(false, ...(ngDevMode ? [{ debugName: "isResponsive" }] : /* istanbul ignore next */ []));
55
71
  /** Whether the responsive top navigation is expanded */
56
72
  responsiveMenuExpanded = signal(false, ...(ngDevMode ? [{ debugName: "responsiveMenuExpanded" }] : /* istanbul ignore next */ []));
73
+ /** Current desktop sidebar width in pixels */
74
+ desktopSidebarWidth = signal(DEFAULT_SIDEBAR_WIDTH, ...(ngDevMode ? [{ debugName: "desktopSidebarWidth" }] : /* istanbul ignore next */ []));
57
75
  /** Whether the menu section is currently visible */
58
76
  showSidebarMenu = computed(() => this.isResponsive() ? this.responsiveMenuExpanded() : !this.collapsed(), ...(ngDevMode ? [{ debugName: "showSidebarMenu" }] : /* istanbul ignore next */ []));
77
+ /** Sidebar gradient respects RTL direction */
78
+ sidebarGradient = computed(() => {
79
+ const dir = this.isRtl() ? 'to left' : 'to right';
80
+ return `linear-gradient(${dir}, #EBEBF0, #e5e5ea)`;
81
+ }, ...(ngDevMode ? [{ debugName: "sidebarGradient" }] : /* istanbul ignore next */ []));
59
82
  /** Sidebar width reacts to collapsed + hover */
60
83
  sidebarWidth = computed(() => {
61
84
  if (this.isResponsive()) {
62
85
  return '100%';
63
86
  }
64
87
  if (this.collapsed()) {
65
- return this.dividerHovered() ? '12px' : '9px';
88
+ return this.sidebarHovered() ? '12px' : '9px';
66
89
  }
67
- return this.dividerHovered() ? '15.8%' : '16%';
90
+ return `${this.desktopSidebarWidth()}px`;
68
91
  }, ...(ngDevMode ? [{ debugName: "sidebarWidth" }] : /* istanbul ignore next */ []));
92
+ /** Whether the current state allows divider dragging */
93
+ canResize = computed(() => this.resizable() && !this.isResponsive() && !this.collapsed(), ...(ngDevMode ? [{ debugName: "canResize" }] : /* istanbul ignore next */ []));
69
94
  /** Icon for the collapse toggle button */
70
- toggleIcon = computed(() => this.isResponsive()
71
- ? this.responsiveMenuExpanded()
72
- ? 'arrow.chevron-up'
73
- : 'arrow.chevron-down'
74
- : this.collapsed()
75
- ? 'arrow.chevron-right'
76
- : 'arrow.chevron-left', ...(ngDevMode ? [{ debugName: "toggleIcon" }] : /* istanbul ignore next */ []));
95
+ toggleIcon = computed(() => {
96
+ if (this.isResponsive()) {
97
+ return this.responsiveMenuExpanded()
98
+ ? 'arrow.chevron-up'
99
+ : 'arrow.chevron-down';
100
+ }
101
+ const rtl = this.isRtl();
102
+ if (this.collapsed()) {
103
+ return rtl ? 'arrow.chevron-left' : 'arrow.chevron-right';
104
+ }
105
+ return rtl ? 'arrow.chevron-right' : 'arrow.chevron-left';
106
+ }, ...(ngDevMode ? [{ debugName: "toggleIcon" }] : /* istanbul ignore next */ []));
77
107
  /** Accessible label for the toggle button */
78
108
  toggleAriaLabel = computed(() => this.isResponsive()
79
109
  ? this.responsiveMenuExpanded()
@@ -82,12 +112,32 @@ class ClientPage {
82
112
  : this.collapsed()
83
113
  ? 'Expand sidebar'
84
114
  : 'Collapse sidebar', ...(ngDevMode ? [{ debugName: "toggleAriaLabel" }] : /* istanbul ignore next */ []));
115
+ constructor() {
116
+ effect(() => {
117
+ const item = this.selectedItem();
118
+ if (item && !this.initialEmitted) {
119
+ this.initialEmitted = true;
120
+ this.menuItemClick.emit(item);
121
+ }
122
+ });
123
+ effect(() => {
124
+ const minWidth = this.getNormalizedMinSidebarWidth();
125
+ const maxWidth = this.getNormalizedMaxSidebarWidth(minWidth);
126
+ const storage = this.getStorage();
127
+ const storageKey = this.getNormalizedStorageKey();
128
+ const persistedState = storage && storageKey
129
+ ? this.readPersistedState(storage, storageKey)
130
+ : null;
131
+ this.desktopSidebarWidth.set(this.normalizeSidebarWidth(persistedState?.sidebarWidth, this.getDefaultDesktopSidebarWidth(minWidth, maxWidth), minWidth, maxWidth));
132
+ });
133
+ this.setupResponsiveBehavior();
134
+ }
85
135
  toggleCollapsed() {
86
136
  if (this.isResponsive()) {
87
137
  this.responsiveMenuExpanded.update((expanded) => !expanded);
88
138
  return;
89
139
  }
90
- this.collapsed.update((v) => !v);
140
+ this.collapsed.update((value) => !value);
91
141
  }
92
142
  selectItem(item) {
93
143
  this.selectedKey.set(item.key);
@@ -96,7 +146,45 @@ class ClientPage {
96
146
  }
97
147
  this.menuItemClick.emit(item);
98
148
  }
149
+ startResize(event) {
150
+ if (!this.canResize()) {
151
+ return;
152
+ }
153
+ const layoutRoot = this.layoutRoot()?.nativeElement;
154
+ if (!layoutRoot) {
155
+ return;
156
+ }
157
+ event.preventDefault();
158
+ event.stopPropagation();
159
+ const layoutRect = layoutRoot.getBoundingClientRect();
160
+ const minWidth = this.getNormalizedMinSidebarWidth();
161
+ const maxWidth = this.getNormalizedMaxSidebarWidth(minWidth);
162
+ this.isResizing.set(true);
163
+ this.sidebarHovered.set(true);
164
+ this.desktopSidebarWidth.set(this.getWidthFromPointer(event.clientX, layoutRect, minWidth, maxWidth));
165
+ this.zone.runOutsideAngular(() => {
166
+ this.resizeMoveListener = (moveEvent) => {
167
+ const nextWidth = this.getWidthFromPointer(moveEvent.clientX, layoutRect, minWidth, maxWidth);
168
+ if (nextWidth === this.desktopSidebarWidth()) {
169
+ return;
170
+ }
171
+ this.zone.run(() => this.desktopSidebarWidth.set(nextWidth));
172
+ };
173
+ this.resizeUpListener = (_event) => {
174
+ this.zone.run(() => {
175
+ this.isResizing.set(false);
176
+ this.persistSidebarWidth();
177
+ });
178
+ this.cleanupResizeListeners();
179
+ };
180
+ this.document.addEventListener('mousemove', this.resizeMoveListener);
181
+ this.document.addEventListener('mouseup', this.resizeUpListener, {
182
+ once: true,
183
+ });
184
+ });
185
+ }
99
186
  ngOnDestroy() {
187
+ this.cleanupResizeListeners();
100
188
  if (!this.mediaQueryList) {
101
189
  return;
102
190
  }
@@ -134,23 +222,115 @@ class ClientPage {
134
222
  }
135
223
  this.mediaQueryList.addListener(this.handleResponsiveBreakpointChange);
136
224
  }
225
+ cleanupResizeListeners() {
226
+ if (this.resizeMoveListener) {
227
+ this.document.removeEventListener('mousemove', this.resizeMoveListener);
228
+ this.resizeMoveListener = null;
229
+ }
230
+ if (this.resizeUpListener) {
231
+ this.document.removeEventListener('mouseup', this.resizeUpListener);
232
+ this.resizeUpListener = null;
233
+ }
234
+ }
137
235
  syncResponsiveState(isResponsive) {
138
236
  const wasResponsive = this.isResponsive();
139
237
  this.isResponsive.set(isResponsive);
140
- this.dividerHovered.set(false);
238
+ this.sidebarHovered.set(false);
141
239
  if (wasResponsive !== isResponsive) {
142
240
  this.responsiveMenuExpanded.set(false);
143
241
  }
144
242
  }
243
+ getWidthFromPointer(clientX, layoutRect, minWidth, maxWidth) {
244
+ const rawWidth = this.isRtl()
245
+ ? layoutRect.right - clientX
246
+ : clientX - layoutRect.left;
247
+ return this.normalizeSidebarWidth(rawWidth, minWidth, minWidth, maxWidth);
248
+ }
249
+ persistSidebarWidth() {
250
+ const storage = this.getStorage();
251
+ const storageKey = this.getNormalizedStorageKey();
252
+ if (!storage || !storageKey) {
253
+ return;
254
+ }
255
+ this.writePersistedState(storage, storageKey, {
256
+ sidebarWidth: this.desktopSidebarWidth(),
257
+ });
258
+ }
259
+ getNormalizedStorageKey() {
260
+ const storageKey = this.storageKey()?.trim();
261
+ return storageKey ? `mt-client-page:${storageKey}` : null;
262
+ }
263
+ getStorage() {
264
+ if (typeof window === 'undefined') {
265
+ return null;
266
+ }
267
+ return this.storageMode() === 'session'
268
+ ? window.sessionStorage
269
+ : window.localStorage;
270
+ }
271
+ getDefaultDesktopSidebarWidth(minWidth, maxWidth) {
272
+ const configuredWidth = this.defaultSidebarWidth();
273
+ if (configuredWidth !== null && configuredWidth !== undefined) {
274
+ return this.normalizeSidebarWidth(configuredWidth, DEFAULT_SIDEBAR_WIDTH, minWidth, maxWidth);
275
+ }
276
+ if (typeof window !== 'undefined') {
277
+ return this.normalizeSidebarWidth(Math.round(window.innerWidth * 0.16), DEFAULT_SIDEBAR_WIDTH, minWidth, maxWidth);
278
+ }
279
+ return this.normalizeSidebarWidth(DEFAULT_SIDEBAR_WIDTH, DEFAULT_SIDEBAR_WIDTH, minWidth, maxWidth);
280
+ }
281
+ getNormalizedMinSidebarWidth() {
282
+ const minWidth = Number(this.minSidebarWidth());
283
+ return Number.isFinite(minWidth) && minWidth > 0
284
+ ? Math.trunc(minWidth)
285
+ : DEFAULT_MIN_SIDEBAR_WIDTH;
286
+ }
287
+ getNormalizedMaxSidebarWidth(minWidth) {
288
+ const maxWidth = Number(this.maxSidebarWidth());
289
+ if (!Number.isFinite(maxWidth) || maxWidth < minWidth) {
290
+ return Math.max(DEFAULT_MAX_SIDEBAR_WIDTH, minWidth);
291
+ }
292
+ return Math.trunc(maxWidth);
293
+ }
294
+ normalizeSidebarWidth(value, fallback, minWidth, maxWidth) {
295
+ const normalized = Number(value);
296
+ if (!Number.isFinite(normalized)) {
297
+ return this.clampSidebarWidth(fallback, minWidth, maxWidth);
298
+ }
299
+ return this.clampSidebarWidth(normalized, minWidth, maxWidth);
300
+ }
301
+ clampSidebarWidth(value, minWidth, maxWidth) {
302
+ return Math.min(maxWidth, Math.max(minWidth, Math.round(value)));
303
+ }
304
+ readPersistedState(storage, storageKey) {
305
+ try {
306
+ const raw = storage.getItem(storageKey);
307
+ if (!raw) {
308
+ return null;
309
+ }
310
+ const parsed = JSON.parse(raw);
311
+ return parsed && typeof parsed === 'object' ? parsed : null;
312
+ }
313
+ catch {
314
+ return null;
315
+ }
316
+ }
317
+ writePersistedState(storage, storageKey, state) {
318
+ try {
319
+ storage.setItem(storageKey, JSON.stringify(state));
320
+ }
321
+ catch {
322
+ // Ignore storage write failures.
323
+ }
324
+ }
145
325
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
146
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientPage, isStandalone: true, selector: "mt-client-page", inputs: { menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null }, menuTitle: { classPropertyName: "menuTitle", publicName: "menuTitle", isSignal: true, isRequired: false, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, menuItemsLoading: { classPropertyName: "menuItemsLoading", publicName: "menuItemsLoading", isSignal: true, isRequired: false, transformFunction: null }, activeItem: { classPropertyName: "activeItem", publicName: "activeItem", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", menuItemClick: "menuItemClick" }, host: { classAttribute: "block h-full" }, queries: [{ propertyName: "headerEnd", first: true, predicate: ["headerClientPageEnd"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div class=\"flex h-full min-h-0\" [class.flex-col]=\"isResponsive()\">\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden transition-all duration-300 ease-in-out\"\r\n [style.width]=\"sidebarWidth()\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-lg shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative w-px flex-shrink-0\"\r\n (mouseenter)=\"dividerHovered.set(true)\"\r\n (mouseleave)=\"dividerHovered.set(false)\"\r\n >\r\n <!-- Toggle Button at bottom of divider -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 flex h-6 w-6 -translate-x-1/2 cursor-pointer items-center justify-center rounded-full border border-gray-300 bg-white shadow-sm transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 hover:shadow-md active:scale-95 active:bg-gray-100 group\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"text-xs text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div class=\"flex min-h-0 min-w-0 flex-1 flex-col\">\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n", dependencies: [{ kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: ClientPageMenu, selector: "mt-client-page-menu", inputs: ["menuItems", "loading", "selectedItem"], outputs: ["menuItemClick"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
326
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ClientPage, isStandalone: true, selector: "mt-client-page", inputs: { menuIcon: { classPropertyName: "menuIcon", publicName: "menuIcon", isSignal: true, isRequired: false, transformFunction: null }, menuTitle: { classPropertyName: "menuTitle", publicName: "menuTitle", isSignal: true, isRequired: false, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, menuItemsLoading: { classPropertyName: "menuItemsLoading", publicName: "menuItemsLoading", isSignal: true, isRequired: false, transformFunction: null }, activeItem: { classPropertyName: "activeItem", publicName: "activeItem", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, storageMode: { classPropertyName: "storageMode", publicName: "storageMode", isSignal: true, isRequired: false, transformFunction: null }, minSidebarWidth: { classPropertyName: "minSidebarWidth", publicName: "minSidebarWidth", isSignal: true, isRequired: false, transformFunction: null }, maxSidebarWidth: { classPropertyName: "maxSidebarWidth", publicName: "maxSidebarWidth", isSignal: true, isRequired: false, transformFunction: null }, defaultSidebarWidth: { classPropertyName: "defaultSidebarWidth", publicName: "defaultSidebarWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", menuItemClick: "menuItemClick" }, host: { classAttribute: "block h-full" }, queries: [{ propertyName: "headerEnd", first: true, predicate: ["headerClientPageEnd"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "layoutRoot", first: true, predicate: ["layoutRoot"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div\r\n #layoutRoot\r\n class=\"flex h-full min-h-0\"\r\n [class.flex-col]=\"isResponsive()\"\r\n >\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden rounded-lg transition-all duration-300 ease-in-out\"\r\n [class.transition-none]=\"isResizing()\"\r\n [style.width]=\"sidebarWidth()\"\r\n [style.background]=\"\r\n collapsed() && !isResponsive() ? null : sidebarGradient()\r\n \"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-xl shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative flex w-0 shrink-0 items-stretch\"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-surface-300 transition-opacity duration-200 ease-in-out\"\r\n [class.opacity-0]=\"!sidebarHovered() && !isResizing()\"\r\n [class.opacity-100]=\"sidebarHovered() || isResizing()\"\r\n ></div>\r\n\r\n @if (resizable()) {\r\n <div\r\n class=\"absolute inset-y-0 left-1/2 z-0 w-5 -translate-x-1/2\"\r\n [class.cursor-col-resize]=\"canResize()\"\r\n [class.cursor-default]=\"!canResize()\"\r\n (mousedown)=\"startResize($event)\"\r\n ></div>\r\n }\r\n\r\n <!-- Toggle Button at bottom of divider (visible on sidebar hover or when collapsed) -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 grid h-7 w-7 -translate-x-1/2 cursor-pointer place-items-center rounded-full border border-gray-300 bg-white p-0 transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 active:scale-95 active:bg-gray-100 group\"\r\n [class.opacity-0]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.pointer-events-none]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.opacity-100]=\"\r\n sidebarHovered() || collapsed() || isResizing()\r\n \"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"block text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n [style.--mt-icon-prev-width]=\"'0.875rem'\"\r\n [style.--mt-icon-prev-height]=\"'0.875rem'\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div\r\n class=\"relative flex min-h-0 min-w-0 flex-1 flex-col\"\r\n [class.ltr:[box-shadow:inset_8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n [class.rtl:[box-shadow:inset_-8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n >\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4 [box-shadow:0_2px_3px_-1px_rgba(0,0,0,0.07)]\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n", dependencies: [{ kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: ClientPageMenu, selector: "mt-client-page-menu", inputs: ["menuItems", "loading", "selectedItem"], outputs: ["menuItemClick"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
147
327
  }
148
328
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ClientPage, decorators: [{
149
329
  type: Component,
150
330
  args: [{ selector: 'mt-client-page', standalone: true, imports: [Avatar, Card, ClientPageMenu, Icon, NgTemplateOutlet], host: {
151
331
  class: 'block h-full',
152
- }, template: "<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div class=\"flex h-full min-h-0\" [class.flex-col]=\"isResponsive()\">\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden transition-all duration-300 ease-in-out\"\r\n [style.width]=\"sidebarWidth()\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-lg shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative w-px flex-shrink-0\"\r\n (mouseenter)=\"dividerHovered.set(true)\"\r\n (mouseleave)=\"dividerHovered.set(false)\"\r\n >\r\n <!-- Toggle Button at bottom of divider -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 flex h-6 w-6 -translate-x-1/2 cursor-pointer items-center justify-center rounded-full border border-gray-300 bg-white shadow-sm transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 hover:shadow-md active:scale-95 active:bg-gray-100 group\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"text-xs text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div class=\"flex min-h-0 min-w-0 flex-1 flex-col\">\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n" }]
153
- }], ctorParameters: () => [], propDecorators: { menuIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuIcon", required: false }] }], menuTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuTitle", required: false }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], menuItemsLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItemsLoading", required: false }] }], activeItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeItem", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], headerEnd: [{ type: i0.ContentChild, args: ['headerClientPageEnd', { isSignal: true }] }], menuItemClick: [{ type: i0.Output, args: ["menuItemClick"] }] } });
332
+ }, template: "<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div\r\n #layoutRoot\r\n class=\"flex h-full min-h-0\"\r\n [class.flex-col]=\"isResponsive()\"\r\n >\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden rounded-lg transition-all duration-300 ease-in-out\"\r\n [class.transition-none]=\"isResizing()\"\r\n [style.width]=\"sidebarWidth()\"\r\n [style.background]=\"\r\n collapsed() && !isResponsive() ? null : sidebarGradient()\r\n \"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-xl shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative flex w-0 shrink-0 items-stretch\"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-surface-300 transition-opacity duration-200 ease-in-out\"\r\n [class.opacity-0]=\"!sidebarHovered() && !isResizing()\"\r\n [class.opacity-100]=\"sidebarHovered() || isResizing()\"\r\n ></div>\r\n\r\n @if (resizable()) {\r\n <div\r\n class=\"absolute inset-y-0 left-1/2 z-0 w-5 -translate-x-1/2\"\r\n [class.cursor-col-resize]=\"canResize()\"\r\n [class.cursor-default]=\"!canResize()\"\r\n (mousedown)=\"startResize($event)\"\r\n ></div>\r\n }\r\n\r\n <!-- Toggle Button at bottom of divider (visible on sidebar hover or when collapsed) -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 grid h-7 w-7 -translate-x-1/2 cursor-pointer place-items-center rounded-full border border-gray-300 bg-white p-0 transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 active:scale-95 active:bg-gray-100 group\"\r\n [class.opacity-0]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.pointer-events-none]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.opacity-100]=\"\r\n sidebarHovered() || collapsed() || isResizing()\r\n \"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"block text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n [style.--mt-icon-prev-width]=\"'0.875rem'\"\r\n [style.--mt-icon-prev-height]=\"'0.875rem'\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div\r\n class=\"relative flex min-h-0 min-w-0 flex-1 flex-col\"\r\n [class.ltr:[box-shadow:inset_8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n [class.rtl:[box-shadow:inset_-8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n >\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4 [box-shadow:0_2px_3px_-1px_rgba(0,0,0,0.07)]\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n" }]
333
+ }], ctorParameters: () => [], propDecorators: { menuIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuIcon", required: false }] }], menuTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuTitle", required: false }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], menuItemsLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItemsLoading", required: false }] }], activeItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeItem", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], storageKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "storageKey", required: false }] }], storageMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "storageMode", required: false }] }], minSidebarWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSidebarWidth", required: false }] }], maxSidebarWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSidebarWidth", required: false }] }], defaultSidebarWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultSidebarWidth", required: false }] }], headerEnd: [{ type: i0.ContentChild, args: ['headerClientPageEnd', { isSignal: true }] }], menuItemClick: [{ type: i0.Output, args: ["menuItemClick"] }], layoutRoot: [{ type: i0.ViewChild, args: ['layoutRoot', { isSignal: true }] }] } });
154
334
 
155
335
  /**
156
336
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"masterteam-components-client-page.mjs","sources":["../../../../packages/masterteam/components/client-page/client-page.ts","../../../../packages/masterteam/components/client-page/client-page.html","../../../../packages/masterteam/components/client-page/masterteam-components-client-page.ts"],"sourcesContent":["import {\r\n Component,\r\n contentChild,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n linkedSignal,\r\n model,\r\n OnDestroy,\r\n output,\r\n PLATFORM_ID,\r\n signal,\r\n TemplateRef,\r\n} from '@angular/core';\r\nimport { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';\r\nimport { Avatar } from '@masterteam/components/avatar';\r\nimport { Card } from '@masterteam/components/card';\r\nimport {\r\n ClientPageMenu,\r\n ClientPageMenuItem,\r\n} from '@masterteam/components/client-page-menu';\r\nimport { Icon } from '@masterteam/icons';\r\n\r\n@Component({\r\n selector: 'mt-client-page',\r\n standalone: true,\r\n imports: [Avatar, Card, ClientPageMenu, Icon, NgTemplateOutlet],\r\n templateUrl: './client-page.html',\r\n host: {\r\n class: 'block h-full',\r\n },\r\n})\r\nexport class ClientPage implements OnDestroy {\r\n private readonly platformId = inject(PLATFORM_ID);\r\n private mediaQueryList: MediaQueryList | null = null;\r\n\r\n /** Icon for the left sidebar header */\r\n readonly menuIcon = input<string>();\r\n\r\n /** Title for the left sidebar header */\r\n readonly menuTitle = input<string>();\r\n\r\n /** List of menu items for the left sidebar */\r\n readonly menuItems = input<ClientPageMenuItem[]>([]);\r\n\r\n /** Whether menu items are loading */\r\n readonly menuItemsLoading = input<boolean>(false);\r\n\r\n /** Key of the active menu item (defaults to first item) */\r\n readonly activeItem = input<string>();\r\n\r\n /** Whether the sidebar menu is collapsed (two-way bindable) */\r\n readonly collapsed = model<boolean>(false);\r\n\r\n /** Template projected into the left sidebar header end */\r\n readonly headerEnd = contentChild<TemplateRef<any>>('headerClientPageEnd');\r\n\r\n /** Emitted when a menu item is clicked */\r\n readonly menuItemClick = output<ClientPageMenuItem>();\r\n\r\n /** Tracks activeItem input, overridable by user clicks */\r\n private readonly selectedKey = linkedSignal(() => this.activeItem());\r\n\r\n /** Currently selected menu item */\r\n readonly selectedItem = computed(() => {\r\n const key = this.selectedKey();\r\n const items = this.menuItems();\r\n if (key) {\r\n return items.find((item) => item.key === key) ?? items[0] ?? null;\r\n }\r\n return items[0] ?? null;\r\n });\r\n\r\n /** Whether the initial default selection has been emitted */\r\n private initialEmitted = false;\r\n\r\n constructor() {\r\n effect(() => {\r\n const item = this.selectedItem();\r\n if (item && !this.initialEmitted) {\r\n this.initialEmitted = true;\r\n this.menuItemClick.emit(item);\r\n }\r\n });\r\n\r\n this.setupResponsiveBehavior();\r\n }\r\n\r\n /** Whether the divider/toggle area is being hovered */\r\n readonly dividerHovered = signal(false);\r\n\r\n /** Whether the viewport is at or below the responsive breakpoint */\r\n readonly isResponsive = signal(false);\r\n\r\n /** Whether the responsive top navigation is expanded */\r\n readonly responsiveMenuExpanded = signal(false);\r\n\r\n /** Whether the menu section is currently visible */\r\n readonly showSidebarMenu = computed(() =>\r\n this.isResponsive() ? this.responsiveMenuExpanded() : !this.collapsed(),\r\n );\r\n\r\n /** Sidebar width reacts to collapsed + hover */\r\n readonly sidebarWidth = computed(() => {\r\n if (this.isResponsive()) {\r\n return '100%';\r\n }\r\n if (this.collapsed()) {\r\n return this.dividerHovered() ? '12px' : '9px';\r\n }\r\n return this.dividerHovered() ? '15.8%' : '16%';\r\n });\r\n\r\n /** Icon for the collapse toggle button */\r\n readonly toggleIcon = computed(() =>\r\n this.isResponsive()\r\n ? this.responsiveMenuExpanded()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down'\r\n : this.collapsed()\r\n ? 'arrow.chevron-right'\r\n : 'arrow.chevron-left',\r\n );\r\n\r\n /** Accessible label for the toggle button */\r\n readonly toggleAriaLabel = computed(() =>\r\n this.isResponsive()\r\n ? this.responsiveMenuExpanded()\r\n ? 'Collapse menu'\r\n : 'Expand menu'\r\n : this.collapsed()\r\n ? 'Expand sidebar'\r\n : 'Collapse sidebar',\r\n );\r\n\r\n toggleCollapsed(): void {\r\n if (this.isResponsive()) {\r\n this.responsiveMenuExpanded.update((expanded) => !expanded);\r\n return;\r\n }\r\n this.collapsed.update((v) => !v);\r\n }\r\n\r\n selectItem(item: ClientPageMenuItem): void {\r\n this.selectedKey.set(item.key);\r\n if (this.isResponsive()) {\r\n this.responsiveMenuExpanded.set(false);\r\n }\r\n this.menuItemClick.emit(item);\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (!this.mediaQueryList) {\r\n return;\r\n }\r\n\r\n if (typeof this.mediaQueryList.removeEventListener === 'function') {\r\n this.mediaQueryList.removeEventListener(\r\n 'change',\r\n this.handleResponsiveBreakpointChange,\r\n );\r\n return;\r\n }\r\n\r\n this.mediaQueryList.removeListener(this.handleResponsiveBreakpointChange);\r\n }\r\n\r\n getAvatarStyle(color: string): Record<string, string> {\r\n if (color.startsWith('#')) {\r\n return {\r\n '--p-avatar-background': color + '1a',\r\n '--p-avatar-color': color,\r\n };\r\n }\r\n return {\r\n '--p-avatar-background': `var(--p-${color}-100)`,\r\n '--p-avatar-color': `var(--p-${color}-700)`,\r\n };\r\n }\r\n\r\n private readonly handleResponsiveBreakpointChange = (\r\n event: MediaQueryListEvent,\r\n ): void => {\r\n this.syncResponsiveState(event.matches);\r\n };\r\n\r\n private setupResponsiveBehavior(): void {\r\n if (\r\n !isPlatformBrowser(this.platformId) ||\r\n typeof window.matchMedia !== 'function'\r\n ) {\r\n return;\r\n }\r\n\r\n this.mediaQueryList = window.matchMedia('(max-width: 1024px)');\r\n this.syncResponsiveState(this.mediaQueryList.matches);\r\n\r\n if (typeof this.mediaQueryList.addEventListener === 'function') {\r\n this.mediaQueryList.addEventListener(\r\n 'change',\r\n this.handleResponsiveBreakpointChange,\r\n );\r\n return;\r\n }\r\n\r\n this.mediaQueryList.addListener(this.handleResponsiveBreakpointChange);\r\n }\r\n\r\n private syncResponsiveState(isResponsive: boolean): void {\r\n const wasResponsive = this.isResponsive();\r\n this.isResponsive.set(isResponsive);\r\n this.dividerHovered.set(false);\r\n\r\n if (wasResponsive !== isResponsive) {\r\n this.responsiveMenuExpanded.set(false);\r\n }\r\n }\r\n}\r\n","<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div class=\"flex h-full min-h-0\" [class.flex-col]=\"isResponsive()\">\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden transition-all duration-300 ease-in-out\"\r\n [style.width]=\"sidebarWidth()\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-lg shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative w-px flex-shrink-0\"\r\n (mouseenter)=\"dividerHovered.set(true)\"\r\n (mouseleave)=\"dividerHovered.set(false)\"\r\n >\r\n <!-- Toggle Button at bottom of divider -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 flex h-6 w-6 -translate-x-1/2 cursor-pointer items-center justify-center rounded-full border border-gray-300 bg-white shadow-sm transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 hover:shadow-md active:scale-95 active:bg-gray-100 group\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"text-xs text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div class=\"flex min-h-0 min-w-0 flex-1 flex-col\">\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"masterteam-components-client-page.mjs","sources":["../../../../packages/masterteam/components/client-page/client-page.ts","../../../../packages/masterteam/components/client-page/client-page.html","../../../../packages/masterteam/components/client-page/masterteam-components-client-page.ts"],"sourcesContent":["import {\r\n booleanAttribute,\r\n Component,\r\n contentChild,\r\n computed,\r\n ElementRef,\r\n effect,\r\n inject,\r\n input,\r\n linkedSignal,\r\n model,\r\n NgZone,\r\n OnDestroy,\r\n output,\r\n PLATFORM_ID,\r\n signal,\r\n TemplateRef,\r\n viewChild,\r\n} from '@angular/core';\r\nimport { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';\r\nimport { Directionality } from '@angular/cdk/bidi';\r\nimport { Avatar } from '@masterteam/components/avatar';\r\nimport { Card } from '@masterteam/components/card';\r\nimport {\r\n ClientPageMenu,\r\n ClientPageMenuItem,\r\n} from '@masterteam/components/client-page-menu';\r\nimport { Icon } from '@masterteam/icons';\r\n\r\ninterface PersistedClientPageState {\r\n sidebarWidth?: number;\r\n}\r\n\r\nconst DEFAULT_SIDEBAR_WIDTH = 280;\r\nconst DEFAULT_MIN_SIDEBAR_WIDTH = 220;\r\nconst DEFAULT_MAX_SIDEBAR_WIDTH = 420;\r\n\r\n@Component({\r\n selector: 'mt-client-page',\r\n standalone: true,\r\n imports: [Avatar, Card, ClientPageMenu, Icon, NgTemplateOutlet],\r\n templateUrl: './client-page.html',\r\n host: {\r\n class: 'block h-full',\r\n },\r\n})\r\nexport class ClientPage implements OnDestroy {\r\n private readonly platformId = inject(PLATFORM_ID);\r\n private readonly document = inject(DOCUMENT);\r\n private readonly dir = inject(Directionality, { optional: true });\r\n private readonly zone = inject(NgZone);\r\n\r\n private mediaQueryList: MediaQueryList | null = null;\r\n private resizeMoveListener: ((event: MouseEvent) => void) | null = null;\r\n private resizeUpListener: ((event: MouseEvent) => void) | null = null;\r\n\r\n /** Whether the current direction is RTL */\r\n readonly isRtl = computed(() => this.dir?.value === 'rtl');\r\n\r\n /** Icon for the left sidebar header */\r\n readonly menuIcon = input<string>();\r\n\r\n /** Title for the left sidebar header */\r\n readonly menuTitle = input<string>();\r\n\r\n /** List of menu items for the left sidebar */\r\n readonly menuItems = input<ClientPageMenuItem[]>([]);\r\n\r\n /** Whether menu items are loading */\r\n readonly menuItemsLoading = input<boolean>(false);\r\n\r\n /** Key of the active menu item (defaults to first item) */\r\n readonly activeItem = input<string>();\r\n\r\n /** Whether the sidebar menu is collapsed (two-way bindable) */\r\n readonly collapsed = model<boolean>(false);\r\n\r\n /** Enables desktop sidebar resizing from the divider */\r\n readonly resizable = input<boolean, unknown>(true, {\r\n transform: booleanAttribute,\r\n });\r\n\r\n /** Unique storage seed used to persist the sidebar width */\r\n readonly storageKey = input<string | null>(null);\r\n\r\n /** Storage location used for persisted sidebar width */\r\n readonly storageMode = input<'local' | 'session'>('local');\r\n\r\n /** Minimum desktop sidebar width in pixels */\r\n readonly minSidebarWidth = input<number>(DEFAULT_MIN_SIDEBAR_WIDTH);\r\n\r\n /** Maximum desktop sidebar width in pixels */\r\n readonly maxSidebarWidth = input<number>(DEFAULT_MAX_SIDEBAR_WIDTH);\r\n\r\n /** Optional default desktop sidebar width in pixels */\r\n readonly defaultSidebarWidth = input<number | null>(null);\r\n\r\n /** Template projected into the left sidebar header end */\r\n readonly headerEnd = contentChild<TemplateRef<any>>('headerClientPageEnd');\r\n\r\n /** Emitted when a menu item is clicked */\r\n readonly menuItemClick = output<ClientPageMenuItem>();\r\n\r\n private readonly layoutRoot =\r\n viewChild<ElementRef<HTMLElement>>('layoutRoot');\r\n\r\n /** Tracks activeItem input, overridable by user clicks */\r\n private readonly selectedKey = linkedSignal(() => this.activeItem());\r\n\r\n /** Currently selected menu item */\r\n readonly selectedItem = computed(() => {\r\n const key = this.selectedKey();\r\n const items = this.menuItems();\r\n if (key) {\r\n return items.find((item) => item.key === key) ?? items[0] ?? null;\r\n }\r\n return items[0] ?? null;\r\n });\r\n\r\n /** Whether the initial default selection has been emitted */\r\n private initialEmitted = false;\r\n\r\n /** Whether the sidebar area is being hovered */\r\n readonly sidebarHovered = signal(false);\r\n\r\n /** Whether the desktop divider is currently resizing */\r\n readonly isResizing = signal(false);\r\n\r\n /** Whether the viewport is at or below the responsive breakpoint */\r\n readonly isResponsive = signal(false);\r\n\r\n /** Whether the responsive top navigation is expanded */\r\n readonly responsiveMenuExpanded = signal(false);\r\n\r\n /** Current desktop sidebar width in pixels */\r\n private readonly desktopSidebarWidth = signal(DEFAULT_SIDEBAR_WIDTH);\r\n\r\n /** Whether the menu section is currently visible */\r\n readonly showSidebarMenu = computed(() =>\r\n this.isResponsive() ? this.responsiveMenuExpanded() : !this.collapsed(),\r\n );\r\n\r\n /** Sidebar gradient respects RTL direction */\r\n readonly sidebarGradient = computed(() => {\r\n const dir = this.isRtl() ? 'to left' : 'to right';\r\n return `linear-gradient(${dir}, #EBEBF0, #e5e5ea)`;\r\n });\r\n\r\n /** Sidebar width reacts to collapsed + hover */\r\n readonly sidebarWidth = computed(() => {\r\n if (this.isResponsive()) {\r\n return '100%';\r\n }\r\n if (this.collapsed()) {\r\n return this.sidebarHovered() ? '12px' : '9px';\r\n }\r\n return `${this.desktopSidebarWidth()}px`;\r\n });\r\n\r\n /** Whether the current state allows divider dragging */\r\n readonly canResize = computed(\r\n () => this.resizable() && !this.isResponsive() && !this.collapsed(),\r\n );\r\n\r\n /** Icon for the collapse toggle button */\r\n readonly toggleIcon = computed(() => {\r\n if (this.isResponsive()) {\r\n return this.responsiveMenuExpanded()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down';\r\n }\r\n const rtl = this.isRtl();\r\n if (this.collapsed()) {\r\n return rtl ? 'arrow.chevron-left' : 'arrow.chevron-right';\r\n }\r\n return rtl ? 'arrow.chevron-right' : 'arrow.chevron-left';\r\n });\r\n\r\n /** Accessible label for the toggle button */\r\n readonly toggleAriaLabel = computed(() =>\r\n this.isResponsive()\r\n ? this.responsiveMenuExpanded()\r\n ? 'Collapse menu'\r\n : 'Expand menu'\r\n : this.collapsed()\r\n ? 'Expand sidebar'\r\n : 'Collapse sidebar',\r\n );\r\n\r\n constructor() {\r\n effect(() => {\r\n const item = this.selectedItem();\r\n if (item && !this.initialEmitted) {\r\n this.initialEmitted = true;\r\n this.menuItemClick.emit(item);\r\n }\r\n });\r\n\r\n effect(() => {\r\n const minWidth = this.getNormalizedMinSidebarWidth();\r\n const maxWidth = this.getNormalizedMaxSidebarWidth(minWidth);\r\n const storage = this.getStorage();\r\n const storageKey = this.getNormalizedStorageKey();\r\n const persistedState =\r\n storage && storageKey\r\n ? this.readPersistedState(storage, storageKey)\r\n : null;\r\n\r\n this.desktopSidebarWidth.set(\r\n this.normalizeSidebarWidth(\r\n persistedState?.sidebarWidth,\r\n this.getDefaultDesktopSidebarWidth(minWidth, maxWidth),\r\n minWidth,\r\n maxWidth,\r\n ),\r\n );\r\n });\r\n\r\n this.setupResponsiveBehavior();\r\n }\r\n\r\n toggleCollapsed(): void {\r\n if (this.isResponsive()) {\r\n this.responsiveMenuExpanded.update((expanded) => !expanded);\r\n return;\r\n }\r\n this.collapsed.update((value) => !value);\r\n }\r\n\r\n selectItem(item: ClientPageMenuItem): void {\r\n this.selectedKey.set(item.key);\r\n if (this.isResponsive()) {\r\n this.responsiveMenuExpanded.set(false);\r\n }\r\n this.menuItemClick.emit(item);\r\n }\r\n\r\n startResize(event: MouseEvent): void {\r\n if (!this.canResize()) {\r\n return;\r\n }\r\n\r\n const layoutRoot = this.layoutRoot()?.nativeElement;\r\n if (!layoutRoot) {\r\n return;\r\n }\r\n\r\n event.preventDefault();\r\n event.stopPropagation();\r\n\r\n const layoutRect = layoutRoot.getBoundingClientRect();\r\n const minWidth = this.getNormalizedMinSidebarWidth();\r\n const maxWidth = this.getNormalizedMaxSidebarWidth(minWidth);\r\n\r\n this.isResizing.set(true);\r\n this.sidebarHovered.set(true);\r\n this.desktopSidebarWidth.set(\r\n this.getWidthFromPointer(event.clientX, layoutRect, minWidth, maxWidth),\r\n );\r\n\r\n this.zone.runOutsideAngular(() => {\r\n this.resizeMoveListener = (moveEvent: MouseEvent) => {\r\n const nextWidth = this.getWidthFromPointer(\r\n moveEvent.clientX,\r\n layoutRect,\r\n minWidth,\r\n maxWidth,\r\n );\r\n\r\n if (nextWidth === this.desktopSidebarWidth()) {\r\n return;\r\n }\r\n\r\n this.zone.run(() => this.desktopSidebarWidth.set(nextWidth));\r\n };\r\n\r\n this.resizeUpListener = (_event: MouseEvent) => {\r\n this.zone.run(() => {\r\n this.isResizing.set(false);\r\n this.persistSidebarWidth();\r\n });\r\n this.cleanupResizeListeners();\r\n };\r\n\r\n this.document.addEventListener('mousemove', this.resizeMoveListener);\r\n this.document.addEventListener('mouseup', this.resizeUpListener, {\r\n once: true,\r\n });\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.cleanupResizeListeners();\r\n\r\n if (!this.mediaQueryList) {\r\n return;\r\n }\r\n\r\n if (typeof this.mediaQueryList.removeEventListener === 'function') {\r\n this.mediaQueryList.removeEventListener(\r\n 'change',\r\n this.handleResponsiveBreakpointChange,\r\n );\r\n return;\r\n }\r\n\r\n this.mediaQueryList.removeListener(this.handleResponsiveBreakpointChange);\r\n }\r\n\r\n getAvatarStyle(color: string): Record<string, string> {\r\n if (color.startsWith('#')) {\r\n return {\r\n '--p-avatar-background': color + '1a',\r\n '--p-avatar-color': color,\r\n };\r\n }\r\n return {\r\n '--p-avatar-background': `var(--p-${color}-100)`,\r\n '--p-avatar-color': `var(--p-${color}-700)`,\r\n };\r\n }\r\n\r\n private readonly handleResponsiveBreakpointChange = (\r\n event: MediaQueryListEvent,\r\n ): void => {\r\n this.syncResponsiveState(event.matches);\r\n };\r\n\r\n private setupResponsiveBehavior(): void {\r\n if (\r\n !isPlatformBrowser(this.platformId) ||\r\n typeof window.matchMedia !== 'function'\r\n ) {\r\n return;\r\n }\r\n\r\n this.mediaQueryList = window.matchMedia('(max-width: 1024px)');\r\n this.syncResponsiveState(this.mediaQueryList.matches);\r\n\r\n if (typeof this.mediaQueryList.addEventListener === 'function') {\r\n this.mediaQueryList.addEventListener(\r\n 'change',\r\n this.handleResponsiveBreakpointChange,\r\n );\r\n return;\r\n }\r\n\r\n this.mediaQueryList.addListener(this.handleResponsiveBreakpointChange);\r\n }\r\n\r\n private cleanupResizeListeners(): void {\r\n if (this.resizeMoveListener) {\r\n this.document.removeEventListener('mousemove', this.resizeMoveListener);\r\n this.resizeMoveListener = null;\r\n }\r\n\r\n if (this.resizeUpListener) {\r\n this.document.removeEventListener('mouseup', this.resizeUpListener);\r\n this.resizeUpListener = null;\r\n }\r\n }\r\n\r\n private syncResponsiveState(isResponsive: boolean): void {\r\n const wasResponsive = this.isResponsive();\r\n this.isResponsive.set(isResponsive);\r\n this.sidebarHovered.set(false);\r\n\r\n if (wasResponsive !== isResponsive) {\r\n this.responsiveMenuExpanded.set(false);\r\n }\r\n }\r\n\r\n private getWidthFromPointer(\r\n clientX: number,\r\n layoutRect: DOMRect,\r\n minWidth: number,\r\n maxWidth: number,\r\n ): number {\r\n const rawWidth = this.isRtl()\r\n ? layoutRect.right - clientX\r\n : clientX - layoutRect.left;\r\n\r\n return this.normalizeSidebarWidth(rawWidth, minWidth, minWidth, maxWidth);\r\n }\r\n\r\n private persistSidebarWidth(): void {\r\n const storage = this.getStorage();\r\n const storageKey = this.getNormalizedStorageKey();\r\n\r\n if (!storage || !storageKey) {\r\n return;\r\n }\r\n\r\n this.writePersistedState(storage, storageKey, {\r\n sidebarWidth: this.desktopSidebarWidth(),\r\n });\r\n }\r\n\r\n private getNormalizedStorageKey(): string | null {\r\n const storageKey = this.storageKey()?.trim();\r\n return storageKey ? `mt-client-page:${storageKey}` : null;\r\n }\r\n\r\n private getStorage(): Storage | null {\r\n if (typeof window === 'undefined') {\r\n return null;\r\n }\r\n\r\n return this.storageMode() === 'session'\r\n ? window.sessionStorage\r\n : window.localStorage;\r\n }\r\n\r\n private getDefaultDesktopSidebarWidth(\r\n minWidth: number,\r\n maxWidth: number,\r\n ): number {\r\n const configuredWidth = this.defaultSidebarWidth();\r\n if (configuredWidth !== null && configuredWidth !== undefined) {\r\n return this.normalizeSidebarWidth(\r\n configuredWidth,\r\n DEFAULT_SIDEBAR_WIDTH,\r\n minWidth,\r\n maxWidth,\r\n );\r\n }\r\n\r\n if (typeof window !== 'undefined') {\r\n return this.normalizeSidebarWidth(\r\n Math.round(window.innerWidth * 0.16),\r\n DEFAULT_SIDEBAR_WIDTH,\r\n minWidth,\r\n maxWidth,\r\n );\r\n }\r\n\r\n return this.normalizeSidebarWidth(\r\n DEFAULT_SIDEBAR_WIDTH,\r\n DEFAULT_SIDEBAR_WIDTH,\r\n minWidth,\r\n maxWidth,\r\n );\r\n }\r\n\r\n private getNormalizedMinSidebarWidth(): number {\r\n const minWidth = Number(this.minSidebarWidth());\r\n return Number.isFinite(minWidth) && minWidth > 0\r\n ? Math.trunc(minWidth)\r\n : DEFAULT_MIN_SIDEBAR_WIDTH;\r\n }\r\n\r\n private getNormalizedMaxSidebarWidth(minWidth: number): number {\r\n const maxWidth = Number(this.maxSidebarWidth());\r\n if (!Number.isFinite(maxWidth) || maxWidth < minWidth) {\r\n return Math.max(DEFAULT_MAX_SIDEBAR_WIDTH, minWidth);\r\n }\r\n\r\n return Math.trunc(maxWidth);\r\n }\r\n\r\n private normalizeSidebarWidth(\r\n value: unknown,\r\n fallback: number,\r\n minWidth: number,\r\n maxWidth: number,\r\n ): number {\r\n const normalized = Number(value);\r\n if (!Number.isFinite(normalized)) {\r\n return this.clampSidebarWidth(fallback, minWidth, maxWidth);\r\n }\r\n\r\n return this.clampSidebarWidth(normalized, minWidth, maxWidth);\r\n }\r\n\r\n private clampSidebarWidth(\r\n value: number,\r\n minWidth: number,\r\n maxWidth: number,\r\n ): number {\r\n return Math.min(maxWidth, Math.max(minWidth, Math.round(value)));\r\n }\r\n\r\n private readPersistedState(\r\n storage: Storage,\r\n storageKey: string,\r\n ): PersistedClientPageState | null {\r\n try {\r\n const raw = storage.getItem(storageKey);\r\n if (!raw) {\r\n return null;\r\n }\r\n\r\n const parsed = JSON.parse(raw) as PersistedClientPageState;\r\n return parsed && typeof parsed === 'object' ? parsed : null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n private writePersistedState(\r\n storage: Storage,\r\n storageKey: string,\r\n state: PersistedClientPageState,\r\n ): void {\r\n try {\r\n storage.setItem(storageKey, JSON.stringify(state));\r\n } catch {\r\n // Ignore storage write failures.\r\n }\r\n }\r\n}\r\n","<mt-card class=\"bg-gray-200 w-full h-full\">\r\n <ng-template #headless>\r\n <div\r\n #layoutRoot\r\n class=\"flex h-full min-h-0\"\r\n [class.flex-col]=\"isResponsive()\"\r\n >\r\n <!-- Left Sidebar -->\r\n <div\r\n class=\"flex min-h-0 flex-col overflow-hidden rounded-lg transition-all duration-300 ease-in-out\"\r\n [class.transition-none]=\"isResizing()\"\r\n [style.width]=\"sidebarWidth()\"\r\n [style.background]=\"\r\n collapsed() && !isResponsive() ? null : sidebarGradient()\r\n \"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <!-- Sidebar Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between overflow-hidden border-b border-gray-300 px-5 py-4\"\r\n [class.whitespace-nowrap]=\"!isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-2\">\r\n @if (menuIcon()) {\r\n <div class=\"text-xl shrink-0\">\r\n <mt-icon [icon]=\"menuIcon()!\" />\r\n </div>\r\n }\r\n @if (menuTitle()) {\r\n <div class=\"min-w-0 truncate font-semibold\">\r\n {{ menuTitle() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isResponsive()) {\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"ml-3 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-500 transition-all duration-200 ease-in-out hover:bg-gray-50 hover:text-primary-600 active:scale-95 active:bg-gray-100\"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon [icon]=\"toggleIcon()\" class=\"text-sm\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSidebarMenu()) {\r\n <!-- Menu Items -->\r\n <div\r\n class=\"min-h-0 overflow-hidden\"\r\n [class.flex-1]=\"!isResponsive()\"\r\n [style.maxHeight]=\"isResponsive() ? '22rem' : null\"\r\n >\r\n <mt-client-page-menu\r\n class=\"h-full\"\r\n [menuItems]=\"menuItems()\"\r\n [selectedItem]=\"selectedItem()\"\r\n [loading]=\"menuItemsLoading()\"\r\n (menuItemClick)=\"selectItem($event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Vertical Divider with Toggle Button -->\r\n @if (!isResponsive()) {\r\n <div\r\n class=\"relative flex w-0 shrink-0 items-stretch\"\r\n (mouseenter)=\"sidebarHovered.set(true)\"\r\n (mouseleave)=\"sidebarHovered.set(false)\"\r\n >\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-surface-300 transition-opacity duration-200 ease-in-out\"\r\n [class.opacity-0]=\"!sidebarHovered() && !isResizing()\"\r\n [class.opacity-100]=\"sidebarHovered() || isResizing()\"\r\n ></div>\r\n\r\n @if (resizable()) {\r\n <div\r\n class=\"absolute inset-y-0 left-1/2 z-0 w-5 -translate-x-1/2\"\r\n [class.cursor-col-resize]=\"canResize()\"\r\n [class.cursor-default]=\"!canResize()\"\r\n (mousedown)=\"startResize($event)\"\r\n ></div>\r\n }\r\n\r\n <!-- Toggle Button at bottom of divider (visible on sidebar hover or when collapsed) -->\r\n <button\r\n type=\"button\"\r\n (click)=\"toggleCollapsed()\"\r\n class=\"absolute bottom-6 left-1/2 z-10 grid h-7 w-7 -translate-x-1/2 cursor-pointer place-items-center rounded-full border border-gray-300 bg-white p-0 transition-all duration-200 ease-in-out hover:scale-125 hover:bg-gray-50 active:scale-95 active:bg-gray-100 group\"\r\n [class.opacity-0]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.pointer-events-none]=\"\r\n !sidebarHovered() && !collapsed() && !isResizing()\r\n \"\r\n [class.opacity-100]=\"\r\n sidebarHovered() || collapsed() || isResizing()\r\n \"\r\n [attr.aria-label]=\"toggleAriaLabel()\"\r\n >\r\n <mt-icon\r\n [icon]=\"toggleIcon()\"\r\n class=\"block text-gray-500 transition-all duration-200 group-hover:animate-pulse group-hover:text-primary-600\"\r\n [style.--mt-icon-prev-width]=\"'0.875rem'\"\r\n [style.--mt-icon-prev-height]=\"'0.875rem'\"\r\n />\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Right Content Panel -->\r\n <div\r\n class=\"relative flex min-h-0 min-w-0 flex-1 flex-col\"\r\n [class.ltr:[box-shadow:inset_8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n [class.rtl:[box-shadow:inset_-8px_0_10px_-8px_rgba(0,0,0,0.12)]]=\"\r\n !isResponsive() && !collapsed()\r\n \"\r\n >\r\n <mt-card class=\"h-full min-h-0\">\r\n <ng-template #headless>\r\n <!-- Content Header -->\r\n <div\r\n class=\"flex shrink-0 items-center justify-between border-b border-surface px-5 py-4 [box-shadow:0_2px_3px_-1px_rgba(0,0,0,0.07)]\"\r\n [class.flex-col]=\"isResponsive()\"\r\n [class.items-start]=\"isResponsive()\"\r\n [class.gap-3]=\"isResponsive()\"\r\n >\r\n <div class=\"flex min-w-0 items-center gap-3\">\r\n @if (selectedItem(); as selected) {\r\n <mt-avatar\r\n [style]=\"getAvatarStyle(selected.color)\"\r\n [icon]=\"selected.icon\"\r\n shape=\"square\"\r\n size=\"large\"\r\n />\r\n <div class=\"min-w-0\">\r\n <div class=\"text-md truncate font-semibold\">\r\n {{ selected.title }}\r\n </div>\r\n @if (selected.subtitle) {\r\n <div class=\"truncate text-sm font-semibold text-gray-500\">\r\n {{ selected.subtitle }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (headerEnd(); as template) {\r\n <div\r\n class=\"flex gap-2\"\r\n [class.w-full]=\"isResponsive()\"\r\n [class.flex-wrap]=\"isResponsive()\"\r\n [class.justify-end]=\"isResponsive()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"template\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Content Body -->\r\n <div\r\n class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto p-5\"\r\n >\r\n <ng-content />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</mt-card>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}