ng-blatui 1.0.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,1831 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import * as i0 from '@angular/core';
4
+ import { input, computed, Directive, Component, signal, model, inject, PLATFORM_ID, effect, Injectable } from '@angular/core';
5
+ import { cva } from 'class-variance-authority';
6
+ export { AccordionContent, AccordionGroup, AccordionPanel, AccordionTrigger, ɵɵDeferredContent, ɵɵDeferredContentAware } from '@angular/aria/accordion';
7
+ export { Tab, TabContent, TabList, TabPanel, Tabs } from '@angular/aria/tabs';
8
+ export { DIALOG_DATA, Dialog, DialogModule, DialogRef } from '@angular/cdk/dialog';
9
+ import { isPlatformBrowser } from '@angular/common';
10
+
11
+ /**
12
+ * Merge class names with `clsx` semantics and resolve Tailwind conflicts with
13
+ * `tailwind-merge`. The single utility every ng-blatui component uses to combine
14
+ * its variant classes with consumer-provided ones.
15
+ */
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+
20
+ const buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
21
+ variants: {
22
+ variant: {
23
+ default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
24
+ destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40',
25
+ outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
26
+ secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
27
+ ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
28
+ link: 'text-primary underline-offset-4 hover:underline',
29
+ },
30
+ size: {
31
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
32
+ xs: 'h-7 gap-1 rounded-md px-2.5 text-xs has-[>svg]:px-2 [&_svg:not([class*=size-])]:size-3.5',
33
+ sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
34
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
35
+ icon: 'size-9',
36
+ 'icon-xs': 'size-7 [&_svg:not([class*=size-])]:size-3.5',
37
+ 'icon-sm': 'size-8',
38
+ 'icon-lg': 'size-10',
39
+ },
40
+ },
41
+ defaultVariants: { variant: 'default', size: 'default' },
42
+ });
43
+ /**
44
+ * Applies BlatUI button styling to a native `<button>` or `<a>`, keeping native
45
+ * semantics, focus and keyboard behavior (accessibility for free).
46
+ */
47
+ class BuiButton {
48
+ variant = input('default', /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
50
+ size = input('default', /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
52
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
53
+ computedClass = computed(() => cn(buttonVariants({ variant: this.variant(), size: this.size() }), this.userClass()), /* @ts-ignore */
54
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
55
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
56
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiButton, isStandalone: true, selector: "button[buiButton], a[buiButton]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", 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 } }, host: { attributes: { "data-slot": "button" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
57
+ }
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiButton, decorators: [{
59
+ type: Directive,
60
+ args: [{
61
+ selector: 'button[buiButton], a[buiButton]',
62
+ host: {
63
+ 'data-slot': 'button',
64
+ '[class]': 'computedClass()',
65
+ },
66
+ }]
67
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
68
+
69
+ const BASE = 'inline-flex items-center justify-center rounded-md border font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden';
70
+ const SIZES = {
71
+ sm: 'px-1.5 py-px text-[0.625rem]',
72
+ default: 'px-2 py-0.5 text-xs',
73
+ lg: 'px-3 py-1 text-sm [&>svg]:size-3.5',
74
+ };
75
+ const VARIANTS = {
76
+ default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
77
+ secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
78
+ destructive: 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
79
+ outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
80
+ };
81
+ const TONES = {
82
+ success: {
83
+ soft: 'border-transparent bg-success/10 text-success dark:bg-success/15',
84
+ solid: 'border-transparent bg-success text-success-foreground',
85
+ outline: 'text-success border-success/40',
86
+ },
87
+ warning: {
88
+ soft: 'border-transparent bg-warning/10 text-warning dark:bg-warning/15',
89
+ solid: 'border-transparent bg-warning text-warning-foreground',
90
+ outline: 'text-warning border-warning/40',
91
+ },
92
+ danger: {
93
+ soft: 'border-transparent bg-destructive/10 text-destructive dark:bg-destructive/15',
94
+ solid: 'border-transparent bg-destructive text-destructive-foreground',
95
+ outline: 'text-destructive border-destructive/40',
96
+ },
97
+ info: {
98
+ soft: 'border-transparent bg-info/10 text-info dark:bg-info/15',
99
+ solid: 'border-transparent bg-info text-info-foreground',
100
+ outline: 'text-info border-info/40',
101
+ },
102
+ neutral: {
103
+ soft: 'border-transparent bg-muted text-muted-foreground',
104
+ solid: 'border-transparent bg-foreground/85 text-background',
105
+ outline: 'text-muted-foreground border-border',
106
+ },
107
+ };
108
+ function brandClass(variant) {
109
+ switch (variant) {
110
+ case 'secondary': {
111
+ return VARIANTS.secondary;
112
+ }
113
+ case 'destructive': {
114
+ return VARIANTS.destructive;
115
+ }
116
+ case 'outline': {
117
+ return VARIANTS.outline;
118
+ }
119
+ default: {
120
+ return VARIANTS.default;
121
+ }
122
+ }
123
+ }
124
+ function intensityFor(variant) {
125
+ return variant === 'solid' || variant === 'outline' ? variant : 'soft';
126
+ }
127
+ /**
128
+ * BlatUI badge. Brand variants (`default | secondary | destructive | outline`)
129
+ * or semantic `tone` (`success | warning | danger | info | neutral`) where
130
+ * `variant` then selects intensity (`soft | solid | outline`). Built on a native
131
+ * element so it stays in the document flow and screen-reader friendly.
132
+ */
133
+ class BuiBadge {
134
+ variant = input('default', /* @ts-ignore */
135
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
136
+ tone = input(null, /* @ts-ignore */
137
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
138
+ size = input('default', /* @ts-ignore */
139
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
140
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
141
+ computedClass = computed(() => {
142
+ const tone = this.tone();
143
+ const toneOrVariant = tone === null ? brandClass(this.variant()) : TONES[tone][intensityFor(this.variant())];
144
+ return cn(BASE, SIZES[this.size()], toneOrVariant, this.userClass());
145
+ }, /* @ts-ignore */
146
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
147
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiBadge, deps: [], target: i0.ɵɵFactoryTarget.Directive });
148
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiBadge, isStandalone: true, selector: "[buiBadge]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", 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 } }, host: { attributes: { "data-slot": "badge" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
149
+ }
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiBadge, decorators: [{
151
+ type: Directive,
152
+ args: [{
153
+ selector: '[buiBadge]',
154
+ host: {
155
+ 'data-slot': 'badge',
156
+ '[class]': 'computedClass()',
157
+ },
158
+ }]
159
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
160
+
161
+ /**
162
+ * BlatUI label. Apply to a native `<label>` (use `for`/`id` to associate it with
163
+ * a control) so the click-to-focus and screen-reader association work natively.
164
+ */
165
+ class BuiLabel {
166
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
167
+ computedClass = computed(() => cn('flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50', this.userClass()), /* @ts-ignore */
168
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
169
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
170
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiLabel, isStandalone: true, selector: "label[buiLabel]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "label" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
171
+ }
172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiLabel, decorators: [{
173
+ type: Directive,
174
+ args: [{
175
+ selector: 'label[buiLabel]',
176
+ host: {
177
+ 'data-slot': 'label',
178
+ '[class]': 'computedClass()',
179
+ },
180
+ }]
181
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
182
+
183
+ /**
184
+ * BlatUI separator. `decorative` (default) renders `role="none"`; set
185
+ * `[decorative]="false"` for a semantic `role="separator"` with `aria-orientation`.
186
+ */
187
+ class BuiSeparator {
188
+ orientation = input('horizontal', /* @ts-ignore */
189
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
190
+ decorative = input(true, /* @ts-ignore */
191
+ ...(ngDevMode ? [{ debugName: "decorative" }] : /* istanbul ignore next */ []));
192
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
193
+ computedClass = computed(() => cn('shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px', this.userClass()), /* @ts-ignore */
194
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
195
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
196
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiSeparator, isStandalone: true, selector: "[buiSeparator]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, decorative: { classPropertyName: "decorative", publicName: "decorative", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "separator" }, properties: { "attr.role": "decorative() ? 'none' : 'separator'", "attr.aria-orientation": "decorative() ? null : orientation()", "attr.data-orientation": "orientation()", "class": "computedClass()" } }, ngImport: i0 });
197
+ }
198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSeparator, decorators: [{
199
+ type: Directive,
200
+ args: [{
201
+ selector: '[buiSeparator]',
202
+ host: {
203
+ 'data-slot': 'separator',
204
+ '[attr.role]': "decorative() ? 'none' : 'separator'",
205
+ '[attr.aria-orientation]': 'decorative() ? null : orientation()',
206
+ '[attr.data-orientation]': 'orientation()',
207
+ '[class]': 'computedClass()',
208
+ },
209
+ }]
210
+ }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], decorative: [{ type: i0.Input, args: [{ isSignal: true, alias: "decorative", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
211
+
212
+ /**
213
+ * BlatUI skeleton placeholder. Marked `aria-hidden` so assistive tech ignores the
214
+ * shimmering placeholder; announce real loading state separately (e.g. aria-busy).
215
+ */
216
+ class BuiSkeleton {
217
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
218
+ computedClass = computed(() => cn('animate-pulse rounded-md bg-accent', this.userClass()), /* @ts-ignore */
219
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
220
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
221
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiSkeleton, isStandalone: true, selector: "[buiSkeleton]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "skeleton", "aria-hidden": "true" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
222
+ }
223
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSkeleton, decorators: [{
224
+ type: Directive,
225
+ args: [{
226
+ selector: '[buiSkeleton]',
227
+ host: {
228
+ 'data-slot': 'skeleton',
229
+ 'aria-hidden': 'true',
230
+ '[class]': 'computedClass()',
231
+ },
232
+ }]
233
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
234
+
235
+ const CARD_VARIANTS = {
236
+ // Simple padded box — the dominant use case.
237
+ default: 'bg-card text-card-foreground rounded-xl border p-6 shadow-sm',
238
+ // Sectioned layout for card-header / card-content / card-footer (each supplies its own px-6).
239
+ sectioned: 'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
240
+ };
241
+ /** BlatUI card container. Use `variant="sectioned"` with the header/content/footer parts. */
242
+ class BuiCard {
243
+ variant = input('default', /* @ts-ignore */
244
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
245
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
246
+ computedClass = computed(() => cn(CARD_VARIANTS[this.variant()], this.userClass()), /* @ts-ignore */
247
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
248
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCard, deps: [], target: i0.ɵɵFactoryTarget.Directive });
249
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCard, isStandalone: true, selector: "[buiCard]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
250
+ }
251
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCard, decorators: [{
252
+ type: Directive,
253
+ args: [{
254
+ selector: '[buiCard]',
255
+ host: { 'data-slot': 'card', '[class]': 'computedClass()' },
256
+ }]
257
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
258
+ class BuiCardHeader {
259
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
260
+ computedClass = computed(() => cn('@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', this.userClass()), /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
262
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
263
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardHeader, isStandalone: true, selector: "[buiCardHeader]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-header" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
264
+ }
265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardHeader, decorators: [{
266
+ type: Directive,
267
+ args: [{
268
+ selector: '[buiCardHeader]',
269
+ host: { 'data-slot': 'card-header', '[class]': 'computedClass()' },
270
+ }]
271
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
272
+ class BuiCardTitle {
273
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
274
+ computedClass = computed(() => cn('leading-none font-semibold', this.userClass()), /* @ts-ignore */
275
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
276
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
277
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardTitle, isStandalone: true, selector: "[buiCardTitle]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-title" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
278
+ }
279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardTitle, decorators: [{
280
+ type: Directive,
281
+ args: [{
282
+ selector: '[buiCardTitle]',
283
+ host: { 'data-slot': 'card-title', '[class]': 'computedClass()' },
284
+ }]
285
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
286
+ class BuiCardDescription {
287
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
288
+ computedClass = computed(() => cn('text-sm text-muted-foreground', this.userClass()), /* @ts-ignore */
289
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
290
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
291
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardDescription, isStandalone: true, selector: "[buiCardDescription]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-description" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
292
+ }
293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardDescription, decorators: [{
294
+ type: Directive,
295
+ args: [{
296
+ selector: '[buiCardDescription]',
297
+ host: { 'data-slot': 'card-description', '[class]': 'computedClass()' },
298
+ }]
299
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
300
+ class BuiCardAction {
301
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
302
+ computedClass = computed(() => cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', this.userClass()), /* @ts-ignore */
303
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardAction, deps: [], target: i0.ɵɵFactoryTarget.Directive });
305
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardAction, isStandalone: true, selector: "[buiCardAction]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-action" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
306
+ }
307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardAction, decorators: [{
308
+ type: Directive,
309
+ args: [{
310
+ selector: '[buiCardAction]',
311
+ host: { 'data-slot': 'card-action', '[class]': 'computedClass()' },
312
+ }]
313
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
314
+ class BuiCardContent {
315
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
316
+ computedClass = computed(() => cn('px-6', this.userClass()), /* @ts-ignore */
317
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
318
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
319
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardContent, isStandalone: true, selector: "[buiCardContent]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-content" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
320
+ }
321
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardContent, decorators: [{
322
+ type: Directive,
323
+ args: [{
324
+ selector: '[buiCardContent]',
325
+ host: { 'data-slot': 'card-content', '[class]': 'computedClass()' },
326
+ }]
327
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
328
+ class BuiCardFooter {
329
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
330
+ computedClass = computed(() => cn('flex items-center px-6 [.border-t]:pt-6', this.userClass()), /* @ts-ignore */
331
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
332
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardFooter, deps: [], target: i0.ɵɵFactoryTarget.Directive });
333
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiCardFooter, isStandalone: true, selector: "[buiCardFooter]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "card-footer" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
334
+ }
335
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCardFooter, decorators: [{
336
+ type: Directive,
337
+ args: [{
338
+ selector: '[buiCardFooter]',
339
+ host: { 'data-slot': 'card-footer', '[class]': 'computedClass()' },
340
+ }]
341
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
342
+
343
+ const ALERT_BASE = 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current';
344
+ const ALERT_VARIANTS = {
345
+ default: 'bg-card text-card-foreground',
346
+ destructive: 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive',
347
+ };
348
+ const ALERT_TONES = {
349
+ success: 'border-success/20 bg-success/10 text-success [&>svg]:text-success *:data-[slot=alert-description]:text-success',
350
+ warning: 'border-warning/20 bg-warning/10 text-warning [&>svg]:text-warning *:data-[slot=alert-description]:text-warning',
351
+ danger: 'border-destructive/20 bg-destructive/10 text-destructive [&>svg]:text-destructive *:data-[slot=alert-description]:text-destructive',
352
+ info: 'border-info/20 bg-info/10 text-info [&>svg]:text-info *:data-[slot=alert-description]:text-info',
353
+ neutral: 'border-border bg-muted text-foreground [&>svg]:text-foreground *:data-[slot=alert-description]:text-muted-foreground',
354
+ };
355
+ /**
356
+ * BlatUI alert. `role="alert"` for assistive tech. Brand `variant`
357
+ * (`default | destructive`) or semantic `tone` (`success | warning | danger | info | neutral`).
358
+ */
359
+ class BuiAlert {
360
+ variant = input('default', /* @ts-ignore */
361
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
362
+ tone = input(null, /* @ts-ignore */
363
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
364
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
365
+ computedClass = computed(() => {
366
+ const tone = this.tone();
367
+ const toneOrVariant = tone === null ? ALERT_VARIANTS[this.variant()] : ALERT_TONES[tone];
368
+ return cn(ALERT_BASE, toneOrVariant, this.userClass());
369
+ }, /* @ts-ignore */
370
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
371
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlert, deps: [], target: i0.ɵɵFactoryTarget.Directive });
372
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAlert, isStandalone: true, selector: "[buiAlert]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "alert", "role": "alert" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
373
+ }
374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlert, decorators: [{
375
+ type: Directive,
376
+ args: [{
377
+ selector: '[buiAlert]',
378
+ host: { 'data-slot': 'alert', role: 'alert', '[class]': 'computedClass()' },
379
+ }]
380
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
381
+ class BuiAlertTitle {
382
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
383
+ computedClass = computed(() => cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', this.userClass()), /* @ts-ignore */
384
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
385
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlertTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
386
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAlertTitle, isStandalone: true, selector: "[buiAlertTitle]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "alert-title" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
387
+ }
388
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlertTitle, decorators: [{
389
+ type: Directive,
390
+ args: [{
391
+ selector: '[buiAlertTitle]',
392
+ host: { 'data-slot': 'alert-title', '[class]': 'computedClass()' },
393
+ }]
394
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
395
+ class BuiAlertDescription {
396
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
397
+ computedClass = computed(() => cn('col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed', this.userClass()), /* @ts-ignore */
398
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
399
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlertDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
400
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAlertDescription, isStandalone: true, selector: "[buiAlertDescription]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "alert-description" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
401
+ }
402
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAlertDescription, decorators: [{
403
+ type: Directive,
404
+ args: [{
405
+ selector: '[buiAlertDescription]',
406
+ host: { 'data-slot': 'alert-description', '[class]': 'computedClass()' },
407
+ }]
408
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
409
+
410
+ const INPUT_BASE = 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:border-0 file:bg-transparent file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive';
411
+ const INPUT_SIZES = {
412
+ sm: 'h-8 px-2.5 py-1 text-sm file:h-6 file:text-xs',
413
+ default: 'h-9 px-3 py-1 text-base md:text-sm file:h-7 file:text-sm',
414
+ lg: 'h-10 px-4 py-2 text-base file:h-8 file:text-sm',
415
+ };
416
+ /** Applies BlatUI input styling to a native `<input>` (keeps native validation & a11y). */
417
+ class BuiInput {
418
+ size = input('default', /* @ts-ignore */
419
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
420
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
421
+ computedClass = computed(() => cn(INPUT_BASE, INPUT_SIZES[this.size()], this.userClass()), /* @ts-ignore */
422
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
423
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
424
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiInput, isStandalone: true, selector: "input[buiInput]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "input" }, properties: { "attr.data-size": "size()", "class": "computedClass()" } }, ngImport: i0 });
425
+ }
426
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiInput, decorators: [{
427
+ type: Directive,
428
+ args: [{
429
+ selector: 'input[buiInput]',
430
+ host: {
431
+ 'data-slot': 'input',
432
+ '[attr.data-size]': 'size()',
433
+ '[class]': 'computedClass()',
434
+ },
435
+ }]
436
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
437
+
438
+ const TEXTAREA_BASE = 'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content w-full rounded-md border bg-transparent shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50';
439
+ const TEXTAREA_SIZES = {
440
+ sm: 'min-h-14 px-2.5 py-1.5 text-sm',
441
+ default: 'min-h-16 px-3 py-2 text-base md:text-sm',
442
+ lg: 'min-h-20 px-4 py-2.5 text-base',
443
+ };
444
+ /** Applies BlatUI textarea styling to a native `<textarea>` (auto-grows via field-sizing). */
445
+ class BuiTextarea {
446
+ size = input('default', /* @ts-ignore */
447
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
448
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
449
+ computedClass = computed(() => cn(TEXTAREA_BASE, TEXTAREA_SIZES[this.size()], this.userClass()), /* @ts-ignore */
450
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
451
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTextarea, deps: [], target: i0.ɵɵFactoryTarget.Directive });
452
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiTextarea, isStandalone: true, selector: "textarea[buiTextarea]", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "textarea" }, properties: { "attr.data-size": "size()", "class": "computedClass()" } }, ngImport: i0 });
453
+ }
454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTextarea, decorators: [{
455
+ type: Directive,
456
+ args: [{
457
+ selector: 'textarea[buiTextarea]',
458
+ host: {
459
+ 'data-slot': 'textarea',
460
+ '[attr.data-size]': 'size()',
461
+ '[class]': 'computedClass()',
462
+ },
463
+ }]
464
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
465
+
466
+ /**
467
+ * BlatUI linear progress bar. Exposes the full `progressbar` ARIA contract
468
+ * (valuemin/max/now/text). Set `[indeterminate]="true"` for an unknown-duration bar.
469
+ */
470
+ class BuiProgress {
471
+ value = input(0, /* @ts-ignore */
472
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
473
+ indeterminate = input(false, /* @ts-ignore */
474
+ ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
475
+ ariaLabel = input('Progress', /* @ts-ignore */
476
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
477
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
478
+ pct = computed(() => Math.max(0, Math.min(100, this.value())), /* @ts-ignore */
479
+ ...(ngDevMode ? [{ debugName: "pct" }] : /* istanbul ignore next */ []));
480
+ rounded = computed(() => Math.round(this.pct()), /* @ts-ignore */
481
+ ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
482
+ valueText = computed(() => `${this.rounded()}%`, /* @ts-ignore */
483
+ ...(ngDevMode ? [{ debugName: "valueText" }] : /* istanbul ignore next */ []));
484
+ indicatorTransform = computed(() => `translateX(-${100 - this.pct()}%)`, /* @ts-ignore */
485
+ ...(ngDevMode ? [{ debugName: "indicatorTransform" }] : /* istanbul ignore next */ []));
486
+ computedClass = computed(() => cn('relative block h-2 w-full overflow-hidden rounded-full bg-primary/20', this.userClass()), /* @ts-ignore */
487
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiProgress, deps: [], target: i0.ɵɵFactoryTarget.Component });
489
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiProgress, isStandalone: true, selector: "bui-progress", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "progress", "role": "progressbar", "aria-valuemin": "0", "aria-valuemax": "100" }, properties: { "attr.aria-label": "ariaLabel()", "attr.aria-valuenow": "indeterminate() ? null : rounded()", "attr.aria-valuetext": "indeterminate() ? null : valueText()", "class": "computedClass()" } }, ngImport: i0, template: `
490
+ @if (indeterminate()) {
491
+ <span
492
+ data-slot="progress-indicator"
493
+ class="absolute inset-y-0 w-2/5 animate-progress-indeterminate rounded-full bg-primary"
494
+ ></span>
495
+ } @else {
496
+ <span
497
+ data-slot="progress-indicator"
498
+ class="block h-full w-full rounded-full bg-primary transition-all"
499
+ [style.transform]="indicatorTransform()"
500
+ ></span>
501
+ }
502
+ `, isInline: true });
503
+ }
504
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiProgress, decorators: [{
505
+ type: Component,
506
+ args: [{
507
+ selector: 'bui-progress',
508
+ host: {
509
+ 'data-slot': 'progress',
510
+ role: 'progressbar',
511
+ 'aria-valuemin': '0',
512
+ 'aria-valuemax': '100',
513
+ '[attr.aria-label]': 'ariaLabel()',
514
+ '[attr.aria-valuenow]': 'indeterminate() ? null : rounded()',
515
+ '[attr.aria-valuetext]': 'indeterminate() ? null : valueText()',
516
+ '[class]': 'computedClass()',
517
+ },
518
+ template: `
519
+ @if (indeterminate()) {
520
+ <span
521
+ data-slot="progress-indicator"
522
+ class="absolute inset-y-0 w-2/5 animate-progress-indeterminate rounded-full bg-primary"
523
+ ></span>
524
+ } @else {
525
+ <span
526
+ data-slot="progress-indicator"
527
+ class="block h-full w-full rounded-full bg-primary transition-all"
528
+ [style.transform]="indicatorTransform()"
529
+ ></span>
530
+ }
531
+ `,
532
+ }]
533
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
534
+
535
+ /**
536
+ * BlatUI avatar. Renders the image when `src` is set and loads; otherwise shows
537
+ * the projected fallback (initials, icon). Falls back automatically on load error.
538
+ */
539
+ class BuiAvatar {
540
+ src = input(null, /* @ts-ignore */
541
+ ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
542
+ alt = input('', /* @ts-ignore */
543
+ ...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
544
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
545
+ error = signal(false, /* @ts-ignore */
546
+ ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
547
+ computedClass = computed(() => cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', this.userClass()), /* @ts-ignore */
548
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
549
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
550
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiAvatar, isStandalone: true, selector: "bui-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "avatar" }, properties: { "class": "computedClass()" } }, ngImport: i0, template: `
551
+ @if (src() && !error()) {
552
+ <img
553
+ data-slot="avatar-image"
554
+ class="aspect-square size-full"
555
+ [src]="src()"
556
+ [alt]="alt()"
557
+ (error)="error.set(true)"
558
+ />
559
+ } @else {
560
+ <span
561
+ data-slot="avatar-fallback"
562
+ class="flex size-full items-center justify-center rounded-full bg-muted text-sm"
563
+ >
564
+ <ng-content />
565
+ </span>
566
+ }
567
+ `, isInline: true });
568
+ }
569
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAvatar, decorators: [{
570
+ type: Component,
571
+ args: [{
572
+ selector: 'bui-avatar',
573
+ host: {
574
+ 'data-slot': 'avatar',
575
+ '[class]': 'computedClass()',
576
+ },
577
+ template: `
578
+ @if (src() && !error()) {
579
+ <img
580
+ data-slot="avatar-image"
581
+ class="aspect-square size-full"
582
+ [src]="src()"
583
+ [alt]="alt()"
584
+ (error)="error.set(true)"
585
+ />
586
+ } @else {
587
+ <span
588
+ data-slot="avatar-fallback"
589
+ class="flex size-full items-center justify-center rounded-full bg-muted text-sm"
590
+ >
591
+ <ng-content />
592
+ </span>
593
+ }
594
+ `,
595
+ }]
596
+ }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
597
+
598
+ const CHECKBOX_BASE = 'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground data-[state=indeterminate]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 flex items-center justify-center';
599
+ /**
600
+ * BlatUI checkbox. A native `<button role="checkbox">` (Space/Enter toggle for free)
601
+ * with `[(checked)]` two-way binding and tri-state `[(indeterminate)]`.
602
+ */
603
+ class BuiCheckbox {
604
+ checked = model(false, /* @ts-ignore */
605
+ ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
606
+ indeterminate = model(false, /* @ts-ignore */
607
+ ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
608
+ disabled = input(false, /* @ts-ignore */
609
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
610
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
611
+ state = computed(() => {
612
+ if (this.indeterminate()) {
613
+ return 'indeterminate';
614
+ }
615
+ return this.checked() ? 'checked' : 'unchecked';
616
+ }, /* @ts-ignore */
617
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
618
+ ariaChecked = computed(() => this.indeterminate() ? 'mixed' : String(this.checked()), /* @ts-ignore */
619
+ ...(ngDevMode ? [{ debugName: "ariaChecked" }] : /* istanbul ignore next */ []));
620
+ computedClass = computed(() => cn(CHECKBOX_BASE, this.userClass()), /* @ts-ignore */
621
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
622
+ toggle() {
623
+ if (this.disabled()) {
624
+ return;
625
+ }
626
+ if (this.indeterminate()) {
627
+ this.indeterminate.set(false);
628
+ this.checked.set(true);
629
+ }
630
+ else {
631
+ this.checked.update((value) => !value);
632
+ }
633
+ }
634
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
635
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiCheckbox, isStandalone: true, selector: "button[buiCheckbox]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", indeterminate: "indeterminateChange" }, host: { attributes: { "type": "button", "role": "checkbox", "data-slot": "checkbox" }, listeners: { "click": "toggle()" }, properties: { "attr.aria-checked": "ariaChecked()", "attr.data-state": "state()", "disabled": "disabled()", "class": "computedClass()" } }, ngImport: i0, template: `
636
+ @if (checked() || indeterminate()) {
637
+ <span data-slot="checkbox-indicator" class="flex items-center justify-center text-current">
638
+ @if (indeterminate()) {
639
+ <svg
640
+ xmlns="http://www.w3.org/2000/svg"
641
+ class="size-3.5"
642
+ viewBox="0 0 24 24"
643
+ fill="none"
644
+ stroke="currentColor"
645
+ stroke-width="2"
646
+ stroke-linecap="round"
647
+ stroke-linejoin="round"
648
+ aria-hidden="true"
649
+ >
650
+ <path d="M5 12h14" />
651
+ </svg>
652
+ } @else {
653
+ <svg
654
+ xmlns="http://www.w3.org/2000/svg"
655
+ class="size-3.5"
656
+ viewBox="0 0 24 24"
657
+ fill="none"
658
+ stroke="currentColor"
659
+ stroke-width="2"
660
+ stroke-linecap="round"
661
+ stroke-linejoin="round"
662
+ aria-hidden="true"
663
+ >
664
+ <path d="M20 6 9 17l-5-5" />
665
+ </svg>
666
+ }
667
+ </span>
668
+ }
669
+ `, isInline: true });
670
+ }
671
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCheckbox, decorators: [{
672
+ type: Component,
673
+ args: [{
674
+ selector: 'button[buiCheckbox]',
675
+ host: {
676
+ type: 'button',
677
+ role: 'checkbox',
678
+ 'data-slot': 'checkbox',
679
+ '[attr.aria-checked]': 'ariaChecked()',
680
+ '[attr.data-state]': 'state()',
681
+ '[disabled]': 'disabled()',
682
+ '[class]': 'computedClass()',
683
+ '(click)': 'toggle()',
684
+ },
685
+ template: `
686
+ @if (checked() || indeterminate()) {
687
+ <span data-slot="checkbox-indicator" class="flex items-center justify-center text-current">
688
+ @if (indeterminate()) {
689
+ <svg
690
+ xmlns="http://www.w3.org/2000/svg"
691
+ class="size-3.5"
692
+ viewBox="0 0 24 24"
693
+ fill="none"
694
+ stroke="currentColor"
695
+ stroke-width="2"
696
+ stroke-linecap="round"
697
+ stroke-linejoin="round"
698
+ aria-hidden="true"
699
+ >
700
+ <path d="M5 12h14" />
701
+ </svg>
702
+ } @else {
703
+ <svg
704
+ xmlns="http://www.w3.org/2000/svg"
705
+ class="size-3.5"
706
+ viewBox="0 0 24 24"
707
+ fill="none"
708
+ stroke="currentColor"
709
+ stroke-width="2"
710
+ stroke-linecap="round"
711
+ stroke-linejoin="round"
712
+ aria-hidden="true"
713
+ >
714
+ <path d="M20 6 9 17l-5-5" />
715
+ </svg>
716
+ }
717
+ </span>
718
+ }
719
+ `,
720
+ }]
721
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
722
+
723
+ const SWITCH_BASE = 'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50';
724
+ const SWITCH_TRACK = {
725
+ sm: 'h-4 w-7',
726
+ default: 'h-[1.15rem] w-8',
727
+ lg: 'h-6 w-10',
728
+ };
729
+ const SWITCH_THUMB = {
730
+ sm: 'size-3.5',
731
+ default: 'size-4',
732
+ lg: 'size-5',
733
+ };
734
+ const THUMB_BASE = 'bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0';
735
+ /**
736
+ * BlatUI switch. A native `<button role="switch">` with `[(checked)]` two-way
737
+ * binding; keyboard activation (Space/Enter) and focus come from the native button.
738
+ */
739
+ class BuiSwitch {
740
+ checked = model(false, /* @ts-ignore */
741
+ ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
742
+ disabled = input(false, /* @ts-ignore */
743
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
744
+ size = input('default', /* @ts-ignore */
745
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
746
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
747
+ state = computed(() => (this.checked() ? 'checked' : 'unchecked'), /* @ts-ignore */
748
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
749
+ computedClass = computed(() => cn(SWITCH_BASE, SWITCH_TRACK[this.size()], this.userClass()), /* @ts-ignore */
750
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
751
+ thumbClass = computed(() => cn(THUMB_BASE, SWITCH_THUMB[this.size()]), /* @ts-ignore */
752
+ ...(ngDevMode ? [{ debugName: "thumbClass" }] : /* istanbul ignore next */ []));
753
+ toggle() {
754
+ if (this.disabled()) {
755
+ return;
756
+ }
757
+ this.checked.update((value) => !value);
758
+ }
759
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSwitch, deps: [], target: i0.ɵɵFactoryTarget.Component });
760
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: BuiSwitch, isStandalone: true, selector: "button[buiSwitch]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", 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: { checked: "checkedChange" }, host: { attributes: { "type": "button", "role": "switch", "data-slot": "switch" }, listeners: { "click": "toggle()" }, properties: { "attr.aria-checked": "checked()", "attr.data-state": "state()", "disabled": "disabled()", "class": "computedClass()" } }, ngImport: i0, template: `
761
+ <span data-slot="switch-thumb" [attr.data-state]="state()" [class]="thumbClass()"></span>
762
+ `, isInline: true });
763
+ }
764
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSwitch, decorators: [{
765
+ type: Component,
766
+ args: [{
767
+ selector: 'button[buiSwitch]',
768
+ host: {
769
+ type: 'button',
770
+ role: 'switch',
771
+ 'data-slot': 'switch',
772
+ '[attr.aria-checked]': 'checked()',
773
+ '[attr.data-state]': 'state()',
774
+ '[disabled]': 'disabled()',
775
+ '[class]': 'computedClass()',
776
+ '(click)': 'toggle()',
777
+ },
778
+ template: `
779
+ <span data-slot="switch-thumb" [attr.data-state]="state()" [class]="thumbClass()"></span>
780
+ `,
781
+ }]
782
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
783
+
784
+ const TRIGGER = 'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&>svg]:text-muted-foreground [&>svg]:pointer-events-none [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:translate-y-0.5 [&>svg]:transition-transform [&>svg]:duration-200 [&[aria-expanded=true]>svg]:rotate-180';
785
+ class BuiAccordion {
786
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
787
+ computedClass = computed(() => cn('w-full', this.userClass()), /* @ts-ignore */
788
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
789
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordion, deps: [], target: i0.ɵɵFactoryTarget.Directive });
790
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAccordion, isStandalone: true, selector: "[buiAccordion]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "accordion" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
791
+ }
792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordion, decorators: [{
793
+ type: Directive,
794
+ args: [{
795
+ selector: '[buiAccordion]',
796
+ host: { 'data-slot': 'accordion', '[class]': 'computedClass()' },
797
+ }]
798
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
799
+ class BuiAccordionItem {
800
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
801
+ computedClass = computed(() => cn('border-b last:border-b-0', this.userClass()), /* @ts-ignore */
802
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
803
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
804
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAccordionItem, isStandalone: true, selector: "[buiAccordionItem]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "accordion-item" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
805
+ }
806
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionItem, decorators: [{
807
+ type: Directive,
808
+ args: [{
809
+ selector: '[buiAccordionItem]',
810
+ host: { 'data-slot': 'accordion-item', '[class]': 'computedClass()' },
811
+ }]
812
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
813
+ class BuiAccordionTrigger {
814
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
815
+ computedClass = computed(() => cn(TRIGGER, this.userClass()), /* @ts-ignore */
816
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
817
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
818
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAccordionTrigger, isStandalone: true, selector: "button[buiAccordionTrigger]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "accordion-trigger" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
819
+ }
820
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionTrigger, decorators: [{
821
+ type: Directive,
822
+ args: [{
823
+ selector: 'button[buiAccordionTrigger]',
824
+ host: { 'data-slot': 'accordion-trigger', '[class]': 'computedClass()' },
825
+ }]
826
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
827
+ class BuiAccordionContent {
828
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
829
+ computedClass = computed(() => cn('overflow-hidden text-sm', this.userClass()), /* @ts-ignore */
830
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
831
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
832
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiAccordionContent, isStandalone: true, selector: "[buiAccordionContent]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "accordion-content" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
833
+ }
834
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiAccordionContent, decorators: [{
835
+ type: Directive,
836
+ args: [{
837
+ selector: '[buiAccordionContent]',
838
+ host: { 'data-slot': 'accordion-content', '[class]': 'computedClass()' },
839
+ }]
840
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
841
+
842
+ const TAB_TRIGGER = 'text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 cursor-default items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-selected:bg-background aria-selected:shadow-sm dark:aria-selected:text-foreground dark:aria-selected:border-input dark:aria-selected:bg-input/30';
843
+ class BuiTabs {
844
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
845
+ computedClass = computed(() => cn('flex flex-col gap-2', this.userClass()), /* @ts-ignore */
846
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
847
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
848
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiTabs, isStandalone: true, selector: "[buiTabs]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "tabs" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
849
+ }
850
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabs, decorators: [{
851
+ type: Directive,
852
+ args: [{
853
+ selector: '[buiTabs]',
854
+ host: { 'data-slot': 'tabs', '[class]': 'computedClass()' },
855
+ }]
856
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
857
+ class BuiTabList {
858
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
859
+ computedClass = computed(() => cn('inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground', this.userClass()), /* @ts-ignore */
860
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
861
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
862
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiTabList, isStandalone: true, selector: "[buiTabList]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "tabs-list" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
863
+ }
864
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabList, decorators: [{
865
+ type: Directive,
866
+ args: [{
867
+ selector: '[buiTabList]',
868
+ host: { 'data-slot': 'tabs-list', '[class]': 'computedClass()' },
869
+ }]
870
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
871
+ class BuiTabTrigger {
872
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
873
+ computedClass = computed(() => cn(TAB_TRIGGER, this.userClass()), /* @ts-ignore */
874
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
875
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
876
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiTabTrigger, isStandalone: true, selector: "[buiTabTrigger]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "tabs-trigger" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
877
+ }
878
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabTrigger, decorators: [{
879
+ type: Directive,
880
+ args: [{
881
+ selector: '[buiTabTrigger]',
882
+ host: { 'data-slot': 'tabs-trigger', '[class]': 'computedClass()' },
883
+ }]
884
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
885
+ class BuiTabPanel {
886
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
887
+ computedClass = computed(() => cn('flex-1 outline-none', this.userClass()), /* @ts-ignore */
888
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
889
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
890
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiTabPanel, isStandalone: true, selector: "[buiTabPanel]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "tabs-content" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
891
+ }
892
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiTabPanel, decorators: [{
893
+ type: Directive,
894
+ args: [{
895
+ selector: '[buiTabPanel]',
896
+ host: { 'data-slot': 'tabs-content', '[class]': 'computedClass()' },
897
+ }]
898
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
899
+
900
+ const DIALOG_CONTENT = 'bg-background relative grid w-full max-w-[calc(100%-2rem)] gap-4 rounded-lg border p-6 shadow-lg sm:max-w-lg';
901
+ class BuiDialogContent {
902
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
903
+ computedClass = computed(() => cn(DIALOG_CONTENT, this.userClass()), /* @ts-ignore */
904
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
905
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
906
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiDialogContent, isStandalone: true, selector: "[buiDialogContent]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "dialog-content" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
907
+ }
908
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogContent, decorators: [{
909
+ type: Directive,
910
+ args: [{
911
+ selector: '[buiDialogContent]',
912
+ host: { 'data-slot': 'dialog-content', '[class]': 'computedClass()' },
913
+ }]
914
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
915
+ class BuiDialogHeader {
916
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
917
+ computedClass = computed(() => cn('flex flex-col gap-2 text-center sm:text-left', this.userClass()), /* @ts-ignore */
918
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
919
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
920
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiDialogHeader, isStandalone: true, selector: "[buiDialogHeader]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "dialog-header" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
921
+ }
922
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogHeader, decorators: [{
923
+ type: Directive,
924
+ args: [{
925
+ selector: '[buiDialogHeader]',
926
+ host: { 'data-slot': 'dialog-header', '[class]': 'computedClass()' },
927
+ }]
928
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
929
+ class BuiDialogTitle {
930
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
931
+ computedClass = computed(() => cn('text-lg leading-none font-semibold', this.userClass()), /* @ts-ignore */
932
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
934
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiDialogTitle, isStandalone: true, selector: "[buiDialogTitle]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "dialog-title" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
935
+ }
936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogTitle, decorators: [{
937
+ type: Directive,
938
+ args: [{
939
+ selector: '[buiDialogTitle]',
940
+ host: { 'data-slot': 'dialog-title', '[class]': 'computedClass()' },
941
+ }]
942
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
943
+ class BuiDialogDescription {
944
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
945
+ computedClass = computed(() => cn('text-sm text-muted-foreground', this.userClass()), /* @ts-ignore */
946
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
948
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiDialogDescription, isStandalone: true, selector: "[buiDialogDescription]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "dialog-description" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
949
+ }
950
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogDescription, decorators: [{
951
+ type: Directive,
952
+ args: [{
953
+ selector: '[buiDialogDescription]',
954
+ host: { 'data-slot': 'dialog-description', '[class]': 'computedClass()' },
955
+ }]
956
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
957
+ class BuiDialogFooter {
958
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
959
+ computedClass = computed(() => cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', this.userClass()), /* @ts-ignore */
960
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
961
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogFooter, deps: [], target: i0.ɵɵFactoryTarget.Directive });
962
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiDialogFooter, isStandalone: true, selector: "[buiDialogFooter]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "dialog-footer" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
963
+ }
964
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiDialogFooter, decorators: [{
965
+ type: Directive,
966
+ args: [{
967
+ selector: '[buiDialogFooter]',
968
+ host: { 'data-slot': 'dialog-footer', '[class]': 'computedClass()' },
969
+ }]
970
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
971
+
972
+ /** Every token the `@theme inline` mapping references — emitted into `:root` on export. */
973
+ const THEME_TOKENS = [
974
+ '--radius',
975
+ '--spacing',
976
+ '--tracking-normal',
977
+ '--font-sans',
978
+ '--font-serif',
979
+ '--font-mono',
980
+ '--shadow-2xs',
981
+ '--shadow-xs',
982
+ '--shadow-sm',
983
+ '--shadow',
984
+ '--shadow-md',
985
+ '--shadow-lg',
986
+ '--shadow-xl',
987
+ '--shadow-2xl',
988
+ '--background',
989
+ '--foreground',
990
+ '--card',
991
+ '--card-foreground',
992
+ '--popover',
993
+ '--popover-foreground',
994
+ '--primary',
995
+ '--primary-foreground',
996
+ '--secondary',
997
+ '--secondary-foreground',
998
+ '--muted',
999
+ '--muted-foreground',
1000
+ '--accent',
1001
+ '--accent-foreground',
1002
+ '--destructive',
1003
+ '--destructive-foreground',
1004
+ '--border',
1005
+ '--input',
1006
+ '--ring',
1007
+ '--chart-1',
1008
+ '--chart-2',
1009
+ '--chart-3',
1010
+ '--chart-4',
1011
+ '--chart-5',
1012
+ '--sidebar',
1013
+ '--sidebar-foreground',
1014
+ '--sidebar-primary',
1015
+ '--sidebar-primary-foreground',
1016
+ '--sidebar-accent',
1017
+ '--sidebar-accent-foreground',
1018
+ '--sidebar-border',
1019
+ '--sidebar-ring',
1020
+ ];
1021
+ const THEME_SCAFFOLD = `@import 'tailwindcss';
1022
+
1023
+ @custom-variant dark (&:is(.dark *));
1024
+
1025
+ @theme inline {
1026
+ --font-sans: var(--font-sans);
1027
+ --font-serif: var(--font-serif);
1028
+ --font-mono: var(--font-mono);
1029
+
1030
+ --radius-sm: calc(var(--radius) - 4px);
1031
+ --radius-md: calc(var(--radius) - 2px);
1032
+ --radius-lg: var(--radius);
1033
+ --radius-xl: calc(var(--radius) + 4px);
1034
+
1035
+ --spacing: var(--spacing);
1036
+
1037
+ --tracking-tighter: calc(var(--tracking-normal) - 0.05em);
1038
+ --tracking-tight: calc(var(--tracking-normal) - 0.025em);
1039
+ --tracking-normal: var(--tracking-normal);
1040
+ --tracking-wide: calc(var(--tracking-normal) + 0.025em);
1041
+ --tracking-wider: calc(var(--tracking-normal) + 0.05em);
1042
+ --tracking-widest: calc(var(--tracking-normal) + 0.1em);
1043
+
1044
+ --shadow-2xs: var(--shadow-2xs);
1045
+ --shadow-xs: var(--shadow-xs);
1046
+ --shadow-sm: var(--shadow-sm);
1047
+ --shadow: var(--shadow);
1048
+ --shadow-md: var(--shadow-md);
1049
+ --shadow-lg: var(--shadow-lg);
1050
+ --shadow-xl: var(--shadow-xl);
1051
+ --shadow-2xl: var(--shadow-2xl);
1052
+
1053
+ --color-background: var(--background);
1054
+ --color-foreground: var(--foreground);
1055
+ --color-card: var(--card);
1056
+ --color-card-foreground: var(--card-foreground);
1057
+ --color-popover: var(--popover);
1058
+ --color-popover-foreground: var(--popover-foreground);
1059
+ --color-primary: var(--primary);
1060
+ --color-primary-foreground: var(--primary-foreground);
1061
+ --color-secondary: var(--secondary);
1062
+ --color-secondary-foreground: var(--secondary-foreground);
1063
+ --color-muted: var(--muted);
1064
+ --color-muted-foreground: var(--muted-foreground);
1065
+ --color-accent: var(--accent);
1066
+ --color-accent-foreground: var(--accent-foreground);
1067
+ --color-destructive: var(--destructive);
1068
+ --color-destructive-foreground: var(--destructive-foreground);
1069
+ --color-border: var(--border);
1070
+ --color-input: var(--input);
1071
+ --color-ring: var(--ring);
1072
+ --color-chart-1: var(--chart-1);
1073
+ --color-chart-2: var(--chart-2);
1074
+ --color-chart-3: var(--chart-3);
1075
+ --color-chart-4: var(--chart-4);
1076
+ --color-chart-5: var(--chart-5);
1077
+ --color-sidebar: var(--sidebar);
1078
+ --color-sidebar-foreground: var(--sidebar-foreground);
1079
+ --color-sidebar-primary: var(--sidebar-primary);
1080
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
1081
+ --color-sidebar-accent: var(--sidebar-accent);
1082
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
1083
+ --color-sidebar-border: var(--sidebar-border);
1084
+ --color-sidebar-ring: var(--sidebar-ring);
1085
+ }`;
1086
+ const DEFAULTS = {
1087
+ mode: 'light',
1088
+ base: 'neutral',
1089
+ preset: 'default',
1090
+ radius: '0.625',
1091
+ font: 'sans',
1092
+ fontHeading: 'sans',
1093
+ shadow: 'default',
1094
+ spacing: 'default',
1095
+ tracking: 'normal',
1096
+ inputStyle: 'outline',
1097
+ };
1098
+ /**
1099
+ * Live theme engine (the brain behind `<bui-theme-customizer>`). Mirrors BlatUI:
1100
+ * every dimension just toggles a `data-*` attribute / the `.dark` class on `<html>`,
1101
+ * and `blatui.css` holds all the token values. `exportCss()` serialises the active
1102
+ * theme to a paste-ready stylesheet. SSR-safe and persisted to localStorage.
1103
+ */
1104
+ class ThemeStore {
1105
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1106
+ mode = signal(DEFAULTS.mode, /* @ts-ignore */
1107
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1108
+ base = signal(DEFAULTS.base, /* @ts-ignore */
1109
+ ...(ngDevMode ? [{ debugName: "base" }] : /* istanbul ignore next */ []));
1110
+ preset = signal(DEFAULTS.preset, /* @ts-ignore */
1111
+ ...(ngDevMode ? [{ debugName: "preset" }] : /* istanbul ignore next */ []));
1112
+ radius = signal(DEFAULTS.radius, /* @ts-ignore */
1113
+ ...(ngDevMode ? [{ debugName: "radius" }] : /* istanbul ignore next */ []));
1114
+ font = signal(DEFAULTS.font, /* @ts-ignore */
1115
+ ...(ngDevMode ? [{ debugName: "font" }] : /* istanbul ignore next */ []));
1116
+ fontHeading = signal(DEFAULTS.fontHeading, /* @ts-ignore */
1117
+ ...(ngDevMode ? [{ debugName: "fontHeading" }] : /* istanbul ignore next */ []));
1118
+ shadow = signal(DEFAULTS.shadow, /* @ts-ignore */
1119
+ ...(ngDevMode ? [{ debugName: "shadow" }] : /* istanbul ignore next */ []));
1120
+ spacing = signal(DEFAULTS.spacing, /* @ts-ignore */
1121
+ ...(ngDevMode ? [{ debugName: "spacing" }] : /* istanbul ignore next */ []));
1122
+ tracking = signal(DEFAULTS.tracking, /* @ts-ignore */
1123
+ ...(ngDevMode ? [{ debugName: "tracking" }] : /* istanbul ignore next */ []));
1124
+ inputStyle = signal(DEFAULTS.inputStyle, /* @ts-ignore */
1125
+ ...(ngDevMode ? [{ debugName: "inputStyle" }] : /* istanbul ignore next */ []));
1126
+ systemDark = signal(false, /* @ts-ignore */
1127
+ ...(ngDevMode ? [{ debugName: "systemDark" }] : /* istanbul ignore next */ []));
1128
+ isDark = computed(() => this.mode() === 'dark' || (this.mode() === 'system' && this.systemDark()), /* @ts-ignore */
1129
+ ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
1130
+ constructor() {
1131
+ if (!this.isBrowser) {
1132
+ return;
1133
+ }
1134
+ this.load();
1135
+ const mql = typeof globalThis.matchMedia === 'function'
1136
+ ? globalThis.matchMedia('(prefers-color-scheme: dark)')
1137
+ : null;
1138
+ if (mql) {
1139
+ this.systemDark.set(mql.matches);
1140
+ mql.addEventListener('change', (event) => {
1141
+ this.systemDark.set(event.matches);
1142
+ });
1143
+ }
1144
+ effect(() => {
1145
+ this.apply();
1146
+ });
1147
+ }
1148
+ setMode(value) {
1149
+ this.mode.set(value);
1150
+ }
1151
+ setBase(value) {
1152
+ this.base.set(value);
1153
+ }
1154
+ setPreset(value) {
1155
+ this.preset.set(value);
1156
+ }
1157
+ setRadius(value) {
1158
+ this.radius.set(value);
1159
+ }
1160
+ setFont(value) {
1161
+ this.font.set(value);
1162
+ }
1163
+ setFontHeading(value) {
1164
+ this.fontHeading.set(value);
1165
+ }
1166
+ setShadow(value) {
1167
+ this.shadow.set(value);
1168
+ }
1169
+ setSpacing(value) {
1170
+ this.spacing.set(value);
1171
+ }
1172
+ setTracking(value) {
1173
+ this.tracking.set(value);
1174
+ }
1175
+ setInputStyle(value) {
1176
+ this.inputStyle.set(value);
1177
+ }
1178
+ toggle() {
1179
+ this.setMode(this.isDark() ? 'light' : 'dark');
1180
+ }
1181
+ reset() {
1182
+ this.mode.set(DEFAULTS.mode);
1183
+ this.base.set(DEFAULTS.base);
1184
+ this.preset.set(DEFAULTS.preset);
1185
+ this.radius.set(DEFAULTS.radius);
1186
+ this.font.set(DEFAULTS.font);
1187
+ this.fontHeading.set(DEFAULTS.fontHeading);
1188
+ this.shadow.set(DEFAULTS.shadow);
1189
+ this.spacing.set(DEFAULTS.spacing);
1190
+ this.tracking.set(DEFAULTS.tracking);
1191
+ this.inputStyle.set(DEFAULTS.inputStyle);
1192
+ }
1193
+ /** Serialise the active theme to a complete, paste-ready stylesheet. */
1194
+ exportCss() {
1195
+ if (!this.isBrowser) {
1196
+ return '';
1197
+ }
1198
+ const root = document.documentElement;
1199
+ const wasDark = root.classList.contains('dark');
1200
+ root.classList.remove('dark');
1201
+ const light = this.readTokens();
1202
+ root.classList.add('dark');
1203
+ const dark = this.readTokens();
1204
+ root.classList.toggle('dark', wasDark);
1205
+ const block = (source, keys) => keys
1206
+ .filter((token) => source[token])
1207
+ .map((token) => ` ${token}: ${source[token] ?? ''};`)
1208
+ .join('\n');
1209
+ const darkKeys = THEME_TOKENS.filter((token) => dark[token] && dark[token] !== light[token]);
1210
+ return `${THEME_SCAFFOLD}\n\n:root {\n${block(light, THEME_TOKENS)}\n}\n\n.dark {\n${block(dark, darkKeys)}\n}\n`;
1211
+ }
1212
+ readTokens() {
1213
+ const cs = getComputedStyle(document.documentElement);
1214
+ const out = {};
1215
+ for (const token of THEME_TOKENS) {
1216
+ out[token] = cs.getPropertyValue(token).trim();
1217
+ }
1218
+ return out;
1219
+ }
1220
+ load() {
1221
+ const read = (key, fallback) => localStorage.getItem(`theme:${key}`) ?? fallback;
1222
+ this.mode.set(read('mode', DEFAULTS.mode));
1223
+ this.base.set(read('base', DEFAULTS.base));
1224
+ this.preset.set(read('preset', DEFAULTS.preset));
1225
+ this.radius.set(read('radius', DEFAULTS.radius));
1226
+ this.font.set(read('font', DEFAULTS.font));
1227
+ this.fontHeading.set(read('fontHeading', DEFAULTS.fontHeading));
1228
+ this.shadow.set(read('shadow', DEFAULTS.shadow));
1229
+ this.spacing.set(read('spacing', DEFAULTS.spacing));
1230
+ this.tracking.set(read('tracking', DEFAULTS.tracking));
1231
+ this.inputStyle.set(read('inputStyle', DEFAULTS.inputStyle));
1232
+ }
1233
+ apply() {
1234
+ const root = document.documentElement;
1235
+ const state = {
1236
+ mode: this.mode(),
1237
+ base: this.base(),
1238
+ preset: this.preset(),
1239
+ radius: this.radius(),
1240
+ font: this.font(),
1241
+ fontHeading: this.fontHeading(),
1242
+ shadow: this.shadow(),
1243
+ spacing: this.spacing(),
1244
+ tracking: this.tracking(),
1245
+ inputStyle: this.inputStyle(),
1246
+ };
1247
+ root.classList.toggle('dark', this.isDark());
1248
+ this.attr(root, 'data-base', state.base, DEFAULTS.base);
1249
+ this.attr(root, 'data-theme', state.preset, DEFAULTS.preset);
1250
+ this.attr(root, 'data-font', state.font, DEFAULTS.font);
1251
+ this.attr(root, 'data-font-heading', state.fontHeading, DEFAULTS.fontHeading);
1252
+ this.attr(root, 'data-shadow', state.shadow, DEFAULTS.shadow);
1253
+ this.attr(root, 'data-spacing', state.spacing, DEFAULTS.spacing);
1254
+ this.attr(root, 'data-tracking', state.tracking, DEFAULTS.tracking);
1255
+ this.attr(root, 'data-input-style', state.inputStyle, DEFAULTS.inputStyle);
1256
+ root.setAttribute('data-radius', state.radius);
1257
+ for (const [key, value] of Object.entries(state)) {
1258
+ localStorage.setItem(`theme:${key}`, value);
1259
+ }
1260
+ }
1261
+ attr(root, name, value, fallback) {
1262
+ if (value && value !== fallback) {
1263
+ root.setAttribute(name, value);
1264
+ }
1265
+ else {
1266
+ root.removeAttribute(name);
1267
+ }
1268
+ }
1269
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ThemeStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1270
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ThemeStore, providedIn: 'root' });
1271
+ }
1272
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ThemeStore, decorators: [{
1273
+ type: Injectable,
1274
+ args: [{ providedIn: 'root' }]
1275
+ }], ctorParameters: () => [] });
1276
+
1277
+ const FONTS = [
1278
+ { value: 'sans', label: 'Default', family: "'Instrument Sans', sans-serif" },
1279
+ { value: 'inter', label: 'Inter', family: "'Inter', sans-serif" },
1280
+ { value: 'geist', label: 'Geist', family: "'Geist', sans-serif" },
1281
+ { value: 'manrope', label: 'Manrope', family: "'Manrope', sans-serif" },
1282
+ { value: 'jakarta', label: 'Jakarta', family: "'Plus Jakarta Sans', sans-serif" },
1283
+ { value: 'space-grotesk', label: 'Grotesk', family: "'Space Grotesk', sans-serif" },
1284
+ { value: 'dm-sans', label: 'DM Sans', family: "'DM Sans', sans-serif" },
1285
+ { value: 'outfit', label: 'Outfit', family: "'Outfit', sans-serif" },
1286
+ { value: 'sora', label: 'Sora', family: "'Sora', sans-serif" },
1287
+ { value: 'system', label: 'System', family: 'system-ui, sans-serif' },
1288
+ { value: 'serif', label: 'Serif', family: 'ui-serif, Georgia, serif' },
1289
+ { value: 'mono', label: 'Mono', family: 'ui-monospace, monospace' },
1290
+ ];
1291
+ /**
1292
+ * BlatUI's "Customize" toolbar, ported to Angular. Tunes every theme dimension
1293
+ * live (mode, base, accent, radius, input style, fonts, shadow, spacing, tracking)
1294
+ * through {@link ThemeStore}, then exports a paste-ready stylesheet via "Copy CSS".
1295
+ */
1296
+ class BuiThemeCustomizer {
1297
+ theme = inject(ThemeStore);
1298
+ open = signal(false, /* @ts-ignore */
1299
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1300
+ copied = signal(false, /* @ts-ignore */
1301
+ ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
1302
+ modes = [
1303
+ { value: 'light', label: 'Light' },
1304
+ { value: 'dark', label: 'Dark' },
1305
+ { value: 'system', label: 'System' },
1306
+ ];
1307
+ bases = [
1308
+ { value: 'neutral', color: 'oklch(0.205 0 0)' },
1309
+ { value: 'stone', color: 'oklch(0.216 0.006 56.043)' },
1310
+ { value: 'zinc', color: 'oklch(0.21 0.006 285.885)' },
1311
+ { value: 'slate', color: 'oklch(0.208 0.042 265.755)' },
1312
+ { value: 'gray', color: 'oklch(0.21 0.034 264.665)' },
1313
+ { value: 'mauve', color: 'oklch(0.212 0.019 322.12)' },
1314
+ { value: 'olive', color: 'oklch(0.228 0.013 107.4)' },
1315
+ { value: 'mist', color: 'oklch(0.218 0.008 223.9)' },
1316
+ { value: 'taupe', color: 'oklch(0.214 0.009 43.1)' },
1317
+ ];
1318
+ accents = [
1319
+ { value: 'default', color: 'var(--primary)' },
1320
+ { value: 'blue', color: 'oklch(0.488 0.243 264.376)' },
1321
+ { value: 'indigo', color: 'oklch(0.457 0.24 277.023)' },
1322
+ { value: 'violet', color: 'oklch(0.491 0.27 292.581)' },
1323
+ { value: 'purple', color: 'oklch(0.496 0.265 301.924)' },
1324
+ { value: 'fuchsia', color: 'oklch(0.518 0.253 323.949)' },
1325
+ { value: 'pink', color: 'oklch(0.525 0.223 3.958)' },
1326
+ { value: 'rose', color: 'oklch(0.514 0.222 16.935)' },
1327
+ { value: 'red', color: 'oklch(0.505 0.213 27.518)' },
1328
+ { value: 'orange', color: 'oklch(0.553 0.195 38.402)' },
1329
+ { value: 'amber', color: 'oklch(0.555 0.163 48.998)' },
1330
+ { value: 'green', color: 'oklch(0.527 0.154 150.069)' },
1331
+ { value: 'emerald', color: 'oklch(0.508 0.118 165.612)' },
1332
+ { value: 'teal', color: 'oklch(0.511 0.096 186.391)' },
1333
+ { value: 'cyan', color: 'oklch(0.52 0.105 223.128)' },
1334
+ { value: 'sky', color: 'oklch(0.5 0.134 242.749)' },
1335
+ ];
1336
+ radii = ['0', '0.3', '0.5', '0.625', '0.75', '1'];
1337
+ inputStyles = [
1338
+ { value: 'outline', label: 'Outline' },
1339
+ { value: 'fill', label: 'Fill' },
1340
+ { value: 'inset', label: 'Inset' },
1341
+ ];
1342
+ fonts = FONTS;
1343
+ shadows = [
1344
+ { value: 'none', label: 'None' },
1345
+ { value: 'sm', label: 'SM' },
1346
+ { value: 'default', label: 'Base' },
1347
+ { value: 'lg', label: 'LG' },
1348
+ { value: 'xl', label: 'XL' },
1349
+ ];
1350
+ spacings = [
1351
+ { value: 'compact', label: 'S' },
1352
+ { value: 'default', label: 'M' },
1353
+ { value: 'comfortable', label: 'L' },
1354
+ ];
1355
+ trackings = [
1356
+ { value: 'tight', label: 'S' },
1357
+ { value: 'normal', label: 'M' },
1358
+ { value: 'wide', label: 'L' },
1359
+ ];
1360
+ _hasClipboard = computed(() => typeof navigator !== 'undefined' && !!navigator.clipboard, /* @ts-ignore */
1361
+ ...(ngDevMode ? [{ debugName: "_hasClipboard" }] : /* istanbul ignore next */ []));
1362
+ async copyCss() {
1363
+ if (!this._hasClipboard()) {
1364
+ return;
1365
+ }
1366
+ try {
1367
+ await navigator.clipboard.writeText(this.theme.exportCss());
1368
+ this.copied.set(true);
1369
+ setTimeout(() => {
1370
+ this.copied.set(false);
1371
+ }, 2000);
1372
+ }
1373
+ catch {
1374
+ // Clipboard unavailable / permission denied — silently ignore.
1375
+ }
1376
+ }
1377
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiThemeCustomizer, deps: [], target: i0.ɵɵFactoryTarget.Component });
1378
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiThemeCustomizer, isStandalone: true, selector: "bui-theme-customizer", host: { attributes: { "data-slot": "theme-customizer" } }, ngImport: i0, template: `
1379
+ <button
1380
+ buiButton
1381
+ variant="outline"
1382
+ size="sm"
1383
+ class="fixed right-4 bottom-4 z-50 shadow-md"
1384
+ [attr.aria-expanded]="open()"
1385
+ (click)="open.set(!open())"
1386
+ >
1387
+ {{ open() ? 'Close' : 'Customize' }}
1388
+ </button>
1389
+
1390
+ @if (open()) {
1391
+ <div
1392
+ role="dialog"
1393
+ aria-label="Theme customizer"
1394
+ class="fixed right-4 bottom-16 z-50 max-h-[80vh] w-[340px] space-y-5 overflow-y-auto rounded-lg border bg-popover p-4 text-popover-foreground shadow-lg"
1395
+ >
1396
+ <div class="flex items-center justify-between">
1397
+ <div>
1398
+ <h4 class="text-sm font-semibold">Customize</h4>
1399
+ <p class="text-xs text-muted-foreground">
1400
+ Tune it live. Every preset is pure CSS variables.
1401
+ </p>
1402
+ </div>
1403
+ <button buiButton variant="ghost" size="sm" (click)="theme.reset()">Reset</button>
1404
+ </div>
1405
+
1406
+ <!-- Mode -->
1407
+ <div class="space-y-1.5">
1408
+ <span class="text-xs font-medium">Mode</span>
1409
+ <div class="grid grid-cols-3 gap-2">
1410
+ @for (m of modes; track m.value) {
1411
+ <button
1412
+ type="button"
1413
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1414
+ [attr.data-active]="theme.mode() === m.value"
1415
+ (click)="theme.setMode(m.value)"
1416
+ >
1417
+ {{ m.label }}
1418
+ </button>
1419
+ }
1420
+ </div>
1421
+ </div>
1422
+
1423
+ <!-- Base color -->
1424
+ <div class="space-y-1.5">
1425
+ <span class="text-xs font-medium">Base color</span>
1426
+ <div class="grid grid-cols-9 gap-2">
1427
+ @for (b of bases; track b.value) {
1428
+ <button
1429
+ type="button"
1430
+ class="flex h-8 items-center justify-center rounded-md border border-input ring-offset-2 ring-offset-background transition-all data-[active=true]:ring-2 data-[active=true]:ring-ring"
1431
+ [style.background]="b.color"
1432
+ [attr.data-active]="theme.base() === b.value"
1433
+ (click)="theme.setBase(b.value)"
1434
+ >
1435
+ <span class="sr-only">{{ b.value }}</span>
1436
+ </button>
1437
+ }
1438
+ </div>
1439
+ </div>
1440
+
1441
+ <!-- Accent -->
1442
+ <div class="space-y-1.5">
1443
+ <span class="text-xs font-medium">Accent</span>
1444
+ <div class="grid grid-cols-6 gap-2">
1445
+ @for (a of accents; track a.value) {
1446
+ <button
1447
+ type="button"
1448
+ class="flex h-8 items-center justify-center rounded-md border border-input ring-offset-2 ring-offset-background transition-all data-[active=true]:ring-2 data-[active=true]:ring-ring"
1449
+ [style.background]="a.color"
1450
+ [attr.data-active]="theme.preset() === a.value"
1451
+ (click)="theme.setPreset(a.value)"
1452
+ >
1453
+ <span class="sr-only">{{ a.value }}</span>
1454
+ </button>
1455
+ }
1456
+ </div>
1457
+ </div>
1458
+
1459
+ <!-- Radius -->
1460
+ <div class="space-y-1.5">
1461
+ <span class="text-xs font-medium">Radius</span>
1462
+ <div class="grid grid-cols-6 gap-2">
1463
+ @for (r of radii; track r) {
1464
+ <button
1465
+ type="button"
1466
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1467
+ [attr.data-active]="theme.radius() === r"
1468
+ (click)="theme.setRadius(r)"
1469
+ >
1470
+ {{ r }}
1471
+ </button>
1472
+ }
1473
+ </div>
1474
+ </div>
1475
+
1476
+ <!-- Input style -->
1477
+ <div class="space-y-1.5">
1478
+ <span class="text-xs font-medium">Input style</span>
1479
+ <div class="grid grid-cols-3 gap-2">
1480
+ @for (i of inputStyles; track i.value) {
1481
+ <button
1482
+ type="button"
1483
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1484
+ [attr.data-active]="theme.inputStyle() === i.value"
1485
+ (click)="theme.setInputStyle(i.value)"
1486
+ >
1487
+ {{ i.label }}
1488
+ </button>
1489
+ }
1490
+ </div>
1491
+ </div>
1492
+
1493
+ <!-- Body font -->
1494
+ <div class="space-y-1.5">
1495
+ <span class="text-xs font-medium">Body font</span>
1496
+ <div class="grid grid-cols-3 gap-2">
1497
+ @for (f of fonts; track f.value) {
1498
+ <button
1499
+ type="button"
1500
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1501
+ [style.fontFamily]="f.family"
1502
+ [attr.data-active]="theme.font() === f.value"
1503
+ (click)="theme.setFont(f.value)"
1504
+ >
1505
+ {{ f.label }}
1506
+ </button>
1507
+ }
1508
+ </div>
1509
+ </div>
1510
+
1511
+ <!-- Heading font -->
1512
+ <div class="space-y-1.5">
1513
+ <span class="text-xs font-medium">Heading font</span>
1514
+ <div class="grid grid-cols-3 gap-2">
1515
+ @for (f of fonts; track f.value) {
1516
+ <button
1517
+ type="button"
1518
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1519
+ [style.fontFamily]="f.family"
1520
+ [attr.data-active]="theme.fontHeading() === f.value"
1521
+ (click)="theme.setFontHeading(f.value)"
1522
+ >
1523
+ {{ f.label }}
1524
+ </button>
1525
+ }
1526
+ </div>
1527
+ </div>
1528
+
1529
+ <!-- Shadow -->
1530
+ <div class="space-y-1.5">
1531
+ <span class="text-xs font-medium">Shadow</span>
1532
+ <div class="grid grid-cols-5 gap-2">
1533
+ @for (s of shadows; track s.value) {
1534
+ <button
1535
+ type="button"
1536
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1537
+ [attr.data-active]="theme.shadow() === s.value"
1538
+ (click)="theme.setShadow(s.value)"
1539
+ >
1540
+ {{ s.label }}
1541
+ </button>
1542
+ }
1543
+ </div>
1544
+ </div>
1545
+
1546
+ <!-- Spacing + Tracking -->
1547
+ <div class="grid grid-cols-2 gap-4">
1548
+ <div class="space-y-1.5">
1549
+ <span class="text-xs font-medium">Spacing</span>
1550
+ <div class="grid grid-cols-3 gap-1.5">
1551
+ @for (s of spacings; track s.value) {
1552
+ <button
1553
+ type="button"
1554
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1555
+ [attr.data-active]="theme.spacing() === s.value"
1556
+ (click)="theme.setSpacing(s.value)"
1557
+ >
1558
+ {{ s.label }}
1559
+ </button>
1560
+ }
1561
+ </div>
1562
+ </div>
1563
+ <div class="space-y-1.5">
1564
+ <span class="text-xs font-medium">Tracking</span>
1565
+ <div class="grid grid-cols-3 gap-1.5">
1566
+ @for (t of trackings; track t.value) {
1567
+ <button
1568
+ type="button"
1569
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1570
+ [attr.data-active]="theme.tracking() === t.value"
1571
+ (click)="theme.setTracking(t.value)"
1572
+ >
1573
+ {{ t.label }}
1574
+ </button>
1575
+ }
1576
+ </div>
1577
+ </div>
1578
+ </div>
1579
+
1580
+ <!-- Export -->
1581
+ <div class="border-t pt-4">
1582
+ <p class="mb-2.5 text-xs leading-relaxed text-muted-foreground">
1583
+ Copy your complete theme and paste it as your app stylesheet — the Tailwind import,
1584
+ every token and the
1585
+ <code class="rounded bg-muted px-1 py-0.5 text-[11px]">&#64;theme</code>
1586
+ mapping are included.
1587
+ </p>
1588
+ <button buiButton size="sm" class="w-full" (click)="copyCss()">
1589
+ {{ copied() ? 'Copied to clipboard' : 'Copy theme CSS' }}
1590
+ </button>
1591
+ </div>
1592
+ </div>
1593
+ }
1594
+ `, isInline: true, dependencies: [{ kind: "directive", type: BuiButton, selector: "button[buiButton], a[buiButton]", inputs: ["variant", "size", "class"] }] });
1595
+ }
1596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiThemeCustomizer, decorators: [{
1597
+ type: Component,
1598
+ args: [{
1599
+ selector: 'bui-theme-customizer',
1600
+ imports: [BuiButton],
1601
+ host: { 'data-slot': 'theme-customizer' },
1602
+ template: `
1603
+ <button
1604
+ buiButton
1605
+ variant="outline"
1606
+ size="sm"
1607
+ class="fixed right-4 bottom-4 z-50 shadow-md"
1608
+ [attr.aria-expanded]="open()"
1609
+ (click)="open.set(!open())"
1610
+ >
1611
+ {{ open() ? 'Close' : 'Customize' }}
1612
+ </button>
1613
+
1614
+ @if (open()) {
1615
+ <div
1616
+ role="dialog"
1617
+ aria-label="Theme customizer"
1618
+ class="fixed right-4 bottom-16 z-50 max-h-[80vh] w-[340px] space-y-5 overflow-y-auto rounded-lg border bg-popover p-4 text-popover-foreground shadow-lg"
1619
+ >
1620
+ <div class="flex items-center justify-between">
1621
+ <div>
1622
+ <h4 class="text-sm font-semibold">Customize</h4>
1623
+ <p class="text-xs text-muted-foreground">
1624
+ Tune it live. Every preset is pure CSS variables.
1625
+ </p>
1626
+ </div>
1627
+ <button buiButton variant="ghost" size="sm" (click)="theme.reset()">Reset</button>
1628
+ </div>
1629
+
1630
+ <!-- Mode -->
1631
+ <div class="space-y-1.5">
1632
+ <span class="text-xs font-medium">Mode</span>
1633
+ <div class="grid grid-cols-3 gap-2">
1634
+ @for (m of modes; track m.value) {
1635
+ <button
1636
+ type="button"
1637
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1638
+ [attr.data-active]="theme.mode() === m.value"
1639
+ (click)="theme.setMode(m.value)"
1640
+ >
1641
+ {{ m.label }}
1642
+ </button>
1643
+ }
1644
+ </div>
1645
+ </div>
1646
+
1647
+ <!-- Base color -->
1648
+ <div class="space-y-1.5">
1649
+ <span class="text-xs font-medium">Base color</span>
1650
+ <div class="grid grid-cols-9 gap-2">
1651
+ @for (b of bases; track b.value) {
1652
+ <button
1653
+ type="button"
1654
+ class="flex h-8 items-center justify-center rounded-md border border-input ring-offset-2 ring-offset-background transition-all data-[active=true]:ring-2 data-[active=true]:ring-ring"
1655
+ [style.background]="b.color"
1656
+ [attr.data-active]="theme.base() === b.value"
1657
+ (click)="theme.setBase(b.value)"
1658
+ >
1659
+ <span class="sr-only">{{ b.value }}</span>
1660
+ </button>
1661
+ }
1662
+ </div>
1663
+ </div>
1664
+
1665
+ <!-- Accent -->
1666
+ <div class="space-y-1.5">
1667
+ <span class="text-xs font-medium">Accent</span>
1668
+ <div class="grid grid-cols-6 gap-2">
1669
+ @for (a of accents; track a.value) {
1670
+ <button
1671
+ type="button"
1672
+ class="flex h-8 items-center justify-center rounded-md border border-input ring-offset-2 ring-offset-background transition-all data-[active=true]:ring-2 data-[active=true]:ring-ring"
1673
+ [style.background]="a.color"
1674
+ [attr.data-active]="theme.preset() === a.value"
1675
+ (click)="theme.setPreset(a.value)"
1676
+ >
1677
+ <span class="sr-only">{{ a.value }}</span>
1678
+ </button>
1679
+ }
1680
+ </div>
1681
+ </div>
1682
+
1683
+ <!-- Radius -->
1684
+ <div class="space-y-1.5">
1685
+ <span class="text-xs font-medium">Radius</span>
1686
+ <div class="grid grid-cols-6 gap-2">
1687
+ @for (r of radii; track r) {
1688
+ <button
1689
+ type="button"
1690
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1691
+ [attr.data-active]="theme.radius() === r"
1692
+ (click)="theme.setRadius(r)"
1693
+ >
1694
+ {{ r }}
1695
+ </button>
1696
+ }
1697
+ </div>
1698
+ </div>
1699
+
1700
+ <!-- Input style -->
1701
+ <div class="space-y-1.5">
1702
+ <span class="text-xs font-medium">Input style</span>
1703
+ <div class="grid grid-cols-3 gap-2">
1704
+ @for (i of inputStyles; track i.value) {
1705
+ <button
1706
+ type="button"
1707
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1708
+ [attr.data-active]="theme.inputStyle() === i.value"
1709
+ (click)="theme.setInputStyle(i.value)"
1710
+ >
1711
+ {{ i.label }}
1712
+ </button>
1713
+ }
1714
+ </div>
1715
+ </div>
1716
+
1717
+ <!-- Body font -->
1718
+ <div class="space-y-1.5">
1719
+ <span class="text-xs font-medium">Body font</span>
1720
+ <div class="grid grid-cols-3 gap-2">
1721
+ @for (f of fonts; track f.value) {
1722
+ <button
1723
+ type="button"
1724
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1725
+ [style.fontFamily]="f.family"
1726
+ [attr.data-active]="theme.font() === f.value"
1727
+ (click)="theme.setFont(f.value)"
1728
+ >
1729
+ {{ f.label }}
1730
+ </button>
1731
+ }
1732
+ </div>
1733
+ </div>
1734
+
1735
+ <!-- Heading font -->
1736
+ <div class="space-y-1.5">
1737
+ <span class="text-xs font-medium">Heading font</span>
1738
+ <div class="grid grid-cols-3 gap-2">
1739
+ @for (f of fonts; track f.value) {
1740
+ <button
1741
+ type="button"
1742
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1743
+ [style.fontFamily]="f.family"
1744
+ [attr.data-active]="theme.fontHeading() === f.value"
1745
+ (click)="theme.setFontHeading(f.value)"
1746
+ >
1747
+ {{ f.label }}
1748
+ </button>
1749
+ }
1750
+ </div>
1751
+ </div>
1752
+
1753
+ <!-- Shadow -->
1754
+ <div class="space-y-1.5">
1755
+ <span class="text-xs font-medium">Shadow</span>
1756
+ <div class="grid grid-cols-5 gap-2">
1757
+ @for (s of shadows; track s.value) {
1758
+ <button
1759
+ type="button"
1760
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1761
+ [attr.data-active]="theme.shadow() === s.value"
1762
+ (click)="theme.setShadow(s.value)"
1763
+ >
1764
+ {{ s.label }}
1765
+ </button>
1766
+ }
1767
+ </div>
1768
+ </div>
1769
+
1770
+ <!-- Spacing + Tracking -->
1771
+ <div class="grid grid-cols-2 gap-4">
1772
+ <div class="space-y-1.5">
1773
+ <span class="text-xs font-medium">Spacing</span>
1774
+ <div class="grid grid-cols-3 gap-1.5">
1775
+ @for (s of spacings; track s.value) {
1776
+ <button
1777
+ type="button"
1778
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1779
+ [attr.data-active]="theme.spacing() === s.value"
1780
+ (click)="theme.setSpacing(s.value)"
1781
+ >
1782
+ {{ s.label }}
1783
+ </button>
1784
+ }
1785
+ </div>
1786
+ </div>
1787
+ <div class="space-y-1.5">
1788
+ <span class="text-xs font-medium">Tracking</span>
1789
+ <div class="grid grid-cols-3 gap-1.5">
1790
+ @for (t of trackings; track t.value) {
1791
+ <button
1792
+ type="button"
1793
+ class="inline-flex h-8 items-center justify-center rounded-md border border-input text-xs font-medium transition-colors hover:bg-accent data-[active=true]:border-primary data-[active=true]:bg-accent"
1794
+ [attr.data-active]="theme.tracking() === t.value"
1795
+ (click)="theme.setTracking(t.value)"
1796
+ >
1797
+ {{ t.label }}
1798
+ </button>
1799
+ }
1800
+ </div>
1801
+ </div>
1802
+ </div>
1803
+
1804
+ <!-- Export -->
1805
+ <div class="border-t pt-4">
1806
+ <p class="mb-2.5 text-xs leading-relaxed text-muted-foreground">
1807
+ Copy your complete theme and paste it as your app stylesheet — the Tailwind import,
1808
+ every token and the
1809
+ <code class="rounded bg-muted px-1 py-0.5 text-[11px]">&#64;theme</code>
1810
+ mapping are included.
1811
+ </p>
1812
+ <button buiButton size="sm" class="w-full" (click)="copyCss()">
1813
+ {{ copied() ? 'Copied to clipboard' : 'Copy theme CSS' }}
1814
+ </button>
1815
+ </div>
1816
+ </div>
1817
+ }
1818
+ `,
1819
+ }]
1820
+ }] });
1821
+
1822
+ /*
1823
+ * Public API Surface of ng-blatui
1824
+ */
1825
+
1826
+ /**
1827
+ * Generated bundle index. Do not edit.
1828
+ */
1829
+
1830
+ export { BuiAccordion, BuiAccordionContent, BuiAccordionItem, BuiAccordionTrigger, BuiAlert, BuiAlertDescription, BuiAlertTitle, BuiAvatar, BuiBadge, BuiButton, BuiCard, BuiCardAction, BuiCardContent, BuiCardDescription, BuiCardFooter, BuiCardHeader, BuiCardTitle, BuiCheckbox, BuiDialogContent, BuiDialogDescription, BuiDialogFooter, BuiDialogHeader, BuiDialogTitle, BuiInput, BuiLabel, BuiProgress, BuiSeparator, BuiSkeleton, BuiSwitch, BuiTabList, BuiTabPanel, BuiTabTrigger, BuiTabs, BuiTextarea, BuiThemeCustomizer, THEME_TOKENS, ThemeStore, buttonVariants, cn };
1831
+ //# sourceMappingURL=ng-blatui.mjs.map