wally-ui 1.10.0 → 1.11.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wally-ui",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "About Where’s Wally? Right here — bringing you ready-to-use Angular components with Wally-UI. Stop searching, start building.",
5
5
  "bin": {
6
6
  "wally": "dist/cli.js"
@@ -1,10 +1,7 @@
1
1
  <button [type]="type()" [disabled]="disabled() || loading()" [attr.aria-label]="ariaLabel() || null"
2
2
  [attr.aria-describedby]="ariaDescribedBy() || null" [attr.aria-pressed]="ariaPressed()" [attr.aria-busy]="loading()"
3
- (click)="handleClick()" class="group relative w-full flex items-center justify-center gap-2 text-sm font-medium disabled:pointer-events-none p-2.5 rounded-md transition duration-300 ease-in-out antialiased cursor-pointer
4
- " [ngClass]="{
5
- 'text-white dark:text-[#0a0a0a] bg-[#0a0a0a] hover:bg-[#0a0a0a]/85 disabled:bg-[#0a0a0a]/85 dark:bg-white dark:hover:bg-white/85 dark:disabled:bg-white/85 dark:disabled:text-[#0a0a0a]/60': variant() === 'primary',
6
- 'text-[#0a0a0a] bg-neutral-200 hover:bg-neutral-200/60 disabled:bg-neutral-200/60 disabled:text-neutral-400 dark:text-white dark:bg-white/20 dark:hover:bg-white/10 dark:disabled:bg-white/5 dark:disabled:text-white/50': variant() === 'secondary',
7
- }">
3
+ (click)="handleClick()"
4
+ [class]="'group relative w-full flex items-center justify-center gap-2 text-sm font-medium text-white disabled:pointer-events-none p-2.5 rounded-md transition-all duration-300 ease-in-out antialiased cursor-pointer ' + variantClasses()">
8
5
 
9
6
  @if (showNotification()) {
10
7
  <span class="absolute top-0 right-0 -mt-1 -mr-1 flex size-3">
@@ -16,10 +13,7 @@
16
13
  }
17
14
 
18
15
  @if (loading()) {
19
- <svg class="size-4 animate-spin" [ngClass]="{
20
- 'text-white dark:text-[#0a0a0a]': variant() === 'primary',
21
- 'dark:text-white': variant() === 'secondary',
22
- }" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
16
+ <svg class="size-4 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
23
17
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
24
18
  <path class="opacity-75" fill="currentColor"
25
19
  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
@@ -1,7 +1,8 @@
1
- import { Component, input, InputSignal, output, OutputEmitterRef } from '@angular/core';
1
+ import { Component, computed, inject, input, InputSignal, output, OutputEmitterRef, Signal } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
+ import { Router } from '@angular/router';
3
4
 
4
- type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive';
5
+ type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
5
6
 
6
7
  @Component({
7
8
  selector: 'wally-button',
@@ -12,11 +13,14 @@ type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructiv
12
13
  templateUrl: './button.html',
13
14
  })
14
15
  export class Button {
16
+ private router = inject(Router);
17
+
15
18
  type: InputSignal<string> = input<string>('button');
16
19
  disabled: InputSignal<boolean> = input<boolean>(false);
17
20
  loading: InputSignal<boolean> = input<boolean>(false);
18
21
  showNotification: InputSignal<boolean> = input<boolean>(false);
19
22
  variant: InputSignal<ButtonVariant> = input<ButtonVariant>('primary');
23
+ href: InputSignal<string> = input<string>('');
20
24
 
21
25
  // Accessibility properties
22
26
  ariaLabel: InputSignal<string> = input<string>('');
@@ -25,7 +29,29 @@ export class Button {
25
29
 
26
30
  click: OutputEmitterRef<void> = output<void>();
27
31
 
32
+ // Computed classes based on variant
33
+ variantClasses: Signal<string> = computed(() => {
34
+ const variantMap: Record<ButtonVariant, string> = {
35
+ primary: 'bg-[#0a0a0a] hover:bg-[#0a0a0a]/85 disabled:bg-[#0a0a0a]/85 dark:text-[#0a0a0a] dark:bg-white dark:hover:bg-white/85 dark:disabled:bg-white/85 dark:disabled:text-[#0a0a0a]/60',
36
+ secondary: '!text-[#0a0a0a] bg-neutral-200 hover:bg-neutral-200/60 disabled:bg-neutral-200/80 disabled:!text-neutral-400 dark:!text-white dark:bg-white/20 dark:hover:bg-white/10 dark:disabled:bg-white/5 dark:disabled:text-white/50',
37
+ destructive: 'dark:text-white bg-red-500 hover:bg-red-500/80 disabled:bg-red-500/80 disabled:text-white/80 dark:disabled:text-white/60',
38
+ outline: '!text-[#0a0a0a] bg-transparent border border-neutral-400 hover:bg-neutral-200/60 disabled:!text-neutral-500 dark:!text-white dark:border-neutral-500 dark:bg-neutral-500/10 dark:hover:bg-neutral-500/20 dark:disabled:!text-white/60',
39
+ ghost: '!text-[#0a0a0a] bg-transparent hover:bg-neutral-100 disabled:!text-neutral-400 dark:!text-white dark:hover:bg-white/5 dark:disabled:!text-white/50',
40
+ link: '!text-blue-600 bg-transparent underline-offset-4 hover:underline disabled:!text-blue-400 dark:!text-blue-600 dark:hover:!text-blue-500 dark:disabled:!text-blue-400'
41
+ };
42
+
43
+ return variantMap[this.variant()] || variantMap.primary;
44
+ });
45
+
28
46
  handleClick(): void {
47
+ if (this.variant() === 'link' && this.href()) {
48
+ if (this.href().startsWith('http://') || this.href().startsWith('https://')) {
49
+ window.open(this.href(), '_blank');
50
+ } else {
51
+ this.router.navigate([this.href()]);
52
+ }
53
+ }
54
+
29
55
  this.click.emit();
30
56
  }
31
57
  }
@@ -8,34 +8,171 @@ export const ButtonCodeExamples = {
8
8
  componentImport: `@Component({
9
9
  selector: 'app-example',
10
10
  imports: [Button],
11
- templateUrl: './example.html',
12
- styleUrl: './example.css'
11
+ templateUrl: './example.html'
13
12
  })`,
14
13
 
15
14
  // Basic usage
16
- basicUsage: `<wally-button>Wally Button</wally-button>`,
15
+ basicUsage: `<wally-button>Click me</wally-button>`,
17
16
 
18
- // Variants
19
- primaryVariant: `<wally-button variant="primary">Primary Button</wally-button>`,
17
+ // === VARIANTS ===
18
+
19
+ // Primary (Default)
20
+ primaryVariant: `<!-- Default variant -->
21
+ <wally-button>Primary Button</wally-button>
22
+
23
+ <!-- Explicit primary -->
24
+ <wally-button variant="primary">Primary Button</wally-button>`,
25
+
26
+ // Secondary
20
27
  secondaryVariant: `<wally-button variant="secondary">Secondary Button</wally-button>`,
21
28
 
22
- // States
29
+ // Destructive
30
+ destructiveVariant: `<wally-button variant="destructive">Delete Account</wally-button>`,
31
+
32
+ // Outline
33
+ outlineVariant: `<wally-button variant="outline">Outline Button</wally-button>`,
34
+
35
+ // Ghost
36
+ ghostVariant: `<wally-button variant="ghost">Ghost Button</wally-button>`,
37
+
38
+ // Link
39
+ linkVariant: `<!-- Internal navigation -->
40
+ <wally-button variant="link" href="/components">View Components</wally-button>
41
+
42
+ <!-- External link -->
43
+ <wally-button variant="link" href="https://github.com">GitHub</wally-button>`,
44
+
45
+ // === STATES ===
46
+
23
47
  disabled: `<wally-button [disabled]="true">Disabled</wally-button>`,
24
- loading: `<wally-button [loading]="true">Loading</wally-button>`,
48
+
49
+ loading: `<wally-button [loading]="true">Loading...</wally-button>`,
50
+
25
51
  notification: `<wally-button [showNotification]="true">Messages</wally-button>`,
26
52
 
27
- // Types
53
+ // === PRODUCTION EXAMPLES ===
54
+
55
+ // CTA Button
56
+ ctaExample: `<!-- Call-to-Action Example -->
57
+ <wally-button>
58
+ Get Started Free
59
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
60
+ stroke-width="2" stroke="currentColor" class="size-5">
61
+ <path stroke-linecap="round" stroke-linejoin="round"
62
+ d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
63
+ </svg>
64
+ </wally-button>`,
65
+
66
+ // Login Form
67
+ loginExample: `<!-- Login Form Example -->
68
+ <form (ngSubmit)="onLogin()">
69
+ <!-- ... form fields ... -->
70
+
71
+ <div class="flex gap-2">
72
+ <wally-button
73
+ type="submit"
74
+ [loading]="isLoggingIn()"
75
+ [disabled]="!loginForm.valid">
76
+ Sign In
77
+ </wally-button>
78
+
79
+ <wally-button
80
+ variant="secondary"
81
+ type="button"
82
+ (click)="goToSignUp()">
83
+ Create Account
84
+ </wally-button>
85
+ </div>
86
+ </form>`,
87
+
88
+ loginExampleTs: `export class LoginComponent {
89
+ isLoggingIn = signal(false);
90
+ loginForm: FormGroup;
91
+
92
+ onLogin() {
93
+ this.isLoggingIn.set(true);
94
+
95
+ this.authService.login(this.loginForm.value)
96
+ .subscribe({
97
+ next: () => this.router.navigate(['/dashboard']),
98
+ error: () => this.isLoggingIn.set(false)
99
+ });
100
+ }
101
+ }`,
102
+
103
+ // Delete Confirmation
104
+ deleteExample: `<!-- Delete Confirmation Modal -->
105
+ <div class="modal">
106
+ <h2>Delete Account?</h2>
107
+ <p>This action cannot be undone.</p>
108
+
109
+ <div class="flex gap-2 justify-end">
110
+ <wally-button
111
+ variant="ghost"
112
+ (click)="closeModal()">
113
+ Cancel
114
+ </wally-button>
115
+
116
+ <wally-button
117
+ variant="destructive"
118
+ [loading]="isDeleting()"
119
+ (click)="confirmDelete()">
120
+ Delete Account
121
+ </wally-button>
122
+ </div>
123
+ </div>`,
124
+
125
+ // Dashboard Actions
126
+ dashboardExample: `<!-- Dashboard Actions -->
127
+ <div class="dashboard-header">
128
+ <wally-button variant="outline" (click)="exportData()">
129
+ Export
130
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
131
+ stroke-width="1.5" stroke="currentColor" class="size-5">
132
+ <path stroke-linecap="round" stroke-linejoin="round"
133
+ d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
134
+ </svg>
135
+ </wally-button>
136
+
137
+ <wally-button (click)="createNew()">
138
+ Create New
139
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
140
+ stroke-width="2" stroke="currentColor" class="size-5">
141
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
142
+ </svg>
143
+ </wally-button>
144
+ </div>`,
145
+
146
+ // Icon Button with Notification
147
+ notificationButton: `<!-- Notification Icon Button -->
148
+ <wally-button
149
+ [showNotification]="hasUnreadMessages()"
150
+ ariaLabel="View messages">
151
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
152
+ stroke-width="1.5" stroke="currentColor" class="size-5">
153
+ <path stroke-linecap="round" stroke-linejoin="round"
154
+ d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
155
+ </svg>
156
+ </wally-button>`,
157
+
158
+ // === BUTTON TYPES ===
159
+
28
160
  submit: `<wally-button type="submit">Submit Form</wally-button>`,
161
+ reset: `<wally-button type="reset" variant="ghost">Reset</wally-button>`,
162
+
163
+ // === EVENTS ===
29
164
 
30
- // Events
31
165
  clickTemplate: `<wally-button (click)="handleClick()">Click Me</wally-button>`,
166
+
32
167
  clickMethod: `handleClick(): void {
33
- this.clickMessage.set('Button clicked!');
168
+ console.log('Button clicked!');
169
+ // Your logic here
34
170
  }`,
35
171
 
36
- // Icons
172
+ // === ICONS ===
173
+
37
174
  iconWithText: `<wally-button>
38
- Save
175
+ Save Changes
39
176
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
40
177
  stroke-width="1.5" stroke="currentColor" class="size-5">
41
178
  <path stroke-linecap="round" stroke-linejoin="round"
@@ -43,7 +180,7 @@ export const ButtonCodeExamples = {
43
180
  </svg>
44
181
  </wally-button>`,
45
182
 
46
- iconOnly: `<wally-button>
183
+ iconOnly: `<wally-button ariaLabel="Notifications">
47
184
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
48
185
  stroke-width="1.5" stroke="currentColor" class="size-5">
49
186
  <path stroke-linecap="round" stroke-linejoin="round"
@@ -51,8 +188,19 @@ export const ButtonCodeExamples = {
51
188
  </svg>
52
189
  </wally-button>`,
53
190
 
54
- // Accessibility
55
- ariaLabel: `<wally-button ariaLabel="Save document">
191
+ iconLeft: `<wally-button>
192
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
193
+ stroke-width="2" stroke="currentColor" class="size-5">
194
+ <path stroke-linecap="round" stroke-linejoin="round"
195
+ d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
196
+ </svg>
197
+ Back
198
+ </wally-button>`,
199
+
200
+ // === ACCESSIBILITY ===
201
+
202
+ ariaLabel: `<!-- Essential for icon-only buttons -->
203
+ <wally-button ariaLabel="Save document">
56
204
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
57
205
  stroke-width="1.5" stroke="currentColor" class="size-5">
58
206
  <path stroke-linecap="round" stroke-linejoin="round"
@@ -60,20 +208,21 @@ export const ButtonCodeExamples = {
60
208
  </svg>
61
209
  </wally-button>`,
62
210
 
63
- ariaPressed: `<wally-button [ariaPressed]="isToggled">
64
- Toggle Setting
211
+ ariaPressed: `<!-- For toggle buttons -->
212
+ <wally-button [ariaPressed]="isMuted()">
213
+ {{ isMuted() ? 'Unmute' : 'Mute' }}
65
214
  </wally-button>`,
66
215
 
67
- ariaBusy: `<wally-button [loading]="true">
68
- Processing...
216
+ ariaBusy: `<!-- Automatically set when loading="true" -->
217
+ <wally-button [loading]="isSaving()">
218
+ Save Changes
69
219
  </wally-button>`,
70
220
 
71
- // Properties
72
- propertyType: `type: string = 'button';`,
73
- propertyDisabled: `disabled: boolean = false;`,
74
- propertyLoading: `loading: boolean = false;`,
75
- propertyShowNotification: `showNotification: boolean = false;`,
76
- propertyAriaLabel: `ariaLabel: string = '';`,
77
- propertyAriaPressed: `ariaPressed: boolean | undefined = undefined;`,
78
- propertyAriaDescribedBy: `ariaDescribedBy: string = '';`,
79
- };
221
+ ariaDescribedBy: `<!-- Connect button to description -->
222
+ <wally-button ariaDescribedBy="save-description">
223
+ Save
224
+ </wally-button>
225
+ <p id="save-description" class="sr-only">
226
+ Saves your changes permanently
227
+ </p>`,
228
+ };