@ng-cn/core 1.0.18 → 1.0.20

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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/app/lib/components/ui/alert-dialog/alert-dialog-content.component.ts +1 -1
  3. package/src/app/lib/components/ui/calendar/calendar.component.ts +65 -12
  4. package/src/app/lib/components/ui/context-menu/context-menu-content.component.ts +1 -0
  5. package/src/app/lib/components/ui/country-selector/country-data.ts +63 -0
  6. package/src/app/lib/components/ui/country-selector/country-selector.component.ts +199 -0
  7. package/src/app/lib/components/ui/country-selector/index.ts +2 -0
  8. package/src/app/lib/components/ui/date-picker/date-picker.component.ts +47 -5
  9. package/src/app/lib/components/ui/dialog/dialog-content.component.ts +1 -1
  10. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-content.component.ts +23 -21
  11. package/src/app/lib/components/ui/field/field-content.component.ts +34 -0
  12. package/src/app/lib/components/ui/field/field-description.component.ts +35 -0
  13. package/src/app/lib/components/ui/field/field-error.component.ts +48 -0
  14. package/src/app/lib/components/ui/field/field-group.component.ts +34 -0
  15. package/src/app/lib/components/ui/field/field-label.component.ts +46 -0
  16. package/src/app/lib/components/ui/field/field-legend.component.ts +41 -0
  17. package/src/app/lib/components/ui/field/field-separator.component.ts +49 -0
  18. package/src/app/lib/components/ui/field/field-set.component.ts +37 -0
  19. package/src/app/lib/components/ui/field/field-title.component.ts +30 -0
  20. package/src/app/lib/components/ui/field/field.component.ts +66 -0
  21. package/src/app/lib/components/ui/field/index.ts +15 -0
  22. package/src/app/lib/components/ui/item/index.ts +21 -0
  23. package/src/app/lib/components/ui/item/item-actions.component.ts +29 -0
  24. package/src/app/lib/components/ui/item/item-content.component.ts +31 -0
  25. package/src/app/lib/components/ui/item/item-description.component.ts +30 -0
  26. package/src/app/lib/components/ui/item/item-footer.component.ts +30 -0
  27. package/src/app/lib/components/ui/item/item-group.component.ts +32 -0
  28. package/src/app/lib/components/ui/item/item-header.component.ts +30 -0
  29. package/src/app/lib/components/ui/item/item-media.component.ts +63 -0
  30. package/src/app/lib/components/ui/item/item-separator.component.ts +33 -0
  31. package/src/app/lib/components/ui/item/item-title.component.ts +27 -0
  32. package/src/app/lib/components/ui/item/item.component.ts +77 -0
  33. package/src/app/lib/components/ui/phone-input/index.ts +1 -0
  34. package/src/app/lib/components/ui/phone-input/phone-input.component.ts +169 -0
  35. package/src/app/lib/components/ui/resizable/resizable-handle.component.ts +2 -2
  36. package/src/app/lib/components/ui/sheet/sheet-content.component.ts +1 -1
  37. package/src/app/lib/components/ui/tabs/tabs-list.component.ts +3 -1
  38. package/src/app/lib/components/ui/textarea/textarea.component.ts +110 -10
  39. package/src/app/lib/components/ui/toast/toast.service.ts +1 -1
@@ -0,0 +1,34 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldContent component - groups a field's title/label and description,
6
+ * useful in horizontal orientation next to a control.
7
+ *
8
+ * @example
9
+ * <Field orientation="horizontal">
10
+ * <FieldContent>
11
+ * <FieldTitle>Enable notifications</FieldTitle>
12
+ * <FieldDescription>Receive updates about your account.</FieldDescription>
13
+ * </FieldContent>
14
+ * <Switch />
15
+ * </Field>
16
+ */
17
+ @Component({
18
+ selector: 'FieldContent',
19
+ template: `<ng-content />`,
20
+ host: {
21
+ 'attr.data-slot': '"field-content"',
22
+ '[class]': 'computedClass()',
23
+ },
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ })
26
+ export class FieldContent {
27
+ /** Additional CSS classes to apply */
28
+ readonly class = input<string>('');
29
+
30
+ /** Computed class combining base styles and custom classes */
31
+ protected readonly computedClass = computed(() =>
32
+ cn('group/field-content flex flex-1 flex-col gap-1.5 leading-snug', this.class()),
33
+ );
34
+ }
@@ -0,0 +1,35 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldDescription component - helper text for a Field.
6
+ *
7
+ * @example
8
+ * <Field>
9
+ * <FieldLabel htmlFor="email">Email</FieldLabel>
10
+ * <input Input id="email" type="email" />
11
+ * <FieldDescription>We will never share your email.</FieldDescription>
12
+ * </Field>
13
+ */
14
+ @Component({
15
+ selector: 'FieldDescription',
16
+ template: `<ng-content />`,
17
+ host: {
18
+ 'attr.data-slot': '"field-description"',
19
+ '[class]': 'computedClass()',
20
+ },
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ })
23
+ export class FieldDescription {
24
+ /** Additional CSS classes to apply */
25
+ readonly class = input<string>('');
26
+
27
+ /** Computed class combining base styles and custom classes */
28
+ protected readonly computedClass = computed(() =>
29
+ cn(
30
+ 'text-muted-foreground text-sm leading-normal font-normal',
31
+ 'group-has-[[data-orientation=horizontal]]/field:text-balance',
32
+ this.class(),
33
+ ),
34
+ );
35
+ }
@@ -0,0 +1,48 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldError component - validation error message(s) for a Field.
6
+ *
7
+ * @example
8
+ * <!-- With projected content -->
9
+ * <FieldError>This field is required.</FieldError>
10
+ *
11
+ * <!-- With an errors array -->
12
+ * <FieldError [errors]="['Too short.', 'Must include a number.']" />
13
+ */
14
+ @Component({
15
+ selector: 'FieldError',
16
+ template: `
17
+ @if (errors() && errors()!.length > 0) {
18
+ @if (errors()!.length === 1) {
19
+ {{ errors()![0] }}
20
+ } @else {
21
+ <ul class="ml-4 flex list-disc flex-col gap-1">
22
+ @for (error of errors(); track error) {
23
+ <li>{{ error }}</li>
24
+ }
25
+ </ul>
26
+ }
27
+ }
28
+ <ng-content />
29
+ `,
30
+ host: {
31
+ 'attr.data-slot': '"field-error"',
32
+ role: 'alert',
33
+ '[class]': 'computedClass()',
34
+ },
35
+ changeDetection: ChangeDetectionStrategy.OnPush,
36
+ })
37
+ export class FieldError {
38
+ /** List of error messages to render. When omitted, projected content is shown. */
39
+ readonly errors = input<string[] | undefined>(undefined);
40
+
41
+ /** Additional CSS classes to apply */
42
+ readonly class = input<string>('');
43
+
44
+ /** Computed class combining base styles and custom classes */
45
+ protected readonly computedClass = computed(() =>
46
+ cn('text-destructive text-sm font-normal', this.class()),
47
+ );
48
+ }
@@ -0,0 +1,34 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldGroup component - container that stacks multiple Field components.
6
+ *
7
+ * @example
8
+ * <FieldGroup>
9
+ * <Field>...</Field>
10
+ * <FieldSeparator />
11
+ * <Field>...</Field>
12
+ * </FieldGroup>
13
+ */
14
+ @Component({
15
+ selector: 'FieldGroup',
16
+ template: `<ng-content />`,
17
+ host: {
18
+ 'attr.data-slot': '"field-group"',
19
+ '[class]': 'computedClass()',
20
+ },
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ })
23
+ export class FieldGroup {
24
+ /** Additional CSS classes to apply */
25
+ readonly class = input<string>('');
26
+
27
+ /** Computed class combining base styles and custom classes */
28
+ protected readonly computedClass = computed(() =>
29
+ cn(
30
+ 'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3',
31
+ this.class(),
32
+ ),
33
+ );
34
+ }
@@ -0,0 +1,46 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldLabel component - label for a form control inside a Field.
6
+ * Renders a native label element for accessibility.
7
+ *
8
+ * @example
9
+ * <FieldLabel htmlFor="username">Username</FieldLabel>
10
+ * <input Input id="username" />
11
+ */
12
+ @Component({
13
+ selector: 'FieldLabel',
14
+ template: `
15
+ <label class="contents" [attr.for]="forId()">
16
+ <ng-content />
17
+ </label>
18
+ `,
19
+ host: {
20
+ 'attr.data-slot': '"field-label"',
21
+ '[class]': 'computedClass()',
22
+ },
23
+ changeDetection: ChangeDetectionStrategy.OnPush,
24
+ })
25
+ export class FieldLabel {
26
+ /** ID of the form element this label is for */
27
+ readonly for = input<string>();
28
+
29
+ /** Alternative binding for 'for' attribute (React-style) */
30
+ readonly htmlFor = input<string>();
31
+
32
+ /** Additional CSS classes to apply */
33
+ readonly class = input<string>('');
34
+
35
+ /** Computed ID - prefers 'for' over 'htmlFor' */
36
+ protected readonly forId = computed(() => this.for() || this.htmlFor());
37
+
38
+ /** Computed class combining base styles and custom classes */
39
+ protected readonly computedClass = computed(() =>
40
+ cn(
41
+ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
42
+ 'text-sm font-medium',
43
+ this.class(),
44
+ ),
45
+ );
46
+ }
@@ -0,0 +1,41 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ export type FieldLegendVariant = 'legend' | 'label';
5
+
6
+ /**
7
+ * FieldLegend component - legend/title for a FieldSet.
8
+ *
9
+ * @example
10
+ * <FieldLegend>Address Information</FieldLegend>
11
+ *
12
+ * <!-- Label-sized legend -->
13
+ * <FieldLegend variant="label">Notifications</FieldLegend>
14
+ */
15
+ @Component({
16
+ selector: 'FieldLegend',
17
+ template: `<ng-content />`,
18
+ host: {
19
+ 'attr.data-slot': '"field-legend"',
20
+ '[attr.data-variant]': 'variant()',
21
+ '[class]': 'computedClass()',
22
+ },
23
+ changeDetection: ChangeDetectionStrategy.OnPush,
24
+ })
25
+ export class FieldLegend {
26
+ /** The visual variant of the legend */
27
+ readonly variant = input<FieldLegendVariant>('legend');
28
+
29
+ /** Additional CSS classes to apply */
30
+ readonly class = input<string>('');
31
+
32
+ /** Computed class combining base styles and custom classes */
33
+ protected readonly computedClass = computed(() =>
34
+ cn(
35
+ 'mb-3 font-medium',
36
+ 'data-[variant=legend]:text-base',
37
+ 'data-[variant=label]:text-sm',
38
+ this.class(),
39
+ ),
40
+ );
41
+ }
@@ -0,0 +1,49 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldSeparator component - visual divider between fields,
6
+ * with optional centered content text (e.g. "Or continue with").
7
+ *
8
+ * @example
9
+ * <!-- Plain separator -->
10
+ * <FieldSeparator />
11
+ *
12
+ * <!-- With centered content -->
13
+ * <FieldSeparator content="Or continue with" />
14
+ */
15
+ @Component({
16
+ selector: 'FieldSeparator',
17
+ template: `
18
+ <div class="bg-border absolute inset-0 top-1/2 h-px w-full" aria-hidden="true"></div>
19
+ @if (content()) {
20
+ <span
21
+ class="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
22
+ data-slot="field-separator-content"
23
+ >
24
+ {{ content() }}
25
+ </span>
26
+ }
27
+ `,
28
+ host: {
29
+ 'attr.data-slot': '"field-separator"',
30
+ '[attr.data-content]': 'hasContent()',
31
+ '[class]': 'computedClass()',
32
+ },
33
+ changeDetection: ChangeDetectionStrategy.OnPush,
34
+ })
35
+ export class FieldSeparator {
36
+ /** Optional content text rendered centered on the separator line */
37
+ readonly content = input<string>('');
38
+
39
+ /** Additional CSS classes to apply */
40
+ readonly class = input<string>('');
41
+
42
+ /** Whether content text is present */
43
+ protected readonly hasContent = computed(() => !!this.content());
44
+
45
+ /** Computed class combining base styles and custom classes */
46
+ protected readonly computedClass = computed(() =>
47
+ cn('relative -my-2 h-5 text-sm', this.class()),
48
+ );
49
+ }
@@ -0,0 +1,37 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldSet component - groups related fields together with fieldset semantics.
6
+ *
7
+ * @example
8
+ * <FieldSet>
9
+ * <FieldLegend>Profile</FieldLegend>
10
+ * <FieldGroup>
11
+ * <Field>...</Field>
12
+ * </FieldGroup>
13
+ * </FieldSet>
14
+ */
15
+ @Component({
16
+ selector: 'FieldSet',
17
+ template: `<ng-content />`,
18
+ host: {
19
+ 'attr.data-slot': '"field-set"',
20
+ role: 'group',
21
+ '[class]': 'computedClass()',
22
+ },
23
+ changeDetection: ChangeDetectionStrategy.OnPush,
24
+ })
25
+ export class FieldSet {
26
+ /** Additional CSS classes to apply */
27
+ readonly class = input<string>('');
28
+
29
+ /** Computed class combining base styles and custom classes */
30
+ protected readonly computedClass = computed(() =>
31
+ cn(
32
+ 'flex flex-col gap-6',
33
+ 'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
34
+ this.class(),
35
+ ),
36
+ );
37
+ }
@@ -0,0 +1,30 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * FieldTitle component - title text inside FieldContent.
6
+ *
7
+ * @example
8
+ * <FieldContent>
9
+ * <FieldTitle>Two-factor authentication</FieldTitle>
10
+ * <FieldDescription>Add an extra layer of security.</FieldDescription>
11
+ * </FieldContent>
12
+ */
13
+ @Component({
14
+ selector: 'FieldTitle',
15
+ template: `<ng-content />`,
16
+ host: {
17
+ 'attr.data-slot': '"field-title"',
18
+ '[class]': 'computedClass()',
19
+ },
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ })
22
+ export class FieldTitle {
23
+ /** Additional CSS classes to apply */
24
+ readonly class = input<string>('');
25
+
26
+ /** Computed class combining base styles and custom classes */
27
+ protected readonly computedClass = computed(() =>
28
+ cn('flex w-fit items-center gap-2 text-sm leading-snug font-medium', this.class()),
29
+ );
30
+ }
@@ -0,0 +1,66 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ /**
6
+ * Field variants using class-variance-authority.
7
+ * Matches shadcn/ui React field exactly.
8
+ */
9
+ export const fieldVariants = cva('group/field flex w-full gap-3 data-[invalid=true]:text-destructive', {
10
+ variants: {
11
+ orientation: {
12
+ vertical: 'flex-col [&>*]:w-full [&>.sr-only]:w-auto',
13
+ horizontal: 'flex-row items-center [&>[data-slot=field-label]]:flex-auto',
14
+ responsive:
15
+ 'flex-col [&>*]:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto',
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ orientation: 'vertical',
20
+ },
21
+ });
22
+
23
+ export type FieldVariants = VariantProps<typeof fieldVariants>;
24
+ export type FieldOrientation = 'vertical' | 'horizontal' | 'responsive';
25
+
26
+ /**
27
+ * Field component - wraps a single form field with its label,
28
+ * control, description and error message.
29
+ *
30
+ * @example
31
+ * <!-- Vertical (default) -->
32
+ * <Field>
33
+ * <FieldLabel htmlFor="email">Email</FieldLabel>
34
+ * <input Input id="email" type="email" />
35
+ * <FieldDescription>We never share your email.</FieldDescription>
36
+ * </Field>
37
+ *
38
+ * <!-- Horizontal -->
39
+ * <Field orientation="horizontal">
40
+ * <FieldLabel htmlFor="newsletter">Subscribe to newsletter</FieldLabel>
41
+ * <Switch id="newsletter" />
42
+ * </Field>
43
+ */
44
+ @Component({
45
+ selector: 'Field',
46
+ template: `<ng-content />`,
47
+ host: {
48
+ 'attr.data-slot': '"field"',
49
+ role: 'group',
50
+ '[attr.data-orientation]': 'orientation()',
51
+ '[class]': 'computedClass()',
52
+ },
53
+ changeDetection: ChangeDetectionStrategy.OnPush,
54
+ })
55
+ export class Field {
56
+ /** Layout orientation of the field */
57
+ readonly orientation = input<FieldOrientation>('vertical');
58
+
59
+ /** Additional CSS classes to apply */
60
+ readonly class = input<string>('');
61
+
62
+ /** Computed class combining base styles, variants and custom classes */
63
+ protected readonly computedClass = computed(() =>
64
+ cn(fieldVariants({ orientation: this.orientation() }), this.class()),
65
+ );
66
+ }
@@ -0,0 +1,15 @@
1
+ export { FieldContent } from './field-content.component';
2
+ export { FieldDescription } from './field-description.component';
3
+ export { FieldError } from './field-error.component';
4
+ export { FieldGroup } from './field-group.component';
5
+ export { FieldLabel } from './field-label.component';
6
+ export { FieldLegend, type FieldLegendVariant } from './field-legend.component';
7
+ export { FieldSeparator } from './field-separator.component';
8
+ export { FieldSet } from './field-set.component';
9
+ export { FieldTitle } from './field-title.component';
10
+ export {
11
+ Field,
12
+ fieldVariants,
13
+ type FieldOrientation,
14
+ type FieldVariants,
15
+ } from './field.component';
@@ -0,0 +1,21 @@
1
+ export { ItemActions } from './item-actions.component';
2
+ export { ItemContent } from './item-content.component';
3
+ export { ItemDescription } from './item-description.component';
4
+ export { ItemFooter } from './item-footer.component';
5
+ export { ItemGroup } from './item-group.component';
6
+ export { ItemHeader } from './item-header.component';
7
+ export {
8
+ ItemMedia,
9
+ itemMediaVariants,
10
+ type ItemMediaVariant,
11
+ type ItemMediaVariants,
12
+ } from './item-media.component';
13
+ export { ItemSeparator } from './item-separator.component';
14
+ export { ItemTitle } from './item-title.component';
15
+ export {
16
+ Item,
17
+ itemVariants,
18
+ type ItemSize,
19
+ type ItemVariant,
20
+ type ItemVariants,
21
+ } from './item.component';
@@ -0,0 +1,29 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemActions component - trailing actions (buttons, icons) for an Item.
6
+ *
7
+ * @example
8
+ * <ItemActions>
9
+ * <Button variant="outline" size="sm">Open</Button>
10
+ * </ItemActions>
11
+ */
12
+ @Component({
13
+ selector: 'ItemActions',
14
+ template: `<ng-content />`,
15
+ host: {
16
+ 'attr.data-slot': '"item-actions"',
17
+ '[class]': 'computedClass()',
18
+ },
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ })
21
+ export class ItemActions {
22
+ /** Additional CSS classes to apply */
23
+ readonly class = input<string>('');
24
+
25
+ /** Computed class combining base styles and custom classes */
26
+ protected readonly computedClass = computed(() =>
27
+ cn('flex items-center gap-2', this.class()),
28
+ );
29
+ }
@@ -0,0 +1,31 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemContent component - main content area of an Item, holding
6
+ * the title and description.
7
+ *
8
+ * @example
9
+ * <ItemContent>
10
+ * <ItemTitle>Title</ItemTitle>
11
+ * <ItemDescription>Description</ItemDescription>
12
+ * </ItemContent>
13
+ */
14
+ @Component({
15
+ selector: 'ItemContent',
16
+ template: `<ng-content />`,
17
+ host: {
18
+ 'attr.data-slot': '"item-content"',
19
+ '[class]': 'computedClass()',
20
+ },
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ })
23
+ export class ItemContent {
24
+ /** Additional CSS classes to apply */
25
+ readonly class = input<string>('');
26
+
27
+ /** Computed class combining base styles and custom classes */
28
+ protected readonly computedClass = computed(() =>
29
+ cn('flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none', this.class()),
30
+ );
31
+ }
@@ -0,0 +1,30 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemDescription component - secondary description text for an Item.
6
+ *
7
+ * @example
8
+ * <ItemDescription>A short summary of the item.</ItemDescription>
9
+ */
10
+ @Component({
11
+ selector: 'ItemDescription',
12
+ template: `<ng-content />`,
13
+ host: {
14
+ 'attr.data-slot': '"item-description"',
15
+ '[class]': 'computedClass()',
16
+ },
17
+ changeDetection: ChangeDetectionStrategy.OnPush,
18
+ })
19
+ export class ItemDescription {
20
+ /** Additional CSS classes to apply */
21
+ readonly class = input<string>('');
22
+
23
+ /** Computed class combining base styles and custom classes */
24
+ protected readonly computedClass = computed(() =>
25
+ cn(
26
+ 'text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance',
27
+ this.class(),
28
+ ),
29
+ );
30
+ }
@@ -0,0 +1,30 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemFooter component - full-width footer row inside an Item.
6
+ *
7
+ * @example
8
+ * <Item>
9
+ * <ItemContent>...</ItemContent>
10
+ * <ItemFooter>Footer content</ItemFooter>
11
+ * </Item>
12
+ */
13
+ @Component({
14
+ selector: 'ItemFooter',
15
+ template: `<ng-content />`,
16
+ host: {
17
+ 'attr.data-slot': '"item-footer"',
18
+ '[class]': 'computedClass()',
19
+ },
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ })
22
+ export class ItemFooter {
23
+ /** Additional CSS classes to apply */
24
+ readonly class = input<string>('');
25
+
26
+ /** Computed class combining base styles and custom classes */
27
+ protected readonly computedClass = computed(() =>
28
+ cn('flex basis-full items-center justify-between gap-2', this.class()),
29
+ );
30
+ }
@@ -0,0 +1,32 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemGroup component - container that stacks multiple Item components.
6
+ *
7
+ * @example
8
+ * <ItemGroup>
9
+ * <Item>...</Item>
10
+ * <ItemSeparator />
11
+ * <Item>...</Item>
12
+ * </ItemGroup>
13
+ */
14
+ @Component({
15
+ selector: 'ItemGroup',
16
+ template: `<ng-content />`,
17
+ host: {
18
+ 'attr.data-slot': '"item-group"',
19
+ role: 'list',
20
+ '[class]': 'computedClass()',
21
+ },
22
+ changeDetection: ChangeDetectionStrategy.OnPush,
23
+ })
24
+ export class ItemGroup {
25
+ /** Additional CSS classes to apply */
26
+ readonly class = input<string>('');
27
+
28
+ /** Computed class combining base styles and custom classes */
29
+ protected readonly computedClass = computed(() =>
30
+ cn('group/item-group flex flex-col', this.class()),
31
+ );
32
+ }
@@ -0,0 +1,30 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+
4
+ /**
5
+ * ItemHeader component - full-width header row inside an Item.
6
+ *
7
+ * @example
8
+ * <Item>
9
+ * <ItemHeader>Header content</ItemHeader>
10
+ * <ItemContent>...</ItemContent>
11
+ * </Item>
12
+ */
13
+ @Component({
14
+ selector: 'ItemHeader',
15
+ template: `<ng-content />`,
16
+ host: {
17
+ 'attr.data-slot': '"item-header"',
18
+ '[class]': 'computedClass()',
19
+ },
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ })
22
+ export class ItemHeader {
23
+ /** Additional CSS classes to apply */
24
+ readonly class = input<string>('');
25
+
26
+ /** Computed class combining base styles and custom classes */
27
+ protected readonly computedClass = computed(() =>
28
+ cn('flex basis-full items-center justify-between gap-2', this.class()),
29
+ );
30
+ }