@deay/ui 0.0.1 → 0.0.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,216 @@
1
+ # @deay/ui
2
+
3
+ A modern Angular UI component library built with Angular 19+, following Figma design specifications. Features accessible, customizable components with signal inputs and zoneless-ready architecture.
4
+
5
+ ## Features
6
+
7
+ - Angular 19+ with standalone components
8
+ - Signal inputs for reactivity
9
+ - Zoneless-ready with OnPush change detection
10
+ - Full accessibility support (ARIA)
11
+ - Three size variants (sm, md, lg)
12
+ - Comprehensive error and disabled states
13
+ - Figma design specifications
14
+ - TypeScript support
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @deay/ui
20
+ ```
21
+
22
+ ## Requirements
23
+
24
+ - Angular 19.0.0 or higher
25
+ - @angular/common 19.0.0 or higher
26
+ - @angular/forms 19.0.0 or higher
27
+
28
+ ## Available Components
29
+
30
+ ### Button Component (`<dai-button>`)
31
+
32
+ A versatile button component with loading states and multiple sizes.
33
+
34
+ **Selector:** `dai-button`
35
+
36
+ **Inputs:**
37
+ - `variant`: `'primary'` (default) - Visual style variant
38
+ - `size`: `'sm' | 'md' | 'lg'` (default: `'md'`) - Button size
39
+ - `disabled`: `boolean` (default: `false`) - Disable the button
40
+ - `loading`: `boolean` (default: `false`) - Show loading spinner
41
+
42
+ **Usage:**
43
+
44
+ ```html
45
+ <!-- Standard button -->
46
+ <dai-button size="md">
47
+ Button
48
+ </dai-button>
49
+
50
+ <!-- All sizes -->
51
+ <dai-button size="sm">Button</dai-button>
52
+ <dai-button size="md">Button</dai-button>
53
+ <dai-button size="lg">Button</dai-button>
54
+
55
+ <!-- Loading state -->
56
+ <dai-button size="md" [loading]="true">
57
+ Button
58
+ </dai-button>
59
+
60
+ <!-- Disabled state -->
61
+ <dai-button size="md" [disabled]="true">
62
+ Button
63
+ </dai-button>
64
+ ```
65
+
66
+ **Design Specifications:**
67
+ - Font: Poppins, weight 400
68
+ - Border radius: 50px (fully rounded)
69
+ - Size heights: sm (24px, 12px font), md (32px, 14px font), lg (40px, 16px font)
70
+ - Hover: Primary/600 (#5164F7)
71
+ - Focus: Primary/800 border (#112EAC)
72
+ - Disabled: Primary/400 (#9098FA)
73
+ - Loading: Primary/500 (#6B7FFF)
74
+
75
+ ### Input Component (`<dai-input>`)
76
+
77
+ A feature-rich input component with validation states and Angular Forms integration.
78
+
79
+ **Selector:** `dai-input`
80
+
81
+ **Inputs:**
82
+ - `label`: `string` - Label text displayed above input
83
+ - `placeholder`: `string` (default: `'Text'`) - Placeholder text
84
+ - `type`: `'text' | 'password' | 'email'` (default: `'text'`) - HTML input type
85
+ - `size`: `'sm' | 'md' | 'lg'` (default: `'md'`) - Input size
86
+ - `disabled`: `boolean` (default: `false`) - Disable the input
87
+ - `errorMessage`: `string` - Error message that triggers error styling
88
+ - `value`: `string` - Two-way bound value (Angular 19+ model)
89
+
90
+ **Usage:**
91
+
92
+ ```html
93
+ <!-- Basic input -->
94
+ <dai-input
95
+ label="Label"
96
+ size="md"
97
+ placeholder="Text"
98
+ />
99
+
100
+ <!-- All sizes -->
101
+ <dai-input label="Small" size="sm" placeholder="Text" />
102
+ <dai-input label="Medium" size="md" placeholder="Text" />
103
+ <dai-input label="Large" size="lg" placeholder="Text" />
104
+
105
+ <!-- Error state -->
106
+ <dai-input
107
+ label="Email"
108
+ size="md"
109
+ placeholder="Text"
110
+ [errorMessage]="'Invalid email format'"
111
+ />
112
+
113
+ <!-- Disabled state -->
114
+ <dai-input
115
+ label="Label"
116
+ size="md"
117
+ placeholder="Text"
118
+ [disabled]="true"
119
+ />
120
+
121
+ <!-- Two-way binding -->
122
+ <dai-input
123
+ label="Email"
124
+ size="md"
125
+ placeholder="Text"
126
+ [(value)]="email"
127
+ />
128
+
129
+ <!-- Reactive forms -->
130
+ <dai-input
131
+ label="Email"
132
+ size="md"
133
+ [formControl]="emailControl"
134
+ [errorMessage]="emailControl.errors ? 'Error Message' : ''"
135
+ />
136
+ ```
137
+
138
+ **Design Specifications:**
139
+ - Font: Poppins, weight 400
140
+ - Border radius: 50px (fully rounded)
141
+ - Label: 16px, color #000000
142
+ - Input text: 14px, placeholder #ABA7AF
143
+ - Default border: #E5E0EB (1px)
144
+ - Focus border sm: #061764 (2px)
145
+ - Focus border md/lg: #112EAC (1px)
146
+ - Error border: #D51A52 (2px)
147
+ - Error text: #D51A52, 14px
148
+ - Disabled: #DFDFDF background and border
149
+ - Heights: sm (~36px), md (44px), lg (50px)
150
+ - Gap between elements: 8px
151
+
152
+ ## Best Practices
153
+
154
+ ### Button
155
+ - Use `size="sm"` for dense UI areas and tables
156
+ - Use `size="md"` for standard buttons (default)
157
+ - Use `size="lg"` for prominent call-to-action buttons
158
+ - Loading state automatically disables the button
159
+ - Always provide accessible button text
160
+
161
+ ### Input
162
+ - Always provide labels for accessibility
163
+ - Use appropriate input types for better mobile experience
164
+ - Connect error messages to form validation
165
+ - Use size="sm" for compact forms
166
+ - Use size="md" for standard forms (default)
167
+ - Use size="lg" for prominent inputs
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ # Install dependencies
173
+ npm install
174
+
175
+ # Build the library
176
+ npm run build
177
+
178
+ # Watch mode for development
179
+ npm run dev
180
+ ```
181
+
182
+ ## Build Output
183
+
184
+ The library builds to the `dist/` directory using ng-packagr and includes:
185
+ - Angular library format
186
+ - TypeScript definitions
187
+ - Metadata
188
+ - Styles
189
+
190
+ ## Accessibility
191
+
192
+ All components follow WCAG 2.1 guidelines:
193
+ - Proper ARIA attributes
194
+ - Keyboard navigation support
195
+ - Focus indicators
196
+ - Screen reader support
197
+ - Semantic HTML
198
+
199
+ ## Browser Support
200
+
201
+ - Chrome (latest)
202
+ - Firefox (latest)
203
+ - Safari (latest)
204
+ - Edge (latest)
205
+
206
+ ## License
207
+
208
+ MIT
209
+
210
+ ## Contributing
211
+
212
+ Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
213
+
214
+ ## Related Packages
215
+
216
+ - [@deay/mcp](../mcp-server) - MCP server providing AI assistants with @deay/ui component documentation
@@ -0,0 +1,186 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, Component, model, output, forwardRef } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
+
6
+ class DaiButtonComponent {
7
+ // Signal Inputs (Angular 19+)
8
+ variant = input('primary');
9
+ size = input('md');
10
+ disabled = input(false);
11
+ loading = input(false);
12
+ // Computed properties for dynamic classes
13
+ computedClasses = computed(() => {
14
+ const classes = [
15
+ 'dai-button',
16
+ `dai-button-${this.variant()}`,
17
+ `dai-button-${this.size()}`,
18
+ ];
19
+ if (this.loading()) {
20
+ classes.push('dai-button-loading');
21
+ }
22
+ return classes.join(' ');
23
+ });
24
+ // Computed disabled state (disabled or loading)
25
+ isDisabled = computed(() => this.disabled() || this.loading());
26
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DaiButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
27
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: DaiButtonComponent, isStandalone: true, selector: "dai-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
28
+ <button
29
+ [class]="computedClasses()"
30
+ [disabled]="isDisabled()"
31
+ [attr.aria-disabled]="isDisabled()"
32
+ [attr.aria-busy]="loading()"
33
+ >
34
+ @if (loading()) {
35
+ <span class="dai-spinner" aria-hidden="true"></span>
36
+ }
37
+ <ng-content />
38
+ </button>
39
+ `, isInline: true, styles: [":host{display:inline-block}button{display:inline-flex;align-items:center;justify-content:center;gap:4px;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-weight:400;line-height:1.5;border-radius:50px;cursor:pointer;transition:all .15s ease-in-out;border:1px solid transparent;box-sizing:border-box}button:focus-visible{outline:none}button:disabled{cursor:not-allowed;opacity:1}.dai-button-sm{padding:2px 8px;height:24px;font-size:12px;line-height:1.5em}.dai-button-md{padding:6px 12px;height:32px;font-size:14px;line-height:1.4285714285714286em}.dai-button-lg{padding:8px 16px;height:40px;font-size:16px;line-height:1.375em}.dai-button-primary{background-color:#2047f4;color:#fff}.dai-button-primary:hover:not(:disabled){background-color:#5164f7}.dai-button-primary:focus-visible{background-color:#2047f4;border-color:#112eac;box-shadow:0 0 0 1px #112eac}.dai-button-primary:disabled,.dai-button-primary.dai-button-loading{background-color:#9098fa}.dai-button-primary.dai-button-loading{cursor:wait}.dai-button-sm .dai-spinner{width:14px;height:14px}.dai-button-md .dai-spinner{width:16px;height:16px}.dai-button-lg .dai-spinner{width:22px;height:22px}.dai-spinner{display:inline-block;border:2px solid #FFFFFF;border-right-color:transparent;border-radius:50%;animation:dai-spin .6s linear infinite}@keyframes dai-spin{to{transform:rotate(360deg)}}\n"] });
40
+ }
41
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DaiButtonComponent, decorators: [{
42
+ type: Component,
43
+ args: [{ selector: 'dai-button', standalone: true, template: `
44
+ <button
45
+ [class]="computedClasses()"
46
+ [disabled]="isDisabled()"
47
+ [attr.aria-disabled]="isDisabled()"
48
+ [attr.aria-busy]="loading()"
49
+ >
50
+ @if (loading()) {
51
+ <span class="dai-spinner" aria-hidden="true"></span>
52
+ }
53
+ <ng-content />
54
+ </button>
55
+ `, styles: [":host{display:inline-block}button{display:inline-flex;align-items:center;justify-content:center;gap:4px;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-weight:400;line-height:1.5;border-radius:50px;cursor:pointer;transition:all .15s ease-in-out;border:1px solid transparent;box-sizing:border-box}button:focus-visible{outline:none}button:disabled{cursor:not-allowed;opacity:1}.dai-button-sm{padding:2px 8px;height:24px;font-size:12px;line-height:1.5em}.dai-button-md{padding:6px 12px;height:32px;font-size:14px;line-height:1.4285714285714286em}.dai-button-lg{padding:8px 16px;height:40px;font-size:16px;line-height:1.375em}.dai-button-primary{background-color:#2047f4;color:#fff}.dai-button-primary:hover:not(:disabled){background-color:#5164f7}.dai-button-primary:focus-visible{background-color:#2047f4;border-color:#112eac;box-shadow:0 0 0 1px #112eac}.dai-button-primary:disabled,.dai-button-primary.dai-button-loading{background-color:#9098fa}.dai-button-primary.dai-button-loading{cursor:wait}.dai-button-sm .dai-spinner{width:14px;height:14px}.dai-button-md .dai-spinner{width:16px;height:16px}.dai-button-lg .dai-spinner{width:22px;height:22px}.dai-spinner{display:inline-block;border:2px solid #FFFFFF;border-right-color:transparent;border-radius:50%;animation:dai-spin .6s linear infinite}@keyframes dai-spin{to{transform:rotate(360deg)}}\n"] }]
56
+ }] });
57
+
58
+ class DaiInputComponent {
59
+ // Signal Inputs
60
+ label = input('');
61
+ placeholder = input('Text');
62
+ type = input('text');
63
+ disabled = input(false);
64
+ errorMessage = input('');
65
+ size = input('md');
66
+ inputId = input(`dai-input-${Math.random().toString(36).substr(2, 9)}`);
67
+ // Model for two-way binding (Angular 19+)
68
+ value = model('');
69
+ // Outputs for custom handling
70
+ valueChange = output();
71
+ blur = output();
72
+ // Internal state
73
+ onChange = () => { };
74
+ onTouched = () => { };
75
+ // Computed error state
76
+ hasError = computed(() => !!this.errorMessage());
77
+ // Unique error message ID
78
+ errorId = () => `${this.inputId()}-error`;
79
+ // Handle input changes
80
+ onInput(event) {
81
+ const target = event.target;
82
+ const newValue = target.value;
83
+ this.value.set(newValue);
84
+ this.onChange(newValue);
85
+ this.valueChange.emit(newValue);
86
+ }
87
+ // ControlValueAccessor implementation
88
+ writeValue(value) {
89
+ this.value.set(value || '');
90
+ }
91
+ registerOnChange(fn) {
92
+ this.onChange = fn;
93
+ }
94
+ registerOnTouched(fn) {
95
+ this.onTouched = fn;
96
+ }
97
+ setDisabledState(isDisabled) {
98
+ // Handled through the disabled input signal
99
+ }
100
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DaiInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
101
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: DaiInputComponent, isStandalone: true, selector: "dai-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", valueChange: "valueChange", blur: "blur" }, providers: [
102
+ {
103
+ provide: NG_VALUE_ACCESSOR,
104
+ useExisting: forwardRef(() => DaiInputComponent),
105
+ multi: true,
106
+ },
107
+ ], ngImport: i0, template: `
108
+ <div class="dai-input-wrapper" [class.dai-input-error]="hasError()">
109
+ <label [for]="inputId()" class="dai-input-label">
110
+ {{ label() }}
111
+ </label>
112
+
113
+ <input
114
+ [id]="inputId()"
115
+ [type]="type()"
116
+ [placeholder]="placeholder()"
117
+ [value]="value()"
118
+ (input)="onInput($event)"
119
+ (blur)="onTouched()"
120
+ [disabled]="disabled()"
121
+ [class.dai-input-field]="true"
122
+ [class.dai-input-sm]="size() === 'sm'"
123
+ [class.dai-input-md]="size() === 'md'"
124
+ [class.dai-input-lg]="size() === 'lg'"
125
+ [attr.aria-invalid]="hasError()"
126
+ [attr.aria-describedby]="hasError() ? errorId() : null"
127
+ />
128
+
129
+ @if (hasError() && errorMessage()) {
130
+ <span [id]="errorId()" class="dai-input-error-message" role="alert">
131
+ {{ errorMessage() }}
132
+ </span>
133
+ }
134
+ </div>
135
+ `, isInline: true, styles: [":host{display:block;width:100%}.dai-input-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.dai-input-label{display:block;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:16px;font-weight:400;line-height:1.5em;color:#000}.dai-input-field{width:100%;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;font-weight:400;line-height:1.5em;color:#000;background-color:#fff;border:1px solid #E5E0EB;border-radius:50px;box-sizing:border-box;outline:none;transition:all .15s ease-in-out}.dai-input-field::placeholder{color:#aba7af}.dai-input-sm{padding:8px 12px}.dai-input-md{padding:12px;height:44px}.dai-input-lg{padding:14px 12px;height:50px}.dai-input-sm:focus{border-color:#061764;border-width:2px;padding:7px 11px}.dai-input-md:focus,.dai-input-lg:focus{border-color:#112eac;border-width:1px}.dai-input-field:disabled{background-color:#dfdfdf;border-color:#dfdfdf;color:#aba7af;cursor:not-allowed}.dai-input-error .dai-input-field{border-color:#d51a52;border-width:2px}.dai-input-error .dai-input-sm{padding:7px 11px}.dai-input-error .dai-input-md{padding:11px}.dai-input-error .dai-input-lg{padding:13px 11px}.dai-input-error-message{display:block;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;font-weight:400;line-height:1.5em;color:#d51a52}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
136
+ }
137
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: DaiInputComponent, decorators: [{
138
+ type: Component,
139
+ args: [{ selector: 'dai-input', standalone: true, imports: [CommonModule], template: `
140
+ <div class="dai-input-wrapper" [class.dai-input-error]="hasError()">
141
+ <label [for]="inputId()" class="dai-input-label">
142
+ {{ label() }}
143
+ </label>
144
+
145
+ <input
146
+ [id]="inputId()"
147
+ [type]="type()"
148
+ [placeholder]="placeholder()"
149
+ [value]="value()"
150
+ (input)="onInput($event)"
151
+ (blur)="onTouched()"
152
+ [disabled]="disabled()"
153
+ [class.dai-input-field]="true"
154
+ [class.dai-input-sm]="size() === 'sm'"
155
+ [class.dai-input-md]="size() === 'md'"
156
+ [class.dai-input-lg]="size() === 'lg'"
157
+ [attr.aria-invalid]="hasError()"
158
+ [attr.aria-describedby]="hasError() ? errorId() : null"
159
+ />
160
+
161
+ @if (hasError() && errorMessage()) {
162
+ <span [id]="errorId()" class="dai-input-error-message" role="alert">
163
+ {{ errorMessage() }}
164
+ </span>
165
+ }
166
+ </div>
167
+ `, providers: [
168
+ {
169
+ provide: NG_VALUE_ACCESSOR,
170
+ useExisting: forwardRef(() => DaiInputComponent),
171
+ multi: true,
172
+ },
173
+ ], styles: [":host{display:block;width:100%}.dai-input-wrapper{display:flex;flex-direction:column;gap:8px;width:100%}.dai-input-label{display:block;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:16px;font-weight:400;line-height:1.5em;color:#000}.dai-input-field{width:100%;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;font-weight:400;line-height:1.5em;color:#000;background-color:#fff;border:1px solid #E5E0EB;border-radius:50px;box-sizing:border-box;outline:none;transition:all .15s ease-in-out}.dai-input-field::placeholder{color:#aba7af}.dai-input-sm{padding:8px 12px}.dai-input-md{padding:12px;height:44px}.dai-input-lg{padding:14px 12px;height:50px}.dai-input-sm:focus{border-color:#061764;border-width:2px;padding:7px 11px}.dai-input-md:focus,.dai-input-lg:focus{border-color:#112eac;border-width:1px}.dai-input-field:disabled{background-color:#dfdfdf;border-color:#dfdfdf;color:#aba7af;cursor:not-allowed}.dai-input-error .dai-input-field{border-color:#d51a52;border-width:2px}.dai-input-error .dai-input-sm{padding:7px 11px}.dai-input-error .dai-input-md{padding:11px}.dai-input-error .dai-input-lg{padding:13px 11px}.dai-input-error-message{display:block;font-family:Poppins,-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;font-weight:400;line-height:1.5em;color:#d51a52}\n"] }]
174
+ }] });
175
+
176
+ /*
177
+ * Public API Surface of @deay/ui
178
+ */
179
+ // Export components
180
+
181
+ /**
182
+ * Generated bundle index. Do not edit.
183
+ */
184
+
185
+ export { DaiButtonComponent, DaiInputComponent };
186
+ //# sourceMappingURL=deay-ui.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deay-ui.mjs","sources":["../../src/lib/button/button.component.ts","../../src/lib/input/input.component.ts","../../src/public-api.ts","../../src/deay-ui.ts"],"sourcesContent":["import { Component, input, computed } from '@angular/core';\nimport type { CvButtonVariant, CvButtonSize } from './button.variants';\n\n@Component({\n selector: 'dai-button',\n standalone: true,\n template: `\n <button\n [class]=\"computedClasses()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-disabled]=\"isDisabled()\"\n [attr.aria-busy]=\"loading()\"\n >\n @if (loading()) {\n <span class=\"dai-spinner\" aria-hidden=\"true\"></span>\n }\n <ng-content />\n </button>\n `,\n styles: [\n `\n :host {\n display: inline-block;\n }\n\n button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;\n font-weight: 400;\n line-height: 1.5;\n border-radius: 50px;\n cursor: pointer;\n transition: all 150ms ease-in-out;\n border: 1px solid transparent;\n box-sizing: border-box;\n }\n\n button:focus-visible {\n outline: none;\n }\n\n button:disabled {\n cursor: not-allowed;\n opacity: 1;\n }\n\n /* Sizes */\n .dai-button-sm {\n padding: 2px 8px;\n height: 24px;\n font-size: 12px;\n line-height: 1.5em;\n }\n\n .dai-button-md {\n padding: 6px 12px;\n height: 32px;\n font-size: 14px;\n line-height: 1.4285714285714286em;\n }\n\n .dai-button-lg {\n padding: 8px 16px;\n height: 40px;\n font-size: 16px;\n line-height: 1.375em;\n }\n\n /* Primary Variant - Default State */\n .dai-button-primary {\n background-color: #2047F4;\n color: #FFFFFF;\n }\n\n .dai-button-primary:hover:not(:disabled) {\n background-color: #5164F7;\n }\n\n .dai-button-primary:focus-visible {\n background-color: #2047F4;\n border-color: #112EAC;\n box-shadow: 0 0 0 1px #112EAC;\n }\n\n .dai-button-primary:disabled,\n .dai-button-primary.dai-button-loading {\n background-color: #9098FA;\n }\n\n .dai-button-primary.dai-button-loading {\n cursor: wait;\n }\n\n /* Spinner sizes */\n .dai-button-sm .dai-spinner {\n width: 14px;\n height: 14px;\n }\n\n .dai-button-md .dai-spinner {\n width: 16px;\n height: 16px;\n }\n\n .dai-button-lg .dai-spinner {\n width: 22px;\n height: 22px;\n }\n\n .dai-spinner {\n display: inline-block;\n border: 2px solid #FFFFFF;\n border-right-color: transparent;\n border-radius: 50%;\n animation: dai-spin 0.6s linear infinite;\n }\n\n @keyframes dai-spin {\n to {\n transform: rotate(360deg);\n }\n }\n `,\n ],\n})\nexport class DaiButtonComponent {\n // Signal Inputs (Angular 19+)\n readonly variant = input<CvButtonVariant>('primary');\n readonly size = input<CvButtonSize>('md');\n readonly disabled = input<boolean>(false);\n readonly loading = input<boolean>(false);\n\n // Computed properties for dynamic classes\n protected computedClasses = computed(() => {\n const classes = [\n 'dai-button',\n `dai-button-${this.variant()}`,\n `dai-button-${this.size()}`,\n ];\n\n if (this.loading()) {\n classes.push('dai-button-loading');\n }\n\n return classes.join(' ');\n });\n\n // Computed disabled state (disabled or loading)\n protected isDisabled = computed(() => this.disabled() || this.loading());\n}\n","import { Component, input, output, model, forwardRef, computed } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n type ControlValueAccessor,\n NG_VALUE_ACCESSOR,\n} from '@angular/forms';\n\nexport type CvInputSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'dai-input',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"dai-input-wrapper\" [class.dai-input-error]=\"hasError()\">\n <label [for]=\"inputId()\" class=\"dai-input-label\">\n {{ label() }}\n </label>\n\n <input\n [id]=\"inputId()\"\n [type]=\"type()\"\n [placeholder]=\"placeholder()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\"\n [disabled]=\"disabled()\"\n [class.dai-input-field]=\"true\"\n [class.dai-input-sm]=\"size() === 'sm'\"\n [class.dai-input-md]=\"size() === 'md'\"\n [class.dai-input-lg]=\"size() === 'lg'\"\n [attr.aria-invalid]=\"hasError()\"\n [attr.aria-describedby]=\"hasError() ? errorId() : null\"\n />\n\n @if (hasError() && errorMessage()) {\n <span [id]=\"errorId()\" class=\"dai-input-error-message\" role=\"alert\">\n {{ errorMessage() }}\n </span>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n width: 100%;\n }\n\n .dai-input-wrapper {\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: 100%;\n }\n\n .dai-input-label {\n display: block;\n font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 1.5em;\n color: #000000;\n }\n\n .dai-input-field {\n width: 100%;\n font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5em;\n color: #000000;\n background-color: #FFFFFF;\n border: 1px solid #E5E0EB;\n border-radius: 50px;\n box-sizing: border-box;\n outline: none;\n transition: all 150ms ease-in-out;\n }\n\n .dai-input-field::placeholder {\n color: #ABA7AF;\n }\n\n /* Sizes */\n .dai-input-sm {\n padding: 8px 12px;\n }\n\n .dai-input-md {\n padding: 12px;\n height: 44px;\n }\n\n .dai-input-lg {\n padding: 14px 12px;\n height: 50px;\n }\n\n /* Focus State */\n .dai-input-sm:focus {\n border-color: #061764;\n border-width: 2px;\n padding: 7px 11px; /* Adjust for 2px border */\n }\n\n .dai-input-md:focus,\n .dai-input-lg:focus {\n border-color: #112EAC;\n border-width: 1px;\n }\n\n /* Disabled State */\n .dai-input-field:disabled {\n background-color: #DFDFDF;\n border-color: #DFDFDF;\n color: #ABA7AF;\n cursor: not-allowed;\n }\n\n /* Error State */\n .dai-input-error .dai-input-field {\n border-color: #D51A52;\n border-width: 2px;\n }\n\n .dai-input-error .dai-input-sm {\n padding: 7px 11px; /* Adjust for 2px border */\n }\n\n .dai-input-error .dai-input-md {\n padding: 11px; /* Adjust for 2px border */\n }\n\n .dai-input-error .dai-input-lg {\n padding: 13px 11px; /* Adjust for 2px border */\n }\n\n .dai-input-error-message {\n display: block;\n font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5em;\n color: #D51A52;\n }\n `,\n ],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DaiInputComponent),\n multi: true,\n },\n ],\n})\nexport class DaiInputComponent implements ControlValueAccessor {\n // Signal Inputs\n readonly label = input<string>('');\n readonly placeholder = input<string>('Text');\n readonly type = input<'text' | 'password' | 'email'>('text');\n readonly disabled = input<boolean>(false);\n readonly errorMessage = input<string>('');\n readonly size = input<CvInputSize>('md');\n readonly inputId = input<string>(`dai-input-${Math.random().toString(36).substr(2, 9)}`);\n\n // Model for two-way binding (Angular 19+)\n readonly value = model<string>('');\n\n // Outputs for custom handling\n readonly valueChange = output<string>();\n readonly blur = output<void>();\n\n // Internal state\n private onChange: (value: string) => void = () => {};\n onTouched: () => void = () => {};\n\n // Computed error state\n protected hasError = computed(() => !!this.errorMessage());\n\n // Unique error message ID\n protected errorId = () => `${this.inputId()}-error`;\n\n // Handle input changes\n protected onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n const newValue = target.value;\n this.value.set(newValue);\n this.onChange(newValue);\n this.valueChange.emit(newValue);\n }\n\n // ControlValueAccessor implementation\n writeValue(value: string): void {\n this.value.set(value || '');\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n // Handled through the disabled input signal\n }\n}\n","/*\n * Public API Surface of @deay/ui\n */\n\n// Export components\nexport * from './lib/button/button.component';\nexport * from './lib/button/button.variants';\nexport * from './lib/input/input.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;MAgIa,kBAAkB,CAAA;;AAEpB,IAAA,OAAO,GAAG,KAAK,CAAkB,SAAS,CAAC;AAC3C,IAAA,IAAI,GAAG,KAAK,CAAe,IAAI,CAAC;AAChC,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,CAAC;AAChC,IAAA,OAAO,GAAG,KAAK,CAAU,KAAK,CAAC;;AAG9B,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAK;AACxC,QAAA,MAAM,OAAO,GAAG;YACd,YAAY;AACZ,YAAA,CAAA,WAAA,EAAc,IAAI,CAAC,OAAO,EAAE,CAAA,CAAE;AAC9B,YAAA,CAAA,WAAA,EAAc,IAAI,CAAC,IAAI,EAAE,CAAA,CAAE;SAC5B;AAED,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;AAClB,YAAA,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC;QACpC;AAEA,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1B,IAAA,CAAC,CAAC;;AAGQ,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wGAvB7D,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA1HnB;;;;;;;;;;;;AAYT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,21CAAA,CAAA,EAAA,CAAA;;4FA8GU,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBA7H9B,SAAS;+BACE,YAAY,EAAA,UAAA,EACV,IAAI,EAAA,QAAA,EACN;;;;;;;;;;;;AAYT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,21CAAA,CAAA,EAAA;;;MC0IU,iBAAiB,CAAA;;AAEnB,IAAA,KAAK,GAAG,KAAK,CAAS,EAAE,CAAC;AACzB,IAAA,WAAW,GAAG,KAAK,CAAS,MAAM,CAAC;AACnC,IAAA,IAAI,GAAG,KAAK,CAAgC,MAAM,CAAC;AACnD,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,CAAC;AAChC,IAAA,YAAY,GAAG,KAAK,CAAS,EAAE,CAAC;AAChC,IAAA,IAAI,GAAG,KAAK,CAAc,IAAI,CAAC;IAC/B,OAAO,GAAG,KAAK,CAAS,CAAA,UAAA,EAAa,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA,CAAE,CAAC;;AAG/E,IAAA,KAAK,GAAG,KAAK,CAAS,EAAE,CAAC;;IAGzB,WAAW,GAAG,MAAM,EAAU;IAC9B,IAAI,GAAG,MAAM,EAAQ;;AAGtB,IAAA,QAAQ,GAA4B,MAAK,EAAE,CAAC;AACpD,IAAA,SAAS,GAAe,MAAK,EAAE,CAAC;;AAGtB,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;;IAGhD,OAAO,GAAG,MAAM,CAAA,EAAG,IAAI,CAAC,OAAO,EAAE,CAAA,MAAA,CAAQ;;AAGzC,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;AAC/C,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK;AAC7B,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;AACxB,QAAA,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACvB,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;IACjC;;AAGA,IAAA,UAAU,CAAC,KAAa,EAAA;QACtB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAC7B;AAEA,IAAA,gBAAgB,CAAC,EAA2B,EAAA;AAC1C,QAAA,IAAI,CAAC,QAAQ,GAAG,EAAE;IACpB;AAEA,IAAA,iBAAiB,CAAC,EAAc,EAAA;AAC9B,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE;IACrB;AAEA,IAAA,gBAAgB,CAAC,UAAmB,EAAA;;IAEpC;wGAnDW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,KAAA,EAAA,aAAA,EAAA,WAAA,EAAA,aAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EARjB;AACT,YAAA;AACE,gBAAA,OAAO,EAAE,iBAAiB;AAC1B,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,iBAAiB,CAAC;AAChD,gBAAA,KAAK,EAAE,IAAI;AACZ,aAAA;SACF,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA7IS;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+zCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA7BS,YAAY,EAAA,CAAA,EAAA,CAAA;;4FAgJX,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAnJ7B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,cACT,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,QAAA,EACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BT,EAAA,SAAA,EA2GU;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,iBAAiB;AAC1B,4BAAA,WAAW,EAAE,UAAU,CAAC,uBAAuB,CAAC;AAChD,4BAAA,KAAK,EAAE,IAAI;AACZ,yBAAA;AACF,qBAAA,EAAA,MAAA,EAAA,CAAA,+zCAAA,CAAA,EAAA;;;AC1JH;;AAEG;AAEH;;ACJA;;AAEG;;;;"}
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="@deay/ui" />
5
+ export * from './public-api';
@@ -0,0 +1,12 @@
1
+ import type { CvButtonSize } from './button.variants';
2
+ import * as i0 from "@angular/core";
3
+ export declare class DaiButtonComponent {
4
+ readonly variant: import("@angular/core").InputSignal<"primary">;
5
+ readonly size: import("@angular/core").InputSignal<CvButtonSize>;
6
+ readonly disabled: import("@angular/core").InputSignal<boolean>;
7
+ readonly loading: import("@angular/core").InputSignal<boolean>;
8
+ protected computedClasses: import("@angular/core").Signal<string>;
9
+ protected isDisabled: import("@angular/core").Signal<boolean>;
10
+ static ɵfac: i0.ɵɵFactoryDeclaration<DaiButtonComponent, never>;
11
+ static ɵcmp: i0.ɵɵComponentDeclaration<DaiButtonComponent, "dai-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
12
+ }
@@ -1,3 +1,2 @@
1
- // Only primary variant based on Figma design
2
1
  export type CvButtonVariant = 'primary';
3
2
  export type CvButtonSize = 'sm' | 'md' | 'lg';
@@ -0,0 +1,26 @@
1
+ import { type ControlValueAccessor } from '@angular/forms';
2
+ import * as i0 from "@angular/core";
3
+ export type CvInputSize = 'sm' | 'md' | 'lg';
4
+ export declare class DaiInputComponent implements ControlValueAccessor {
5
+ readonly label: import("@angular/core").InputSignal<string>;
6
+ readonly placeholder: import("@angular/core").InputSignal<string>;
7
+ readonly type: import("@angular/core").InputSignal<"text" | "password" | "email">;
8
+ readonly disabled: import("@angular/core").InputSignal<boolean>;
9
+ readonly errorMessage: import("@angular/core").InputSignal<string>;
10
+ readonly size: import("@angular/core").InputSignal<CvInputSize>;
11
+ readonly inputId: import("@angular/core").InputSignal<string>;
12
+ readonly value: import("@angular/core").ModelSignal<string>;
13
+ readonly valueChange: import("@angular/core").OutputEmitterRef<string>;
14
+ readonly blur: import("@angular/core").OutputEmitterRef<void>;
15
+ private onChange;
16
+ onTouched: () => void;
17
+ protected hasError: import("@angular/core").Signal<boolean>;
18
+ protected errorId: () => string;
19
+ protected onInput(event: Event): void;
20
+ writeValue(value: string): void;
21
+ registerOnChange(fn: (value: string) => void): void;
22
+ registerOnTouched(fn: () => void): void;
23
+ setDisabledState(isDisabled: boolean): void;
24
+ static ɵfac: i0.ɵɵFactoryDeclaration<DaiInputComponent, never>;
25
+ static ɵcmp: i0.ɵɵComponentDeclaration<DaiInputComponent, "dai-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "valueChange": "valueChange"; "blur": "blur"; }, never, never, true, never>;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deay/ui",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
6
  "@angular/core": "^19.0.0",
@@ -1,8 +1,3 @@
1
- /*
2
- * Public API Surface of @deay/ui
3
- */
4
-
5
- // Export components
6
1
  export * from './lib/button/button.component';
7
2
  export * from './lib/button/button.variants';
8
3
  export * from './lib/input/input.component';
package/ng-package.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "../../dist/ui",
4
- "lib": {
5
- "entryFile": "src/public-api.ts"
6
- }
7
- }
@@ -1,153 +0,0 @@
1
- import { Component, input, computed } from '@angular/core';
2
- import type { CvButtonVariant, CvButtonSize } from './button.variants';
3
-
4
- @Component({
5
- selector: 'dai-button',
6
- standalone: true,
7
- template: `
8
- <button
9
- [class]="computedClasses()"
10
- [disabled]="isDisabled()"
11
- [attr.aria-disabled]="isDisabled()"
12
- [attr.aria-busy]="loading()"
13
- >
14
- @if (loading()) {
15
- <span class="dai-spinner" aria-hidden="true"></span>
16
- }
17
- <ng-content />
18
- </button>
19
- `,
20
- styles: [
21
- `
22
- :host {
23
- display: inline-block;
24
- }
25
-
26
- button {
27
- display: inline-flex;
28
- align-items: center;
29
- justify-content: center;
30
- gap: 4px;
31
- font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
32
- font-weight: 400;
33
- line-height: 1.5;
34
- border-radius: 50px;
35
- cursor: pointer;
36
- transition: all 150ms ease-in-out;
37
- border: 1px solid transparent;
38
- box-sizing: border-box;
39
- }
40
-
41
- button:focus-visible {
42
- outline: none;
43
- }
44
-
45
- button:disabled {
46
- cursor: not-allowed;
47
- opacity: 1;
48
- }
49
-
50
- /* Sizes */
51
- .dai-button-sm {
52
- padding: 2px 8px;
53
- height: 24px;
54
- font-size: 12px;
55
- line-height: 1.5em;
56
- }
57
-
58
- .dai-button-md {
59
- padding: 6px 12px;
60
- height: 32px;
61
- font-size: 14px;
62
- line-height: 1.4285714285714286em;
63
- }
64
-
65
- .dai-button-lg {
66
- padding: 8px 16px;
67
- height: 40px;
68
- font-size: 16px;
69
- line-height: 1.375em;
70
- }
71
-
72
- /* Primary Variant - Default State */
73
- .dai-button-primary {
74
- background-color: #2047F4;
75
- color: #FFFFFF;
76
- }
77
-
78
- .dai-button-primary:hover:not(:disabled) {
79
- background-color: #5164F7;
80
- }
81
-
82
- .dai-button-primary:focus-visible {
83
- background-color: #2047F4;
84
- border-color: #112EAC;
85
- box-shadow: 0 0 0 1px #112EAC;
86
- }
87
-
88
- .dai-button-primary:disabled,
89
- .dai-button-primary.dai-button-loading {
90
- background-color: #9098FA;
91
- }
92
-
93
- .dai-button-primary.dai-button-loading {
94
- cursor: wait;
95
- }
96
-
97
- /* Spinner sizes */
98
- .dai-button-sm .dai-spinner {
99
- width: 14px;
100
- height: 14px;
101
- }
102
-
103
- .dai-button-md .dai-spinner {
104
- width: 16px;
105
- height: 16px;
106
- }
107
-
108
- .dai-button-lg .dai-spinner {
109
- width: 22px;
110
- height: 22px;
111
- }
112
-
113
- .dai-spinner {
114
- display: inline-block;
115
- border: 2px solid #FFFFFF;
116
- border-right-color: transparent;
117
- border-radius: 50%;
118
- animation: dai-spin 0.6s linear infinite;
119
- }
120
-
121
- @keyframes dai-spin {
122
- to {
123
- transform: rotate(360deg);
124
- }
125
- }
126
- `,
127
- ],
128
- })
129
- export class DaiButtonComponent {
130
- // Signal Inputs (Angular 19+)
131
- readonly variant = input<CvButtonVariant>('primary');
132
- readonly size = input<CvButtonSize>('md');
133
- readonly disabled = input<boolean>(false);
134
- readonly loading = input<boolean>(false);
135
-
136
- // Computed properties for dynamic classes
137
- protected computedClasses = computed(() => {
138
- const classes = [
139
- 'dai-button',
140
- `dai-button-${this.variant()}`,
141
- `dai-button-${this.size()}`,
142
- ];
143
-
144
- if (this.loading()) {
145
- classes.push('dai-button-loading');
146
- }
147
-
148
- return classes.join(' ');
149
- });
150
-
151
- // Computed disabled state (disabled or loading)
152
- protected isDisabled = computed(() => this.disabled() || this.loading());
153
- }
@@ -1,209 +0,0 @@
1
- import { Component, input, output, model, forwardRef, computed } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import {
4
- type ControlValueAccessor,
5
- NG_VALUE_ACCESSOR,
6
- } from '@angular/forms';
7
-
8
- export type CvInputSize = 'sm' | 'md' | 'lg';
9
-
10
- @Component({
11
- selector: 'dai-input',
12
- standalone: true,
13
- imports: [CommonModule],
14
- template: `
15
- <div class="dai-input-wrapper" [class.dai-input-error]="hasError()">
16
- <label [for]="inputId()" class="dai-input-label">
17
- {{ label() }}
18
- </label>
19
-
20
- <input
21
- [id]="inputId()"
22
- [type]="type()"
23
- [placeholder]="placeholder()"
24
- [value]="value()"
25
- (input)="onInput($event)"
26
- (blur)="onTouched()"
27
- [disabled]="disabled()"
28
- [class.dai-input-field]="true"
29
- [class.dai-input-sm]="size() === 'sm'"
30
- [class.dai-input-md]="size() === 'md'"
31
- [class.dai-input-lg]="size() === 'lg'"
32
- [attr.aria-invalid]="hasError()"
33
- [attr.aria-describedby]="hasError() ? errorId() : null"
34
- />
35
-
36
- @if (hasError() && errorMessage()) {
37
- <span [id]="errorId()" class="dai-input-error-message" role="alert">
38
- {{ errorMessage() }}
39
- </span>
40
- }
41
- </div>
42
- `,
43
- styles: [
44
- `
45
- :host {
46
- display: block;
47
- width: 100%;
48
- }
49
-
50
- .dai-input-wrapper {
51
- display: flex;
52
- flex-direction: column;
53
- gap: 8px;
54
- width: 100%;
55
- }
56
-
57
- .dai-input-label {
58
- display: block;
59
- font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
60
- font-size: 16px;
61
- font-weight: 400;
62
- line-height: 1.5em;
63
- color: #000000;
64
- }
65
-
66
- .dai-input-field {
67
- width: 100%;
68
- font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
69
- font-size: 14px;
70
- font-weight: 400;
71
- line-height: 1.5em;
72
- color: #000000;
73
- background-color: #FFFFFF;
74
- border: 1px solid #E5E0EB;
75
- border-radius: 50px;
76
- box-sizing: border-box;
77
- outline: none;
78
- transition: all 150ms ease-in-out;
79
- }
80
-
81
- .dai-input-field::placeholder {
82
- color: #ABA7AF;
83
- }
84
-
85
- /* Sizes */
86
- .dai-input-sm {
87
- padding: 8px 12px;
88
- }
89
-
90
- .dai-input-md {
91
- padding: 12px;
92
- height: 44px;
93
- }
94
-
95
- .dai-input-lg {
96
- padding: 14px 12px;
97
- height: 50px;
98
- }
99
-
100
- /* Focus State */
101
- .dai-input-sm:focus {
102
- border-color: #061764;
103
- border-width: 2px;
104
- padding: 7px 11px; /* Adjust for 2px border */
105
- }
106
-
107
- .dai-input-md:focus,
108
- .dai-input-lg:focus {
109
- border-color: #112EAC;
110
- border-width: 1px;
111
- }
112
-
113
- /* Disabled State */
114
- .dai-input-field:disabled {
115
- background-color: #DFDFDF;
116
- border-color: #DFDFDF;
117
- color: #ABA7AF;
118
- cursor: not-allowed;
119
- }
120
-
121
- /* Error State */
122
- .dai-input-error .dai-input-field {
123
- border-color: #D51A52;
124
- border-width: 2px;
125
- }
126
-
127
- .dai-input-error .dai-input-sm {
128
- padding: 7px 11px; /* Adjust for 2px border */
129
- }
130
-
131
- .dai-input-error .dai-input-md {
132
- padding: 11px; /* Adjust for 2px border */
133
- }
134
-
135
- .dai-input-error .dai-input-lg {
136
- padding: 13px 11px; /* Adjust for 2px border */
137
- }
138
-
139
- .dai-input-error-message {
140
- display: block;
141
- font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
142
- font-size: 14px;
143
- font-weight: 400;
144
- line-height: 1.5em;
145
- color: #D51A52;
146
- }
147
- `,
148
- ],
149
- providers: [
150
- {
151
- provide: NG_VALUE_ACCESSOR,
152
- useExisting: forwardRef(() => DaiInputComponent),
153
- multi: true,
154
- },
155
- ],
156
- })
157
- export class DaiInputComponent implements ControlValueAccessor {
158
- // Signal Inputs
159
- readonly label = input<string>('');
160
- readonly placeholder = input<string>('Text');
161
- readonly type = input<'text' | 'password' | 'email'>('text');
162
- readonly disabled = input<boolean>(false);
163
- readonly errorMessage = input<string>('');
164
- readonly size = input<CvInputSize>('md');
165
- readonly inputId = input<string>(`dai-input-${Math.random().toString(36).substr(2, 9)}`);
166
-
167
- // Model for two-way binding (Angular 19+)
168
- readonly value = model<string>('');
169
-
170
- // Outputs for custom handling
171
- readonly valueChange = output<string>();
172
- readonly blur = output<void>();
173
-
174
- // Internal state
175
- private onChange: (value: string) => void = () => {};
176
- onTouched: () => void = () => {};
177
-
178
- // Computed error state
179
- protected hasError = computed(() => !!this.errorMessage());
180
-
181
- // Unique error message ID
182
- protected errorId = () => `${this.inputId()}-error`;
183
-
184
- // Handle input changes
185
- protected onInput(event: Event): void {
186
- const target = event.target as HTMLInputElement;
187
- const newValue = target.value;
188
- this.value.set(newValue);
189
- this.onChange(newValue);
190
- this.valueChange.emit(newValue);
191
- }
192
-
193
- // ControlValueAccessor implementation
194
- writeValue(value: string): void {
195
- this.value.set(value || '');
196
- }
197
-
198
- registerOnChange(fn: (value: string) => void): void {
199
- this.onChange = fn;
200
- }
201
-
202
- registerOnTouched(fn: () => void): void {
203
- this.onTouched = fn;
204
- }
205
-
206
- setDisabledState(isDisabled: boolean): void {
207
- // Handled through the disabled input signal
208
- }
209
- }
package/tsconfig.lib.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc/packages/ui",
5
- "declaration": true,
6
- "declarationMap": true,
7
- "inlineSources": true,
8
- "types": []
9
- },
10
- "include": ["src/**/*.ts"],
11
- "exclude": ["src/test.ts", "**/*.spec.ts"]
12
- }