@pegasusheavy/ngx-tailwindcss 0.1.1 → 0.1.2

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/README.md ADDED
@@ -0,0 +1,342 @@
1
+ # @pegasus-heavy/ngx-tailwindcss
2
+
3
+ A highly customizable Angular component library designed for **Tailwind CSS 4+**. This library provides beautiful, accessible UI components that leverage Tailwind's utility-first approach while giving you complete control over styling.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Fully Customizable** - Override any styling through class props or global configuration
8
+ - 🌊 **Tailwind CSS 4+ Ready** - Built for the latest Tailwind with CSS-first configuration
9
+ - ♿ **Accessible** - WCAG compliant with proper ARIA attributes and keyboard navigation
10
+ - 📦 **Tree-Shakeable** - Import only what you need with secondary entry points
11
+ - 🔧 **No Bundled CSS** - Your Tailwind config, your rules
12
+ - ⚡ **Standalone Components** - No NgModule required, works with Angular 19+
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @pegasus-heavy/ngx-tailwindcss
18
+ # or
19
+ npm install @pegasus-heavy/ngx-tailwindcss
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ ```bash
25
+ pnpm add @angular/cdk
26
+ ```
27
+
28
+ ## Tailwind CSS Configuration
29
+
30
+ This library **does not** import Tailwind CSS directly. You must configure Tailwind in your application.
31
+
32
+ ### 1. Configure Content Paths
33
+
34
+ Add the library's component templates to your Tailwind content configuration so the PostCSS parser can detect the utility classes used:
35
+
36
+ **For Tailwind CSS 4+ (CSS-based config):**
37
+
38
+ ```css
39
+ /* app.css or styles.css */
40
+ @import "tailwindcss";
41
+
42
+ @source "../node_modules/@pegasus-heavy/ngx-tailwindcss/**/*.{js,mjs}";
43
+ ```
44
+
45
+ **For Tailwind CSS 3.x (tailwind.config.js):**
46
+
47
+ ```js
48
+ // tailwind.config.js
49
+ module.exports = {
50
+ content: [
51
+ "./src/**/*.{html,ts}",
52
+ "./node_modules/@pegasus-heavy/ngx-tailwindcss/**/*.{js,mjs}",
53
+ ],
54
+ // ... rest of your config
55
+ };
56
+ ```
57
+
58
+ ### 2. Import Components
59
+
60
+ Import components directly in your Angular components:
61
+
62
+ ```typescript
63
+ import { Component } from '@angular/core';
64
+ import {
65
+ TwButtonComponent,
66
+ TwCardComponent,
67
+ TwCardBodyDirective
68
+ } from '@pegasus-heavy/ngx-tailwindcss';
69
+
70
+ @Component({
71
+ selector: 'app-example',
72
+ standalone: true,
73
+ imports: [TwButtonComponent, TwCardComponent, TwCardBodyDirective],
74
+ template: `
75
+ <tw-card>
76
+ <tw-card-body>
77
+ <tw-button variant="primary">Click me!</tw-button>
78
+ </tw-card-body>
79
+ </tw-card>
80
+ `,
81
+ })
82
+ export class ExampleComponent {}
83
+ ```
84
+
85
+ Or import everything at once:
86
+
87
+ ```typescript
88
+ import { TW_ALL } from '@pegasus-heavy/ngx-tailwindcss';
89
+
90
+ @Component({
91
+ imports: [...TW_ALL],
92
+ })
93
+ export class ExampleComponent {}
94
+ ```
95
+
96
+ ## Global Configuration
97
+
98
+ Customize default styles and behavior globally:
99
+
100
+ ```typescript
101
+ // app.config.ts
102
+ import { ApplicationConfig } from '@angular/core';
103
+ import { provideTwConfig } from '@pegasus-heavy/ngx-tailwindcss';
104
+
105
+ export const appConfig: ApplicationConfig = {
106
+ providers: [
107
+ provideTwConfig({
108
+ theme: {
109
+ primary: 'bg-indigo-600 hover:bg-indigo-700 text-white',
110
+ secondary: 'bg-purple-600 hover:bg-purple-700 text-white',
111
+ // ... customize other variants
112
+ },
113
+ animationDuration: 300,
114
+ }),
115
+ ],
116
+ };
117
+ ```
118
+
119
+ ## Components
120
+
121
+ ### Button
122
+
123
+ ```html
124
+ <tw-button>Default</tw-button>
125
+ <tw-button variant="primary">Primary</tw-button>
126
+ <tw-button variant="danger" size="lg">Large Danger</tw-button>
127
+ <tw-button variant="outline" [loading]="isLoading">Submit</tw-button>
128
+ <tw-button variant="ghost" [iconOnly]="true">
129
+ <svg twButtonIcon>...</svg>
130
+ </tw-button>
131
+ ```
132
+
133
+ **Props:**
134
+ - `variant`: `'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'ghost' | 'outline' | 'link'`
135
+ - `size`: `'xs' | 'sm' | 'md' | 'lg' | 'xl'`
136
+ - `disabled`, `loading`, `fullWidth`, `iconOnly`: `boolean`
137
+ - `classOverride`: Additional classes to merge
138
+ - `classReplace`: Complete class replacement
139
+
140
+ ### Card
141
+
142
+ ```html
143
+ <tw-card variant="elevated">
144
+ <tw-card-header>
145
+ <tw-card-title>Card Title</tw-card-title>
146
+ <tw-card-subtitle>Subtitle</tw-card-subtitle>
147
+ </tw-card-header>
148
+ <tw-card-body>
149
+ Content goes here
150
+ </tw-card-body>
151
+ <tw-card-footer>
152
+ <tw-button variant="primary">Action</tw-button>
153
+ </tw-card-footer>
154
+ </tw-card>
155
+ ```
156
+
157
+ ### Input
158
+
159
+ ```html
160
+ <tw-input
161
+ [(ngModel)]="email"
162
+ type="email"
163
+ label="Email"
164
+ placeholder="you@example.com"
165
+ hint="We'll never share your email"
166
+ [error]="emailError"
167
+ [clearable]="true">
168
+ </tw-input>
169
+
170
+ <tw-textarea
171
+ [(ngModel)]="bio"
172
+ label="Bio"
173
+ [rows]="4"
174
+ [maxlength]="500"
175
+ [showCount]="true"
176
+ [autoResize]="true">
177
+ </tw-textarea>
178
+ ```
179
+
180
+ ### Badge
181
+
182
+ ```html
183
+ <tw-badge variant="success">Active</tw-badge>
184
+ <tw-badge variant="warning" badgeStyle="soft">Pending</tw-badge>
185
+ <tw-badge variant="danger" badgeStyle="dot">Offline</tw-badge>
186
+ <tw-badge variant="info" [pill]="true" [removable]="true" [remove]="onRemove">
187
+ Tag
188
+ </tw-badge>
189
+ ```
190
+
191
+ ### Alert
192
+
193
+ ```html
194
+ <tw-alert variant="success" alertStyle="soft" [dismissible]="true">
195
+ <tw-alert-title>Success!</tw-alert-title>
196
+ <tw-alert-description>Your changes have been saved.</tw-alert-description>
197
+ </tw-alert>
198
+
199
+ <tw-alert variant="danger" alertStyle="accent">
200
+ An error occurred while processing your request.
201
+ </tw-alert>
202
+ ```
203
+
204
+ ### Modal
205
+
206
+ ```html
207
+ <tw-button (click)="isOpen = true">Open Modal</tw-button>
208
+
209
+ <tw-modal [(open)]="isOpen" size="md">
210
+ <tw-modal-header>
211
+ <tw-modal-title>Confirm Action</tw-modal-title>
212
+ </tw-modal-header>
213
+ <tw-modal-body>
214
+ Are you sure you want to continue?
215
+ </tw-modal-body>
216
+ <tw-modal-footer>
217
+ <tw-button variant="ghost" (click)="isOpen = false">Cancel</tw-button>
218
+ <tw-button variant="primary" (click)="confirm()">Confirm</tw-button>
219
+ </tw-modal-footer>
220
+ </tw-modal>
221
+ ```
222
+
223
+ ### Dropdown
224
+
225
+ ```html
226
+ <tw-dropdown>
227
+ <button twDropdownTrigger class="px-4 py-2 bg-white border rounded-lg">
228
+ Options
229
+ </button>
230
+ <tw-dropdown-menu>
231
+ <tw-dropdown-header>Actions</tw-dropdown-header>
232
+ <button twDropdownItem (click)="edit()">Edit</button>
233
+ <button twDropdownItem (click)="duplicate()">Duplicate</button>
234
+ <tw-dropdown-divider></tw-dropdown-divider>
235
+ <button twDropdownItem class="text-rose-600">Delete</button>
236
+ </tw-dropdown-menu>
237
+ </tw-dropdown>
238
+ ```
239
+
240
+ ### Tabs
241
+
242
+ ```html
243
+ <tw-tabs [(value)]="activeTab" variant="line">
244
+ <tw-tab-panel value="overview" label="Overview">
245
+ Overview content
246
+ </tw-tab-panel>
247
+ <tw-tab-panel value="settings" label="Settings">
248
+ Settings content
249
+ </tw-tab-panel>
250
+ <tw-tab-panel value="billing" label="Billing" [disabled]="!isPremium">
251
+ Billing content
252
+ </tw-tab-panel>
253
+ </tw-tabs>
254
+ ```
255
+
256
+ ## Directives
257
+
258
+ ### Ripple Effect
259
+
260
+ ```html
261
+ <button twRipple class="px-4 py-2 bg-blue-600 text-white rounded">
262
+ Click me
263
+ </button>
264
+ ```
265
+
266
+ ### Tooltip
267
+
268
+ ```html
269
+ <button twTooltip="Save changes" tooltipPosition="top">
270
+ Save
271
+ </button>
272
+ ```
273
+
274
+ ### Click Outside
275
+
276
+ ```html
277
+ <div (twClickOutside)="closeMenu()" [clickOutsideEnabled]="isMenuOpen">
278
+ Menu content
279
+ </div>
280
+ ```
281
+
282
+ ### Focus Trap
283
+
284
+ ```html
285
+ <div twFocusTrap [focusTrapAutoFocus]="true">
286
+ <input type="text">
287
+ <button>Submit</button>
288
+ </div>
289
+ ```
290
+
291
+ ### Class Merge
292
+
293
+ ```html
294
+ <!-- Intelligently merges Tailwind classes, resolving conflicts -->
295
+ <div [twClass]="'px-4 py-2 bg-blue-500'" [twClassMerge]="'px-8 bg-red-500'">
296
+ Will have px-8 py-2 bg-red-500
297
+ </div>
298
+ ```
299
+
300
+ ## Customization Examples
301
+
302
+ ### Per-Component Override
303
+
304
+ ```html
305
+ <tw-button
306
+ variant="primary"
307
+ classOverride="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600">
308
+ Gradient Button
309
+ </tw-button>
310
+ ```
311
+
312
+ ### Complete Class Replacement
313
+
314
+ ```html
315
+ <tw-button classReplace="your-completely-custom-classes">
316
+ Custom Styled
317
+ </tw-button>
318
+ ```
319
+
320
+ ### Using the Class Service
321
+
322
+ ```typescript
323
+ import { TwClassService } from '@pegasus-heavy/ngx-tailwindcss';
324
+
325
+ @Component({...})
326
+ export class MyComponent {
327
+ private twClass = inject(TwClassService);
328
+
329
+ get buttonClasses() {
330
+ return this.twClass.merge(
331
+ 'px-4 py-2 rounded',
332
+ this.isPrimary ? 'bg-blue-600 text-white' : 'bg-gray-100',
333
+ this.isDisabled && 'opacity-50 cursor-not-allowed'
334
+ );
335
+ }
336
+ }
337
+ ```
338
+
339
+ ## License
340
+
341
+ MIT © Pegasus Heavy Industries LLC
342
+
@@ -9474,11 +9474,11 @@ class TwDatatablesComponent {
9474
9474
  return [this.baseToolbarClass, this.toolbarClass].filter(Boolean).join(' ');
9475
9475
  }
9476
9476
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwDatatablesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9477
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TwDatatablesComponent, isStandalone: true, selector: "tw-datatables", inputs: { data: "data", columns: "columns", title: "title", subtitle: "subtitle", description: "description", tableSize: "tableSize", tableVariant: "tableVariant", selectable: "selectable", selectionMode: "selectionMode", showGlobalFilter: "showGlobalFilter", paginator: "paginator", rows: "rows", hoverable: "hoverable", responsive: "responsive", emptyMessage: "emptyMessage", classOverride: "classOverride", containerClass: "containerClass", toolbarClass: "toolbarClass" }, outputs: { selectionChange: "selectionChange", rowClick: "rowClick", sortChange: "sortChange", pageChange: "pageChange" }, ngImport: i0, template: "<section [class]=\"containerClassList\">\n <header class=\"flex flex-wrap items-start justify-between gap-6\">\n <div class=\"space-y-1 max-w-3xl\">\n <p class=\"text-xs font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\" *ngIf=\"subtitle\">\n {{ subtitle }}\n </p>\n <h3 class=\"text-xl font-semibold text-slate-900 dark:text-white\">{{ title }}</h3>\n <p class=\"text-sm text-slate-500 dark:text-slate-300\" *ngIf=\"description\">\n {{ description }}\n </p>\n </div>\n <div [class]=\"toolbarClassList\">\n <ng-content select=\"[twDatatablesActions]\"></ng-content>\n </div>\n </header>\n\n <div class=\"space-y-4\">\n <tw-table\n [data]=\"data\"\n [columns]=\"columns\"\n [size]=\"tableSize\"\n [variant]=\"tableVariant\"\n [selectable]=\"selectable\"\n [selectionMode]=\"selectionMode\"\n [showGlobalFilter]=\"showGlobalFilter\"\n [paginator]=\"paginator\"\n [rows]=\"rows\"\n [hoverable]=\"hoverable\"\n [responsive]=\"responsive\"\n [emptyMessage]=\"emptyMessage\"\n [classOverride]=\"classOverride\"\n (selectionChange)=\"selectionChange.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n (sortChange)=\"sortChange.emit($event)\"\n (pageChange)=\"pageChange.emit($event)\"\n ></tw-table>\n </div>\n</section>\n\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: TwTableComponent, selector: "tw-table", inputs: ["data", "columns", "title", "size", "variant", "selectable", "selectionMode", "showGlobalFilter", "filterPlaceholder", "paginator", "rows", "rowsPerPageOptions", "emptyMessage", "hoverable", "responsive", "trackByFn", "classOverride"], outputs: ["selectionChange", "rowClick", "sortChange", "pageChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9477
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwDatatablesComponent, isStandalone: true, selector: "tw-datatables", inputs: { data: "data", columns: "columns", title: "title", subtitle: "subtitle", description: "description", tableSize: "tableSize", tableVariant: "tableVariant", selectable: "selectable", selectionMode: "selectionMode", showGlobalFilter: "showGlobalFilter", paginator: "paginator", rows: "rows", hoverable: "hoverable", responsive: "responsive", emptyMessage: "emptyMessage", classOverride: "classOverride", containerClass: "containerClass", toolbarClass: "toolbarClass" }, outputs: { selectionChange: "selectionChange", rowClick: "rowClick", sortChange: "sortChange", pageChange: "pageChange" }, ngImport: i0, template: "<section [class]=\"containerClassList\">\n <header class=\"flex flex-wrap items-start justify-between gap-6\">\n <div class=\"space-y-1 max-w-3xl\">\n @if (subtitle) {\n <p class=\"text-xs font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\">\n {{ subtitle }}\n </p>\n }\n <h3 class=\"text-xl font-semibold text-slate-900 dark:text-white\">{{ title }}</h3>\n @if (description) {\n <p class=\"text-sm text-slate-500 dark:text-slate-300\">\n {{ description }}\n </p>\n }\n </div>\n <div [class]=\"toolbarClassList\">\n <ng-content select=\"[twDatatablesActions]\"></ng-content>\n </div>\n </header>\n\n <div class=\"space-y-4\">\n <tw-table\n [data]=\"data\"\n [columns]=\"columns\"\n [size]=\"tableSize\"\n [variant]=\"tableVariant\"\n [selectable]=\"selectable\"\n [selectionMode]=\"selectionMode\"\n [showGlobalFilter]=\"showGlobalFilter\"\n [paginator]=\"paginator\"\n [rows]=\"rows\"\n [hoverable]=\"hoverable\"\n [responsive]=\"responsive\"\n [emptyMessage]=\"emptyMessage\"\n [classOverride]=\"classOverride\"\n (selectionChange)=\"selectionChange.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n (sortChange)=\"sortChange.emit($event)\"\n (pageChange)=\"pageChange.emit($event)\"\n ></tw-table>\n </div>\n</section>\n\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TwTableComponent, selector: "tw-table", inputs: ["data", "columns", "title", "size", "variant", "selectable", "selectionMode", "showGlobalFilter", "filterPlaceholder", "paginator", "rows", "rowsPerPageOptions", "emptyMessage", "hoverable", "responsive", "trackByFn", "classOverride"], outputs: ["selectionChange", "rowClick", "sortChange", "pageChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9478
9478
  }
9479
9479
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwDatatablesComponent, decorators: [{
9480
9480
  type: Component,
9481
- args: [{ selector: 'tw-datatables', standalone: true, imports: [CommonModule, TwTableComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section [class]=\"containerClassList\">\n <header class=\"flex flex-wrap items-start justify-between gap-6\">\n <div class=\"space-y-1 max-w-3xl\">\n <p class=\"text-xs font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\" *ngIf=\"subtitle\">\n {{ subtitle }}\n </p>\n <h3 class=\"text-xl font-semibold text-slate-900 dark:text-white\">{{ title }}</h3>\n <p class=\"text-sm text-slate-500 dark:text-slate-300\" *ngIf=\"description\">\n {{ description }}\n </p>\n </div>\n <div [class]=\"toolbarClassList\">\n <ng-content select=\"[twDatatablesActions]\"></ng-content>\n </div>\n </header>\n\n <div class=\"space-y-4\">\n <tw-table\n [data]=\"data\"\n [columns]=\"columns\"\n [size]=\"tableSize\"\n [variant]=\"tableVariant\"\n [selectable]=\"selectable\"\n [selectionMode]=\"selectionMode\"\n [showGlobalFilter]=\"showGlobalFilter\"\n [paginator]=\"paginator\"\n [rows]=\"rows\"\n [hoverable]=\"hoverable\"\n [responsive]=\"responsive\"\n [emptyMessage]=\"emptyMessage\"\n [classOverride]=\"classOverride\"\n (selectionChange)=\"selectionChange.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n (sortChange)=\"sortChange.emit($event)\"\n (pageChange)=\"pageChange.emit($event)\"\n ></tw-table>\n </div>\n</section>\n\n\n" }]
9481
+ args: [{ selector: 'tw-datatables', standalone: true, imports: [CommonModule, TwTableComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section [class]=\"containerClassList\">\n <header class=\"flex flex-wrap items-start justify-between gap-6\">\n <div class=\"space-y-1 max-w-3xl\">\n @if (subtitle) {\n <p class=\"text-xs font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400\">\n {{ subtitle }}\n </p>\n }\n <h3 class=\"text-xl font-semibold text-slate-900 dark:text-white\">{{ title }}</h3>\n @if (description) {\n <p class=\"text-sm text-slate-500 dark:text-slate-300\">\n {{ description }}\n </p>\n }\n </div>\n <div [class]=\"toolbarClassList\">\n <ng-content select=\"[twDatatablesActions]\"></ng-content>\n </div>\n </header>\n\n <div class=\"space-y-4\">\n <tw-table\n [data]=\"data\"\n [columns]=\"columns\"\n [size]=\"tableSize\"\n [variant]=\"tableVariant\"\n [selectable]=\"selectable\"\n [selectionMode]=\"selectionMode\"\n [showGlobalFilter]=\"showGlobalFilter\"\n [paginator]=\"paginator\"\n [rows]=\"rows\"\n [hoverable]=\"hoverable\"\n [responsive]=\"responsive\"\n [emptyMessage]=\"emptyMessage\"\n [classOverride]=\"classOverride\"\n (selectionChange)=\"selectionChange.emit($event)\"\n (rowClick)=\"rowClick.emit($event)\"\n (sortChange)=\"sortChange.emit($event)\"\n (pageChange)=\"pageChange.emit($event)\"\n ></tw-table>\n </div>\n</section>\n\n\n" }]
9482
9482
  }], propDecorators: { data: [{
9483
9483
  type: Input
9484
9484
  }], columns: [{