@styloviz/badge 0.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StyloViz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @styloviz/badge
2
+
3
+ > Compact status and category labels with 7 semantic variants and 3 visual styles.
4
+
5
+ Part of the **StyloViz UI Kit** — a premium Angular 21 + Tailwind CSS 4 dashboard component library. Standalone, `OnPush`, strongly typed, dark-mode ready.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @styloviz/core @styloviz/badge
11
+ ```
12
+
13
+ Requires `@angular/core` and `@angular/common` >= 21. `@styloviz/core` is a peer dependency shared by every component. Prefer everything at once? `@styloviz/all` installs the whole free tier in one command.
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { SvBadgeComponent } from '@styloviz/badge';
19
+
20
+ @Component({
21
+ standalone: true,
22
+ imports: [SvBadgeComponent],
23
+ template: `
24
+ <sv-badge label="Active" variant="success" />
25
+ `,
26
+ })
27
+ export class DemoComponent {}
28
+ ```
29
+
30
+ ## Inputs
31
+
32
+ | Input | Type | Default | Description |
33
+ | --- | --- | --- | --- |
34
+ | `variant` | `BadgeVariant` | `'primary'` | Semantic color/intent (primary, success, warning, danger…). |
35
+ | `badgeStyle` | `BadgeStyle` | `'soft'` | Visual treatment: solid, soft or outline. |
36
+ | `shape` | `BadgeShape` | `'pill'` | Corner style: pill, rounded or square. |
37
+ | `size` | `BadgeSize` | `'md'` | Badge size: sm, md or lg. |
38
+ | `label` | `string` | `''` | Badge text. Omit when using count mode or projecting content. |
39
+ | `dot` | `boolean` | `false` | Show status dot. |
40
+ | `pulseDot` | `boolean` | `false` | Animate dot with ping. |
41
+ | `count` | `number` | `0` | When > 0, renders count mode (overrides label). |
42
+ | `maxCount` | `number` | `99` | Cap for count display (shows "N+"). |
43
+ | `iconPath` | `string` | `''` | SVG path `d` for leading icon. |
44
+ | `dismissible` | `boolean` | `false` | Show dismiss button. |
45
+ | `customClass` | `string` | `''` | Extra CSS classes merged on root. |
46
+ | `ariaLabel` | `string` | `''` | aria-label override — replaces the visible text as the accessible name. |
47
+ | `live` | `boolean` | `false` | Announce the badge as a polite ARIA live region (`role="status"`). Enable for dynamic notification counts/statuses that update in place so assistive tech is notified of changes. Leave `false` for static labels and tags to avoid noisy announcements. |
48
+
49
+ ## Outputs
50
+
51
+ | Output | Type | Description |
52
+ | --- | --- | --- |
53
+ | `dismissed` | `void` | Emitted when the dismiss button is clicked. |
54
+
55
+ ## Documentation
56
+
57
+ Full API reference and live demos: https://styloviz.dev/docs/badge
58
+
59
+ ## License
60
+
61
+ MIT
@@ -0,0 +1,190 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { NgClass } from '@angular/common';
4
+
5
+ // ─── Component ───────────────────────────────────────────────────────────────
6
+ /**
7
+ * SvBadgeComponent — Compact, accessible label for statuses, counts, and tags.
8
+ *
9
+ * Install: npm install @styloviz/badge
10
+ * Import: import { SvBadgeComponent } from '@styloviz/badge';
11
+ *
12
+ * Features:
13
+ * - 7 variants × 3 styles × 3 shapes × 3 sizes
14
+ * - Optional status dot with pulse animation
15
+ * - Count mode (notification badge)
16
+ * - Leading icon slot
17
+ * - Dismiss button
18
+ * - Full dark mode + OnPush
19
+ */
20
+ class SvBadgeComponent {
21
+ // ── Inputs ────────────────────────────────────────────────────────────────
22
+ /** Semantic color/intent (primary, success, warning, danger…). @default 'primary' */
23
+ variant = input('primary', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
24
+ /** Visual treatment: solid, soft or outline. @default 'soft' */
25
+ badgeStyle = input('soft', ...(ngDevMode ? [{ debugName: "badgeStyle" }] : /* istanbul ignore next */ []));
26
+ /** Corner style: pill, rounded or square. @default 'pill' */
27
+ shape = input('pill', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
28
+ /** Badge size: sm, md or lg. @default 'md' */
29
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
30
+ /** Badge text. Omit when using count mode or projecting content. */
31
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
32
+ /** Show status dot. @default false */
33
+ dot = input(false, ...(ngDevMode ? [{ debugName: "dot" }] : /* istanbul ignore next */ []));
34
+ /** Animate dot with ping. @default false */
35
+ pulseDot = input(false, ...(ngDevMode ? [{ debugName: "pulseDot" }] : /* istanbul ignore next */ []));
36
+ /**
37
+ * When > 0, renders count mode (overrides label).
38
+ * @default 0
39
+ */
40
+ count = input(0, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
41
+ /** Cap for count display (shows "N+"). @default 99 */
42
+ maxCount = input(99, ...(ngDevMode ? [{ debugName: "maxCount" }] : /* istanbul ignore next */ []));
43
+ /** SVG path `d` for leading icon. */
44
+ iconPath = input('', ...(ngDevMode ? [{ debugName: "iconPath" }] : /* istanbul ignore next */ []));
45
+ /** Show dismiss button. @default false */
46
+ dismissible = input(false, ...(ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []));
47
+ /** Extra CSS classes merged on root. */
48
+ customClass = input('', ...(ngDevMode ? [{ debugName: "customClass" }] : /* istanbul ignore next */ []));
49
+ /** aria-label override — replaces the visible text as the accessible name. */
50
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
51
+ /**
52
+ * Announce the badge as a polite ARIA live region (`role="status"`).
53
+ * Enable for dynamic notification counts/statuses that update in place so
54
+ * assistive tech is notified of changes. Leave `false` for static labels
55
+ * and tags to avoid noisy announcements. @default false
56
+ */
57
+ live = input(false, ...(ngDevMode ? [{ debugName: "live" }] : /* istanbul ignore next */ []));
58
+ // ── Outputs ───────────────────────────────────────────────────────────────
59
+ /** Emitted when the dismiss button is clicked. */
60
+ dismissed = output();
61
+ // ── Computed ──────────────────────────────────────────────────────────────
62
+ resolvedAriaLabel = computed(() => this.ariaLabel() || this.label(), ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
63
+ /**
64
+ * Host ARIA role. A live badge is a polite status region; an aria-label
65
+ * override exposes the badge as a single labelled image; otherwise the
66
+ * visible text is the accessible name and no role is needed.
67
+ */
68
+ hostRole = computed(() => {
69
+ if (this.live())
70
+ return 'status';
71
+ if (this.ariaLabel())
72
+ return 'img';
73
+ return null;
74
+ }, ...(ngDevMode ? [{ debugName: "hostRole" }] : /* istanbul ignore next */ []));
75
+ isCountMode = computed(() => this.count() > 0, ...(ngDevMode ? [{ debugName: "isCountMode" }] : /* istanbul ignore next */ []));
76
+ displayCount = computed(() => {
77
+ const n = this.count(), max = this.maxCount();
78
+ return n > max ? `${max}+` : `${n}`;
79
+ }, ...(ngDevMode ? [{ debugName: "displayCount" }] : /* istanbul ignore next */ []));
80
+ rootClasses = computed(() => [
81
+ 'inline-flex items-center font-medium select-none transition-colors duration-150',
82
+ this._sizeClasses(),
83
+ this._shapeClasses(),
84
+ this._colorClasses(),
85
+ this.customClass(),
86
+ ].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "rootClasses" }] : /* istanbul ignore next */ []));
87
+ _sizeClasses = computed(() => {
88
+ const cm = this.isCountMode();
89
+ switch (this.size()) {
90
+ case 'sm': return cm ? 'h-5 min-w-5 px-1 text-[10px] justify-center gap-1' : 'px-2.5 py-1 text-[11px] gap-1';
91
+ case 'lg': return cm ? 'h-7 min-w-7 px-2 text-sm justify-center gap-2' : 'px-4 py-1.5 text-sm gap-2';
92
+ default: return cm ? 'h-6 min-w-6 px-1.5 text-xs justify-center gap-1.5' : 'px-3 py-1 text-xs gap-1.5';
93
+ }
94
+ }, ...(ngDevMode ? [{ debugName: "_sizeClasses" }] : /* istanbul ignore next */ []));
95
+ _shapeClasses = computed(() => {
96
+ switch (this.shape()) {
97
+ case 'square': return 'rounded-none';
98
+ case 'rounded': return 'rounded-md';
99
+ default: return 'rounded-full';
100
+ }
101
+ }, ...(ngDevMode ? [{ debugName: "_shapeClasses" }] : /* istanbul ignore next */ []));
102
+ _colorClasses = computed(() => {
103
+ const map = {
104
+ primary: {
105
+ filled: 'bg-blue-600 text-white dark:bg-blue-600',
106
+ soft: 'bg-blue-50 text-blue-700 ring-1 ring-blue-100 dark:bg-blue-950/60 dark:text-blue-300 dark:ring-blue-900/50',
107
+ outline: 'bg-transparent text-blue-700 ring-1 ring-inset ring-blue-400 dark:text-blue-400 dark:ring-blue-700',
108
+ },
109
+ secondary: {
110
+ filled: 'bg-gray-700 text-white dark:bg-gray-500',
111
+ soft: 'bg-gray-100 text-gray-700 ring-1 ring-gray-200 dark:bg-gray-800/70 dark:text-gray-300 dark:ring-gray-700/60',
112
+ outline: 'bg-transparent text-gray-600 ring-1 ring-inset ring-gray-300 dark:text-gray-400 dark:ring-gray-600',
113
+ },
114
+ success: {
115
+ filled: 'bg-emerald-600 text-white dark:bg-emerald-600',
116
+ soft: 'bg-emerald-50 text-emerald-700 ring-1 ring-emerald-100 dark:bg-emerald-950/60 dark:text-emerald-300 dark:ring-emerald-900/50',
117
+ outline: 'bg-transparent text-emerald-700 ring-1 ring-inset ring-emerald-400 dark:text-emerald-400 dark:ring-emerald-700',
118
+ },
119
+ warning: {
120
+ filled: 'bg-amber-500 text-white dark:bg-amber-500',
121
+ soft: 'bg-amber-50 text-amber-700 ring-1 ring-amber-100 dark:bg-amber-950/60 dark:text-amber-300 dark:ring-amber-900/50',
122
+ outline: 'bg-transparent text-amber-700 ring-1 ring-inset ring-amber-400 dark:text-amber-400 dark:ring-amber-700',
123
+ },
124
+ danger: {
125
+ filled: 'bg-red-600 text-white dark:bg-red-600',
126
+ soft: 'bg-red-50 text-red-700 ring-1 ring-red-100 dark:bg-red-950/60 dark:text-red-300 dark:ring-red-900/50',
127
+ outline: 'bg-transparent text-red-700 ring-1 ring-inset ring-red-400 dark:text-red-400 dark:ring-red-700',
128
+ },
129
+ info: {
130
+ filled: 'bg-sky-600 text-white dark:bg-sky-600',
131
+ soft: 'bg-sky-50 text-sky-700 ring-1 ring-sky-100 dark:bg-sky-950/60 dark:text-sky-300 dark:ring-sky-900/50',
132
+ outline: 'bg-transparent text-sky-700 ring-1 ring-inset ring-sky-400 dark:text-sky-400 dark:ring-sky-700',
133
+ },
134
+ neutral: {
135
+ filled: 'bg-gray-900 text-white dark:bg-gray-200 dark:text-gray-900',
136
+ soft: 'bg-gray-100 text-gray-600 ring-1 ring-gray-200 dark:bg-gray-800/80 dark:text-gray-400 dark:ring-gray-700/60',
137
+ outline: 'bg-transparent text-gray-600 ring-1 ring-inset ring-gray-300 dark:text-gray-400 dark:ring-gray-600',
138
+ },
139
+ };
140
+ return map[this.variant()][this.badgeStyle()];
141
+ }, ...(ngDevMode ? [{ debugName: "_colorClasses" }] : /* istanbul ignore next */ []));
142
+ dotColorClass = computed(() => {
143
+ const map = {
144
+ primary: 'bg-blue-500 dark:bg-blue-400',
145
+ secondary: 'bg-gray-500 dark:bg-gray-400',
146
+ success: 'bg-emerald-500 dark:bg-emerald-400',
147
+ warning: 'bg-amber-500 dark:bg-amber-400',
148
+ danger: 'bg-red-500 dark:bg-red-400',
149
+ info: 'bg-sky-500 dark:bg-sky-400',
150
+ neutral: 'bg-zinc-500 dark:bg-zinc-400',
151
+ };
152
+ return map[this.variant()];
153
+ }, ...(ngDevMode ? [{ debugName: "dotColorClass" }] : /* istanbul ignore next */ []));
154
+ dismissColorClass = computed(() => {
155
+ if (this.badgeStyle() === 'filled') {
156
+ return 'text-white/70 hover:text-white hover:bg-white/20';
157
+ }
158
+ const map = {
159
+ primary: 'text-blue-500 hover:text-blue-700 hover:bg-blue-100 dark:text-blue-400 dark:hover:text-blue-200 dark:hover:bg-blue-800/40',
160
+ secondary: 'text-gray-400 hover:text-gray-600 hover:bg-gray-200 dark:text-gray-500 dark:hover:text-gray-200 dark:hover:bg-gray-700',
161
+ success: 'text-emerald-500 hover:text-emerald-700 hover:bg-emerald-100 dark:text-emerald-400 dark:hover:text-emerald-200 dark:hover:bg-emerald-800/40',
162
+ warning: 'text-amber-500 hover:text-amber-700 hover:bg-amber-100 dark:text-amber-400 dark:hover:text-amber-200 dark:hover:bg-amber-800/40',
163
+ danger: 'text-red-500 hover:text-red-700 hover:bg-red-100 dark:text-red-400 dark:hover:text-red-200 dark:hover:bg-red-800/40',
164
+ info: 'text-sky-500 hover:text-sky-700 hover:bg-sky-100 dark:text-sky-400 dark:hover:text-sky-200 dark:hover:bg-sky-800/40',
165
+ neutral: 'text-zinc-500 hover:text-zinc-700 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:text-zinc-200 dark:hover:bg-zinc-700',
166
+ };
167
+ return map[this.variant()];
168
+ }, ...(ngDevMode ? [{ debugName: "dismissColorClass" }] : /* istanbul ignore next */ []));
169
+ onDismiss(event) {
170
+ event.stopPropagation();
171
+ this.dismissed.emit();
172
+ }
173
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SvBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
174
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: SvBadgeComponent, isStandalone: true, selector: "sv-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, badgeStyle: { classPropertyName: "badgeStyle", publicName: "badgeStyle", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, pulseDot: { classPropertyName: "pulseDot", publicName: "pulseDot", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, maxCount: { classPropertyName: "maxCount", publicName: "maxCount", isSignal: true, isRequired: false, transformFunction: null }, iconPath: { classPropertyName: "iconPath", publicName: "iconPath", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, customClass: { classPropertyName: "customClass", publicName: "customClass", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed" }, ngImport: i0, template: "<span\n [ngClass]=\"rootClasses()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.role]=\"hostRole()\"\n [attr.aria-live]=\"live() ? 'polite' : null\"\n>\n @if (dot()) {\n <span\n class=\"relative inline-flex shrink-0 rounded-full\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-1.5 w-1.5' : size() === 'lg' ? 'h-2.5 w-2.5' : 'h-2 w-2',\n dotColorClass()\n ]\"\n aria-hidden=\"true\"\n >\n @if (pulseDot()) {\n <span\n class=\"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75\"\n [ngClass]=\"dotColorClass()\"\n ></span>\n }\n </span>\n }\n\n @if (iconPath()) {\n <svg\n class=\"shrink-0\"\n [ngClass]=\"size() === 'sm' ? 'h-3 w-3' : size() === 'lg' ? 'h-4.5 w-4.5' : 'h-3.5 w-3.5'\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path [attr.d]=\"iconPath()\" />\n </svg>\n }\n\n @if (isCountMode()) {\n <span class=\"tabular-nums leading-none\">{{ displayCount() }}</span>\n } @else if (label()) {\n <span class=\"leading-none whitespace-nowrap\">{{ label() }}</span>\n }\n\n @if (dismissible() && !isCountMode()) {\n <button\n type=\"button\"\n class=\"inline-flex shrink-0 items-center justify-center rounded-full transition-colors\n duration-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-3 w-3 -mr-0.5' : size() === 'lg' ? 'h-4.5 w-4.5 -mr-0.5' : 'h-3.5 w-3.5 -mr-0.5',\n dismissColorClass()\n ]\"\n (click)=\"onDismiss($event)\"\n [attr.aria-label]=\"'Remove ' + (label() || ariaLabel() || 'badge')\"\n >\n <svg class=\"h-full w-full\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M3 3l8 8M11 3l-8 8\" />\n </svg>\n </button>\n }\n</span>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
175
+ }
176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SvBadgeComponent, decorators: [{
177
+ type: Component,
178
+ args: [{ selector: 'sv-badge', standalone: true, imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span\n [ngClass]=\"rootClasses()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.role]=\"hostRole()\"\n [attr.aria-live]=\"live() ? 'polite' : null\"\n>\n @if (dot()) {\n <span\n class=\"relative inline-flex shrink-0 rounded-full\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-1.5 w-1.5' : size() === 'lg' ? 'h-2.5 w-2.5' : 'h-2 w-2',\n dotColorClass()\n ]\"\n aria-hidden=\"true\"\n >\n @if (pulseDot()) {\n <span\n class=\"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75\"\n [ngClass]=\"dotColorClass()\"\n ></span>\n }\n </span>\n }\n\n @if (iconPath()) {\n <svg\n class=\"shrink-0\"\n [ngClass]=\"size() === 'sm' ? 'h-3 w-3' : size() === 'lg' ? 'h-4.5 w-4.5' : 'h-3.5 w-3.5'\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path [attr.d]=\"iconPath()\" />\n </svg>\n }\n\n @if (isCountMode()) {\n <span class=\"tabular-nums leading-none\">{{ displayCount() }}</span>\n } @else if (label()) {\n <span class=\"leading-none whitespace-nowrap\">{{ label() }}</span>\n }\n\n @if (dismissible() && !isCountMode()) {\n <button\n type=\"button\"\n class=\"inline-flex shrink-0 items-center justify-center rounded-full transition-colors\n duration-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-3 w-3 -mr-0.5' : size() === 'lg' ? 'h-4.5 w-4.5 -mr-0.5' : 'h-3.5 w-3.5 -mr-0.5',\n dismissColorClass()\n ]\"\n (click)=\"onDismiss($event)\"\n [attr.aria-label]=\"'Remove ' + (label() || ariaLabel() || 'badge')\"\n >\n <svg class=\"h-full w-full\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M3 3l8 8M11 3l-8 8\" />\n </svg>\n </button>\n }\n</span>\n" }]
179
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], badgeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeStyle", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], pulseDot: [{ type: i0.Input, args: [{ isSignal: true, alias: "pulseDot", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], maxCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCount", required: false }] }], iconPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPath", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }] } });
180
+
181
+ /**
182
+ * @styloviz/badge — Public API
183
+ */
184
+
185
+ /**
186
+ * Generated bundle index. Do not edit.
187
+ */
188
+
189
+ export { SvBadgeComponent };
190
+ //# sourceMappingURL=styloviz-badge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styloviz-badge.mjs","sources":["../../../../projects/badge/src/lib/badge.component.ts","../../../../projects/badge/src/lib/badge.component.html","../../../../projects/badge/src/public-api.ts","../../../../projects/badge/src/styloviz-badge.ts"],"sourcesContent":["import {\n Component,\n input,\n output,\n computed,\n ChangeDetectionStrategy,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type BadgeVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral';\nexport type BadgeSize = 'sm' | 'md' | 'lg';\nexport type BadgeStyle = 'filled' | 'soft' | 'outline';\nexport type BadgeShape = 'rounded' | 'pill' | 'square';\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * SvBadgeComponent — Compact, accessible label for statuses, counts, and tags.\n *\n * Install: npm install @styloviz/badge\n * Import: import { SvBadgeComponent } from '@styloviz/badge';\n *\n * Features:\n * - 7 variants × 3 styles × 3 shapes × 3 sizes\n * - Optional status dot with pulse animation\n * - Count mode (notification badge)\n * - Leading icon slot\n * - Dismiss button\n * - Full dark mode + OnPush\n */\n@Component({\n selector: 'sv-badge',\n standalone: true,\n imports: [NgClass],\n templateUrl: './badge.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvBadgeComponent {\n // ── Inputs ────────────────────────────────────────────────────────────────\n\n /** Semantic color/intent (primary, success, warning, danger…). @default 'primary' */\n variant = input<BadgeVariant>('primary');\n /** Visual treatment: solid, soft or outline. @default 'soft' */\n badgeStyle = input<BadgeStyle>('soft');\n /** Corner style: pill, rounded or square. @default 'pill' */\n shape = input<BadgeShape>('pill');\n /** Badge size: sm, md or lg. @default 'md' */\n size = input<BadgeSize>('md');\n /** Badge text. Omit when using count mode or projecting content. */\n label = input<string>('');\n /** Show status dot. @default false */\n dot = input<boolean>(false);\n /** Animate dot with ping. @default false */\n pulseDot = input<boolean>(false);\n /**\n * When > 0, renders count mode (overrides label).\n * @default 0\n */\n count = input<number>(0);\n /** Cap for count display (shows \"N+\"). @default 99 */\n maxCount = input<number>(99);\n /** SVG path `d` for leading icon. */\n iconPath = input<string>('');\n /** Show dismiss button. @default false */\n dismissible = input<boolean>(false);\n /** Extra CSS classes merged on root. */\n customClass = input<string>('');\n /** aria-label override — replaces the visible text as the accessible name. */\n ariaLabel = input<string>('');\n /**\n * Announce the badge as a polite ARIA live region (`role=\"status\"`).\n * Enable for dynamic notification counts/statuses that update in place so\n * assistive tech is notified of changes. Leave `false` for static labels\n * and tags to avoid noisy announcements. @default false\n */\n live = input<boolean>(false);\n\n // ── Outputs ───────────────────────────────────────────────────────────────\n\n /** Emitted when the dismiss button is clicked. */\n dismissed = output<void>();\n\n // ── Computed ──────────────────────────────────────────────────────────────\n\n resolvedAriaLabel = computed(() => this.ariaLabel() || this.label());\n /**\n * Host ARIA role. A live badge is a polite status region; an aria-label\n * override exposes the badge as a single labelled image; otherwise the\n * visible text is the accessible name and no role is needed.\n */\n hostRole = computed<'status' | 'img' | null>(() => {\n if (this.live()) return 'status';\n if (this.ariaLabel()) return 'img';\n return null;\n });\n isCountMode = computed(() => this.count() > 0);\n displayCount = computed(() => {\n const n = this.count(), max = this.maxCount();\n return n > max ? `${max}+` : `${n}`;\n });\n\n rootClasses = computed(() => [\n 'inline-flex items-center font-medium select-none transition-colors duration-150',\n this._sizeClasses(),\n this._shapeClasses(),\n this._colorClasses(),\n this.customClass(),\n ].filter(Boolean).join(' '));\n\n private _sizeClasses = computed<string>(() => {\n const cm = this.isCountMode();\n switch (this.size()) {\n case 'sm': return cm ? 'h-5 min-w-5 px-1 text-[10px] justify-center gap-1' : 'px-2.5 py-1 text-[11px] gap-1';\n case 'lg': return cm ? 'h-7 min-w-7 px-2 text-sm justify-center gap-2' : 'px-4 py-1.5 text-sm gap-2';\n default: return cm ? 'h-6 min-w-6 px-1.5 text-xs justify-center gap-1.5' : 'px-3 py-1 text-xs gap-1.5';\n }\n });\n\n private _shapeClasses = computed<string>(() => {\n switch (this.shape()) {\n case 'square': return 'rounded-none';\n case 'rounded': return 'rounded-md';\n default: return 'rounded-full';\n }\n });\n\n private _colorClasses = computed<string>(() => {\n const map: Record<BadgeVariant, Record<BadgeStyle, string>> = {\n primary: {\n filled: 'bg-blue-600 text-white dark:bg-blue-600',\n soft: 'bg-blue-50 text-blue-700 ring-1 ring-blue-100 dark:bg-blue-950/60 dark:text-blue-300 dark:ring-blue-900/50',\n outline: 'bg-transparent text-blue-700 ring-1 ring-inset ring-blue-400 dark:text-blue-400 dark:ring-blue-700',\n },\n secondary: {\n filled: 'bg-gray-700 text-white dark:bg-gray-500',\n soft: 'bg-gray-100 text-gray-700 ring-1 ring-gray-200 dark:bg-gray-800/70 dark:text-gray-300 dark:ring-gray-700/60',\n outline: 'bg-transparent text-gray-600 ring-1 ring-inset ring-gray-300 dark:text-gray-400 dark:ring-gray-600',\n },\n success: {\n filled: 'bg-emerald-600 text-white dark:bg-emerald-600',\n soft: 'bg-emerald-50 text-emerald-700 ring-1 ring-emerald-100 dark:bg-emerald-950/60 dark:text-emerald-300 dark:ring-emerald-900/50',\n outline: 'bg-transparent text-emerald-700 ring-1 ring-inset ring-emerald-400 dark:text-emerald-400 dark:ring-emerald-700',\n },\n warning: {\n filled: 'bg-amber-500 text-white dark:bg-amber-500',\n soft: 'bg-amber-50 text-amber-700 ring-1 ring-amber-100 dark:bg-amber-950/60 dark:text-amber-300 dark:ring-amber-900/50',\n outline: 'bg-transparent text-amber-700 ring-1 ring-inset ring-amber-400 dark:text-amber-400 dark:ring-amber-700',\n },\n danger: {\n filled: 'bg-red-600 text-white dark:bg-red-600',\n soft: 'bg-red-50 text-red-700 ring-1 ring-red-100 dark:bg-red-950/60 dark:text-red-300 dark:ring-red-900/50',\n outline: 'bg-transparent text-red-700 ring-1 ring-inset ring-red-400 dark:text-red-400 dark:ring-red-700',\n },\n info: {\n filled: 'bg-sky-600 text-white dark:bg-sky-600',\n soft: 'bg-sky-50 text-sky-700 ring-1 ring-sky-100 dark:bg-sky-950/60 dark:text-sky-300 dark:ring-sky-900/50',\n outline: 'bg-transparent text-sky-700 ring-1 ring-inset ring-sky-400 dark:text-sky-400 dark:ring-sky-700',\n },\n neutral: {\n filled: 'bg-gray-900 text-white dark:bg-gray-200 dark:text-gray-900',\n soft: 'bg-gray-100 text-gray-600 ring-1 ring-gray-200 dark:bg-gray-800/80 dark:text-gray-400 dark:ring-gray-700/60',\n outline: 'bg-transparent text-gray-600 ring-1 ring-inset ring-gray-300 dark:text-gray-400 dark:ring-gray-600',\n },\n };\n return map[this.variant()][this.badgeStyle()];\n });\n\n dotColorClass = computed<string>(() => {\n const map: Record<BadgeVariant, string> = {\n primary: 'bg-blue-500 dark:bg-blue-400',\n secondary: 'bg-gray-500 dark:bg-gray-400',\n success: 'bg-emerald-500 dark:bg-emerald-400',\n warning: 'bg-amber-500 dark:bg-amber-400',\n danger: 'bg-red-500 dark:bg-red-400',\n info: 'bg-sky-500 dark:bg-sky-400',\n neutral: 'bg-zinc-500 dark:bg-zinc-400',\n };\n return map[this.variant()];\n });\n\n dismissColorClass = computed<string>(() => {\n if (this.badgeStyle() === 'filled') {\n return 'text-white/70 hover:text-white hover:bg-white/20';\n }\n const map: Record<BadgeVariant, string> = {\n primary: 'text-blue-500 hover:text-blue-700 hover:bg-blue-100 dark:text-blue-400 dark:hover:text-blue-200 dark:hover:bg-blue-800/40',\n secondary: 'text-gray-400 hover:text-gray-600 hover:bg-gray-200 dark:text-gray-500 dark:hover:text-gray-200 dark:hover:bg-gray-700',\n success: 'text-emerald-500 hover:text-emerald-700 hover:bg-emerald-100 dark:text-emerald-400 dark:hover:text-emerald-200 dark:hover:bg-emerald-800/40',\n warning: 'text-amber-500 hover:text-amber-700 hover:bg-amber-100 dark:text-amber-400 dark:hover:text-amber-200 dark:hover:bg-amber-800/40',\n danger: 'text-red-500 hover:text-red-700 hover:bg-red-100 dark:text-red-400 dark:hover:text-red-200 dark:hover:bg-red-800/40',\n info: 'text-sky-500 hover:text-sky-700 hover:bg-sky-100 dark:text-sky-400 dark:hover:text-sky-200 dark:hover:bg-sky-800/40',\n neutral: 'text-zinc-500 hover:text-zinc-700 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:text-zinc-200 dark:hover:bg-zinc-700',\n };\n return map[this.variant()];\n });\n\n onDismiss(event: MouseEvent): void {\n event.stopPropagation();\n this.dismissed.emit();\n }\n}\n","<span\n [ngClass]=\"rootClasses()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.role]=\"hostRole()\"\n [attr.aria-live]=\"live() ? 'polite' : null\"\n>\n @if (dot()) {\n <span\n class=\"relative inline-flex shrink-0 rounded-full\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-1.5 w-1.5' : size() === 'lg' ? 'h-2.5 w-2.5' : 'h-2 w-2',\n dotColorClass()\n ]\"\n aria-hidden=\"true\"\n >\n @if (pulseDot()) {\n <span\n class=\"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75\"\n [ngClass]=\"dotColorClass()\"\n ></span>\n }\n </span>\n }\n\n @if (iconPath()) {\n <svg\n class=\"shrink-0\"\n [ngClass]=\"size() === 'sm' ? 'h-3 w-3' : size() === 'lg' ? 'h-4.5 w-4.5' : 'h-3.5 w-3.5'\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path [attr.d]=\"iconPath()\" />\n </svg>\n }\n\n @if (isCountMode()) {\n <span class=\"tabular-nums leading-none\">{{ displayCount() }}</span>\n } @else if (label()) {\n <span class=\"leading-none whitespace-nowrap\">{{ label() }}</span>\n }\n\n @if (dismissible() && !isCountMode()) {\n <button\n type=\"button\"\n class=\"inline-flex shrink-0 items-center justify-center rounded-full transition-colors\n duration-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current\"\n [ngClass]=\"[\n size() === 'sm' ? 'h-3 w-3 -mr-0.5' : size() === 'lg' ? 'h-4.5 w-4.5 -mr-0.5' : 'h-3.5 w-3.5 -mr-0.5',\n dismissColorClass()\n ]\"\n (click)=\"onDismiss($event)\"\n [attr.aria-label]=\"'Remove ' + (label() || ariaLabel() || 'badge')\"\n >\n <svg class=\"h-full w-full\" viewBox=\"0 0 14 14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M3 3l8 8M11 3l-8 8\" />\n </svg>\n </button>\n }\n</span>\n","/**\n * @styloviz/badge — Public API\n */\nexport { SvBadgeComponent } from './lib/badge.component';\nexport type {\n BadgeVariant,\n BadgeSize,\n BadgeStyle,\n BadgeShape,\n} from './lib/badge.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@styloviz/badge",
3
+ "version": "0.1.1",
4
+ "description": "Compact status and category labels with 7 semantic variants and 3 visual styles.",
5
+ "keywords": [
6
+ "angular",
7
+ "tailwind",
8
+ "ui-kit",
9
+ "badge",
10
+ "styloviz"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "sazzad-bs23",
14
+ "homepage": "https://styloviz.dev",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/rhossain/angular-tailwind-dashboard-components.git",
18
+ "directory": "projects/badge"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/rhossain/angular-tailwind-dashboard-components/issues"
22
+ },
23
+ "peerDependencies": {
24
+ "@angular/common": ">=21.0.0",
25
+ "@angular/core": ">=21.0.0",
26
+ "@styloviz/core": ">=0.1.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "engines": {
32
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
33
+ },
34
+ "sideEffects": false,
35
+ "module": "fesm2022/styloviz-badge.mjs",
36
+ "typings": "types/styloviz-badge.d.ts",
37
+ "exports": {
38
+ "./package.json": {
39
+ "default": "./package.json"
40
+ },
41
+ ".": {
42
+ "types": "./types/styloviz-badge.d.ts",
43
+ "default": "./fesm2022/styloviz-badge.mjs"
44
+ }
45
+ },
46
+ "type": "module",
47
+ "dependencies": {
48
+ "tslib": "^2.3.0"
49
+ }
50
+ }
@@ -0,0 +1,81 @@
1
+ import * as _angular_core from '@angular/core';
2
+
3
+ type BadgeVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral';
4
+ type BadgeSize = 'sm' | 'md' | 'lg';
5
+ type BadgeStyle = 'filled' | 'soft' | 'outline';
6
+ type BadgeShape = 'rounded' | 'pill' | 'square';
7
+ /**
8
+ * SvBadgeComponent — Compact, accessible label for statuses, counts, and tags.
9
+ *
10
+ * Install: npm install @styloviz/badge
11
+ * Import: import { SvBadgeComponent } from '@styloviz/badge';
12
+ *
13
+ * Features:
14
+ * - 7 variants × 3 styles × 3 shapes × 3 sizes
15
+ * - Optional status dot with pulse animation
16
+ * - Count mode (notification badge)
17
+ * - Leading icon slot
18
+ * - Dismiss button
19
+ * - Full dark mode + OnPush
20
+ */
21
+ declare class SvBadgeComponent {
22
+ /** Semantic color/intent (primary, success, warning, danger…). @default 'primary' */
23
+ variant: _angular_core.InputSignal<BadgeVariant>;
24
+ /** Visual treatment: solid, soft or outline. @default 'soft' */
25
+ badgeStyle: _angular_core.InputSignal<BadgeStyle>;
26
+ /** Corner style: pill, rounded or square. @default 'pill' */
27
+ shape: _angular_core.InputSignal<BadgeShape>;
28
+ /** Badge size: sm, md or lg. @default 'md' */
29
+ size: _angular_core.InputSignal<BadgeSize>;
30
+ /** Badge text. Omit when using count mode or projecting content. */
31
+ label: _angular_core.InputSignal<string>;
32
+ /** Show status dot. @default false */
33
+ dot: _angular_core.InputSignal<boolean>;
34
+ /** Animate dot with ping. @default false */
35
+ pulseDot: _angular_core.InputSignal<boolean>;
36
+ /**
37
+ * When > 0, renders count mode (overrides label).
38
+ * @default 0
39
+ */
40
+ count: _angular_core.InputSignal<number>;
41
+ /** Cap for count display (shows "N+"). @default 99 */
42
+ maxCount: _angular_core.InputSignal<number>;
43
+ /** SVG path `d` for leading icon. */
44
+ iconPath: _angular_core.InputSignal<string>;
45
+ /** Show dismiss button. @default false */
46
+ dismissible: _angular_core.InputSignal<boolean>;
47
+ /** Extra CSS classes merged on root. */
48
+ customClass: _angular_core.InputSignal<string>;
49
+ /** aria-label override — replaces the visible text as the accessible name. */
50
+ ariaLabel: _angular_core.InputSignal<string>;
51
+ /**
52
+ * Announce the badge as a polite ARIA live region (`role="status"`).
53
+ * Enable for dynamic notification counts/statuses that update in place so
54
+ * assistive tech is notified of changes. Leave `false` for static labels
55
+ * and tags to avoid noisy announcements. @default false
56
+ */
57
+ live: _angular_core.InputSignal<boolean>;
58
+ /** Emitted when the dismiss button is clicked. */
59
+ dismissed: _angular_core.OutputEmitterRef<void>;
60
+ resolvedAriaLabel: _angular_core.Signal<string>;
61
+ /**
62
+ * Host ARIA role. A live badge is a polite status region; an aria-label
63
+ * override exposes the badge as a single labelled image; otherwise the
64
+ * visible text is the accessible name and no role is needed.
65
+ */
66
+ hostRole: _angular_core.Signal<"status" | "img" | null>;
67
+ isCountMode: _angular_core.Signal<boolean>;
68
+ displayCount: _angular_core.Signal<string>;
69
+ rootClasses: _angular_core.Signal<string>;
70
+ private _sizeClasses;
71
+ private _shapeClasses;
72
+ private _colorClasses;
73
+ dotColorClass: _angular_core.Signal<string>;
74
+ dismissColorClass: _angular_core.Signal<string>;
75
+ onDismiss(event: MouseEvent): void;
76
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SvBadgeComponent, never>;
77
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SvBadgeComponent, "sv-badge", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "badgeStyle": { "alias": "badgeStyle"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; "pulseDot": { "alias": "pulseDot"; "required": false; "isSignal": true; }; "count": { "alias": "count"; "required": false; "isSignal": true; }; "maxCount": { "alias": "maxCount"; "required": false; "isSignal": true; }; "iconPath": { "alias": "iconPath"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "customClass": { "alias": "customClass"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "live": { "alias": "live"; "required": false; "isSignal": true; }; }, { "dismissed": "dismissed"; }, never, never, true, never>;
78
+ }
79
+
80
+ export { SvBadgeComponent };
81
+ export type { BadgeShape, BadgeSize, BadgeStyle, BadgeVariant };