wally-ui 1.13.1 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/playground/showcase/package-lock.json +48 -0
  3. package/playground/showcase/package.json +1 -0
  4. package/playground/showcase/src/app/app.routes.server.ts +4 -0
  5. package/playground/showcase/src/app/components/ai/ai-chat/ai-chat.html +7 -2
  6. package/playground/showcase/src/app/components/ai/ai-chat/ai-chat.ts +12 -1
  7. package/playground/showcase/src/app/components/ai/ai-chat.service.spec.ts +16 -0
  8. package/playground/showcase/src/app/components/ai/ai-chat.service.ts +6 -0
  9. package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.html +14 -7
  10. package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.ts +3 -1
  11. package/playground/showcase/src/app/components/ai/ai-message/ai-message.css +0 -0
  12. package/playground/showcase/src/app/components/ai/ai-message/ai-message.html +165 -0
  13. package/playground/showcase/src/app/components/ai/ai-message/ai-message.spec.ts +23 -0
  14. package/playground/showcase/src/app/components/ai/ai-message/ai-message.ts +51 -0
  15. package/playground/showcase/src/app/components/button/button.html +1 -1
  16. package/playground/showcase/src/app/components/button/button.ts +3 -3
  17. package/playground/showcase/src/app/components/selection-popover/selection-popover.css +0 -0
  18. package/playground/showcase/src/app/components/selection-popover/selection-popover.html +33 -0
  19. package/playground/showcase/src/app/components/selection-popover/selection-popover.spec.ts +23 -0
  20. package/playground/showcase/src/app/components/selection-popover/selection-popover.ts +269 -0
  21. package/playground/showcase/src/app/pages/documentation/chat-sdk/chat-sdk.html +1 -1
  22. package/playground/showcase/src/app/pages/documentation/components/button-docs/button-docs.examples.ts +10 -10
  23. package/playground/showcase/src/app/pages/documentation/components/button-docs/button-docs.html +2 -2
  24. package/playground/showcase/src/app/pages/documentation/components/components.html +27 -0
  25. package/playground/showcase/src/app/pages/documentation/components/components.routes.ts +4 -0
  26. package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.css +1 -0
  27. package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.examples.ts +328 -0
  28. package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.html +555 -0
  29. package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.ts +97 -0
  30. package/playground/showcase/src/app/pages/home/home.html +2 -2
  31. package/playground/showcase/src/styles.css +1 -0
@@ -0,0 +1,555 @@
1
+ <main class="min-h-dvh bg-white dark:bg-[#0a0a0a] font-mono" role="main">
2
+ <div class="max-w-4xl mx-auto px-4 py-8 sm:px-6 md:px-8">
3
+
4
+ <!-- Breadcrumb Navigation -->
5
+ <nav class="mb-8" aria-label="Breadcrumb navigation">
6
+ <wally-breadcrumb [items]="breadcrumbItems"></wally-breadcrumb>
7
+ </nav>
8
+
9
+ <!-- Header -->
10
+ <header class="mb-12" role="banner">
11
+ <h1 id="page-title" class="text-3xl sm:text-4xl font-bold text-[#0a0a0a] dark:text-white mb-4 uppercase">
12
+ <span aria-hidden="true">&gt;_ </span>Selection Popover
13
+ </h1>
14
+ <p
15
+ class="text-sm sm:text-base text-neutral-700 dark:text-neutral-300 border-l-2 border-neutral-300 dark:border-neutral-700 pl-4 leading-relaxed">
16
+ Floating action toolbar that appears above selected text, similar to Medium, Notion, and Google Docs. Built with
17
+ Selection API, viewport-aware positioning, and zero-flash rendering for seamless UX.
18
+ </p>
19
+ </header>
20
+
21
+ <!-- AI Assistant Links -->
22
+ <div class="flex flex-wrap gap-0 mt-6 mb-12 border-2 border-neutral-300 dark:border-neutral-700" role="list"
23
+ aria-label="Open documentation in AI coding assistants">
24
+ <a [href]="claudeUrl" target="_blank"
25
+ class="group flex-1 min-w-[200px] flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-[#0a0a0a] border-b-2 sm:border-b-0 sm:border-r-2 border-neutral-300 dark:border-neutral-700 hover:bg-[#0a0a0a] dark:hover:bg-white transition-all duration-150 cursor-pointer"
26
+ rel="noopener noreferrer" role="listitem"
27
+ aria-label="Open Selection Popover component documentation in Claude AI assistant (opens in new tab)">
28
+ <svg xmlns="http://www.w3.org/2000/svg"
29
+ class="size-5 text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150"
30
+ viewBox="0 0 24 24" aria-hidden="true">
31
+ <path fill="currentColor"
32
+ d="m4.714 15.956l4.718-2.648l.079-.23l-.08-.128h-.23l-.79-.048l-2.695-.073l-2.337-.097l-2.265-.122l-.57-.121l-.535-.704l.055-.353l.48-.321l.685.06l1.518.104l2.277.157l1.651.098l2.447.255h.389l.054-.158l-.133-.097l-.103-.098l-2.356-1.596l-2.55-1.688l-1.336-.972l-.722-.491L2 6.223l-.158-1.008l.656-.722l.88.06l.224.061l.893.686l1.906 1.476l2.49 1.833l.364.304l.146-.104l.018-.072l-.164-.274l-1.354-2.446l-1.445-2.49l-.644-1.032l-.17-.619a3 3 0 0 1-.103-.729L6.287.133L6.7 0l.995.134l.42.364l.619 1.415L9.735 4.14l1.555 3.03l.455.898l.243.832l.09.255h.159V9.01l.127-1.706l.237-2.095l.23-2.695l.08-.76l.376-.91l.747-.492l.583.28l.48.685l-.067.444l-.286 1.851l-.558 2.903l-.365 1.942h.213l.243-.242l.983-1.306l1.652-2.064l.728-.82l.85-.904l.547-.431h1.032l.759 1.129l-.34 1.166l-1.063 1.347l-.88 1.142l-1.263 1.7l-.79 1.36l.074.11l.188-.02l2.853-.606l1.542-.28l1.84-.315l.832.388l.09.395l-.327.807l-1.967.486l-2.307.462l-3.436.813l-.043.03l.049.061l1.548.146l.662.036h1.62l3.018.225l.79.522l.473.638l-.08.485l-1.213.62l-1.64-.389l-3.825-.91l-1.31-.329h-.183v.11l1.093 1.068l2.003 1.81l2.508 2.33l.127.578l-.321.455l-.34-.049l-2.204-1.657l-.85-.747l-1.925-1.62h-.127v.17l.443.649l2.343 3.521l.122 1.08l-.17.353l-.607.213l-.668-.122l-1.372-1.924l-1.415-2.168l-1.141-1.943l-.14.08l-.674 7.254l-.316.37l-.728.28l-.607-.461l-.322-.747l.322-1.476l.388-1.924l.316-1.53l.285-1.9l.17-.632l-.012-.042l-.14.018l-1.432 1.967l-2.18 2.945l-1.724 1.845l-.413.164l-.716-.37l.066-.662l.401-.589l2.386-3.036l1.439-1.882l.929-1.086l-.006-.158h-.055L4.138 18.56l-1.13.146l-.485-.456l.06-.746l.231-.243l1.907-1.312Z" />
33
+ </svg>
34
+ <span
35
+ class="text-xs sm:text-sm font-medium text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150">
36
+ Open in Claude
37
+ </span>
38
+ </a>
39
+
40
+ <a [href]="chatGptUrl" target="_blank"
41
+ class="group flex-1 min-w-[200px] flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-[#0a0a0a] hover:bg-[#0a0a0a] dark:hover:bg-white transition-all duration-150 cursor-pointer"
42
+ rel="noopener noreferrer" role="listitem"
43
+ aria-label="Open Selection Popover component documentation in ChatGPT AI assistant (opens in new tab)">
44
+ <svg xmlns="http://www.w3.org/2000/svg"
45
+ class="size-5 text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150"
46
+ viewBox="0 0 48 48" aria-hidden="true">
47
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
48
+ d="M18.38 27.94v-14.4l11.19-6.46c6.2-3.58 17.3 5.25 12.64 13.33" />
49
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
50
+ d="m18.38 20.94l12.47-7.2l11.19 6.46c6.2 3.58 4.1 17.61-5.23 17.61" />
51
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
52
+ d="m24.44 17.44l12.47 7.2v12.93c0 7.16-13.2 12.36-17.86 4.28" />
53
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
54
+ d="M30.5 21.2v14.14L19.31 41.8c-6.2 3.58-17.3-5.25-12.64-13.33" />
55
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
56
+ d="m30.5 27.94l-12.47 7.2l-11.19-6.46c-6.21-3.59-4.11-17.61 5.22-17.61" />
57
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
58
+ d="m24.44 31.44l-12.47-7.2V11.31c0-7.16 13.2-12.36 17.86-4.28" />
59
+ </svg>
60
+ <span
61
+ class="text-xs sm:text-sm font-medium text-[#0a0a0a] dark:text-neutral-400 group-hover:text-white dark:group-hover:text-[#0a0a0a] transition-colors duration-150">
62
+ Open in ChatGPT
63
+ </span>
64
+ </a>
65
+ </div>
66
+
67
+ <!-- Installation -->
68
+ <section id="installation" class="mb-12" aria-labelledby="installation-heading">
69
+ <h2 id="installation-heading"
70
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
71
+ [ Installation ]
72
+ </h2>
73
+ <div
74
+ class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white block bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3"
75
+ aria-label="Installation command for Selection Popover component">
76
+ <pre><code [innerHTML]="installationCode"></code></pre>
77
+ </div>
78
+ </section>
79
+
80
+ <!-- Import -->
81
+ <section id="import" class="mb-12" aria-labelledby="import-heading">
82
+ <h2 id="import-heading"
83
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
84
+ [ Import ]
85
+ </h2>
86
+ <div class="space-y-3">
87
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
88
+ <pre><code [innerHTML]="importCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
89
+ </div>
90
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
91
+ <pre><code [innerHTML]="componentImportCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
92
+ </div>
93
+ </div>
94
+ </section>
95
+
96
+ <!-- Basic Usage -->
97
+ <section id="basic-usage" class="mb-12" aria-labelledby="basic-usage-heading">
98
+ <h2 id="basic-usage-heading"
99
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
100
+ [ Basic Usage ]
101
+ </h2>
102
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
103
+ <pre><code [innerHTML]="basicUsageCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
104
+ </div>
105
+
106
+ <div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
107
+ aria-label="Live preview of basic selection popover">
108
+ <wally-selection-popover (textSelected)="onTextSelected($event)">
109
+ <article class="prose dark:prose-invert max-w-none">
110
+ <h3 class="text-lg font-bold text-[#0a0a0a] dark:text-white mb-2">Try It Out</h3>
111
+ <p class="text-sm text-neutral-600 dark:text-neutral-400 leading-relaxed">
112
+ Select any text in this paragraph to see the popover appear above your selection. The component
113
+ automatically
114
+ detects text selection and positions the toolbar centered above the selected text. Try selecting text near
115
+ the edges of the screen to see automatic viewport adjustment.
116
+ </p>
117
+ </article>
118
+ </wally-selection-popover>
119
+
120
+ @if (actionMessage()) {
121
+ <p class="text-sm text-green-600 dark:text-green-400 mt-4 font-medium">
122
+ {{ actionMessage() }}
123
+ </p>
124
+ }
125
+ </div>
126
+ </section>
127
+
128
+ <!-- Custom Actions -->
129
+ <section id="custom-actions" class="mb-12" aria-labelledby="custom-actions-heading">
130
+ <h2 id="custom-actions-heading"
131
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
132
+ [ Custom Actions ]
133
+ </h2>
134
+
135
+ <!-- Multiple Actions -->
136
+ <article class="mb-8" aria-labelledby="multiple-actions-heading">
137
+ <h3 id="multiple-actions-heading"
138
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
139
+ <span aria-hidden="true">&gt; </span>Multiple Custom Actions
140
+ </h3>
141
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
142
+ Add custom action buttons using the <span
143
+ class="bg-neutral-100 dark:bg-neutral-800 px-1 rounded">popoverActions</span> attribute.
144
+ </p>
145
+ <div class="space-y-3 mb-4">
146
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
147
+ <pre><code [innerHTML]="customActionsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
148
+ </div>
149
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
150
+ <pre><code [innerHTML]="customActionsTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
151
+ </div>
152
+ </div>
153
+ <div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
154
+ aria-label="Live preview of selection popover with custom actions">
155
+ <wally-selection-popover (textSelected)="onTextSelected($event)">
156
+ <div popoverActions class="flex gap-1">
157
+ <button class="px-3 py-2 text-sm text-[#0a0a0a] dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors cursor-pointer">
158
+ Ask AI
159
+ </button>
160
+ <button class="px-3 py-2 text-sm text-[#0a0a0a] dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors cursor-pointer">
161
+ Copy
162
+ </button>
163
+ <button class="px-3 py-2 text-sm text-[#0a0a0a] dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors cursor-pointer">
164
+ Share
165
+ </button>
166
+ </div>
167
+
168
+ <article class="prose dark:prose-invert max-w-none">
169
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
170
+ Select text to see custom actions: Ask AI, Copy, and Share buttons.
171
+ </p>
172
+ </article>
173
+ </wally-selection-popover>
174
+ </div>
175
+ </article>
176
+
177
+ <!-- With Wally Button -->
178
+ <article class="mb-8" aria-labelledby="wally-button-heading">
179
+ <h3 id="wally-button-heading"
180
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
181
+ <span aria-hidden="true">&gt; </span>Using with Wally Button Component
182
+ </h3>
183
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
184
+ Combine with the Button component for consistent styling and accessibility.
185
+ </p>
186
+ <div class="space-y-3 mb-4">
187
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
188
+ <pre><code [innerHTML]="withWallyButtonCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
189
+ </div>
190
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
191
+ <pre><code [innerHTML]="withWallyButtonTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
192
+ </div>
193
+ </div>
194
+ <div class="p-8 border-2 border-neutral-300 dark:border-neutral-700 bg-white dark:bg-[#0a0a0a]" role="img"
195
+ aria-label="Live preview of selection popover with Wally Button">
196
+ <wally-selection-popover (textSelected)="askAI($event)">
197
+ <div popoverActions>
198
+ <wally-button variant="ghost">
199
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
200
+ stroke="currentColor" class="size-4">
201
+ <path stroke-linecap="round" stroke-linejoin="round"
202
+ d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
203
+ </svg>
204
+ Ask AI
205
+ </wally-button>
206
+ </div>
207
+
208
+ <article class="prose dark:prose-invert max-w-none">
209
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
210
+ Select text and click "Ask AI" to trigger the custom action.
211
+ </p>
212
+ </article>
213
+ </wally-selection-popover>
214
+ </div>
215
+ </article>
216
+ </section>
217
+
218
+ <!-- Production Examples -->
219
+ <section id="production-examples" class="mb-12" aria-labelledby="production-heading">
220
+ <h2 id="production-heading"
221
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
222
+ [ Production Examples ]
223
+ </h2>
224
+
225
+ <!-- Blog Article -->
226
+ <article class="mb-8" aria-labelledby="blog-example-heading">
227
+ <h3 id="blog-example-heading"
228
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
229
+ <span aria-hidden="true">&gt; </span>Blog Article with Highlight & Comments
230
+ </h3>
231
+ <div class="space-y-3 mb-4">
232
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
233
+ <pre><code [innerHTML]="blogExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
234
+ </div>
235
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
236
+ <pre><code [innerHTML]="blogExampleTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
237
+ </div>
238
+ </div>
239
+ </article>
240
+
241
+ <!-- Documentation Site -->
242
+ <article class="mb-8" aria-labelledby="docs-example-heading">
243
+ <h3 id="docs-example-heading"
244
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
245
+ <span aria-hidden="true">&gt; </span>Documentation with AI Explanations
246
+ </h3>
247
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
248
+ <pre><code [innerHTML]="documentationExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
249
+ </div>
250
+ </article>
251
+
252
+ <!-- Reading Mode -->
253
+ <article class="mb-8" aria-labelledby="reading-example-heading">
254
+ <h3 id="reading-example-heading"
255
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
256
+ <span aria-hidden="true">&gt; </span>Reading Mode with Assistance Tools
257
+ </h3>
258
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
259
+ <pre><code [innerHTML]="readingModeExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
260
+ </div>
261
+ </article>
262
+ </section>
263
+
264
+ <!-- Advanced Features -->
265
+ <section id="advanced-features" class="mb-12" aria-labelledby="advanced-heading">
266
+ <h2 id="advanced-heading"
267
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
268
+ [ Advanced Features ]
269
+ </h2>
270
+
271
+ <!-- Minimum Selection Length -->
272
+ <article class="mb-8" aria-labelledby="min-length-heading">
273
+ <h3 id="min-length-heading"
274
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
275
+ <span aria-hidden="true">&gt; </span>Minimum Selection Length
276
+ </h3>
277
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
278
+ The component requires a minimum of 3 characters to be selected before showing the popover. This prevents
279
+ accidental triggers on short selections.
280
+ </p>
281
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
282
+ <pre><code [innerHTML]="minSelectionLengthCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
283
+ </div>
284
+ </article>
285
+
286
+ <!-- Viewport-Aware Positioning -->
287
+ <article class="mb-8" aria-labelledby="positioning-heading">
288
+ <h3 id="positioning-heading"
289
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
290
+ <span aria-hidden="true">&gt; </span>Viewport-Aware Positioning
291
+ </h3>
292
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
293
+ The popover automatically adjusts its position to stay within viewport boundaries. It uses <span
294
+ class="bg-neutral-100 dark:bg-neutral-800 px-1 rounded">position: fixed</span> with <span
295
+ class="bg-neutral-100 dark:bg-neutral-800 px-1 rounded">getBoundingClientRect()</span> for accurate
296
+ positioning.
297
+ </p>
298
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
299
+ <pre><code [innerHTML]="positioningBehaviorCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
300
+ </div>
301
+ </article>
302
+
303
+ <!-- Keyboard Accessibility -->
304
+ <article class="mb-8" aria-labelledby="keyboard-heading">
305
+ <h3 id="keyboard-heading"
306
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
307
+ <span aria-hidden="true">&gt; </span>Keyboard Accessibility
308
+ </h3>
309
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
310
+ Full keyboard support with ESC to close, click outside to dismiss, and proper ARIA dialog attributes.
311
+ </p>
312
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
313
+ <pre><code [innerHTML]="keyboardAccessibilityCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
314
+ </div>
315
+ </article>
316
+
317
+ <!-- Event Handling -->
318
+ <article class="mb-8" aria-labelledby="events-heading">
319
+ <h3 id="events-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
320
+ <span aria-hidden="true">&gt; </span>Event Handling
321
+ </h3>
322
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
323
+ The <span class="bg-neutral-100 dark:bg-neutral-800 px-1 rounded">textSelected</span> event fires when any
324
+ custom action is clicked. The popover automatically closes and clears selection after the event.
325
+ </p>
326
+ <div class="space-y-3">
327
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
328
+ <pre><code [innerHTML]="eventHandlingCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
329
+ </div>
330
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
331
+ <pre><code [innerHTML]="eventHandlingTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
332
+ </div>
333
+ </div>
334
+ </article>
335
+
336
+ <!-- Mobile Support -->
337
+ <article class="mb-8" aria-labelledby="mobile-support-heading">
338
+ <h3 id="mobile-support-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">
339
+ <span aria-hidden="true">&gt; </span>Mobile Support
340
+ </h3>
341
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-3 leading-relaxed">
342
+ The component works seamlessly on mobile devices with automatic optimizations for touch interactions. All features are enabled by default with zero configuration.
343
+ </p>
344
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3 mb-4">
345
+ <pre><code [innerHTML]="mobileSupportCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
346
+ </div>
347
+ <div class="bg-white dark:bg-[#0f0f0f] border-2 border-neutral-300 dark:border-neutral-800 p-4">
348
+ <h4 class="text-xs font-bold text-[#0a0a0a] dark:text-white mb-3 uppercase">Automatic Mobile Features:</h4>
349
+ <ul class="space-y-2 text-xs sm:text-sm text-neutral-600 dark:text-neutral-400">
350
+ <li class="flex items-start gap-2">
351
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
352
+ <span>Touch event support (touchstart, touchend) with adaptive delays for better detection</span>
353
+ </li>
354
+ <li class="flex items-start gap-2">
355
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
356
+ <span>Native selection menu prevention - only your custom popover appears</span>
357
+ </li>
358
+ <li class="flex items-start gap-2">
359
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
360
+ <span>Viewport-aware positioning that avoids virtual keyboard overlap</span>
361
+ </li>
362
+ <li class="flex items-start gap-2">
363
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
364
+ <span>Larger touch targets (44x44px minimum) for better tap accuracy</span>
365
+ </li>
366
+ <li class="flex items-start gap-2">
367
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
368
+ <span>Touch scroll prevention on popover to avoid accidental dismissal</span>
369
+ </li>
370
+ <li class="flex items-start gap-2">
371
+ <span class="text-green-600 dark:text-green-400 mt-0.5">✓</span>
372
+ <span>Smart device detection using multiple methods (touch points, window width, touch API)</span>
373
+ </li>
374
+ </ul>
375
+ </div>
376
+ </article>
377
+ </section>
378
+
379
+ <!-- API Reference -->
380
+ <section id="api-reference" class="mb-12" aria-labelledby="api-heading">
381
+ <h2 id="api-heading"
382
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
383
+ [ API Reference ]
384
+ </h2>
385
+
386
+ <!-- Output Events -->
387
+ <article class="mb-8" aria-labelledby="output-events-heading">
388
+ <h3 id="output-events-heading"
389
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-4 uppercase">
390
+ <span aria-hidden="true">&gt; </span>Output Events
391
+ </h3>
392
+ <div class="bg-white dark:bg-[#0f0f0f] border-2 border-neutral-300 dark:border-neutral-800 overflow-hidden">
393
+ <div class="overflow-x-auto">
394
+ <table class="w-full text-sm">
395
+ <thead class="bg-gray-50 dark:bg-gray-900">
396
+ <tr>
397
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Event</th>
398
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Payload</th>
399
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Description</th>
400
+ </tr>
401
+ </thead>
402
+ <tbody>
403
+ <tr>
404
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">textSelected</td>
405
+ <td class="p-4 font-mono text-purple-600 dark:text-purple-400">string</td>
406
+ <td class="p-4 text-gray-700 dark:text-gray-300">
407
+ Emitted when any custom action is clicked (or fallback button if no custom actions). Receives the
408
+ selected text as payload. Popover automatically closes after this event.
409
+ </td>
410
+ </tr>
411
+ </tbody>
412
+ </table>
413
+ </div>
414
+ </div>
415
+ </article>
416
+
417
+ <!-- Content Projection -->
418
+ <article class="mb-8" aria-labelledby="content-projection-heading">
419
+ <h3 id="content-projection-heading"
420
+ class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-4 uppercase">
421
+ <span aria-hidden="true">&gt; </span>Content Projection Slots
422
+ </h3>
423
+ <div class="bg-white dark:bg-[#0f0f0f] border-2 border-neutral-300 dark:border-neutral-800 overflow-hidden">
424
+ <div class="overflow-x-auto">
425
+ <table class="w-full text-sm">
426
+ <thead class="bg-gray-50 dark:bg-gray-900">
427
+ <tr>
428
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Selector</th>
429
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Description</th>
430
+ </tr>
431
+ </thead>
432
+ <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
433
+ <tr>
434
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">popoverActions</td>
435
+ <td class="p-4 text-gray-700 dark:text-gray-300">
436
+ Custom action buttons displayed in the popover. If provided, replaces the default fallback button.
437
+ Use this attribute on a wrapper div containing your custom action buttons.
438
+ </td>
439
+ </tr>
440
+ <tr>
441
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">(default)</td>
442
+ <td class="p-4 text-gray-700 dark:text-gray-300">
443
+ The selectable content. Any child elements without specific attributes will be treated as selectable
444
+ content.
445
+ </td>
446
+ </tr>
447
+ </tbody>
448
+ </table>
449
+ </div>
450
+ </div>
451
+ </article>
452
+
453
+ <!-- Behavior & Features -->
454
+ <article class="mb-8" aria-labelledby="behavior-heading">
455
+ <h3 id="behavior-heading" class="text-sm sm:text-base font-bold text-[#0a0a0a] dark:text-white mb-4 uppercase">
456
+ <span aria-hidden="true">&gt; </span>Automatic Behavior
457
+ </h3>
458
+ <div class="bg-white dark:bg-[#0f0f0f] border-2 border-neutral-300 dark:border-neutral-800 overflow-hidden">
459
+ <div class="overflow-x-auto">
460
+ <table class="w-full text-sm">
461
+ <thead class="bg-gray-50 dark:bg-gray-900">
462
+ <tr>
463
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Feature</th>
464
+ <th class="text-left p-4 font-medium text-gray-900 dark:text-gray-100">Description</th>
465
+ </tr>
466
+ </thead>
467
+ <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
468
+ <tr>
469
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Minimum Selection</td>
470
+ <td class="p-4 text-gray-700 dark:text-gray-300">
471
+ Requires 3+ characters selected to show popover (prevents accidental triggers)
472
+ </td>
473
+ </tr>
474
+ <tr>
475
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Zero-Flash Rendering</td>
476
+ <td class="p-4 text-gray-700 dark:text-gray-300">
477
+ Two-step positioning algorithm: renders invisible first, calculates real width, then fades in at
478
+ correct position
479
+ </td>
480
+ </tr>
481
+ <tr>
482
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Viewport Adjustment</td>
483
+ <td class="p-4 text-gray-700 dark:text-gray-300">
484
+ Automatically adjusts horizontal position to stay within viewport (10px padding from edges)
485
+ </td>
486
+ </tr>
487
+ <tr>
488
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Positioning Strategy</td>
489
+ <td class="p-4 text-gray-700 dark:text-gray-300">
490
+ Uses position: fixed + getBoundingClientRect() for viewport-relative positioning (stays in place
491
+ during scroll)
492
+ </td>
493
+ </tr>
494
+ <tr>
495
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Auto-Close</td>
496
+ <td class="p-4 text-gray-700 dark:text-gray-300">
497
+ Closes on ESC key, click outside, or after action execution. Automatically clears browser text
498
+ selection.
499
+ </td>
500
+ </tr>
501
+ <tr>
502
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">ARIA Support</td>
503
+ <td class="p-4 text-gray-700 dark:text-gray-300">
504
+ Full accessibility with role="dialog" and aria-label="Selection actions"
505
+ </td>
506
+ </tr>
507
+ <tr>
508
+ <td class="p-4 font-mono text-blue-600 dark:text-blue-400">Mobile Support</td>
509
+ <td class="p-4 text-gray-700 dark:text-gray-300">
510
+ Automatic touch event support, native menu prevention, larger touch targets, scroll prevention, and
511
+ viewport-aware positioning that avoids virtual keyboard overlap
512
+ </td>
513
+ </tr>
514
+ </tbody>
515
+ </table>
516
+ </div>
517
+ </div>
518
+ </article>
519
+ </section>
520
+
521
+ <!-- Complete Example -->
522
+ <section id="complete-example" class="mb-12" aria-labelledby="complete-heading">
523
+ <h2 id="complete-heading"
524
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
525
+ [ Complete Example ]
526
+ </h2>
527
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
528
+ Full implementation with all features: custom actions, event handling, and content styling.
529
+ </p>
530
+ <div class="space-y-3">
531
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
532
+ <pre><code [innerHTML]="fullExampleCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
533
+ </div>
534
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
535
+ <pre><code [innerHTML]="fullExampleTsCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
536
+ </div>
537
+ </div>
538
+ </section>
539
+
540
+ <!-- Custom Styling -->
541
+ <section id="custom-styling" class="mb-12" aria-labelledby="styling-heading">
542
+ <h2 id="styling-heading"
543
+ class="text-[10px] sm:text-xs text-neutral-500 dark:text-neutral-500 uppercase tracking-wider mb-4">
544
+ [ Custom Styling ]
545
+ </h2>
546
+ <p class="text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 mb-4 leading-relaxed">
547
+ Customize popover action buttons using Tailwind CSS classes for complete design control.
548
+ </p>
549
+ <div class="bg-neutral-100 dark:bg-[#121212] border border-neutral-300 dark:border-neutral-700 p-3">
550
+ <pre><code [innerHTML]="customStylingCode" class="text-xs sm:text-sm text-[#0a0a0a] dark:text-white"></code></pre>
551
+ </div>
552
+ </section>
553
+
554
+ </div>
555
+ </main>
@@ -0,0 +1,97 @@
1
+ import { Component, signal, WritableSignal } from '@angular/core';
2
+
3
+ import { AiPromptService } from '../../../../core/services/ai-prompt.service';
4
+ import { getFormattedCode } from '../../../../core/utils/prism';
5
+
6
+ import { SelectionPopoverCodeExamples } from './selection-popover-docs.examples';
7
+
8
+ import { Breadcrumb, BreadcrumbItem } from '../../../../components/breadcrumb/breadcrumb';
9
+ import { Button } from '../../../../components/button/button';
10
+ import { SelectionPopover } from '../../../../components/selection-popover/selection-popover';
11
+
12
+ @Component({
13
+ selector: 'app-selection-popover-docs',
14
+ imports: [
15
+ Button,
16
+ Breadcrumb,
17
+ SelectionPopover
18
+ ],
19
+ templateUrl: './selection-popover-docs.html',
20
+ styleUrl: './selection-popover-docs.css'
21
+ })
22
+ export class SelectionPopoverDocs {
23
+ breadcrumbItems: BreadcrumbItem[] = [
24
+ { label: 'Home', url: '/' },
25
+ { label: 'Documentation', url: '/documentation' },
26
+ { label: 'Components', url: '/documentation/components' },
27
+ { label: 'Selection Popover' }
28
+ ];
29
+
30
+ // Installation & Import
31
+ installationCode = getFormattedCode(SelectionPopoverCodeExamples.installation, 'bash');
32
+ importCode = getFormattedCode(SelectionPopoverCodeExamples.import, 'typescript');
33
+ componentImportCode = getFormattedCode(SelectionPopoverCodeExamples.componentImport, 'typescript');
34
+ basicUsageCode = getFormattedCode(SelectionPopoverCodeExamples.basicUsage, 'html');
35
+
36
+ // Custom Actions
37
+ customActionsCode = getFormattedCode(SelectionPopoverCodeExamples.customActions, 'html');
38
+ customActionsTsCode = getFormattedCode(SelectionPopoverCodeExamples.customActionsTs, 'typescript');
39
+ withWallyButtonCode = getFormattedCode(SelectionPopoverCodeExamples.withWallyButton, 'html');
40
+ withWallyButtonTsCode = getFormattedCode(SelectionPopoverCodeExamples.withWallyButtonTs, 'typescript');
41
+
42
+ // Production Examples
43
+ blogExampleCode = getFormattedCode(SelectionPopoverCodeExamples.blogExample, 'html');
44
+ blogExampleTsCode = getFormattedCode(SelectionPopoverCodeExamples.blogExampleTs, 'typescript');
45
+ documentationExampleCode = getFormattedCode(SelectionPopoverCodeExamples.documentationExample, 'html');
46
+ readingModeExampleCode = getFormattedCode(SelectionPopoverCodeExamples.readingModeExample, 'html');
47
+
48
+ // Advanced
49
+ minSelectionLengthCode = getFormattedCode(SelectionPopoverCodeExamples.minSelectionLength, 'html');
50
+ positioningBehaviorCode = getFormattedCode(SelectionPopoverCodeExamples.positioningBehavior, 'html');
51
+ keyboardAccessibilityCode = getFormattedCode(SelectionPopoverCodeExamples.keyboardAccessibility, 'html');
52
+ eventHandlingCode = getFormattedCode(SelectionPopoverCodeExamples.eventHandling, 'html');
53
+ eventHandlingTsCode = getFormattedCode(SelectionPopoverCodeExamples.eventHandlingTs, 'typescript');
54
+ mobileSupportCode = getFormattedCode(SelectionPopoverCodeExamples.mobileSupport, 'html');
55
+
56
+ // Full Example
57
+ fullExampleCode = getFormattedCode(SelectionPopoverCodeExamples.fullExample, 'html');
58
+ fullExampleTsCode = getFormattedCode(SelectionPopoverCodeExamples.fullExampleTs, 'typescript');
59
+
60
+ // Styling
61
+ customStylingCode = getFormattedCode(SelectionPopoverCodeExamples.customStyling, 'html');
62
+
63
+ // Interactive examples
64
+ selectedText: WritableSignal<string> = signal<string>('');
65
+ actionMessage: WritableSignal<string> = signal<string>('');
66
+
67
+ constructor(
68
+ private aiPromptService: AiPromptService
69
+ ) { }
70
+
71
+ onTextSelected(text: string): void {
72
+ this.selectedText.set(text);
73
+ this.actionMessage.set(`Selected: "${text}"`);
74
+
75
+ // Clear message after 3 seconds
76
+ setTimeout(() => {
77
+ this.actionMessage.set('');
78
+ }, 3000);
79
+ }
80
+
81
+ askAI(text: string): void {
82
+ this.selectedText.set(text);
83
+ this.actionMessage.set(`Asking AI about: "${text}"`);
84
+
85
+ setTimeout(() => {
86
+ this.actionMessage.set('');
87
+ }, 3000);
88
+ }
89
+
90
+ get claudeUrl(): string {
91
+ return this.aiPromptService.generateClaudeUrl();
92
+ }
93
+
94
+ get chatGptUrl(): string {
95
+ return this.aiPromptService.generateChatGptUrl();
96
+ }
97
+ }