@spartan-ng/cli 0.0.1-alpha.656 → 0.0.1-alpha.658

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/package.json +1 -1
  2. package/src/generators/base/generator.js +4 -6
  3. package/src/generators/base/generator.js.map +1 -1
  4. package/src/generators/healthcheck/generator.js +2 -0
  5. package/src/generators/healthcheck/generator.js.map +1 -1
  6. package/src/generators/healthcheck/healthchecks/hlm-form-field.d.ts +2 -0
  7. package/src/generators/healthcheck/healthchecks/hlm-form-field.js +35 -0
  8. package/src/generators/healthcheck/healthchecks/hlm-form-field.js.map +1 -0
  9. package/src/generators/migrate-form-field/compat.d.ts +2 -0
  10. package/src/generators/migrate-form-field/compat.js +6 -0
  11. package/src/generators/migrate-form-field/compat.js.map +1 -0
  12. package/src/generators/migrate-form-field/generator.d.ts +4 -0
  13. package/src/generators/migrate-form-field/generator.js +60 -0
  14. package/src/generators/migrate-form-field/generator.js.map +1 -0
  15. package/src/generators/migrate-form-field/schema.d.ts +4 -0
  16. package/src/generators/migrate-form-field/schema.json +19 -0
  17. package/src/generators/ui/libs/autocomplete/files/lib/hlm-autocomplete-input.ts.template +9 -4
  18. package/src/generators/ui/libs/badge/files/lib/hlm-badge.ts.template +7 -8
  19. package/src/generators/ui/libs/card/files/lib/hlm-card-action.ts.template +1 -1
  20. package/src/generators/ui/libs/card/files/lib/hlm-card-content.ts.template +1 -1
  21. package/src/generators/ui/libs/card/files/lib/hlm-card-description.ts.template +1 -1
  22. package/src/generators/ui/libs/card/files/lib/hlm-card-footer.ts.template +1 -4
  23. package/src/generators/ui/libs/card/files/lib/hlm-card-header.ts.template +1 -1
  24. package/src/generators/ui/libs/card/files/lib/hlm-card-title.ts.template +1 -1
  25. package/src/generators/ui/libs/card/files/lib/hlm-card.ts.template +1 -4
  26. package/src/generators/ui/libs/checkbox/files/lib/hlm-checkbox.ts.template +18 -2
  27. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-chip-input.ts.template +1 -1
  28. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-chips.ts.template +22 -7
  29. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-input.ts.template +8 -4
  30. package/src/generators/ui/libs/combobox/files/lib/hlm-combobox-trigger.ts.template +10 -1
  31. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-picker-multi.ts.template +38 -5
  32. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-picker.ts.template +36 -6
  33. package/src/generators/ui/libs/date-picker/files/lib/hlm-date-range-picker.ts.template +33 -4
  34. package/src/generators/ui/libs/field/files/index.ts.template +3 -3
  35. package/src/generators/ui/libs/field/files/lib/hlm-field-content.ts.template +1 -1
  36. package/src/generators/ui/libs/field/files/lib/hlm-field-description.ts.template +40 -2
  37. package/src/generators/ui/libs/field/files/lib/hlm-field-error.ts.template +89 -27
  38. package/src/generators/ui/libs/field/files/lib/hlm-field-group.ts.template +1 -1
  39. package/src/generators/ui/libs/field/files/lib/hlm-field-legend.ts.template +2 -2
  40. package/src/generators/ui/libs/field/files/lib/hlm-field.ts.template +9 -6
  41. package/src/generators/ui/libs/input/files/lib/hlm-input.ts.template +10 -69
  42. package/src/generators/ui/libs/input-group/files/lib/hlm-input-group.ts.template +15 -5
  43. package/src/generators/ui/libs/label/files/lib/hlm-label.ts.template +4 -1
  44. package/src/generators/ui/libs/native-select/files/lib/hlm-native-select.ts.template +32 -13
  45. package/src/generators/ui/libs/radio-group/files/lib/hlm-radio-group.ts.template +18 -2
  46. package/src/generators/ui/libs/radio-group/files/lib/hlm-radio.ts.template +16 -1
  47. package/src/generators/ui/libs/select/files/lib/hlm-select-trigger.ts.template +5 -2
  48. package/src/generators/ui/libs/slider/files/lib/hlm-slider.ts.template +3 -4
  49. package/src/generators/ui/libs/textarea/files/lib/hlm-textarea.ts.template +10 -71
  50. package/src/generators/ui/primitive-deps.js +0 -1
  51. package/src/generators/ui/primitive-deps.js.map +1 -1
  52. package/src/generators/ui/primitives.d.ts +1 -1
  53. package/src/generators/ui/style-lyra.css +16 -16
  54. package/src/generators/ui/style-maia.css +16 -16
  55. package/src/generators/ui/style-mira.css +16 -16
  56. package/src/generators/ui/style-nova.css +16 -16
  57. package/src/generators/ui/style-vega.css +16 -16
  58. package/src/generators/ui/supported-ui-libraries.json +54 -56
  59. package/src/generators/ui/libs/form-field/files/index.ts.template +0 -9
  60. package/src/generators/ui/libs/form-field/files/lib/hlm-error.ts.template +0 -12
  61. package/src/generators/ui/libs/form-field/files/lib/hlm-form-field.ts.template +0 -39
  62. package/src/generators/ui/libs/form-field/files/lib/hlm-hint.ts.template +0 -12
  63. package/src/generators/ui/libs/form-field/generator.d.ts +0 -3
  64. package/src/generators/ui/libs/form-field/generator.js +0 -9
  65. package/src/generators/ui/libs/form-field/generator.js.map +0 -1
@@ -1,4 +1,5 @@
1
- import { Directive } from '@angular/core';
1
+ import { computed, contentChild, Directive, inject } from '@angular/core';
2
+ import { BrnFieldControl } from '@spartan-ng/brain/field';
2
3
  import { classes } from '<%- importAlias %>/utils';
3
4
 
4
5
  @Directive({
@@ -9,6 +10,15 @@ import { classes } from '<%- importAlias %>/utils';
9
10
  },
10
11
  })
11
12
  export class HlmInputGroup {
13
+ private readonly _fieldControl = inject(BrnFieldControl, { optional: true });
14
+ private readonly _fieldControlChild = contentChild(BrnFieldControl);
15
+
16
+ private readonly _spartanInvalid = computed(() => {
17
+ if (this._fieldControl) return this._fieldControl.spartanInvalid();
18
+
19
+ return this._fieldControlChild()?.spartanInvalid();
20
+ });
21
+
12
22
  constructor() {
13
23
  classes(() => [
14
24
  'group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',
@@ -18,10 +28,10 @@ export class HlmInputGroup {
18
28
  'has-[>[data-align=inline-end]]:[&>input]:pe-2',
19
29
  'has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3',
20
30
  'has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3',
21
- // Focus state.
22
- 'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',
23
- // Error state.
24
- 'has-[>.ng-invalid.ng-touched]:ring-destructive/20 has-[>.ng-invalid.ng-touched]:border-destructive dark:has-[>.ng-invalid.ng-touched]:ring-destructive/40',
31
+ 'has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',
32
+ this._spartanInvalid?.()
33
+ ? 'has-[>[data-matches-spartan-invalid=true]]:ring-destructive/20 has-[>[data-matches-spartan-invalid=true]]:border-destructive dark:has-[>[data-matches-spartan-invalid=true]]:ring-destructive/40'
34
+ : 'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50',
25
35
  ]);
26
36
  }
27
37
  }
@@ -7,9 +7,12 @@ import { classes } from '<%- importAlias %>/utils';
7
7
  hostDirectives: [
8
8
  {
9
9
  directive: BrnLabel,
10
- inputs: ['id'],
10
+ inputs: ['id', 'for'],
11
11
  },
12
12
  ],
13
+ host: {
14
+ 'data-slot': 'label',
15
+ },
13
16
  })
14
17
  export class HlmLabel {
15
18
  constructor() {
@@ -5,19 +5,20 @@ import {
5
5
  Component,
6
6
  computed,
7
7
  forwardRef,
8
+ inject,
8
9
  input,
9
10
  linkedSignal,
10
11
  model,
11
12
  output,
12
13
  } from '@angular/core';
13
- import { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
14
+ import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms';
14
15
  import { NgIcon, provideIcons } from '@ng-icons/core';
15
16
  import { lucideChevronDown } from '@ng-icons/lucide';
16
- import type { ChangeFn, TouchFn } from '@spartan-ng/brain/forms';
17
+ import { BrnFieldControl, provideBrnLabelable } from '@spartan-ng/brain/field';
18
+ import { type ChangeFn, type TouchFn } from '@spartan-ng/brain/forms';
17
19
  import { classes, hlm } from '<%- importAlias %>/utils';
18
20
  import type { ClassValue } from 'clsx';
19
21
 
20
- // TODO support BrnFormFieldControl
21
22
  export const HLM_NATIVE_SELECT_VALUE_ACCESSOR = {
22
23
  provide: NG_VALUE_ACCESSOR,
23
24
  useExisting: forwardRef(() => HlmNativeSelect),
@@ -27,8 +28,13 @@ export const HLM_NATIVE_SELECT_VALUE_ACCESSOR = {
27
28
  @Component({
28
29
  selector: 'hlm-native-select',
29
30
  imports: [NgIcon],
30
- providers: [HLM_NATIVE_SELECT_VALUE_ACCESSOR, provideIcons({ lucideChevronDown })],
31
+ providers: [
32
+ HLM_NATIVE_SELECT_VALUE_ACCESSOR,
33
+ provideIcons({ lucideChevronDown }),
34
+ provideBrnLabelable(HlmNativeSelect),
35
+ ],
31
36
  changeDetection: ChangeDetectionStrategy.OnPush,
37
+ hostDirectives: [BrnFieldControl],
32
38
  host: {
33
39
  'data-slot': 'native-select-wrapper',
34
40
  '[attr.data-size]': 'size()',
@@ -36,10 +42,14 @@ export const HLM_NATIVE_SELECT_VALUE_ACCESSOR = {
36
42
  template: `
37
43
  <select
38
44
  data-slot="native-select"
39
- [id]="_selectId()"
45
+ [id]="selectId()"
40
46
  [class]="_computedSelectClass()"
41
47
  [attr.data-size]="size()"
42
- [attr.aria-invalid]="ariaInvalid() ? 'true' : null"
48
+ [attr.aria-invalid]="_ariaInvalid() ? 'true' : null"
49
+ [attr.data-invalid]="_ariaInvalid() ? 'true' : null"
50
+ [attr.data-dirty]="_dirty?.() ? 'true' : null"
51
+ [attr.data-touched]="_touched?.() ? 'true' : null"
52
+ [attr.data-matches-spartan-invalid]="_spartanInvalid?.() ? 'true' : null"
43
53
  [value]="value()"
44
54
  [disabled]="_disabled()"
45
55
  (change)="_valueChanged($event)"
@@ -57,19 +67,18 @@ export const HLM_NATIVE_SELECT_VALUE_ACCESSOR = {
57
67
  `,
58
68
  })
59
69
  export class HlmNativeSelect implements ControlValueAccessor {
60
- private static _id = 0;
70
+ private readonly _fieldControl = inject(BrnFieldControl, { optional: true });
61
71
 
62
- public readonly selectId = input<string>('');
72
+ private static _id = 0;
63
73
 
64
- protected readonly _selectId = computed(() => this.selectId() || `hlm-native-select-${HlmNativeSelect._id++}`);
74
+ public readonly selectId = input<string>(`hlm-native-select-${HlmNativeSelect._id++}`);
65
75
 
66
76
  public readonly selectClass = input<ClassValue>('');
67
77
 
68
78
  protected readonly _computedSelectClass = computed(() =>
69
79
  hlm(
70
80
  'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent py-1 pr-8 pl-2.5 text-sm shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8',
71
- // TODO support BrnFormFieldControl
72
- 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 aria-invalid:ring-3',
81
+ 'data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 data-[matches-spartan-invalid=true]:ring-3',
73
82
  this.selectClass(),
74
83
  ),
75
84
  );
@@ -89,11 +98,14 @@ export class HlmNativeSelect implements ControlValueAccessor {
89
98
 
90
99
  protected readonly _disabled = linkedSignal(this.disabled);
91
100
 
92
- public readonly ariaInvalid = input<boolean, BooleanInput>(false, {
93
- transform: booleanAttribute,
101
+ /** Manual override for aria-invalid. When not set, auto-detects from the parent autocomplete error state. */
102
+ public readonly ariaInvalidOverride = input<boolean | undefined, BooleanInput>(undefined, {
103
+ transform: (v: BooleanInput) => (v === '' || v === undefined ? undefined : booleanAttribute(v)),
94
104
  alias: 'aria-invalid',
95
105
  });
96
106
 
107
+ protected readonly _ariaInvalid = computed(() => this.ariaInvalidOverride() ?? this._invalid?.());
108
+
97
109
  public readonly value = model<string | null>('');
98
110
 
99
111
  public readonly valueChange = output<string | null>();
@@ -101,6 +113,13 @@ export class HlmNativeSelect implements ControlValueAccessor {
101
113
  protected _onChange?: ChangeFn<string | null>;
102
114
  protected _onTouched?: TouchFn;
103
115
 
116
+ public readonly labelableId = this.selectId;
117
+
118
+ protected readonly _invalid = this._fieldControl?.invalid;
119
+ protected readonly _touched = this._fieldControl?.touched;
120
+ protected readonly _dirty = this._fieldControl?.dirty;
121
+ protected readonly _spartanInvalid = this._fieldControl?.spartanInvalid;
122
+
104
123
  constructor() {
105
124
  classes(() => 'group/native-select relative w-fit has-[select:disabled]:opacity-50');
106
125
  }
@@ -1,6 +1,8 @@
1
- import { Directive } from '@angular/core';
1
+ import { computed, Directive, inject, input } from '@angular/core';
2
+ import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
2
3
  import { BrnRadioGroup } from '@spartan-ng/brain/radio-group';
3
4
  import { classes } from '<%- importAlias %>/utils';
5
+ import type { ClassValue } from 'clsx';
4
6
 
5
7
  @Directive({
6
8
  selector: '[hlmRadioGroup],hlm-radio-group',
@@ -10,13 +12,27 @@ import { classes } from '<%- importAlias %>/utils';
10
12
  inputs: ['name', 'value', 'disabled', 'required'],
11
13
  outputs: ['valueChange'],
12
14
  },
15
+ BrnFieldControlDescribedBy,
13
16
  ],
14
17
  host: {
15
18
  'data-slot': 'radio-group',
19
+ '[attr.aria-invalid]': '_ariaInvalid() ? "true" : null',
20
+ '[attr.data-invalid]': '_ariaInvalid() ? "true" : null',
21
+ '[attr.data-dirty]': '_dirty() ? "true" : null',
22
+ '[attr.data-touched]': '_touched() ? "true" : null',
16
23
  },
17
24
  })
18
25
  export class HlmRadioGroup {
26
+ public readonly userClass = input<ClassValue>('', { alias: 'class' });
27
+ private readonly _brnRadioGroup = inject(BrnRadioGroup);
28
+ protected readonly _ariaInvalid = computed(() => this._brnRadioGroup.controlState?.()?.invalid);
29
+
30
+ protected readonly _touched = computed(() => this._brnRadioGroup.controlState?.()?.touched);
31
+ protected readonly _dirty = computed(() => this._brnRadioGroup.controlState?.()?.dirty);
32
+
33
+ protected readonly _errorState = computed(() => this._brnRadioGroup.controlState?.()?.spartanInvalid);
34
+
19
35
  constructor() {
20
- classes(() => 'grid gap-3');
36
+ classes(() => ['grid gap-3', this.userClass(), this._errorState() ? 'data-[invalid=true]:text-destructive' : '']);
21
37
  }
22
38
  }
@@ -14,7 +14,7 @@ import {
14
14
  PLATFORM_ID,
15
15
  Renderer2,
16
16
  } from '@angular/core';
17
- import { BrnRadio, type BrnRadioChange } from '@spartan-ng/brain/radio-group';
17
+ import { BrnRadio, BrnRadioGroup, type BrnRadioChange } from '@spartan-ng/brain/radio-group';
18
18
  import { hlm } from '<%- importAlias %>/utils';
19
19
  import type { ClassValue } from 'clsx';
20
20
 
@@ -37,6 +37,11 @@ import type { ClassValue } from 'clsx';
37
37
  [value]="value()"
38
38
  [required]="required()"
39
39
  [disabled]="disabled()"
40
+ [attr.aria-invalid]="_ariaInvalid() ? 'true' : null"
41
+ [attr.data-invalid]="_ariaInvalid() ? 'true' : null"
42
+ [attr.data-dirty]="_dirty() ? 'true' : null"
43
+ [attr.data-touched]="_touched() ? 'true' : null"
44
+ [attr.data-matches-spartan-invalid]="_groupSpartanInvalid() ? 'true' : null"
40
45
  [aria-label]="ariaLabel()"
41
46
  [aria-labelledby]="ariaLabelledby()"
42
47
  [aria-describedby]="ariaDescribedby()"
@@ -52,6 +57,15 @@ export class HlmRadio<T = unknown> {
52
57
  private readonly _renderer = inject(Renderer2);
53
58
  private readonly _elementRef = inject(ElementRef);
54
59
  private readonly _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
60
+ private readonly _radioGroup = inject(BrnRadioGroup, { optional: true });
61
+
62
+ protected readonly _ariaInvalid = computed(() => this._radioGroup?.controlState?.()?.invalid);
63
+
64
+ protected readonly _touched = computed(() => this._radioGroup?.controlState?.()?.touched);
65
+ protected readonly _dirty = computed(() => this._radioGroup?.controlState?.()?.dirty);
66
+ protected readonly _groupSpartanInvalid = computed(() => this._radioGroup?.controlState?.()?.spartanInvalid);
67
+
68
+ protected readonly _errorStateClass = computed(() => (this._groupSpartanInvalid() ? 'text-destructive' : ''));
55
69
 
56
70
  public readonly userClass = input<ClassValue>('', { alias: 'class' });
57
71
  protected readonly _computedClass = computed(() =>
@@ -59,6 +73,7 @@ export class HlmRadio<T = unknown> {
59
73
  'group relative flex items-center gap-x-3',
60
74
  'data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
61
75
  this.userClass(),
76
+ this._errorStateClass(),
62
77
  ),
63
78
  );
64
79
 
@@ -1,19 +1,21 @@
1
1
  import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
2
2
  import { NgIcon, provideIcons } from '@ng-icons/core';
3
3
  import { lucideChevronDown } from '@ng-icons/lucide';
4
+ import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
4
5
  import { BrnSelectTrigger, BrnSelectTriggerWrapper } from '@spartan-ng/brain/select';
5
6
  import { hlm } from '<%- importAlias %>/utils';
6
7
  import type { ClassValue } from 'clsx';
7
8
 
8
9
  @Component({
9
10
  selector: 'hlm-select-trigger',
10
- imports: [NgIcon, BrnSelectTrigger],
11
+ imports: [NgIcon, BrnSelectTrigger, BrnFieldControlDescribedBy],
11
12
  providers: [provideIcons({ lucideChevronDown })],
12
13
  changeDetection: ChangeDetectionStrategy.OnPush,
13
14
  hostDirectives: [BrnSelectTriggerWrapper],
14
15
  template: `
15
16
  <button
16
17
  brnSelectTrigger
18
+ brnFieldControlDescribedBy
17
19
  [id]="buttonId()"
18
20
  [class]="_computedClass()"
19
21
  [attr.data-size]="size()"
@@ -30,7 +32,8 @@ export class HlmSelectTrigger {
30
32
  public readonly userClass = input<ClassValue>('', { alias: 'class' });
31
33
  protected readonly _computedClass = computed(() =>
32
34
  hlm(
33
- "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 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:aria-invalid:border-destructive/50 flex w-full items-center justify-between gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0 [&_ng-icon:not([class*='text-'])]:text-base",
35
+ "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 flex w-full items-center justify-between gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0 [&_ng-icon:not([class*='text-'])]:text-base",
36
+ 'data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 data-[matches-spartan-invalid=true]:ring-3',
34
37
  this.userClass(),
35
38
  ),
36
39
  );
@@ -72,9 +72,8 @@ export class HlmSlider {
72
72
  protected readonly _slider = injectBrnSlider();
73
73
 
74
74
  constructor() {
75
- classes(
76
- () =>
77
- 'group flex w-full touch-none flex-col select-none data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-row data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
78
- );
75
+ classes(() => [
76
+ 'group flex w-full touch-none flex-col select-none data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-row data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
77
+ ]);
79
78
  }
80
79
  }
@@ -1,29 +1,15 @@
1
- import {
2
- computed,
3
- Directive,
4
- type DoCheck,
5
- effect,
6
- forwardRef,
7
- inject,
8
- Injector,
9
- input,
10
- linkedSignal,
11
- signal,
12
- untracked,
13
- } from '@angular/core';
14
- import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
15
- import { BrnFormFieldControl } from '@spartan-ng/brain/form-field';
16
- import { ErrorStateMatcher, ErrorStateTracker } from '@spartan-ng/brain/forms';
1
+ import { Directive, input } from '@angular/core';
2
+ import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
3
+ import { BrnTextarea } from '@spartan-ng/brain/textarea';
17
4
  import { classes } from '<%- importAlias %>/utils';
18
5
  import { cva, type VariantProps } from 'class-variance-authority';
19
- import { ClassValue } from 'clsx';
20
6
 
21
7
  export const textareaVariants = cva(
22
8
  'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 dark:bg-input/30 flex [field-sizing:content] min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
23
9
  {
24
10
  variants: {
25
11
  error: {
26
- auto: '[&.ng-invalid.ng-touched]:border-destructive [&.ng-invalid.ng-touched]:ring-destructive/20 dark:[&.ng-invalid.ng-touched]:ring-destructive/40',
12
+ auto: 'data-[matches-spartan-invalid=true]:border-destructive data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40',
27
13
  true: 'border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
28
14
  },
29
15
  },
@@ -36,65 +22,18 @@ type TextareaVariants = VariantProps<typeof textareaVariants>;
36
22
 
37
23
  @Directive({
38
24
  selector: '[hlmTextarea]',
39
- providers: [
40
- {
41
- provide: BrnFormFieldControl,
42
- useExisting: forwardRef(() => HlmTextarea),
43
- },
44
- ],
25
+ hostDirectives: [{ directive: BrnTextarea, inputs: ['id'] }, BrnFieldControlDescribedBy],
45
26
  host: {
46
27
  'data-slot': 'textarea',
47
28
  },
48
29
  })
49
- export class HlmTextarea implements BrnFormFieldControl, DoCheck {
50
- private readonly _injector = inject(Injector);
51
- private readonly _additionalClasses = signal<ClassValue>('');
52
-
53
- private readonly _errorStateTracker: ErrorStateTracker;
54
-
55
- private readonly _defaultErrorStateMatcher = inject(ErrorStateMatcher);
56
- private readonly _parentForm = inject(NgForm, { optional: true });
57
- private readonly _parentFormGroup = inject(FormGroupDirective, { optional: true });
58
-
30
+ export class HlmTextarea {
31
+ /** Controls the error visual state of the textarea.
32
+ * Defaults to 'auto', which infers the state from the associated form control.
33
+ */
59
34
  public readonly error = input<TextareaVariants['error']>('auto');
60
35
 
61
- protected readonly _state = linkedSignal(() => ({ error: this.error() }));
62
-
63
- public readonly ngControl: NgControl | null = this._injector.get(NgControl, null);
64
-
65
- public readonly errorState = computed(() => this._errorStateTracker.errorState());
66
-
67
36
  constructor() {
68
- classes(() => [textareaVariants({ error: this._state().error }), this._additionalClasses()]);
69
-
70
- this._errorStateTracker = new ErrorStateTracker(
71
- this._defaultErrorStateMatcher,
72
- this.ngControl,
73
- this._parentFormGroup,
74
- this._parentForm,
75
- );
76
-
77
- effect(() => {
78
- const error = this._errorStateTracker.errorState();
79
- untracked(() => {
80
- if (this.ngControl) {
81
- const shouldShowError = error && this.ngControl.invalid && (this.ngControl.touched || this.ngControl.dirty);
82
- this._errorStateTracker.errorState.set(shouldShowError ? true : false);
83
- this.setError(shouldShowError ? true : 'auto');
84
- }
85
- });
86
- });
87
- }
88
-
89
- ngDoCheck() {
90
- this._errorStateTracker.updateErrorState();
91
- }
92
-
93
- public setError(error: TextareaVariants['error']): void {
94
- this._state.set({ error });
95
- }
96
-
97
- public setClass(classes: string): void {
98
- this._additionalClasses.set(classes);
37
+ classes(() => textareaVariants({ error: this.error() }));
99
38
  }
100
39
  }
@@ -25,7 +25,6 @@ exports.primitiveDependencies = {
25
25
  'dropdown-menu': ['utils', 'icon'],
26
26
  empty: ['utils'],
27
27
  field: ['utils', 'label', 'separator'],
28
- 'form-field': ['utils'],
29
28
  'hover-card': ['utils'],
30
29
  icon: [],
31
30
  input: ['utils'],
@@ -1 +1 @@
1
- {"version":3,"file":"primitive-deps.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/ui/primitive-deps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"primitive-deps.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/generators/ui/primitive-deps.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- export type Primitive = 'accordion' | 'alert' | 'alert-dialog' | 'aspect-ratio' | 'autocomplete' | 'avatar' | 'badge' | 'breadcrumb' | 'button' | 'button-group' | 'calendar' | 'card' | 'carousel' | 'checkbox' | 'collapsible' | 'combobox' | 'command' | 'context-menu' | 'date-picker' | 'dialog' | 'dropdown-menu' | 'empty' | 'field' | 'form-field' | 'hover-card' | 'icon' | 'input' | 'input-group' | 'input-otp' | 'item' | 'kbd' | 'label' | 'menubar' | 'native-select' | 'navigation-menu' | 'pagination' | 'popover' | 'progress' | 'radio-group' | 'resizable' | 'scroll-area' | 'select' | 'separator' | 'sheet' | 'sidebar' | 'skeleton' | 'slider' | 'sonner' | 'spinner' | 'switch' | 'table' | 'tabs' | 'textarea' | 'toggle' | 'toggle-group' | 'tooltip' | 'typography' | 'utils';
1
+ export type Primitive = 'accordion' | 'alert' | 'alert-dialog' | 'aspect-ratio' | 'autocomplete' | 'avatar' | 'badge' | 'breadcrumb' | 'button' | 'button-group' | 'calendar' | 'card' | 'carousel' | 'checkbox' | 'collapsible' | 'combobox' | 'command' | 'context-menu' | 'date-picker' | 'dialog' | 'dropdown-menu' | 'empty' | 'field' | 'hover-card' | 'icon' | 'input' | 'input-group' | 'input-otp' | 'item' | 'kbd' | 'label' | 'menubar' | 'native-select' | 'navigation-menu' | 'pagination' | 'popover' | 'progress' | 'radio-group' | 'resizable' | 'scroll-area' | 'select' | 'separator' | 'sheet' | 'sidebar' | 'skeleton' | 'slider' | 'sonner' | 'spinner' | 'switch' | 'table' | 'tabs' | 'textarea' | 'toggle' | 'toggle-group' | 'tooltip' | 'typography' | 'utils';
@@ -89,7 +89,7 @@
89
89
 
90
90
  /* MARK: Badge */
91
91
  .spartan-badge {
92
- @apply h-5 gap-1 rounded-none border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&>svg]:size-3!;
92
+ @apply h-5 gap-1 rounded-none border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&>ng-icon]:text-[--spacing(3)];
93
93
  }
94
94
 
95
95
  .spartan-badge-variant-default {
@@ -143,7 +143,7 @@
143
143
 
144
144
  /* MARK: Button */
145
145
  .spartan-button {
146
- @apply 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:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)];
146
+ @apply focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 [&_ng-icon:not([class*='text-'])]:text-[--spacing(4)];
147
147
  }
148
148
 
149
149
  .spartan-button-variant-default {
@@ -260,7 +260,7 @@
260
260
 
261
261
  /* MARK: Checkbox */
262
262
  .spartan-checkbox {
263
- @apply border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1;
263
+ @apply border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary data-[matches-spartan-invalid=true]:aria-checked:border-primary data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1;
264
264
  }
265
265
 
266
266
  .spartan-checkbox-indicator {
@@ -313,7 +313,7 @@
313
313
  }
314
314
 
315
315
  .spartan-combobox-chips {
316
- @apply dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-8 flex-wrap items-center gap-1 rounded-none border bg-transparent bg-clip-padding px-2.5 py-1 text-xs transition-colors focus-within:ring-1 has-aria-invalid:ring-1 has-data-[slot=combobox-chip]:px-1;
316
+ @apply dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 flex min-h-8 flex-wrap items-center gap-1 rounded-none border bg-transparent bg-clip-padding px-2.5 py-1 text-xs transition-colors focus-within:ring-1 has-data-[slot=combobox-chip]:px-1 data-[matches-spartan-invalid=true]:ring-1;
317
317
  }
318
318
 
319
319
  .spartan-combobox-chip {
@@ -614,7 +614,7 @@
614
614
 
615
615
  /* MARK: Input */
616
616
  .spartan-input {
617
- @apply dark:bg-input/30 border-input 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:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-none border bg-transparent px-2.5 py-1 text-xs transition-colors file:h-6 file:text-xs file:font-medium focus-visible:ring-1 aria-invalid:ring-1 md:text-xs;
617
+ @apply dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-none border bg-transparent px-2.5 py-1 text-xs transition-colors file:h-6 file:text-xs file:font-medium focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 md:text-xs;
618
618
  }
619
619
 
620
620
  /* MARK: Input OTP */
@@ -623,11 +623,11 @@
623
623
  }
624
624
 
625
625
  .spartan-input-otp-group {
626
- @apply has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-none has-aria-invalid:ring-1;
626
+ @apply data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive rounded-none data-[matches-spartan-invalid=true]:ring-1;
627
627
  }
628
628
 
629
629
  .spartan-input-otp-slot {
630
- @apply dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-8 border-y border-r text-xs transition-all outline-none first:rounded-none first:border-l last:rounded-none data-[active=true]:ring-1;
630
+ @apply dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[active=true]:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive data-[active=true]:data-[matches-spartan-invalid=true]:border-destructive size-8 border-y border-r text-xs transition-all outline-none first:rounded-none first:border-l last:rounded-none data-[active=true]:ring-1;
631
631
  }
632
632
 
633
633
  .spartan-input-otp-caret-line {
@@ -833,7 +833,7 @@
833
833
 
834
834
  /* MARK: Native Select */
835
835
  .spartan-native-select {
836
- @apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 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:aria-invalid:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-none border bg-transparent py-1 ps-2.5 pe-8 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 data-[size=sm]:h-7 data-[size=sm]:rounded-none data-[size=sm]:py-0.5;
836
+ @apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-none border bg-transparent py-1 ps-2.5 pe-8 text-xs transition-colors select-none focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 data-[size=sm]:h-7 data-[size=sm]:rounded-none data-[size=sm]:py-0.5;
837
837
  }
838
838
 
839
839
  .spartan-native-select-icon {
@@ -905,7 +905,7 @@
905
905
  }
906
906
 
907
907
  .spartan-radio-group-item {
908
- @apply border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 dark:aria-invalid:border-destructive/50 flex size-4 rounded-full focus-visible:ring-3 aria-invalid:ring-3;
908
+ @apply border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary data-[matches-spartan-invalid=true]:aria-checked:border-primary data-[matches-spartan-invalid=true]:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 dark:data-[matches-spartan-invalid=true]:border-destructive/50 flex size-4 rounded-full focus-visible:ring-3 data-[matches-spartan-invalid=true]:ring-3;
909
909
  }
910
910
 
911
911
  .spartan-radio-group-indicator {
@@ -932,7 +932,7 @@
932
932
 
933
933
  /* MARK: Select */
934
934
  .spartan-select-trigger {
935
- @apply border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 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:aria-invalid:border-destructive/50 gap-1.5 rounded-none border bg-transparent py-2 ps-2.5 pe-2 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)];
935
+ @apply border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 gap-1.5 rounded-none border bg-transparent py-2 ps-2.5 pe-2 text-xs transition-colors select-none focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)];
936
936
  }
937
937
 
938
938
  .spartan-select-value {
@@ -1163,7 +1163,7 @@
1163
1163
 
1164
1164
  /* MARK: Switch */
1165
1165
  .spartan-switch {
1166
- @apply data-checked:bg-primary data-unchecked:bg-input 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:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent focus-visible:ring-1 aria-invalid:ring-1 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px];
1166
+ @apply data-checked:bg-primary data-unchecked:bg-input focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px];
1167
1167
  }
1168
1168
 
1169
1169
  .spartan-switch-thumb {
@@ -1226,12 +1226,12 @@
1226
1226
 
1227
1227
  /* MARK: Textarea */
1228
1228
  .spartan-textarea {
1229
- @apply 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 dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-none border bg-transparent px-2.5 py-2 text-xs transition-colors focus-visible:ring-1 aria-invalid:ring-1 md:text-xs;
1229
+ @apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-none border bg-transparent px-2.5 py-2 text-xs transition-colors focus-visible:ring-1 data-[matches-spartan-invalid=true]:ring-1 md:text-xs;
1230
1230
  }
1231
1231
 
1232
1232
  /* MARK: Toggle */
1233
1233
  .spartan-toggle {
1234
- @apply hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[state=on]:bg-muted gap-1 rounded-none text-xs font-medium transition-all [&_ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)];
1234
+ @apply hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive data-[state=on]:bg-muted gap-1 rounded-none text-xs font-medium transition-all [&_ng-icon:not([class*='text-'])]:text-[calc(var(--spacing)*4)];
1235
1235
  }
1236
1236
 
1237
1237
  .spartan-toggle-variant-default {
@@ -1282,7 +1282,7 @@
1282
1282
 
1283
1283
  /* MARK: Input Group */
1284
1284
  .spartan-input-group {
1285
- @apply border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-none border transition-colors in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot][aria-invalid=true]]:ring-1 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pe-1.5 has-[>[data-align=inline-start]]:[&>input]:ps-1.5;
1285
+ @apply border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][data-matches-spartan-invalid=true]]:ring-destructive/20 has-[[data-slot][data-matches-spartan-invalid=true]]:border-destructive dark:has-[[data-slot][data-matches-spartan-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-none border transition-colors in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot][data-matches-spartan-invalid=true]]:ring-1 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pe-1.5 has-[>[data-align=inline-start]]:[&>input]:ps-1.5;
1286
1286
  }
1287
1287
 
1288
1288
  .spartan-input-group-addon {
@@ -1326,10 +1326,10 @@
1326
1326
  }
1327
1327
 
1328
1328
  .spartan-input-group-input {
1329
- @apply rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent;
1329
+ @apply rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent data-[matches-spartan-invalid=true]:ring-0 dark:bg-transparent dark:disabled:bg-transparent;
1330
1330
  }
1331
1331
 
1332
1332
  .spartan-input-group-textarea {
1333
- @apply rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent;
1333
+ @apply rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent data-[matches-spartan-invalid=true]:ring-0 dark:bg-transparent dark:disabled:bg-transparent;
1334
1334
  }
1335
1335
  }