@nanahoshi/mona-ui 0.18.5 → 0.19.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 +71 -13
- package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +11 -3
- package/fesm2022/nanahoshi-mona-ui-breadcrumb.mjs +17 -9
- package/fesm2022/nanahoshi-mona-ui-button-group.mjs +16 -7
- package/fesm2022/nanahoshi-mona-ui-calendar.mjs +122 -48
- package/fesm2022/nanahoshi-mona-ui-card.mjs +22 -14
- package/fesm2022/nanahoshi-mona-ui-chart.mjs +78 -18
- package/fesm2022/nanahoshi-mona-ui-check-box.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-chip.mjs +199 -77
- package/fesm2022/nanahoshi-mona-ui-circular-progress-bar.mjs +1 -2
- package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +23 -5
- package/fesm2022/nanahoshi-mona-ui-color-palette.mjs +49 -28
- package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +42 -26
- package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +11 -3
- package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +28 -16
- package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +74 -51
- package/fesm2022/nanahoshi-mona-ui-dialog.mjs +57 -25
- package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +13 -5
- package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +17 -6
- package/fesm2022/nanahoshi-mona-ui-editor.mjs +178 -59
- package/fesm2022/nanahoshi-mona-ui-filter.mjs +137 -54
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-cell.component-WEDbZ5vy.mjs → nanahoshi-mona-ui-grid-grid-footer-cell.component-Bg7rckst.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-ibH5osCg.mjs → nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-BSSpaCLL.mjs} +2 -2
- package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-D9AwQFDV.mjs +145 -0
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-toggle.component-hc-LeIb0.mjs → nanahoshi-mona-ui-grid-grid-toggle.component-Byb9iTes.mjs} +21 -17
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-DYajpOb9.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-CBmdtrXm.mjs} +5 -5
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Pm8Fbp6M.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-DPmJmCap.mjs} +90 -42
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-i18n.mjs +1168 -0
- package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +12 -4
- package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +48 -21
- package/fesm2022/nanahoshi-mona-ui-list-box.mjs +33 -18
- package/fesm2022/nanahoshi-mona-ui-list-view.mjs +10 -10
- package/fesm2022/nanahoshi-mona-ui-list.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-menubar.mjs +29 -16
- package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +12 -3
- package/fesm2022/nanahoshi-mona-ui-notification.mjs +23 -18
- package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +211 -106
- package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +14 -6
- package/fesm2022/nanahoshi-mona-ui-pager.mjs +46 -19
- package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +57 -46
- package/fesm2022/nanahoshi-mona-ui-popup.mjs +35 -15
- package/fesm2022/nanahoshi-mona-ui-progress-bar.mjs +38 -17
- package/fesm2022/nanahoshi-mona-ui-rating.mjs +25 -7
- package/fesm2022/nanahoshi-mona-ui-scroll-view.mjs +431 -101
- package/fesm2022/nanahoshi-mona-ui-segmented.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-sheet.mjs +26 -8
- package/fesm2022/nanahoshi-mona-ui-sidebar.mjs +222 -28
- package/fesm2022/nanahoshi-mona-ui-slider.mjs +91 -36
- package/fesm2022/nanahoshi-mona-ui-spinner.mjs +34 -15
- package/fesm2022/nanahoshi-mona-ui-split-button.mjs +39 -23
- package/fesm2022/nanahoshi-mona-ui-splitter.mjs +50 -25
- package/fesm2022/nanahoshi-mona-ui-stepper.mjs +32 -14
- package/fesm2022/nanahoshi-mona-ui-switch.mjs +9 -9
- package/fesm2022/nanahoshi-mona-ui-tabs.mjs +74 -56
- package/fesm2022/nanahoshi-mona-ui-text-box.mjs +16 -8
- package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +33 -19
- package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +66 -27
- package/fesm2022/nanahoshi-mona-ui-tree-view.mjs +23 -15
- package/fesm2022/nanahoshi-mona-ui-window.mjs +71 -40
- package/i18n/package.json +4 -0
- package/package.json +5 -1
- package/types/nanahoshi-mona-ui-auto-complete.d.ts +6 -1
- package/types/nanahoshi-mona-ui-breadcrumb.d.ts +9 -3
- package/types/nanahoshi-mona-ui-button-group.d.ts +9 -3
- package/types/nanahoshi-mona-ui-calendar.d.ts +13 -3
- package/types/nanahoshi-mona-ui-card.d.ts +8 -2
- package/types/nanahoshi-mona-ui-chart.d.ts +10 -1
- package/types/nanahoshi-mona-ui-chip.d.ts +14 -5
- package/types/nanahoshi-mona-ui-color-gradient.d.ts +6 -1
- package/types/nanahoshi-mona-ui-color-palette.d.ts +21 -10
- package/types/nanahoshi-mona-ui-color-picker.d.ts +30 -20
- package/types/nanahoshi-mona-ui-combo-box.d.ts +6 -1
- package/types/nanahoshi-mona-ui-date-picker.d.ts +7 -2
- package/types/nanahoshi-mona-ui-datetime-picker.d.ts +33 -8
- package/types/nanahoshi-mona-ui-dialog.d.ts +16 -7
- package/types/nanahoshi-mona-ui-dropdown-list.d.ts +6 -1
- package/types/nanahoshi-mona-ui-dropdowns.d.ts +4 -1
- package/types/nanahoshi-mona-ui-editor.d.ts +6 -1
- package/types/nanahoshi-mona-ui-filter.d.ts +16 -10
- package/types/nanahoshi-mona-ui-grid.d.ts +38 -1
- package/types/nanahoshi-mona-ui-i18n.d.ts +633 -0
- package/types/nanahoshi-mona-ui-internal-list.d.ts +7 -2
- package/types/nanahoshi-mona-ui-internal-tree.d.ts +7 -3
- package/types/nanahoshi-mona-ui-list-box.d.ts +9 -4
- package/types/nanahoshi-mona-ui-list-view.d.ts +6 -6
- package/types/nanahoshi-mona-ui-list.d.ts +1 -1
- package/types/nanahoshi-mona-ui-menubar.d.ts +59 -58
- package/types/nanahoshi-mona-ui-multi-select.d.ts +6 -1
- package/types/nanahoshi-mona-ui-notification.d.ts +11 -5
- package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +24 -15
- package/types/nanahoshi-mona-ui-otp-input.d.ts +9 -3
- package/types/nanahoshi-mona-ui-pager.d.ts +19 -6
- package/types/nanahoshi-mona-ui-popup.d.ts +2 -1
- package/types/nanahoshi-mona-ui-progress-bar.d.ts +9 -7
- package/types/nanahoshi-mona-ui-rating.d.ts +19 -12
- package/types/nanahoshi-mona-ui-scroll-view.d.ts +34 -28
- package/types/nanahoshi-mona-ui-sheet.d.ts +8 -2
- package/types/nanahoshi-mona-ui-sidebar.d.ts +3 -0
- package/types/nanahoshi-mona-ui-slider.d.ts +44 -26
- package/types/nanahoshi-mona-ui-spinner.d.ts +13 -4
- package/types/nanahoshi-mona-ui-split-button.d.ts +62 -56
- package/types/nanahoshi-mona-ui-splitter.d.ts +9 -6
- package/types/nanahoshi-mona-ui-stepper.d.ts +9 -3
- package/types/nanahoshi-mona-ui-switch.d.ts +1 -1
- package/types/nanahoshi-mona-ui-tabs.d.ts +96 -35
- package/types/nanahoshi-mona-ui-text-box.d.ts +11 -5
- package/types/nanahoshi-mona-ui-time-picker.d.ts +7 -2
- package/types/nanahoshi-mona-ui-time-selector.d.ts +16 -5
- package/types/nanahoshi-mona-ui-tree-view.d.ts +15 -10
- package/types/nanahoshi-mona-ui-window.d.ts +26 -20
- package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-ZM3CneJ0.mjs +0 -145
|
@@ -9,6 +9,7 @@ import { Subject, take, tap, fromEvent, takeUntil, filter, switchMap } from 'rxj
|
|
|
9
9
|
import { v4 } from 'uuid';
|
|
10
10
|
import { NgTemplateOutlet } from '@angular/common';
|
|
11
11
|
import { LucideChevronRight, LucideCheck } from '@lucide/angular';
|
|
12
|
+
import { injectComponentDirection, MonaI18nService } from '@nanahoshi/mona-ui/i18n';
|
|
12
13
|
import { twMerge } from 'tailwind-merge';
|
|
13
14
|
import { cva } from 'class-variance-authority';
|
|
14
15
|
|
|
@@ -166,9 +167,9 @@ const popupMenuGroupHeaderThemeVariants = cva(`inline-flex w-full select-none px
|
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
});
|
|
169
|
-
const popupMenuIconContainerThemeVariants = cva(`absolute
|
|
170
|
+
const popupMenuIconContainerThemeVariants = cva(`absolute start-2 flex items-center justify-center`);
|
|
170
171
|
const popupMenuItemThemeVariants = cva(`
|
|
171
|
-
relative flex cursor-pointer select-none items-center gap-2 py-1
|
|
172
|
+
relative flex cursor-pointer select-none items-center gap-2 py-1 pe-2 ps-8
|
|
172
173
|
text-foreground outline-none
|
|
173
174
|
hover:bg-[var(--mona-menu-item-hover-background,var(--color-hover))] hover:text-foreground
|
|
174
175
|
focus-within:bg-[var(--mona-menu-item-hover-background,var(--color-hover))]
|
|
@@ -200,7 +201,9 @@ class PopupMenuListComponent {
|
|
|
200
201
|
#close$;
|
|
201
202
|
#config;
|
|
202
203
|
#destroyRef;
|
|
204
|
+
#direction;
|
|
203
205
|
#host;
|
|
206
|
+
#i18n;
|
|
204
207
|
#navigationItems;
|
|
205
208
|
#parentConfig;
|
|
206
209
|
#popupService;
|
|
@@ -241,7 +244,11 @@ class PopupMenuListComponent {
|
|
|
241
244
|
}, /* @ts-ignore */
|
|
242
245
|
...(ngDevMode ? [{ debugName: "#config" }] : /* istanbul ignore next */ []));
|
|
243
246
|
this.#destroyRef = inject(DestroyRef);
|
|
247
|
+
this.#direction = injectComponentDirection();
|
|
244
248
|
this.#host = inject((ElementRef));
|
|
249
|
+
this.#i18n = inject(MonaI18nService);
|
|
250
|
+
this.isRtl = computed(() => this.#direction() === "rtl", /* @ts-ignore */
|
|
251
|
+
...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
|
|
245
252
|
this.#navigationItems = computed(() => {
|
|
246
253
|
const items = this.#parentConfig.items;
|
|
247
254
|
return selectMany(items, i => i.source)
|
|
@@ -268,10 +275,7 @@ class PopupMenuListComponent {
|
|
|
268
275
|
...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
|
|
269
276
|
this.groupHeaderClasses = computed(() => {
|
|
270
277
|
const size = this.#parentConfig.size();
|
|
271
|
-
|
|
272
|
-
const variantClasses = popupMenuGroupHeaderThemeVariants({ size });
|
|
273
|
-
const iconClasses = ""; //hasIcon ? "pl-8" : "pl-2";
|
|
274
|
-
return twMerge(variantClasses, iconClasses);
|
|
278
|
+
return popupMenuGroupHeaderThemeVariants({ size });
|
|
275
279
|
}, /* @ts-ignore */
|
|
276
280
|
...(ngDevMode ? [{ debugName: "groupHeaderClasses" }] : /* istanbul ignore next */ []));
|
|
277
281
|
this.iconAreaVisible = computed(() => {
|
|
@@ -295,7 +299,7 @@ class PopupMenuListComponent {
|
|
|
295
299
|
const size = this.#parentConfig.size();
|
|
296
300
|
const hasIcon = this.iconAreaVisible();
|
|
297
301
|
const variantClasses = popupMenuItemThemeVariants({ rounded, size });
|
|
298
|
-
const iconClasses = hasIcon ? "
|
|
302
|
+
const iconClasses = hasIcon ? "ps-8" : "ps-2";
|
|
299
303
|
return twMerge(variantClasses, iconClasses);
|
|
300
304
|
}, /* @ts-ignore */
|
|
301
305
|
...(ngDevMode ? [{ debugName: "menuItemClasses" }] : /* istanbul ignore next */ []));
|
|
@@ -317,7 +321,8 @@ class PopupMenuListComponent {
|
|
|
317
321
|
this.#host.nativeElement.firstElementChild?.focus();
|
|
318
322
|
if (!this.#parentConfig.isRoot && this.#parentConfig.viaKeyboardNavigation) {
|
|
319
323
|
const item = this.focusFirstItem();
|
|
320
|
-
this.
|
|
324
|
+
const isRtl = this.isRtl();
|
|
325
|
+
this.notifyNavigation(item, isRtl ? "left" : "right");
|
|
321
326
|
}
|
|
322
327
|
}
|
|
323
328
|
});
|
|
@@ -340,6 +345,9 @@ class PopupMenuListComponent {
|
|
|
340
345
|
}
|
|
341
346
|
this.#close$.next();
|
|
342
347
|
}
|
|
348
|
+
closeSubmenu() {
|
|
349
|
+
this.#parentConfig.childCloseRequest$?.next();
|
|
350
|
+
}
|
|
343
351
|
createSubmenu(target, viaKeyboard) {
|
|
344
352
|
this.#viaKeyboardNavigation.set(viaKeyboard);
|
|
345
353
|
const minWidth = this.#parentConfig.minWidth?.() ?? undefined;
|
|
@@ -410,24 +418,6 @@ class PopupMenuListComponent {
|
|
|
410
418
|
}
|
|
411
419
|
return firstItem;
|
|
412
420
|
}
|
|
413
|
-
handleArrowLeftKey() {
|
|
414
|
-
this.#parentConfig.childCloseRequest$?.next();
|
|
415
|
-
}
|
|
416
|
-
handleArrowRightKey() {
|
|
417
|
-
const activeItem = this.activeMenuItem();
|
|
418
|
-
if (!activeItem) {
|
|
419
|
-
return { item: null, hasChild: false };
|
|
420
|
-
}
|
|
421
|
-
const target = this.#host.nativeElement.querySelector('[data-active="true"]');
|
|
422
|
-
if (target && activeItem.items.length > 0) {
|
|
423
|
-
this.createSubmenu(target, true).subscribe();
|
|
424
|
-
return { item: activeItem, hasChild: true };
|
|
425
|
-
}
|
|
426
|
-
if (activeItem && activeItem.items.length > 0) {
|
|
427
|
-
return { item: activeItem, hasChild: true };
|
|
428
|
-
}
|
|
429
|
-
return { item: null, hasChild: false };
|
|
430
|
-
}
|
|
431
421
|
handleArrowUpKey() {
|
|
432
422
|
if (!this.activeMenuItem()) {
|
|
433
423
|
const lastItem = this.#navigationItems().lastOrDefault();
|
|
@@ -481,21 +471,26 @@ class PopupMenuListComponent {
|
|
|
481
471
|
}
|
|
482
472
|
}
|
|
483
473
|
handleNavigationKey(event) {
|
|
474
|
+
const isRtl = this.isRtl();
|
|
475
|
+
const openKey = isRtl ? "ArrowLeft" : "ArrowRight";
|
|
476
|
+
const closeKey = isRtl ? "ArrowRight" : "ArrowLeft";
|
|
477
|
+
const forwardDir = isRtl ? "left" : "right";
|
|
478
|
+
const backwardDir = isRtl ? "right" : "left";
|
|
484
479
|
let item = null;
|
|
485
480
|
if (event.key === "ArrowDown") {
|
|
486
481
|
item = this.handleArrowDownKey();
|
|
487
482
|
this.notifyNavigation(item, "down");
|
|
488
483
|
}
|
|
489
|
-
else if (event.key ===
|
|
490
|
-
this.
|
|
484
|
+
else if (event.key === closeKey) {
|
|
485
|
+
this.closeSubmenu();
|
|
491
486
|
if (this.#parentConfig.level === 0) {
|
|
492
|
-
this.notifyNavigation(null,
|
|
487
|
+
this.notifyNavigation(null, backwardDir);
|
|
493
488
|
}
|
|
494
489
|
}
|
|
495
|
-
else if (event.key ===
|
|
496
|
-
const result = this.
|
|
490
|
+
else if (event.key === openKey) {
|
|
491
|
+
const result = this.openSubmenu();
|
|
497
492
|
if (!result.hasChild) {
|
|
498
|
-
this.notifyNavigation(item,
|
|
493
|
+
this.notifyNavigation(item, forwardDir);
|
|
499
494
|
}
|
|
500
495
|
}
|
|
501
496
|
else if (event.key === "ArrowUp") {
|
|
@@ -526,6 +521,21 @@ class PopupMenuListComponent {
|
|
|
526
521
|
level: this.#parentConfig.level
|
|
527
522
|
});
|
|
528
523
|
}
|
|
524
|
+
openSubmenu() {
|
|
525
|
+
const activeItem = this.activeMenuItem();
|
|
526
|
+
if (!activeItem) {
|
|
527
|
+
return { item: null, hasChild: false };
|
|
528
|
+
}
|
|
529
|
+
const target = this.#host.nativeElement.querySelector('[data-active="true"]');
|
|
530
|
+
if (target && activeItem.items.length > 0) {
|
|
531
|
+
this.createSubmenu(target, true).subscribe();
|
|
532
|
+
return { item: activeItem, hasChild: true };
|
|
533
|
+
}
|
|
534
|
+
if (activeItem && activeItem.items.length > 0) {
|
|
535
|
+
return { item: activeItem, hasChild: true };
|
|
536
|
+
}
|
|
537
|
+
return { item: null, hasChild: false };
|
|
538
|
+
}
|
|
529
539
|
setKeyboardEvents() {
|
|
530
540
|
const element = this.#host.nativeElement.firstElementChild;
|
|
531
541
|
fromEvent(element, "keydown")
|
|
@@ -542,18 +552,6 @@ class PopupMenuListComponent {
|
|
|
542
552
|
}))
|
|
543
553
|
.subscribe();
|
|
544
554
|
}
|
|
545
|
-
setPopupCloseSubscription() {
|
|
546
|
-
this.#parentConfig.parentClose$
|
|
547
|
-
.pipe(takeUntilDestroyed(this.#destroyRef), tap(() => this.closePopup()))
|
|
548
|
-
.subscribe();
|
|
549
|
-
this.#childCloseRequest$
|
|
550
|
-
.pipe(takeUntilDestroyed(this.#destroyRef), tap(() => {
|
|
551
|
-
const activeItem = this.activeMenuItem();
|
|
552
|
-
this.notifyNavigation(activeItem, "left");
|
|
553
|
-
this.closePopup();
|
|
554
|
-
}))
|
|
555
|
-
.subscribe();
|
|
556
|
-
}
|
|
557
555
|
setPointerSubscriptions() {
|
|
558
556
|
this.pointerEnter$
|
|
559
557
|
.pipe(takeUntilDestroyed(this.#destroyRef), takeUntil(this.#parentConfig.parentClose$), filter(e => e.item !== this.activeMenuItem()), tap(e => {
|
|
@@ -570,6 +568,19 @@ class PopupMenuListComponent {
|
|
|
570
568
|
.pipe(takeUntilDestroyed(this.#destroyRef), takeUntil(this.pointerEnter$))
|
|
571
569
|
.subscribe(() => this.popupRef?.close());
|
|
572
570
|
}
|
|
571
|
+
setPopupCloseSubscription() {
|
|
572
|
+
this.#parentConfig.parentClose$
|
|
573
|
+
.pipe(takeUntilDestroyed(this.#destroyRef), tap(() => this.closePopup()))
|
|
574
|
+
.subscribe();
|
|
575
|
+
this.#childCloseRequest$
|
|
576
|
+
.pipe(takeUntilDestroyed(this.#destroyRef), tap(() => {
|
|
577
|
+
const activeItem = this.activeMenuItem();
|
|
578
|
+
const isRtl = this.isRtl();
|
|
579
|
+
this.notifyNavigation(activeItem, isRtl ? "right" : "left");
|
|
580
|
+
this.closePopup();
|
|
581
|
+
}))
|
|
582
|
+
.subscribe();
|
|
583
|
+
}
|
|
573
584
|
setSubscriptions() {
|
|
574
585
|
this.setKeyboardEvents();
|
|
575
586
|
this.setPointerSubscriptions();
|
|
@@ -582,13 +593,13 @@ class PopupMenuListComponent {
|
|
|
582
593
|
.subscribe(buffer => this.cycleThroughMatchedItems(buffer));
|
|
583
594
|
}
|
|
584
595
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupMenuListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
585
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: PopupMenuListComponent, isStandalone: true, selector: "mona-popup-menu-list", host: { properties: { "class": "baseClasses()" } }, ngImport: i0, template: "<div\n role=\"menu\"\n [attr.tabindex]=\"0\"\n [attr.aria-orientation]=\"'vertical'\"\n [attr.aria-activedescendant]=\"activeMenuItem()?.uid\"\n [attr.id]=\"menuId()\"\n [class]=\"containerClasses()\">\n @for (group of menuItems(); track $index) {\n @if (typeof group.key === \"string\") {\n @let groupTemplate = group.first().groupTemplate?.() ?? popupGroupTemplate() ?? null;\n <div role=\"group\" [attr.aria-labelledby]=\"`${group.key}-${$index}`\" [class]=\"groupHeaderClasses()\">\n <div [id]=\"`${group.key}-${$index}`\" role=\"presentation\">\n @if (groupTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"groupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: group.key }\"></ng-container>\n } @else {\n <span>{{ group.key }}</span>\n }\n </div>\n </div>\n }\n @for (item of group.source; track $index) {\n @if (item.separator) {\n <div role=\"separator\" class=\"bg-border my-1 h-px\"></div>\n } @else {\n @let iconTemplate = item.iconTemplate?.() ?? popupIconTemplate() ?? null;\n @let shortcutTemplate = item.shortcutTemplate?.() ?? popupShortcutTemplate() ?? null;\n @let textTemplate = item.textTemplate?.() ?? popupTextTemplate() ?? null;\n <div\n role=\"menuitem\"\n [id]=\"item.uid\"\n (pointerenter)=\"pointerEnter$.next({ event: $event, item: item })\"\n (pointerleave)=\"pointerLeave$.next({ event: $event, item: item })\"\n (click)=\"onItemClick($event, item)\"\n [attr.data-active]=\"item === activeMenuItem()\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-expanded]=\"item.items.length > 0 && item === activeMenuItem() && !!popupRef\"\n [attr.aria-haspopup]=\"item.items.length > 0 ? 'menu' : null\"\n [attr.aria-label]=\"item.label || null\"\n [attr.data-disabled]=\"item.disabled\"\n [class]=\"menuItemClasses()\">\n @if (iconAreaVisible()) {\n <div [class]=\"iconContainerClasses()\">\n @if (!item.checkable && !item.radio) {\n <ng-container\n [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else if (item.checkable) {\n @if (item.checked?.()) {\n <svg lucideCheck [size]=\"14\"></svg>\n }\n } @else if (item.radio) {\n @if (item.selected?.()) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"lucide lucide-circle size-2 fill-current\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n </svg>\n }\n }\n </div>\n }\n <div class=\"flex-1\">\n @if (textTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"textTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n {{ item.label }}\n }\n </div>\n @if (item.items.length > 0) {\n @if (!item.checkable) {\n <div [class]=\"linkContainerClasses()\">\n <svg lucideChevronRight [size]=\"16\"></svg>\n </div>\n }\n } @else if (shortcutTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"shortcutTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n }\n </div>\n }\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }] }); }
|
|
596
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: PopupMenuListComponent, isStandalone: true, selector: "mona-popup-menu-list", host: { properties: { "class": "baseClasses()" } }, ngImport: i0, template: "<div\n role=\"menu\"\n [attr.tabindex]=\"0\"\n [attr.aria-orientation]=\"'vertical'\"\n [attr.aria-activedescendant]=\"activeMenuItem()?.uid\"\n [attr.id]=\"menuId()\"\n [class]=\"containerClasses()\">\n @for (group of menuItems(); track $index) {\n @if (typeof group.key === \"string\") {\n @let groupTemplate = group.first().groupTemplate?.() ?? popupGroupTemplate() ?? null;\n <div role=\"group\" [attr.aria-labelledby]=\"`${group.key}-${$index}`\" [class]=\"groupHeaderClasses()\">\n <div [id]=\"`${group.key}-${$index}`\" role=\"presentation\">\n @if (groupTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"groupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: group.key }\"></ng-container>\n } @else {\n <span>{{ group.key }}</span>\n }\n </div>\n </div>\n }\n @for (item of group.source; track $index) {\n @if (item.separator) {\n <div role=\"separator\" class=\"bg-border my-1 h-px\"></div>\n } @else {\n @let iconTemplate = item.iconTemplate?.() ?? popupIconTemplate() ?? null;\n @let shortcutTemplate = item.shortcutTemplate?.() ?? popupShortcutTemplate() ?? null;\n @let textTemplate = item.textTemplate?.() ?? popupTextTemplate() ?? null;\n <div\n role=\"menuitem\"\n [id]=\"item.uid\"\n (pointerenter)=\"pointerEnter$.next({ event: $event, item: item })\"\n (pointerleave)=\"pointerLeave$.next({ event: $event, item: item })\"\n (click)=\"onItemClick($event, item)\"\n [attr.data-active]=\"item === activeMenuItem()\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-expanded]=\"item.items.length > 0 && item === activeMenuItem() && !!popupRef\"\n [attr.aria-haspopup]=\"item.items.length > 0 ? 'menu' : null\"\n [attr.aria-label]=\"item.label || null\"\n [attr.data-disabled]=\"item.disabled\"\n [class]=\"menuItemClasses()\">\n @if (iconAreaVisible()) {\n <div [class]=\"iconContainerClasses()\">\n @if (!item.checkable && !item.radio) {\n <ng-container\n [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else if (item.checkable) {\n @if (item.checked?.()) {\n <svg lucideCheck [size]=\"14\"></svg>\n }\n } @else if (item.radio) {\n @if (item.selected?.()) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"lucide lucide-circle size-2 fill-current\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n </svg>\n }\n }\n </div>\n }\n <div class=\"flex-1\">\n @if (textTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"textTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n {{ item.label }}\n }\n </div>\n @if (item.items.length > 0) {\n @if (!item.checkable) {\n <div [class]=\"linkContainerClasses()\">\n <svg lucideChevronRight [size]=\"16\" class=\"rtl:rotate-180\"></svg>\n </div>\n }\n } @else if (shortcutTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"shortcutTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n }\n </div>\n }\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }] }); }
|
|
586
597
|
}
|
|
587
598
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupMenuListComponent, decorators: [{
|
|
588
599
|
type: Component,
|
|
589
600
|
args: [{ selector: "mona-popup-menu-list", imports: [NgTemplateOutlet, LucideChevronRight, LucideCheck], host: {
|
|
590
601
|
"[class]": "baseClasses()"
|
|
591
|
-
}, template: "<div\n role=\"menu\"\n [attr.tabindex]=\"0\"\n [attr.aria-orientation]=\"'vertical'\"\n [attr.aria-activedescendant]=\"activeMenuItem()?.uid\"\n [attr.id]=\"menuId()\"\n [class]=\"containerClasses()\">\n @for (group of menuItems(); track $index) {\n @if (typeof group.key === \"string\") {\n @let groupTemplate = group.first().groupTemplate?.() ?? popupGroupTemplate() ?? null;\n <div role=\"group\" [attr.aria-labelledby]=\"`${group.key}-${$index}`\" [class]=\"groupHeaderClasses()\">\n <div [id]=\"`${group.key}-${$index}`\" role=\"presentation\">\n @if (groupTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"groupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: group.key }\"></ng-container>\n } @else {\n <span>{{ group.key }}</span>\n }\n </div>\n </div>\n }\n @for (item of group.source; track $index) {\n @if (item.separator) {\n <div role=\"separator\" class=\"bg-border my-1 h-px\"></div>\n } @else {\n @let iconTemplate = item.iconTemplate?.() ?? popupIconTemplate() ?? null;\n @let shortcutTemplate = item.shortcutTemplate?.() ?? popupShortcutTemplate() ?? null;\n @let textTemplate = item.textTemplate?.() ?? popupTextTemplate() ?? null;\n <div\n role=\"menuitem\"\n [id]=\"item.uid\"\n (pointerenter)=\"pointerEnter$.next({ event: $event, item: item })\"\n (pointerleave)=\"pointerLeave$.next({ event: $event, item: item })\"\n (click)=\"onItemClick($event, item)\"\n [attr.data-active]=\"item === activeMenuItem()\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-expanded]=\"item.items.length > 0 && item === activeMenuItem() && !!popupRef\"\n [attr.aria-haspopup]=\"item.items.length > 0 ? 'menu' : null\"\n [attr.aria-label]=\"item.label || null\"\n [attr.data-disabled]=\"item.disabled\"\n [class]=\"menuItemClasses()\">\n @if (iconAreaVisible()) {\n <div [class]=\"iconContainerClasses()\">\n @if (!item.checkable && !item.radio) {\n <ng-container\n [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else if (item.checkable) {\n @if (item.checked?.()) {\n <svg lucideCheck [size]=\"14\"></svg>\n }\n } @else if (item.radio) {\n @if (item.selected?.()) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"lucide lucide-circle size-2 fill-current\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n </svg>\n }\n }\n </div>\n }\n <div class=\"flex-1\">\n @if (textTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"textTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n {{ item.label }}\n }\n </div>\n @if (item.items.length > 0) {\n @if (!item.checkable) {\n <div [class]=\"linkContainerClasses()\">\n <svg lucideChevronRight [size]=\"16\"></svg>\n </div>\n }\n } @else if (shortcutTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"shortcutTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n }\n </div>\n }\n }\n }\n</div>\n" }]
|
|
602
|
+
}, template: "<div\n role=\"menu\"\n [attr.tabindex]=\"0\"\n [attr.aria-orientation]=\"'vertical'\"\n [attr.aria-activedescendant]=\"activeMenuItem()?.uid\"\n [attr.id]=\"menuId()\"\n [class]=\"containerClasses()\">\n @for (group of menuItems(); track $index) {\n @if (typeof group.key === \"string\") {\n @let groupTemplate = group.first().groupTemplate?.() ?? popupGroupTemplate() ?? null;\n <div role=\"group\" [attr.aria-labelledby]=\"`${group.key}-${$index}`\" [class]=\"groupHeaderClasses()\">\n <div [id]=\"`${group.key}-${$index}`\" role=\"presentation\">\n @if (groupTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"groupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: group.key }\"></ng-container>\n } @else {\n <span>{{ group.key }}</span>\n }\n </div>\n </div>\n }\n @for (item of group.source; track $index) {\n @if (item.separator) {\n <div role=\"separator\" class=\"bg-border my-1 h-px\"></div>\n } @else {\n @let iconTemplate = item.iconTemplate?.() ?? popupIconTemplate() ?? null;\n @let shortcutTemplate = item.shortcutTemplate?.() ?? popupShortcutTemplate() ?? null;\n @let textTemplate = item.textTemplate?.() ?? popupTextTemplate() ?? null;\n <div\n role=\"menuitem\"\n [id]=\"item.uid\"\n (pointerenter)=\"pointerEnter$.next({ event: $event, item: item })\"\n (pointerleave)=\"pointerLeave$.next({ event: $event, item: item })\"\n (click)=\"onItemClick($event, item)\"\n [attr.data-active]=\"item === activeMenuItem()\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-expanded]=\"item.items.length > 0 && item === activeMenuItem() && !!popupRef\"\n [attr.aria-haspopup]=\"item.items.length > 0 ? 'menu' : null\"\n [attr.aria-label]=\"item.label || null\"\n [attr.data-disabled]=\"item.disabled\"\n [class]=\"menuItemClasses()\">\n @if (iconAreaVisible()) {\n <div [class]=\"iconContainerClasses()\">\n @if (!item.checkable && !item.radio) {\n <ng-container\n [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else if (item.checkable) {\n @if (item.checked?.()) {\n <svg lucideCheck [size]=\"14\"></svg>\n }\n } @else if (item.radio) {\n @if (item.selected?.()) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"lucide lucide-circle size-2 fill-current\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n </svg>\n }\n }\n </div>\n }\n <div class=\"flex-1\">\n @if (textTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"textTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n {{ item.label }}\n }\n </div>\n @if (item.items.length > 0) {\n @if (!item.checkable) {\n <div [class]=\"linkContainerClasses()\">\n <svg lucideChevronRight [size]=\"16\" class=\"rtl:rotate-180\"></svg>\n </div>\n }\n } @else if (shortcutTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"shortcutTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n }\n </div>\n }\n }\n }\n</div>\n" }]
|
|
592
603
|
}], ctorParameters: () => [] });
|
|
593
604
|
|
|
594
605
|
class PopupMenuComponent {
|
|
@@ -8,6 +8,7 @@ import { Directionality } from '@angular/cdk/bidi';
|
|
|
8
8
|
import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
|
|
9
9
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
10
10
|
import { ScrollDispatcher } from '@angular/cdk/scrolling';
|
|
11
|
+
import { MonaI18nService, resolveComponentDirection, observeComponentDirection } from '@nanahoshi/mona-ui/i18n';
|
|
11
12
|
import { NgTemplateOutlet } from '@angular/common';
|
|
12
13
|
|
|
13
14
|
class PopupCloseEvent extends PreventableEvent {
|
|
@@ -552,11 +553,12 @@ class PopupService {
|
|
|
552
553
|
#destroyRef = inject(DestroyRef);
|
|
553
554
|
#directionality = inject(Directionality, { optional: true });
|
|
554
555
|
#document = inject(DOCUMENT);
|
|
556
|
+
#i18n = inject(MonaI18nService, { optional: true });
|
|
555
557
|
#injector = inject(Injector);
|
|
556
558
|
#outsideEventsToClose = ["click", "mousedown", "dblclick", "contextmenu", "auxclick"];
|
|
557
559
|
#overlay = inject(Overlay);
|
|
558
|
-
#selectorSubscriptions = new Map();
|
|
559
560
|
#scrollDispatcher = inject(ScrollDispatcher);
|
|
561
|
+
#selectorSubscriptions = new Map();
|
|
560
562
|
create(settings) {
|
|
561
563
|
if (typeof settings.anchor === "string") {
|
|
562
564
|
return this.createMultiElementPopup(settings);
|
|
@@ -628,7 +630,12 @@ class PopupService {
|
|
|
628
630
|
createOverlay(settings) {
|
|
629
631
|
const positionStrategy = this.createPositionStrategy(settings);
|
|
630
632
|
const panelClass = this.buildPanelClass(settings.popupClass);
|
|
631
|
-
const
|
|
633
|
+
const anchorElement = settings.anchor instanceof ElementRef
|
|
634
|
+
? settings.anchor.nativeElement
|
|
635
|
+
: settings.anchor instanceof HTMLElement
|
|
636
|
+
? settings.anchor
|
|
637
|
+
: null;
|
|
638
|
+
const direction = resolveComponentDirection(anchorElement, this.#directionality);
|
|
632
639
|
return this.#overlay.create(new OverlayConfig({
|
|
633
640
|
positionStrategy,
|
|
634
641
|
hasBackdrop: settings.hasBackdrop ?? false,
|
|
@@ -688,6 +695,7 @@ class PopupService {
|
|
|
688
695
|
this.setupScrollTracking(settings, overlayRef, popupReference);
|
|
689
696
|
this.setupEscapeKeyListener(settings, popupReference);
|
|
690
697
|
this.setupPositionChangeTracking(settings, overlayRef, popupReference);
|
|
698
|
+
this.setupDirectionTracking(settings, overlayRef, popupReference);
|
|
691
699
|
return popupReference.popupRef;
|
|
692
700
|
}
|
|
693
701
|
getAnchorElement(anchor) {
|
|
@@ -732,6 +740,19 @@ class PopupService {
|
|
|
732
740
|
.forEach(scrollable => addScrollable(scrollable));
|
|
733
741
|
return Array.from(scrollablesByElement.values());
|
|
734
742
|
}
|
|
743
|
+
handleFocusRestoration(settings, originallyFocusedElement) {
|
|
744
|
+
const restoreFocus = settings.restoreFocus ?? "auto";
|
|
745
|
+
if (restoreFocus === false) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
if (restoreFocus === "auto") {
|
|
749
|
+
if (originallyFocusedElement) {
|
|
750
|
+
originallyFocusedElement.focus();
|
|
751
|
+
}
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
this.restoreFocusToAnchor(settings.anchor);
|
|
755
|
+
}
|
|
735
756
|
isScrollableElement(element) {
|
|
736
757
|
const defaultView = this.#document.defaultView;
|
|
737
758
|
if (!defaultView) {
|
|
@@ -747,19 +768,6 @@ class PopupService {
|
|
|
747
768
|
isScrollableOverflow(value) {
|
|
748
769
|
return value === "auto" || value === "scroll" || value === "overlay";
|
|
749
770
|
}
|
|
750
|
-
handleFocusRestoration(settings, originallyFocusedElement) {
|
|
751
|
-
const restoreFocus = settings.restoreFocus ?? "auto";
|
|
752
|
-
if (restoreFocus === false) {
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
if (restoreFocus === "auto") {
|
|
756
|
-
if (originallyFocusedElement) {
|
|
757
|
-
originallyFocusedElement.focus();
|
|
758
|
-
}
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
this.restoreFocusToAnchor(settings.anchor);
|
|
762
|
-
}
|
|
763
771
|
/**
|
|
764
772
|
* Resolves a PopupAnchor to a FlexibleConnectedPositionStrategyOrigin.
|
|
765
773
|
* This method should only receive actual elements or ElementRefs, not CSS selector strings.
|
|
@@ -936,6 +944,18 @@ class PopupService {
|
|
|
936
944
|
.subscribe(change => popupReference.positionChanges$.next(change.connectionPair));
|
|
937
945
|
}
|
|
938
946
|
}
|
|
947
|
+
setupDirectionTracking(settings, overlayRef, popupReference) {
|
|
948
|
+
const resolvedAnchor = this.resolveAnchor(settings.anchor);
|
|
949
|
+
const anchorElement = this.getAnchorElement(resolvedAnchor);
|
|
950
|
+
const cleanup = observeComponentDirection(anchorElement, this.#directionality, nextDir => {
|
|
951
|
+
overlayRef.setDirection(nextDir);
|
|
952
|
+
overlayRef.updatePosition();
|
|
953
|
+
popupReference.componentRef?.changeDetectorRef.markForCheck();
|
|
954
|
+
});
|
|
955
|
+
popupReference.closed.pipe(take(1)).subscribe(() => {
|
|
956
|
+
cleanup();
|
|
957
|
+
});
|
|
958
|
+
}
|
|
939
959
|
setupScrollClosing(settings, popupReference, overlayRef) {
|
|
940
960
|
if (!settings.closeOnScroll) {
|
|
941
961
|
return;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { NgTemplateOutlet
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
3
|
import { inject, TemplateRef, Directive, computed, contentChild, input, Component } from '@angular/core';
|
|
4
4
|
import { getPercentage } from '@nanahoshi/mona-ui/common';
|
|
5
|
+
import { injectComponentDirection, MonaI18nService, formatNumber } from '@nanahoshi/mona-ui/i18n';
|
|
5
6
|
import { twMerge } from 'tailwind-merge';
|
|
6
7
|
import { cva } from 'class-variance-authority';
|
|
7
8
|
|
|
@@ -62,8 +63,8 @@ const progressBarTrackThemeVariants = cva(`
|
|
|
62
63
|
data-[next='true']:duration-(--mona-motion-fast)
|
|
63
64
|
data-[next='true']:ease-in
|
|
64
65
|
data-[value='0']:bg-transparent
|
|
65
|
-
data-[
|
|
66
|
-
data-[
|
|
66
|
+
data-[start='true']:justify-start
|
|
67
|
+
data-[end='true']:justify-end
|
|
67
68
|
`, {
|
|
68
69
|
variants: {
|
|
69
70
|
rounded: {
|
|
@@ -84,6 +85,18 @@ class ProgressBarComponent {
|
|
|
84
85
|
return typeof color === "string" ? color : color?.(progress);
|
|
85
86
|
}, /* @ts-ignore */
|
|
86
87
|
...(ngDevMode ? [{ debugName: "#color" }] : /* istanbul ignore next */ []));
|
|
88
|
+
this.#direction = injectComponentDirection();
|
|
89
|
+
this.#i18n = inject(MonaI18nService);
|
|
90
|
+
this.isRtl = computed(() => this.#direction() === "rtl", /* @ts-ignore */
|
|
91
|
+
...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
|
|
92
|
+
this.formattedProgress = computed(() => {
|
|
93
|
+
return formatNumber(this.progress(), this.#i18n.localeId(), {
|
|
94
|
+
minimumFractionDigits: 0,
|
|
95
|
+
maximumFractionDigits: 2,
|
|
96
|
+
useGrouping: false
|
|
97
|
+
});
|
|
98
|
+
}, /* @ts-ignore */
|
|
99
|
+
...(ngDevMode ? [{ debugName: "formattedProgress" }] : /* istanbul ignore next */ []));
|
|
87
100
|
this.baseClasses = computed(() => {
|
|
88
101
|
const rounded = this.rounded();
|
|
89
102
|
const classes = progressBarBaseThemeVariants({ rounded });
|
|
@@ -103,7 +116,10 @@ class ProgressBarComponent {
|
|
|
103
116
|
...(ngDevMode ? [{ debugName: "labelTemplate" }] : /* istanbul ignore next */ []));
|
|
104
117
|
this.nextTrackClipPath = computed(() => {
|
|
105
118
|
const progress = this.progress();
|
|
106
|
-
|
|
119
|
+
const isRtl = this.isRtl();
|
|
120
|
+
return isRtl
|
|
121
|
+
? `inset(-1px ${progress}% -1px -1px)`
|
|
122
|
+
: `inset(-1px -1px -1px ${progress}%)`;
|
|
107
123
|
}, /* @ts-ignore */
|
|
108
124
|
...(ngDevMode ? [{ debugName: "nextTrackClipPath" }] : /* istanbul ignore next */ []));
|
|
109
125
|
this.prevTrackClipPath = computed(() => {
|
|
@@ -112,8 +128,11 @@ class ProgressBarComponent {
|
|
|
112
128
|
if (indeterminate) {
|
|
113
129
|
return `inset(-1px -1px -1px -1px)`;
|
|
114
130
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
131
|
+
const offset = progress < 100 ? 8 : 0;
|
|
132
|
+
const isRtl = this.isRtl();
|
|
133
|
+
return isRtl
|
|
134
|
+
? `inset(-1px 0px -1px ${offset}px)`
|
|
135
|
+
: `inset(-1px ${offset}px -1px 0px)`;
|
|
117
136
|
}, /* @ts-ignore */
|
|
118
137
|
...(ngDevMode ? [{ debugName: "prevTrackClipPath" }] : /* istanbul ignore next */ []));
|
|
119
138
|
this.progress = computed(() => getPercentage(this.value(), this.min(), this.max()), /* @ts-ignore */
|
|
@@ -127,6 +146,13 @@ class ProgressBarComponent {
|
|
|
127
146
|
}, /* @ts-ignore */
|
|
128
147
|
...(ngDevMode ? [{ debugName: "trackClasses" }] : /* istanbul ignore next */ []));
|
|
129
148
|
this.trackColor = this.#color;
|
|
149
|
+
/**
|
|
150
|
+
* @description Enables CSS transitions on fill and color changes.
|
|
151
|
+
* Set to `false` when updating `value` at high frequency (e.g., a real-time byte counter).
|
|
152
|
+
* @default true
|
|
153
|
+
*/
|
|
154
|
+
this.animate = input(true, /* @ts-ignore */
|
|
155
|
+
...(ngDevMode ? [{ debugName: "animate" }] : /* istanbul ignore next */ []));
|
|
130
156
|
/**
|
|
131
157
|
* @description Accessible label for the progress bar host element.
|
|
132
158
|
* Use to describe what is being measured (e.g., "Upload progress").
|
|
@@ -142,13 +168,6 @@ class ProgressBarComponent {
|
|
|
142
168
|
* @default ""
|
|
143
169
|
*/
|
|
144
170
|
this.ariaValueText = input("", { ...(ngDevMode ? { debugName: "ariaValueText" } : /* istanbul ignore next */ {}), alias: "aria-valuetext" });
|
|
145
|
-
/**
|
|
146
|
-
* @description Enables CSS transitions on fill and color changes.
|
|
147
|
-
* Set to `false` when updating `value` at high frequency (e.g., a real-time byte counter).
|
|
148
|
-
* @default true
|
|
149
|
-
*/
|
|
150
|
-
this.animate = input(true, /* @ts-ignore */
|
|
151
|
-
...(ngDevMode ? [{ debugName: "animate" }] : /* istanbul ignore next */ []));
|
|
152
171
|
/**
|
|
153
172
|
* @description Fill color of the progress track.
|
|
154
173
|
* Pass a CSS color string, or a function that receives the current percentage (0–100) and returns a color string.
|
|
@@ -222,12 +241,14 @@ class ProgressBarComponent {
|
|
|
222
241
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
223
242
|
}
|
|
224
243
|
#color;
|
|
244
|
+
#direction;
|
|
245
|
+
#i18n;
|
|
225
246
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
226
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: ProgressBarComponent, isStandalone: true, selector: "mona-progress-bar", inputs: {
|
|
247
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: ProgressBarComponent, isStandalone: true, selector: "mona-progress-bar", inputs: { animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaValueText: { classPropertyName: "ariaValueText", publicName: "aria-valuetext", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null }, labelStyles: { classPropertyName: "labelStyles", publicName: "labelStyles", isSignal: true, isRequired: false, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "labelVisible", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "progressbar" }, properties: { "class": "baseClasses()", "attr.aria-busy": "indeterminate() || null", "attr.aria-label": "ariaLabel() || null", "attr.aria-valuemin": "indeterminate() ? null : min()", "attr.aria-valuemax": "indeterminate() ? null : max()", "attr.aria-valuenow": "indeterminate() ? null : value()", "attr.aria-valuetext": "ariaValueText() || null", "attr.aria-disabled": "disabled() || null", "attr.data-disabled": "disabled()" } }, queries: [{ propertyName: "labelTemplate", first: true, predicate: ProgressBarLabelTemplateDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n [class]=\"trackClasses()\"\n [style.background-color]=\"trackColor()\"\n [attr.data-prev]=\"true\"\n [attr.data-start]=\"labelPosition() === 'start'\"\n [attr.data-end]=\"labelPosition() === 'end'\"\n [attr.data-indeterminate]=\"indeterminate()\"\n [attr.data-value]=\"progress()\"\n [style.clip-path]=\"prevTrackClipPath()\">\n @if (indeterminate()) {\n <div [class]=\"indeterminateClasses()\" class=\"indeterminate\"></div>\n } @else if (labelVisible()) {\n @if (labelTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"labelTemplate()?.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), min: min(), max: max(), percent: progress() }\">\n </ng-container>\n } @else {\n <span [class]=\"labelClasses()\" [style]=\"labelStyles()\">{{ formattedProgress() }}</span>\n }\n }\n</div>\n\n@if (!indeterminate()) {\n <div\n [class]=\"trackClasses()\"\n [attr.data-next]=\"true\"\n [attr.data-start]=\"labelPosition() === 'start'\"\n [attr.data-end]=\"labelPosition() === 'end'\"\n [style.clip-path]=\"nextTrackClipPath()\">\n @if (labelVisible()) {\n @if (labelTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"labelTemplate()?.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), min: min(), max: max(), percent: progress() }\">\n </ng-container>\n } @else {\n <span [class]=\"labelClasses()\" [style]=\"labelStyles()\">{{ formattedProgress() }}</span>\n }\n }\n </div>\n}\n", styles: [".indeterminate{animation:indeterminate 16s linear infinite}@keyframes indeterminate{0%{background-position:0 0}to{background-position:100% 0}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
|
|
227
248
|
}
|
|
228
249
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: ProgressBarComponent, decorators: [{
|
|
229
250
|
type: Component,
|
|
230
|
-
args: [{ selector: "mona-progress-bar", imports: [
|
|
251
|
+
args: [{ selector: "mona-progress-bar", imports: [NgTemplateOutlet], host: {
|
|
231
252
|
"[class]": "baseClasses()",
|
|
232
253
|
"[attr.aria-busy]": "indeterminate() || null",
|
|
233
254
|
"[attr.aria-label]": "ariaLabel() || null",
|
|
@@ -238,8 +259,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
238
259
|
"[attr.aria-disabled]": "disabled() || null",
|
|
239
260
|
"[attr.data-disabled]": "disabled()",
|
|
240
261
|
role: "progressbar"
|
|
241
|
-
}, template: "<div\n [class]=\"trackClasses()\"\n [style.background-color]=\"trackColor()\"\n [attr.data-prev]=\"true\"\n [attr.data-
|
|
242
|
-
}], propDecorators: { labelTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ProgressBarLabelTemplateDirective), { isSignal: true }] }],
|
|
262
|
+
}, template: "<div\n [class]=\"trackClasses()\"\n [style.background-color]=\"trackColor()\"\n [attr.data-prev]=\"true\"\n [attr.data-start]=\"labelPosition() === 'start'\"\n [attr.data-end]=\"labelPosition() === 'end'\"\n [attr.data-indeterminate]=\"indeterminate()\"\n [attr.data-value]=\"progress()\"\n [style.clip-path]=\"prevTrackClipPath()\">\n @if (indeterminate()) {\n <div [class]=\"indeterminateClasses()\" class=\"indeterminate\"></div>\n } @else if (labelVisible()) {\n @if (labelTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"labelTemplate()?.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), min: min(), max: max(), percent: progress() }\">\n </ng-container>\n } @else {\n <span [class]=\"labelClasses()\" [style]=\"labelStyles()\">{{ formattedProgress() }}</span>\n }\n }\n</div>\n\n@if (!indeterminate()) {\n <div\n [class]=\"trackClasses()\"\n [attr.data-next]=\"true\"\n [attr.data-start]=\"labelPosition() === 'start'\"\n [attr.data-end]=\"labelPosition() === 'end'\"\n [style.clip-path]=\"nextTrackClipPath()\">\n @if (labelVisible()) {\n @if (labelTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"labelTemplate()?.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), min: min(), max: max(), percent: progress() }\">\n </ng-container>\n } @else {\n <span [class]=\"labelClasses()\" [style]=\"labelStyles()\">{{ formattedProgress() }}</span>\n }\n }\n </div>\n}\n", styles: [".indeterminate{animation:indeterminate 16s linear infinite}@keyframes indeterminate{0%{background-position:0 0}to{background-position:100% 0}}\n"] }]
|
|
263
|
+
}], propDecorators: { labelTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ProgressBarLabelTemplateDirective), { isSignal: true }] }], animate: [{ type: i0.Input, args: [{ isSignal: true, alias: "animate", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaValueText: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-valuetext", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }], labelStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelStyles", required: false }] }], labelVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelVisible", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
|
|
243
264
|
|
|
244
265
|
/*
|
|
245
266
|
* Public API Surface of @nanahoshi/mona-ui/progress-bar
|
|
@@ -4,9 +4,15 @@ import { inject, TemplateRef, Directive, computed, viewChild, contentChild, sign
|
|
|
4
4
|
import { LucideStar, LucideHeart, LucideFlame, LucideDiamond, LucideCircle, LucideDynamicIcon } from '@lucide/angular';
|
|
5
5
|
import { createElementControlId } from '@nanahoshi/mona-ui/internal';
|
|
6
6
|
import { range } from '@mirei/ts-collections';
|
|
7
|
+
import { injectComponentDirection, MonaI18nService } from '@nanahoshi/mona-ui/i18n';
|
|
7
8
|
import { twMerge } from 'tailwind-merge';
|
|
8
9
|
import { cva } from 'class-variance-authority';
|
|
9
10
|
|
|
11
|
+
const RATING_DEFAULT_MESSAGES = {
|
|
12
|
+
notRated: "Not rated",
|
|
13
|
+
valueText: (value, max) => `${value} out of ${max}`
|
|
14
|
+
};
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* @description Renders the pointer-preview visual for rating items. Overrides the default preset
|
|
12
18
|
* overlay whenever the visible state comes from pointer preview.
|
|
@@ -15,7 +21,7 @@ class RatingHoveredItemTemplateDirective {
|
|
|
15
21
|
constructor() {
|
|
16
22
|
this.templateRef = inject(TemplateRef);
|
|
17
23
|
}
|
|
18
|
-
static ngTemplateContextGuard(_directive,
|
|
24
|
+
static ngTemplateContextGuard(_directive, _context) {
|
|
19
25
|
return true;
|
|
20
26
|
}
|
|
21
27
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: RatingHoveredItemTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -36,7 +42,7 @@ class RatingItemTemplateDirective {
|
|
|
36
42
|
constructor() {
|
|
37
43
|
this.templateRef = inject(TemplateRef);
|
|
38
44
|
}
|
|
39
|
-
static ngTemplateContextGuard(_directive,
|
|
45
|
+
static ngTemplateContextGuard(_directive, _context) {
|
|
40
46
|
return true;
|
|
41
47
|
}
|
|
42
48
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: RatingItemTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -57,7 +63,7 @@ class RatingSelectedItemTemplateDirective {
|
|
|
57
63
|
constructor() {
|
|
58
64
|
this.templateRef = inject(TemplateRef);
|
|
59
65
|
}
|
|
60
|
-
static ngTemplateContextGuard(_directive,
|
|
66
|
+
static ngTemplateContextGuard(_directive, _context) {
|
|
61
67
|
return true;
|
|
62
68
|
}
|
|
63
69
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: RatingSelectedItemTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -263,6 +269,8 @@ const ratingIcons = {
|
|
|
263
269
|
};
|
|
264
270
|
class RatingComponent {
|
|
265
271
|
constructor() {
|
|
272
|
+
this.#direction = injectComponentDirection();
|
|
273
|
+
this.#i18n = inject(MonaI18nService);
|
|
266
274
|
this.activeState = computed(() => this.previewActive() ? "hovered" : "selected", /* @ts-ignore */
|
|
267
275
|
...(ngDevMode ? [{ debugName: "activeState" }] : /* istanbul ignore next */ []));
|
|
268
276
|
this.containerClasses = computed(() => twMerge(ratingContainerThemeVariants(), this.userClass()), /* @ts-ignore */
|
|
@@ -288,7 +296,9 @@ class RatingComponent {
|
|
|
288
296
|
if (custom) {
|
|
289
297
|
return custom(this.normalizedValue(), this.itemsCount());
|
|
290
298
|
}
|
|
291
|
-
return this.normalizedValue() === 0
|
|
299
|
+
return this.normalizedValue() === 0
|
|
300
|
+
? this.messages().notRated
|
|
301
|
+
: this.messages().valueText(this.normalizedValue(), this.itemsCount());
|
|
292
302
|
}, /* @ts-ignore */
|
|
293
303
|
...(ngDevMode ? [{ debugName: "effectiveAriaValueText" }] : /* istanbul ignore next */ []));
|
|
294
304
|
this.effectiveTabIndex = computed(() => (this.disabled() ? -1 : this.tabindex()), /* @ts-ignore */
|
|
@@ -304,6 +314,8 @@ class RatingComponent {
|
|
|
304
314
|
...(ngDevMode ? [{ debugName: "interactionStep" }] : /* istanbul ignore next */ []));
|
|
305
315
|
this.invalidState = computed(() => this.touched() && this.invalid(), /* @ts-ignore */
|
|
306
316
|
...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
|
|
317
|
+
this.isRtl = computed(() => this.#direction() === "rtl", /* @ts-ignore */
|
|
318
|
+
...(ngDevMode ? [{ debugName: "isRtl" }] : /* istanbul ignore next */ []));
|
|
307
319
|
this.itemClasses = computed(() => ratingItemThemeVariants({ size: this.size() }), /* @ts-ignore */
|
|
308
320
|
...(ngDevMode ? [{ debugName: "itemClasses" }] : /* istanbul ignore next */ []));
|
|
309
321
|
this.itemTemplate = contentChild(RatingItemTemplateDirective, { ...(ngDevMode ? { debugName: "itemTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
|
|
@@ -314,6 +326,7 @@ class RatingComponent {
|
|
|
314
326
|
this.labelClasses = computed(() => ratingLabelThemeVariants({ size: this.size() }), /* @ts-ignore */
|
|
315
327
|
...(ngDevMode ? [{ debugName: "labelClasses" }] : /* istanbul ignore next */ []));
|
|
316
328
|
this.labelId = createElementControlId();
|
|
329
|
+
this.messages = this.#i18n.componentMessages("rating", RATING_DEFAULT_MESSAGES);
|
|
317
330
|
this.normalizedValue = computed(() => normalizeRatingValue(this.value(), this.itemsCount(), this.precision()), /* @ts-ignore */
|
|
318
331
|
...(ngDevMode ? [{ debugName: "normalizedValue" }] : /* istanbul ignore next */ []));
|
|
319
332
|
this.overlayClipClasses = computed(() => ratingOverlayClipThemeVariants(), /* @ts-ignore */
|
|
@@ -459,6 +472,8 @@ class RatingComponent {
|
|
|
459
472
|
this.value = model(0, /* @ts-ignore */
|
|
460
473
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
461
474
|
}
|
|
475
|
+
#direction;
|
|
476
|
+
#i18n;
|
|
462
477
|
blur() {
|
|
463
478
|
this.controlElement().nativeElement.blur();
|
|
464
479
|
}
|
|
@@ -564,11 +579,14 @@ class RatingComponent {
|
|
|
564
579
|
});
|
|
565
580
|
}
|
|
566
581
|
getKeyAction(key) {
|
|
582
|
+
const isRtl = this.isRtl();
|
|
583
|
+
const decreaseKey = isRtl ? "ArrowRight" : "ArrowLeft";
|
|
584
|
+
const increaseKey = isRtl ? "ArrowLeft" : "ArrowRight";
|
|
567
585
|
switch (key) {
|
|
568
|
-
case
|
|
586
|
+
case increaseKey:
|
|
569
587
|
case "ArrowUp":
|
|
570
588
|
return "increase";
|
|
571
|
-
case
|
|
589
|
+
case decreaseKey:
|
|
572
590
|
case "ArrowDown":
|
|
573
591
|
return "decrease";
|
|
574
592
|
case "Home":
|
|
@@ -602,5 +620,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
602
620
|
* Generated bundle index. Do not edit.
|
|
603
621
|
*/
|
|
604
622
|
|
|
605
|
-
export { RatingComponent, RatingHoveredItemTemplateDirective, RatingItemTemplateDirective, RatingSelectedItemTemplateDirective };
|
|
623
|
+
export { RATING_DEFAULT_MESSAGES, RatingComponent, RatingHoveredItemTemplateDirective, RatingItemTemplateDirective, RatingSelectedItemTemplateDirective };
|
|
606
624
|
//# sourceMappingURL=nanahoshi-mona-ui-rating.mjs.map
|