ng-blatui 1.32.2 → 1.33.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.
@@ -189,6 +189,11 @@ const VARIANTS$2 = {
189
189
  outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
190
190
  };
191
191
  const TONES$2 = {
192
+ primary: {
193
+ soft: 'border-transparent bg-primary/10 text-primary dark:bg-primary/15',
194
+ solid: 'border-transparent bg-primary text-primary-foreground',
195
+ outline: 'text-primary border-primary/40',
196
+ },
192
197
  success: {
193
198
  soft: 'border-transparent bg-success/10 text-success dark:bg-success/15',
194
199
  solid: 'border-transparent bg-success text-success-foreground',
@@ -236,7 +241,7 @@ function intensityFor(variant) {
236
241
  }
237
242
  /**
238
243
  * BlatUI badge. Brand variants (`default | secondary | destructive | outline`)
239
- * or semantic `tone` (`success | warning | danger | info | neutral`) where
244
+ * or semantic `tone` (`primary | success | warning | danger | info | neutral`) where
240
245
  * `variant` then selects intensity (`soft | solid | outline`). Built on a native
241
246
  * element so it stays in the document flow and screen-reader friendly.
242
247
  */
@@ -819,7 +824,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
819
824
 
820
825
  // eslint-disable-next-line @typescript-eslint/no-empty-function
821
826
  const noop$i = () => { };
822
- 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';
827
+ const CHECKBOX_BASE = 'peer border-input dark:bg-input/30 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';
828
+ /** Per-tone checked/indeterminate fill colors. `primary` is the default (unchanged) look. */
829
+ const CHECKBOX_TONES = {
830
+ primary: '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',
831
+ success: 'data-[state=checked]:bg-success data-[state=checked]:text-success-foreground data-[state=checked]:border-success data-[state=indeterminate]:bg-success data-[state=indeterminate]:text-success-foreground data-[state=indeterminate]:border-success',
832
+ warning: 'data-[state=checked]:bg-warning data-[state=checked]:text-warning-foreground data-[state=checked]:border-warning data-[state=indeterminate]:bg-warning data-[state=indeterminate]:text-warning-foreground data-[state=indeterminate]:border-warning',
833
+ danger: 'data-[state=checked]:bg-destructive data-[state=checked]:text-white data-[state=checked]:border-destructive data-[state=indeterminate]:bg-destructive data-[state=indeterminate]:text-white data-[state=indeterminate]:border-destructive',
834
+ info: 'data-[state=checked]:bg-info data-[state=checked]:text-info-foreground data-[state=checked]:border-info data-[state=indeterminate]:bg-info data-[state=indeterminate]:text-info-foreground data-[state=indeterminate]:border-info',
835
+ };
823
836
  /**
824
837
  * BlatUI checkbox. A native `<button role="checkbox">` (Space/Enter toggle for free)
825
838
  * with `[(checked)]` two-way binding and tri-state `[(indeterminate)]`.
@@ -834,6 +847,9 @@ class BuiCheckbox {
834
847
  /** Whether the checkbox is disabled. Two-way bindable with `[(disabled)]`. */
835
848
  disabled = model(false, /* @ts-ignore */
836
849
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
850
+ /** Semantic color of the checked/indeterminate fill. */
851
+ tone = input('primary', /* @ts-ignore */
852
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
837
853
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
838
854
  onChange = noop$i;
839
855
  onTouched = noop$i;
@@ -846,7 +862,7 @@ class BuiCheckbox {
846
862
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
847
863
  ariaChecked = computed(() => this.indeterminate() ? 'mixed' : String(this.checked()), /* @ts-ignore */
848
864
  ...(ngDevMode ? [{ debugName: "ariaChecked" }] : /* istanbul ignore next */ []));
849
- computedClass = computed(() => cn(CHECKBOX_BASE, this.userClass()), /* @ts-ignore */
865
+ computedClass = computed(() => cn(CHECKBOX_BASE, CHECKBOX_TONES[this.tone()], this.userClass()), /* @ts-ignore */
850
866
  ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
851
867
  toggle() {
852
868
  if (this.disabled()) {
@@ -875,7 +891,7 @@ class BuiCheckbox {
875
891
  this.disabled.set(isDisabled);
876
892
  }
877
893
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
878
- 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", disabled: "disabledChange" }, host: { attributes: { "type": "button", "role": "checkbox", "data-slot": "checkbox" }, listeners: { "click": "toggle()", "blur": "onTouched()" }, properties: { "attr.aria-checked": "ariaChecked()", "attr.data-state": "state()", "disabled": "disabled()", "class": "computedClass()" } }, providers: [
894
+ 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 }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", indeterminate: "indeterminateChange", disabled: "disabledChange" }, host: { attributes: { "type": "button", "role": "checkbox", "data-slot": "checkbox" }, listeners: { "click": "toggle()", "blur": "onTouched()" }, properties: { "attr.aria-checked": "ariaChecked()", "attr.data-state": "state()", "disabled": "disabled()", "class": "computedClass()" } }, providers: [
879
895
  { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => BuiCheckbox), multi: true },
880
896
  ], ngImport: i0, template: `
881
897
  @if (checked() || indeterminate()) {
@@ -967,7 +983,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
967
983
  }
968
984
  `,
969
985
  }]
970
- }], 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 }] }, { type: i0.Output, args: ["disabledChange"] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
986
+ }], 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 }] }, { type: i0.Output, args: ["disabledChange"] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
971
987
 
972
988
  // eslint-disable-next-line @typescript-eslint/no-empty-function
973
989
  const noop$h = () => { };
@@ -6215,6 +6231,12 @@ class BuiSelect {
6215
6231
  /** Whether the select is disabled. Two-way bindable with `[(disabled)]`. */
6216
6232
  disabled = model(false, /* @ts-ignore */
6217
6233
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
6234
+ /**
6235
+ * Accessible name for the combobox. Bound from a plain `aria-label="…"` on the host
6236
+ * (consumed as an input, so it's applied to the inner combobox button rather than left
6237
+ * as a prohibited ARIA attribute on the custom element).
6238
+ */
6239
+ ariaLabel = input('', { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
6218
6240
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
6219
6241
  onChange = noop$b;
6220
6242
  onTouched = noop$b;
@@ -6355,12 +6377,13 @@ class BuiSelect {
6355
6377
  this.disabled.set(isDisabled);
6356
6378
  }
6357
6379
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
6358
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiSelect, isStandalone: true, selector: "bui-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", 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: { value: "valueChange", values: "valuesChange", disabled: "disabledChange" }, host: { attributes: { "data-slot": "select" }, listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "computedClass()" } }, providers: [
6380
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiSelect, isStandalone: true, selector: "bui-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", values: "valuesChange", disabled: "disabledChange" }, host: { attributes: { "data-slot": "select" }, listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "computedClass()" } }, providers: [
6359
6381
  { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => BuiSelect), multi: true },
6360
6382
  ], ngImport: i0, template: `
6361
6383
  <button
6362
6384
  type="button"
6363
6385
  role="combobox"
6386
+ [attr.aria-label]="ariaLabel() || placeholder()"
6364
6387
  [attr.aria-expanded]="open()"
6365
6388
  aria-haspopup="listbox"
6366
6389
  [attr.aria-controls]="listId"
@@ -6477,6 +6500,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
6477
6500
  <button
6478
6501
  type="button"
6479
6502
  role="combobox"
6503
+ [attr.aria-label]="ariaLabel() || placeholder()"
6480
6504
  [attr.aria-expanded]="open()"
6481
6505
  aria-haspopup="listbox"
6482
6506
  [attr.aria-controls]="listId"
@@ -6577,7 +6601,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
6577
6601
  }
6578
6602
  `,
6579
6603
  }]
6580
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
6604
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
6581
6605
 
6582
6606
  const VARIANTS$1 = {
6583
6607
  primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
@@ -10838,6 +10862,60 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
10838
10862
  }]
10839
10863
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
10840
10864
 
10865
+ const ICON_TILE_TONES = {
10866
+ primary: 'bg-primary/10 text-primary',
10867
+ success: 'bg-success/10 text-success dark:bg-success/15',
10868
+ warning: 'bg-warning/10 text-warning dark:bg-warning/15',
10869
+ info: 'bg-info/10 text-info dark:bg-info/15',
10870
+ destructive: 'bg-destructive/10 text-destructive dark:bg-destructive/15',
10871
+ muted: 'bg-muted text-foreground',
10872
+ 'chart-1': 'bg-chart-1/10 text-chart-1',
10873
+ 'chart-2': 'bg-chart-2/10 text-chart-2',
10874
+ 'chart-3': 'bg-chart-3/10 text-chart-3',
10875
+ 'chart-4': 'bg-chart-4/10 text-chart-4',
10876
+ 'chart-5': 'bg-chart-5/10 text-chart-5',
10877
+ };
10878
+ const ICON_TILE_SIZES = {
10879
+ sm: 'size-8 [&_svg]:size-4',
10880
+ default: 'size-10 [&_svg]:size-5',
10881
+ lg: 'size-12 [&_svg]:size-6',
10882
+ };
10883
+ const ICON_TILE_SHAPES = {
10884
+ sm: 'rounded-md',
10885
+ default: 'rounded-lg',
10886
+ lg: 'rounded-xl',
10887
+ };
10888
+ /**
10889
+ * A tinted rounded square that frames a projected icon — the small "tile" used
10890
+ * across dashboards for stat-card leading icons, list-row markers, feature
10891
+ * bullets, etc. Project an icon (e.g. an inline `<svg>`) as its content; the
10892
+ * tile sizes it automatically.
10893
+ */
10894
+ class BuiIconTile {
10895
+ /** Colour tone (semantic or chart palette). */
10896
+ tone = input('primary', /* @ts-ignore */
10897
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
10898
+ /** Square size preset. */
10899
+ size = input('default', /* @ts-ignore */
10900
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
10901
+ /** Tile shape — a rounded square (default) or a full circle. */
10902
+ shape = input('square', /* @ts-ignore */
10903
+ ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
10904
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
10905
+ computedClass = computed(() => cn('inline-flex shrink-0 items-center justify-center', ICON_TILE_SIZES[this.size()], this.shape() === 'circle' ? 'rounded-full' : ICON_TILE_SHAPES[this.size()], ICON_TILE_TONES[this.tone()], this.userClass()), /* @ts-ignore */
10906
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
10907
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiIconTile, deps: [], target: i0.ɵɵFactoryTarget.Component });
10908
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: BuiIconTile, isStandalone: true, selector: "bui-icon-tile", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "icon-tile" }, properties: { "class": "computedClass()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
10909
+ }
10910
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiIconTile, decorators: [{
10911
+ type: Component,
10912
+ args: [{
10913
+ selector: 'bui-icon-tile',
10914
+ host: { 'data-slot': 'icon-tile', '[class]': 'computedClass()' },
10915
+ template: `<ng-content />`,
10916
+ }]
10917
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
10918
+
10841
10919
  /** A lazy image with a blurred placeholder / skeleton and a graceful error state. */
10842
10920
  class BuiImage {
10843
10921
  /** Source URL of the full image to load. */
@@ -16881,56 +16959,185 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
16881
16959
  const PALETTE = ['#6366f1', '#22c55e', '#f59e0b', '#ec4899', '#06b6d4'];
16882
16960
  const WIDTH = 600;
16883
16961
  const PAD = 8;
16884
- /** A lightweight SVG chart (line / area / bar). Pure + SSR-safe. */
16962
+ /** Circumference of the donut ring (2·π·r with r = 40 in the 100×100 viewBox). */
16963
+ const DONUT_C = 251.33;
16964
+ /** A lightweight SVG chart (line / area / bar / donut). Pure + SSR-safe. */
16885
16965
  class BuiChart {
16886
- /** Chart render style: line, bar, or filled area. */
16966
+ /** Chart render style: line, bar, filled area, a donut ring, or a half-ring gauge. */
16887
16967
  type = input('line', /* @ts-ignore */
16888
16968
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
16889
- /** Series to plot; bar charts use only the first series. */
16969
+ /** Series to plot; bar/donut use only the first series. */
16890
16970
  series = input([], /* @ts-ignore */
16891
16971
  ...(ngDevMode ? [{ debugName: "series" }] : /* istanbul ignore next */ []));
16892
16972
  /** X-axis tick labels rendered below the chart. */
16893
16973
  labels = input([], /* @ts-ignore */
16894
16974
  ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
16895
- /** Chart height in pixels (SVG viewBox height). */
16975
+ /** Chart height in pixels (SVG viewBox height / donut diameter). */
16896
16976
  height = input(220, /* @ts-ignore */
16897
16977
  ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
16978
+ /**
16979
+ * Override for the value the chart scales against. For a donut it defines the
16980
+ * full ring (e.g. `max=100` with data `[62]` fills 62%); otherwise the max of
16981
+ * the series data is used.
16982
+ */
16983
+ max = input(null, /* @ts-ignore */
16984
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
16985
+ /** Donut ring thickness, in the 100×100 viewBox units. */
16986
+ thickness = input(12, /* @ts-ignore */
16987
+ ...(ngDevMode ? [{ debugName: "thickness" }] : /* istanbul ignore next */ []));
16988
+ /**
16989
+ * Index of the highlighted bar (bar charts). When set, the active bar renders
16990
+ * solid and the rest are tinted; when `null` every bar renders solid.
16991
+ */
16992
+ activeIndex = input(null, /* @ts-ignore */
16993
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
16994
+ /** Value text shown above each bar (bar charts); pass one entry per data point. */
16995
+ barLabels = input([], /* @ts-ignore */
16996
+ ...(ngDevMode ? [{ debugName: "barLabels" }] : /* istanbul ignore next */ []));
16997
+ /** Stack every series into one bar per data point, bottom-to-top (bar charts). */
16998
+ stacked = input(false, /* @ts-ignore */
16999
+ ...(ngDevMode ? [{ debugName: "stacked" }] : /* istanbul ignore next */ []));
17000
+ /** Render every series side-by-side within each data slot (grouped bar charts). */
17001
+ grouped = input(false, /* @ts-ignore */
17002
+ ...(ngDevMode ? [{ debugName: "grouped" }] : /* istanbul ignore next */ []));
17003
+ /** Per-bar fill colors (bar charts); index-aligned with the first series' data. Overrides the series color. */
17004
+ barColors = input([], /* @ts-ignore */
17005
+ ...(ngDevMode ? [{ debugName: "barColors" }] : /* istanbul ignore next */ []));
17006
+ /** Draw a faint full-height track behind each bar (bar charts). */
17007
+ track = input(false, /* @ts-ignore */
17008
+ ...(ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []));
17009
+ /** Render a dot marker at each data point (line/area charts). */
17010
+ dots = input(false, /* @ts-ignore */
17011
+ ...(ngDevMode ? [{ debugName: "dots" }] : /* istanbul ignore next */ []));
17012
+ /** Number of evenly-spaced vertical grid lines (line/area charts; 0 hides them). */
17013
+ gridX = input(0, /* @ts-ignore */
17014
+ ...(ngDevMode ? [{ debugName: "gridX" }] : /* istanbul ignore next */ []));
16898
17015
  /** Accessible label for the chart's `aria-label`. */
16899
17016
  label = input('Chart', /* @ts-ignore */
16900
17017
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
16901
17018
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
16902
17019
  width = WIDTH;
16903
- max = computed(() => Math.max(1, ...this.series().flatMap((s) => s.data)), /* @ts-ignore */
16904
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
16905
- bars = computed(() => {
16906
- const series = this.series().at(0);
16907
- if (!series) {
17020
+ donutC = DONUT_C;
17021
+ scaleMax = computed(() => this.max() ?? Math.max(1, ...this.series().flatMap((s) => s.data)), /* @ts-ignore */
17022
+ ...(ngDevMode ? [{ debugName: "scaleMax" }] : /* istanbul ignore next */ []));
17023
+ /**
17024
+ * HTML bar columns. Each column has an overall height (`pct`, scaled against the
17025
+ * max) plus one or more stacked `segments` (a single segment in the common case,
17026
+ * several when `stacked`). Inactive bars are tinted when `activeIndex` is set.
17027
+ */
17028
+ barColumns = computed(() => {
17029
+ const seriesList = this.series();
17030
+ const first = seriesList.at(0);
17031
+ if (!first || first.data.length === 0) {
16908
17032
  return [];
16909
17033
  }
16910
- const height = this.height();
16911
- const count = series.data.length;
16912
- const gap = 8;
16913
- const barWidth = (WIDTH - gap * (count + 1)) / Math.max(1, count);
16914
- return series.data.map((value, index) => {
16915
- const barHeight = (value / this.max()) * (height - PAD * 2);
17034
+ const axisLabels = this.labels();
17035
+ const count = first.data.length;
17036
+ if (this.stacked() && seriesList.length > 1) {
17037
+ const totals = Array.from({ length: count }, (_, index) => seriesList.reduce((sum, s) => sum + Math.max(0, s.data[index] ?? 0), 0));
17038
+ const max = this.max() ?? Math.max(1, ...totals);
17039
+ return Array.from({ length: count }, (_, index) => ({
17040
+ id: index,
17041
+ pct: (totals[index] / max) * 100,
17042
+ segments: seriesList
17043
+ .map((s, si) => ({
17044
+ id: si,
17045
+ share: totals[index] > 0 ? (Math.max(0, s.data[index] ?? 0) / totals[index]) * 100 : 0,
17046
+ fill: s.color ?? PALETTE[si % PALETTE.length],
17047
+ }))
17048
+ .filter((seg) => seg.share > 0),
17049
+ value: '',
17050
+ label: axisLabels[index] ?? '',
17051
+ }));
17052
+ }
17053
+ const total = this.scaleMax();
17054
+ const active = this.activeIndex();
17055
+ const valueLabels = this.barLabels();
17056
+ const perBar = this.barColors();
17057
+ const color = first.color ?? PALETTE[0];
17058
+ return first.data.map((value, index) => {
17059
+ const base = perBar[index] ?? color;
17060
+ const isDimmed = active !== null && active !== index;
17061
+ const fill = isDimmed ? `color-mix(in oklab, ${base} 20%, transparent)` : base;
16916
17062
  return {
16917
17063
  id: index,
16918
- x: gap + index * (barWidth + gap),
16919
- y: height - PAD - barHeight,
16920
- w: barWidth,
16921
- h: barHeight,
16922
- color: series.color ?? PALETTE[0],
17064
+ pct: Math.max(0, (Math.max(0, value) / total) * 100),
17065
+ segments: [{ id: 0, share: 100, fill }],
17066
+ value: valueLabels[index] ?? '',
17067
+ label: axisLabels[index] ?? '',
16923
17068
  };
16924
17069
  });
16925
17070
  }, /* @ts-ignore */
16926
- ...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
17071
+ ...(ngDevMode ? [{ debugName: "barColumns" }] : /* istanbul ignore next */ []));
17072
+ /** Grouped bars: one slot per data point, each holding a side-by-side sub-bar per series. */
17073
+ barGroups = computed(() => {
17074
+ const seriesList = this.series();
17075
+ const first = seriesList.at(0);
17076
+ if (!first || first.data.length === 0) {
17077
+ return [];
17078
+ }
17079
+ const axisLabels = this.labels();
17080
+ const total = this.scaleMax();
17081
+ return first.data.map((_, index) => ({
17082
+ id: index,
17083
+ label: axisLabels[index] ?? '',
17084
+ bars: seriesList.map((s, si) => ({
17085
+ id: si,
17086
+ pct: Math.max(0, (Math.max(0, s.data[index] ?? 0) / total) * 100),
17087
+ fill: s.color ?? PALETTE[si % PALETTE.length],
17088
+ })),
17089
+ }));
17090
+ }, /* @ts-ignore */
17091
+ ...(ngDevMode ? [{ debugName: "barGroups" }] : /* istanbul ignore next */ []));
17092
+ /** Half-ring gauge geometry: a 180° arc filled to the first value's share of `max`. */
17093
+ gauge = computed(() => {
17094
+ const value = this.series().at(0)?.data.at(0) ?? 0;
17095
+ const total = this.max() ?? 100;
17096
+ const fraction = Math.min(1, Math.max(0, value / total));
17097
+ // Semicircle path length for r=40 in the 100×60 viewBox (π·r).
17098
+ const arc = Math.PI * 40;
17099
+ return {
17100
+ dash: (fraction * arc).toFixed(2),
17101
+ gap: arc.toFixed(2),
17102
+ color: this.series().at(0)?.color,
17103
+ };
17104
+ }, /* @ts-ignore */
17105
+ ...(ngDevMode ? [{ debugName: "gauge" }] : /* istanbul ignore next */ []));
17106
+ /** Dot markers (line/area) as viewBox-relative percentages, immune to the non-uniform SVG scale. */
17107
+ lineDots = computed(() => {
17108
+ const series = this.series().at(0);
17109
+ if (!series || !this.dots()) {
17110
+ return [];
17111
+ }
17112
+ const height = this.height();
17113
+ const data = series.data;
17114
+ const step = data.length > 1 ? (WIDTH - PAD * 2) / (data.length - 1) : 0;
17115
+ const color = series.color ?? PALETTE[0];
17116
+ return data.map((value, index) => ({
17117
+ id: index,
17118
+ x: ((PAD + index * step) / WIDTH) * 100,
17119
+ y: ((height - PAD - (value / this.scaleMax()) * (height - PAD * 2)) / height) * 100,
17120
+ color,
17121
+ }));
17122
+ }, /* @ts-ignore */
17123
+ ...(ngDevMode ? [{ debugName: "lineDots" }] : /* istanbul ignore next */ []));
17124
+ /** Evenly-spaced vertical grid line x-positions (in viewBox units). */
17125
+ gridLines = computed(() => {
17126
+ const n = this.gridX();
17127
+ if (n <= 0) {
17128
+ return [];
17129
+ }
17130
+ const span = WIDTH - PAD * 2;
17131
+ return Array.from({ length: n }, (_, index) => PAD + ((index + 1) / (n + 1)) * span);
17132
+ }, /* @ts-ignore */
17133
+ ...(ngDevMode ? [{ debugName: "gridLines" }] : /* istanbul ignore next */ []));
16927
17134
  paths = computed(() => this.series().map((series, seriesIndex) => {
16928
17135
  const data = series.data;
16929
17136
  const height = this.height();
16930
17137
  const step = data.length > 1 ? (WIDTH - PAD * 2) / (data.length - 1) : 0;
16931
17138
  const points = data.map((value, index) => ({
16932
17139
  x: PAD + index * step,
16933
- y: height - PAD - (value / this.max()) * (height - PAD * 2),
17140
+ y: height - PAD - (value / this.scaleMax()) * (height - PAD * 2),
16934
17141
  }));
16935
17142
  const coords = points
16936
17143
  .map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`)
@@ -16948,51 +17155,198 @@ class BuiChart {
16948
17155
  };
16949
17156
  }), /* @ts-ignore */
16950
17157
  ...(ngDevMode ? [{ debugName: "paths" }] : /* istanbul ignore next */ []));
17158
+ /** Donut arcs: each value in the first series becomes a ring segment. */
17159
+ donutSegments = computed(() => {
17160
+ const series = this.series().at(0);
17161
+ if (!series) {
17162
+ return [];
17163
+ }
17164
+ const total = this.scaleMax();
17165
+ let accumulated = 0;
17166
+ return series.data.map((value, index) => {
17167
+ const fraction = Math.max(0, value) / total;
17168
+ const segment = {
17169
+ id: index,
17170
+ dash: (fraction * DONUT_C).toFixed(2),
17171
+ offset: (-accumulated * DONUT_C).toFixed(2),
17172
+ color: series.color ?? PALETTE[index % PALETTE.length],
17173
+ };
17174
+ accumulated += fraction;
17175
+ return segment;
17176
+ });
17177
+ }, /* @ts-ignore */
17178
+ ...(ngDevMode ? [{ debugName: "donutSegments" }] : /* istanbul ignore next */ []));
16951
17179
  computedClass = computed(() => cn('block', this.userClass()), /* @ts-ignore */
16952
17180
  ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
16953
17181
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
16954
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiChart, isStandalone: true, selector: "bui-chart", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "chart" }, properties: { "class": "computedClass()" } }, ngImport: i0, template: `
16955
- <svg
16956
- [attr.viewBox]="'0 0 ' + width + ' ' + height()"
16957
- [attr.height]="height()"
16958
- class="w-full"
16959
- role="img"
16960
- [attr.aria-label]="label()"
16961
- preserveAspectRatio="none"
16962
- >
16963
- @if (type() === 'bar') {
16964
- @for (bar of bars(); track bar.id) {
16965
- <rect
16966
- [attr.x]="bar.x"
16967
- [attr.y]="bar.y"
16968
- [attr.width]="bar.w"
16969
- [attr.height]="bar.h"
16970
- [attr.fill]="bar.color"
16971
- rx="3"
17182
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiChart, isStandalone: true, selector: "bui-chart", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, thickness: { classPropertyName: "thickness", publicName: "thickness", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, barLabels: { classPropertyName: "barLabels", publicName: "barLabels", isSignal: true, isRequired: false, transformFunction: null }, stacked: { classPropertyName: "stacked", publicName: "stacked", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, barColors: { classPropertyName: "barColors", publicName: "barColors", isSignal: true, isRequired: false, transformFunction: null }, track: { classPropertyName: "track", publicName: "track", isSignal: true, isRequired: false, transformFunction: null }, dots: { classPropertyName: "dots", publicName: "dots", isSignal: true, isRequired: false, transformFunction: null }, gridX: { classPropertyName: "gridX", publicName: "gridX", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "chart" }, properties: { "class": "computedClass()" } }, ngImport: i0, template: `
17183
+ @if (type() === 'donut') {
17184
+ <div class="relative mx-auto aspect-square" [style.height.px]="height()">
17185
+ <svg
17186
+ viewBox="0 0 100 100"
17187
+ class="size-full -rotate-90"
17188
+ role="img"
17189
+ [attr.aria-label]="label()"
17190
+ >
17191
+ <circle
17192
+ cx="50"
17193
+ cy="50"
17194
+ r="40"
17195
+ fill="none"
17196
+ class="stroke-muted"
17197
+ [attr.stroke-width]="thickness()"
16972
17198
  />
16973
- }
16974
- } @else {
16975
- @for (path of paths(); track path.id) {
16976
- @if (type() === 'area') {
16977
- <path [attr.d]="path.area" [attr.fill]="path.color" fill-opacity="0.12" />
17199
+ @for (seg of donutSegments(); track seg.id) {
17200
+ <circle
17201
+ cx="50"
17202
+ cy="50"
17203
+ r="40"
17204
+ fill="none"
17205
+ stroke-linecap="round"
17206
+ [attr.stroke]="seg.color"
17207
+ [attr.stroke-width]="thickness()"
17208
+ [attr.stroke-dasharray]="seg.dash + ' ' + donutC"
17209
+ [attr.stroke-dashoffset]="seg.offset"
17210
+ />
16978
17211
  }
17212
+ </svg>
17213
+ <div class="absolute inset-0 flex flex-col items-center justify-center leading-none">
17214
+ <ng-content />
17215
+ </div>
17216
+ </div>
17217
+ } @else if (type() === 'gauge') {
17218
+ <div class="relative mx-auto w-full" [style.maxWidth.px]="height() * 1.9">
17219
+ <svg viewBox="0 0 100 56" class="w-full" role="img" [attr.aria-label]="label()">
16979
17220
  <path
16980
- [attr.d]="path.line"
17221
+ d="M10,50 A40,40 0 0 1 90,50"
16981
17222
  fill="none"
16982
- [attr.stroke]="path.color"
16983
- stroke-width="2"
16984
- stroke-linejoin="round"
17223
+ class="stroke-muted"
16985
17224
  stroke-linecap="round"
17225
+ [attr.stroke-width]="thickness()"
16986
17226
  />
17227
+ <path
17228
+ d="M10,50 A40,40 0 0 1 90,50"
17229
+ fill="none"
17230
+ stroke-linecap="round"
17231
+ [attr.stroke]="gauge().color"
17232
+ [attr.stroke-width]="thickness()"
17233
+ [attr.stroke-dasharray]="gauge().dash + ' ' + gauge().gap"
17234
+ />
17235
+ </svg>
17236
+ <div class="absolute inset-x-0 bottom-0 flex flex-col items-center leading-none">
17237
+ <ng-content />
17238
+ </div>
17239
+ </div>
17240
+ } @else if (type() === 'bar' && grouped()) {
17241
+ <div
17242
+ class="flex items-end gap-2"
17243
+ [style.height.px]="height()"
17244
+ role="img"
17245
+ [attr.aria-label]="label()"
17246
+ >
17247
+ @for (group of barGroups(); track group.id) {
17248
+ <div class="flex h-full flex-1 flex-col items-center justify-end gap-2">
17249
+ <div class="flex w-full flex-1 items-end justify-center gap-1">
17250
+ @for (sub of group.bars; track sub.id) {
17251
+ <div
17252
+ class="min-h-1 w-2 max-w-3 rounded-t-[4px] rounded-b-[2px] transition-[height]"
17253
+ [style.height.%]="sub.pct"
17254
+ [style.background]="sub.fill"
17255
+ ></div>
17256
+ }
17257
+ </div>
17258
+ @if (group.label) {
17259
+ <span class="text-xs text-muted-foreground">{{ group.label }}</span>
17260
+ }
17261
+ </div>
16987
17262
  }
16988
- }
16989
- </svg>
16990
- @if (labels().length > 0) {
16991
- <div class="mt-1 flex justify-between text-xs text-muted-foreground">
16992
- @for (tick of labels(); track $index) {
16993
- <span>{{ tick }}</span>
17263
+ </div>
17264
+ } @else if (type() === 'bar') {
17265
+ <div
17266
+ class="flex items-end gap-1"
17267
+ [style.height.px]="height()"
17268
+ role="img"
17269
+ [attr.aria-label]="label()"
17270
+ >
17271
+ @for (bar of barColumns(); track bar.id) {
17272
+ <div class="flex h-full flex-1 flex-col items-center justify-end gap-2">
17273
+ <div class="relative flex w-full flex-1 items-end justify-center">
17274
+ @if (track()) {
17275
+ <div class="absolute inset-y-0 w-[70%] max-w-9 rounded-[6px] bg-muted"></div>
17276
+ }
17277
+ <div
17278
+ class="relative flex min-h-1 w-[70%] max-w-9 flex-col-reverse overflow-hidden rounded-t-[10px] rounded-b-[4px] transition-[height]"
17279
+ [style.height.%]="bar.pct"
17280
+ >
17281
+ @for (seg of bar.segments; track seg.id) {
17282
+ <div [style.height.%]="seg.share" [style.background]="seg.fill"></div>
17283
+ }
17284
+ @if (bar.value) {
17285
+ <span
17286
+ class="absolute -top-6 left-1/2 -translate-x-1/2 text-sm font-semibold whitespace-nowrap text-foreground"
17287
+ >{{ bar.value }}</span
17288
+ >
17289
+ }
17290
+ </div>
17291
+ </div>
17292
+ @if (bar.label) {
17293
+ <span class="text-xs text-muted-foreground">{{ bar.label }}</span>
17294
+ }
17295
+ </div>
17296
+ }
17297
+ </div>
17298
+ } @else {
17299
+ <div class="relative w-full" [style.height.px]="height()">
17300
+ <svg
17301
+ [attr.viewBox]="'0 0 ' + width + ' ' + height()"
17302
+ [attr.height]="height()"
17303
+ class="w-full"
17304
+ role="img"
17305
+ [attr.aria-label]="label()"
17306
+ preserveAspectRatio="none"
17307
+ >
17308
+ @for (x of gridLines(); track x) {
17309
+ <line
17310
+ [attr.x1]="x"
17311
+ y1="2"
17312
+ [attr.x2]="x"
17313
+ [attr.y2]="height() - 2"
17314
+ class="stroke-border"
17315
+ stroke-width="1"
17316
+ stroke-dasharray="2 3"
17317
+ vector-effect="non-scaling-stroke"
17318
+ />
17319
+ }
17320
+ @for (path of paths(); track path.id) {
17321
+ @if (type() === 'area') {
17322
+ <path [attr.d]="path.area" [attr.fill]="path.color" fill-opacity="0.12" />
17323
+ }
17324
+ <path
17325
+ [attr.d]="path.line"
17326
+ fill="none"
17327
+ [attr.stroke]="path.color"
17328
+ stroke-width="2"
17329
+ stroke-linejoin="round"
17330
+ stroke-linecap="round"
17331
+ />
17332
+ }
17333
+ </svg>
17334
+ @for (dot of lineDots(); track dot.id) {
17335
+ <span
17336
+ class="absolute size-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 bg-background"
17337
+ [style.left.%]="dot.x"
17338
+ [style.top.%]="dot.y"
17339
+ [style.border-color]="dot.color"
17340
+ ></span>
16994
17341
  }
16995
17342
  </div>
17343
+ @if (labels().length > 0) {
17344
+ <div class="mt-1 flex justify-between text-xs text-muted-foreground">
17345
+ @for (tick of labels(); track $index) {
17346
+ <span>{{ tick }}</span>
17347
+ }
17348
+ </div>
17349
+ }
16996
17350
  }
16997
17351
  `, isInline: true });
16998
17352
  }
@@ -17002,51 +17356,177 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
17002
17356
  selector: 'bui-chart',
17003
17357
  host: { 'data-slot': 'chart', '[class]': 'computedClass()' },
17004
17358
  template: `
17005
- <svg
17006
- [attr.viewBox]="'0 0 ' + width + ' ' + height()"
17007
- [attr.height]="height()"
17008
- class="w-full"
17009
- role="img"
17010
- [attr.aria-label]="label()"
17011
- preserveAspectRatio="none"
17012
- >
17013
- @if (type() === 'bar') {
17014
- @for (bar of bars(); track bar.id) {
17015
- <rect
17016
- [attr.x]="bar.x"
17017
- [attr.y]="bar.y"
17018
- [attr.width]="bar.w"
17019
- [attr.height]="bar.h"
17020
- [attr.fill]="bar.color"
17021
- rx="3"
17359
+ @if (type() === 'donut') {
17360
+ <div class="relative mx-auto aspect-square" [style.height.px]="height()">
17361
+ <svg
17362
+ viewBox="0 0 100 100"
17363
+ class="size-full -rotate-90"
17364
+ role="img"
17365
+ [attr.aria-label]="label()"
17366
+ >
17367
+ <circle
17368
+ cx="50"
17369
+ cy="50"
17370
+ r="40"
17371
+ fill="none"
17372
+ class="stroke-muted"
17373
+ [attr.stroke-width]="thickness()"
17022
17374
  />
17023
- }
17024
- } @else {
17025
- @for (path of paths(); track path.id) {
17026
- @if (type() === 'area') {
17027
- <path [attr.d]="path.area" [attr.fill]="path.color" fill-opacity="0.12" />
17375
+ @for (seg of donutSegments(); track seg.id) {
17376
+ <circle
17377
+ cx="50"
17378
+ cy="50"
17379
+ r="40"
17380
+ fill="none"
17381
+ stroke-linecap="round"
17382
+ [attr.stroke]="seg.color"
17383
+ [attr.stroke-width]="thickness()"
17384
+ [attr.stroke-dasharray]="seg.dash + ' ' + donutC"
17385
+ [attr.stroke-dashoffset]="seg.offset"
17386
+ />
17028
17387
  }
17388
+ </svg>
17389
+ <div class="absolute inset-0 flex flex-col items-center justify-center leading-none">
17390
+ <ng-content />
17391
+ </div>
17392
+ </div>
17393
+ } @else if (type() === 'gauge') {
17394
+ <div class="relative mx-auto w-full" [style.maxWidth.px]="height() * 1.9">
17395
+ <svg viewBox="0 0 100 56" class="w-full" role="img" [attr.aria-label]="label()">
17029
17396
  <path
17030
- [attr.d]="path.line"
17397
+ d="M10,50 A40,40 0 0 1 90,50"
17031
17398
  fill="none"
17032
- [attr.stroke]="path.color"
17033
- stroke-width="2"
17034
- stroke-linejoin="round"
17399
+ class="stroke-muted"
17035
17400
  stroke-linecap="round"
17401
+ [attr.stroke-width]="thickness()"
17036
17402
  />
17403
+ <path
17404
+ d="M10,50 A40,40 0 0 1 90,50"
17405
+ fill="none"
17406
+ stroke-linecap="round"
17407
+ [attr.stroke]="gauge().color"
17408
+ [attr.stroke-width]="thickness()"
17409
+ [attr.stroke-dasharray]="gauge().dash + ' ' + gauge().gap"
17410
+ />
17411
+ </svg>
17412
+ <div class="absolute inset-x-0 bottom-0 flex flex-col items-center leading-none">
17413
+ <ng-content />
17414
+ </div>
17415
+ </div>
17416
+ } @else if (type() === 'bar' && grouped()) {
17417
+ <div
17418
+ class="flex items-end gap-2"
17419
+ [style.height.px]="height()"
17420
+ role="img"
17421
+ [attr.aria-label]="label()"
17422
+ >
17423
+ @for (group of barGroups(); track group.id) {
17424
+ <div class="flex h-full flex-1 flex-col items-center justify-end gap-2">
17425
+ <div class="flex w-full flex-1 items-end justify-center gap-1">
17426
+ @for (sub of group.bars; track sub.id) {
17427
+ <div
17428
+ class="min-h-1 w-2 max-w-3 rounded-t-[4px] rounded-b-[2px] transition-[height]"
17429
+ [style.height.%]="sub.pct"
17430
+ [style.background]="sub.fill"
17431
+ ></div>
17432
+ }
17433
+ </div>
17434
+ @if (group.label) {
17435
+ <span class="text-xs text-muted-foreground">{{ group.label }}</span>
17436
+ }
17437
+ </div>
17037
17438
  }
17038
- }
17039
- </svg>
17040
- @if (labels().length > 0) {
17041
- <div class="mt-1 flex justify-between text-xs text-muted-foreground">
17042
- @for (tick of labels(); track $index) {
17043
- <span>{{ tick }}</span>
17439
+ </div>
17440
+ } @else if (type() === 'bar') {
17441
+ <div
17442
+ class="flex items-end gap-1"
17443
+ [style.height.px]="height()"
17444
+ role="img"
17445
+ [attr.aria-label]="label()"
17446
+ >
17447
+ @for (bar of barColumns(); track bar.id) {
17448
+ <div class="flex h-full flex-1 flex-col items-center justify-end gap-2">
17449
+ <div class="relative flex w-full flex-1 items-end justify-center">
17450
+ @if (track()) {
17451
+ <div class="absolute inset-y-0 w-[70%] max-w-9 rounded-[6px] bg-muted"></div>
17452
+ }
17453
+ <div
17454
+ class="relative flex min-h-1 w-[70%] max-w-9 flex-col-reverse overflow-hidden rounded-t-[10px] rounded-b-[4px] transition-[height]"
17455
+ [style.height.%]="bar.pct"
17456
+ >
17457
+ @for (seg of bar.segments; track seg.id) {
17458
+ <div [style.height.%]="seg.share" [style.background]="seg.fill"></div>
17459
+ }
17460
+ @if (bar.value) {
17461
+ <span
17462
+ class="absolute -top-6 left-1/2 -translate-x-1/2 text-sm font-semibold whitespace-nowrap text-foreground"
17463
+ >{{ bar.value }}</span
17464
+ >
17465
+ }
17466
+ </div>
17467
+ </div>
17468
+ @if (bar.label) {
17469
+ <span class="text-xs text-muted-foreground">{{ bar.label }}</span>
17470
+ }
17471
+ </div>
17472
+ }
17473
+ </div>
17474
+ } @else {
17475
+ <div class="relative w-full" [style.height.px]="height()">
17476
+ <svg
17477
+ [attr.viewBox]="'0 0 ' + width + ' ' + height()"
17478
+ [attr.height]="height()"
17479
+ class="w-full"
17480
+ role="img"
17481
+ [attr.aria-label]="label()"
17482
+ preserveAspectRatio="none"
17483
+ >
17484
+ @for (x of gridLines(); track x) {
17485
+ <line
17486
+ [attr.x1]="x"
17487
+ y1="2"
17488
+ [attr.x2]="x"
17489
+ [attr.y2]="height() - 2"
17490
+ class="stroke-border"
17491
+ stroke-width="1"
17492
+ stroke-dasharray="2 3"
17493
+ vector-effect="non-scaling-stroke"
17494
+ />
17495
+ }
17496
+ @for (path of paths(); track path.id) {
17497
+ @if (type() === 'area') {
17498
+ <path [attr.d]="path.area" [attr.fill]="path.color" fill-opacity="0.12" />
17499
+ }
17500
+ <path
17501
+ [attr.d]="path.line"
17502
+ fill="none"
17503
+ [attr.stroke]="path.color"
17504
+ stroke-width="2"
17505
+ stroke-linejoin="round"
17506
+ stroke-linecap="round"
17507
+ />
17508
+ }
17509
+ </svg>
17510
+ @for (dot of lineDots(); track dot.id) {
17511
+ <span
17512
+ class="absolute size-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 bg-background"
17513
+ [style.left.%]="dot.x"
17514
+ [style.top.%]="dot.y"
17515
+ [style.border-color]="dot.color"
17516
+ ></span>
17044
17517
  }
17045
17518
  </div>
17519
+ @if (labels().length > 0) {
17520
+ <div class="mt-1 flex justify-between text-xs text-muted-foreground">
17521
+ @for (tick of labels(); track $index) {
17522
+ <span>{{ tick }}</span>
17523
+ }
17524
+ </div>
17525
+ }
17046
17526
  }
17047
17527
  `,
17048
17528
  }]
17049
- }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
17529
+ }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], thickness: [{ type: i0.Input, args: [{ isSignal: true, alias: "thickness", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], barLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "barLabels", required: false }] }], stacked: [{ type: i0.Input, args: [{ isSignal: true, alias: "stacked", required: false }] }], grouped: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouped", required: false }] }], barColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "barColors", required: false }] }], track: [{ type: i0.Input, args: [{ isSignal: true, alias: "track", required: false }] }], dots: [{ type: i0.Input, args: [{ isSignal: true, alias: "dots", required: false }] }], gridX: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridX", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
17050
17530
 
17051
17531
  // eslint-disable-next-line @typescript-eslint/no-empty-function
17052
17532
  const noop$2 = () => { };
@@ -19544,9 +20024,103 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
19544
20024
  }]
19545
20025
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], formattingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "formattingLabel", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], editor: [{ type: i0.ViewChild, args: ['editor', { isSignal: true }] }] } });
19546
20026
 
19547
- /** A collapsible vertical navigation sidebar. Toggle width with the `open` model. */
20027
+ /**
20028
+ * Shared state for an inset sidebar layout, owned by {@link BuiSidebarProvider} and
20029
+ * injected by the sidebar, trigger and inset. Tracks the desktop expanded/rail state,
20030
+ * the mobile off-canvas drawer, and a `matchMedia` breakpoint (SSR-safe).
20031
+ */
20032
+ class BuiSidebarState {
20033
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
20034
+ /** Desktop: whether the sidebar is expanded (vs. an icon rail / hidden). */
20035
+ open = signal(true, /* @ts-ignore */
20036
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
20037
+ /** Mobile: whether the off-canvas drawer is open. */
20038
+ mobileOpen = signal(false, /* @ts-ignore */
20039
+ ...(ngDevMode ? [{ debugName: "mobileOpen" }] : /* istanbul ignore next */ []));
20040
+ /** Whether the viewport is below the `md` breakpoint. */
20041
+ isMobile = signal(false, /* @ts-ignore */
20042
+ ...(ngDevMode ? [{ debugName: "isMobile" }] : /* istanbul ignore next */ []));
20043
+ constructor() {
20044
+ if (!this.isBrowser || typeof globalThis.matchMedia !== 'function') {
20045
+ return;
20046
+ }
20047
+ const mql = globalThis.matchMedia('(max-width: 767px)');
20048
+ this.isMobile.set(mql.matches);
20049
+ mql.addEventListener('change', (event) => {
20050
+ this.isMobile.set(event.matches);
20051
+ });
20052
+ }
20053
+ /** Toggle the drawer on mobile, otherwise the desktop expanded state. */
20054
+ toggle() {
20055
+ if (this.isMobile()) {
20056
+ this.mobileOpen.update((value) => !value);
20057
+ }
20058
+ else {
20059
+ this.open.update((value) => !value);
20060
+ }
20061
+ }
20062
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
20063
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarState });
20064
+ }
20065
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarState, decorators: [{
20066
+ type: Injectable
20067
+ }], ctorParameters: () => [] });
20068
+ /**
20069
+ * Layout wrapper for an inset sidebar shell. Provides {@link BuiSidebarState} to a
20070
+ * {@link BuiSidebar} + {@link BuiSidebarInset}, lays them out in a row, and renders the
20071
+ * mobile backdrop. Wrap a dashboard's sidebar and main content in this provider.
20072
+ */
20073
+ class BuiSidebarProvider {
20074
+ /** Shared sidebar state; exposed (via `exportAs="buiSidebar"`) for template access. */
20075
+ state = inject(BuiSidebarState);
20076
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
20077
+ computedClass = computed(() => cn('flex min-h-svh w-full', this.userClass()), /* @ts-ignore */
20078
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
20079
+ /** Close the mobile drawer (e.g. after a nav item is chosen). */
20080
+ closeMobile() {
20081
+ this.state.mobileOpen.set(false);
20082
+ }
20083
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarProvider, deps: [], target: i0.ɵɵFactoryTarget.Component });
20084
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BuiSidebarProvider, isStandalone: true, selector: "bui-sidebar-provider", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "sidebar-provider" }, properties: { "class": "computedClass()" } }, providers: [BuiSidebarState], exportAs: ["buiSidebar"], ngImport: i0, template: `
20085
+ <ng-content />
20086
+ @if (state.isMobile() && state.mobileOpen()) {
20087
+ <button
20088
+ type="button"
20089
+ aria-label="Close sidebar"
20090
+ class="fixed inset-0 z-40 bg-black/50 md:hidden"
20091
+ (click)="state.mobileOpen.set(false)"
20092
+ ></button>
20093
+ }
20094
+ `, isInline: true });
20095
+ }
20096
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarProvider, decorators: [{
20097
+ type: Component,
20098
+ args: [{
20099
+ selector: 'bui-sidebar-provider',
20100
+ exportAs: 'buiSidebar',
20101
+ providers: [BuiSidebarState],
20102
+ host: { 'data-slot': 'sidebar-provider', '[class]': 'computedClass()' },
20103
+ template: `
20104
+ <ng-content />
20105
+ @if (state.isMobile() && state.mobileOpen()) {
20106
+ <button
20107
+ type="button"
20108
+ aria-label="Close sidebar"
20109
+ class="fixed inset-0 z-40 bg-black/50 md:hidden"
20110
+ (click)="state.mobileOpen.set(false)"
20111
+ ></button>
20112
+ }
20113
+ `,
20114
+ }]
20115
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
20116
+ /**
20117
+ * A collapsible vertical navigation sidebar. Standalone it toggles via the `open` model;
20118
+ * inside a {@link BuiSidebarProvider} it reads shared state and becomes a mobile off-canvas
20119
+ * drawer + desktop icon-rail automatically.
20120
+ */
19548
20121
  class BuiSidebar {
19549
- /** Whether the sidebar is expanded. Two-way bindable with `[(open)]`. */
20122
+ state = inject(BuiSidebarState, { optional: true });
20123
+ /** Whether the sidebar is expanded. Two-way bindable with `[(open)]` (standalone use). */
19550
20124
  open = model(true, /* @ts-ignore */
19551
20125
  ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
19552
20126
  /** Edge the sidebar is docked to, which sets its border side. */
@@ -19556,12 +20130,30 @@ class BuiSidebar {
19556
20130
  collapsible = input('offcanvas', /* @ts-ignore */
19557
20131
  ...(ngDevMode ? [{ debugName: "collapsible" }] : /* istanbul ignore next */ []));
19558
20132
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
20133
+ /** Effective expanded state — provider-driven when present, else the local `open` model. */
20134
+ expanded = computed(() => this.state?.open() ?? this.open(), /* @ts-ignore */
20135
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
19559
20136
  collapsedWidth = computed(() => this.collapsible() === 'icon' ? 'w-14' : 'w-0 border-0 p-0', /* @ts-ignore */
19560
20137
  ...(ngDevMode ? [{ debugName: "collapsedWidth" }] : /* istanbul ignore next */ []));
19561
- computedClass = computed(() => cn('flex h-full flex-col gap-2 overflow-hidden bg-card p-2 text-card-foreground transition-[width] duration-200', this.side() === 'left' ? 'border-r' : 'border-l', this.open() ? 'w-64' : this.collapsedWidth(), this.userClass()), /* @ts-ignore */
20138
+ computedClass = computed(() => {
20139
+ const base = 'group/sidebar flex flex-col gap-2 overflow-hidden bg-card p-2 text-card-foreground transition-[width,transform] duration-200';
20140
+ const border = this.side() === 'left' ? 'border-r' : 'border-l';
20141
+ if (this.state) {
20142
+ // Inset shell mode: mobile off-canvas drawer, desktop sticky expand/icon-rail.
20143
+ // All Tailwind classes are literals so the JIT scanner can see them.
20144
+ const dock = this.side() === 'left'
20145
+ ? 'left-0 -translate-x-full md:translate-x-0'
20146
+ : 'right-0 translate-x-full md:translate-x-0';
20147
+ const drawer = this.state.mobileOpen() ? 'max-md:translate-x-0' : '';
20148
+ const collapsedDesktop = this.collapsible() === 'icon' ? 'md:w-14' : 'md:w-0 md:border-0 md:p-0';
20149
+ const desktop = this.state.open() ? 'md:w-64' : collapsedDesktop;
20150
+ return cn(base, border, 'fixed inset-y-0 z-50 h-svh w-64 md:sticky md:top-0 md:z-auto md:h-svh', dock, drawer, desktop, this.userClass());
20151
+ }
20152
+ return cn(base, border, 'h-full', this.expanded() ? 'w-64' : this.collapsedWidth(), this.userClass());
20153
+ }, /* @ts-ignore */
19562
20154
  ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
19563
20155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
19564
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: BuiSidebar, isStandalone: true, selector: "bui-sidebar", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { attributes: { "data-slot": "sidebar" }, properties: { "attr.data-state": "open() ? 'expanded' : 'collapsed'", "attr.data-side": "side()", "class": "computedClass()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
20156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: BuiSidebar, isStandalone: true, selector: "bui-sidebar", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { attributes: { "data-slot": "sidebar" }, properties: { "attr.data-state": "expanded() ? 'expanded' : 'collapsed'", "attr.data-side": "side()", "class": "computedClass()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
19565
20157
  }
19566
20158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebar, decorators: [{
19567
20159
  type: Component,
@@ -19569,13 +20161,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
19569
20161
  selector: 'bui-sidebar',
19570
20162
  host: {
19571
20163
  'data-slot': 'sidebar',
19572
- '[attr.data-state]': "open() ? 'expanded' : 'collapsed'",
20164
+ '[attr.data-state]': "expanded() ? 'expanded' : 'collapsed'",
19573
20165
  '[attr.data-side]': 'side()',
19574
20166
  '[class]': 'computedClass()',
19575
20167
  },
19576
20168
  template: `<ng-content />`,
19577
20169
  }]
19578
20170
  }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
20171
+ /** The main content panel that sits beside a {@link BuiSidebar} inside a {@link BuiSidebarProvider}. */
20172
+ class BuiSidebarInset {
20173
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
20174
+ computedClass = computed(() => cn('flex min-w-0 flex-1 flex-col', this.userClass()), /* @ts-ignore */
20175
+ ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
20176
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarInset, deps: [], target: i0.ɵɵFactoryTarget.Directive });
20177
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: BuiSidebarInset, isStandalone: true, selector: "[buiSidebarInset]", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "sidebar-inset" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
20178
+ }
20179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarInset, decorators: [{
20180
+ type: Directive,
20181
+ args: [{
20182
+ selector: '[buiSidebarInset]',
20183
+ host: { 'data-slot': 'sidebar-inset', '[class]': 'computedClass()' },
20184
+ }]
20185
+ }], propDecorators: { userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
20186
+ /** A button that toggles the sidebar (desktop rail / mobile drawer) via {@link BuiSidebarState}. */
20187
+ class BuiSidebarTrigger {
20188
+ state = inject(BuiSidebarState, { optional: true });
20189
+ toggle() {
20190
+ this.state?.toggle();
20191
+ }
20192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
20193
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: BuiSidebarTrigger, isStandalone: true, selector: "[buiSidebarTrigger]", host: { attributes: { "data-slot": "sidebar-trigger", "type": "button" }, listeners: { "click": "toggle()" } }, ngImport: i0 });
20194
+ }
20195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BuiSidebarTrigger, decorators: [{
20196
+ type: Directive,
20197
+ args: [{
20198
+ selector: '[buiSidebarTrigger]',
20199
+ host: { 'data-slot': 'sidebar-trigger', type: 'button', '(click)': 'toggle()' },
20200
+ }]
20201
+ }] });
19579
20202
  /** A styled, accessible item button for a {@link BuiSidebar} menu. */
19580
20203
  class BuiSidebarMenuButton {
19581
20204
  /** Whether this item is the current page; sets active styling and `aria-current`. */
@@ -20296,5 +20919,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
20296
20919
  * Generated bundle index. Do not edit.
20297
20920
  */
20298
20921
 
20299
- export { BUI_DEFAULT_LABELS, BUI_LABELS, BuiAccent, BuiAccordion, BuiAccordionContent, BuiAccordionItem, BuiAccordionTrigger, BuiAddToCart, BuiAlert, BuiAlertDescription, BuiAlertDialogAction, BuiAlertDialogCancel, BuiAlertDialogContent, BuiAlertDialogDescription, BuiAlertDialogFooter, BuiAlertDialogHeader, BuiAlertDialogTitle, BuiAlertTitle, BuiAnimatedBeam, BuiAspectRatio, BuiAudioPlayer, BuiAurora, BuiAutocomplete, BuiAutosizeTextarea, BuiAvatar, BuiAvatarGroup, BuiBackToTop, BuiBadge, BuiBanner, BuiBentoGrid, BuiBentoItem, BuiBorderBeam, BuiBottomNavigation, BuiBottomNavigationItem, BuiBreadcrumb, BuiBreadcrumbEllipsis, BuiBreadcrumbItem, BuiBreadcrumbLink, BuiBreadcrumbList, BuiBreadcrumbPage, BuiBreadcrumbSeparator, BuiButton, BuiButtonGroup, BuiButtonGroupText, BuiCalendar, BuiCard, BuiCardAction, BuiCardContent, BuiCardDescription, BuiCardFooter, BuiCardHeader, BuiCardTitle, BuiCarousel, BuiChart, BuiChat, BuiChatMessage, BuiCheckbox, BuiCitation, BuiCodeBlock, BuiCollapsible, BuiCollapsibleContent, BuiCollapsibleTrigger, BuiColorPicker, BuiCombobox, BuiCommand, BuiComparisonSlider, BuiComparisonTable, BuiConfetti, BuiContainer, BuiContextMenu, BuiCookieConsent, BuiCopyButton, BuiCountdown, BuiDataTable, BuiDatePicker, BuiDatetimePicker, BuiDescriptionItem, BuiDescriptionList, BuiDialogContent, BuiDialogDescription, BuiDialogFooter, BuiDialogHeader, BuiDialogTitle, BuiDiffViewer, BuiDock, BuiDockItem, BuiDotPattern, BuiDrawer, BuiDropdownMenu, BuiDropdownMenuItem, BuiDropdownMenuLabel, BuiDropdownMenuSeparator, BuiEditable, BuiEmpty, BuiEmptyContent, BuiEmptyDescription, BuiEmptyHeader, BuiEmptyMedia, BuiEmptyTitle, BuiField, BuiFieldContent, BuiFieldDescription, BuiFieldError, BuiFieldGroup, BuiFieldLabel, BuiFieldLegend, BuiFieldSeparator, BuiFieldSet, BuiFieldTitle, BuiFileUpload, BuiFlipCard, BuiGallery, BuiGantt, BuiGradientText, BuiGridPattern, BuiHeatmap, BuiHoverCard, BuiHoverCardContent, BuiImage, BuiInfiniteScroll, BuiInput, BuiInputGroup, BuiInputGroupAddon, BuiInputGroupButton, BuiInputGroupInput, BuiInputGroupText, BuiInputMask, BuiInputOtp, BuiItem, BuiItemActions, BuiItemContent, BuiItemDescription, BuiItemGroup, BuiItemMedia, BuiItemTitle, BuiJsonViewer, BuiJsonViewerNode, BuiKanban, BuiKbd, BuiKbdGroup, BuiKnob, BuiLabel, BuiLink, BuiLoadingOverlay, BuiMap, BuiMarkdownEditor, BuiMarquee, BuiMasonry, BuiMentionInput, BuiMenubar, BuiMenubarTrigger, BuiMeteors, BuiMeter, BuiMiniCart, BuiNavigationMenu, BuiNotificationCenter, BuiNumberInput, BuiNumberTicker, BuiOnboardingTour, BuiOrgChart, BuiOrgChartNode, BuiPageHeader, BuiPagination, BuiPaginationContent, BuiPaginationEllipsis, BuiPaginationItem, BuiPaginationLink, BuiParallax, BuiPasswordStrength, BuiPhoneInput, BuiPopover, BuiPopoverContent, BuiPresence, BuiPrice, BuiProductCard, BuiProgress, BuiPromptInput, BuiQrCode, BuiQuantitySelector, BuiQuote, BuiRadioGroup, BuiRadioGroupItem, BuiRating, BuiReasoning, BuiRepeater, BuiResizableHandle, BuiResizablePanel, BuiResizablePanelGroup, BuiRichTextEditor, BuiScheduler, BuiScrollArea, BuiScrollspy, BuiSegmentedControl, BuiSelect, BuiSeparator, BuiSheet, BuiSidebar, BuiSidebarMenuButton, BuiSignaturePad, BuiSkeleton, BuiSlider, BuiSonner, BuiSparkline, BuiSpeedDial, BuiSpinner, BuiSpotlightCard, BuiStack, BuiStat, BuiStepper, BuiStepperItem, BuiStreamingText, BuiSwitch, BuiTabList, BuiTabPanel, BuiTabTrigger, BuiTable, BuiTableBody, BuiTableCaption, BuiTableCell, BuiTableContainer, BuiTableFooter, BuiTableHead, BuiTableHeader, BuiTableRow, BuiTabs, BuiTagsInput, BuiTerminal, BuiTextReveal, BuiTextarea, BuiThemeCustomizer, BuiTiltCard, BuiTimeField, BuiTimeline, BuiTimelineItem, BuiToaster, BuiToggle, BuiToggleGroup, BuiToggleGroupItem, BuiToolCall, BuiTooltip, BuiTooltipContent, BuiTopProgress, BuiTree, BuiTreeNode, BuiTreeTable, BuiTypewriter, BuiTypography, BuiVariantSelector, BuiVideo, BuiVisuallyHidden, THEME_TOKENS, ThemeStore, buiLabel, buttonVariants, cn, encodeQr, provideBuiLabels, toggleVariants };
20922
+ export { BUI_DEFAULT_LABELS, BUI_LABELS, BuiAccent, BuiAccordion, BuiAccordionContent, BuiAccordionItem, BuiAccordionTrigger, BuiAddToCart, BuiAlert, BuiAlertDescription, BuiAlertDialogAction, BuiAlertDialogCancel, BuiAlertDialogContent, BuiAlertDialogDescription, BuiAlertDialogFooter, BuiAlertDialogHeader, BuiAlertDialogTitle, BuiAlertTitle, BuiAnimatedBeam, BuiAspectRatio, BuiAudioPlayer, BuiAurora, BuiAutocomplete, BuiAutosizeTextarea, BuiAvatar, BuiAvatarGroup, BuiBackToTop, BuiBadge, BuiBanner, BuiBentoGrid, BuiBentoItem, BuiBorderBeam, BuiBottomNavigation, BuiBottomNavigationItem, BuiBreadcrumb, BuiBreadcrumbEllipsis, BuiBreadcrumbItem, BuiBreadcrumbLink, BuiBreadcrumbList, BuiBreadcrumbPage, BuiBreadcrumbSeparator, BuiButton, BuiButtonGroup, BuiButtonGroupText, BuiCalendar, BuiCard, BuiCardAction, BuiCardContent, BuiCardDescription, BuiCardFooter, BuiCardHeader, BuiCardTitle, BuiCarousel, BuiChart, BuiChat, BuiChatMessage, BuiCheckbox, BuiCitation, BuiCodeBlock, BuiCollapsible, BuiCollapsibleContent, BuiCollapsibleTrigger, BuiColorPicker, BuiCombobox, BuiCommand, BuiComparisonSlider, BuiComparisonTable, BuiConfetti, BuiContainer, BuiContextMenu, BuiCookieConsent, BuiCopyButton, BuiCountdown, BuiDataTable, BuiDatePicker, BuiDatetimePicker, BuiDescriptionItem, BuiDescriptionList, BuiDialogContent, BuiDialogDescription, BuiDialogFooter, BuiDialogHeader, BuiDialogTitle, BuiDiffViewer, BuiDock, BuiDockItem, BuiDotPattern, BuiDrawer, BuiDropdownMenu, BuiDropdownMenuItem, BuiDropdownMenuLabel, BuiDropdownMenuSeparator, BuiEditable, BuiEmpty, BuiEmptyContent, BuiEmptyDescription, BuiEmptyHeader, BuiEmptyMedia, BuiEmptyTitle, BuiField, BuiFieldContent, BuiFieldDescription, BuiFieldError, BuiFieldGroup, BuiFieldLabel, BuiFieldLegend, BuiFieldSeparator, BuiFieldSet, BuiFieldTitle, BuiFileUpload, BuiFlipCard, BuiGallery, BuiGantt, BuiGradientText, BuiGridPattern, BuiHeatmap, BuiHoverCard, BuiHoverCardContent, BuiIconTile, BuiImage, BuiInfiniteScroll, BuiInput, BuiInputGroup, BuiInputGroupAddon, BuiInputGroupButton, BuiInputGroupInput, BuiInputGroupText, BuiInputMask, BuiInputOtp, BuiItem, BuiItemActions, BuiItemContent, BuiItemDescription, BuiItemGroup, BuiItemMedia, BuiItemTitle, BuiJsonViewer, BuiJsonViewerNode, BuiKanban, BuiKbd, BuiKbdGroup, BuiKnob, BuiLabel, BuiLink, BuiLoadingOverlay, BuiMap, BuiMarkdownEditor, BuiMarquee, BuiMasonry, BuiMentionInput, BuiMenubar, BuiMenubarTrigger, BuiMeteors, BuiMeter, BuiMiniCart, BuiNavigationMenu, BuiNotificationCenter, BuiNumberInput, BuiNumberTicker, BuiOnboardingTour, BuiOrgChart, BuiOrgChartNode, BuiPageHeader, BuiPagination, BuiPaginationContent, BuiPaginationEllipsis, BuiPaginationItem, BuiPaginationLink, BuiParallax, BuiPasswordStrength, BuiPhoneInput, BuiPopover, BuiPopoverContent, BuiPresence, BuiPrice, BuiProductCard, BuiProgress, BuiPromptInput, BuiQrCode, BuiQuantitySelector, BuiQuote, BuiRadioGroup, BuiRadioGroupItem, BuiRating, BuiReasoning, BuiRepeater, BuiResizableHandle, BuiResizablePanel, BuiResizablePanelGroup, BuiRichTextEditor, BuiScheduler, BuiScrollArea, BuiScrollspy, BuiSegmentedControl, BuiSelect, BuiSeparator, BuiSheet, BuiSidebar, BuiSidebarInset, BuiSidebarMenuButton, BuiSidebarProvider, BuiSidebarState, BuiSidebarTrigger, BuiSignaturePad, BuiSkeleton, BuiSlider, BuiSonner, BuiSparkline, BuiSpeedDial, BuiSpinner, BuiSpotlightCard, BuiStack, BuiStat, BuiStepper, BuiStepperItem, BuiStreamingText, BuiSwitch, BuiTabList, BuiTabPanel, BuiTabTrigger, BuiTable, BuiTableBody, BuiTableCaption, BuiTableCell, BuiTableContainer, BuiTableFooter, BuiTableHead, BuiTableHeader, BuiTableRow, BuiTabs, BuiTagsInput, BuiTerminal, BuiTextReveal, BuiTextarea, BuiThemeCustomizer, BuiTiltCard, BuiTimeField, BuiTimeline, BuiTimelineItem, BuiToaster, BuiToggle, BuiToggleGroup, BuiToggleGroupItem, BuiToolCall, BuiTooltip, BuiTooltipContent, BuiTopProgress, BuiTree, BuiTreeNode, BuiTreeTable, BuiTypewriter, BuiTypography, BuiVariantSelector, BuiVideo, BuiVisuallyHidden, THEME_TOKENS, ThemeStore, buiLabel, buttonVariants, cn, encodeQr, provideBuiLabels, toggleVariants };
20300
20923
  //# sourceMappingURL=ng-blatui.mjs.map