diff --git a/package-lock.json b/package-lock.json
index 897d7e2..5529199 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,10 +21,11 @@
"@ng-icons/bootstrap-icons": "^32.1.0",
"@ng-icons/core": "^32.1.0",
"@ng-icons/lucide": "^32.1.0",
+ "@ng-icons/tabler-icons": "^32.1.0",
"@slateui/theme": "^1.0.0",
"@tailwindcss/postcss": "^4.1.11",
"express": "^5.1.0",
- "ng-primitives": "^0.74.0",
+ "ng-primitives": "^0.80.0",
"ngx-highlightjs": "^14.0.1",
"postcss": "^8.5.6",
"rxjs": "~7.8.0",
@@ -758,9 +759,9 @@
}
},
"node_modules/@angular/ssr": {
- "version": "20.1.3",
- "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-20.1.3.tgz",
- "integrity": "sha512-ZXTUlLC6iC4Tl7uY3ACaS5+1JC6bnbByk/y0agnCXjHN/5qogIR44ECQDPlLmPZfC1dZaxF4d0WSge9OZdeu4Q==",
+ "version": "20.3.2",
+ "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-20.3.2.tgz",
+ "integrity": "sha512-IMSl4wUM1297HmAB+iT72hbku3mP7uswV/EoE87oK7txvQExSNEFtO/8NJbgBrdFOLhG0JH+axqfwZo1vfBJAA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
@@ -1530,6 +1531,16 @@
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT"
},
+ "node_modules/@inquirer/ansi": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz",
+ "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@inquirer/checkbox": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz",
@@ -1578,15 +1589,15 @@
}
},
"node_modules/@inquirer/core": {
- "version": "10.1.15",
- "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz",
- "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz",
+ "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@inquirer/ansi": "^1.0.0",
"@inquirer/figures": "^1.0.13",
"@inquirer/type": "^3.0.8",
- "ansi-escapes": "^4.3.2",
"cli-width": "^4.1.0",
"mute-stream": "^2.0.0",
"signal-exit": "^4.1.0",
@@ -1606,15 +1617,15 @@
}
},
"node_modules/@inquirer/editor": {
- "version": "4.2.15",
- "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz",
- "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==",
+ "version": "4.2.20",
+ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz",
+ "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@inquirer/core": "^10.1.15",
- "@inquirer/type": "^3.0.8",
- "external-editor": "^3.1.0"
+ "@inquirer/core": "^10.2.2",
+ "@inquirer/external-editor": "^1.0.2",
+ "@inquirer/type": "^3.0.8"
},
"engines": {
"node": ">=18"
@@ -1651,6 +1662,45 @@
}
}
},
+ "node_modules/@inquirer/external-editor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz",
+ "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.0",
+ "iconv-lite": "^0.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor/node_modules/iconv-lite": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+ "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/@inquirer/figures": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz",
@@ -2595,6 +2645,15 @@
"tslib": "^2.3.0"
}
},
+ "node_modules/@ng-icons/tabler-icons": {
+ "version": "32.1.0",
+ "resolved": "https://registry.npmjs.org/@ng-icons/tabler-icons/-/tabler-icons-32.1.0.tgz",
+ "integrity": "sha512-zqUaJcpllT7kl2UYExoO6oTnLtaQweSKs2wBK4xRsPlhwaU8RjbmZFdYLfa/IAEvLveD5D1i0orIPepu55ESqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ }
+ },
"node_modules/@npmcli/agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz",
@@ -4729,9 +4788,9 @@
}
},
"node_modules/chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz",
+ "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==",
"dev": true,
"license": "MIT"
},
@@ -5895,34 +5954,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/external-editor/node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7354,16 +7385,6 @@
"node": ">=8"
}
},
- "node_modules/karma/node_modules/tmp": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
- "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.14"
- }
- },
"node_modules/karma/node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -8450,9 +8471,9 @@
}
},
"node_modules/ng-primitives": {
- "version": "0.74.0",
- "resolved": "https://registry.npmjs.org/ng-primitives/-/ng-primitives-0.74.0.tgz",
- "integrity": "sha512-Hw7qn7F8FriJCkO/QWTPWdvOKgsQABXfhiHqa/lt1vwgiDqoSrUVTdI0Jry9M3IuY8AoWJ/0wzq37HX98nQNWA==",
+ "version": "0.80.0",
+ "resolved": "https://registry.npmjs.org/ng-primitives/-/ng-primitives-0.80.0.tgz",
+ "integrity": "sha512-G8tlaVpn1Clq2dcM+CCDQ6cr5r9bJTXKoPSuR3eTJ1SjOPICJxGAakkhthtcAJAU9doebUFiUYIgI1352gK9Mg==",
"license": "Apache-2.0",
"dependencies": {
"@phenomnomnominal/tsquery": "6.1.3",
@@ -8870,16 +8891,6 @@
"license": "MIT",
"optional": true
},
- "node_modules/os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/p-map": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
@@ -10460,16 +10471,13 @@
}
},
"node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
"engines": {
- "node": ">=0.6.0"
+ "node": ">=14.14"
}
},
"node_modules/to-regex-range": {
diff --git a/package.json b/package.json
index ed3b4fc..eab97aa 100644
--- a/package.json
+++ b/package.json
@@ -36,10 +36,11 @@
"@ng-icons/bootstrap-icons": "^32.1.0",
"@ng-icons/core": "^32.1.0",
"@ng-icons/lucide": "^32.1.0",
+ "@ng-icons/tabler-icons": "^32.1.0",
"@slateui/theme": "^1.0.0",
"@tailwindcss/postcss": "^4.1.11",
"express": "^5.1.0",
- "ng-primitives": "^0.74.0",
+ "ng-primitives": "^0.80.0",
"ngx-highlightjs": "^14.0.1",
"postcss": "^8.5.6",
"rxjs": "~7.8.0",
diff --git a/projects/docs/src/app/pages/docs/components/alert/alert.variants.ts b/projects/docs/src/app/pages/docs/components/alert/alert.variants.ts
index fd62b22..d81fc0c 100644
--- a/projects/docs/src/app/pages/docs/components/alert/alert.variants.ts
+++ b/projects/docs/src/app/pages/docs/components/alert/alert.variants.ts
@@ -8,12 +8,12 @@ import { lucideCircleAlert } from '@ng-icons/lucide';
@Component({
selector: 'alert-default-example',
template: `
-
-
Heads up!
-
+
+ Heads up!
+
You can add components to your app using the CLI.
-
-
+
+
`,
imports: [UiAlert, UiAlertTitle, UiAlertDescription]
})
@@ -22,12 +22,12 @@ export class AlertDefaultExample {}
@Component({
selector: 'alert-destructive-example',
template: `
-
-
Error
-
+
+ Error
+
Your session has expired. Please log in again.
-
-
+
+
`,
imports: [UiAlert, UiAlertTitle, UiAlertDescription]
})
@@ -36,13 +36,13 @@ export class AlertDestructiveExample {}
@Component({
selector: 'alert-with-icon-example',
template: `
-
+
- Well done!
-
+ Well done!
+
You successfully read this important alert message.
-
-
+
+
`,
providers: [provideIcons({ lucideCircleAlert })],
imports: [UiAlert, UiAlertTitle, UiAlertDescription, NgIcon]
@@ -54,11 +54,11 @@ export const alertMeta: IComponentMeta = {
description: 'Displays a callout for user attention.',
installation: {
package: 'alert',
- import: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/directives/alert';`,
- usage: `
-
Alert Title
-
Alert description
-
`
+ import: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/alert';`,
+ usage: `
+ Alert Title
+ Alert description
+`
},
api: {
props: [
@@ -81,17 +81,17 @@ export const alertVariants: IVariant[] = [
{
title: 'Default',
description: 'The default alert appearance.',
- code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/directives/alert';
+ code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/alert';
@Component({
selector: 'alert-default-example',
template: \`
-
-
Heads up!
-
+
+ Heads up!
+
You can add components to your app using the CLI.
-
-
+
+
\`,
imports: [UiAlert, UiAlertTitle, UiAlertDescription]
})
@@ -101,17 +101,17 @@ export class AlertDefaultExample {}`,
{
title: 'Destructive',
description: 'An alert for error messages.',
- code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/directives/alert';
+ code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/alert';
@Component({
selector: 'alert-destructive-example',
template: \`
-
-
Error
-
+
+ Error
+
Your session has expired. Please log in again.
-
-
+
+
\`,
imports: [UiAlert, UiAlertTitle, UiAlertDescription]
})
@@ -121,20 +121,20 @@ export class AlertDestructiveExample {}`,
{
title: 'With Icon',
description: 'An alert with an icon.',
- code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/directives/alert';
+ code: `import { UiAlert, UiAlertTitle, UiAlertDescription } from '@workspace/ui/alert';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideCircleAlert } from '@ng-icons/lucide';
@Component({
selector: 'alert-with-icon-example',
template: \`
-
+
- Well done!
-
+ Well done!
+
You successfully read this important alert message.
-
-
+
+
\`,
providers: [provideIcons({ lucideCircleAlert })],
imports: [UiAlert, UiAlertTitle, UiAlertDescription, NgIcon]
diff --git a/projects/docs/src/app/pages/docs/components/avatar/avatar.variants.ts b/projects/docs/src/app/pages/docs/components/avatar/avatar.variants.ts
index c70c9a6..a3c210d 100644
--- a/projects/docs/src/app/pages/docs/components/avatar/avatar.variants.ts
+++ b/projects/docs/src/app/pages/docs/components/avatar/avatar.variants.ts
@@ -5,10 +5,10 @@ import { IVariant, IComponentMeta } from '@components/component-preview/componen
@Component({
selector: 'avatar-default-example',
template: `
-
-

-
AB
-
+
+
+ AB
+
`,
imports: [UiAvatar, UiAvatarImage, UiAvatarFallback]
})
@@ -17,10 +17,10 @@ export class AvatarDefaultExample {}
@Component({
selector: 'avatar-fallback-delay-example',
template: `
-
-

-
AB
-
+
+
+ AB
+
`,
imports: [UiAvatar, UiAvatarImage, UiAvatarFallback]
})
@@ -31,16 +31,16 @@ export const avatarMeta: IComponentMeta = {
description: 'An image representation of a user or entity with a fallback.',
installation: {
package: 'avatar',
- import: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/directives/avatar';`,
- usage: `
-

-
AB
-
`
+ import: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/avatar';`,
+ usage: `
+
+ AB
+`
},
api: {
props: [
{ name: 'class', type: 'string', description: 'Additional CSS classes for the avatar container.' },
- { name: 'uiAvatarFallbackDelay', type: 'number', default: '0', description: 'Delay in ms before showing fallback.' }
+ { name: 'delay', type: 'number', default: '0', description: 'Delay in ms before showing fallback.' }
]
}
};
@@ -49,15 +49,15 @@ export const avatarVariants: IVariant[] = [
{
title: 'Default',
description: 'Avatar with image and fallback content.',
- code: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/directives/avatar';
+ code: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/avatar';
@Component({
selector: 'avatar-default-example',
template: \`
-
-

-
AB
-
+
+
+ AB
+
\`,
imports: [UiAvatar, UiAvatarImage, UiAvatarFallback]
})
@@ -67,15 +67,15 @@ export class AvatarDefaultExample {}`,
{
title: 'Fallback delay',
description: 'Show fallback after a delay when image fails to load.',
- code: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/directives/avatar';
+ code: `import { UiAvatar, UiAvatarImage, UiAvatarFallback } from '@workspace/ui/avatar';
@Component({
selector: 'avatar-fallback-delay-example',
template: \`
-
-

-
AB
-
+
+
+ AB
+
\`,
imports: [UiAvatar, UiAvatarImage, UiAvatarFallback]
})
diff --git a/projects/docs/src/app/pages/docs/components/badge/badge.variants.ts b/projects/docs/src/app/pages/docs/components/badge/badge.variants.ts
index d892d1b..51b4658 100644
--- a/projects/docs/src/app/pages/docs/components/badge/badge.variants.ts
+++ b/projects/docs/src/app/pages/docs/components/badge/badge.variants.ts
@@ -4,28 +4,28 @@ import { IVariant, IComponentMeta } from '@components/component-preview/componen
@Component({
selector: 'badge-default-example',
- template: `Default`,
+ template: `Default`,
imports: [UiBadge]
})
export class BadgeDefaultExample {}
@Component({
selector: 'badge-secondary-example',
- template: `Secondary`,
+ template: `Secondary`,
imports: [UiBadge]
})
export class BadgeSecondaryExample {}
@Component({
selector: 'badge-destructive-example',
- template: `Destructive`,
+ template: `Destructive`,
imports: [UiBadge]
})
export class BadgeDestructiveExample {}
@Component({
selector: 'badge-outline-example',
- template: `Outline`,
+ template: `Outline`,
imports: [UiBadge]
})
export class BadgeOutlineExample {}
@@ -35,8 +35,8 @@ export const badgeMeta: IComponentMeta = {
description: 'Displays a badge for labelling content.',
installation: {
package: 'badge',
- import: `import { UiBadge } from '@workspace/ui/directives/badge';`,
- usage: `Badge`
+ import: `import { UiBadge } from '@workspace/ui/badge';`,
+ usage: `Badge`
},
api: {
props: [
@@ -49,11 +49,11 @@ export const badgeMeta: IComponentMeta = {
export const badgeVariants: IVariant[] = [
{
title: 'Default',
- code: `import { UiBadge } from '@workspace/ui/directives/badge';
+ code: `import { UiBadge } from '@workspace/ui/badge';
@Component({
selector: 'badge-default-example',
- template: \`Default\`,
+ template: \`Default\`,
imports: [UiBadge]
})
export class BadgeDefaultExample {}`,
@@ -61,11 +61,11 @@ export class BadgeDefaultExample {}`,
},
{
title: 'Secondary',
- code: `import { UiBadge } from '@workspace/ui/directives/badge';
+ code: `import { UiBadge } from '@workspace/ui/badge';
@Component({
selector: 'badge-secondary-example',
- template: \`Secondary\`,
+ template: \`Secondary\`,
imports: [UiBadge]
})
export class BadgeSecondaryExample {}`,
@@ -73,11 +73,11 @@ export class BadgeSecondaryExample {}`,
},
{
title: 'Destructive',
- code: `import { UiBadge } from '@workspace/ui/directives/badge';
+ code: `import { UiBadge } from '@workspace/ui/badge';
@Component({
selector: 'badge-destructive-example',
- template: \`Destructive\`,
+ template: \`Destructive\`,
imports: [UiBadge]
})
export class BadgeDestructiveExample {}`,
@@ -85,11 +85,11 @@ export class BadgeDestructiveExample {}`,
},
{
title: 'Outline',
- code: `import { UiBadge } from '@workspace/ui/directives/badge';
+ code: `import { UiBadge } from '@workspace/ui/badge';
@Component({
selector: 'badge-outline-example',
- template: \`Outline\`,
+ template: \`Outline\`,
imports: [UiBadge]
})
export class BadgeOutlineExample {}`,
diff --git a/projects/docs/src/app/pages/docs/components/components.routes.ts b/projects/docs/src/app/pages/docs/components/components.routes.ts
index 4700696..bbd99c0 100644
--- a/projects/docs/src/app/pages/docs/components/components.routes.ts
+++ b/projects/docs/src/app/pages/docs/components/components.routes.ts
@@ -104,6 +104,10 @@ export const routes: Routes = [
path: 'switch',
loadComponent: () => import('./switch/switch').then(m => m.Switch)
},
+ {
+ path: 'radio-group',
+ loadComponent: () => import('./radio-group/radio-group').then(m => m.RadioGroup)
+ },
{
path: '',
redirectTo: 'alert',
diff --git a/projects/docs/src/app/pages/docs/components/radio-group/radio-group.ts b/projects/docs/src/app/pages/docs/components/radio-group/radio-group.ts
new file mode 100644
index 0000000..b6cc8f7
--- /dev/null
+++ b/projects/docs/src/app/pages/docs/components/radio-group/radio-group.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+import { ComponentPreview } from '@components/component-preview/component-preview';
+import { radioGroupVariants, radioGroupMeta } from './radio-group.variants';
+
+@Component({
+ selector: 'docs-radio-group',
+ standalone: true,
+ imports: [ComponentPreview],
+ template: `
+
+
+ `
+})
+export class RadioGroup {
+ radioGroupMeta = radioGroupMeta;
+ radioGroupVariants = radioGroupVariants;
+}
diff --git a/projects/docs/src/app/pages/docs/components/radio-group/radio-group.variants.ts b/projects/docs/src/app/pages/docs/components/radio-group/radio-group.variants.ts
new file mode 100644
index 0000000..d85b371
--- /dev/null
+++ b/projects/docs/src/app/pages/docs/components/radio-group/radio-group.variants.ts
@@ -0,0 +1,344 @@
+import { Component, model } from '@angular/core';
+import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from 'ui';
+import { IVariant, IComponentMeta } from '@components/component-preview/component-preview';
+
+// Radio Group example components for dynamic rendering
+@Component({
+ selector: 'radio-group-default-example',
+ template: `
+
+ `,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupDefaultExample {
+ selectedValue = model('comfortable');
+}
+
+@Component({
+ selector: 'radio-group-disabled-example',
+ template: `
+
+ `,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupDisabledExample {
+ selectedValue = model('option1');
+}
+
+@Component({
+ selector: 'radio-group-horizontal-example',
+ template: `
+
+ `,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupHorizontalExample {
+ selectedValue = model('yes');
+}
+
+@Component({
+ selector: 'radio-group-form-example',
+ template: `
+
+ `,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupFormExample {
+ notificationType = model('all');
+
+ onSubmit() {
+ console.log('Selected:', this.notificationType());
+ }
+}
+
+export const radioGroupMeta: IComponentMeta = {
+ title: 'Radio Group',
+ description: 'A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.',
+ installation: {
+ package: 'radio-group',
+ import: `import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from '@workspace/ui/directives/radio-group';`,
+ usage: ``
+ },
+ api: {
+ props: [
+ {
+ name: 'defaultValue',
+ type: 'string',
+ default: 'undefined',
+ description: 'The value of the selected radio button.'
+ },
+ {
+ name: 'disabled',
+ type: 'boolean',
+ default: 'false',
+ description: 'Whether the radio group is disabled.'
+ },
+ {
+ name: 'orientation',
+ type: '"vertical" | "horizontal"',
+ default: '"vertical"',
+ description: 'The orientation of the radio group.'
+ },
+ {
+ name: 'class',
+ type: 'string',
+ description: 'Additional CSS classes to apply to the radio group.'
+ }
+ ],
+ outputs: [
+ {
+ name: 'defaultValueChange',
+ type: 'string',
+ description: 'Emitted when the selected value changes.'
+ }
+ ]
+ }
+};
+
+export const radioGroupVariants: IVariant[] = [
+ {
+ title: 'Default',
+ description: 'A basic radio group with multiple options.',
+ code: `import { Component, model } from '@angular/core';
+import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from '@workspace/ui/directives/radio-group';
+
+@Component({
+ selector: 'radio-group-default-example',
+ template: \`
+
+ \`,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupDefaultExample {
+ selectedValue = model('comfortable');
+}`,
+ component: RadioGroupDefaultExample
+ },
+ {
+ title: 'Disabled',
+ description: 'A disabled radio group where all options are non-interactive.',
+ code: `import { Component, model } from '@angular/core';
+import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from '@workspace/ui/directives/radio-group';
+
+@Component({
+ selector: 'radio-group-disabled-example',
+ template: \`
+
+ \`,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupDisabledExample {
+ selectedValue = model('option1');
+}`,
+ component: RadioGroupDisabledExample
+ },
+ {
+ title: 'Horizontal',
+ description: 'A radio group with horizontal orientation.',
+ code: `import { Component, model } from '@angular/core';
+import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from '@workspace/ui/directives/radio-group';
+
+@Component({
+ selector: 'radio-group-horizontal-example',
+ template: \`
+
+ \`,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupHorizontalExample {
+ selectedValue = model('yes');
+}`,
+ component: RadioGroupHorizontalExample
+ },
+ {
+ title: 'Form',
+ description: 'A radio group used in a form context with proper form integration.',
+ code: `import { Component, model } from '@angular/core';
+import { UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel } from '@workspace/ui/directives/radio-group';
+
+@Component({
+ selector: 'radio-group-form-example',
+ template: \`
+
+ \`,
+ imports: [UiRadioGroup, UiRadioItem, UiRadio, UiRadioIndicator, UiLabel]
+})
+export class RadioGroupFormExample {
+ notificationType = model('all');
+
+ onSubmit() {
+ console.log('Selected:', this.notificationType());
+ }
+}`,
+ component: RadioGroupFormExample
+ }
+];
diff --git a/projects/docs/src/app/pages/docs/introduction/introduction.html b/projects/docs/src/app/pages/docs/introduction/introduction.html
index f6c64e4..4505c0f 100644
--- a/projects/docs/src/app/pages/docs/introduction/introduction.html
+++ b/projects/docs/src/app/pages/docs/introduction/introduction.html
@@ -104,7 +104,7 @@ Responsive
-
+
diff --git a/projects/docs/src/app/shared/components/header/header.html b/projects/docs/src/app/shared/components/header/header.html
index 9df51d8..239fdc0 100644
--- a/projects/docs/src/app/shared/components/header/header.html
+++ b/projects/docs/src/app/shared/components/header/header.html
@@ -3,7 +3,7 @@
diff --git a/projects/docs/src/app/shared/components/header/header.ts b/projects/docs/src/app/shared/components/header/header.ts
index af91e7b..b82afcb 100644
--- a/projects/docs/src/app/shared/components/header/header.ts
+++ b/projects/docs/src/app/shared/components/header/header.ts
@@ -2,14 +2,14 @@ import { Component, inject, model } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
import { ThemeService } from '@slateui/theme';
import { NgIcon, provideIcons } from '@ng-icons/core';
-import { lucideMenu, lucideSun, lucideMoon } from '@ng-icons/lucide';
import { bootstrapGithub, bootstrapTwitterX } from '@ng-icons/bootstrap-icons'
+import { tablerBlur, tablerMenu } from '@ng-icons/tabler-icons';
import { UiButton } from 'ui';
@Component({
selector: 'docs-header',
imports: [NgIcon, UiButton, RouterLink],
- viewProviders: [provideIcons({ lucideMenu, lucideSun, lucideMoon, bootstrapGithub, bootstrapTwitterX })],
+ viewProviders: [provideIcons({ bootstrapGithub, bootstrapTwitterX, tablerBlur, tablerMenu })],
templateUrl: './header.html'
})
export class Header {
@@ -28,7 +28,5 @@ export class Header {
this.themeService.setTheme(
this.themeService.isDark() ? 'light' : 'dark'
);
- // The HighlightThemeService will automatically update the highlight.js theme
- // through its subscription to theme changes
}
}
diff --git a/projects/docs/src/app/shared/components/sidebar/sidebar.ts b/projects/docs/src/app/shared/components/sidebar/sidebar.ts
index 6357783..0db45c9 100644
--- a/projects/docs/src/app/shared/components/sidebar/sidebar.ts
+++ b/projects/docs/src/app/shared/components/sidebar/sidebar.ts
@@ -67,6 +67,7 @@ export class Sidebar {
{ name: 'Popover', path: 'popover' },
{ name: 'Progress', path: 'progress' },
// { name: 'Select', path: 'select' },
+ { name: 'Radio Group', path: 'radio-group' },
{ name: 'Separator', path: 'separator' },
{ name: 'Switch', path: 'switch' },
{ name: 'Tabs', path: 'tabs' },
diff --git a/projects/docs/src/index.html b/projects/docs/src/index.html
index 14e9a6a..04f6ca3 100644
--- a/projects/docs/src/index.html
+++ b/projects/docs/src/index.html
@@ -7,8 +7,8 @@
-
SlateUI - Modern UI Component Library for Angular | Built with Angular Primitives
-
+
SlateUI - Modern UI Components for Angular | Built with Angular Primitives
+
diff --git a/projects/ui/src/directives/alert-dialog.ts b/projects/ui/src/directives/alert-dialog.ts
index 8ce015a..ff18110 100644
--- a/projects/ui/src/directives/alert-dialog.ts
+++ b/projects/ui/src/directives/alert-dialog.ts
@@ -1,6 +1,6 @@
import { computed, Directive, input } from "@angular/core";
import { tv } from "tailwind-variants";
-import { NgpDialog, NgpDialogDescription, NgpDialogOverlay, NgpDialogTitle, NgpDialogTrigger } from "ng-primitives/dialog";
+import { NgpDialog, NgpDialogDescription, NgpDialogOverlay, NgpDialogTitle, NgpDialogTrigger, provideDialogConfig } from "ng-primitives/dialog";
const alertDialogVariants = tv({
slots: {
@@ -105,7 +105,8 @@ export class UiAlertDialogFooter {
'ngpDialogTriggerCloseOnEscape: uiAlertDialogTriggerCloseOnEscape'
]
}
- ]
+ ],
+ providers: [provideDialogConfig({ closeOnEscape: false })]
})
export class UiAlertDialogTrigger {
inputClass = input
('', { alias: 'class' });
@@ -125,7 +126,8 @@ export class UiAlertDialogTrigger {
'ngpDialogOverlayCloseOnClick: uiAlertDialogOverlayCloseOnClick'
]
}
- ]
+ ],
+ providers: [provideDialogConfig({ closeOnClick: false })]
})
export class UiAlertDialogOverlay {
inputClass = input('', { alias: 'class' });
diff --git a/projects/ui/src/directives/alert.ts b/projects/ui/src/directives/alert.ts
index 447b5b9..26c3083 100644
--- a/projects/ui/src/directives/alert.ts
+++ b/projects/ui/src/directives/alert.ts
@@ -1,4 +1,4 @@
-import { computed, Directive, input } from '@angular/core';
+import { computed, Component, ChangeDetectionStrategy, input } from '@angular/core';
import { tv, VariantProps } from 'tailwind-variants';
const alertVariants = tv({
@@ -22,12 +22,14 @@ const { alert, alertTitle, alertDescription } = alertVariants();
type AlertVariants = VariantProps;
-@Directive({
- selector: '[uiAlert]',
+@Component({
+ selector: 'ui-alert',
exportAs: 'uiAlert',
+ changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ''
})
export class UiAlert {
inputClass = input('', { alias: 'class' });
@@ -35,24 +37,28 @@ export class UiAlert {
computedClass = computed(() => alert({ variant: this.variant(), class: this.inputClass() }));
}
-@Directive({
- selector: '[uiAlertTitle]',
+@Component({
+ selector: 'ui-alert-title',
exportAs: 'uiAlertTitle',
+ changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ''
})
export class UiAlertTitle {
inputClass = input('', { alias: 'class' });
computedClass = computed(() => alertTitle({ class: this.inputClass() }));
}
-@Directive({
- selector: '[uiAlertDescription]',
+@Component({
+ selector: 'ui-alert-description',
exportAs: 'uiAlertDescription',
+ changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ''
})
export class UiAlertDescription {
inputClass = input('', { alias: 'class' });
diff --git a/projects/ui/src/directives/avatar.ts b/projects/ui/src/directives/avatar.ts
index fd02715..811db83 100644
--- a/projects/ui/src/directives/avatar.ts
+++ b/projects/ui/src/directives/avatar.ts
@@ -1,5 +1,4 @@
-import { NumberInput } from '@angular/cdk/coercion';
-import { computed, Directive, input, numberAttribute } from '@angular/core';
+import { Component, computed, input } from '@angular/core';
import { NgpAvatar, NgpAvatarFallback, NgpAvatarImage } from "ng-primitives/avatar";
import { tv } from "tailwind-variants";
@@ -13,48 +12,43 @@ const avatarVariants = tv({
const { avatar, avatarImage, avatarFallback } = avatarVariants();
-@Directive({
- selector: '[uiAvatar]',
+@Component({
+ selector: 'ui-avatar',
exportAs: 'uiAvatar',
hostDirectives: [NgpAvatar],
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ``
})
export class UiAvatar {
inputClass = input('', { alias: 'class' });
computedClass = computed(() => avatar({ class: this.inputClass() }));
}
-@Directive({
- selector: '[uiAvatarImage]',
+@Component({
+ selector: 'ui-avatar-image',
exportAs: 'uiAvatarImage',
- hostDirectives: [NgpAvatarImage],
- host: {
- '[class]': 'computedClass()'
- }
+ template: `
`,
+ imports: [NgpAvatarImage]
})
export class UiAvatarImage {
inputClass = input('', { alias: 'class' });
computedClass = computed(() => avatarImage({ class: this.inputClass() }));
+ src = input('', { alias: 'src' });
+ alt = input('', { alias: 'alt' });
}
-@Directive({
- selector: '[uiAvatarFallback]',
+@Component({
+ selector: 'ui-avatar-fallback',
exportAs: 'uiAvatarFallback',
- hostDirectives: [{
- directive: NgpAvatarFallback,
- inputs: ['ngpAvatarFallbackDelay: delay()'],
- }],
+ hostDirectives: [NgpAvatarFallback],
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ``,
})
export class UiAvatarFallback {
inputClass = input('', { alias: 'class' });
computedClass = computed(() => avatarFallback({ class: this.inputClass() }));
- readonly delay = input(0, {
- alias: 'uiAvatarFallbackDelay',
- transform: numberAttribute,
- });
}
\ No newline at end of file
diff --git a/projects/ui/src/directives/badge.ts b/projects/ui/src/directives/badge.ts
index 090c4e7..e0f16d6 100644
--- a/projects/ui/src/directives/badge.ts
+++ b/projects/ui/src/directives/badge.ts
@@ -1,4 +1,4 @@
-import { computed, Directive, input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
import { tv, VariantProps } from 'tailwind-variants';
const badgeVariants = tv({
@@ -18,15 +18,17 @@ const badgeVariants = tv({
type BadgeVariants = VariantProps;
-@Directive({
- selector: '[uiBadge]',
+@Component({
+ selector: 'ui-badge',
exportAs: 'uiBadge',
+ changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class]': 'computedClass()'
- }
+ },
+ template: ''
})
export class UiBadge {
inputClass = input('', { alias: 'class' });
variant = input('default');
computedClass = computed(() => badgeVariants({ variant: this.variant(), class: this.inputClass() }));
-}
+}
\ No newline at end of file
diff --git a/projects/ui/src/directives/radio-group.ts b/projects/ui/src/directives/radio-group.ts
new file mode 100644
index 0000000..4203e5a
--- /dev/null
+++ b/projects/ui/src/directives/radio-group.ts
@@ -0,0 +1,84 @@
+import { computed, Directive, input } from '@angular/core';
+import { tv } from "tailwind-variants";
+import { NgpRadioGroup, NgpRadioItem, NgpRadioIndicator } from 'ng-primitives/radio';
+
+const radioGroupVariants = tv({
+ slots: {
+ root: 'grid gap-3',
+ item: 'flex items-center space-x-2 cursor-pointer',
+ radio: 'relative border-input text-primary data-[focus-visible]:border-ring data-[focus-visible]:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none data-[focus-visible]:ring-[3px] data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
+ indicator: 'size-2 rounded-full absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 data-[checked]:bg-primary'
+ }
+});
+
+const { root, item, radio, indicator } = radioGroupVariants();
+
+@Directive({
+ selector: '[uiRadioGroup]',
+ exportAs: 'uiRadioGroup',
+ host: {
+ '[class]': 'computedClass()'
+ },
+ hostDirectives: [
+ {
+ directive: NgpRadioGroup,
+ inputs: [
+ 'ngpRadioGroupValue: defaultValue',
+ 'ngpRadioGroupDisabled: disabled'
+ ],
+ outputs: [
+ 'ngpRadioGroupValueChange: defaultValueChange'
+ ],
+ },
+ ],
+})
+export class UiRadioGroup {
+ inputClass = input('', { alias: 'class' });
+ computedClass = computed(() => root({ class: this.inputClass() }));
+}
+
+@Directive({
+ selector: '[uiRadioItem]',
+ exportAs: 'uiRadioItem',
+ host: {
+ '[class]': 'computedClass()'
+ },
+ hostDirectives: [
+ {
+ directive: NgpRadioItem,
+ inputs: [
+ 'ngpRadioItemValue: value',
+ 'ngpRadioItemDisabled: disabled'
+ ],
+ },
+ ],
+})
+export class UiRadioItem {
+ inputClass = input('', { alias: 'class' });
+ computedClass = computed(() => item({ class: this.inputClass() }));
+}
+
+@Directive({
+ selector: '[uiRadio]',
+ exportAs: 'uiRadio',
+ host: {
+ '[class]': 'computedClass()'
+ },
+ hostDirectives: [NgpRadioIndicator]
+})
+export class UiRadio {
+ inputClass = input('', { alias: 'class' });
+ computedClass = computed(() => radio({ class: this.inputClass() }));
+}
+
+@Directive({
+ selector: '[uiRadioIndicator]',
+ exportAs: 'uiRadioIndicator',
+ host: {
+ '[class]': 'computedClass()'
+ }
+})
+export class UiRadioIndicator {
+ inputClass = input('', { alias: 'class' });
+ computedClass = computed(() => indicator({ class: this.inputClass() }));
+}
diff --git a/projects/ui/src/directives/switch.ts b/projects/ui/src/directives/switch.ts
index 2c63ad3..a432faf 100644
--- a/projects/ui/src/directives/switch.ts
+++ b/projects/ui/src/directives/switch.ts
@@ -4,8 +4,8 @@ import { NgpSwitch, NgpSwitchThumb } from "ng-primitives/switch";
const switchVariants = tv({
slots: {
- switchRoot: 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors data-[focus-visible]:outline-none data-[focus-visible]:ring-2 data-[focus-visible]:ring-ring data-[focus-visible]:ring-offset-2 data-[focus-visible]:ring-offset-background data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 data-[checked]:bg-primary bg-input',
- switchThumb: 'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[checked]:translate-x-5 translate-x-0'
+ switchRoot: 'peer data-[checked]:bg-primary bg-input data-[focus-visible]:border-ring data-[focus-visible]:ring-ring/50 dark:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none data-[focus-visible]:ring-[3px] data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',
+ switchThumb: 'bg-background dark:bg-foreground dark:data-[checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[checked]:translate-x-[calc(100%-2px)] translate-x-0'
}
});
diff --git a/projects/ui/src/public-api.ts b/projects/ui/src/public-api.ts
index eb1c919..469211c 100644
--- a/projects/ui/src/public-api.ts
+++ b/projects/ui/src/public-api.ts
@@ -23,4 +23,5 @@ export * from './directives/checkbox';
export * from './directives/toggle';
export * from './directives/toggle-group';
export * from './directives/skeleton';
-export * from './directives/switch';
\ No newline at end of file
+export * from './directives/switch';
+export * from './directives/radio-group';
\ No newline at end of file