@spartan-ng/cli 0.0.1-alpha.715 → 0.0.1-alpha.716

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 (51) hide show
  1. package/package.json +1 -1
  2. package/src/generators/migrate-module-imports/import-map.js +1 -1
  3. package/src/generators/ui/libs/avatar/files/lib/hlm-avatar-fallback.ts.template +1 -1
  4. package/src/generators/ui/libs/avatar/files/lib/hlm-avatar-image.ts.template +1 -1
  5. package/src/generators/ui/libs/breadcrumb/files/index.ts.template +0 -5
  6. package/src/generators/ui/libs/calendar/files/lib/hlm-calendar-multi.ts.template +1 -0
  7. package/src/generators/ui/libs/calendar/files/lib/hlm-calendar-range.ts.template +1 -0
  8. package/src/generators/ui/libs/calendar/files/lib/hlm-calendar.ts.template +1 -0
  9. package/src/generators/ui/libs/command/files/lib/hlm-command-dialog.ts.template +2 -2
  10. package/src/generators/ui/libs/command/files/lib/hlm-command-empty.ts.template +1 -1
  11. package/src/generators/ui/libs/command/files/lib/hlm-command-group-label.ts.template +1 -1
  12. package/src/generators/ui/libs/command/files/lib/hlm-command-group.ts.template +1 -1
  13. package/src/generators/ui/libs/command/files/lib/hlm-command-input.ts.template +7 -6
  14. package/src/generators/ui/libs/command/files/lib/hlm-command-item.ts.template +1 -1
  15. package/src/generators/ui/libs/command/files/lib/hlm-command-list.ts.template +1 -1
  16. package/src/generators/ui/libs/command/files/lib/hlm-command-separator.ts.template +1 -1
  17. package/src/generators/ui/libs/command/files/lib/hlm-command-shortcut.ts.template +1 -1
  18. package/src/generators/ui/libs/command/files/lib/hlm-command.ts.template +1 -1
  19. package/src/generators/ui/libs/context-menu/files/lib/hlm-context-menu-trigger.ts.template +3 -0
  20. package/src/generators/ui/libs/dropdown-menu/files/index.ts.template +3 -0
  21. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-checkbox-indicator.ts.template +3 -2
  22. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-checkbox.ts.template +30 -10
  23. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-group.ts.template +1 -3
  24. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-item-sub-indicator.ts.template +2 -2
  25. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-item.ts.template +1 -1
  26. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-label.ts.template +4 -4
  27. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-radio-indicator.ts.template +5 -4
  28. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-radio.ts.template +24 -9
  29. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-separator.ts.template +2 -4
  30. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-shortcut.ts.template +2 -4
  31. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-sub-trigger.ts.template +44 -0
  32. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-sub.ts.template +1 -4
  33. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu-trigger.ts.template +1 -3
  34. package/src/generators/ui/libs/dropdown-menu/files/lib/hlm-dropdown-menu.ts.template +1 -1
  35. package/src/generators/ui/libs/input/files/lib/hlm-input.ts.template +1 -0
  36. package/src/generators/ui/libs/popover/files/lib/hlm-popover-content.ts.template +1 -0
  37. package/src/generators/ui/libs/popover/files/lib/hlm-popover.ts.template +1 -0
  38. package/src/generators/ui/libs/progress/files/lib/hlm-progress-indicator.ts.template +5 -1
  39. package/src/generators/ui/libs/progress/files/lib/hlm-progress.ts.template +1 -0
  40. package/src/generators/ui/libs/switch/files/lib/hlm-switch-thumb.ts.template +1 -0
  41. package/src/generators/ui/libs/switch/files/lib/hlm-switch.ts.template +1 -0
  42. package/src/generators/ui/primitive-deps.js +1 -0
  43. package/src/generators/ui/primitive-deps.js.map +1 -1
  44. package/src/generators/ui/primitives.d.ts +1 -1
  45. package/src/generators/ui/style-luma.css +13 -123
  46. package/src/generators/ui/style-lyra.css +11 -121
  47. package/src/generators/ui/style-maia.css +12 -122
  48. package/src/generators/ui/style-mira.css +11 -121
  49. package/src/generators/ui/style-nova.css +13 -123
  50. package/src/generators/ui/style-vega.css +14 -124
  51. package/src/generators/ui/supported-ui-libraries.json +42 -42
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spartan-ng/cli",
3
- "version": "0.0.1-alpha.715",
3
+ "version": "0.0.1-alpha.716",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/spartan-ng/spartan"
@@ -40,7 +40,7 @@ exports.default = {
40
40
  HlmAutocompleteModule: 'HlmAutocompleteImports',
41
41
  HlmAvatarModule: 'HlmAvatarImports',
42
42
  HlmBadgeModule: 'HlmBadgeImports',
43
- HlmBreadCrumbModule: 'HlmBreadCrumbImports',
43
+ HlmBreadCrumbModule: 'HlmBreadcrumbImports',
44
44
  HlmButtonModule: 'HlmButtonImports',
45
45
  HlmCalendarModule: 'HlmCalendarImports',
46
46
  HlmCardModule: 'HlmCardImports',
@@ -4,7 +4,7 @@ import { classes } from '<%- importAlias %>/utils';
4
4
 
5
5
  @Directive({
6
6
  selector: '[hlmAvatarFallback]',
7
- exportAs: 'avatarFallback',
7
+ exportAs: 'hlmAvatarFallback',
8
8
  hostDirectives: [BrnAvatarFallback],
9
9
  host: {
10
10
  'data-slot': 'avatar-fallback',
@@ -4,7 +4,7 @@ import { classes } from '<%- importAlias %>/utils';
4
4
 
5
5
  @Directive({
6
6
  selector: 'img[hlmAvatarImage]',
7
- exportAs: 'avatarImage',
7
+ exportAs: 'hlmAvatarImage',
8
8
  hostDirectives: [BrnAvatarImage],
9
9
  host: {
10
10
  'data-slot': 'avatar-image',
@@ -23,8 +23,3 @@ export const HlmBreadcrumbImports = [
23
23
  HlmBreadcrumbPage,
24
24
  HlmBreadcrumbList,
25
25
  ] as const;
26
-
27
- /**
28
- * @deprecated Use `HlmBreadcrumbImports` instead.
29
- */
30
- export const HlmBreadCrumbImports = HlmBreadcrumbImports;
@@ -25,6 +25,7 @@ import type { ClassValue } from 'clsx';
25
25
  imports: [BrnCalendarImports, NgIcon, HlmIcon, NgTemplateOutlet, HlmSelectImports],
26
26
  viewProviders: [provideIcons({ lucideChevronLeft, lucideChevronRight })],
27
27
  changeDetection: ChangeDetectionStrategy.OnPush,
28
+ host: { 'data-slot': 'calendar' },
28
29
  template: `
29
30
  <div
30
31
  brnCalendarMulti
@@ -25,6 +25,7 @@ import type { ClassValue } from 'clsx';
25
25
  imports: [BrnCalendarImports, NgIcon, HlmIcon, HlmSelectImports, NgTemplateOutlet],
26
26
  viewProviders: [provideIcons({ lucideChevronLeft, lucideChevronRight })],
27
27
  changeDetection: ChangeDetectionStrategy.OnPush,
28
+ host: { 'data-slot': 'calendar' },
28
29
  template: `
29
30
  <div
30
31
  brnCalendarRange
@@ -25,6 +25,7 @@ import type { ClassValue } from 'clsx';
25
25
  imports: [BrnCalendarImports, NgIcon, HlmIcon, HlmSelectImports, NgTemplateOutlet],
26
26
  viewProviders: [provideIcons({ lucideChevronLeft, lucideChevronRight })],
27
27
  changeDetection: ChangeDetectionStrategy.OnPush,
28
+ host: { 'data-slot': 'calendar' },
28
29
  template: `
29
30
  <div
30
31
  brnCalendar
@@ -8,14 +8,14 @@ import {
8
8
  linkedSignal,
9
9
  output,
10
10
  } from '@angular/core';
11
- import { BrnDialogContent, BrnDialogState } from '@spartan-ng/brain/dialog';
11
+ import { BrnDialogState } from '@spartan-ng/brain/dialog';
12
12
  import { HlmDialogImports } from '<%- importAlias %>/dialog';
13
13
  import { hlm } from '<%- importAlias %>/utils';
14
14
  import { ClassValue } from 'clsx';
15
15
 
16
16
  @Component({
17
17
  selector: 'hlm-command-dialog',
18
- imports: [HlmDialogImports, BrnDialogContent],
18
+ imports: [HlmDialogImports],
19
19
  changeDetection: ChangeDetectionStrategy.OnPush,
20
20
  template: `
21
21
  <hlm-dialog [state]="_state()" (stateChanged)="stateChanged($event)">
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCommandEmpty {
11
11
  constructor() {
12
- classes(() => 'py-6 text-center text-sm');
12
+ classes(() => 'spartan-command-empty');
13
13
  }
14
14
  }
@@ -10,6 +10,6 @@ import { classes } from '<%- importAlias %>/utils';
10
10
  })
11
11
  export class HlmCommandGroupLabel {
12
12
  constructor() {
13
- classes(() => 'text-muted-foreground block px-2 py-1.5 text-xs font-medium');
13
+ classes(() => 'inline-block');
14
14
  }
15
15
  }
@@ -16,6 +16,6 @@ import { classes } from '<%- importAlias %>/utils';
16
16
  })
17
17
  export class HlmCommandGroup {
18
18
  constructor() {
19
- classes(() => 'text-foreground block overflow-hidden p-1 data-hidden:hidden');
19
+ classes(() => 'spartan-command-group block data-hidden:hidden');
20
20
  }
21
21
  }
@@ -10,20 +10,21 @@ import { classes } from '<%- importAlias %>/utils';
10
10
  imports: [HlmInputGroupImports, NgIcon, BrnCommandInput],
11
11
  providers: [provideIcons({ lucideSearch })],
12
12
  changeDetection: ChangeDetectionStrategy.OnPush,
13
+ host: {
14
+ 'data-slot': 'command-input-wrapper',
15
+ },
13
16
  template: `
14
- <hlm-input-group
15
- class="bg-input/30 border-input/30 h-8 rounded-lg shadow-none *:data-[slot=input-group-addon]:pl-2"
16
- >
17
+ <hlm-input-group class="spartan-command-input-group">
17
18
  <input
18
19
  brnCommandInput
19
20
  data-slot="command-input"
20
- class="w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
21
+ class="spartan-command-input outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
21
22
  [id]="inputId()"
22
23
  [placeholder]="placeholder()"
23
24
  />
24
25
 
25
26
  <hlm-input-group-addon>
26
- <ng-icon name="lucideSearch" />
27
+ <ng-icon name="lucideSearch" class="spartan-command-input-icon" />
27
28
  </hlm-input-group-addon>
28
29
  </hlm-input-group>
29
30
  `,
@@ -33,6 +34,6 @@ export class HlmCommandInput {
33
34
  public readonly placeholder = input<string>('');
34
35
 
35
36
  constructor() {
36
- classes(() => 'p-1 pb-0');
37
+ classes(() => 'spartan-command-input-wrapper');
37
38
  }
38
39
  }
@@ -19,7 +19,7 @@ export class HlmCommandItem {
19
19
  constructor() {
20
20
  classes(
21
21
  () =>
22
- "data-[selected]:bg-accent data-[selected=true]:text-accent-foreground [&>ng-icon:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-hidden:hidden data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>ng-icon]:pointer-events-none [&>ng-icon]:shrink-0 [&>ng-icon:not([class*='text-'])]:text-base",
22
+ 'spartan-command-item group/command-item w-full data-disabled:pointer-events-none data-disabled:opacity-50 data-hidden:hidden [&>ng-icon]:pointer-events-none [&>ng-icon]:shrink-0',
23
23
  );
24
24
  }
25
25
  }
@@ -16,6 +16,6 @@ import { classes } from '<%- importAlias %>/utils';
16
16
  })
17
17
  export class HlmCommandList {
18
18
  constructor() {
19
- classes(() => 'no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none');
19
+ classes(() => 'spartan-command-list overflow-x-hidden overflow-y-auto');
20
20
  }
21
21
  }
@@ -11,6 +11,6 @@ import { classes } from '<%- importAlias %>/utils';
11
11
  })
12
12
  export class HlmCommandSeparator {
13
13
  constructor() {
14
- classes(() => 'bg-border -mx-1 block h-px w-auto data-hidden:hidden');
14
+ classes(() => 'spartan-command-separator block data-hidden:hidden');
15
15
  }
16
16
  }
@@ -9,6 +9,6 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  })
10
10
  export class HlmCommandShortcut {
11
11
  constructor() {
12
- classes(() => 'text-muted-foreground ml-auto text-xs tracking-widest');
12
+ classes(() => 'spartan-command-shortcut');
13
13
  }
14
14
  }
@@ -17,6 +17,6 @@ import { classes } from '<%- importAlias %>/utils';
17
17
  })
18
18
  export class HlmCommand {
19
19
  constructor() {
20
- classes(() => 'bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-xl p-1');
20
+ classes(() => 'spartan-command flex size-full flex-col overflow-hidden');
21
21
  }
22
22
  }
@@ -3,6 +3,7 @@ import { CdkContextMenuTrigger } from '@angular/cdk/menu';
3
3
  import { booleanAttribute, computed, Directive, effect, inject, input } from '@angular/core';
4
4
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
5
  import { createMenuPosition, type MenuAlign, type MenuSide } from '@spartan-ng/brain/core';
6
+ import { classes } from '<%- importAlias %>/utils';
6
7
  import { injectHlmContextMenuConfig } from './hlm-context-menu-token';
7
8
 
8
9
  @Directive({
@@ -50,5 +51,7 @@ export class HlmContextMenuTrigger {
50
51
  effect(() => {
51
52
  this._cdkTrigger.menuPosition = this._menuPosition();
52
53
  });
54
+
55
+ classes(() => 'select-none');
53
56
  }
54
57
  }
@@ -10,6 +10,7 @@ import { HlmDropdownMenuRadioIndicator } from './lib/hlm-dropdown-menu-radio-ind
10
10
  import { HlmDropdownMenuSeparator } from './lib/hlm-dropdown-menu-separator';
11
11
  import { HlmDropdownMenuShortcut } from './lib/hlm-dropdown-menu-shortcut';
12
12
  import { HlmDropdownMenuSub } from './lib/hlm-dropdown-menu-sub';
13
+ import { HlmDropdownMenuSubTrigger } from './lib/hlm-dropdown-menu-sub-trigger';
13
14
 
14
15
  import { HlmDropdownMenuTrigger } from './lib/hlm-dropdown-menu-trigger';
15
16
 
@@ -25,6 +26,7 @@ export * from './lib/hlm-dropdown-menu-radio-indicator';
25
26
  export * from './lib/hlm-dropdown-menu-separator';
26
27
  export * from './lib/hlm-dropdown-menu-shortcut';
27
28
  export * from './lib/hlm-dropdown-menu-sub';
29
+ export * from './lib/hlm-dropdown-menu-sub-trigger';
28
30
  export * from './lib/hlm-dropdown-menu-token';
29
31
  export * from './lib/hlm-dropdown-menu-trigger';
30
32
 
@@ -41,5 +43,6 @@ export const HlmDropdownMenuImports = [
41
43
  HlmDropdownMenuSeparator,
42
44
  HlmDropdownMenuShortcut,
43
45
  HlmDropdownMenuSub,
46
+ HlmDropdownMenuSubTrigger,
44
47
  HlmDropdownMenuTrigger,
45
48
  ] as const;
@@ -8,15 +8,16 @@ import { classes } from '<%- importAlias %>/utils';
8
8
  imports: [NgIcon],
9
9
  providers: [provideIcons({ lucideCheck })],
10
10
  changeDetection: ChangeDetectionStrategy.OnPush,
11
+ host: { 'data-slot': 'dropdown-menu-checkbox-item-indicator' },
11
12
  template: `
12
- <ng-icon class="text-base" name="lucideCheck" />
13
+ <ng-icon name="lucideCheck" />
13
14
  `,
14
15
  })
15
16
  export class HlmDropdownMenuCheckboxIndicator {
16
17
  constructor() {
17
18
  classes(
18
19
  () =>
19
- 'pointer-events-none absolute left-2 flex size-3.5 items-center justify-center opacity-0 group-data-[checked]:opacity-100',
20
+ 'spartan-dropdown-menu-item-indicator pointer-events-none opacity-0 group-data-checked/dropdown-menu-checkbox:opacity-100',
20
21
  );
21
22
  }
22
23
  }
@@ -1,32 +1,52 @@
1
1
  import { type BooleanInput } from '@angular/cdk/coercion';
2
- import { CdkMenuItemCheckbox } from '@angular/cdk/menu';
2
+ import { CdkMenuItem, CdkMenuItemCheckbox, CdkMenuItemSelectable } from '@angular/cdk/menu';
3
3
  import { Directive, booleanAttribute, inject, input } from '@angular/core';
4
4
  import { classes } from '<%- importAlias %>/utils';
5
5
 
6
+ /** @internal. Use HlmDropdownMenuCheckbox instead. */
6
7
  @Directive({
7
- selector: '[hlmDropdownMenuCheckbox]',
8
+ selector: '[hlmDropdownMenuCheckboxCdk]',
9
+ providers: [
10
+ { provide: CdkMenuItemCheckbox, useExisting: HlmDropdownMenuCheckboxCdk },
11
+ { provide: CdkMenuItemSelectable, useExisting: HlmDropdownMenuCheckboxCdk },
12
+ { provide: CdkMenuItem, useExisting: CdkMenuItemSelectable },
13
+ ],
14
+ })
15
+ export class HlmDropdownMenuCheckboxCdk extends CdkMenuItemCheckbox {
16
+ public readonly keepOpen = input<boolean, BooleanInput>(true, { transform: booleanAttribute });
17
+
18
+ public override trigger(options?: { keepOpen: boolean }) {
19
+ super.trigger({ ...options, keepOpen: this.keepOpen() });
20
+ }
21
+ }
22
+
23
+ @Directive({
24
+ selector: '[hlmDropdownMenuCheckbox],[hlmDropdownMenuCheckboxItem]',
8
25
  hostDirectives: [
9
26
  {
10
- directive: CdkMenuItemCheckbox,
11
- inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked'],
27
+ directive: HlmDropdownMenuCheckboxCdk,
28
+ inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked', 'keepOpen'],
12
29
  outputs: ['cdkMenuItemTriggered: triggered'],
13
30
  },
14
31
  ],
15
32
  host: {
16
33
  'data-slot': 'dropdown-menu-checkbox-item',
17
- '[attr.data-disabled]': 'disabled() ? "" : null',
18
- '[attr.data-checked]': 'checked() ? "" : null',
34
+ '[attr.data-disabled]': '_cdkMenuItem.disabled ? "" : null',
35
+ '[attr.data-checked]': '_cdkMenuItem.checked ? "" : null',
36
+ '[attr.data-inset]': 'inset() ? "" : null',
19
37
  },
20
38
  })
21
39
  export class HlmDropdownMenuCheckbox {
22
- private readonly _cdkMenuItem = inject(CdkMenuItemCheckbox);
23
- public readonly checked = input<boolean, BooleanInput>(this._cdkMenuItem.checked, { transform: booleanAttribute });
24
- public readonly disabled = input<boolean, BooleanInput>(this._cdkMenuItem.disabled, { transform: booleanAttribute });
40
+ protected readonly _cdkMenuItem = inject(HlmDropdownMenuCheckboxCdk);
41
+
42
+ public readonly inset = input<boolean, BooleanInput>(false, {
43
+ transform: booleanAttribute,
44
+ });
25
45
 
26
46
  constructor() {
27
47
  classes(
28
48
  () =>
29
- 'hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground group relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:ps-2 has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:pe-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:[&>hlm-dropdown-menu-checkbox-indicator]:start-auto has-[>hlm-dropdown-menu-checkbox-indicator:last-child]:[&>hlm-dropdown-menu-checkbox-indicator]:end-2',
49
+ 'spartan-dropdown-menu-checkbox-item group/dropdown-menu-checkbox relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0',
30
50
  );
31
51
  }
32
52
  }
@@ -5,9 +5,7 @@ import { classes } from '<%- importAlias %>/utils';
5
5
  @Directive({
6
6
  selector: '[hlmDropdownMenuGroup],hlm-dropdown-menu-group',
7
7
  hostDirectives: [CdkMenuGroup],
8
- host: {
9
- 'data-slot': 'dropdown-menu-group',
10
- },
8
+ host: { 'data-slot': 'dropdown-menu-group' },
11
9
  })
12
10
  export class HlmDropdownMenuGroup {
13
11
  constructor() {
@@ -9,11 +9,11 @@ import { classes } from '<%- importAlias %>/utils';
9
9
  providers: [provideIcons({ lucideChevronRight })],
10
10
  changeDetection: ChangeDetectionStrategy.OnPush,
11
11
  template: `
12
- <ng-icon name="lucideChevronRight" class="text-base rtl:rotate-180" />
12
+ <ng-icon name="lucideChevronRight" class="text-[calc(var(--spacing)*4)] rtl:rotate-180" />
13
13
  `,
14
14
  })
15
15
  export class HlmDropdownMenuItemSubIndicator {
16
16
  constructor() {
17
- classes(() => 'ms-auto size-4');
17
+ classes(() => 'ms-auto flex items-center justify-center');
18
18
  }
19
19
  }
@@ -34,7 +34,7 @@ export class HlmDropdownMenuItem {
34
34
  constructor() {
35
35
  classes(
36
36
  () =>
37
- "hover:bg-accent focus-visible:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[ng-icon]:!text-destructive [&_ng-icon:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0 [&_svg:not([class*='text-'])]:text-base",
37
+ 'spartan-dropdown-menu-item group/dropdown-menu-item relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0',
38
38
  );
39
39
  }
40
40
  }
@@ -10,11 +10,11 @@ import { classes } from '<%- importAlias %>/utils';
10
10
  },
11
11
  })
12
12
  export class HlmDropdownMenuLabel {
13
- constructor() {
14
- classes(() => 'block px-2 py-1.5 text-sm font-medium data-[inset]:pl-8');
15
- }
16
-
17
13
  public readonly inset = input<boolean, BooleanInput>(false, {
18
14
  transform: booleanAttribute,
19
15
  });
16
+
17
+ constructor() {
18
+ classes(() => 'spartan-dropdown-menu-label block');
19
+ }
20
20
  }
@@ -1,22 +1,23 @@
1
1
  import { ChangeDetectionStrategy, Component } from '@angular/core';
2
2
  import { NgIcon, provideIcons } from '@ng-icons/core';
3
- import { lucideCircle } from '@ng-icons/lucide';
3
+ import { lucideCheck } from '@ng-icons/lucide';
4
4
  import { classes } from '<%- importAlias %>/utils';
5
5
 
6
6
  @Component({
7
7
  selector: 'hlm-dropdown-menu-radio-indicator',
8
8
  imports: [NgIcon],
9
- providers: [provideIcons({ lucideCircle })],
9
+ providers: [provideIcons({ lucideCheck })],
10
10
  changeDetection: ChangeDetectionStrategy.OnPush,
11
+ host: { 'data-slot': 'dropdown-menu-radio-item-indicator' },
11
12
  template: `
12
- <ng-icon name="lucideCircle" class="text-[0.5rem] *:[svg]:fill-current" />
13
+ <ng-icon name="lucideCheck" />
13
14
  `,
14
15
  })
15
16
  export class HlmDropdownMenuRadioIndicator {
16
17
  constructor() {
17
18
  classes(
18
19
  () =>
19
- 'pointer-events-none absolute start-2 flex size-3.5 items-center justify-center opacity-0 group-data-[checked]:opacity-100',
20
+ 'spartan-dropdown-menu-item-indicator pointer-events-none opacity-0 group-data-checked/dropdown-menu-radio:opacity-100',
20
21
  );
21
22
  }
22
23
  }
@@ -1,32 +1,47 @@
1
1
  import { type BooleanInput } from '@angular/cdk/coercion';
2
- import { CdkMenuItemRadio } from '@angular/cdk/menu';
2
+ import { CdkMenuItem, CdkMenuItemRadio, CdkMenuItemSelectable } from '@angular/cdk/menu';
3
3
  import { Directive, booleanAttribute, inject, input } from '@angular/core';
4
4
  import { classes } from '<%- importAlias %>/utils';
5
5
 
6
+ /** @internal. Use HlmDropdownMenuRadio instead. */
7
+ @Directive({
8
+ selector: '[hlmDropdownMenuRadioCdk]',
9
+ providers: [
10
+ { provide: CdkMenuItemRadio, useExisting: HlmDropdownMenuRadioCdk },
11
+ { provide: CdkMenuItemSelectable, useExisting: HlmDropdownMenuRadioCdk },
12
+ { provide: CdkMenuItem, useExisting: CdkMenuItemSelectable },
13
+ ],
14
+ })
15
+ export class HlmDropdownMenuRadioCdk extends CdkMenuItemRadio {
16
+ public readonly keepOpen = input<boolean, BooleanInput>(true, { transform: booleanAttribute });
17
+
18
+ public override trigger(options?: { keepOpen: boolean }) {
19
+ super.trigger({ ...options, keepOpen: this.keepOpen() });
20
+ }
21
+ }
22
+
6
23
  @Directive({
7
24
  selector: '[hlmDropdownMenuRadio]',
8
25
  hostDirectives: [
9
26
  {
10
- directive: CdkMenuItemRadio,
11
- inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked'],
27
+ directive: HlmDropdownMenuRadioCdk,
28
+ inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked', 'keepOpen'],
12
29
  outputs: ['cdkMenuItemTriggered: triggered'],
13
30
  },
14
31
  ],
15
32
  host: {
16
33
  'data-slot': 'dropdown-menu-radio-item',
17
- '[attr.data-disabled]': 'disabled() ? "" : null',
18
- '[attr.data-checked]': 'checked() ? "" : null',
34
+ '[attr.data-disabled]': '_cdkMenuItem.disabled ? "" : null',
35
+ '[attr.data-checked]': '_cdkMenuItem.checked ? "" : null',
19
36
  },
20
37
  })
21
38
  export class HlmDropdownMenuRadio {
22
- private readonly _cdkMenuItem = inject(CdkMenuItemRadio);
23
- public readonly checked = input<boolean, BooleanInput>(this._cdkMenuItem.checked, { transform: booleanAttribute });
24
- public readonly disabled = input<boolean, BooleanInput>(this._cdkMenuItem.disabled, { transform: booleanAttribute });
39
+ protected readonly _cdkMenuItem = inject(HlmDropdownMenuRadioCdk);
25
40
 
26
41
  constructor() {
27
42
  classes(
28
43
  () =>
29
- 'hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground group relative flex w-full cursor-default items-center rounded-sm py-1.5 ps-8 pe-2 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
44
+ 'spartan-dropdown-menu-radio-item group/dropdown-menu-radio relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0',
30
45
  );
31
46
  }
32
47
  }
@@ -3,12 +3,10 @@ import { classes } from '<%- importAlias %>/utils';
3
3
 
4
4
  @Directive({
5
5
  selector: '[hlmDropdownMenuSeparator],hlm-dropdown-menu-separator',
6
- host: {
7
- 'data-slot': 'dropdown-menu-separator',
8
- },
6
+ host: { 'data-slot': 'dropdown-menu-separator' },
9
7
  })
10
8
  export class HlmDropdownMenuSeparator {
11
9
  constructor() {
12
- classes(() => 'bg-border -mx-1 my-1 block h-px');
10
+ classes(() => 'spartan-dropdown-menu-separator block');
13
11
  }
14
12
  }
@@ -3,12 +3,10 @@ import { classes } from '<%- importAlias %>/utils';
3
3
 
4
4
  @Directive({
5
5
  selector: '[hlmDropdownMenuShortcut],hlm-dropdown-menu-shortcut',
6
- host: {
7
- 'data-slot': 'dropdown-menu-shortcut',
8
- },
6
+ host: { 'data-slot': 'dropdown-menu-shortcut' },
9
7
  })
10
8
  export class HlmDropdownMenuShortcut {
11
9
  constructor() {
12
- classes(() => 'text-muted-foreground ml-auto text-xs tracking-widest');
10
+ classes(() => 'spartan-dropdown-menu-shortcut');
13
11
  }
14
12
  }
@@ -0,0 +1,44 @@
1
+ import { CdkMenuTrigger } from '@angular/cdk/menu';
2
+ import { computed, Directive, effect, inject, input } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { createMenuPosition, type MenuAlign, type MenuSide } from '@spartan-ng/brain/core';
5
+ import { classes } from '<%- importAlias %>/utils';
6
+ import { injectHlmDropdownMenuConfig } from './hlm-dropdown-menu-token';
7
+
8
+ @Directive({
9
+ selector: '[hlmDropdownMenuSubTrigger]',
10
+ hostDirectives: [
11
+ {
12
+ directive: CdkMenuTrigger,
13
+ inputs: ['cdkMenuTriggerFor: hlmDropdownMenuSubTrigger', 'cdkMenuTriggerData: hlmDropdownMenuTriggerData'],
14
+ outputs: ['cdkMenuOpened: hlmDropdownMenuSubOpened', 'cdkMenuClosed: hlmDropdownMenuSubClosed'],
15
+ },
16
+ ],
17
+ host: { 'data-slot': 'dropdown-menu-sub-trigger' },
18
+ })
19
+ export class HlmDropdownMenuSubTrigger {
20
+ private readonly _cdkTrigger = inject(CdkMenuTrigger, { host: true });
21
+ private readonly _config = injectHlmDropdownMenuConfig();
22
+
23
+ public readonly align = input<MenuAlign>(this._config.align);
24
+ public readonly side = input<MenuSide>(this._config.side);
25
+
26
+ private readonly _menuPosition = computed(() => createMenuPosition(this.align(), this.side()));
27
+
28
+ constructor() {
29
+ this._cdkTrigger.opened.pipe(takeUntilDestroyed()).subscribe(() =>
30
+ setTimeout(
31
+ () =>
32
+ // eslint-disable-next-line
33
+ ((this._cdkTrigger as any)._spartanLastPosition = // eslint-disable-next-line
34
+ (this._cdkTrigger as any).overlayRef._positionStrategy._lastPosition),
35
+ ),
36
+ );
37
+
38
+ effect(() => {
39
+ this._cdkTrigger.menuPosition = this._menuPosition();
40
+ });
41
+
42
+ classes(() => 'aria-expanded:bg-accent aria-expanded:text-accent-foreground');
43
+ }
44
+ }
@@ -24,10 +24,7 @@ export class HlmDropdownMenuSub {
24
24
  // TODO: figure out a way for us to know the host is about to be closed. might not be possible with CDK
25
25
  this._host.closed.pipe(takeUntilDestroyed()).subscribe(() => this._state.set('closed'));
26
26
 
27
- classes(
28
- () =>
29
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-top overflow-hidden rounded-md border p-1 shadow-lg',
30
- );
27
+ classes(() => 'spartan-dropdown-menu-sub-content w-auto');
31
28
  }
32
29
 
33
30
  private setSideWithDarkMagic() {
@@ -13,9 +13,7 @@ import { injectHlmDropdownMenuConfig } from './hlm-dropdown-menu-token';
13
13
  outputs: ['cdkMenuOpened: hlmDropdownMenuOpened', 'cdkMenuClosed: hlmDropdownMenuClosed'],
14
14
  },
15
15
  ],
16
- host: {
17
- 'data-slot': 'dropdown-menu-trigger',
18
- },
16
+ host: { 'data-slot': 'dropdown-menu-trigger' },
19
17
  })
20
18
  export class HlmDropdownMenuTrigger {
21
19
  private readonly _cdkTrigger = inject(CdkMenuTrigger, { host: true });
@@ -25,7 +25,7 @@ export class HlmDropdownMenu {
25
25
  constructor() {
26
26
  classes(
27
27
  () =>
28
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 my-[--spacing(var(--side-offset))] min-w-[8rem] origin-top overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
28
+ 'spartan-dropdown-menu-content my-[--spacing(var(--side-offset))] overflow-x-hidden overflow-y-auto outline-none',
29
29
  );
30
30
 
31
31
  this.setSideWithDarkMagic();
@@ -6,6 +6,7 @@ import { classes } from '<%- importAlias %>/utils';
6
6
  @Directive({
7
7
  selector: '[hlmInput]',
8
8
  hostDirectives: [{ directive: BrnInput, inputs: ['id', 'forceInvalid'] }, BrnFieldControlDescribedBy],
9
+ host: { 'data-slot': 'input' },
9
10
  })
10
11
  export class HlmInput {
11
12
  constructor() {
@@ -4,6 +4,7 @@ import { classes } from '<%- importAlias %>/utils';
4
4
 
5
5
  @Directive({
6
6
  selector: '[hlmPopoverContent],hlm-popover-content',
7
+ host: { 'data-slot': 'popover-content' },
7
8
  })
8
9
  export class HlmPopoverContent {
9
10
  private readonly _stateProvider = injectExposesStateProvider({ host: true });
@@ -20,5 +20,6 @@ import { BrnPopover } from '@spartan-ng/brain/popover';
20
20
  outputs: ['stateChanged', 'closed'],
21
21
  },
22
22
  ],
23
+ host: { 'data-slot': 'popover' },
23
24
  })
24
25
  export class HlmPopover {}
@@ -6,7 +6,11 @@ import { classes } from '<%- importAlias %>/utils';
6
6
  @Directive({
7
7
  selector: '[hlmProgressIndicator],hlm-progress-indicator',
8
8
  hostDirectives: [BrnProgressIndicator],
9
- host: { '[class.animate-indeterminate]': '_indeterminate()', '[style.transform]': '_transform()' },
9
+ host: {
10
+ 'data-slot': 'progress-indicator',
11
+ '[class.animate-indeterminate]': '_indeterminate()',
12
+ '[style.transform]': '_transform()',
13
+ },
10
14
  })
11
15
  export class HlmProgressIndicator {
12
16
  private readonly _progress = injectBrnProgress();