Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@
"printWidth": 120,
"arrowParens": "avoid",
"htmlWhitespaceSensitivity": "ignore",
"plugins": ["prettier-plugin-tailwindcss"]
"plugins": ["prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
}
23 changes: 14 additions & 9 deletions apps/web/public/components/card/demo/default.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
```angular-ts showLineNumbers copyButton
import { Component } from '@angular/core';

import { generateId } from '../../../utils/merge-classes';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardCardComponent } from '../card.component';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardCardComponent } from '@/shared/components/card/card.component';
import { generateId } from '@/shared/utils/merge-classes';

@Component({
selector: 'z-demo-card-default',
imports: [ZardCardComponent, ZardButtonComponent],
standalone: true,
template: `
<z-card
class="w-full max-w-sm"
class="w-full md:w-94"
zTitle="Login to your account"
zDescription="Enter your email below to login to your account"
zAction="Sign Up"
(zActionClick)="onActionClick()"
>
<div class="space-y-4">
<div class="space-y-2">
Expand Down Expand Up @@ -48,17 +49,21 @@ import { ZardCardComponent } from '../card.component';
required
/>
</div>
<div class="space-y-2">
<z-button zType="default" class="w-full">Login</z-button>
<z-button zType="outline" class="w-full">Login with Google</z-button>
</div>
</div>
<div card-footer class="flex w-full flex-col gap-2">
<z-button zType="default">Login</z-button>
<z-button zType="outline">Login with Google</z-button>
</div>
</z-card>
`,
})
export class ZardDemoCardDefaultComponent {
protected readonly idEmail = generateId('email');
protected readonly idPassword = generateId('password');

protected onActionClick(): void {
alert('Redirect to Sign Up');
}
}

```
21 changes: 15 additions & 6 deletions apps/web/public/components/card/doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

## [z-card] <span class="api-type-label component">Component</span>

> z-card is a component that provides a structured container for displaying content with optional header and body sections.
> z-card is a component that provides a structured container for displaying content with body section and optional header and footer sections.

| Property | Description | Type | Default |
| ---------------- | --------------------------------------------- | ----------------------------- | ------- |
| `[zTitle]` | Card title content (string or template) | `string \| TemplateRef<void>` | - |
| `[zDescription]` | Card description content (string or template) | `string \| TemplateRef<void>` | - |
| `[class]` | Custom css classes | `ClassValue` | `''` |
| Property | Description | Type | Default |
| ----------------- | ------------------------- | ------------------------------------------ | ------- |
| `[class]` | Custom css classes | `ClassValue` | `''` |
| `[zAction]` | Card action button¹ | `string` | `''` |
| `[zDescription]` | Card description content¹ | `string \| TemplateRef<void> \| undefined` | - |
| `[zFooterBorder]` | Card footer with border | `boolean` | `false` |
| `[zHeaderBorder]` | Card header with border¹ | `boolean` | `false` |
| `[zTitle]` | Card title content | `string \| TemplateRef<void> \| undefined` | - |
| `(zActionClick)` | Emits action button click | `void` | - |

¹ `zTitle` is required to have header.

🚨Note: optional card footer content is defined with selector **card-footer**\
(eg. `<div card-footer> </div>`)
107 changes: 78 additions & 29 deletions apps/web/public/installation/manual/card.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,86 +6,135 @@ import {
Component,
computed,
input,
output,
type TemplateRef,
ViewEncapsulation,
} from '@angular/core';

import type { ClassValue } from 'clsx';

import { cardBodyVariants, cardHeaderVariants, cardVariants } from './card.variants';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardStringTemplateOutletDirective } from '@/shared/core/directives/string-template-outlet/string-template-outlet.directive';
import { mergeClasses } from '@/shared/utils/merge-classes';
import { generateId, mergeClasses } from '@/shared/utils/merge-classes';

import { cardBodyVariants, cardFooterVariants, cardHeaderVariants, cardVariants } from './card.variants';

@Component({
selector: 'z-card',
imports: [ZardStringTemplateOutletDirective],
standalone: true,
imports: [ZardStringTemplateOutletDirective, ZardButtonComponent],
template: `
@if (zTitle()) {
<div [class]="headerClasses()">
<div class="text-2xl leading-none font-semibold tracking-tight">
<ng-container *zStringTemplateOutlet="zTitle()">{{ zTitle() }}</ng-container>
@let title = zTitle();
@if (title) {
<div [class]="headerClasses()" data-slot="card-header">
<div class="leading-none font-semibold" [id]="titleId" data-slot="card-title">
<ng-container *zStringTemplateOutlet="title">{{ title }}</ng-container>
</div>

@if (zDescription()) {
<div class="text-muted-foreground text-sm">
<ng-container *zStringTemplateOutlet="zDescription()">{{ zDescription() }}</ng-container>
@let description = zDescription();
@if (description) {
<div class="text-muted-foreground text-sm" [id]="descriptionId" data-slot="card-description">
<ng-container *zStringTemplateOutlet="description">{{ description }}</ng-container>
</div>
}

@let action = zAction();
@if (action) {
<button
z-button
type="button"
zType="link"
class="col-start-2 row-span-2 row-start-1 self-start justify-self-end"
data-slot="card-action"
(click)="onClick()"
>
{{ action }}
</button>
}
</div>
}

<div [class]="bodyClasses()">
<div [class]="bodyClasses()" data-slot="card-content">
<ng-content />
</div>

<div [class]="footerClasses()" data-slot="card-footer">
<ng-content select="[card-footer]" />
</div>
`,
styles: `
[data-slot='card-footer']:empty {
display: none;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'data-slot': 'card',
'[class]': 'classes()',
'[attr.aria-labelledby]': 'zTitle() ? titleId : null',
'[attr.aria-describedby]': 'zDescription() ? descriptionId : null',
},
exportAs: 'zCard',
})
export class ZardCardComponent {
readonly zTitle = input<string | TemplateRef<void>>();
readonly class = input<ClassValue>('');
readonly zFooterBorder = input(false);
readonly zHeaderBorder = input(false);
readonly zAction = input('');
readonly zDescription = input<string | TemplateRef<void>>();
readonly zTitle = input<string | TemplateRef<void>>();

readonly class = input<ClassValue>('');
readonly zActionClick = output<void>();

protected readonly titleId = generateId('card-title');
protected readonly descriptionId = generateId('card-description');

protected readonly classes = computed(() => mergeClasses(cardVariants(), this.class()));
protected readonly headerClasses = computed(() => mergeClasses(cardHeaderVariants()));
protected readonly bodyClasses = computed(() => mergeClasses(cardBodyVariants()));
protected readonly footerClasses = computed(() =>
mergeClasses(cardFooterVariants(), this.zFooterBorder() ? 'border-t' : ''),
);

protected readonly headerClasses = computed(() =>
mergeClasses(cardHeaderVariants(), this.zHeaderBorder() ? 'border-b' : ''),
);

protected onClick(): void {
this.zActionClick.emit();
}
}

```



```angular-ts title="card.variants.ts" expandable="true" expandableTitle="Expand" copyButton showLineNumbers
import { cva, type VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';

import { mergeClasses } from '@/shared/utils/merge-classes';

export const cardVariants = cva('bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm');

export const cardHeaderVariants = cva(
mergeClasses(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6',
'has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
),
);

export const cardVariants = cva('block rounded-lg border bg-card text-card-foreground shadow-sm w-full p-6', {
variants: {},
});
export type ZardCardVariants = VariantProps<typeof cardVariants>;
export const cardActionVariants = cva('col-start-2 row-span-2 row-start-1 self-start justify-self-end');

export const cardHeaderVariants = cva('w-full flex flex-col space-y-1.5 pb-0 gap-1.5', {
variants: {},
});
export type ZardCardHeaderVariants = VariantProps<typeof cardHeaderVariants>;
export const cardBodyVariants = cva('px-6');

export const cardBodyVariants = cva('w-full block mt-6', {
variants: {},
});
export type ZardCardBodyVariants = VariantProps<typeof cardBodyVariants>;
export const cardFooterVariants = cva('flex flex-col gap-2 items-center px-6 [.border-t]:pt-6');

```



```angular-ts title="index.ts" expandable="true" expandableTitle="Expand" copyButton showLineNumbers
export * from './card.component';
export * from './card.variants';

```

Loading