@nanahoshi/mona-ui 0.3.0 → 0.5.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 (35) hide show
  1. package/card/package.json +4 -0
  2. package/collapsible/package.json +4 -0
  3. package/fesm2022/nanahoshi-mona-ui-button.mjs +1 -2
  4. package/fesm2022/nanahoshi-mona-ui-card.mjs +307 -0
  5. package/fesm2022/nanahoshi-mona-ui-collapsible.mjs +304 -0
  6. package/fesm2022/nanahoshi-mona-ui-common.mjs +19 -1
  7. package/fesm2022/nanahoshi-mona-ui-contextmenu.mjs +1 -1
  8. package/fesm2022/nanahoshi-mona-ui-dropdown-button.mjs +1 -1
  9. package/fesm2022/nanahoshi-mona-ui-expansion-panel.mjs +20 -9
  10. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-cell.component-B6FBLtrr.mjs → nanahoshi-mona-ui-grid-grid-footer-cell.component-D-JCCvYJ.mjs} +2 -2
  11. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-OJe4UYuw.mjs → nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-7zwgl7u1.mjs} +2 -2
  12. package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-DCd8K6ng.mjs → nanahoshi-mona-ui-grid-grid-list.component-n3eQNIza.mjs} +5 -5
  13. package/fesm2022/{nanahoshi-mona-ui-grid-grid-toggle.component-Dl8Hlkhj.mjs → nanahoshi-mona-ui-grid-grid-toggle.component-BhS7c7rW.mjs} +2 -2
  14. package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-C5FRZlvv.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-B4VB3xFw.mjs} +5 -5
  15. package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-BRZUd8cE.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-A6Cq2a4Q.mjs} +11 -11
  16. package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
  17. package/fesm2022/nanahoshi-mona-ui-menubar.mjs +1 -1
  18. package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +18 -16
  19. package/fesm2022/nanahoshi-mona-ui-sidebar.mjs +2025 -0
  20. package/fesm2022/nanahoshi-mona-ui-skeleton.mjs +96 -0
  21. package/fesm2022/nanahoshi-mona-ui-split-button.mjs +1 -1
  22. package/fesm2022/nanahoshi-mona-ui-theme.mjs +71 -25
  23. package/fesm2022/nanahoshi-mona-ui-tooltip.mjs +49 -2
  24. package/package.json +17 -1
  25. package/sidebar/package.json +4 -0
  26. package/skeleton/package.json +4 -0
  27. package/types/nanahoshi-mona-ui-card.d.ts +104 -0
  28. package/types/nanahoshi-mona-ui-circular-progress-bar.d.ts +2 -1
  29. package/types/nanahoshi-mona-ui-collapsible.d.ts +92 -0
  30. package/types/nanahoshi-mona-ui-common.d.ts +6 -2
  31. package/types/nanahoshi-mona-ui-expansion-panel.d.ts +11 -3
  32. package/types/nanahoshi-mona-ui-popup-menu.d.ts +6 -4
  33. package/types/nanahoshi-mona-ui-sidebar.d.ts +695 -0
  34. package/types/nanahoshi-mona-ui-skeleton.d.ts +51 -0
  35. package/types/nanahoshi-mona-ui-theme.d.ts +28 -2
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/nanahoshi-mona-ui-card.mjs",
3
+ "typings": "../types/nanahoshi-mona-ui-card.d.ts"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/nanahoshi-mona-ui-collapsible.mjs",
3
+ "typings": "../types/nanahoshi-mona-ui-collapsible.d.ts"
4
+ }
@@ -305,8 +305,7 @@ class ButtonDirective {
305
305
  const size = this.effectiveSize();
306
306
  const selected = this.selected();
307
307
  const userClass = this.userClass();
308
- const variants = buttonThemeVariants;
309
- const variantClasses = variants({ disabled, iconOnly, loading, look, rounded, selected, size });
308
+ const variantClasses = buttonThemeVariants({ disabled, iconOnly, loading, look, rounded, selected, size });
310
309
  return twMerge(variantClasses, userClass);
311
310
  }, /* @ts-ignore */
312
311
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
@@ -0,0 +1,307 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, TemplateRef, Directive, signal, Service, computed, contentChild, input, Component, viewChild, afterRenderEffect, DestroyRef, contentChildren } from '@angular/core';
4
+ import { classInputToClass } from '@nanahoshi/mona-ui/common';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { themeRaisedBackdropClasses, createElementControlId } from '@nanahoshi/mona-ui/internal';
7
+ import { cva } from 'class-variance-authority';
8
+
9
+ class CardContentDirective {
10
+ constructor() {
11
+ this.templateRef = inject(TemplateRef);
12
+ }
13
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
14
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CardContentDirective, isStandalone: true, selector: "[monaCardContent]", ngImport: i0 }); }
15
+ }
16
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardContentDirective, decorators: [{
17
+ type: Directive,
18
+ args: [{
19
+ selector: "[monaCardContent]"
20
+ }]
21
+ }] });
22
+
23
+ class CardService {
24
+ constructor() {
25
+ this.actionTemplate = signal([], /* @ts-ignore */
26
+ ...(ngDevMode ? [{ debugName: "actionTemplate" }] : /* istanbul ignore next */ []));
27
+ this.descriptionId = signal(null, /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "descriptionId" }] : /* istanbul ignore next */ []));
29
+ this.descriptionTemplate = signal(null, /* @ts-ignore */
30
+ ...(ngDevMode ? [{ debugName: "descriptionTemplate" }] : /* istanbul ignore next */ []));
31
+ this.footerClass = signal(undefined, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
33
+ this.footerTemplate = signal(null, /* @ts-ignore */
34
+ ...(ngDevMode ? [{ debugName: "footerTemplate" }] : /* istanbul ignore next */ []));
35
+ this.headerClass = signal(undefined, /* @ts-ignore */
36
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
37
+ this.headerTemplate = signal(null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "headerTemplate" }] : /* istanbul ignore next */ []));
39
+ this.titleId = signal(null, /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "titleId" }] : /* istanbul ignore next */ []));
41
+ this.titleTemplate = signal(null, /* @ts-ignore */
42
+ ...(ngDevMode ? [{ debugName: "titleTemplate" }] : /* istanbul ignore next */ []));
43
+ }
44
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
45
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.6", ngImport: i0, type: CardService, autoProvided: false }); }
46
+ }
47
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardService, decorators: [{
48
+ type: Service,
49
+ args: [{ autoProvided: false }]
50
+ }] });
51
+
52
+ const roundedTopVariants = {
53
+ small: "rounded-tl-sm rounded-tr-sm",
54
+ medium: "rounded-tl-md rounded-tr-md",
55
+ large: "rounded-tl-lg rounded-tr-lg",
56
+ xlarge: "rounded-tl-xl rounded-tr-xl",
57
+ xxlarge: "rounded-tl-2xl rounded-tr-2xl",
58
+ none: "rounded-none"
59
+ };
60
+ const roundedBottomVariants = {
61
+ small: "rounded-bl-sm rounded-br-sm",
62
+ medium: "rounded-bl-md rounded-br-md",
63
+ large: "rounded-bl-lg rounded-br-lg",
64
+ xlarge: "rounded-bl-xl rounded-br-xl",
65
+ xxlarge: "rounded-bl-2xl rounded-br-2xl",
66
+ none: "rounded-none"
67
+ };
68
+ const roundedAllVariants = {
69
+ small: "rounded-sm",
70
+ medium: "rounded-md",
71
+ large: "rounded-lg",
72
+ xlarge: "rounded-xl",
73
+ xxlarge: "rounded-2xl",
74
+ none: "rounded-none"
75
+ };
76
+ const cardBaseThemeVariants = cva(`
77
+ flex flex-col gap-2 py-4
78
+ ${themeRaisedBackdropClasses}
79
+ border border-border
80
+ bg-(--color-card) text-(--color-card-foreground)
81
+ shadow-(--shadow-raised)
82
+ `, {
83
+ variants: {
84
+ rounded: roundedAllVariants,
85
+ hasFooter: {
86
+ true: "pb-0",
87
+ false: ""
88
+ },
89
+ hasHeader: {
90
+ true: "pt-0",
91
+ false: ""
92
+ }
93
+ }
94
+ });
95
+ const cardFooterThemeVariants = cva(`
96
+ flex flex-row
97
+ justify-between
98
+ items-center
99
+ p-4
100
+ border-t
101
+ border-border
102
+ `, {
103
+ variants: {
104
+ rounded: roundedBottomVariants
105
+ }
106
+ });
107
+ const cardHeaderThemeVariants = cva(`
108
+ grid grid-cols-[1fr_auto] grid-rows-2
109
+ p-4 pb-0
110
+ `, {
111
+ variants: {
112
+ rounded: roundedTopVariants
113
+ }
114
+ });
115
+ const cardHeaderTitleThemeVariants = cva(`col-start-1 row-start-1 items-center`);
116
+ const cardHeaderDescriptionThemeVariants = cva(`col-start-1 row-start-2`);
117
+ const cardHeaderActionsThemeVariants = cva(`col-start-2 row-start-1 row-span-2 self-start justify-self-end`);
118
+
119
+ class CardComponent {
120
+ constructor() {
121
+ this.#cardService = inject(CardService);
122
+ this.actionsCellClass = cardHeaderActionsThemeVariants();
123
+ this.actionTemplate = this.#cardService.actionTemplate.asReadonly();
124
+ this.baseClass = computed(() => {
125
+ const rounded = this.rounded();
126
+ const hasHeader = !!this.headerTemplate();
127
+ const hasFooter = !!this.footerTemplate();
128
+ const variantClass = cardBaseThemeVariants({ rounded, hasHeader, hasFooter });
129
+ const userClass = classInputToClass(this.userClass());
130
+ return twMerge(variantClass, userClass);
131
+ }, /* @ts-ignore */
132
+ ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
133
+ this.contentDirective = contentChild(CardContentDirective, /* @ts-ignore */
134
+ ...(ngDevMode ? [{ debugName: "contentDirective" }] : /* istanbul ignore next */ []));
135
+ this.descriptionCellClass = cardHeaderDescriptionThemeVariants();
136
+ this.descriptionId = this.#cardService.descriptionId.asReadonly();
137
+ this.descriptionTemplate = this.#cardService.descriptionTemplate.asReadonly();
138
+ this.footerClass = computed(() => {
139
+ const rounded = this.rounded();
140
+ const variantClass = cardFooterThemeVariants({ rounded });
141
+ const userClass = classInputToClass(this.#cardService.footerClass());
142
+ return twMerge(variantClass, userClass);
143
+ }, /* @ts-ignore */
144
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
145
+ this.footerTemplate = this.#cardService.footerTemplate.asReadonly();
146
+ this.headerClass = computed(() => {
147
+ const rounded = this.rounded();
148
+ const variantClass = cardHeaderThemeVariants({ rounded });
149
+ const userClass = classInputToClass(this.#cardService.headerClass());
150
+ return twMerge(variantClass, userClass);
151
+ }, /* @ts-ignore */
152
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
153
+ this.headerTemplate = this.#cardService.headerTemplate.asReadonly();
154
+ this.titleCellClass = cardHeaderTitleThemeVariants();
155
+ this.titleId = this.#cardService.titleId.asReadonly();
156
+ this.titleTemplate = this.#cardService.titleTemplate.asReadonly();
157
+ /**
158
+ * @description Controls the border radius applied to the card and its header/footer regions.
159
+ * @default "medium"
160
+ */
161
+ this.rounded = input("medium", /* @ts-ignore */
162
+ ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
163
+ /**
164
+ * @description Additional CSS classes merged onto the host element via `tailwind-merge`.
165
+ * @default ""
166
+ */
167
+ this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
168
+ }
169
+ #cardService;
170
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
171
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: CardComponent, isStandalone: true, selector: "mona-card", inputs: { rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "baseClass()", "attr.aria-labelledby": "titleId()", "attr.aria-describedby": "descriptionId()" } }, providers: [CardService], queries: [{ propertyName: "contentDirective", first: true, predicate: CardContentDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@if (headerTemplate()) {\n <div [class]=\"headerClass()\">\n @if (titleTemplate()) {\n <div [class]=\"titleCellClass\">\n <ng-container [ngTemplateOutlet]=\"titleTemplate()\" [ngTemplateOutletContext]=\"{ $implicit: titleId() }\" />\n </div>\n }\n @if (descriptionTemplate()) {\n <div [class]=\"descriptionCellClass\">\n <ng-container\n [ngTemplateOutlet]=\"descriptionTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: descriptionId() }\"\n />\n </div>\n }\n @if (actionTemplate().length > 0) {\n <div [class]=\"actionsCellClass\" role=\"group\" aria-label=\"Card actions\">\n @for (template of actionTemplate(); track template) {\n <ng-container [ngTemplateOutlet]=\"template\" />\n }\n </div>\n }\n </div>\n}\n<ng-container [ngTemplateOutlet]=\"contentDirective()?.templateRef ?? null\" />\n@if (footerTemplate()) {\n <div [class]=\"footerClass()\">\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
172
+ }
173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardComponent, decorators: [{
174
+ type: Component,
175
+ args: [{ selector: "mona-card", imports: [NgTemplateOutlet], host: {
176
+ "[class]": "baseClass()",
177
+ "[attr.aria-labelledby]": "titleId()",
178
+ "[attr.aria-describedby]": "descriptionId()"
179
+ }, providers: [CardService], template: "@if (headerTemplate()) {\n <div [class]=\"headerClass()\">\n @if (titleTemplate()) {\n <div [class]=\"titleCellClass\">\n <ng-container [ngTemplateOutlet]=\"titleTemplate()\" [ngTemplateOutletContext]=\"{ $implicit: titleId() }\" />\n </div>\n }\n @if (descriptionTemplate()) {\n <div [class]=\"descriptionCellClass\">\n <ng-container\n [ngTemplateOutlet]=\"descriptionTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: descriptionId() }\"\n />\n </div>\n }\n @if (actionTemplate().length > 0) {\n <div [class]=\"actionsCellClass\" role=\"group\" aria-label=\"Card actions\">\n @for (template of actionTemplate(); track template) {\n <ng-container [ngTemplateOutlet]=\"template\" />\n }\n </div>\n }\n </div>\n}\n<ng-container [ngTemplateOutlet]=\"contentDirective()?.templateRef ?? null\" />\n@if (footerTemplate()) {\n <div [class]=\"footerClass()\">\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\" />\n </div>\n}\n" }]
180
+ }], propDecorators: { contentDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CardContentDirective), { isSignal: true }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
181
+
182
+ class CardFooterComponent {
183
+ #cardService;
184
+ constructor() {
185
+ this.#cardService = inject(CardService);
186
+ this.footerTemplate = viewChild.required(TemplateRef, /* @ts-ignore */
187
+ ...(ngDevMode ? [{ debugName: "footerTemplate" }] : /* istanbul ignore next */ []));
188
+ /**
189
+ * @description Additional CSS classes merged onto the rendered footer element via `tailwind-merge`.
190
+ * @default ""
191
+ */
192
+ this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
193
+ afterRenderEffect({
194
+ read: () => {
195
+ this.#cardService.footerTemplate.set(this.footerTemplate());
196
+ this.#cardService.footerClass.set(this.userClass());
197
+ }
198
+ });
199
+ inject(DestroyRef).onDestroy(() => {
200
+ this.#cardService.footerTemplate.set(null);
201
+ this.#cardService.footerClass.set(null);
202
+ });
203
+ }
204
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
205
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.6", type: CardFooterComponent, isStandalone: true, selector: "mona-card-footer", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "footerTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }); }
206
+ }
207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardFooterComponent, decorators: [{
208
+ type: Component,
209
+ args: [{ selector: "mona-card-footer", imports: [], template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }]
210
+ }], ctorParameters: () => [], propDecorators: { footerTemplate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
211
+
212
+ class CardActionDirective {
213
+ constructor() {
214
+ this.templateRef = inject(TemplateRef);
215
+ }
216
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardActionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
217
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CardActionDirective, isStandalone: true, selector: "[monaCardAction]", ngImport: i0 }); }
218
+ }
219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardActionDirective, decorators: [{
220
+ type: Directive,
221
+ args: [{
222
+ selector: "[monaCardAction]"
223
+ }]
224
+ }] });
225
+
226
+ class CardDescriptionDirective {
227
+ constructor() {
228
+ this.id = createElementControlId();
229
+ this.templateRef = inject(TemplateRef);
230
+ }
231
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardDescriptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
232
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CardDescriptionDirective, isStandalone: true, selector: "[monaCardDescription]", ngImport: i0 }); }
233
+ }
234
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardDescriptionDirective, decorators: [{
235
+ type: Directive,
236
+ args: [{
237
+ selector: "[monaCardDescription]"
238
+ }]
239
+ }] });
240
+
241
+ class CardTitleDirective {
242
+ constructor() {
243
+ this.id = createElementControlId();
244
+ this.templateRef = inject(TemplateRef);
245
+ }
246
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardTitleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
247
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CardTitleDirective, isStandalone: true, selector: "[monaCardTitle]", ngImport: i0 }); }
248
+ }
249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardTitleDirective, decorators: [{
250
+ type: Directive,
251
+ args: [{
252
+ selector: "[monaCardTitle]"
253
+ }]
254
+ }] });
255
+
256
+ class CardHeaderComponent {
257
+ #cardService;
258
+ constructor() {
259
+ this.#cardService = inject(CardService);
260
+ this.actionDirective = contentChildren(CardActionDirective, /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "actionDirective" }] : /* istanbul ignore next */ []));
262
+ this.descriptionDirective = contentChild(CardDescriptionDirective, /* @ts-ignore */
263
+ ...(ngDevMode ? [{ debugName: "descriptionDirective" }] : /* istanbul ignore next */ []));
264
+ this.headerTemplate = viewChild.required(TemplateRef, /* @ts-ignore */
265
+ ...(ngDevMode ? [{ debugName: "headerTemplate" }] : /* istanbul ignore next */ []));
266
+ this.titleDirective = contentChild(CardTitleDirective, /* @ts-ignore */
267
+ ...(ngDevMode ? [{ debugName: "titleDirective" }] : /* istanbul ignore next */ []));
268
+ /**
269
+ * @description Additional CSS classes merged onto the rendered header element via `tailwind-merge`.
270
+ * @default ""
271
+ */
272
+ this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
273
+ afterRenderEffect({
274
+ read: () => {
275
+ this.#cardService.actionTemplate.set(this.actionDirective().map(directive => directive.templateRef));
276
+ this.#cardService.descriptionId.set(this.descriptionDirective()?.id ?? null);
277
+ this.#cardService.descriptionTemplate.set(this.descriptionDirective()?.templateRef ?? null);
278
+ this.#cardService.headerTemplate.set(this.headerTemplate());
279
+ this.#cardService.headerClass.set(this.userClass());
280
+ this.#cardService.titleId.set(this.titleDirective()?.id ?? null);
281
+ this.#cardService.titleTemplate.set(this.titleDirective()?.templateRef ?? null);
282
+ }
283
+ });
284
+ inject(DestroyRef).onDestroy(() => {
285
+ this.#cardService.actionTemplate.set([]);
286
+ this.#cardService.descriptionId.set(null);
287
+ this.#cardService.descriptionTemplate.set(null);
288
+ this.#cardService.headerTemplate.set(null);
289
+ this.#cardService.headerClass.set(null);
290
+ this.#cardService.titleId.set(null);
291
+ this.#cardService.titleTemplate.set(null);
292
+ });
293
+ }
294
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
295
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.6", type: CardHeaderComponent, isStandalone: true, selector: "mona-card-header", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "actionDirective", predicate: CardActionDirective, isSignal: true }, { propertyName: "descriptionDirective", first: true, predicate: CardDescriptionDirective, descendants: true, isSignal: true }, { propertyName: "titleDirective", first: true, predicate: CardTitleDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "headerTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }); }
296
+ }
297
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CardHeaderComponent, decorators: [{
298
+ type: Component,
299
+ args: [{ selector: "mona-card-header", template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }]
300
+ }], ctorParameters: () => [], propDecorators: { actionDirective: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CardActionDirective), { isSignal: true }] }], descriptionDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CardDescriptionDirective), { isSignal: true }] }], headerTemplate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], titleDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CardTitleDirective), { isSignal: true }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
301
+
302
+ /**
303
+ * Generated bundle index. Do not edit.
304
+ */
305
+
306
+ export { CardActionDirective, CardComponent, CardContentDirective, CardDescriptionDirective, CardFooterComponent, CardHeaderComponent, CardTitleDirective };
307
+ //# sourceMappingURL=nanahoshi-mona-ui-card.mjs.map
@@ -0,0 +1,304 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, computed, input, model, forwardRef, Directive, inject, ElementRef, signal, DestroyRef, afterNextRender, effect, untracked } from '@angular/core';
3
+ import { createElementControlId } from '@nanahoshi/mona-ui/internal';
4
+
5
+ const CollapsibleToken = new InjectionToken("MONA_COLLAPSIBLE");
6
+
7
+ /**
8
+ * @description
9
+ * Headless disclosure root. Adds no markup of its own, so it can be applied to any element
10
+ * (`li`, `div`, `section`) and paired with `monaCollapsibleTrigger` and `monaCollapsibleContent`.
11
+ */
12
+ class CollapsibleDirective {
13
+ constructor() {
14
+ this.state = computed(() => (this.expanded() ? "open" : "closed"), /* @ts-ignore */
15
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
16
+ /**
17
+ * @description Enables the height transition applied to the collapsible content.
18
+ * @default true
19
+ */
20
+ this.animate = input(true, /* @ts-ignore */
21
+ ...(ngDevMode ? [{ debugName: "animate" }] : /* istanbul ignore next */ []));
22
+ /**
23
+ * @description Id assigned to the content element and referenced by the trigger through `aria-controls`.
24
+ */
25
+ this.contentId = createElementControlId();
26
+ /**
27
+ * @description Suppresses toggling through the trigger. `expand()` and `collapse()` still apply.
28
+ * @default false
29
+ */
30
+ this.disabled = input(false, /* @ts-ignore */
31
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
32
+ /**
33
+ * @description Sets whether the content is expanded. Supports two-way binding.
34
+ * @default false
35
+ */
36
+ this.expanded = model(false, /* @ts-ignore */
37
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
38
+ }
39
+ collapse() {
40
+ this.expanded.set(false);
41
+ }
42
+ expand() {
43
+ this.expanded.set(true);
44
+ }
45
+ toggle() {
46
+ if (this.disabled()) {
47
+ return;
48
+ }
49
+ this.expanded.update(expanded => !expanded);
50
+ }
51
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
52
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: CollapsibleDirective, isStandalone: true, selector: "[monaCollapsible]", inputs: { animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { properties: { "attr.data-state": "state()" } }, providers: [{ provide: CollapsibleToken, useExisting: forwardRef(() => CollapsibleDirective) }], exportAs: ["monaCollapsible"], ngImport: i0 }); }
53
+ }
54
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleDirective, decorators: [{
55
+ type: Directive,
56
+ args: [{
57
+ selector: "[monaCollapsible]",
58
+ exportAs: "monaCollapsible",
59
+ host: {
60
+ "[attr.data-state]": "state()"
61
+ },
62
+ providers: [{ provide: CollapsibleToken, useExisting: forwardRef(() => CollapsibleDirective) }]
63
+ }]
64
+ }], propDecorators: { animate: [{ type: i0.Input, args: [{ isSignal: true, alias: "animate", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }] } });
65
+
66
+ const CONTENT_HEIGHT_VARIABLE = "--mona-collapsible-content-height";
67
+ /**
68
+ * @description
69
+ * Animates the height of the collapsible content between `0` and its measured size. Adds no wrapper
70
+ * element, so it can be applied directly to a `ul`, `div` or any other container with any number of children.
71
+ *
72
+ * The measured size is also published on the host as the `--mona-collapsible-content-height` custom
73
+ * property, so consumers can drive their own animation instead (pair it with `[animate]="false"` on the root).
74
+ */
75
+ class CollapsibleContentDirective {
76
+ #element;
77
+ #initialized;
78
+ #reducedMotion;
79
+ #fallbackId;
80
+ constructor() {
81
+ this.#element = inject(ElementRef).nativeElement;
82
+ this.#initialized = signal(false, /* @ts-ignore */
83
+ ...(ngDevMode ? [{ debugName: "#initialized" }] : /* istanbul ignore next */ []));
84
+ this.#reducedMotion = signal(false, /* @ts-ignore */
85
+ ...(ngDevMode ? [{ debugName: "#reducedMotion" }] : /* istanbul ignore next */ []));
86
+ this.#fallbackId = null;
87
+ this.collapsible = inject(CollapsibleToken);
88
+ this.inert = computed(() => (this.collapsible.expanded() ? null : true), /* @ts-ignore */
89
+ ...(ngDevMode ? [{ debugName: "inert" }] : /* istanbul ignore next */ []));
90
+ this.state = computed(() => (this.collapsible.expanded() ? "open" : "closed"), /* @ts-ignore */
91
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
92
+ const destroyRef = inject(DestroyRef);
93
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
94
+ const query = window.matchMedia("(prefers-reduced-motion: reduce)");
95
+ const onPreferenceChange = (event) => this.#reducedMotion.set(event.matches);
96
+ this.#reducedMotion.set(query.matches);
97
+ query.addEventListener("change", onPreferenceChange);
98
+ destroyRef.onDestroy(() => query.removeEventListener("change", onPreferenceChange));
99
+ }
100
+ const onTransitionEnd = (event) => {
101
+ if (event.propertyName !== "height" || event.target !== this.#element) {
102
+ return;
103
+ }
104
+ this.#settle();
105
+ };
106
+ this.#element.addEventListener("transitionend", onTransitionEnd);
107
+ destroyRef.onDestroy(() => {
108
+ this.#element.removeEventListener("transitionend", onTransitionEnd);
109
+ this.#clearFallback();
110
+ });
111
+ afterNextRender(() => {
112
+ this.#settle();
113
+ this.#initialized.set(true);
114
+ });
115
+ effect(() => {
116
+ const expanded = this.collapsible.expanded();
117
+ untracked(() => {
118
+ if (!this.#initialized()) {
119
+ return;
120
+ }
121
+ this.#animateTo(expanded);
122
+ });
123
+ });
124
+ }
125
+ /** Runs the height transition towards the requested state. */
126
+ #animateTo(expanded) {
127
+ this.#clearFallback();
128
+ if (!this.collapsible.animate() || this.#reducedMotion()) {
129
+ this.#settle();
130
+ return;
131
+ }
132
+ const element = this.#element;
133
+ const contentHeight = element.scrollHeight;
134
+ element.style.setProperty(CONTENT_HEIGHT_VARIABLE, `${contentHeight}px`);
135
+ this.#clip();
136
+ this.#setTransitionEnabled(true);
137
+ if (expanded) {
138
+ element.style.height = "0px";
139
+ void element.offsetHeight; // force a reflow so the browser has a start value to animate from
140
+ element.style.height = `${contentHeight}px`;
141
+ }
142
+ else {
143
+ element.style.height = `${element.offsetHeight}px`;
144
+ void element.offsetHeight;
145
+ element.style.height = "0px";
146
+ }
147
+ this.#scheduleFallback();
148
+ }
149
+ #clearFallback() {
150
+ if (this.#fallbackId !== null) {
151
+ clearTimeout(this.#fallbackId);
152
+ this.#fallbackId = null;
153
+ }
154
+ }
155
+ /**
156
+ * Clips the box so an explicit height is actually honoured. `min-height` matters because a flex item
157
+ * defaults to `min-height: auto`, which would otherwise hold the element open at its content height.
158
+ */
159
+ #clip() {
160
+ this.#element.style.overflow = "hidden";
161
+ this.#element.style.minHeight = "0px";
162
+ }
163
+ /** Reads the resolved transition duration in milliseconds, which comes from a theme custom property. */
164
+ #getTransitionDuration() {
165
+ const declared = getComputedStyle(this.#element).transitionDuration;
166
+ const durations = declared.split(",").map(part => {
167
+ const trimmed = part.trim();
168
+ const parsed = Number.parseFloat(trimmed);
169
+ if (!Number.isFinite(parsed)) {
170
+ return 0;
171
+ }
172
+ return trimmed.endsWith("ms") ? parsed : parsed * 1000;
173
+ });
174
+ return Math.max(0, ...durations);
175
+ }
176
+ /** Guards against a `transitionend` that never arrives, e.g. when an ancestor hides the element mid-flight. */
177
+ #scheduleFallback() {
178
+ this.#fallbackId = setTimeout(() => this.#settle(), this.#getTransitionDuration() + 50);
179
+ }
180
+ #setTransitionEnabled(enabled) {
181
+ const style = this.#element.style;
182
+ if (!enabled) {
183
+ style.transitionProperty = "";
184
+ style.transitionDuration = "";
185
+ style.transitionTimingFunction = "";
186
+ return;
187
+ }
188
+ style.transitionProperty = "height";
189
+ style.transitionDuration = `var(--mona-motion-standard, 300ms)`;
190
+ style.transitionTimingFunction = "ease-out";
191
+ }
192
+ /** Drops the transition and pins the element to the resting representation of the current state. */
193
+ #settle() {
194
+ this.#clearFallback();
195
+ this.#setTransitionEnabled(false);
196
+ const element = this.#element;
197
+ if (this.collapsible.expanded()) {
198
+ element.style.height = "";
199
+ element.style.overflow = "";
200
+ element.style.minHeight = "";
201
+ }
202
+ else {
203
+ element.style.height = "0px";
204
+ this.#clip();
205
+ }
206
+ element.style.setProperty(CONTENT_HEIGHT_VARIABLE, `${element.scrollHeight}px`);
207
+ }
208
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
209
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CollapsibleContentDirective, isStandalone: true, selector: "[monaCollapsibleContent]", host: { properties: { "attr.data-state": "state()", "attr.id": "collapsible.contentId", "attr.inert": "inert()" } }, ngImport: i0 }); }
210
+ }
211
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleContentDirective, decorators: [{
212
+ type: Directive,
213
+ args: [{
214
+ selector: "[monaCollapsibleContent]",
215
+ host: {
216
+ "[attr.data-state]": "state()",
217
+ "[attr.id]": "collapsible.contentId",
218
+ "[attr.inert]": "inert()"
219
+ }
220
+ }]
221
+ }], ctorParameters: () => [] });
222
+
223
+ /**
224
+ * @description
225
+ * Toggles the closest `monaCollapsible`. Wires `aria-expanded` / `aria-controls` automatically and
226
+ * only adds `role`, `tabindex` and keyboard handling when the host element is not already interactive.
227
+ */
228
+ class CollapsibleTriggerDirective {
229
+ constructor() {
230
+ this.#element = inject(ElementRef).nativeElement;
231
+ this.#nativeAnchor = this.#element.tagName === "A";
232
+ this.#nativeButton = this.#element.tagName === "BUTTON" || this.#element.tagName === "INPUT" || this.#element.tagName === "SUMMARY";
233
+ this.ariaDisabled = computed(() => (this.collapsible.disabled() ? "true" : null), /* @ts-ignore */
234
+ ...(ngDevMode ? [{ debugName: "ariaDisabled" }] : /* istanbul ignore next */ []));
235
+ this.collapsible = inject(CollapsibleToken);
236
+ this.nativeDisabled = computed(() => this.#element.tagName === "BUTTON" && this.collapsible.disabled() ? "" : null, /* @ts-ignore */
237
+ ...(ngDevMode ? [{ debugName: "nativeDisabled" }] : /* istanbul ignore next */ []));
238
+ this.role = this.#nativeButton || this.#nativeAnchor ? null : "button";
239
+ this.state = computed(() => (this.collapsible.expanded() ? "open" : "closed"), /* @ts-ignore */
240
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
241
+ this.tabIndex = computed(() => {
242
+ if (this.#nativeButton || this.#nativeAnchor) {
243
+ return null;
244
+ }
245
+ return this.collapsible.disabled() ? -1 : 0;
246
+ }, /* @ts-ignore */
247
+ ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
248
+ this.type = this.#element.tagName === "BUTTON" ? "button" : null;
249
+ }
250
+ #element;
251
+ #nativeAnchor;
252
+ #nativeButton;
253
+ onClick() {
254
+ this.collapsible.toggle();
255
+ }
256
+ onEnterKeydown(event) {
257
+ // Buttons and anchors already synthesise a click for Enter.
258
+ if (this.#nativeButton || this.#nativeAnchor) {
259
+ return;
260
+ }
261
+ event.preventDefault();
262
+ this.collapsible.toggle();
263
+ }
264
+ onSpaceKeydown(event) {
265
+ // Buttons already synthesise a click for Space; anchors scroll the page instead.
266
+ if (this.#nativeButton) {
267
+ return;
268
+ }
269
+ event.preventDefault();
270
+ this.collapsible.toggle();
271
+ }
272
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
273
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: CollapsibleTriggerDirective, isStandalone: true, selector: "[monaCollapsibleTrigger]", host: { listeners: { "click": "onClick()", "keydown.enter": "onEnterKeydown($event)", "keydown.space": "onSpaceKeydown($event)" }, properties: { "attr.aria-controls": "collapsible.contentId", "attr.aria-disabled": "ariaDisabled()", "attr.aria-expanded": "collapsible.expanded()", "attr.data-state": "state()", "attr.disabled": "nativeDisabled()", "attr.role": "role", "attr.tabindex": "tabIndex()", "attr.type": "type" } }, ngImport: i0 }); }
274
+ }
275
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CollapsibleTriggerDirective, decorators: [{
276
+ type: Directive,
277
+ args: [{
278
+ selector: "[monaCollapsibleTrigger]",
279
+ host: {
280
+ "[attr.aria-controls]": "collapsible.contentId",
281
+ "[attr.aria-disabled]": "ariaDisabled()",
282
+ "[attr.aria-expanded]": "collapsible.expanded()",
283
+ "[attr.data-state]": "state()",
284
+ "[attr.disabled]": "nativeDisabled()",
285
+ "[attr.role]": "role",
286
+ "[attr.tabindex]": "tabIndex()",
287
+ "[attr.type]": "type",
288
+ "(click)": "onClick()",
289
+ "(keydown.enter)": "onEnterKeydown($event)",
290
+ "(keydown.space)": "onSpaceKeydown($event)"
291
+ }
292
+ }]
293
+ }] });
294
+
295
+ /*
296
+ * Public API Surface of @nanahoshi/mona-ui/collapsible
297
+ */
298
+
299
+ /**
300
+ * Generated bundle index. Do not edit.
301
+ */
302
+
303
+ export { CollapsibleContentDirective, CollapsibleDirective, CollapsibleToken, CollapsibleTriggerDirective };
304
+ //# sourceMappingURL=nanahoshi-mona-ui-collapsible.mjs.map