@ruc-lib/drawer 2.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,34 +32,38 @@ If you only need the Drawer component:
32
32
  npm install @ruc-lib/drawer
33
33
  ```
34
34
 
35
- ## Usage
35
+ ### 📦 Version Compatibility
36
36
 
37
- ### 1. Import the Module
38
- In your Angular module file (e.g., `app.module.ts`), import the `RuclibDrawerModule`:
37
+ Please ensure you install the correct version of `@ruc-lib/drawer` based on your Angular version.
39
38
 
40
- ```typescript
41
- // For Complete Library
42
- import { RuclibDrawerModule } from '@uxpractice/ruc-lib/drawer';
43
-
44
- // For Individual Package
45
- import { RuclibDrawerModule } from '@ruc-lib/drawer';
46
-
47
- import { AppComponent } from './app.component';
48
- import { NgModule } from '@angular/core';
49
- import { BrowserModule } from '@angular/platform-browser';
50
- import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
51
-
52
- @NgModule({
53
- declarations: [AppComponent],
54
- imports: [
55
- BrowserModule,
56
- BrowserAnimationsModule,
57
- RuclibDrawerModule
58
- ],
59
- providers: [],
60
- bootstrap: [AppComponent]
61
- })
62
- export class AppModule {}
39
+ | Angular Version | Compatible `@ruc-lib/drawer` Version |
40
+ |--------------------|------------------------------------------|
41
+ | 15.x.x | `npm install @ruc-lib/drawer@^3.0.0` |
42
+ | 16.x.x | `npm install @ruc-lib/drawer@^3.0.0` |
43
+
44
+
45
+ ## Usage
46
+
47
+ ### 1. Import the Component
48
+ In your Angular component file (e.g., `app.component.ts`), import the `RuclibDrawerComponent`:
49
+
50
+ ```typescript
51
+ import { Component } from '@angular/core';
52
+
53
+ // For Complete Library
54
+ import { RuclibDrawerComponent } from '@uxpractice/ruc-lib/drawer';
55
+
56
+ // For Individual Package
57
+ import { RuclibDrawerComponent } from '@ruc-lib/drawer';
58
+
59
+ @Component({
60
+ selector: 'app-root',
61
+ imports: [RuclibDrawerComponent],
62
+ templateUrl: './app.component.html',
63
+ })
64
+ export class AppComponent {
65
+ // Component code here
66
+ }
63
67
  ```
64
68
 
65
69
  ### 2. Use the Component
@@ -203,10 +207,26 @@ export class AppComponent {
203
207
  }
204
208
  ```
205
209
 
210
+ > ⚠️ **IMPORTANT: Custom Theme Usage in Angular Material**
211
+
212
+ When implementing **custom themes**, such as:
213
+
214
+ ```scss
215
+ .custom-theme-1 {
216
+ @include angular-material-theme($custom-theme);
217
+ }
218
+
219
+ // You must also include the typography mixin to ensure text styles are applied correctly as shown below:
220
+ .custom-theme-1 {
221
+ @include angular-material-theme($custom-theme);
222
+ @include mat.typography-level($theme-custom-typography-name, body-1);
223
+ }
224
+ ```
225
+
206
226
  # Contribution
207
227
 
208
228
  Contributions are welcome! Feel free to open issues or pull requests for any enhancements or fixes.
209
229
 
210
230
  # Acknowledgements
211
231
 
212
- Thank you for choosing the Drawer Component Library. If you have any feedback or suggestions, please let us know!
232
+ Thank you for choosing the Drawer Component Library. If you have any feedback or suggestions, please let us know!
@@ -0,0 +1,456 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Pipe, EventEmitter, ViewContainerRef, HostListener, ViewChild, Output, Input, Component } from '@angular/core';
3
+ import { trigger, state, transition, style, animate } from '@angular/animations';
4
+ import * as i1$1 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+ import * as i4 from '@angular/material/sidenav';
7
+ import { MatSidenavModule } from '@angular/material/sidenav';
8
+ import * as i2 from '@angular/material/button';
9
+ import { MatButtonModule } from '@angular/material/button';
10
+ import { MatToolbarModule } from '@angular/material/toolbar';
11
+ import * as i3 from '@angular/material/icon';
12
+ import { MatIconModule } from '@angular/material/icon';
13
+ import * as i1 from '@angular/platform-browser';
14
+
15
+ /**
16
+ * @Pipe SafeHtmlPipe
17
+ * @name safeHtml
18
+ * @description A pipe that bypasses Angular's built-in security and sanitizes HTML content,
19
+ * allowing it to be safely rendered in the DOM. Use with caution and only with trusted HTML sources.
20
+ */
21
+ class SafeHtmlPipe {
22
+ /**
23
+ * @param sanitizer - An instance of DomSanitizer used to bypass security.
24
+ */
25
+ constructor(sanitizer) {
26
+ this.sanitizer = sanitizer;
27
+ }
28
+ /**
29
+ * Transforms a string containing HTML into a SafeHtml object that can be bound to [innerHTML].
30
+ * @param value - The HTML string to sanitize.
31
+ * @returns A SafeHtml object, which Angular trusts as safe HTML.
32
+ */
33
+ transform(value) {
34
+ if (value === null || value === undefined) {
35
+ return value;
36
+ }
37
+ return this.sanitizer.bypassSecurityTrustHtml(value);
38
+ }
39
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe }); }
40
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" }); }
41
+ }
42
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, decorators: [{
43
+ type: Pipe,
44
+ args: [{ name: 'safeHtml', standalone: true }]
45
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
46
+
47
+ /* eslint-disable @typescript-eslint/no-inferrable-types */
48
+ class RuclibDrawerComponent {
49
+ constructor(cdr) {
50
+ this.cdr = cdr;
51
+ /**
52
+ * Input data for configuring the drawer's appearance and behavior.
53
+ * @see RuclibDrawerInput interface for detailed properties.
54
+ */
55
+ this.rucInputData = {};
56
+ /**
57
+ * EventEmitter for various drawer events.
58
+ * Emits objects with `type` (e.g., 'openedStart', 'closedStart', 'openedChange', 'drawerToggle')
59
+ * and `opened` (boolean) and `position` (string) properties.
60
+ */
61
+ this.rucEvent = new EventEmitter();
62
+ /** Reference to the dynamically created component. */
63
+ this.dynamicComponentRef = null;
64
+ /**
65
+ * Current animation state of the drawer ('in' or 'out').
66
+ * @public
67
+ */
68
+ this.drawerAnimationState = 'out';
69
+ /**
70
+ * Current active position of the drawer.
71
+ * @public
72
+ * @default 'left'
73
+ */
74
+ this.currentDrawerPosition = 'left'; // Default
75
+ /**
76
+ * Stores the next position if the drawer is currently open and a new position is requested.
77
+ * Used to sequence close and open animations.
78
+ * @private
79
+ */
80
+ this.pendingDrawerPosition = null;
81
+ /**
82
+ * Flag to indicate if an animation is currently in progress to prevent rapid toggling.
83
+ * @public
84
+ */
85
+ this.isAnimating = false;
86
+ /**
87
+ * Effective animation duration for the drawer, derived from input or default.
88
+ * @public
89
+ * @default '300ms'
90
+ */
91
+ this.effectiveAnimationDuration = '300ms';
92
+ /**
93
+ * Mode of the drawer, primarily for determining backdrop behavior with custom animations.
94
+ * @public
95
+ * @default 'side'
96
+ */
97
+ this.matDrawerMode = 'side';
98
+ /**
99
+ * Actual position ('start' or 'end') used by Angular Material's MatDrawer if it were directly used.
100
+ * Retained for logical consistency in determining layout.
101
+ * @public
102
+ * @default 'start'
103
+ */
104
+ this.matActualPosition = 'start';
105
+ /**
106
+ * Flag indicating if the drawer is in a vertical layout (top/bottom).
107
+ * Helps determine if width or height should be 100%.
108
+ * @public
109
+ */
110
+ this.isVerticalLayout = false;
111
+ /**
112
+ * Effective dimension (width or height) of the drawer panel.
113
+ * @public
114
+ * @default '250px'
115
+ */
116
+ this.effectiveDrawerDimension = '250px';
117
+ }
118
+ /**
119
+ * Angular lifecycle hook that is called after data-bound properties of a directive are initialized.
120
+ */
121
+ ngOnInit() {
122
+ this.applyInputs();
123
+ this.drawerAnimationState = 'out';
124
+ this.loadDynamicContent();
125
+ }
126
+ /**
127
+ * Angular lifecycle hook that is called after Angular has fully initialized a component's view.
128
+ */
129
+ ngAfterViewInit() {
130
+ if (this.rucInputData.initialOpenedState && this.drawerAnimationState === 'out') {
131
+ // Defer state update to the next microtask queue.
132
+ // This helps avoid ExpressionChangedAfterItHasBeenCheckedError when initializing the drawer's open state.
133
+ Promise.resolve().then(() => this.setDrawerOpenState(true));
134
+ }
135
+ }
136
+ /**
137
+ * Angular lifecycle hook that is called when any data-bound property of a directive changes.
138
+ * @param changes - Object containing the changed properties.
139
+ */
140
+ ngOnChanges(changes) {
141
+ if (changes['rucInputData']) {
142
+ const previousRucInputData = changes['rucInputData'].previousValue || {};
143
+ const currentRucInputData = changes['rucInputData'].currentValue || {};
144
+ const oldPosition = previousRucInputData.drawerPosition ?? this.currentDrawerPosition;
145
+ const wasOpen = this.drawerAnimationState === 'in';
146
+ this.currentDrawerPosition = currentRucInputData.drawerPosition ?? 'left';
147
+ this.applyInputs();
148
+ const newPosition = this.currentDrawerPosition;
149
+ const shouldBeOpen = this.rucInputData.initialOpenedState ?? false;
150
+ if (wasOpen && oldPosition !== newPosition) {
151
+ this.pendingDrawerPosition = newPosition;
152
+ this.setDrawerOpenState(false);
153
+ }
154
+ else if (wasOpen !== shouldBeOpen) {
155
+ setTimeout(() => {
156
+ this.setDrawerOpenState(shouldBeOpen);
157
+ });
158
+ }
159
+ else {
160
+ this.loadDynamicContent();
161
+ }
162
+ this.cdr.detectChanges();
163
+ }
164
+ }
165
+ /**
166
+ * Applies input data to component properties, calculating dimensions and positions.
167
+ * @private
168
+ */
169
+ applyInputs() {
170
+ this.matDrawerMode = this.rucInputData.mode ?? 'side';
171
+ this.effectiveAnimationDuration = this.rucInputData.animationDuration || '300ms';
172
+ const getDimension = (inputDimension, defaultDimension) => {
173
+ return (inputDimension && inputDimension.trim() !== '') ? inputDimension : defaultDimension;
174
+ };
175
+ switch (this.currentDrawerPosition) {
176
+ case 'right':
177
+ this.matActualPosition = 'end';
178
+ this.isVerticalLayout = false;
179
+ this.effectiveDrawerDimension = getDimension(this.rucInputData.drawerWidth, '250px');
180
+ break;
181
+ case 'top':
182
+ this.matActualPosition = 'start';
183
+ this.isVerticalLayout = true;
184
+ this.effectiveDrawerDimension = getDimension(this.rucInputData.drawerHeight, '200px');
185
+ break;
186
+ case 'bottom':
187
+ this.matActualPosition = 'end';
188
+ this.isVerticalLayout = true;
189
+ this.effectiveDrawerDimension = getDimension(this.rucInputData.drawerHeight, '200px');
190
+ break;
191
+ case 'left':
192
+ default:
193
+ this.matActualPosition = 'start';
194
+ this.isVerticalLayout = false;
195
+ this.effectiveDrawerDimension = getDimension(this.rucInputData.drawerWidth, '250px');
196
+ break;
197
+ }
198
+ }
199
+ /**
200
+ * Toggles the drawer's open/close state or switches to a new position.
201
+ * @param requestedPosition - The desired position to open the drawer from. If not provided, uses `currentDrawerPosition`.
202
+ */
203
+ toggleDrawer(requestedPosition) {
204
+ if (this.isAnimating && !this.pendingDrawerPosition) {
205
+ return;
206
+ }
207
+ const positionToToggle = requestedPosition || this.currentDrawerPosition;
208
+ const isDrawerOpen = this.drawerAnimationState === 'in';
209
+ const isSamePosition = this.currentDrawerPosition === positionToToggle;
210
+ if (isDrawerOpen) {
211
+ if (isSamePosition) {
212
+ this.setDrawerOpenState(false);
213
+ }
214
+ else {
215
+ this.pendingDrawerPosition = positionToToggle;
216
+ this.setDrawerOpenState(false);
217
+ }
218
+ }
219
+ else {
220
+ if (!isSamePosition) {
221
+ this.currentDrawerPosition = positionToToggle;
222
+ this.applyInputs();
223
+ }
224
+ this.setDrawerOpenState(true);
225
+ }
226
+ }
227
+ /**
228
+ * Sets the drawer's open state and triggers the animation.
229
+ * @param open - Boolean indicating whether to open (true) or close (false) the drawer.
230
+ * @private
231
+ */
232
+ setDrawerOpenState(open) {
233
+ if (this.isAnimating && !this.pendingDrawerPosition && open === (this.drawerAnimationState === 'in')) {
234
+ return;
235
+ }
236
+ this.isAnimating = true;
237
+ this.drawerAnimationState = open ? 'in' : 'out';
238
+ this.rucEvent.emit({
239
+ type: open ? 'openedStart' : 'closedStart',
240
+ opened: open,
241
+ position: this.currentDrawerPosition
242
+ });
243
+ this.cdr.detectChanges();
244
+ }
245
+ /**
246
+ * Callback for when a drawer animation finishes.
247
+ * Manages state transitions, especially when switching between open drawers.
248
+ * @param event - The Angular AnimationEvent.
249
+ */
250
+ onAnimationDone(event) {
251
+ if (event.element.classList.contains('dynamic-drawer')) {
252
+ if (event.toState === 'out') {
253
+ this.rucEvent.emit({ type: 'openedChange', opened: false, position: this.currentDrawerPosition });
254
+ this.rucEvent.emit({ type: 'drawerToggle', opened: false, position: this.currentDrawerPosition });
255
+ if (this.pendingDrawerPosition) {
256
+ this.currentDrawerPosition = this.pendingDrawerPosition;
257
+ this.pendingDrawerPosition = null;
258
+ this.applyInputs();
259
+ setTimeout(() => {
260
+ this.setDrawerOpenState(true);
261
+ });
262
+ return;
263
+ }
264
+ }
265
+ else if (event.toState === 'in') {
266
+ this.rucEvent.emit({ type: 'openedChange', opened: true, position: this.currentDrawerPosition });
267
+ this.rucEvent.emit({ type: 'drawerToggle', opened: true, position: this.currentDrawerPosition });
268
+ }
269
+ this.isAnimating = false;
270
+ this.cdr.detectChanges();
271
+ }
272
+ }
273
+ /**
274
+ * Loads the dynamic component into the drawer if specified in rucInputData.
275
+ * Clears existing dynamic component if any.
276
+ * @private
277
+ */
278
+ loadDynamicContent() {
279
+ this.clearDynamicContent();
280
+ const componentType = this.rucInputData.content?.drawerContentComponent;
281
+ if (componentType && this.drawerComponentHost) {
282
+ this.dynamicComponentRef = this.drawerComponentHost.createComponent(componentType);
283
+ if (this.dynamicComponentRef.instance) {
284
+ this.dynamicComponentRef.changeDetectorRef.detectChanges();
285
+ }
286
+ if (this.rucInputData.drawerContentComponentData && this.dynamicComponentRef.instance) {
287
+ Object.keys(this.rucInputData.drawerContentComponentData).forEach(key => {
288
+ if (key in this.dynamicComponentRef.instance) {
289
+ this.dynamicComponentRef.instance[key] = this.rucInputData.drawerContentComponentData[key];
290
+ }
291
+ });
292
+ this.dynamicComponentRef.changeDetectorRef.detectChanges();
293
+ }
294
+ this.cdr.detectChanges();
295
+ }
296
+ }
297
+ /**
298
+ * Getter for the current animation parameters to be passed to the animation triggers in the template.
299
+ * Includes the current animation state ('in' or 'out') and the effective duration.
300
+ */
301
+ get animationParams() {
302
+ return { value: this.drawerAnimationState, params: { duration: this.effectiveAnimationDuration } };
303
+ }
304
+ /**
305
+ * Getter for the backdrop animation parameters.
306
+ * Typically uses a faster duration than the main drawer animation.
307
+ */
308
+ get backdropAnimationParams() {
309
+ let durationMs = 0;
310
+ if (this.effectiveAnimationDuration.endsWith('ms')) {
311
+ durationMs = parseInt(this.effectiveAnimationDuration, 10);
312
+ }
313
+ else if (this.effectiveAnimationDuration.endsWith('s')) {
314
+ durationMs = parseFloat(this.effectiveAnimationDuration) * 1000;
315
+ }
316
+ else {
317
+ durationMs = parseInt(this.effectiveAnimationDuration, 10) || 300; // Fallback if format is unexpected
318
+ }
319
+ const backdropDurationValue = Math.floor(durationMs / 2); // Ensure integer for ms
320
+ const backdropDuration = backdropDurationValue > 0 ? `${backdropDurationValue}ms` : '150ms'; // Fallback if calculated duration is 0 or less
321
+ return { value: this.drawerAnimationState, params: { duration: backdropDuration } };
322
+ }
323
+ /**
324
+ * Generates an accessible label for the toggle button based on the drawer's state and position.
325
+ * @returns The ARIA label string for the toggle button.
326
+ */
327
+ getToggleButtonAriaLabel() {
328
+ const defaultOpen = `Open ${this.currentDrawerPosition} Drawer`;
329
+ const defaultClose = `Close ${this.currentDrawerPosition} Drawer`;
330
+ const openText = this.rucInputData.toggleButtonText?.open || defaultOpen;
331
+ const closeText = this.rucInputData.toggleButtonText?.close || defaultClose;
332
+ return this.drawerAnimationState === 'in' ? closeText : openText;
333
+ }
334
+ /**
335
+ * Handles clicks on the backdrop.
336
+ * Closes the drawer only if `disableClose` is not true.
337
+ * @public
338
+ */
339
+ handleBackdropClick() {
340
+ if (!(this.rucInputData.disableClose ?? false)) {
341
+ this.toggleDrawer(this.currentDrawerPosition);
342
+ }
343
+ }
344
+ /**
345
+ * Listens for Escape key presses on the document.
346
+ * Closes the drawer if it's open and `disableClose` is not true.
347
+ * @param event - The KeyboardEvent.
348
+ */
349
+ onKeydownHandler(event) {
350
+ if (this.drawerAnimationState === 'in' && !(this.rucInputData.disableClose ?? false)) {
351
+ this.toggleDrawer(this.currentDrawerPosition);
352
+ }
353
+ }
354
+ /**
355
+ * Clears any dynamically loaded component.
356
+ * @private
357
+ */
358
+ clearDynamicContent() {
359
+ if (this.drawerComponentHost) {
360
+ this.drawerComponentHost.clear();
361
+ }
362
+ if (this.dynamicComponentRef) {
363
+ this.dynamicComponentRef.destroy();
364
+ this.dynamicComponentRef = null;
365
+ }
366
+ }
367
+ /**
368
+ * Angular lifecycle hook that is called clear dynamic component content.
369
+ */
370
+ ngOnDestroy() {
371
+ this.clearDynamicContent();
372
+ }
373
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibDrawerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
374
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: RuclibDrawerComponent, isStandalone: true, selector: "uxp-ruclib-drawer", inputs: { rucInputData: "rucInputData", customTheme: "customTheme" }, outputs: { rucEvent: "rucEvent" }, host: { listeners: { "document:keydown.escape": "onKeydownHandler($event)" } }, viewQueries: [{ propertyName: "drawerComponentHost", first: true, predicate: ["drawerComponentHost"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<!--\r\nCustom Backdrop element.\r\nVisible only when:\r\n- rucInputData.mode is 'over'\r\n- rucInputData.hasBackdrop is true (or undefined, defaulting to true)\r\n- drawerAnimationState is 'in' (drawer is open or opening)\r\nAnimates using 'backdropFade' trigger.\r\nClicking the backdrop will toggle the drawer for the current position.\r\nBackground color can be customized via rucInputData.backdropBackgroundColor.\r\n-->\r\n@if (rucInputData.mode === 'over' && (rucInputData.hasBackdrop ?? true) && drawerAnimationState === 'in') {\r\n <div class=\"custom-backdrop\"\r\n [@backdropFade]=\"backdropAnimationParams\"\r\n (click)=\"handleBackdropClick()\"\r\n [style.background-color]=\"rucInputData.hasBackdrop ? rucInputData.backdropBackgroundColor : ''\">\r\n </div>\r\n}\r\n\r\n<!--\r\nCustom Dynamic Drawer element.\r\nThis is the main panel that slides in and out.\r\n- Applies CSS classes based on currentDrawerPosition and customTheme.\r\n- Dynamically sets width and height based on drawer orientation and input data.\r\n- Uses one of four animation triggers (slideInOutLeft, slideInOutRight, slideInOutTop, slideInOutBottom)\r\nbased on currentDrawerPosition. Only the active trigger runs its animation; others are set to an\r\ninstant 'out' state to prevent interference.\r\n- Animation completion events are handled by onAnimationDone().\r\n-->\r\n<div class=\"dynamic-drawer\"\r\n [ngClass]=\"'drawer-' + currentDrawerPosition + ' ' + (customTheme || '')\"\r\n [style.width]=\"!isVerticalLayout ? effectiveDrawerDimension : '100%'\"\r\n [style.height]=\"isVerticalLayout ? effectiveDrawerDimension : '100%'\"\r\n\r\n [@slideInOutLeft]=\"currentDrawerPosition === 'left' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutLeft.done)=\"currentDrawerPosition === 'left' && onAnimationDone($event)\"\r\n\r\n [@slideInOutRight]=\"currentDrawerPosition === 'right' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutRight.done)=\"currentDrawerPosition === 'right' && onAnimationDone($event)\"\r\n\r\n [@slideInOutTop]=\"currentDrawerPosition === 'top' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutTop.done)=\"currentDrawerPosition === 'top' && onAnimationDone($event)\"\r\n\r\n [@slideInOutBottom]=\"currentDrawerPosition === 'bottom' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutBottom.done)=\"currentDrawerPosition === 'bottom' && onAnimationDone($event)\">\r\n\r\n <!-- Wrapper for the content inside the drawer, handles padding and layout. -->\r\n <div class=\"drawer-content-wrapper\">\r\n <!-- Optional title for the drawer. -->\r\n @if (rucInputData.content?.drawerTitle) {\r\n <h2 class=\"ruclib-drawer-title\">\r\n {{ rucInputData.content?.drawerTitle }}\r\n </h2>\r\n }\r\n <!--\r\n Optional close button inside the drawer.\r\n Toggles the drawer for the current position when clicked.\r\n ARIA label provides accessibility.\r\n Dimensions can be customized via rucInputData.closeButtonDimensions.\r\n -->\r\n @if (rucInputData.showCloseIcon) {\r\n <button\r\n mat-icon-button class=\"ruclib-drawer-close-button\"\r\n (click)=\"toggleDrawer(currentDrawerPosition)\"\r\n [attr.aria-label]=\"'Close ' + currentDrawerPosition + ' drawer'\"\r\n [style.width]=\"rucInputData.closeButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.closeButtonDimensions?.height\">\r\n <!-- Material icon for the close button. Size can be customized. -->\r\n <mat-icon [style.font-size]=\"rucInputData.closeButtonDimensions?.iconSize\">close</mat-icon>\r\n </button>\r\n }\r\n <!-- Main body content of the drawer. -->\r\n <div class=\"ruclib-drawer-body\">\r\n <!-- Host for dynamically injected component -->\r\n <ng-template #drawerComponentHost></ng-template>\r\n <!-- Fallback to HTML content if no component is provided -->\r\n @if (!rucInputData.content?.drawerContentComponent) {\r\n <div [innerHTML]=\"rucInputData.content?.drawerBody || '' | safeHtml\"></div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!--\r\nAngular Material Drawer Container.\r\nActs as the main container for the drawer system, especially if 'side' or 'push' modes\r\nwere to be implemented with MatDrawer's native behavior. With custom fixed-position animations,\r\nits primary role here is to host the mat-drawer-content.\r\n- Applies 'vertical-layout' class if the drawer is top/bottom.\r\n- MatDrawer's own backdrop is disabled as a custom one is used.\r\n-->\r\n<mat-drawer-container\r\n class=\"ruclib-drawer-container\"\r\n [class.vertical-layout]=\"isVerticalLayout\"\r\n [hasBackdrop]=\"false\"> <!-- MatDrawer's backdrop is not used with custom animation -->\r\n <mat-drawer-content class=\"ruclib-main-content\">\r\n <button [disabled]=\"rucInputData.disableToggleButtonInMainContent\"\r\n mat-raised-button\r\n color=\"primary\"\r\n title=\"Toggle Drawer\"\r\n class=\"drawer-toggle-button\"\r\n [style.width]=\"rucInputData.toggleButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.toggleButtonDimensions?.height\"\r\n [style.padding]=\"rucInputData.toggleButtonDimensions?.padding\"\r\n [style.font-size]=\"rucInputData.toggleButtonDimensions?.fontSize\"\r\n (click)=\"toggleDrawer(currentDrawerPosition)\"\r\n [attr.aria-label]=\"getToggleButtonAriaLabel()\"\r\n [attr.aria-expanded]=\"drawerAnimationState === 'in'\">\r\n <!-- Optional Material icon for the toggle button. Show only if no image URL is provided. -->\r\n @if (rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl) {\r\n <mat-icon [style.font-size]=\"rucInputData.toggleButtonDimensions?.iconSize\">{{ rucInputData.toggleButtonIcon }}</mat-icon>\r\n }\r\n <!-- Optional image for the toggle button. If present, it should fill the button. -->\r\n @if (rucInputData.toggleButtonImageUrl) {\r\n <img\r\n [src]=\"rucInputData.toggleButtonImageUrl\"\r\n [alt]=\"rucInputData.toggleButtonImageAlt || 'Toggle Drawer'\"\r\n class=\"ruclib-drawer-toggle-image\"\r\n [style.width]=\"rucInputData.toggleButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.toggleButtonDimensions?.height\"\r\n [style.padding]=\"rucInputData.toggleButtonDimensions?.padding\">\r\n }\r\n <!-- Text for the toggle button. Show only if NO icon AND NO image URL is provided. -->\r\n @if (!rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl && (rucInputData.toggleButtonText?.open || rucInputData.toggleButtonText?.close)) {\r\n <span>\r\n {{ drawerAnimationState === 'in' ? (rucInputData.toggleButtonText?.close || 'Close') : (rucInputData.toggleButtonText?.open || 'Open') }}\r\n </span>\r\n }\r\n <!-- Text for the toggle button when an icon (but NO image) IS also present. -->\r\n @if (rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl && (rucInputData.toggleButtonText?.open || rucInputData.toggleButtonText?.close)) {\r\n <span class=\"toggle-button-text-with-icon\">\r\n {{ drawerAnimationState === 'in' ? (rucInputData.toggleButtonText?.close || 'Close') : (rucInputData.toggleButtonText?.open || 'Open') }}\r\n </span>\r\n }\r\n </button>\r\n <!-- Main application content area, rendered from HTML string via safeHtml pipe. -->\r\n <div [innerHTML]=\"rucInputData.content?.mainBody || '' | safeHtml\"></div>\r\n </mat-drawer-content>\r\n</mat-drawer-container>\r\n", styles: [":host{display:block;position:relative;overflow:hidden}.custom-backdrop{position:absolute;inset:0;background-color:#000;z-index:999;visibility:hidden}.ruclib-drawer-container{width:100%;height:400px;border:1px solid #ccc;position:relative;overflow:hidden}.drawer-content-wrapper{padding:16px;position:relative;box-sizing:border-box;height:100%;display:flex;flex-direction:column}.ruclib-drawer-title{margin-top:0;margin-bottom:16px;font-size:1.5em;flex-shrink:0}.ruclib-drawer-close-button{position:absolute;top:8px;right:8px;padding:5px 0 0!important;flex-shrink:0}.ruclib-drawer-body{flex-grow:1;overflow-y:auto;word-break:break-word}.dynamic-drawer{background-color:var(--mat-sidenav-content-background-color, white);position:absolute;box-sizing:border-box;box-shadow:0 2px 10px #0003;z-index:1000;overflow:hidden}.dynamic-drawer.drawer-left{top:0;bottom:0;left:0}.dynamic-drawer.drawer-right{top:0;bottom:0;right:0}.dynamic-drawer.drawer-top{top:0;left:0;right:0}.dynamic-drawer.drawer-bottom{bottom:0;left:0;right:0}.dynamic-drawer.light-theme{background-color:#fff;color:#000000de}.dynamic-drawer.light-theme .ruclib-drawer-title{color:#000000de}.dynamic-drawer.light-theme .ruclib-drawer-close-button .mat-icon{color:#0000008a}.dynamic-drawer.dark-theme{background-color:#424242;color:#fff}.dynamic-drawer.dark-theme .ruclib-drawer-title,.dynamic-drawer.dark-theme .ruclib-drawer-close-button .mat-icon{color:#fff}.dynamic-drawer.custom-theme-one{background-color:#fff;color:#000000de}.dynamic-drawer.custom-theme-one .ruclib-drawer-title{color:#000000de}.dynamic-drawer.custom-theme-one .ruclib-drawer-close-button .mat-icon{color:#0000008a}.dynamic-drawer.custom-theme-two{background-color:#424242;color:#fff}.dynamic-drawer.custom-theme-two .ruclib-drawer-title,.dynamic-drawer.custom-theme-two .ruclib-drawer-close-button .mat-icon{color:#fff}.ruclib-drawer-toggle-image{object-fit:contain;display:block}.drawer-toggle-button{margin-bottom:15px;display:inline-flex;align-items:center;justify-content:center;gap:8px}.ruclib-main-content{padding:16px;display:flex;flex-direction:column;align-items:flex-start}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSidenavModule }, { kind: "component", type: i4.MatDrawerContainer, selector: "mat-drawer-container", inputs: ["autosize", "hasBackdrop"], outputs: ["backdropClick"], exportAs: ["matDrawerContainer"] }, { kind: "component", type: i4.MatDrawerContent, selector: "mat-drawer-content" }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], animations: [
375
+ trigger('backdropFade', [
376
+ state('out', style({ opacity: 0, visibility: 'hidden' })),
377
+ state('in', style({ opacity: 0.6, visibility: 'visible' })),
378
+ transition('out => in', animate('{{duration}} ease-in')),
379
+ transition('in => out', animate('{{duration}} ease-out')),
380
+ ]),
381
+ trigger('slideInOutLeft', [
382
+ state('out', style({ transform: 'translateX(-100%)', visibility: 'hidden' })),
383
+ state('in', style({ transform: 'translateX(0)', visibility: 'visible' })),
384
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
385
+ ]),
386
+ trigger('slideInOutRight', [
387
+ state('out', style({ transform: 'translateX(100%)', visibility: 'hidden' })),
388
+ state('in', style({ transform: 'translateX(0)', visibility: 'visible' })),
389
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
390
+ ]),
391
+ trigger('slideInOutTop', [
392
+ state('out', style({ transform: 'translateY(-100%)', visibility: 'hidden' })),
393
+ state('in', style({ transform: 'translateY(0%)', visibility: 'visible' })),
394
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
395
+ ]),
396
+ trigger('slideInOutBottom', [
397
+ state('out', style({ transform: 'translateY(100%)', visibility: 'hidden' })),
398
+ state('in', style({ transform: 'translateY(0%)', visibility: 'visible' })),
399
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
400
+ ])
401
+ ] }); }
402
+ }
403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibDrawerComponent, decorators: [{
404
+ type: Component,
405
+ args: [{ selector: 'uxp-ruclib-drawer', imports: [CommonModule,
406
+ MatButtonModule,
407
+ MatIconModule,
408
+ MatSidenavModule,
409
+ MatToolbarModule, SafeHtmlPipe], animations: [
410
+ trigger('backdropFade', [
411
+ state('out', style({ opacity: 0, visibility: 'hidden' })),
412
+ state('in', style({ opacity: 0.6, visibility: 'visible' })),
413
+ transition('out => in', animate('{{duration}} ease-in')),
414
+ transition('in => out', animate('{{duration}} ease-out')),
415
+ ]),
416
+ trigger('slideInOutLeft', [
417
+ state('out', style({ transform: 'translateX(-100%)', visibility: 'hidden' })),
418
+ state('in', style({ transform: 'translateX(0)', visibility: 'visible' })),
419
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
420
+ ]),
421
+ trigger('slideInOutRight', [
422
+ state('out', style({ transform: 'translateX(100%)', visibility: 'hidden' })),
423
+ state('in', style({ transform: 'translateX(0)', visibility: 'visible' })),
424
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
425
+ ]),
426
+ trigger('slideInOutTop', [
427
+ state('out', style({ transform: 'translateY(-100%)', visibility: 'hidden' })),
428
+ state('in', style({ transform: 'translateY(0%)', visibility: 'visible' })),
429
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
430
+ ]),
431
+ trigger('slideInOutBottom', [
432
+ state('out', style({ transform: 'translateY(100%)', visibility: 'hidden' })),
433
+ state('in', style({ transform: 'translateY(0%)', visibility: 'visible' })),
434
+ transition('out <=> in', animate('{{duration}} ease-in-out')),
435
+ ])
436
+ ], template: "<!--\r\nCustom Backdrop element.\r\nVisible only when:\r\n- rucInputData.mode is 'over'\r\n- rucInputData.hasBackdrop is true (or undefined, defaulting to true)\r\n- drawerAnimationState is 'in' (drawer is open or opening)\r\nAnimates using 'backdropFade' trigger.\r\nClicking the backdrop will toggle the drawer for the current position.\r\nBackground color can be customized via rucInputData.backdropBackgroundColor.\r\n-->\r\n@if (rucInputData.mode === 'over' && (rucInputData.hasBackdrop ?? true) && drawerAnimationState === 'in') {\r\n <div class=\"custom-backdrop\"\r\n [@backdropFade]=\"backdropAnimationParams\"\r\n (click)=\"handleBackdropClick()\"\r\n [style.background-color]=\"rucInputData.hasBackdrop ? rucInputData.backdropBackgroundColor : ''\">\r\n </div>\r\n}\r\n\r\n<!--\r\nCustom Dynamic Drawer element.\r\nThis is the main panel that slides in and out.\r\n- Applies CSS classes based on currentDrawerPosition and customTheme.\r\n- Dynamically sets width and height based on drawer orientation and input data.\r\n- Uses one of four animation triggers (slideInOutLeft, slideInOutRight, slideInOutTop, slideInOutBottom)\r\nbased on currentDrawerPosition. Only the active trigger runs its animation; others are set to an\r\ninstant 'out' state to prevent interference.\r\n- Animation completion events are handled by onAnimationDone().\r\n-->\r\n<div class=\"dynamic-drawer\"\r\n [ngClass]=\"'drawer-' + currentDrawerPosition + ' ' + (customTheme || '')\"\r\n [style.width]=\"!isVerticalLayout ? effectiveDrawerDimension : '100%'\"\r\n [style.height]=\"isVerticalLayout ? effectiveDrawerDimension : '100%'\"\r\n\r\n [@slideInOutLeft]=\"currentDrawerPosition === 'left' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutLeft.done)=\"currentDrawerPosition === 'left' && onAnimationDone($event)\"\r\n\r\n [@slideInOutRight]=\"currentDrawerPosition === 'right' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutRight.done)=\"currentDrawerPosition === 'right' && onAnimationDone($event)\"\r\n\r\n [@slideInOutTop]=\"currentDrawerPosition === 'top' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutTop.done)=\"currentDrawerPosition === 'top' && onAnimationDone($event)\"\r\n\r\n [@slideInOutBottom]=\"currentDrawerPosition === 'bottom' ? animationParams : {value: 'out', params: {duration: '0ms'}}\"\r\n (@slideInOutBottom.done)=\"currentDrawerPosition === 'bottom' && onAnimationDone($event)\">\r\n\r\n <!-- Wrapper for the content inside the drawer, handles padding and layout. -->\r\n <div class=\"drawer-content-wrapper\">\r\n <!-- Optional title for the drawer. -->\r\n @if (rucInputData.content?.drawerTitle) {\r\n <h2 class=\"ruclib-drawer-title\">\r\n {{ rucInputData.content?.drawerTitle }}\r\n </h2>\r\n }\r\n <!--\r\n Optional close button inside the drawer.\r\n Toggles the drawer for the current position when clicked.\r\n ARIA label provides accessibility.\r\n Dimensions can be customized via rucInputData.closeButtonDimensions.\r\n -->\r\n @if (rucInputData.showCloseIcon) {\r\n <button\r\n mat-icon-button class=\"ruclib-drawer-close-button\"\r\n (click)=\"toggleDrawer(currentDrawerPosition)\"\r\n [attr.aria-label]=\"'Close ' + currentDrawerPosition + ' drawer'\"\r\n [style.width]=\"rucInputData.closeButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.closeButtonDimensions?.height\">\r\n <!-- Material icon for the close button. Size can be customized. -->\r\n <mat-icon [style.font-size]=\"rucInputData.closeButtonDimensions?.iconSize\">close</mat-icon>\r\n </button>\r\n }\r\n <!-- Main body content of the drawer. -->\r\n <div class=\"ruclib-drawer-body\">\r\n <!-- Host for dynamically injected component -->\r\n <ng-template #drawerComponentHost></ng-template>\r\n <!-- Fallback to HTML content if no component is provided -->\r\n @if (!rucInputData.content?.drawerContentComponent) {\r\n <div [innerHTML]=\"rucInputData.content?.drawerBody || '' | safeHtml\"></div>\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!--\r\nAngular Material Drawer Container.\r\nActs as the main container for the drawer system, especially if 'side' or 'push' modes\r\nwere to be implemented with MatDrawer's native behavior. With custom fixed-position animations,\r\nits primary role here is to host the mat-drawer-content.\r\n- Applies 'vertical-layout' class if the drawer is top/bottom.\r\n- MatDrawer's own backdrop is disabled as a custom one is used.\r\n-->\r\n<mat-drawer-container\r\n class=\"ruclib-drawer-container\"\r\n [class.vertical-layout]=\"isVerticalLayout\"\r\n [hasBackdrop]=\"false\"> <!-- MatDrawer's backdrop is not used with custom animation -->\r\n <mat-drawer-content class=\"ruclib-main-content\">\r\n <button [disabled]=\"rucInputData.disableToggleButtonInMainContent\"\r\n mat-raised-button\r\n color=\"primary\"\r\n title=\"Toggle Drawer\"\r\n class=\"drawer-toggle-button\"\r\n [style.width]=\"rucInputData.toggleButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.toggleButtonDimensions?.height\"\r\n [style.padding]=\"rucInputData.toggleButtonDimensions?.padding\"\r\n [style.font-size]=\"rucInputData.toggleButtonDimensions?.fontSize\"\r\n (click)=\"toggleDrawer(currentDrawerPosition)\"\r\n [attr.aria-label]=\"getToggleButtonAriaLabel()\"\r\n [attr.aria-expanded]=\"drawerAnimationState === 'in'\">\r\n <!-- Optional Material icon for the toggle button. Show only if no image URL is provided. -->\r\n @if (rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl) {\r\n <mat-icon [style.font-size]=\"rucInputData.toggleButtonDimensions?.iconSize\">{{ rucInputData.toggleButtonIcon }}</mat-icon>\r\n }\r\n <!-- Optional image for the toggle button. If present, it should fill the button. -->\r\n @if (rucInputData.toggleButtonImageUrl) {\r\n <img\r\n [src]=\"rucInputData.toggleButtonImageUrl\"\r\n [alt]=\"rucInputData.toggleButtonImageAlt || 'Toggle Drawer'\"\r\n class=\"ruclib-drawer-toggle-image\"\r\n [style.width]=\"rucInputData.toggleButtonDimensions?.width\"\r\n [style.height]=\"rucInputData.toggleButtonDimensions?.height\"\r\n [style.padding]=\"rucInputData.toggleButtonDimensions?.padding\">\r\n }\r\n <!-- Text for the toggle button. Show only if NO icon AND NO image URL is provided. -->\r\n @if (!rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl && (rucInputData.toggleButtonText?.open || rucInputData.toggleButtonText?.close)) {\r\n <span>\r\n {{ drawerAnimationState === 'in' ? (rucInputData.toggleButtonText?.close || 'Close') : (rucInputData.toggleButtonText?.open || 'Open') }}\r\n </span>\r\n }\r\n <!-- Text for the toggle button when an icon (but NO image) IS also present. -->\r\n @if (rucInputData.toggleButtonIcon && !rucInputData.toggleButtonImageUrl && (rucInputData.toggleButtonText?.open || rucInputData.toggleButtonText?.close)) {\r\n <span class=\"toggle-button-text-with-icon\">\r\n {{ drawerAnimationState === 'in' ? (rucInputData.toggleButtonText?.close || 'Close') : (rucInputData.toggleButtonText?.open || 'Open') }}\r\n </span>\r\n }\r\n </button>\r\n <!-- Main application content area, rendered from HTML string via safeHtml pipe. -->\r\n <div [innerHTML]=\"rucInputData.content?.mainBody || '' | safeHtml\"></div>\r\n </mat-drawer-content>\r\n</mat-drawer-container>\r\n", styles: [":host{display:block;position:relative;overflow:hidden}.custom-backdrop{position:absolute;inset:0;background-color:#000;z-index:999;visibility:hidden}.ruclib-drawer-container{width:100%;height:400px;border:1px solid #ccc;position:relative;overflow:hidden}.drawer-content-wrapper{padding:16px;position:relative;box-sizing:border-box;height:100%;display:flex;flex-direction:column}.ruclib-drawer-title{margin-top:0;margin-bottom:16px;font-size:1.5em;flex-shrink:0}.ruclib-drawer-close-button{position:absolute;top:8px;right:8px;padding:5px 0 0!important;flex-shrink:0}.ruclib-drawer-body{flex-grow:1;overflow-y:auto;word-break:break-word}.dynamic-drawer{background-color:var(--mat-sidenav-content-background-color, white);position:absolute;box-sizing:border-box;box-shadow:0 2px 10px #0003;z-index:1000;overflow:hidden}.dynamic-drawer.drawer-left{top:0;bottom:0;left:0}.dynamic-drawer.drawer-right{top:0;bottom:0;right:0}.dynamic-drawer.drawer-top{top:0;left:0;right:0}.dynamic-drawer.drawer-bottom{bottom:0;left:0;right:0}.dynamic-drawer.light-theme{background-color:#fff;color:#000000de}.dynamic-drawer.light-theme .ruclib-drawer-title{color:#000000de}.dynamic-drawer.light-theme .ruclib-drawer-close-button .mat-icon{color:#0000008a}.dynamic-drawer.dark-theme{background-color:#424242;color:#fff}.dynamic-drawer.dark-theme .ruclib-drawer-title,.dynamic-drawer.dark-theme .ruclib-drawer-close-button .mat-icon{color:#fff}.dynamic-drawer.custom-theme-one{background-color:#fff;color:#000000de}.dynamic-drawer.custom-theme-one .ruclib-drawer-title{color:#000000de}.dynamic-drawer.custom-theme-one .ruclib-drawer-close-button .mat-icon{color:#0000008a}.dynamic-drawer.custom-theme-two{background-color:#424242;color:#fff}.dynamic-drawer.custom-theme-two .ruclib-drawer-title,.dynamic-drawer.custom-theme-two .ruclib-drawer-close-button .mat-icon{color:#fff}.ruclib-drawer-toggle-image{object-fit:contain;display:block}.drawer-toggle-button{margin-bottom:15px;display:inline-flex;align-items:center;justify-content:center;gap:8px}.ruclib-main-content{padding:16px;display:flex;flex-direction:column;align-items:flex-start}\n"] }]
437
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { rucInputData: [{
438
+ type: Input
439
+ }], customTheme: [{
440
+ type: Input
441
+ }], rucEvent: [{
442
+ type: Output
443
+ }], drawerComponentHost: [{
444
+ type: ViewChild,
445
+ args: ['drawerComponentHost', { read: ViewContainerRef, static: true }]
446
+ }], onKeydownHandler: [{
447
+ type: HostListener,
448
+ args: ['document:keydown.escape', ['$event']]
449
+ }] } });
450
+
451
+ /**
452
+ * Generated bundle index. Do not edit.
453
+ */
454
+
455
+ export { RuclibDrawerComponent };
456
+ //# sourceMappingURL=ruc-lib-drawer.mjs.map