Skip to content

Commit f55db20

Browse files
author
Leonidas
committed
chore: nightly release 2025-11-26 ⚡
1 parent 8fa0f13 commit f55db20

26 files changed

+742
-41
lines changed

libs/brain/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@spartan-ng/brain",
3-
"version": "0.0.1-alpha.566",
3+
"version": "0.0.1-alpha.567",
44
"sideEffects": false,
55
"exports": {
66
"./hlm-tailwind-preset": {

libs/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@spartan-ng/cli",
3-
"version": "0.0.1-alpha.566",
3+
"version": "0.0.1-alpha.567",
44
"type": "commonjs",
55
"dependencies": {
66
"@angular/core": ">=19.0.0",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { HlmContextMenuTrigger } from './lib/hlm-context-menu-trigger';
2+
3+
export * from './lib/hlm-context-menu-token';
4+
export * from './lib/hlm-context-menu-trigger';
5+
6+
export const HlmContextMenuImports = [HlmContextMenuTrigger] as const;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { InjectionToken, type ValueProvider, inject } from '@angular/core';
2+
import { type MenuAlign, type MenuSide } from '@spartan-ng/brain/core';
3+
4+
export interface HlmContextMenuConfig {
5+
align: MenuAlign;
6+
side: MenuSide;
7+
}
8+
9+
const defaultConfig: HlmContextMenuConfig = {
10+
align: 'start',
11+
side: 'bottom',
12+
};
13+
14+
const HlmContextMenuConfigToken = new InjectionToken<HlmContextMenuConfig>('HlmContextMenuConfig');
15+
16+
export function provideHlmContextMenuConfig(config: Partial<HlmContextMenuConfig>): ValueProvider {
17+
return { provide: HlmContextMenuConfigToken, useValue: { ...defaultConfig, ...config } };
18+
}
19+
20+
export function injectHlmContextMenuConfig(): HlmContextMenuConfig {
21+
return inject(HlmContextMenuConfigToken, { optional: true }) ?? defaultConfig;
22+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { type BooleanInput } from '@angular/cdk/coercion';
2+
import { CdkContextMenuTrigger } from '@angular/cdk/menu';
3+
import { booleanAttribute, computed, Directive, effect, inject, input } from '@angular/core';
4+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5+
import { createMenuPosition, type MenuAlign, type MenuSide } from '@spartan-ng/brain/core';
6+
import { injectHlmContextMenuConfig } from './hlm-context-menu-token';
7+
8+
@Directive({
9+
selector: '[hlmContextMenuTrigger]',
10+
hostDirectives: [
11+
{
12+
directive: CdkContextMenuTrigger,
13+
inputs: [
14+
'cdkContextMenuTriggerFor: hlmContextMenuTrigger',
15+
'cdkContextMenuTriggerData: hlmContextMenuTriggerData',
16+
'cdkContextMenuDisabled: disabled',
17+
],
18+
outputs: ['cdkContextMenuOpened: hlmContextMenuOpened', 'cdkContextMenuClosed: hlmContextMenuClosed'],
19+
},
20+
],
21+
host: {
22+
'data-slot': 'context-menu-trigger',
23+
'[attr.data-disabled]': 'disabled() ? "" : null',
24+
},
25+
})
26+
export class HlmContextMenuTrigger {
27+
private readonly _cdkTrigger = inject(CdkContextMenuTrigger, { host: true });
28+
private readonly _config = injectHlmContextMenuConfig();
29+
30+
public readonly disabled = input<boolean, BooleanInput>(this._cdkTrigger.disabled, { transform: booleanAttribute });
31+
32+
public readonly align = input<MenuAlign>(this._config.align);
33+
public readonly side = input<MenuSide>(this._config.side);
34+
35+
private readonly _menuPosition = computed(() => createMenuPosition(this.align(), this.side()));
36+
37+
constructor() {
38+
// once the trigger opens we wait until the next tick and then grab the last position
39+
// used to position the menu. we store this in our trigger which the brnMenu directive has
40+
// access to through DI
41+
this._cdkTrigger.opened.pipe(takeUntilDestroyed()).subscribe(() =>
42+
setTimeout(
43+
() =>
44+
// eslint-disable-next-line
45+
((this._cdkTrigger as any)._spartanLastPosition = // eslint-disable-next-line
46+
(this._cdkTrigger as any).overlayRef._positionStrategy._lastPosition),
47+
),
48+
);
49+
50+
effect(() => {
51+
this._cdkTrigger.menuPosition = this._menuPosition();
52+
});
53+
}
54+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { HlmDropdownMenu } from './lib/hlm-dropdown-menu';
2+
import { HlmDropdownMenuCheckbox } from './lib/hlm-dropdown-menu-checkbox';
3+
import { HlmDropdownMenuCheckboxIndicator } from './lib/hlm-dropdown-menu-checkbox-indicator';
4+
import { HlmDropdownMenuGroup } from './lib/hlm-dropdown-menu-group';
5+
import { HlmDropdownMenuItem } from './lib/hlm-dropdown-menu-item';
6+
import { HlmDropdownMenuItemSubIndicator } from './lib/hlm-dropdown-menu-item-sub-indicator';
7+
import { HlmDropdownMenuLabel } from './lib/hlm-dropdown-menu-label';
8+
import { HlmDropdownMenuRadio } from './lib/hlm-dropdown-menu-radio';
9+
import { HlmDropdownMenuRadioIndicator } from './lib/hlm-dropdown-menu-radio-indicator';
10+
import { HlmDropdownMenuSeparator } from './lib/hlm-dropdown-menu-separator';
11+
import { HlmDropdownMenuShortcut } from './lib/hlm-dropdown-menu-shortcut';
12+
import { HlmDropdownMenuSub } from './lib/hlm-dropdown-menu-sub';
13+
14+
import { HlmDropdownMenuTrigger } from './lib/hlm-dropdown-menu-trigger';
15+
16+
export * from './lib/hlm-dropdown-menu';
17+
export * from './lib/hlm-dropdown-menu-checkbox';
18+
export * from './lib/hlm-dropdown-menu-checkbox-indicator';
19+
export * from './lib/hlm-dropdown-menu-group';
20+
export * from './lib/hlm-dropdown-menu-item';
21+
export * from './lib/hlm-dropdown-menu-item-sub-indicator';
22+
export * from './lib/hlm-dropdown-menu-label';
23+
export * from './lib/hlm-dropdown-menu-radio';
24+
export * from './lib/hlm-dropdown-menu-radio-indicator';
25+
export * from './lib/hlm-dropdown-menu-separator';
26+
export * from './lib/hlm-dropdown-menu-shortcut';
27+
export * from './lib/hlm-dropdown-menu-sub';
28+
export * from './lib/hlm-dropdown-menu-token';
29+
export * from './lib/hlm-dropdown-menu-trigger';
30+
31+
export const HlmDropdownMenuImports = [
32+
HlmDropdownMenu,
33+
HlmDropdownMenuCheckbox,
34+
HlmDropdownMenuCheckboxIndicator,
35+
HlmDropdownMenuGroup,
36+
HlmDropdownMenuItem,
37+
HlmDropdownMenuItemSubIndicator,
38+
HlmDropdownMenuLabel,
39+
HlmDropdownMenuRadio,
40+
HlmDropdownMenuRadioIndicator,
41+
HlmDropdownMenuSeparator,
42+
HlmDropdownMenuShortcut,
43+
HlmDropdownMenuSub,
44+
HlmDropdownMenuTrigger,
45+
] as const;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
2+
import { NgIcon, provideIcons } from '@ng-icons/core';
3+
import { lucideCheck } from '@ng-icons/lucide';
4+
import { hlm } from '<%- importAlias %>/utils';
5+
import type { ClassValue } from 'clsx';
6+
7+
@Component({
8+
selector: 'hlm-dropdown-menu-checkbox-indicator',
9+
imports: [NgIcon],
10+
providers: [provideIcons({ lucideCheck })],
11+
changeDetection: ChangeDetectionStrategy.OnPush,
12+
host: {
13+
'[class]': '_computedClass()',
14+
},
15+
template: `
16+
<ng-icon class="text-base" name="lucideCheck" />
17+
`,
18+
})
19+
export class HlmDropdownMenuCheckboxIndicator {
20+
public readonly userClass = input<ClassValue>('', { alias: 'class' });
21+
protected readonly _computedClass = computed(() =>
22+
hlm(
23+
'pointer-events-none absolute left-2 flex size-3.5 items-center justify-center opacity-0 group-data-[checked]:opacity-100',
24+
this.userClass(),
25+
),
26+
);
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { type BooleanInput } from '@angular/cdk/coercion';
2+
import { CdkMenuItemCheckbox } from '@angular/cdk/menu';
3+
import { Directive, booleanAttribute, computed, inject, input } from '@angular/core';
4+
import { hlm } from '<%- importAlias %>/utils';
5+
import type { ClassValue } from 'clsx';
6+
7+
@Directive({
8+
selector: '[hlmDropdownMenuCheckbox]',
9+
hostDirectives: [
10+
{
11+
directive: CdkMenuItemCheckbox,
12+
inputs: ['cdkMenuItemDisabled: disabled', 'cdkMenuItemChecked: checked'],
13+
outputs: ['cdkMenuItemTriggered: triggered'],
14+
},
15+
],
16+
host: {
17+
'data-slot': 'dropdown-menu-checkbox-item',
18+
'[attr.data-disabled]': 'disabled() ? "" : null',
19+
'[attr.data-checked]': 'checked() ? "" : null',
20+
'[class]': '_computedClass()',
21+
},
22+
})
23+
export class HlmDropdownMenuCheckbox {
24+
private readonly _cdkMenuItem = inject(CdkMenuItemCheckbox);
25+
26+
public readonly userClass = input<ClassValue>('', { alias: 'class' });
27+
protected readonly _computedClass = computed(() =>
28+
hlm(
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 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
30+
this.userClass(),
31+
),
32+
);
33+
34+
public readonly checked = input<boolean, BooleanInput>(this._cdkMenuItem.checked, { transform: booleanAttribute });
35+
public readonly disabled = input<boolean, BooleanInput>(this._cdkMenuItem.disabled, { transform: booleanAttribute });
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { CdkMenuGroup } from '@angular/cdk/menu';
2+
import { computed, Directive, input } from '@angular/core';
3+
import { hlm } from '<%- importAlias %>/utils';
4+
import type { ClassValue } from 'clsx';
5+
6+
@Directive({
7+
selector: '[hlmDropdownMenuGroup],hlm-dropdown-menu-group',
8+
hostDirectives: [CdkMenuGroup],
9+
host: {
10+
'data-slot': 'dropdown-menu-group',
11+
'[class]': '_computedClass()',
12+
},
13+
})
14+
export class HlmDropdownMenuGroup {
15+
public readonly userClass = input<ClassValue>('', { alias: 'class' });
16+
protected readonly _computedClass = computed(() => hlm('block', this.userClass()));
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
2+
import { NgIcon, provideIcons } from '@ng-icons/core';
3+
import { lucideChevronRight } from '@ng-icons/lucide';
4+
import { hlm } from '<%- importAlias %>/utils';
5+
import type { ClassValue } from 'clsx';
6+
7+
@Component({
8+
selector: 'hlm-dropdown-menu-item-sub-indicator',
9+
imports: [NgIcon],
10+
providers: [provideIcons({ lucideChevronRight })],
11+
changeDetection: ChangeDetectionStrategy.OnPush,
12+
host: {
13+
'[class]': '_computedClass()',
14+
},
15+
template: `
16+
<ng-icon name="lucideChevronRight" class="text-base" />
17+
`,
18+
})
19+
export class HlmDropdownMenuItemSubIndicator {
20+
public readonly userClass = input<ClassValue>('', { alias: 'class' });
21+
protected readonly _computedClass = computed(() => hlm('ml-auto size-4', this.userClass()));
22+
}

0 commit comments

Comments
 (0)