@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.
Files changed (112) hide show
  1. package/README.md +71 -13
  2. package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +11 -3
  3. package/fesm2022/nanahoshi-mona-ui-breadcrumb.mjs +17 -9
  4. package/fesm2022/nanahoshi-mona-ui-button-group.mjs +16 -7
  5. package/fesm2022/nanahoshi-mona-ui-calendar.mjs +122 -48
  6. package/fesm2022/nanahoshi-mona-ui-card.mjs +22 -14
  7. package/fesm2022/nanahoshi-mona-ui-chart.mjs +78 -18
  8. package/fesm2022/nanahoshi-mona-ui-check-box.mjs +1 -1
  9. package/fesm2022/nanahoshi-mona-ui-chip.mjs +199 -77
  10. package/fesm2022/nanahoshi-mona-ui-circular-progress-bar.mjs +1 -2
  11. package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +23 -5
  12. package/fesm2022/nanahoshi-mona-ui-color-palette.mjs +49 -28
  13. package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +42 -26
  14. package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +11 -3
  15. package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +28 -16
  16. package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +74 -51
  17. package/fesm2022/nanahoshi-mona-ui-dialog.mjs +57 -25
  18. package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +13 -5
  19. package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +17 -6
  20. package/fesm2022/nanahoshi-mona-ui-editor.mjs +178 -59
  21. package/fesm2022/nanahoshi-mona-ui-filter.mjs +137 -54
  22. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-cell.component-WEDbZ5vy.mjs → nanahoshi-mona-ui-grid-grid-footer-cell.component-Bg7rckst.mjs} +2 -2
  23. 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
  24. package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-D9AwQFDV.mjs +145 -0
  25. package/fesm2022/{nanahoshi-mona-ui-grid-grid-toggle.component-hc-LeIb0.mjs → nanahoshi-mona-ui-grid-grid-toggle.component-Byb9iTes.mjs} +21 -17
  26. package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-DYajpOb9.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-CBmdtrXm.mjs} +5 -5
  27. package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Pm8Fbp6M.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-DPmJmCap.mjs} +90 -42
  28. package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
  29. package/fesm2022/nanahoshi-mona-ui-i18n.mjs +1168 -0
  30. package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +12 -4
  31. package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +48 -21
  32. package/fesm2022/nanahoshi-mona-ui-list-box.mjs +33 -18
  33. package/fesm2022/nanahoshi-mona-ui-list-view.mjs +10 -10
  34. package/fesm2022/nanahoshi-mona-ui-list.mjs +2 -0
  35. package/fesm2022/nanahoshi-mona-ui-menubar.mjs +29 -16
  36. package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +12 -3
  37. package/fesm2022/nanahoshi-mona-ui-notification.mjs +23 -18
  38. package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +211 -106
  39. package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +14 -6
  40. package/fesm2022/nanahoshi-mona-ui-pager.mjs +46 -19
  41. package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +57 -46
  42. package/fesm2022/nanahoshi-mona-ui-popup.mjs +35 -15
  43. package/fesm2022/nanahoshi-mona-ui-progress-bar.mjs +38 -17
  44. package/fesm2022/nanahoshi-mona-ui-rating.mjs +25 -7
  45. package/fesm2022/nanahoshi-mona-ui-scroll-view.mjs +431 -101
  46. package/fesm2022/nanahoshi-mona-ui-segmented.mjs +1 -1
  47. package/fesm2022/nanahoshi-mona-ui-sheet.mjs +26 -8
  48. package/fesm2022/nanahoshi-mona-ui-sidebar.mjs +222 -28
  49. package/fesm2022/nanahoshi-mona-ui-slider.mjs +91 -36
  50. package/fesm2022/nanahoshi-mona-ui-spinner.mjs +34 -15
  51. package/fesm2022/nanahoshi-mona-ui-split-button.mjs +39 -23
  52. package/fesm2022/nanahoshi-mona-ui-splitter.mjs +50 -25
  53. package/fesm2022/nanahoshi-mona-ui-stepper.mjs +32 -14
  54. package/fesm2022/nanahoshi-mona-ui-switch.mjs +9 -9
  55. package/fesm2022/nanahoshi-mona-ui-tabs.mjs +74 -56
  56. package/fesm2022/nanahoshi-mona-ui-text-box.mjs +16 -8
  57. package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +33 -19
  58. package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +66 -27
  59. package/fesm2022/nanahoshi-mona-ui-tree-view.mjs +23 -15
  60. package/fesm2022/nanahoshi-mona-ui-window.mjs +71 -40
  61. package/i18n/package.json +4 -0
  62. package/package.json +5 -1
  63. package/types/nanahoshi-mona-ui-auto-complete.d.ts +6 -1
  64. package/types/nanahoshi-mona-ui-breadcrumb.d.ts +9 -3
  65. package/types/nanahoshi-mona-ui-button-group.d.ts +9 -3
  66. package/types/nanahoshi-mona-ui-calendar.d.ts +13 -3
  67. package/types/nanahoshi-mona-ui-card.d.ts +8 -2
  68. package/types/nanahoshi-mona-ui-chart.d.ts +10 -1
  69. package/types/nanahoshi-mona-ui-chip.d.ts +14 -5
  70. package/types/nanahoshi-mona-ui-color-gradient.d.ts +6 -1
  71. package/types/nanahoshi-mona-ui-color-palette.d.ts +21 -10
  72. package/types/nanahoshi-mona-ui-color-picker.d.ts +30 -20
  73. package/types/nanahoshi-mona-ui-combo-box.d.ts +6 -1
  74. package/types/nanahoshi-mona-ui-date-picker.d.ts +7 -2
  75. package/types/nanahoshi-mona-ui-datetime-picker.d.ts +33 -8
  76. package/types/nanahoshi-mona-ui-dialog.d.ts +16 -7
  77. package/types/nanahoshi-mona-ui-dropdown-list.d.ts +6 -1
  78. package/types/nanahoshi-mona-ui-dropdowns.d.ts +4 -1
  79. package/types/nanahoshi-mona-ui-editor.d.ts +6 -1
  80. package/types/nanahoshi-mona-ui-filter.d.ts +16 -10
  81. package/types/nanahoshi-mona-ui-grid.d.ts +38 -1
  82. package/types/nanahoshi-mona-ui-i18n.d.ts +633 -0
  83. package/types/nanahoshi-mona-ui-internal-list.d.ts +7 -2
  84. package/types/nanahoshi-mona-ui-internal-tree.d.ts +7 -3
  85. package/types/nanahoshi-mona-ui-list-box.d.ts +9 -4
  86. package/types/nanahoshi-mona-ui-list-view.d.ts +6 -6
  87. package/types/nanahoshi-mona-ui-list.d.ts +1 -1
  88. package/types/nanahoshi-mona-ui-menubar.d.ts +59 -58
  89. package/types/nanahoshi-mona-ui-multi-select.d.ts +6 -1
  90. package/types/nanahoshi-mona-ui-notification.d.ts +11 -5
  91. package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +24 -15
  92. package/types/nanahoshi-mona-ui-otp-input.d.ts +9 -3
  93. package/types/nanahoshi-mona-ui-pager.d.ts +19 -6
  94. package/types/nanahoshi-mona-ui-popup.d.ts +2 -1
  95. package/types/nanahoshi-mona-ui-progress-bar.d.ts +9 -7
  96. package/types/nanahoshi-mona-ui-rating.d.ts +19 -12
  97. package/types/nanahoshi-mona-ui-scroll-view.d.ts +34 -28
  98. package/types/nanahoshi-mona-ui-sheet.d.ts +8 -2
  99. package/types/nanahoshi-mona-ui-sidebar.d.ts +3 -0
  100. package/types/nanahoshi-mona-ui-slider.d.ts +44 -26
  101. package/types/nanahoshi-mona-ui-spinner.d.ts +13 -4
  102. package/types/nanahoshi-mona-ui-split-button.d.ts +62 -56
  103. package/types/nanahoshi-mona-ui-splitter.d.ts +9 -6
  104. package/types/nanahoshi-mona-ui-stepper.d.ts +9 -3
  105. package/types/nanahoshi-mona-ui-switch.d.ts +1 -1
  106. package/types/nanahoshi-mona-ui-tabs.d.ts +96 -35
  107. package/types/nanahoshi-mona-ui-text-box.d.ts +11 -5
  108. package/types/nanahoshi-mona-ui-time-picker.d.ts +7 -2
  109. package/types/nanahoshi-mona-ui-time-selector.d.ts +16 -5
  110. package/types/nanahoshi-mona-ui-tree-view.d.ts +15 -10
  111. package/types/nanahoshi-mona-ui-window.d.ts +26 -20
  112. package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-ZM3CneJ0.mjs +0 -145
@@ -96,7 +96,7 @@ const segmentedOptionThemeVariants = cva(`
96
96
  }
97
97
  });
98
98
  const segmentedIndicatorThemeVariants = cva(`
99
- pointer-events-none absolute top-0 left-0
99
+ pointer-events-none absolute top-0 start-0
100
100
  bg-primary shadow-sm ring-1 ring-selected-border
101
101
  data-[disabled='true']:opacity-50
102
102
  motion-reduce:transition-none
@@ -6,12 +6,17 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
6
  import { LucideX } from '@lucide/angular';
7
7
  import { ButtonDirective } from '@nanahoshi/mona-ui/button';
8
8
  import { classInputToClass } from '@nanahoshi/mona-ui/common';
9
+ import { MonaI18nService, mergeMessages } from '@nanahoshi/mona-ui/i18n';
9
10
  import { themeOverlaySurfaceClasses, createElementControlId } from '@nanahoshi/mona-ui/internal';
10
11
  import { PopupService, PopupCloseEvent, PopupCloseSource, slideFromLeftPopupAnimation, slideFromBottomPopupAnimation, slideFromRightPopupAnimation, slideFromTopPopupAnimation } from '@nanahoshi/mona-ui/popup';
11
12
  import { take } from 'rxjs';
12
13
  import { twMerge } from 'tailwind-merge';
13
14
  import { cva } from 'class-variance-authority';
14
15
 
16
+ const SHEET_DEFAULT_MESSAGES = {
17
+ closeSheet: "Close sheet"
18
+ };
19
+
15
20
  /**
16
21
  * `overflow-clip` rather than `overflow-hidden` is deliberate. `hidden` would make the sheet a scroll
17
22
  * container, letting the browser scroll it to reveal content that takes focus while the enter animation
@@ -19,17 +24,18 @@ import { cva } from 'class-variance-authority';
19
24
  */
20
25
  const sheetBaseVariants = cva(`
21
26
  relative flex h-full w-full min-w-0 min-h-0 max-h-[inherit] flex-col overflow-clip
22
- pt-[env(safe-area-inset-top)] pr-[env(safe-area-inset-right)]
23
- pb-[env(safe-area-inset-bottom)] pl-[env(safe-area-inset-left)]
27
+ pt-[env(safe-area-inset-top)] pe-[env(safe-area-inset-right)]
28
+ pb-[env(safe-area-inset-bottom)] ps-[env(safe-area-inset-left)]
29
+ rtl:pe-[env(safe-area-inset-left)] rtl:ps-[env(safe-area-inset-right)]
24
30
  ${themeOverlaySurfaceClasses} text-foreground
25
31
  shadow-(--shadow-overlay) outline-none
26
32
  `, {
27
33
  variants: {
28
34
  side: {
29
35
  top: "border-b border-border",
30
- right: "border-l border-border",
36
+ right: "border-s border-border rtl:border-e rtl:border-s-0",
31
37
  bottom: "border-t border-border",
32
- left: "border-r border-border"
38
+ left: "border-e border-border rtl:border-s rtl:border-e-0"
33
39
  }
34
40
  },
35
41
  defaultVariants: {
@@ -49,7 +55,9 @@ const sheetCloseButtonVariants = cva(`min-h-11 min-w-11 shrink-0`);
49
55
  class SheetComponent {
50
56
  #destroyRef;
51
57
  #document;
58
+ #i18n;
52
59
  #injector;
60
+ #localeMessages;
53
61
  #popupService;
54
62
  #destroyed;
55
63
  #horizontal;
@@ -57,7 +65,9 @@ class SheetComponent {
57
65
  constructor() {
58
66
  this.#destroyRef = inject(DestroyRef);
59
67
  this.#document = inject(DOCUMENT);
68
+ this.#i18n = inject(MonaI18nService);
60
69
  this.#injector = inject(Injector);
70
+ this.#localeMessages = this.#i18n.componentMessages("sheet", SHEET_DEFAULT_MESSAGES);
61
71
  this.#popupService = inject(PopupService);
62
72
  this.#destroyed = false;
63
73
  this.#horizontal = false;
@@ -78,6 +88,10 @@ class SheetComponent {
78
88
  this.descriptionId = createElementControlId();
79
89
  this.headerClass = computed(() => sheetHeaderVariants(), /* @ts-ignore */
80
90
  ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
91
+ this.resolvedMessages = computed(() => {
92
+ return mergeMessages(this.#localeMessages(), this.messages());
93
+ }, /* @ts-ignore */
94
+ ...(ngDevMode ? [{ debugName: "resolvedMessages" }] : /* istanbul ignore next */ []));
81
95
  this.titleClass = computed(() => sheetTitleVariants(), /* @ts-ignore */
82
96
  ...(ngDevMode ? [{ debugName: "titleClass" }] : /* istanbul ignore next */ []));
83
97
  this.titleId = createElementControlId();
@@ -88,6 +102,7 @@ class SheetComponent {
88
102
  this.closable = input(true, /* @ts-ignore */
89
103
  ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
90
104
  /** @description Emitted before the sheet closes. Call preventDefault() to keep it open. */
105
+ // eslint-disable-next-line @angular-eslint/no-output-native
91
106
  this.close = output();
92
107
  /** @description Whether clicking the backdrop requests that the sheet close. @default true */
93
108
  this.closeOnBackdropClick = input(true, /* @ts-ignore */
@@ -103,6 +118,9 @@ class SheetComponent {
103
118
  /** @description Explicit sheet height. Numbers are interpreted as pixels. */
104
119
  this.height = input(/* @ts-ignore */
105
120
  ...(ngDevMode ? [undefined, { debugName: "height" }] : /* istanbul ignore next */ []));
121
+ /** @description Internationalization message overrides for accessible labels. */
122
+ this.messages = input(/* @ts-ignore */
123
+ ...(ngDevMode ? [undefined, { debugName: "messages" }] : /* istanbul ignore next */ []));
106
124
  /** @description Edge of the viewport from which the sheet opens. @default "right" */
107
125
  this.side = input("right", /* @ts-ignore */
108
126
  ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
@@ -225,14 +243,14 @@ class SheetComponent {
225
243
  });
226
244
  }
227
245
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
228
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SheetComponent, isStandalone: true, selector: "mona-sheet", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", closed: "closed" }, host: { classAttribute: "hidden" }, viewQueries: [{ propertyName: "sheetTemplate", first: true, predicate: ["sheetTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #sheetTemplate>\n <section\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [class]=\"baseClass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n [attr.aria-label]=\"!title() ? ariaLabel() : null\">\n @if (title() || description() || closable()) {\n <header [class]=\"headerClass()\">\n <div class=\"min-w-0 flex-1\">\n @if (title()) {\n <h2 [id]=\"titleId\" [class]=\"titleClass()\">{{ title() }}</h2>\n }\n @if (description()) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass()\">{{ description() }}</p>\n }\n </div>\n @if (closable()) {\n <button\n monaButton\n type=\"button\"\n look=\"ghost\"\n [iconOnly]=\"true\"\n aria-label=\"Close sheet\"\n size=\"small\"\n [class]=\"closeButtonClass()\"\n (click)=\"closeSheet()\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </header>\n }\n <div [class]=\"contentClass()\">\n <ng-content></ng-content>\n </div>\n </section>\n</ng-template>\n", dependencies: [{ kind: "directive", type: ButtonDirective, selector: "button[monaButton]", inputs: ["aria-haspopup", "disabled", "iconOnly", "loading", "look", "rounded", "selected", "size", "tabindex", "type", "toggleable", "class"], outputs: ["disabledChange", "loadingChange", "lookChange", "selectedChange"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] }); }
246
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SheetComponent, isStandalone: true, selector: "mona-sheet", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", closed: "closed" }, host: { classAttribute: "hidden" }, viewQueries: [{ propertyName: "sheetTemplate", first: true, predicate: ["sheetTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #sheetTemplate>\n <section\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [class]=\"baseClass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n [attr.aria-label]=\"!title() ? ariaLabel() : null\">\n @if (title() || description() || closable()) {\n <header [class]=\"headerClass()\">\n <div class=\"min-w-0 flex-1\">\n @if (title()) {\n <h2 [id]=\"titleId\" [class]=\"titleClass()\">{{ title() }}</h2>\n }\n @if (description()) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass()\">{{ description() }}</p>\n }\n </div>\n @if (closable()) {\n <button\n monaButton\n type=\"button\"\n look=\"ghost\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"resolvedMessages().closeSheet\"\n size=\"small\"\n [class]=\"closeButtonClass()\"\n (click)=\"closeSheet()\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </header>\n }\n <div [class]=\"contentClass()\">\n <ng-content></ng-content>\n </div>\n </section>\n</ng-template>\n", dependencies: [{ kind: "directive", type: ButtonDirective, selector: "button[monaButton]", inputs: ["aria-haspopup", "disabled", "iconOnly", "loading", "look", "rounded", "selected", "size", "tabindex", "type", "toggleable", "class"], outputs: ["disabledChange", "loadingChange", "lookChange", "selectedChange"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] }); }
229
247
  }
230
248
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SheetComponent, decorators: [{
231
249
  type: Component,
232
250
  args: [{ selector: "mona-sheet", imports: [ButtonDirective, CdkTrapFocus, LucideX], host: {
233
251
  class: "hidden"
234
- }, template: "<ng-template #sheetTemplate>\n <section\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [class]=\"baseClass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n [attr.aria-label]=\"!title() ? ariaLabel() : null\">\n @if (title() || description() || closable()) {\n <header [class]=\"headerClass()\">\n <div class=\"min-w-0 flex-1\">\n @if (title()) {\n <h2 [id]=\"titleId\" [class]=\"titleClass()\">{{ title() }}</h2>\n }\n @if (description()) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass()\">{{ description() }}</p>\n }\n </div>\n @if (closable()) {\n <button\n monaButton\n type=\"button\"\n look=\"ghost\"\n [iconOnly]=\"true\"\n aria-label=\"Close sheet\"\n size=\"small\"\n [class]=\"closeButtonClass()\"\n (click)=\"closeSheet()\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </header>\n }\n <div [class]=\"contentClass()\">\n <ng-content></ng-content>\n </div>\n </section>\n</ng-template>\n" }]
235
- }], ctorParameters: () => [], propDecorators: { sheetTemplate: [{ type: i0.ViewChild, args: ["sheetTemplate", { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }] } });
252
+ }, template: "<ng-template #sheetTemplate>\n <section\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n [class]=\"baseClass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n [attr.aria-label]=\"!title() ? ariaLabel() : null\">\n @if (title() || description() || closable()) {\n <header [class]=\"headerClass()\">\n <div class=\"min-w-0 flex-1\">\n @if (title()) {\n <h2 [id]=\"titleId\" [class]=\"titleClass()\">{{ title() }}</h2>\n }\n @if (description()) {\n <p [id]=\"descriptionId\" [class]=\"descriptionClass()\">{{ description() }}</p>\n }\n </div>\n @if (closable()) {\n <button\n monaButton\n type=\"button\"\n look=\"ghost\"\n [iconOnly]=\"true\"\n [attr.aria-label]=\"resolvedMessages().closeSheet\"\n size=\"small\"\n [class]=\"closeButtonClass()\"\n (click)=\"closeSheet()\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </header>\n }\n <div [class]=\"contentClass()\">\n <ng-content></ng-content>\n </div>\n </section>\n</ng-template>\n" }]
253
+ }], ctorParameters: () => [], propDecorators: { sheetTemplate: [{ type: i0.ViewChild, args: ["sheetTemplate", { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }] } });
236
254
 
237
255
  /*
238
256
  * Public API Surface of @nanahoshi/mona-ui/sheet
@@ -242,5 +260,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
242
260
  * Generated bundle index. Do not edit.
243
261
  */
244
262
 
245
- export { SheetComponent };
263
+ export { SHEET_DEFAULT_MESSAGES, SheetComponent };
246
264
  //# sourceMappingURL=nanahoshi-mona-ui-sheet.mjs.map
@@ -3,6 +3,7 @@ import { CdkTrapFocus } from '@angular/cdk/a11y';
3
3
  import * as i0 from '@angular/core';
4
4
  import { signal, computed, Service, InjectionToken, inject, PLATFORM_ID, linkedSignal, effect, untracked, ElementRef, Injector, input, model, afterNextRender, ChangeDetectionStrategy, Component, DestroyRef, Directive, makeEnvironmentProviders } from '@angular/core';
5
5
  import { classInputToClass } from '@nanahoshi/mona-ui/common';
6
+ import { injectComponentDirection } from '@nanahoshi/mona-ui/i18n';
6
7
  import { twMerge } from 'tailwind-merge';
7
8
  import { createElementControlId, themeRaisedBackdropClasses } from '@nanahoshi/mona-ui/internal';
8
9
  import { isPlatformBrowser } from '@angular/common';
@@ -410,9 +411,19 @@ const sidebarBorderAllowance = Object.freeze({
410
411
  * split pane — overlays only that region, and a layout that fills the page behaves identically.
411
412
  */
412
413
  const sidebarLayoutBaseThemeVariants = cva(`
413
- relative flex flex-row w-full h-screen h-dvh
414
+ relative flex w-full h-screen h-dvh
414
415
  overflow-hidden
415
- `);
416
+ `, {
417
+ variants: {
418
+ reverse: {
419
+ true: "flex-row-reverse",
420
+ false: "flex-row"
421
+ }
422
+ },
423
+ defaultVariants: {
424
+ reverse: false
425
+ }
426
+ });
416
427
  const sidebarContentThemeVariants = cva("flex-1", {
417
428
  variants: {
418
429
  iconOnly: {
@@ -615,13 +626,9 @@ const sidebarThemeVariants = cva(`
615
626
  motion-reduce:transition-none
616
627
  `, {
617
628
  variants: {
618
- // `side` only decides placement; the border it implies is applied per variant below,
619
- // so the `floating` and `inset` treatments are not left fighting a single edge border.
620
- // Flex order is already direction-relative, which is why the borders below have to be
621
- // logical too — a physical `border-r` would land on the wrong edge under RTL.
622
- side: {
623
- start: "order-first",
624
- end: "order-last"
629
+ physicalSide: {
630
+ left: "order-first",
631
+ right: "order-last"
625
632
  },
626
633
  variant: {
627
634
  sidebar: `h-full ${themeRaisedBackdropClasses}`,
@@ -641,10 +648,10 @@ const sidebarThemeVariants = cva(`
641
648
  false: ""
642
649
  },
643
650
  /**
644
- * The compact presentation. The panel leaves the flex flow and becomes an overlay pinned to
645
- * one edge, sliding in on `translate` rather than `width` — a drawer that animated its width
646
- * would reflow its own contents for the whole transition.
647
- */
651
+ * The compact presentation. The panel leaves the flex flow and becomes an overlay pinned to
652
+ * one edge, sliding in on `translate` rather than `width` — a drawer that animated its width
653
+ * would reflow its own contents for the whole transition.
654
+ */
648
655
  drawer: {
649
656
  true: `
650
657
  absolute inset-y-0 z-50 h-full m-0
@@ -660,19 +667,22 @@ const sidebarThemeVariants = cva(`
660
667
  }
661
668
  },
662
669
  compoundVariants: [
663
- { drawer: false, variant: "sidebar", side: "start", class: "border-e border-(--color-sidebar-border)" },
664
- { drawer: false, variant: "sidebar", side: "end", class: "border-s border-(--color-sidebar-border)" },
670
+ { drawer: false, variant: "sidebar", physicalSide: "left", class: "border-r border-(--color-sidebar-border)" },
671
+ { drawer: false, variant: "sidebar", physicalSide: "right", class: "border-l border-(--color-sidebar-border)" },
665
672
  { drawer: false, variant: "floating", flush: true, class: "h-full" },
666
673
  { drawer: false, variant: "inset", flush: true, class: "h-full" },
667
674
  // A drawer is a surface in its own right whatever the docked variant looks like, so the
668
675
  // variant's margins, rounding and transparency are all dropped for it.
669
- { drawer: true, side: "start", class: "start-0" },
670
- { drawer: true, side: "end", class: "end-0" },
671
- { drawer: true, open: false, side: "start", class: "-translate-x-full" },
672
- { drawer: true, open: false, side: "end", class: "translate-x-full" },
676
+ { drawer: true, physicalSide: "left", class: "left-0" },
677
+ { drawer: true, physicalSide: "right", class: "right-0" },
678
+ { drawer: true, open: false, physicalSide: "left", class: "-translate-x-full" },
679
+ { drawer: true, open: false, physicalSide: "right", class: "translate-x-full" },
673
680
  { drawer: true, variant: "floating", class: "rounded-none" },
674
681
  { drawer: true, variant: "inset", class: "bg-(--color-sidebar)" }
675
- ]
682
+ ],
683
+ defaultVariants: {
684
+ physicalSide: "left"
685
+ }
676
686
  });
677
687
  /**
678
688
  * The scrim behind an open drawer. Fades rather than sliding, so it is not competing with the panel's
@@ -713,12 +723,15 @@ const sidebarRailThemeVariants = cva(`
713
723
  sm:flex
714
724
  `, {
715
725
  variants: {
716
- // The rail sits on the sidebar's inner edge, which is the opposite one to the side it is
717
- // docked against. Logical, so it stays on the inner edge under RTL.
718
- side: {
719
- start: "end-0",
720
- end: "start-0"
726
+ // The rail sits on the sidebar's inner edge, which is the opposite one to the physical side it is
727
+ // docked against.
728
+ physicalSide: {
729
+ left: "right-0",
730
+ right: "left-0"
721
731
  }
732
+ },
733
+ defaultVariants: {
734
+ physicalSide: "left"
722
735
  }
723
736
  });
724
737
  const sidebarSeparatorThemeVariants = cva(`
@@ -745,6 +758,7 @@ const sidebarSeparatorThemeVariants = cva(`
745
758
  * `role` and `ariaLabel` if the whole region is navigation.
746
759
  */
747
760
  class SidebarComponent {
761
+ #direction;
748
762
  #focusTrap;
749
763
  #host;
750
764
  #injector;
@@ -752,17 +766,28 @@ class SidebarComponent {
752
766
  #sidebarService;
753
767
  #restoreFocusTo;
754
768
  constructor() {
769
+ this.#direction = injectComponentDirection();
755
770
  this.#focusTrap = inject(CdkTrapFocus);
756
771
  this.#host = inject(ElementRef);
757
772
  this.#injector = inject(Injector);
758
773
  this.#layoutService = inject(SidebarLayoutService, { optional: true });
759
774
  this.#sidebarService = inject(SidebarService);
760
775
  this.#restoreFocusTo = null;
776
+ this.physicalSide = computed(() => {
777
+ const isRtl = this.#direction() === "rtl";
778
+ const side = this.#sidebarService.side();
779
+ if (side === "start") {
780
+ return isRtl ? "right" : "left";
781
+ }
782
+ return isRtl ? "left" : "right";
783
+ }, /* @ts-ignore */
784
+ ...(ngDevMode ? [{ debugName: "physicalSide" }] : /* istanbul ignore next */ []));
785
+ this.drawerPhysicalSide = this.physicalSide;
761
786
  this.baseClass = computed(() => {
762
787
  const variantClass = sidebarThemeVariants({
763
788
  drawer: this.drawer(),
789
+ physicalSide: this.physicalSide(),
764
790
  open: this.#sidebarService.mobileOpen(),
765
- side: this.#sidebarService.side(),
766
791
  variant: this.variant(),
767
792
  flush: this.offCanvasClosed()
768
793
  });
@@ -1010,6 +1035,40 @@ function toCssLength(value) {
1010
1035
  return typeof value === "number" ? `${value}px` : value;
1011
1036
  }
1012
1037
 
1038
+ function resolveSidebarLayoutReverse(options) {
1039
+ const effectiveDirection = options.browserCssDirection ?? options.semanticDirection;
1040
+ return effectiveDirection === "rtl";
1041
+ }
1042
+ function resolveSidebarLayoutBaseClass(options) {
1043
+ const reverse = resolveSidebarLayoutReverse(options);
1044
+ return twMerge(sidebarLayoutBaseThemeVariants({ reverse }), options.userClass ?? "");
1045
+ }
1046
+
1047
+ function getComposedAncestors(startElement) {
1048
+ const chain = [];
1049
+ let current = startElement;
1050
+ while (current) {
1051
+ if (current instanceof Element) {
1052
+ chain.push(current);
1053
+ }
1054
+ if (current.parentElement) {
1055
+ current = current.parentElement;
1056
+ }
1057
+ else if (current.parentNode) {
1058
+ const parent = current.parentNode;
1059
+ if ("host" in parent && parent.host instanceof Element) {
1060
+ current = parent.host;
1061
+ }
1062
+ else {
1063
+ current = parent;
1064
+ }
1065
+ }
1066
+ else {
1067
+ break;
1068
+ }
1069
+ }
1070
+ return chain;
1071
+ }
1013
1072
  /**
1014
1073
  * @description
1015
1074
  * Lays its children out in a row and owns what they share. Project one or more `mona-sidebar`
@@ -1023,15 +1082,28 @@ function toCssLength(value) {
1023
1082
  * closes any other, and they share the single backdrop this layout paints behind them.
1024
1083
  */
1025
1084
  class SidebarLayoutComponent {
1085
+ #destroyRef;
1086
+ #direction;
1087
+ #host;
1026
1088
  #layoutService;
1027
1089
  #matches;
1090
+ #browserCssDirection;
1028
1091
  constructor() {
1092
+ this.#destroyRef = inject(DestroyRef);
1093
+ this.#direction = injectComponentDirection();
1094
+ this.#host = inject(ElementRef);
1029
1095
  this.#layoutService = inject(SidebarLayoutService);
1030
1096
  this.#matches = signal(false, /* @ts-ignore */
1031
1097
  ...(ngDevMode ? [{ debugName: "#matches" }] : /* istanbul ignore next */ []));
1098
+ this.#browserCssDirection = signal(null, /* @ts-ignore */
1099
+ ...(ngDevMode ? [{ debugName: "#browserCssDirection" }] : /* istanbul ignore next */ []));
1032
1100
  this.backdropClass = computed(() => sidebarBackdropThemeVariants({ open: this.mobileOpen() }), /* @ts-ignore */
1033
1101
  ...(ngDevMode ? [{ debugName: "backdropClass" }] : /* istanbul ignore next */ []));
1034
- this.baseClass = computed(() => twMerge(sidebarLayoutBaseThemeVariants(), this.userClass()), /* @ts-ignore */
1102
+ this.baseClass = computed(() => resolveSidebarLayoutBaseClass({
1103
+ semanticDirection: this.#direction(),
1104
+ browserCssDirection: this.#browserCssDirection(),
1105
+ userClass: this.userClass()
1106
+ }), /* @ts-ignore */
1035
1107
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
1036
1108
  this.compact = this.#layoutService.compact;
1037
1109
  /** One backdrop serves every sidebar, because only one of their drawers is ever open. */
@@ -1050,6 +1122,7 @@ class SidebarLayoutComponent {
1050
1122
  this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class",
1051
1123
  transform: value => classInputToClass(value) });
1052
1124
  this.#watchBreakpoint();
1125
+ this.#watchCssDirection();
1053
1126
  }
1054
1127
  onBackdropClick() {
1055
1128
  this.#layoutService.closeAll();
@@ -1097,6 +1170,116 @@ class SidebarLayoutComponent {
1097
1170
  });
1098
1171
  });
1099
1172
  }
1173
+ /**
1174
+ * CSS direction compensation contract:
1175
+ * Reads computed CSS `direction` to compensate flex-row orientation so that semantic
1176
+ * start/end docking matches physical left/right edges.
1177
+ * Reactive updates are supported for:
1178
+ * 1. Host and composed ancestor `class`, `style`, and `dir` mutations (including through `ShadowRoot` boundaries);
1179
+ * 2. Viewport-responsive media query threshold changes via window resize.
1180
+ * Dynamic modification of arbitrary stylesheet rules or adoptedStyleSheets without attribute or resize triggers is out of scope.
1181
+ */
1182
+ #watchCssDirection() {
1183
+ let updateAncestorObservers = () => { };
1184
+ const updateCssDirection = () => {
1185
+ if (typeof window === "undefined") {
1186
+ this.#browserCssDirection.set(null);
1187
+ return;
1188
+ }
1189
+ const el = this.#host.nativeElement;
1190
+ if (!el) {
1191
+ this.#browserCssDirection.set(null);
1192
+ return;
1193
+ }
1194
+ try {
1195
+ const computedDir = window.getComputedStyle(el).direction;
1196
+ if (computedDir === "rtl" || computedDir === "ltr") {
1197
+ this.#browserCssDirection.set(computedDir);
1198
+ }
1199
+ else {
1200
+ this.#browserCssDirection.set(null);
1201
+ }
1202
+ }
1203
+ catch {
1204
+ this.#browserCssDirection.set(null);
1205
+ }
1206
+ updateAncestorObservers();
1207
+ };
1208
+ updateCssDirection();
1209
+ effect(() => {
1210
+ this.#direction();
1211
+ updateCssDirection();
1212
+ });
1213
+ afterNextRender(() => {
1214
+ updateCssDirection();
1215
+ });
1216
+ if (typeof MutationObserver !== "undefined") {
1217
+ let observedElements = new Set();
1218
+ const observer = new MutationObserver(() => {
1219
+ updateCssDirection();
1220
+ });
1221
+ updateAncestorObservers = () => {
1222
+ const el = this.#host.nativeElement;
1223
+ if (!el) {
1224
+ return;
1225
+ }
1226
+ const currentAncestors = getComposedAncestors(el);
1227
+ const nextSet = new Set(currentAncestors);
1228
+ let changed = nextSet.size !== observedElements.size;
1229
+ if (!changed) {
1230
+ for (const item of nextSet) {
1231
+ if (!observedElements.has(item)) {
1232
+ changed = true;
1233
+ break;
1234
+ }
1235
+ }
1236
+ }
1237
+ if (changed) {
1238
+ observer.disconnect();
1239
+ observedElements = nextSet;
1240
+ for (const node of nextSet) {
1241
+ observer.observe(node, {
1242
+ attributes: true,
1243
+ attributeFilter: ["style", "class", "dir"]
1244
+ });
1245
+ }
1246
+ }
1247
+ };
1248
+ updateAncestorObservers();
1249
+ afterNextRender(() => {
1250
+ updateAncestorObservers();
1251
+ updateCssDirection();
1252
+ });
1253
+ this.#destroyRef.onDestroy(() => {
1254
+ observer.disconnect();
1255
+ });
1256
+ }
1257
+ if (typeof window !== "undefined") {
1258
+ let resizeRafId = null;
1259
+ const onResize = () => {
1260
+ if (resizeRafId !== null) {
1261
+ return;
1262
+ }
1263
+ if (typeof window.requestAnimationFrame === "function") {
1264
+ resizeRafId = window.requestAnimationFrame(() => {
1265
+ resizeRafId = null;
1266
+ updateCssDirection();
1267
+ });
1268
+ }
1269
+ else {
1270
+ updateCssDirection();
1271
+ }
1272
+ };
1273
+ window.addEventListener("resize", onResize, { passive: true });
1274
+ this.#destroyRef.onDestroy(() => {
1275
+ if (resizeRafId !== null && typeof window.cancelAnimationFrame === "function") {
1276
+ window.cancelAnimationFrame(resizeRafId);
1277
+ resizeRafId = null;
1278
+ }
1279
+ window.removeEventListener("resize", onResize);
1280
+ });
1281
+ }
1282
+ }
1100
1283
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SidebarLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1101
1284
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SidebarLayoutComponent, isStandalone: true, selector: "mona-sidebar-layout", inputs: { mobileBreakpoint: { classPropertyName: "mobileBreakpoint", publicName: "mobileBreakpoint", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape($event)" }, properties: { "attr.data-compact": "compact() ? 'true' : null", "class": "baseClass()" } }, providers: [SidebarLayoutService], ngImport: i0, template: `
1102
1285
  <ng-content></ng-content>
@@ -1830,12 +2013,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
1830
2013
  */
1831
2014
  class SidebarRailDirective {
1832
2015
  constructor() {
2016
+ this.#direction = injectComponentDirection();
1833
2017
  this.#sidebarService = inject(SidebarService);
1834
- this.baseClass = computed(() => sidebarRailThemeVariants({ side: this.#sidebarService.side() }), /* @ts-ignore */
2018
+ this.physicalSide = computed(() => {
2019
+ const isRtl = this.#direction() === "rtl";
2020
+ const side = this.#sidebarService.side();
2021
+ if (side === "start") {
2022
+ return isRtl ? "right" : "left";
2023
+ }
2024
+ return isRtl ? "left" : "right";
2025
+ }, /* @ts-ignore */
2026
+ ...(ngDevMode ? [{ debugName: "physicalSide" }] : /* istanbul ignore next */ []));
2027
+ this.baseClass = computed(() => sidebarRailThemeVariants({ physicalSide: this.physicalSide() }), /* @ts-ignore */
1835
2028
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
1836
2029
  this.expanded = this.#sidebarService.expanded;
1837
2030
  this.sidebarId = this.#sidebarService.sidebarId;
1838
2031
  }
2032
+ #direction;
1839
2033
  #sidebarService;
1840
2034
  onClick() {
1841
2035
  this.#sidebarService.toggle();