@nanahoshi/mona-ui 0.7.1 → 0.8.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.
@@ -0,0 +1,159 @@
1
+ import * as i0 from '@angular/core';
2
+ import { computed, input, output, model, Component } from '@angular/core';
3
+ import { themeControlSurfaceClasses, createElementControlId } from '@nanahoshi/mona-ui/internal';
4
+ import { twMerge } from 'tailwind-merge';
5
+ import { cva } from 'class-variance-authority';
6
+
7
+ const segmentedContainerThemeVariants = cva(`
8
+ inline-flex items-center gap-0.5
9
+ ${themeControlSurfaceClasses}
10
+ border border-input-border
11
+ rounded-lg
12
+ p-1
13
+ data-[invalid='true']:border-error
14
+ data-[invalid='true']:ring-2 data-[invalid='true']:ring-error/35
15
+ transition-colors duration-(--mona-motion-fast)
16
+ `);
17
+ const segmentedOptionThemeVariants = cva(`
18
+ relative flex flex-1 cursor-pointer items-center justify-center gap-1
19
+ whitespace-nowrap
20
+ rounded-md
21
+ transition-colors duration-(--mona-motion-fast)
22
+ data-[disabled='true']:pointer-events-none
23
+ data-[disabled='true']:cursor-not-allowed
24
+ data-[disabled='true']:text-disabled-foreground
25
+ data-[selected='true']:bg-primary data-[selected='true']:text-primary-foreground
26
+ data-[selected='true']:shadow-sm data-[selected='true']:ring-1 data-[selected='true']:ring-selected-border
27
+ data-[selected='false']:text-muted-foreground
28
+ data-[selected='false']:hover:bg-hover data-[selected='false']:hover:text-foreground
29
+ `, {
30
+ variants: {
31
+ size: {
32
+ small: "h-6 px-1.5 text-sm",
33
+ medium: "h-8 px-2.5 text-sm",
34
+ large: "h-10 px-3.5 text-base"
35
+ }
36
+ },
37
+ defaultVariants: {
38
+ size: "medium"
39
+ }
40
+ });
41
+ const segmentedInputThemeVariants = cva(`
42
+ peer absolute inset-0 z-10 h-full w-full
43
+ cursor-pointer appearance-none opacity-0
44
+ outline-none
45
+ disabled:cursor-not-allowed
46
+ `);
47
+
48
+ class SegmentedComponent {
49
+ constructor() {
50
+ this.containerClasses = computed(() => {
51
+ const classes = segmentedContainerThemeVariants();
52
+ return twMerge(classes, this.userClass());
53
+ }, /* @ts-ignore */
54
+ ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
55
+ this.groupName = createElementControlId();
56
+ this.inputClasses = computed(() => segmentedInputThemeVariants(), /* @ts-ignore */
57
+ ...(ngDevMode ? [{ debugName: "inputClasses" }] : /* istanbul ignore next */ []));
58
+ this.invalidState = computed(() => this.touched() && this.invalid(), /* @ts-ignore */
59
+ ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
60
+ this.optionClasses = computed(() => {
61
+ const size = this.size();
62
+ return segmentedOptionThemeVariants({ size });
63
+ }, /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "optionClasses" }] : /* istanbul ignore next */ []));
65
+ /**
66
+ * @description Accessible name for the radio group. Provide either `aria-label` or `aria-labelledby`.
67
+ * @default null
68
+ */
69
+ this.ariaLabel = input(null, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: "aria-label" });
70
+ /**
71
+ * @description ID of an external element that provides the accessible name for the radio group.
72
+ * @default null
73
+ */
74
+ this.ariaLabelledBy = input(null, { ...(ngDevMode ? { debugName: "ariaLabelledBy" } : /* istanbul ignore next */ {}), alias: "aria-labelledby" });
75
+ /**
76
+ * @description Disables every option in the group and prevents value changes.
77
+ * @default false
78
+ */
79
+ this.disabled = input(false, /* @ts-ignore */
80
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
81
+ /**
82
+ * @description Marks the component as invalid. Error styling requires both `invalid` and `touched` to be `true`.
83
+ * When bound to a signal form field via `[formField]`, this is written by the signal forms `Field` directive.
84
+ * @default false
85
+ */
86
+ this.invalid = input(false, /* @ts-ignore */
87
+ ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
88
+ /**
89
+ * @description The list of selectable options. Exactly one option is selected at a time.
90
+ */
91
+ this.options = input.required(/* @ts-ignore */
92
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
93
+ /**
94
+ * @description Size preset controlling the height, horizontal padding, text size, and spacing of each option.
95
+ * @default "medium"
96
+ */
97
+ this.size = input("medium", /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
99
+ /**
100
+ * @description Emitted when the selected value changes or focus leaves a radio input.
101
+ */
102
+ this.touch = output();
103
+ /**
104
+ * @description Marks the component as touched. When bound to a signal form field via `[formField]`,
105
+ * this is written by the signal forms `Field` directive.
106
+ * @default false
107
+ */
108
+ this.touched = input(false, /* @ts-ignore */
109
+ ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
110
+ /**
111
+ * @description Additional CSS classes merged onto the host element via `tailwind-merge`.
112
+ * @default ""
113
+ */
114
+ this.userClass = input("", { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: "class" });
115
+ /**
116
+ * @description The currently selected value. When it matches no current option, no option is checked.
117
+ * A `null` value is allowed initially but cannot be restored through segmented interaction.
118
+ * @default null
119
+ */
120
+ this.value = model(null, /* @ts-ignore */
121
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
122
+ }
123
+ onOptionBlur() {
124
+ this.touch.emit();
125
+ }
126
+ onOptionChange(option) {
127
+ if (this.disabled() || option.disabled) {
128
+ return;
129
+ }
130
+ this.value.set(option.value);
131
+ this.touch.emit();
132
+ }
133
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SegmentedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
134
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SegmentedComponent, isStandalone: true, selector: "mona-segmented", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", 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 } }, outputs: { touch: "touch", value: "valueChange" }, host: { properties: { "attr.aria-disabled": "disabled() || undefined", "attr.aria-invalid": "invalidState() ? 'true' : null", "attr.aria-label": "ariaLabel()", "attr.aria-labelledby": "ariaLabelledBy()", "attr.data-disabled": "disabled()", "attr.data-invalid": "invalidState() || null", "attr.role": "'radiogroup'", "class": "containerClasses()" } }, ngImport: i0, template: "@for (option of options(); track option.value) {\n @let selected = option.value === value();\n @let optionDisabled = disabled() || option.disabled;\n\n <label [class]=\"optionClasses()\" [attr.data-selected]=\"selected\" [attr.data-disabled]=\"optionDisabled\">\n <input\n type=\"radio\"\n [name]=\"groupName\"\n [value]=\"option.value\"\n [checked]=\"selected\"\n [disabled]=\"optionDisabled\"\n [class]=\"inputClasses()\"\n (change)=\"onOptionChange(option)\"\n (blur)=\"onOptionBlur()\" />\n\n <span\n class=\"pointer-events-none absolute inset-0 flex items-center justify-center gap-1 rounded-md peer-focus-visible:ring-2 peer-focus-visible:ring-focus-indicator/35\">\n {{ option.label }}\n </span>\n </label>\n}\n" }); }
135
+ }
136
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SegmentedComponent, decorators: [{
137
+ type: Component,
138
+ args: [{ selector: "mona-segmented", host: {
139
+ "[attr.aria-disabled]": "disabled() || undefined",
140
+ "[attr.aria-invalid]": "invalidState() ? 'true' : null",
141
+ "[attr.aria-label]": "ariaLabel()",
142
+ "[attr.aria-labelledby]": "ariaLabelledBy()",
143
+ "[attr.data-disabled]": "disabled()",
144
+ "[attr.data-invalid]": "invalidState() || null",
145
+ "[attr.role]": "'radiogroup'",
146
+ "[class]": "containerClasses()"
147
+ }, template: "@for (option of options(); track option.value) {\n @let selected = option.value === value();\n @let optionDisabled = disabled() || option.disabled;\n\n <label [class]=\"optionClasses()\" [attr.data-selected]=\"selected\" [attr.data-disabled]=\"optionDisabled\">\n <input\n type=\"radio\"\n [name]=\"groupName\"\n [value]=\"option.value\"\n [checked]=\"selected\"\n [disabled]=\"optionDisabled\"\n [class]=\"inputClasses()\"\n (change)=\"onOptionChange(option)\"\n (blur)=\"onOptionBlur()\" />\n\n <span\n class=\"pointer-events-none absolute inset-0 flex items-center justify-center gap-1 rounded-md peer-focus-visible:ring-2 peer-focus-visible:ring-focus-indicator/35\">\n {{ option.label }}\n </span>\n </label>\n}\n" }]
148
+ }], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
149
+
150
+ /*
151
+ * Public API Surface of @nanahoshi/mona-ui/segmented
152
+ */
153
+
154
+ /**
155
+ * Generated bundle index. Do not edit.
156
+ */
157
+
158
+ export { SegmentedComponent };
159
+ //# sourceMappingURL=nanahoshi-mona-ui-segmented.mjs.map
@@ -5,11 +5,11 @@ import { NgTemplateOutlet } from '@angular/common';
5
5
  import { firstOrDefault, select } from '@mirei/ts-collections';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { cva } from 'class-variance-authority';
8
- import { themeRaisedBackdropClasses, themeRaisedSurfaceClasses, createElementControlId } from '@nanahoshi/mona-ui/internal';
9
8
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
- import { LucideChevronLeft, LucideChevronRight, LucideX } from '@lucide/angular';
9
+ import { LucideChevronDown, LucideChevronLeft, LucideChevronRight, LucideChevronUp, LucideX } from '@lucide/angular';
11
10
  import { ButtonDirective } from '@nanahoshi/mona-ui/button';
12
11
  import { Subject, switchMap, EMPTY, timer, interval, tap, asapScheduler } from 'rxjs';
12
+ import { createElementControlId } from '@nanahoshi/mona-ui/internal';
13
13
 
14
14
  class TabCloseEvent extends PreventableEvent {
15
15
  #index;
@@ -63,104 +63,120 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
63
63
  }] });
64
64
 
65
65
  const tabListBaseThemeVariants = cva(`
66
- flex w-fit max-w-full overflow-hidden
67
- text-sm font-medium
68
- bg-(--mona-tab-list-background) ${themeRaisedBackdropClasses}
66
+ flex max-w-full min-w-0 overflow-hidden
69
67
  `, {
70
68
  variants: {
71
- rounded: {
72
- small: "rounded-sm",
73
- medium: "rounded-md",
74
- large: "rounded-lg",
75
- full: "rounded-full",
76
- none: "rounded-none"
77
- },
78
- size: {
79
- small: "h-8",
80
- medium: "h-9",
81
- large: "h-10"
69
+ position: {
70
+ top: "flex-row border-b border-border",
71
+ bottom: "flex-row border-t border-border",
72
+ left: "flex-col max-h-full min-h-0 border-r border-border",
73
+ right: "flex-col max-h-full min-h-0 border-l border-border"
82
74
  }
75
+ },
76
+ defaultVariants: {
77
+ position: "top"
83
78
  }
84
79
  });
85
- const tabListListWrapperThemeVariants = cva(`h-full w-full overflow-hidden`);
80
+ const tabListListWrapperThemeVariants = cva(`flex-1 min-w-0 min-h-0 overflow-hidden`);
86
81
  const tabListListThemeVariants = cva(`
87
- flex h-full w-full list-none overflow-hidden p-[3px]
82
+ flex list-none overflow-hidden
88
83
  cursor-default select-none whitespace-nowrap
89
84
  transition-colors duration-(--mona-motion-standard) ease-out
90
- `);
85
+ `, {
86
+ variants: {
87
+ position: {
88
+ top: "flex-row max-w-full min-w-0",
89
+ bottom: "flex-row max-w-full min-w-0",
90
+ left: "flex-col max-h-full min-h-0",
91
+ right: "flex-col max-h-full min-h-0"
92
+ }
93
+ },
94
+ defaultVariants: {
95
+ position: "top"
96
+ }
97
+ });
91
98
  const tabListListItemThemeVariants = cva(`
92
- flex items-center justify-center px-2
93
- cursor-pointer outline-none
99
+ relative flex shrink-0 cursor-pointer outline-none
94
100
  focus-visible:ring-2 focus-visible:ring-focus-indicator/35
101
+ transition-colors duration-(--mona-motion-standard) ease-out
95
102
  `, {
96
103
  variants: {
97
104
  active: {
98
- true: `${themeRaisedSurfaceClasses} font-semibold text-foreground shadow-(--shadow-control) inset-ring-1 inset-ring-border-subtle`,
99
- false: ""
105
+ true: `
106
+ text-foreground font-semibold
107
+ after:absolute after:content-[''] after:bg-primary
108
+ `,
109
+ false: "text-muted-foreground hover:bg-hover hover:text-foreground"
100
110
  },
101
111
  disabled: {
102
- true: "pointer-events-none cursor-not-allowed select-none text-disabled-foreground",
112
+ true: "pointer-events-none cursor-not-allowed select-none text-disabled-foreground after:hidden",
103
113
  false: ""
104
114
  },
105
- rounded: {
106
- small: "rounded-sm",
107
- medium: "rounded-md",
108
- large: "rounded-lg",
109
- full: "rounded-full",
110
- none: "rounded-none"
115
+ position: {
116
+ top: "items-center justify-center whitespace-nowrap after:inset-x-0 after:bottom-0 after:h-[2px]",
117
+ bottom: "items-center justify-center whitespace-nowrap after:inset-x-0 after:top-0 after:h-[2px]",
118
+ left: "w-full items-center justify-start text-start after:inset-y-0 after:right-0 after:w-[2px]",
119
+ right: "w-full items-center justify-start text-start after:inset-y-0 after:left-0 after:w-[2px]"
120
+ },
121
+ size: {
122
+ small: "h-8 px-2 text-xs",
123
+ medium: "h-9 px-3 text-sm",
124
+ large: "h-10 px-4 text-sm"
111
125
  }
112
126
  },
113
127
  defaultVariants: {
114
- rounded: "medium"
128
+ position: "top",
129
+ size: "medium"
115
130
  }
116
131
  });
117
132
  const tabListScrollButtonThemeVariants = cva(`
118
- flex h-full items-center justify-center px-2
133
+ flex self-stretch items-center justify-center px-2
119
134
  text-muted-foreground
120
135
  hover:bg-hover hover:text-foreground
121
136
  active:bg-active
122
137
  `);
123
138
  const tabContentThemeVariants = cva(`
124
- flex-1 w-full overflow-auto
139
+ flex-1 min-w-0 min-h-0 overflow-auto
125
140
  [scrollbar-color:var(--color-scrollbar-thumb)_var(--color-scrollbar-track)] [scrollbar-width:thin]
126
- bg-(--mona-tab-content-background) ${themeRaisedBackdropClasses} text-foreground
127
- border border-border shadow-(--shadow-raised)
141
+ text-foreground
142
+ `);
143
+ const tabsBaseThemeVariants = cva(`
144
+ flex max-w-full min-w-0 min-h-0
128
145
  `, {
129
146
  variants: {
130
- rounded: {
131
- small: "rounded-sm",
132
- medium: "rounded-md",
133
- large: "rounded-lg",
134
- full: "rounded-full",
135
- none: "rounded-none"
147
+ position: {
148
+ top: "flex-col",
149
+ bottom: "flex-col-reverse",
150
+ left: "flex-row",
151
+ right: "flex-row-reverse"
136
152
  }
137
153
  },
138
154
  defaultVariants: {
139
- rounded: "medium"
155
+ position: "top"
140
156
  }
141
157
  });
142
- const tabsBaseThemeVariants = cva(`
143
- flex max-w-full flex-col gap-2
144
- `);
145
158
 
146
159
  class TabListItemDirective {
147
160
  constructor() {
148
161
  this.listItemClass = computed(() => {
149
162
  const active = this.active();
150
163
  const disabled = this.disabled();
151
- const rounded = this.rounded();
152
- return tabListListItemThemeVariants({ active, disabled, rounded });
164
+ const position = this.position();
165
+ const size = this.size();
166
+ return tabListListItemThemeVariants({ active, disabled, position, size });
153
167
  }, /* @ts-ignore */
154
168
  ...(ngDevMode ? [{ debugName: "listItemClass" }] : /* istanbul ignore next */ []));
155
169
  this.active = input.required(/* @ts-ignore */
156
170
  ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
157
171
  this.disabled = input.required(/* @ts-ignore */
158
172
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
159
- this.rounded = input.required(/* @ts-ignore */
160
- ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
173
+ this.position = input.required(/* @ts-ignore */
174
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
175
+ this.size = input.required(/* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
161
177
  }
162
178
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabListItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
163
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: TabListItemDirective, isStandalone: true, selector: "li[monaTabListItem]", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class": "listItemClass()" } }, ngImport: i0 }); }
179
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: TabListItemDirective, isStandalone: true, selector: "li[monaTabListItem]", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class": "listItemClass()" } }, ngImport: i0 }); }
164
180
  }
165
181
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabListItemDirective, decorators: [{
166
182
  type: Directive,
@@ -170,34 +186,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
170
186
  "[class]": "listItemClass()"
171
187
  }
172
188
  }]
173
- }], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: true }] }] } });
189
+ }], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }] } });
190
+
191
+ function getTabOrientation(position) {
192
+ return position === "left" || position === "right"
193
+ ? "vertical"
194
+ : "horizontal";
195
+ }
174
196
 
175
197
  class TabListComponent {
176
198
  #destroyRef;
177
199
  #keydown$;
178
- #resizeObserver;
179
200
  #scrollIntent$;
201
+ #resizeObserver;
180
202
  constructor() {
181
203
  this.#destroyRef = inject(DestroyRef);
182
204
  this.#keydown$ = new Subject();
183
- this.#resizeObserver = null;
184
205
  this.#scrollIntent$ = new Subject();
206
+ this.#resizeObserver = null;
185
207
  this.baseClass = computed(() => {
186
- const rounded = this.rounded();
187
- const size = this.size();
188
- return tabListBaseThemeVariants({ rounded, size });
208
+ return tabListBaseThemeVariants({ position: this.position() });
189
209
  }, /* @ts-ignore */
190
210
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
191
211
  this.listClass = computed(() => {
192
- return tabListListThemeVariants();
212
+ return tabListListThemeVariants({ position: this.position() });
193
213
  }, /* @ts-ignore */
194
214
  ...(ngDevMode ? [{ debugName: "listClass" }] : /* istanbul ignore next */ []));
215
+ this.listWrapperClass = computed(() => {
216
+ return tabListListWrapperThemeVariants();
217
+ }, /* @ts-ignore */
218
+ ...(ngDevMode ? [{ debugName: "listWrapperClass" }] : /* istanbul ignore next */ []));
219
+ this.orientation = computed(() => {
220
+ return getTabOrientation(this.position() ?? "top");
221
+ }, /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
223
+ this.overflowControlsVisible = signal(false, /* @ts-ignore */
224
+ ...(ngDevMode ? [{ debugName: "overflowControlsVisible" }] : /* istanbul ignore next */ []));
195
225
  this.scrollButtonClass = computed(() => {
196
226
  return tabListScrollButtonThemeVariants();
197
227
  }, /* @ts-ignore */
198
228
  ...(ngDevMode ? [{ debugName: "scrollButtonClass" }] : /* istanbul ignore next */ []));
199
- this.scrollsVisible = signal(false, /* @ts-ignore */
200
- ...(ngDevMode ? [{ debugName: "scrollsVisible" }] : /* istanbul ignore next */ []));
201
229
  this.tabListElement = viewChild.required("tabListElement", /* @ts-ignore */
202
230
  ...(ngDevMode ? [{ debugName: "tabListElement" }] : /* istanbul ignore next */ []));
203
231
  this.tabListItems = viewChildren(TabListItemDirective, { ...(ngDevMode ? { debugName: "tabListItems" } : /* istanbul ignore next */ {}), read: ElementRef });
@@ -205,19 +233,28 @@ class TabListComponent {
205
233
  ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
206
234
  this.disabled = input(false, /* @ts-ignore */
207
235
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
208
- this.rounded = input.required(/* @ts-ignore */
209
- ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
236
+ /**
237
+ * @description Placement of the tab list relative to the tab content.
238
+ * @default "top"
239
+ */
240
+ this.position = input.required(/* @ts-ignore */
241
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
210
242
  this.selectedTabId = model(null, /* @ts-ignore */
211
243
  ...(ngDevMode ? [{ debugName: "selectedTabId" }] : /* istanbul ignore next */ []));
244
+ /**
245
+ * @description Size preset controlling the tabs' dimensions.
246
+ * @default "medium"
247
+ */
212
248
  this.size = input.required(/* @ts-ignore */
213
249
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
214
250
  this.tabClose = output();
215
- this.tabSelect = output();
216
251
  this.tabList = input.required(/* @ts-ignore */
217
252
  ...(ngDevMode ? [{ debugName: "tabList" }] : /* istanbul ignore next */ []));
253
+ this.tabSelect = output();
218
254
  afterRenderEffect({
219
255
  read: () => {
220
256
  this.tabList();
257
+ this.position();
221
258
  untracked(() => {
222
259
  this.updateScrollVisibility();
223
260
  });
@@ -261,7 +298,7 @@ class TabListComponent {
261
298
  window.setTimeout(() => {
262
299
  const listElement = tabListElement.querySelector("li[data-selected='true']");
263
300
  if (listElement) {
264
- listElement.scrollIntoView({ behavior: "auto", block: "center" });
301
+ listElement.scrollIntoView({ behavior: "auto", block: "nearest", inline: "nearest" });
265
302
  }
266
303
  });
267
304
  }
@@ -299,16 +336,18 @@ class TabListComponent {
299
336
  }
300
337
  const selectedEnabledIndex = enabledTabs.indexOf(selectedTab);
301
338
  const selectedIndex = selectedEnabledIndex === -1 ? 0 : selectedEnabledIndex;
302
- let nextIndex = selectedIndex;
339
+ const navigationDirection = this.getNavigationDirection(event);
340
+ if (navigationDirection === "previous") {
341
+ event.preventDefault();
342
+ this.activateTab(enabledTabs[selectedIndex === 0 ? enabledTabs.length - 1 : selectedIndex - 1], event);
343
+ return;
344
+ }
345
+ if (navigationDirection === "next") {
346
+ event.preventDefault();
347
+ this.activateTab(enabledTabs[selectedIndex === enabledTabs.length - 1 ? 0 : selectedIndex + 1], event);
348
+ return;
349
+ }
303
350
  switch (event.key) {
304
- case "ArrowLeft":
305
- nextIndex = selectedIndex === 0 ? enabledTabs.length - 1 : selectedIndex - 1;
306
- this.activateTab(enabledTabs[nextIndex], event);
307
- break;
308
- case "ArrowRight":
309
- nextIndex = selectedIndex === enabledTabs.length - 1 ? 0 : selectedIndex + 1;
310
- this.activateTab(enabledTabs[nextIndex], event);
311
- break;
312
351
  case "Home":
313
352
  this.activateTab(enabledTabs[0], event);
314
353
  break;
@@ -346,6 +385,24 @@ class TabListComponent {
346
385
  const tabListItem = tabListItems.find(t => t.nativeElement.getAttribute("data-tab-id") === tab.id);
347
386
  tabListItem?.nativeElement.focus();
348
387
  }
388
+ getNavigationDirection(event) {
389
+ if (this.orientation() === "vertical") {
390
+ if (event.key === "ArrowUp") {
391
+ return "previous";
392
+ }
393
+ if (event.key === "ArrowDown") {
394
+ return "next";
395
+ }
396
+ return null;
397
+ }
398
+ if (event.key === "ArrowLeft") {
399
+ return "previous";
400
+ }
401
+ if (event.key === "ArrowRight") {
402
+ return "next";
403
+ }
404
+ return null;
405
+ }
349
406
  handleTabKey(tab, event) {
350
407
  event.preventDefault();
351
408
  const panelId = tab.id + "-panel";
@@ -355,17 +412,12 @@ class TabListComponent {
355
412
  }
356
413
  }
357
414
  performScroll(element, direction) {
358
- let left = 0;
359
- switch (direction) {
360
- case "left":
361
- left = Math.max(element.scrollLeft - 100, 0);
362
- element.scrollTo({ left, behavior: "smooth" });
363
- break;
364
- case "right":
365
- left = Math.min(element.scrollLeft + 100, element.scrollWidth);
366
- element.scrollTo({ left, behavior: "smooth" });
367
- break;
415
+ const offset = direction === "previous" ? -100 : 100;
416
+ if (this.orientation() === "vertical") {
417
+ element.scrollBy({ top: offset, behavior: "smooth" });
418
+ return;
368
419
  }
420
+ element.scrollBy({ left: offset, behavior: "smooth" });
369
421
  }
370
422
  selectTab(tab) {
371
423
  this.selectedTabId.set(tab.id);
@@ -398,19 +450,30 @@ class TabListComponent {
398
450
  }
399
451
  updateScrollVisibility() {
400
452
  asapScheduler.schedule(() => {
401
- this.scrollsVisible.set(this.tabListElement().nativeElement.scrollWidth > this.tabListElement().nativeElement.clientWidth);
453
+ const element = this.tabListElement().nativeElement;
454
+ const isVertical = this.orientation() === "vertical";
455
+ this.overflowControlsVisible.set(isVertical ? element.scrollHeight > element.clientHeight : element.scrollWidth > element.clientWidth);
402
456
  });
403
457
  }
404
458
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
405
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TabListComponent, isStandalone: true, selector: "mona-tab-list", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: true, transformFunction: null }, selectedTabId: { classPropertyName: "selectedTabId", publicName: "selectedTabId", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null }, tabList: { classPropertyName: "tabList", publicName: "tabList", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { selectedTabId: "selectedTabIdChange", tabClose: "tabClose", tabSelect: "tabSelect" }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class": "baseClass()" } }, viewQueries: [{ propertyName: "tabListElement", first: true, predicate: ["tabListElement"], descendants: true, isSignal: true }, { propertyName: "tabListItems", predicate: TabListItemDirective, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (scrollsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'left', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'left')\"\n (pointerup)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n <svg lucideChevronLeft [size]=\"16\"></svg>\n </button>\n}\n<div class=\"w-full h-full overflow-hidden\">\n <ul [class]=\"listClass()\" #tabListElement role=\"tablist\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let itemDisabled = tab.disabled || disabled();\n <li\n monaTabListItem\n [id]=\"tab.id + '-tab'\"\n [active]=\"selected\"\n [disabled]=\"itemDisabled\"\n [rounded]=\"rounded()\"\n role=\"tab\"\n [tabindex]=\"selected ? 0 : -1\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"itemDisabled\"\n [attr.aria-controls]=\"tab.id + '-panel'\"\n [attr.data-selected]=\"selected\"\n [attr.data-tab-id]=\"tab.id\"\n (click)=\"onTabClick(tab, tabListElement, $event)\">\n <div>\n @if (tab.titleTemplate) {\n <ng-container [ngTemplateOutlet]=\"tab.titleTemplate\"></ng-container>\n } @else {\n <span>{{ tab.title }}</span>\n }\n </div>\n @if (tab.closable || closable()) {\n <button\n monaButton\n look=\"link\"\n [disabled]=\"itemDisabled\"\n (click)=\"onTabClose(tab, $event)\"\n class=\"px-0 h-fit ml-1 w-min\"\n tabindex=\"-1\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </li>\n }\n </ul>\n</div>\n@if (scrollsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'right', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'right')\"\n (pointerup)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n <svg lucideChevronRight [size]=\"16\"></svg>\n </button>\n}\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: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TabListItemDirective, selector: "li[monaTabListItem]", inputs: ["active", "disabled", "rounded"] }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] }); }
459
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TabListComponent, isStandalone: true, selector: "mona-tab-list", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: true, transformFunction: null }, selectedTabId: { classPropertyName: "selectedTabId", publicName: "selectedTabId", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null }, tabList: { classPropertyName: "tabList", publicName: "tabList", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { selectedTabId: "selectedTabIdChange", tabClose: "tabClose", tabSelect: "tabSelect" }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class": "baseClass()" } }, viewQueries: [{ propertyName: "tabListElement", first: true, predicate: ["tabListElement"], descendants: true, isSignal: true }, { propertyName: "tabListItems", predicate: TabListItemDirective, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (overflowControlsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'previous', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'previous')\"\n (pointerup)=\"stopScrolling()\"\n (pointercancel)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n @if (orientation() === 'vertical') {\n <svg lucideChevronUp [size]=\"16\"></svg>\n } @else {\n <svg lucideChevronLeft [size]=\"16\"></svg>\n }\n </button>\n}\n<div [class]=\"listWrapperClass()\">\n <ul [class]=\"listClass()\" #tabListElement role=\"tablist\" [attr.aria-orientation]=\"orientation()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let itemDisabled = tab.disabled || disabled();\n <li\n monaTabListItem\n [id]=\"tab.id + '-tab'\"\n [active]=\"selected\"\n [disabled]=\"itemDisabled\"\n [position]=\"position()\"\n [size]=\"size()\"\n role=\"tab\"\n [tabindex]=\"selected ? 0 : -1\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"itemDisabled\"\n [attr.aria-controls]=\"tab.id + '-panel'\"\n [attr.data-selected]=\"selected\"\n [attr.data-tab-id]=\"tab.id\"\n (click)=\"onTabClick(tab, tabListElement, $event)\">\n <div>\n @if (tab.titleTemplate) {\n <ng-container [ngTemplateOutlet]=\"tab.titleTemplate\"></ng-container>\n } @else {\n <span>{{ tab.title }}</span>\n }\n </div>\n @if (tab.closable || closable()) {\n <button\n monaButton\n look=\"link\"\n [disabled]=\"itemDisabled\"\n (click)=\"onTabClose(tab, $event)\"\n class=\"px-0 h-fit ml-1 w-min\"\n tabindex=\"-1\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </li>\n }\n </ul>\n</div>\n@if (overflowControlsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'next', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'next')\"\n (pointerup)=\"stopScrolling()\"\n (pointercancel)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n @if (orientation() === 'vertical') {\n <svg lucideChevronDown [size]=\"16\"></svg>\n } @else {\n <svg lucideChevronRight [size]=\"16\"></svg>\n }\n </button>\n}\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: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TabListItemDirective, selector: "li[monaTabListItem]", inputs: ["active", "disabled", "position", "size"] }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideChevronUp, selector: "svg[lucideChevronUp]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] }); }
406
460
  }
407
461
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabListComponent, decorators: [{
408
462
  type: Component,
409
- args: [{ selector: "mona-tab-list", imports: [ButtonDirective, NgTemplateOutlet, TabListItemDirective, LucideChevronLeft, LucideChevronRight, LucideX], host: {
463
+ args: [{ selector: "mona-tab-list", imports: [
464
+ ButtonDirective,
465
+ NgTemplateOutlet,
466
+ TabListItemDirective,
467
+ LucideChevronDown,
468
+ LucideChevronLeft,
469
+ LucideChevronRight,
470
+ LucideChevronUp,
471
+ LucideX
472
+ ], host: {
410
473
  "[class]": "baseClass()",
411
474
  "(keydown)": "onKeydown($event)"
412
- }, template: "@if (scrollsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'left', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'left')\"\n (pointerup)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n <svg lucideChevronLeft [size]=\"16\"></svg>\n </button>\n}\n<div class=\"w-full h-full overflow-hidden\">\n <ul [class]=\"listClass()\" #tabListElement role=\"tablist\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let itemDisabled = tab.disabled || disabled();\n <li\n monaTabListItem\n [id]=\"tab.id + '-tab'\"\n [active]=\"selected\"\n [disabled]=\"itemDisabled\"\n [rounded]=\"rounded()\"\n role=\"tab\"\n [tabindex]=\"selected ? 0 : -1\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"itemDisabled\"\n [attr.aria-controls]=\"tab.id + '-panel'\"\n [attr.data-selected]=\"selected\"\n [attr.data-tab-id]=\"tab.id\"\n (click)=\"onTabClick(tab, tabListElement, $event)\">\n <div>\n @if (tab.titleTemplate) {\n <ng-container [ngTemplateOutlet]=\"tab.titleTemplate\"></ng-container>\n } @else {\n <span>{{ tab.title }}</span>\n }\n </div>\n @if (tab.closable || closable()) {\n <button\n monaButton\n look=\"link\"\n [disabled]=\"itemDisabled\"\n (click)=\"onTabClose(tab, $event)\"\n class=\"px-0 h-fit ml-1 w-min\"\n tabindex=\"-1\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </li>\n }\n </ul>\n</div>\n@if (scrollsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'right', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'right')\"\n (pointerup)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n <svg lucideChevronRight [size]=\"16\"></svg>\n </button>\n}\n" }]
413
- }], ctorParameters: () => [], propDecorators: { tabListElement: [{ type: i0.ViewChild, args: ["tabListElement", { isSignal: true }] }], tabListItems: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => TabListItemDirective), { ...{ read: ElementRef }, isSignal: true }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: true }] }], selectedTabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedTabId", required: false }] }, { type: i0.Output, args: ["selectedTabIdChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }], tabClose: [{ type: i0.Output, args: ["tabClose"] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }], tabList: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabList", required: true }] }] } });
475
+ }, template: "@if (overflowControlsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'previous', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'previous')\"\n (pointerup)=\"stopScrolling()\"\n (pointercancel)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n @if (orientation() === 'vertical') {\n <svg lucideChevronUp [size]=\"16\"></svg>\n } @else {\n <svg lucideChevronLeft [size]=\"16\"></svg>\n }\n </button>\n}\n<div [class]=\"listWrapperClass()\">\n <ul [class]=\"listClass()\" #tabListElement role=\"tablist\" [attr.aria-orientation]=\"orientation()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let itemDisabled = tab.disabled || disabled();\n <li\n monaTabListItem\n [id]=\"tab.id + '-tab'\"\n [active]=\"selected\"\n [disabled]=\"itemDisabled\"\n [position]=\"position()\"\n [size]=\"size()\"\n role=\"tab\"\n [tabindex]=\"selected ? 0 : -1\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"itemDisabled\"\n [attr.aria-controls]=\"tab.id + '-panel'\"\n [attr.data-selected]=\"selected\"\n [attr.data-tab-id]=\"tab.id\"\n (click)=\"onTabClick(tab, tabListElement, $event)\">\n <div>\n @if (tab.titleTemplate) {\n <ng-container [ngTemplateOutlet]=\"tab.titleTemplate\"></ng-container>\n } @else {\n <span>{{ tab.title }}</span>\n }\n </div>\n @if (tab.closable || closable()) {\n <button\n monaButton\n look=\"link\"\n [disabled]=\"itemDisabled\"\n (click)=\"onTabClose(tab, $event)\"\n class=\"px-0 h-fit ml-1 w-min\"\n tabindex=\"-1\">\n <svg lucideX [size]=\"16\"></svg>\n </button>\n }\n </li>\n }\n </ul>\n</div>\n@if (overflowControlsVisible()) {\n <button\n monaButton\n look=\"link\"\n size=\"small\"\n [class]=\"scrollButtonClass()\"\n (click)=\"startScrolling(tabListElement, 'next', 'single')\"\n (pointerdown)=\"startContinuousScroll($event, tabListElement, 'next')\"\n (pointerup)=\"stopScrolling()\"\n (pointercancel)=\"stopScrolling()\"\n [attr.aria-hidden]=\"true\"\n tabindex=\"-1\">\n @if (orientation() === 'vertical') {\n <svg lucideChevronDown [size]=\"16\"></svg>\n } @else {\n <svg lucideChevronRight [size]=\"16\"></svg>\n }\n </button>\n}\n" }]
476
+ }], ctorParameters: () => [], propDecorators: { tabListElement: [{ type: i0.ViewChild, args: ["tabListElement", { isSignal: true }] }], tabListItems: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => TabListItemDirective), { ...{ read: ElementRef }, isSignal: true }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: true }] }], selectedTabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedTabId", required: false }] }, { type: i0.Output, args: ["selectedTabIdChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }], tabClose: [{ type: i0.Output, args: ["tabClose"] }], tabList: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabList", required: true }] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }] } });
414
477
 
415
478
  class TabComponent {
416
479
  constructor() {
@@ -488,14 +551,13 @@ class TabsComponent {
488
551
  this.tabs = contentChildren(TabComponent, /* @ts-ignore */
489
552
  ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
490
553
  this.baseClass = computed(() => {
491
- const variantClass = tabsBaseThemeVariants();
554
+ const variantClass = tabsBaseThemeVariants({ position: this.position() });
492
555
  const userClass = this.userClass();
493
556
  return twMerge(variantClass, userClass);
494
557
  }, /* @ts-ignore */
495
558
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
496
559
  this.contentClass = computed(() => {
497
- const rounded = this.rounded() === "full" ? "large" : this.rounded();
498
- return tabContentThemeVariants({ rounded });
560
+ return tabContentThemeVariants();
499
561
  }, /* @ts-ignore */
500
562
  ...(ngDevMode ? [{ debugName: "contentClass" }] : /* istanbul ignore next */ []));
501
563
  this.selectedTabId = linkedSignal({ ...(ngDevMode ? { debugName: "selectedTabId" } : /* istanbul ignore next */ {}), source: () => this.tabList(),
@@ -532,11 +594,11 @@ class TabsComponent {
532
594
  this.keepTabContent = input(true, /* @ts-ignore */
533
595
  ...(ngDevMode ? [{ debugName: "keepTabContent" }] : /* istanbul ignore next */ []));
534
596
  /**
535
- * @description Border-radius preset applied to the tabs and the tab content area.
536
- * @default "medium"
597
+ * @description Placement of the tab list relative to the tab content.
598
+ * @default "top"
537
599
  */
538
- this.rounded = input("medium", /* @ts-ignore */
539
- ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
600
+ this.position = input("top", /* @ts-ignore */
601
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
540
602
  /**
541
603
  * @description Size preset controlling the tabs' dimensions.
542
604
  * @default "medium"
@@ -567,14 +629,14 @@ class TabsComponent {
567
629
  }
568
630
  }
569
631
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
570
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TabsComponent, isStandalone: true, selector: "mona-tabs", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, keepTabContent: { classPropertyName: "keepTabContent", publicName: "keepTabContent", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabClose: "tabClose", tabSelect: "tabSelect" }, host: { properties: { "class": "baseClass()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], viewQueries: [{ propertyName: "tabListComponent", first: true, predicate: TabListComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<mona-tab-list\n [closable]=\"closable()\"\n [disabled]=\"disabled()\"\n [rounded]=\"rounded()\"\n [size]=\"size()\"\n [tabList]=\"tabList()\"\n [selectedTabId]=\"selectedTabId()\"\n (selectedTabIdChange)=\"selectedTabId.set($event)\"\n (tabClose)=\"tabClose.emit($event)\"\n (tabSelect)=\"tabSelect.emit($event)\"></mona-tab-list>\n<div [class]=\"contentClass()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let tabLoaded = keepTabContent() || selected;\n @let tabHidden = keepTabContent() && !selected;\n @if (tabLoaded) {\n <div\n class=\"w-full h-full outline-none\"\n [class.hidden]=\"tabHidden\"\n role=\"tabpanel\"\n [id]=\"tab.id + '-panel'\"\n tabindex=\"0\"\n (keydown)=\"handlePanelKeyDown($event)\"\n [attr.aria-hidden]=\"tabHidden\"\n [attr.aria-labelledby]=\"tab.id + '-tab'\">\n <ng-container [ngTemplateOutlet]=\"tab.contentTemplate\"></ng-container>\n </div>\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TabListComponent, selector: "mona-tab-list", inputs: ["closable", "disabled", "rounded", "selectedTabId", "size", "tabList"], outputs: ["selectedTabIdChange", "tabClose", "tabSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
632
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: TabsComponent, isStandalone: true, selector: "mona-tabs", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, keepTabContent: { classPropertyName: "keepTabContent", publicName: "keepTabContent", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabClose: "tabClose", tabSelect: "tabSelect" }, host: { properties: { "class": "baseClass()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], viewQueries: [{ propertyName: "tabListComponent", first: true, predicate: TabListComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<mona-tab-list\n [closable]=\"closable()\"\n [disabled]=\"disabled()\"\n [position]=\"position()\"\n [size]=\"size()\"\n [tabList]=\"tabList()\"\n [selectedTabId]=\"selectedTabId()\"\n (selectedTabIdChange)=\"selectedTabId.set($event)\"\n (tabClose)=\"tabClose.emit($event)\"\n (tabSelect)=\"tabSelect.emit($event)\"></mona-tab-list>\n<div [class]=\"contentClass()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let tabLoaded = keepTabContent() || selected;\n @let tabHidden = keepTabContent() && !selected;\n @if (tabLoaded) {\n <div\n class=\"w-full h-full outline-none rounded-sm focus-visible:outline-2 focus-visible:outline-focus-indicator focus-visible:outline-offset-2\"\n [class.hidden]=\"tabHidden\"\n role=\"tabpanel\"\n [id]=\"tab.id + '-panel'\"\n tabindex=\"0\"\n (keydown)=\"handlePanelKeyDown($event)\"\n [attr.aria-hidden]=\"tabHidden\"\n [attr.aria-labelledby]=\"tab.id + '-tab'\">\n <ng-container [ngTemplateOutlet]=\"tab.contentTemplate\"></ng-container>\n </div>\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TabListComponent, selector: "mona-tab-list", inputs: ["closable", "disabled", "position", "selectedTabId", "size", "tabList"], outputs: ["selectedTabIdChange", "tabClose", "tabSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
571
633
  }
572
634
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: TabsComponent, decorators: [{
573
635
  type: Component,
574
636
  args: [{ selector: "mona-tabs", changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, TabListComponent], host: {
575
637
  "[class]": "baseClass()"
576
- }, template: "<mona-tab-list\n [closable]=\"closable()\"\n [disabled]=\"disabled()\"\n [rounded]=\"rounded()\"\n [size]=\"size()\"\n [tabList]=\"tabList()\"\n [selectedTabId]=\"selectedTabId()\"\n (selectedTabIdChange)=\"selectedTabId.set($event)\"\n (tabClose)=\"tabClose.emit($event)\"\n (tabSelect)=\"tabSelect.emit($event)\"></mona-tab-list>\n<div [class]=\"contentClass()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let tabLoaded = keepTabContent() || selected;\n @let tabHidden = keepTabContent() && !selected;\n @if (tabLoaded) {\n <div\n class=\"w-full h-full outline-none\"\n [class.hidden]=\"tabHidden\"\n role=\"tabpanel\"\n [id]=\"tab.id + '-panel'\"\n tabindex=\"0\"\n (keydown)=\"handlePanelKeyDown($event)\"\n [attr.aria-hidden]=\"tabHidden\"\n [attr.aria-labelledby]=\"tab.id + '-tab'\">\n <ng-container [ngTemplateOutlet]=\"tab.contentTemplate\"></ng-container>\n </div>\n }\n }\n</div>\n" }]
577
- }], propDecorators: { tabListComponent: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TabListComponent), { isSignal: true }] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], keepTabContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepTabContent", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tabClose: [{ type: i0.Output, args: ["tabClose"] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
638
+ }, template: "<mona-tab-list\n [closable]=\"closable()\"\n [disabled]=\"disabled()\"\n [position]=\"position()\"\n [size]=\"size()\"\n [tabList]=\"tabList()\"\n [selectedTabId]=\"selectedTabId()\"\n (selectedTabIdChange)=\"selectedTabId.set($event)\"\n (tabClose)=\"tabClose.emit($event)\"\n (tabSelect)=\"tabSelect.emit($event)\"></mona-tab-list>\n<div [class]=\"contentClass()\">\n @for (tab of tabList(); track tab) {\n @let selected = tab.id === selectedTabId();\n @let tabLoaded = keepTabContent() || selected;\n @let tabHidden = keepTabContent() && !selected;\n @if (tabLoaded) {\n <div\n class=\"w-full h-full outline-none rounded-sm focus-visible:outline-2 focus-visible:outline-focus-indicator focus-visible:outline-offset-2\"\n [class.hidden]=\"tabHidden\"\n role=\"tabpanel\"\n [id]=\"tab.id + '-panel'\"\n tabindex=\"0\"\n (keydown)=\"handlePanelKeyDown($event)\"\n [attr.aria-hidden]=\"tabHidden\"\n [attr.aria-labelledby]=\"tab.id + '-tab'\">\n <ng-container [ngTemplateOutlet]=\"tab.contentTemplate\"></ng-container>\n </div>\n }\n }\n</div>\n" }]
639
+ }], propDecorators: { tabListComponent: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TabListComponent), { isSignal: true }] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], keepTabContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepTabContent", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tabClose: [{ type: i0.Output, args: ["tabClose"] }], tabSelect: [{ type: i0.Output, args: ["tabSelect"] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
578
640
 
579
641
  /*
580
642
  * Public API Surface of @nanahoshi/mona-ui/tabs
@@ -359,9 +359,7 @@ const components = Object.freeze({
359
359
  "--mona-menubar-background": "var(--color-surface-muted)",
360
360
  "--mona-menubar-shadow": "none",
361
361
  "--mona-pager-background": "var(--color-surface-muted)",
362
- "--mona-slider-handle-border-color": "var(--color-focus-indicator)",
363
- "--mona-tab-list-background": "var(--color-surface-muted)",
364
- "--mona-tab-content-background": "transparent"
362
+ "--mona-slider-handle-border-color": "var(--color-focus-indicator)"
365
363
  });
366
364
  const annaTheme = Object.freeze({
367
365
  name: "anna",
@@ -601,9 +599,7 @@ const lunaTheme = Object.freeze({
601
599
  "--mona-menubar-background": "var(--mona-effect-control-background-color)",
602
600
  "--mona-menubar-shadow": "var(--shadow-control)",
603
601
  "--mona-pager-background": "var(--mona-effect-raised-background-color)",
604
- "--mona-slider-handle-border-color": "var(--color-border-control)",
605
- "--mona-tab-list-background": "#f5f5f6",
606
- "--mona-tab-content-background": "var(--color-surface)"
602
+ "--mona-slider-handle-border-color": "var(--color-border-control)"
607
603
  })
608
604
  }),
609
605
  dark: Object.freeze({
@@ -648,9 +644,7 @@ const lunaTheme = Object.freeze({
648
644
  "--mona-menubar-background": "var(--mona-effect-control-background-color)",
649
645
  "--mona-menubar-shadow": "var(--shadow-control)",
650
646
  "--mona-pager-background": "var(--mona-effect-raised-background-color)",
651
- "--mona-slider-handle-border-color": "var(--color-border-control)",
652
- "--mona-tab-list-background": "var(--mona-effect-raised-background-color)",
653
- "--mona-tab-content-background": "var(--color-surface)"
647
+ "--mona-slider-handle-border-color": "var(--color-border-control)"
654
648
  })
655
649
  })
656
650
  }
@@ -876,9 +870,7 @@ function createComponents() {
876
870
  "--mona-menubar-background": "var(--color-surface-muted)",
877
871
  "--mona-menubar-shadow": "var(--shadow-raised)",
878
872
  "--mona-pager-background": "var(--color-surface-muted)",
879
- "--mona-slider-handle-border-color": "color-mix(in srgb, var(--color-focus-indicator) 50%, transparent)",
880
- "--mona-tab-list-background": "var(--color-surface-muted)",
881
- "--mona-tab-content-background": "var(--color-surface)"
873
+ "--mona-slider-handle-border-color": "color-mix(in srgb, var(--color-focus-indicator) 50%, transparent)"
882
874
  });
883
875
  }
884
876
  const monaTheme = Object.freeze({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanahoshi/mona-ui",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "A very light Angular UI framework",
5
5
  "author": "Nanahoshi",
6
6
  "repository": {
@@ -265,6 +265,10 @@
265
265
  "types": "./types/nanahoshi-mona-ui-scroll-view.d.ts",
266
266
  "default": "./fesm2022/nanahoshi-mona-ui-scroll-view.mjs"
267
267
  },
268
+ "./segmented": {
269
+ "types": "./types/nanahoshi-mona-ui-segmented.d.ts",
270
+ "default": "./fesm2022/nanahoshi-mona-ui-segmented.mjs"
271
+ },
268
272
  "./sheet": {
269
273
  "types": "./types/nanahoshi-mona-ui-sheet.d.ts",
270
274
  "default": "./fesm2022/nanahoshi-mona-ui-sheet.mjs"
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/nanahoshi-mona-ui-segmented.mjs",
3
+ "typings": "../types/nanahoshi-mona-ui-segmented.d.ts"
4
+ }
@@ -0,0 +1,90 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { FormValueControl } from '@angular/forms/signals';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+ import { VariantInputs } from '@nanahoshi/mona-ui/internal';
6
+
7
+ type SegmentedValue = string | number;
8
+
9
+ interface SegmentedOption<T extends SegmentedValue = SegmentedValue> {
10
+ readonly label: string;
11
+ readonly value: T;
12
+ readonly disabled?: boolean;
13
+ }
14
+
15
+ declare const segmentedContainerThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
16
+ declare const segmentedOptionThemeVariants: (props?: ({
17
+ size?: "small" | "medium" | "large" | null | undefined;
18
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
19
+ type SegmentedContainerVariantProps = VariantProps<typeof segmentedContainerThemeVariants>;
20
+ type SegmentedContainerVariantInput = VariantInputs<SegmentedContainerVariantProps>;
21
+ type SegmentedOptionVariantProps = VariantProps<typeof segmentedOptionThemeVariants>;
22
+ type SegmentedOptionVariantInput = VariantInputs<SegmentedOptionVariantProps>;
23
+ type SegmentedVariantProps = SegmentedContainerVariantProps & SegmentedOptionVariantProps;
24
+ type SegmentedVariantInput = SegmentedContainerVariantInput & SegmentedOptionVariantInput;
25
+
26
+ declare class SegmentedComponent<T extends SegmentedValue = SegmentedValue> implements SegmentedVariantInput, FormValueControl<T | null> {
27
+ protected readonly containerClasses: _angular_core.Signal<string>;
28
+ protected readonly groupName: string;
29
+ protected readonly inputClasses: _angular_core.Signal<string>;
30
+ protected readonly invalidState: _angular_core.Signal<boolean>;
31
+ protected readonly optionClasses: _angular_core.Signal<string>;
32
+ /**
33
+ * @description Accessible name for the radio group. Provide either `aria-label` or `aria-labelledby`.
34
+ * @default null
35
+ */
36
+ readonly ariaLabel: _angular_core.InputSignal<string | null>;
37
+ /**
38
+ * @description ID of an external element that provides the accessible name for the radio group.
39
+ * @default null
40
+ */
41
+ readonly ariaLabelledBy: _angular_core.InputSignal<string | null>;
42
+ /**
43
+ * @description Disables every option in the group and prevents value changes.
44
+ * @default false
45
+ */
46
+ readonly disabled: _angular_core.InputSignal<boolean>;
47
+ /**
48
+ * @description Marks the component as invalid. Error styling requires both `invalid` and `touched` to be `true`.
49
+ * When bound to a signal form field via `[formField]`, this is written by the signal forms `Field` directive.
50
+ * @default false
51
+ */
52
+ readonly invalid: _angular_core.InputSignal<boolean>;
53
+ /**
54
+ * @description The list of selectable options. Exactly one option is selected at a time.
55
+ */
56
+ readonly options: _angular_core.InputSignal<readonly SegmentedOption<T>[]>;
57
+ /**
58
+ * @description Size preset controlling the height, horizontal padding, text size, and spacing of each option.
59
+ * @default "medium"
60
+ */
61
+ readonly size: _angular_core.InputSignal<"small" | "medium" | "large" | null | undefined>;
62
+ /**
63
+ * @description Emitted when the selected value changes or focus leaves a radio input.
64
+ */
65
+ readonly touch: _angular_core.OutputEmitterRef<void>;
66
+ /**
67
+ * @description Marks the component as touched. When bound to a signal form field via `[formField]`,
68
+ * this is written by the signal forms `Field` directive.
69
+ * @default false
70
+ */
71
+ readonly touched: _angular_core.InputSignal<boolean>;
72
+ /**
73
+ * @description Additional CSS classes merged onto the host element via `tailwind-merge`.
74
+ * @default ""
75
+ */
76
+ readonly userClass: _angular_core.InputSignal<string>;
77
+ /**
78
+ * @description The currently selected value. When it matches no current option, no option is checked.
79
+ * A `null` value is allowed initially but cannot be restored through segmented interaction.
80
+ * @default null
81
+ */
82
+ readonly value: _angular_core.ModelSignal<T | null>;
83
+ protected onOptionBlur(): void;
84
+ protected onOptionChange(option: SegmentedOption<T>): void;
85
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SegmentedComponent<any>, never>;
86
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SegmentedComponent<any>, "mona-segmented", never, { "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "ariaLabelledBy": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "touch": "touch"; "value": "valueChange"; }, never, never, true, never>;
87
+ }
88
+
89
+ export { SegmentedComponent };
90
+ export type { SegmentedOption, SegmentedValue, SegmentedVariantProps };
@@ -19,6 +19,8 @@ declare class TabSelectEvent extends PreventableEvent {
19
19
  get index(): number;
20
20
  }
21
21
 
22
+ type TabsPosition = "top" | "bottom" | "left" | "right";
23
+
22
24
  declare class TabContentTemplateDirective {
23
25
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabContentTemplateDirective, never>;
24
26
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabContentTemplateDirective, "ng-template[monaTabContentTemplate]", never, {}, {}, never, never, true, never>;
@@ -41,21 +43,23 @@ interface TabItem {
41
43
  }
42
44
 
43
45
  declare const tabListBaseThemeVariants: (props?: ({
44
- rounded?: "small" | "medium" | "large" | "full" | "none" | null | undefined;
45
- size?: "small" | "medium" | "large" | null | undefined;
46
+ position?: "top" | "bottom" | "left" | "right" | null | undefined;
46
47
  } & class_variance_authority_types.ClassProp) | undefined) => string;
47
48
  declare const tabListListWrapperThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
48
- declare const tabListListThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
49
+ declare const tabListListThemeVariants: (props?: ({
50
+ position?: "top" | "bottom" | "left" | "right" | null | undefined;
51
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
49
52
  declare const tabListListItemThemeVariants: (props?: ({
50
53
  active?: boolean | null | undefined;
51
54
  disabled?: boolean | null | undefined;
52
- rounded?: "small" | "medium" | "large" | "full" | "none" | null | undefined;
55
+ position?: "top" | "bottom" | "left" | "right" | null | undefined;
56
+ size?: "small" | "medium" | "large" | null | undefined;
53
57
  } & class_variance_authority_types.ClassProp) | undefined) => string;
54
58
  declare const tabListScrollButtonThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
55
- declare const tabContentThemeVariants: (props?: ({
56
- rounded?: "small" | "medium" | "large" | "full" | "none" | null | undefined;
59
+ declare const tabContentThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
60
+ declare const tabsBaseThemeVariants: (props?: ({
61
+ position?: "top" | "bottom" | "left" | "right" | null | undefined;
57
62
  } & class_variance_authority_types.ClassProp) | undefined) => string;
58
- declare const tabsBaseThemeVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
59
63
  type TabListBaseVariantProps = VariantProps<typeof tabListBaseThemeVariants>;
60
64
  type TabListBaseVariantInput = VariantInputs<TabListBaseVariantProps>;
61
65
  type TabListListWrapperVariantProps = VariantProps<typeof tabListListWrapperThemeVariants>;
@@ -96,10 +100,10 @@ declare class TabsComponent implements TabsVariantInput {
96
100
  */
97
101
  readonly keepTabContent: _angular_core.InputSignal<boolean>;
98
102
  /**
99
- * @description Border-radius preset applied to the tabs and the tab content area.
100
- * @default "medium"
103
+ * @description Placement of the tab list relative to the tab content.
104
+ * @default "top"
101
105
  */
102
- readonly rounded: _angular_core.InputSignal<"small" | "medium" | "large" | "full" | "none" | null | undefined>;
106
+ readonly position: _angular_core.InputSignal<"top" | "bottom" | "left" | "right" | null | undefined>;
103
107
  /**
104
108
  * @description Size preset controlling the tabs' dimensions.
105
109
  * @default "medium"
@@ -120,7 +124,7 @@ declare class TabsComponent implements TabsVariantInput {
120
124
  readonly userClass: _angular_core.InputSignal<string>;
121
125
  protected handlePanelKeyDown(event: KeyboardEvent): void;
122
126
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabsComponent, never>;
123
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TabsComponent, "mona-tabs", never, { "closable": { "alias": "closable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "keepTabContent": { "alias": "keepTabContent"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; }, { "tabClose": "tabClose"; "tabSelect": "tabSelect"; }, ["tabs"], never, true, never>;
127
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TabsComponent, "mona-tabs", never, { "closable": { "alias": "closable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "keepTabContent": { "alias": "keepTabContent"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; }, { "tabClose": "tabClose"; "tabSelect": "tabSelect"; }, ["tabs"], never, true, never>;
124
128
  }
125
129
 
126
130
  declare class TabComponent {
@@ -154,3 +158,4 @@ declare class TabComponent {
154
158
  }
155
159
 
156
160
  export { TabCloseEvent, TabComponent, TabContentTemplateDirective, TabSelectEvent, TabTitleTemplateDirective, TabsComponent };
161
+ export type { TabsPosition };
@@ -23,7 +23,7 @@ type ThemeEffectVariable = `--mona-effect-${ThemeEffectLevel}-background-color`
23
23
  type ThemeEffects = Readonly<Record<ThemeEffectVariable, string>>;
24
24
  type ThemeShapeVariable = "--radius-sm" | "--radius-md" | "--radius-lg";
25
25
  type ThemeShape = Readonly<Record<ThemeShapeVariable, string>>;
26
- type ThemeComponentVariable = "--mona-calendar-background" | "--mona-calendar-shadow" | "--mona-list-background" | "--mona-list-disabled-background" | "--mona-list-group-background" | "--mona-list-group-border-width" | "--mona-list-group-font-weight" | "--mona-menubar-background" | "--mona-menubar-shadow" | "--mona-pager-background" | "--mona-slider-handle-border-color" | "--mona-tab-list-background" | "--mona-tab-content-background";
26
+ type ThemeComponentVariable = "--mona-calendar-background" | "--mona-calendar-shadow" | "--mona-list-background" | "--mona-list-disabled-background" | "--mona-list-group-background" | "--mona-list-group-border-width" | "--mona-list-group-font-weight" | "--mona-menubar-background" | "--mona-menubar-shadow" | "--mona-pager-background" | "--mona-slider-handle-border-color";
27
27
  type ThemeComponents = Readonly<Record<ThemeComponentVariable, string>>;
28
28
  interface ThemeProfile {
29
29
  readonly colors: ThemeColors;