wally-ui 1.12.1 → 1.13.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.
Files changed (67) hide show
  1. package/dist/cli.js +8 -5
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
  4. package/playground/showcase/src/app/app.routes.server.ts +4 -0
  5. package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.html +164 -31
  6. package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.ts +25 -3
  7. package/playground/showcase/src/app/components/ai/ai-prompt-input/ai-prompt-input.html +1 -1
  8. package/playground/showcase/src/app/components/badge/badge.css +0 -0
  9. package/playground/showcase/src/app/components/badge/badge.html +3 -0
  10. package/playground/showcase/src/app/components/badge/badge.ts +24 -0
  11. package/playground/showcase/src/app/components/button/button.html +1 -3
  12. package/playground/showcase/src/app/components/button/button.ts +4 -4
  13. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-content/dropdown-menu-content.css +0 -0
  14. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-content/dropdown-menu-content.html +9 -0
  15. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-content/dropdown-menu-content.spec.ts +23 -0
  16. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-content/dropdown-menu-content.ts +167 -0
  17. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-group/dropdown-menu-group.css +0 -0
  18. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-group/dropdown-menu-group.html +5 -0
  19. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-group/dropdown-menu-group.spec.ts +23 -0
  20. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-group/dropdown-menu-group.ts +10 -0
  21. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-item/dropdown-menu-item.css +0 -0
  22. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-item/dropdown-menu-item.html +6 -0
  23. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-item/dropdown-menu-item.spec.ts +23 -0
  24. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-item/dropdown-menu-item.ts +37 -0
  25. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-label/dropdown-menu-label.css +0 -0
  26. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-label/dropdown-menu-label.html +3 -0
  27. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-label/dropdown-menu-label.spec.ts +23 -0
  28. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-label/dropdown-menu-label.ts +11 -0
  29. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-portal/dropdown-menu-portal.css +0 -0
  30. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-portal/dropdown-menu-portal.html +1 -0
  31. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-portal/dropdown-menu-portal.spec.ts +23 -0
  32. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-portal/dropdown-menu-portal.ts +11 -0
  33. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-separator/dropdown-menu-separator.css +0 -0
  34. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-separator/dropdown-menu-separator.html +1 -0
  35. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-separator/dropdown-menu-separator.spec.ts +23 -0
  36. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-separator/dropdown-menu-separator.ts +11 -0
  37. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub/dropdown-menu-sub.css +0 -0
  38. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub/dropdown-menu-sub.html +3 -0
  39. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub/dropdown-menu-sub.spec.ts +23 -0
  40. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub/dropdown-menu-sub.ts +16 -0
  41. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content.css +0 -0
  42. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content.html +9 -0
  43. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content.spec.ts +23 -0
  44. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content.ts +31 -0
  45. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger.css +0 -0
  46. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger.html +13 -0
  47. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger.spec.ts +23 -0
  48. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger.ts +40 -0
  49. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub.service.spec.ts +16 -0
  50. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-sub.service.ts +23 -0
  51. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger.css +0 -0
  52. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger.html +8 -0
  53. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger.spec.ts +23 -0
  54. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger.ts +55 -0
  55. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu.css +0 -0
  56. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu.html +3 -0
  57. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu.service.spec.ts +16 -0
  58. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu.service.ts +31 -0
  59. package/playground/showcase/src/app/components/dropdown-menu/dropdown-menu.ts +69 -0
  60. package/playground/showcase/src/app/components/tooltip/tooltip.ts +195 -80
  61. package/playground/showcase/src/app/pages/documentation/components/components.html +110 -51
  62. package/playground/showcase/src/app/pages/documentation/components/components.routes.ts +4 -0
  63. package/playground/showcase/src/app/pages/documentation/components/dropdown-menu-docs/dropdown-menu-docs.css +1 -0
  64. package/playground/showcase/src/app/pages/documentation/components/dropdown-menu-docs/dropdown-menu-docs.examples.ts +404 -0
  65. package/playground/showcase/src/app/pages/documentation/components/dropdown-menu-docs/dropdown-menu-docs.html +612 -0
  66. package/playground/showcase/src/app/pages/documentation/components/dropdown-menu-docs/dropdown-menu-docs.ts +127 -0
  67. package/playground/showcase/src/app/pages/home/home.html +10 -6
package/dist/cli.js CHANGED
@@ -64,11 +64,14 @@ program
64
64
  }
65
65
  const playgroundPath = path_1.default.join(__dirname, '..', 'playground', 'showcase', 'src', 'app', 'components', component);
66
66
  try {
67
- const typescriptFile = await fs_extra_1.default.readFile(path_1.default.join(playgroundPath, `${component}.ts`), 'utf8');
68
- const htmlFile = await fs_extra_1.default.readFile(path_1.default.join(playgroundPath, `${component}.html`), 'utf8');
69
- await fs_extra_1.default.ensureDir(componentPath);
70
- await fs_extra_1.default.writeFile(`${componentPath}/${component}.ts`, typescriptFile);
71
- await fs_extra_1.default.writeFile(`${componentPath}/${component}.html`, htmlFile);
67
+ if (!await fs_extra_1.default.pathExists(playgroundPath)) {
68
+ console.log(chalk_1.default.red(`Component '${component}' not found in playground`));
69
+ return;
70
+ }
71
+ await fs_extra_1.default.copy(playgroundPath, componentPath, {
72
+ overwrite: true,
73
+ errorOnExist: false
74
+ });
72
75
  console.log(chalk_1.default.green('Template loaded successfully!'));
73
76
  }
74
77
  catch (error) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wally-ui",
3
- "version": "1.12.1",
3
+ "version": "1.13.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"
@@ -29,6 +29,10 @@ export const serverRoutes: ServerRoute[] = [
29
29
  path: 'documentation/components/tooltip',
30
30
  renderMode: RenderMode.Prerender,
31
31
  },
32
+ {
33
+ path: 'documentation/components/dropdown-menu',
34
+ renderMode: RenderMode.Prerender,
35
+ },
32
36
  {
33
37
  path: 'documentation/chat-sdk',
34
38
  renderMode: RenderMode.Prerender,
@@ -1,15 +1,12 @@
1
1
  <div class="w-full flex flex-col" role="region" aria-label="AI message composer">
2
2
  <!-- Selected Text Context -->
3
3
  <div class="p-0.5">
4
- <div
5
- id="selected-context"
4
+ <div id="selected-context"
6
5
  class="w-full flex items-center gap-1 justify-between border bg-neutral-100 border-neutral-300 dark:bg-[#121212] dark:border-neutral-700 rounded-t-3xl rounded-b-lg p-3"
7
- role="status"
8
- aria-live="polite">
6
+ role="status" aria-live="polite">
9
7
  <div class="w-full flex gap-4 items-center">
10
8
  <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
11
- class="size-5 text-[#0a0a0a] dark:text-white"
12
- aria-hidden="true">
9
+ class="size-5 text-[#0a0a0a] dark:text-white" aria-hidden="true">
13
10
  <path
14
11
  d="M12.5293 6.5293C12.7566 6.30203 13.1081 6.27383 13.3662 6.44434L13.4707 6.5293L17.4707 10.5293C17.7304 10.789 17.7304 11.211 17.4707 11.4707L13.4707 15.4707C13.211 15.7304 12.789 15.7304 12.5293 15.4707C12.2696 15.211 12.2696 14.789 12.5293 14.5293L15.3936 11.665H6C3.97588 11.665 2.33496 10.0241 2.33496 8V4.5C2.33496 4.13273 2.63273 3.83496 3 3.83496C3.36727 3.83496 3.66504 4.13273 3.66504 4.5V8C3.66504 9.28958 4.71042 10.335 6 10.335H15.3936L12.5293 7.4707L12.4443 7.36621C12.2738 7.10808 12.302 6.75657 12.5293 6.5293Z">
15
12
  </path>
@@ -21,10 +18,7 @@
21
18
  </div>
22
19
 
23
20
  <div>
24
- <wally-button
25
- [variant]="'ghost'"
26
- [rounded]="true"
27
- [ariaLabel]="'Clear selected context'">
21
+ <wally-button [variant]="'ghost'" [rounded]="true" [ariaLabel]="'Clear selected context'">
28
22
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
29
23
  stroke="currentColor" class="size-5" aria-hidden="true">
30
24
  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
@@ -40,30 +34,169 @@
40
34
 
41
35
  <!-- Action Buttons -->
42
36
  <div class="w-full flex justify-between px-2 pb-2" role="toolbar" aria-label="Composer actions">
43
- <div class="size-10">
44
- <wally-tooltip [text]="'Add files and more'" position="bottom">
45
- <wally-button
46
- [variant]="'ghost'"
47
- [rounded]="true"
48
- [ariaLabel]="'Add attachment or insert content'">
49
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
50
- stroke="currentColor" class="size-5" aria-hidden="true">
51
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
52
- </svg>
37
+ <div class="w-full flex items-center gap-1">
38
+ <div class="size-10">
39
+ <wally-dropdown-menu>
40
+ <wally-dropdown-menu-trigger>
41
+ <wally-tooltip [text]="'Add files and more'" position="bottom">
42
+ <wally-button [variant]="'ghost'" [rounded]="true"
43
+ [ariaLabel]="'Add attachment or insert content'">
44
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
45
+ stroke="currentColor" class="size-5" aria-hidden="true">
46
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
47
+ </svg>
48
+ </wally-button>
49
+ </wally-tooltip>
50
+ </wally-dropdown-menu-trigger>
51
+
52
+ <wally-dropdown-menu-content [position]="'top'">
53
+ <!-- Grupo 1: Configurações da conta -->
54
+ <wally-dropdown-menu-group [ariaLabel]="'Account settings'">
55
+ <wally-dropdown-menu-item (click)="onItemClick()">
56
+ <div class="flex items-center gap-3">
57
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
58
+ stroke-width="1.5" stroke="currentColor" class="size-6">
59
+ <path stroke-linecap="round" stroke-linejoin="round"
60
+ d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18" />
61
+ </svg>
62
+
63
+ <span>
64
+ Think
65
+ </span>
66
+ </div>
67
+ </wally-dropdown-menu-item>
68
+
69
+ <wally-dropdown-menu-item (click)="onItemClick()">
70
+ <div class="flex items-center gap-3">
71
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
72
+ stroke-width="1.5" stroke="currentColor" class="size-6">
73
+ <path stroke-linecap="round" stroke-linejoin="round"
74
+ d="M9 8.25H7.5a2.25 2.25 0 0 0-2.25 2.25v9a2.25 2.25 0 0 0 2.25 2.25h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25H15m0-3-3-3m0 0-3 3m3-3V15" />
75
+ </svg>
76
+
77
+ <span>
78
+ Upload File
79
+ </span>
80
+ </div>
81
+ </wally-dropdown-menu-item>
82
+
83
+ <!-- Submenu -->
84
+ <wally-dropdown-menu-sub>
85
+ <wally-dropdown-menu-sub-trigger>
86
+ <div class="flex items-center gap-3">
87
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
88
+ stroke-width="1.5" stroke="currentColor" class="size-6">
89
+ <path stroke-linecap="round" stroke-linejoin="round"
90
+ d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
91
+ </svg>
92
+
93
+ <span>
94
+ More
95
+ </span>
96
+ </div>
97
+ </wally-dropdown-menu-sub-trigger>
98
+
99
+ <wally-dropdown-menu-sub-content>
100
+ <wally-dropdown-menu-item (click)="onItemClick()">
101
+ <div class="flex items-center gap-3">
102
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
103
+ stroke-width="1.5" stroke="currentColor" class="size-6">
104
+ <path stroke-linecap="round" stroke-linejoin="round"
105
+ d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
106
+ </svg>
107
+
108
+ <span>
109
+ Search the web
110
+ </span>
111
+ </div>
112
+ </wally-dropdown-menu-item>
113
+ <wally-dropdown-menu-item (click)="onItemClick()">
114
+ <div class="flex items-center gap-3">
115
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
116
+ stroke-width="1.5" stroke="currentColor" class="size-6">
117
+ <path stroke-linecap="round" stroke-linejoin="round"
118
+ d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5" />
119
+ </svg>
120
+
121
+ <span>
122
+ Study
123
+ </span>
124
+ </div>
125
+ </wally-dropdown-menu-item>
126
+ <!-- <wally-dropdown-menu-item (click)="onItemClick()">
127
+ Privacidade
128
+ </wally-dropdown-menu-item> -->
129
+ </wally-dropdown-menu-sub-content>
130
+ </wally-dropdown-menu-sub>
131
+ </wally-dropdown-menu-group>
132
+
133
+ <!-- <wally-dropdown-menu-separator></wally-dropdown-menu-separator> -->
134
+
135
+ <!-- Item individual -->
136
+ <!-- <wally-dropdown-menu-item (click)="onItemClick()">
137
+ Sair
138
+ </wally-dropdown-menu-item> -->
139
+ </wally-dropdown-menu-content>
140
+
141
+ </wally-dropdown-menu>
142
+ </div>
143
+
144
+ <div>
145
+ <wally-button variant="ghost" [rounded]="true">
146
+ <div class="group flex items-center gap-2">
147
+ <div
148
+ class="max-w-0 group-hover:max-w-6 overflow-hidden transition-all duration-300 ease-in-out">
149
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
150
+ stroke="currentColor"
151
+ class="opacity-0 group-hover:opacity-100 transition-opacity duration-300 size-6 rounded-full p-0.5 bg-neutral-300/40 dark:bg-white/10"
152
+ aria-hidden="true">
153
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
154
+ </svg>
155
+ </div>
156
+
157
+ <svg class="size-5" viewBox="0 0 400 600" fill="none" xmlns="http://www.w3.org/2000/svg"
158
+ aria-hidden="true">
159
+ <path
160
+ d="M0 500C0 444.772 44.772 400 100 400H200V500C200 555.228 155.228 600 100 600C44.772 600 0 555.228 0 500Z"
161
+ fill="#24CB71" />
162
+ <path d="M200 0V200H300C355.228 200 400 155.228 400 100C400 44.772 355.228 0 300 0H200Z"
163
+ fill="#FF7237" />
164
+ <path
165
+ d="M299.167 400C354.395 400 399.167 355.228 399.167 300C399.167 244.772 354.395 200 299.167 200C243.939 200 199.167 244.772 199.167 300C199.167 355.228 243.939 400 299.167 400Z"
166
+ fill="#00B6FF" />
167
+ <path d="M0 100C0 155.228 44.772 200 100 200H200V0H100C44.772 0 0 44.772 0 100Z"
168
+ fill="#FF3737" />
169
+ <path d="M0 300C0 355.228 44.772 400 100 400H200V200H100C44.772 200 0 244.772 0 300Z"
170
+ fill="#874FFF" />
171
+ </svg>
172
+
173
+ <span class="text-base">Figma</span>
174
+ </div>
53
175
  </wally-button>
54
- </wally-tooltip>
176
+ </div>
55
177
  </div>
56
178
 
57
- <div class="size-10">
58
- <wally-button
59
- [variant]="'primary'"
60
- [rounded]="true"
61
- [ariaLabel]="'Send message'">
62
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3"
63
- stroke="currentColor" class="size-5" aria-hidden="true">
64
- <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" />
65
- </svg>
66
- </wally-button>
179
+ <div class="w-full size-10 flex gap-2 justify-end">
180
+ <div>
181
+ <wally-tooltip [text]="'Use voice input'" position="bottom">
182
+ <wally-button [variant]="'ghost'" [rounded]="true" [ariaLabel]="'Send message'">
183
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
184
+ stroke="currentColor" class="size-6">
185
+ <path stroke-linecap="round" stroke-linejoin="round"
186
+ d="M12 18.75a6 6 0 0 0 6-6v-1.5m-6 7.5a6 6 0 0 1-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 0 1-3-3V4.5a3 3 0 1 1 6 0v8.25a3 3 0 0 1-3 3Z" />
187
+ </svg>
188
+ </wally-button>
189
+ </wally-tooltip>
190
+ </div>
191
+
192
+ <div>
193
+ <wally-button [variant]="'primary'" [rounded]="true" [ariaLabel]="'Send message'">
194
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3"
195
+ stroke="currentColor" class="size-5" aria-hidden="true">
196
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" />
197
+ </svg>
198
+ </wally-button>
199
+ </div>
67
200
  </div>
68
201
  </div>
69
202
  </div>
@@ -1,19 +1,41 @@
1
1
  import { Component } from '@angular/core';
2
2
 
3
+ import { DropdownMenuSubTrigger } from '../../dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger';
4
+ import { DropdownMenuSubContent } from '../../dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content';
5
+ import { DropdownMenuSeparator } from '../../dropdown-menu/dropdown-menu-separator/dropdown-menu-separator';
6
+ import { DropdownMenuTrigger } from '../../dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger';
7
+ import { DropdownMenuContent } from '../../dropdown-menu/dropdown-menu-content/dropdown-menu-content';
8
+ import { DropdownMenuLabel } from '../../dropdown-menu/dropdown-menu-label/dropdown-menu-label';
9
+ import { DropdownMenuGroup } from '../../dropdown-menu/dropdown-menu-group/dropdown-menu-group';
10
+ import { DropdownMenuItem } from '../../dropdown-menu/dropdown-menu-item/dropdown-menu-item';
11
+ import { DropdownMenuSub } from '../../dropdown-menu/dropdown-menu-sub/dropdown-menu-sub';
3
12
  import { AiPromptInput } from '../ai-prompt-input/ai-prompt-input';
4
- import { Button } from '../../button/button';
13
+ import { DropdownMenu } from '../../dropdown-menu/dropdown-menu';
5
14
  import { Tooltip } from '../../tooltip/tooltip';
15
+ import { Button } from '../../button/button';
6
16
 
7
17
  @Component({
8
18
  selector: 'wally-ai-composer',
9
19
  imports: [
10
20
  AiPromptInput,
11
21
  Tooltip,
12
- Button
22
+ Button,
23
+ DropdownMenu,
24
+ DropdownMenuTrigger,
25
+ DropdownMenuContent,
26
+ DropdownMenuItem,
27
+ DropdownMenuLabel,
28
+ DropdownMenuSeparator,
29
+ DropdownMenuGroup,
30
+ DropdownMenuSub,
31
+ DropdownMenuSubTrigger,
32
+ DropdownMenuSubContent
13
33
  ],
14
34
  templateUrl: './ai-composer.html',
15
35
  styleUrl: './ai-composer.css'
16
36
  })
17
37
  export class AiComposer {
18
-
38
+ onItemClick(): void {
39
+ console.log('Item clicked');
40
+ }
19
41
  }
@@ -1,4 +1,4 @@
1
- <textarea #textarea id="ai-prompt-input" class="w-full h-auto max-h-60 text-base placeholder:text-neutral-400 dark:text-white dark:placeholder:text-neutral-500 resize-none focus:outline-none focus:ring-0 overflow-y-auto [&::-webkit-scrollbar]:w-2
1
+ <textarea #textarea id="ai-prompt-input" class="w-full h-auto max-h-48 text-base placeholder:text-neutral-400 dark:text-white dark:placeholder:text-neutral-500 resize-none focus:outline-none focus:ring-0 overflow-y-auto [&::-webkit-scrollbar]:w-2
2
2
  [&::-webkit-scrollbar-track]:bg-transparent
3
3
  [&::-webkit-scrollbar-thumb]:bg-neutral-300
4
4
  [&::-webkit-scrollbar-thumb]:rounded-full
@@ -0,0 +1,3 @@
1
+ <div [class]="'w-auto py-1.5 px-3 rounded-full ' + variantClasses()">
2
+ <ng-content></ng-content>
3
+ </div>
@@ -0,0 +1,24 @@
1
+ import { Component, computed, input, InputSignal, Signal } from '@angular/core';
2
+
3
+ type Variant = 'default' | 'outline' | 'secondary' | 'destructive';
4
+
5
+ @Component({
6
+ selector: 'wally-badge',
7
+ imports: [],
8
+ templateUrl: './badge.html',
9
+ styleUrl: './badge.css'
10
+ })
11
+ export class Badge {
12
+ variant: InputSignal<Variant> = input<Variant>('default');
13
+
14
+ variantClasses: Signal<string> = computed(() => {
15
+ const variantMap: Record<Variant, string> = {
16
+ default: '#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 shadow',
17
+ outline: '',
18
+ secondary: '',
19
+ destructive: ''
20
+ };
21
+
22
+ return variantMap[this.variant()] || variantMap.default;
23
+ });
24
+ }
@@ -1,8 +1,6 @@
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()"
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 transition-all duration-300 ease-in-out antialiased cursor-pointer ' + variantClasses()"
5
- [ngClass]="{
3
+ (click)="handleClick()" [class]="'group relative w-full flex items-center justify-center gap-2 text-sm font-medium text-white active:scale-95 disabled:pointer-events-none p-2.5 disabled:active:scale-100 transition-all duration-300 ease-in-out antialiased cursor-pointer ' + variantClasses()" [ngClass]="{
6
4
  'rounded-md': !rounded(),
7
5
  'rounded-full': rounded()
8
6
  }">
@@ -33,10 +33,10 @@ export class Button {
33
33
  // Computed classes based on variant
34
34
  variantClasses: Signal<string> = computed(() => {
35
35
  const variantMap: Record<ButtonVariant, string> = {
36
- 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',
37
- 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',
38
- 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',
39
- 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',
36
+ 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 shadow',
37
+ 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 shadow',
38
+ 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 shadow',
39
+ 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 shadow',
40
40
  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',
41
41
  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'
42
42
  };
@@ -0,0 +1,9 @@
1
+ @if (dropdownMenuService.isOpen()) {
2
+ <div
3
+ (mouseenter)="onMouseEnter()"
4
+ (mouseleave)="onMouseLeave()"
5
+ [class]="'absolute bg-white dark:bg-[#1b1b1b] dark:border-neutral-700 rounded-xl shadow-2xl border border-neutral-300 min-w-72 z-50 transition-all duration-200 ease-out ' + positionClasses()"
6
+ role="menu">
7
+ <ng-content></ng-content>
8
+ </div>
9
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { DropdownMenuContent } from './dropdown-menu-content';
4
+
5
+ describe('DropdownMenuContent', () => {
6
+ let component: DropdownMenuContent;
7
+ let fixture: ComponentFixture<DropdownMenuContent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [DropdownMenuContent]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(DropdownMenuContent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,167 @@
1
+ import { Component, computed, effect, ElementRef, input, signal } from '@angular/core';
2
+
3
+ import { DropdownMenuService } from '../dropdown-menu.service';
4
+
5
+ export type DropdownPosition = 'bottom' | 'top' | 'left' | 'right';
6
+
7
+ @Component({
8
+ selector: 'wally-dropdown-menu-content',
9
+ imports: [],
10
+ templateUrl: './dropdown-menu-content.html',
11
+ styleUrl: './dropdown-menu-content.css'
12
+ })
13
+ export class DropdownMenuContent {
14
+ position = input<DropdownPosition>('bottom');
15
+
16
+ calculatedPosition = signal<DropdownPosition>('bottom');
17
+
18
+ positionClasses = computed(() => {
19
+ const position = this.calculatedPosition();
20
+ const scrollClasses = 'max-h-96 overflow-visible';
21
+
22
+ const positionMap = {
23
+ bottom: 'top-full mt-1 left-0',
24
+ top: 'bottom-full mb-1 left-0',
25
+ right: 'left-full ml-1 top-0',
26
+ left: 'right-full mr-1 top-0'
27
+ };
28
+
29
+ return `${scrollClasses} ${positionMap[position]}`;
30
+ });
31
+
32
+ constructor(
33
+ public dropdownMenuService: DropdownMenuService,
34
+ private elementRef: ElementRef
35
+ ) {
36
+ effect(() => {
37
+ if (this.dropdownMenuService.isOpen()) {
38
+ setTimeout(() => {
39
+ const bestPosition = this.calculateBestPosition();
40
+ this.calculatedPosition.set(bestPosition);
41
+ }, 0);
42
+ }
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Measures available space around the trigger element in all directions.
48
+ * @returns Object containing trigger dimensions and available space, or null if trigger not found
49
+ */
50
+ private measureAvailableSpace(): {
51
+ triggerRect: any;
52
+ spaceAbove: any;
53
+ spaceBelow: number;
54
+ spaceLeft: any;
55
+ spaceRight: number;
56
+ } | null {
57
+ const triggerElement = this.elementRef.nativeElement.parentElement;
58
+
59
+ if (!triggerElement) {
60
+ return null;
61
+ }
62
+
63
+ const triggerRect = triggerElement.getBoundingClientRect();
64
+ const viewportWidth = window.innerWidth;
65
+ const viewportHeight = window.innerHeight;
66
+
67
+ const spaceAbove = triggerRect.top;
68
+ const spaceBelow = viewportHeight - triggerRect.bottom;
69
+ const spaceLeft = triggerRect.left;
70
+ const spaceRight = viewportWidth - triggerRect.right;
71
+
72
+ return {
73
+ triggerRect,
74
+ spaceAbove,
75
+ spaceBelow,
76
+ spaceLeft,
77
+ spaceRight
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Calculates the best position for the dropdown based on available viewport space.
83
+ * Falls back to alternative positions if preferred position doesn't fit.
84
+ * @returns The optimal dropdown position
85
+ */
86
+ private calculateBestPosition(): DropdownPosition {
87
+ const space = this.measureAvailableSpace();
88
+
89
+ if (!space) {
90
+ return this.position();
91
+ }
92
+
93
+ const menuDimensions = this.getMenuDimensions();
94
+ const MENU_MIN_HEIGHT = menuDimensions.height + 20;
95
+ const MENU_MIN_WIDTH = menuDimensions.width + 20;
96
+
97
+ const preferred = this.position();
98
+
99
+ switch (preferred) {
100
+ case 'bottom':
101
+ if (space.spaceBelow >= MENU_MIN_HEIGHT) return 'bottom';
102
+ if (space.spaceAbove >= MENU_MIN_HEIGHT) return 'top';
103
+ if (space.spaceRight >= MENU_MIN_WIDTH) return 'right';
104
+ return 'left';
105
+
106
+ case 'top':
107
+ if (space.spaceAbove >= MENU_MIN_HEIGHT) return 'top';
108
+ if (space.spaceBelow >= MENU_MIN_HEIGHT) return 'bottom';
109
+ if (space.spaceRight >= MENU_MIN_WIDTH) return 'right';
110
+ return 'left';
111
+
112
+ case 'right':
113
+ if (space.spaceRight >= MENU_MIN_WIDTH) return 'right';
114
+ if (space.spaceLeft >= MENU_MIN_WIDTH) return 'left';
115
+ if (space.spaceBelow >= MENU_MIN_HEIGHT) return 'bottom';
116
+ return 'top';
117
+
118
+ case 'left':
119
+ if (space.spaceLeft >= MENU_MIN_WIDTH) return 'left';
120
+ if (space.spaceRight >= MENU_MIN_WIDTH) return 'right';
121
+ if (space.spaceBelow >= MENU_MIN_HEIGHT) return 'bottom';
122
+ return 'top';
123
+
124
+ default:
125
+ return 'bottom';
126
+ }
127
+ }
128
+
129
+ private getMenuDimensions(): {
130
+ height: number;
131
+ width: number;
132
+ } {
133
+ const menuElement = this.elementRef.nativeElement.querySelector('[role="menu"]');
134
+
135
+ if (!menuElement) {
136
+ return {
137
+ height: 200,
138
+ width: 224
139
+ };
140
+ }
141
+
142
+ const rect = menuElement.getBoundingClientRect();
143
+
144
+ return {
145
+ height: rect.height || 200,
146
+ width: rect.width || 224
147
+ };
148
+ }
149
+
150
+ onMouseEnter(): void {
151
+ if (this.dropdownMenuService.triggerMode() === 'hover') {
152
+ this.dropdownMenuService.setHoveringContent(true);
153
+ }
154
+ }
155
+
156
+ onMouseLeave(): void {
157
+ if (this.dropdownMenuService.triggerMode() === 'hover') {
158
+ this.dropdownMenuService.setHoveringContent(false);
159
+
160
+ setTimeout(() => {
161
+ if (!this.dropdownMenuService.isHoveringContent()) {
162
+ this.dropdownMenuService.close();
163
+ }
164
+ }, 100);
165
+ }
166
+ }
167
+ }
@@ -0,0 +1,5 @@
1
+ <div
2
+ role="group"
3
+ [attr.aria-label]="ariaLabel() || null">
4
+ <ng-content></ng-content>
5
+ </div>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { DropdownMenuGroup } from './dropdown-menu-group';
4
+
5
+ describe('DropdownMenuGroup', () => {
6
+ let component: DropdownMenuGroup;
7
+ let fixture: ComponentFixture<DropdownMenuGroup>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [DropdownMenuGroup]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(DropdownMenuGroup);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });