@makigamestudio/ui-ionic 0.7.0 → 0.8.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/README.md +1 -0
- package/fesm2022/makigamestudio-ui-ionic.mjs +644 -20
- package/fesm2022/makigamestudio-ui-ionic.mjs.map +1 -1
- package/package.json +2 -2
- package/theme.css +54 -4
- package/theme.scss +69 -4
- package/types/makigamestudio-ui-ionic.d.ts +309 -25
|
@@ -1,10 +1,599 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject,
|
|
2
|
+
import { inject, ElementRef, Renderer2, ApplicationRef, DestroyRef, input, effect, isSignal, TemplateRef, HostListener, Directive, signal, output, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { PopoverController, IonList, IonItem, IonIcon, IonLabel, IonSpinner, IonButton } from '@ionic/angular/standalone';
|
|
4
|
-
import { ActionButtonType, ActionButtonListService, ButtonStateService, ButtonDisplayService, ButtonHandlerService } from '@makigamestudio/ui-core';
|
|
4
|
+
import { TooltipService, TooltipSchedulerService, DeviceDetectionService, ActionButtonType, ActionButtonListService, ButtonStateService, ButtonDisplayService, ButtonHandlerService } from '@makigamestudio/ui-core';
|
|
5
5
|
import { addIcons } from 'ionicons';
|
|
6
6
|
import { chevronDownOutline } from 'ionicons/icons';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @file Maki Tooltip Directive
|
|
10
|
+
* @description Ionic implementation of tooltip directive using TooltipService.
|
|
11
|
+
*
|
|
12
|
+
* This directive provides accessible tooltips that adapt to device type and viewport constraints.
|
|
13
|
+
* On desktop, tooltips appear on hover with a delay. On mobile, they appear on click (except for
|
|
14
|
+
* interactive elements like buttons where they're suppressed to avoid interfering with click handlers).
|
|
15
|
+
*
|
|
16
|
+
* All positioning and visibility logic is delegated to TooltipService from ui-core,
|
|
17
|
+
* while this directive handles Ionic-specific DOM manipulation and event handling.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // Simple string tooltip
|
|
22
|
+
* <button makiTooltip="Save changes">Save</button>
|
|
23
|
+
*
|
|
24
|
+
* // With custom color
|
|
25
|
+
* <ion-button makiTooltip="Delete item" makiTooltipColor="danger">
|
|
26
|
+
* <ion-icon name="trash" />
|
|
27
|
+
* </ion-button>
|
|
28
|
+
*
|
|
29
|
+
* // Template tooltip
|
|
30
|
+
* <ng-template #customTooltip>
|
|
31
|
+
* <strong>Custom</strong> content
|
|
32
|
+
* </ng-template>
|
|
33
|
+
* <div [makiTooltip]="customTooltip">Hover me</div>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Attribute name added to elements with tooltip directive.
|
|
38
|
+
* Useful for testing and debugging.
|
|
39
|
+
*/
|
|
40
|
+
const TOOLTIP_ATTRIBUTE = 'maki-tooltip';
|
|
41
|
+
/**
|
|
42
|
+
* Directive for displaying contextual tooltips with device-aware behavior.
|
|
43
|
+
*
|
|
44
|
+
* The directive automatically adapts its behavior based on device type:
|
|
45
|
+
* - **Desktop**: Shows tooltip on hover after 500ms delay, hides on mouse leave
|
|
46
|
+
* - **Mobile**: Shows/hides tooltip on click (suppressed for interactive elements)
|
|
47
|
+
*
|
|
48
|
+
* Tooltips are positioned intelligently to avoid viewport overflow, appearing
|
|
49
|
+
* above or to the left of the element when necessary.
|
|
50
|
+
*
|
|
51
|
+
* @usageNotes
|
|
52
|
+
*
|
|
53
|
+
* ### Basic String Tooltip
|
|
54
|
+
* ```html
|
|
55
|
+
* <button makiTooltip="Click to save">Save</button>
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ### Colored Tooltip
|
|
59
|
+
* ```html
|
|
60
|
+
* <ion-button makiTooltip="Permanently delete" makiTooltipColor="danger">
|
|
61
|
+
* Delete
|
|
62
|
+
* </ion-button>
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* ### Template Tooltip
|
|
66
|
+
* ```html
|
|
67
|
+
* <ng-template #richTooltip>
|
|
68
|
+
* <div class="custom-tooltip">
|
|
69
|
+
* <h4>Title</h4>
|
|
70
|
+
* <p>Description</p>
|
|
71
|
+
* </div>
|
|
72
|
+
* </ng-template>
|
|
73
|
+
* <div [makiTooltip]="richTooltip">Hover for details</div>
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* ### Live Updates with Signals
|
|
77
|
+
* ```html
|
|
78
|
+
* <span [makiTooltip]="statusTooltip()">Hover</span>
|
|
79
|
+
* ```
|
|
80
|
+
* ```typescript
|
|
81
|
+
* readonly statusTooltip = signal('Status: Ready');
|
|
82
|
+
*
|
|
83
|
+
* // Tooltip updates live while open
|
|
84
|
+
* this.statusTooltip.set('Status: Processing');
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* ### Mobile Behavior
|
|
88
|
+
* On mobile devices, tooltips are suppressed for interactive elements
|
|
89
|
+
* (button, ion-button, ion-select, a) to avoid interfering with their
|
|
90
|
+
* primary click handlers.
|
|
91
|
+
*/
|
|
92
|
+
class MakiTooltipDirective {
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Dependencies
|
|
95
|
+
// ============================================================================
|
|
96
|
+
el = inject(ElementRef);
|
|
97
|
+
renderer = inject(Renderer2);
|
|
98
|
+
appRef = inject(ApplicationRef);
|
|
99
|
+
destroyRef = inject(DestroyRef);
|
|
100
|
+
tooltipService = inject(TooltipService);
|
|
101
|
+
scheduler = inject(TooltipSchedulerService);
|
|
102
|
+
deviceDetection = inject(DeviceDetectionService);
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Inputs
|
|
105
|
+
// ============================================================================
|
|
106
|
+
/**
|
|
107
|
+
* Tooltip content - can be a string or a template reference.
|
|
108
|
+
*
|
|
109
|
+
* If the input is signal-driven, an open tooltip will update live
|
|
110
|
+
* when the signal value changes.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```html
|
|
114
|
+
* <!-- String content -->
|
|
115
|
+
* <button makiTooltip="Save changes">Save</button>
|
|
116
|
+
*
|
|
117
|
+
* <!-- Template content -->
|
|
118
|
+
* <ng-template #tooltip>Rich content</ng-template>
|
|
119
|
+
* <div [makiTooltip]="tooltip">Hover</div>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
content = input.required({ ...(ngDevMode ? { debugName: "content" } : {}), alias: 'makiTooltip' });
|
|
123
|
+
/**
|
|
124
|
+
* Optional color for the tooltip background.
|
|
125
|
+
* Accepts Ionic color names or null for default styling.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```html
|
|
129
|
+
* <button makiTooltip="Delete" makiTooltipColor="danger">Delete</button>
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
color = input(null, { ...(ngDevMode ? { debugName: "color" } : {}), alias: 'makiTooltipColor' });
|
|
133
|
+
/**
|
|
134
|
+
* Preferred tooltip placement. If provided, the directive will attempt to
|
|
135
|
+
* position the tooltip on the specified side before falling back.
|
|
136
|
+
*/
|
|
137
|
+
position = input(undefined, { ...(ngDevMode ? { debugName: "position" } : {}), alias: 'makiTooltipPosition' });
|
|
138
|
+
/**
|
|
139
|
+
* Optional context object for TemplateRef tooltips.
|
|
140
|
+
* Supports signal inputs so templates can react to changes.
|
|
141
|
+
* Open tooltips update live as the context signal changes.
|
|
142
|
+
*
|
|
143
|
+
* The context is exposed as both named properties and `$implicit`.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```html
|
|
147
|
+
* <ng-template #tooltip let-title="title" let-data>
|
|
148
|
+
* <strong>{{ title }}</strong>
|
|
149
|
+
* <span>{{ data?.title }}</span>
|
|
150
|
+
* </ng-template>
|
|
151
|
+
* <span [makiTooltip]="tooltip" [makiTooltipContext]="{ title: 'Info' }">Hover</span>
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
context = input(undefined, { ...(ngDevMode ? { debugName: "context" } : {}), alias: 'makiTooltipContext' });
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Private State
|
|
157
|
+
// ============================================================================
|
|
158
|
+
/**
|
|
159
|
+
* Reference to the tooltip DOM element.
|
|
160
|
+
*/
|
|
161
|
+
tooltip = null;
|
|
162
|
+
/**
|
|
163
|
+
* Timeout handle for hover delay.
|
|
164
|
+
*/
|
|
165
|
+
/**
|
|
166
|
+
* Reference to the embedded view when using template content.
|
|
167
|
+
*/
|
|
168
|
+
viewRef = null;
|
|
169
|
+
/**
|
|
170
|
+
* Tracks the current template reference to detect swaps.
|
|
171
|
+
*/
|
|
172
|
+
currentTemplateRef = null;
|
|
173
|
+
/**
|
|
174
|
+
* Cleanup function for document touch listener.
|
|
175
|
+
*/
|
|
176
|
+
touchListenerCleanup = null;
|
|
177
|
+
/**
|
|
178
|
+
* Animation frame ID for tooltip follow loop.
|
|
179
|
+
*/
|
|
180
|
+
positionAnimationFrame = null;
|
|
181
|
+
/**
|
|
182
|
+
* Timeout handle for fade-out removal.
|
|
183
|
+
*/
|
|
184
|
+
fadeTimeout = null;
|
|
185
|
+
/**
|
|
186
|
+
* Scheduler handle for this directive instance (isolated timers).
|
|
187
|
+
* */
|
|
188
|
+
schedulerHandle = null;
|
|
189
|
+
/**
|
|
190
|
+
* Indicates whether the directive has been destroyed.
|
|
191
|
+
*/
|
|
192
|
+
isDestroyed = false;
|
|
193
|
+
/**
|
|
194
|
+
* Latest resolved TemplateRef context.
|
|
195
|
+
*/
|
|
196
|
+
latestTemplateContext = { $implicit: undefined };
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Constructor
|
|
199
|
+
// ============================================================================
|
|
200
|
+
constructor() {
|
|
201
|
+
// Add custom attribute for identification
|
|
202
|
+
this.el.nativeElement.setAttribute(TOOLTIP_ATTRIBUTE, 'true');
|
|
203
|
+
effect(() => {
|
|
204
|
+
this.latestTemplateContext = this.resolveTemplateContext();
|
|
205
|
+
if (this.viewRef) {
|
|
206
|
+
Object.assign(this.viewRef.context, this.latestTemplateContext);
|
|
207
|
+
this.viewRef.detectChanges();
|
|
208
|
+
this.positionTooltip();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
effect(() => {
|
|
212
|
+
const contentValue = this.content();
|
|
213
|
+
if (!this.tooltip)
|
|
214
|
+
return;
|
|
215
|
+
this.updateTooltipContent(contentValue);
|
|
216
|
+
this.positionTooltip();
|
|
217
|
+
});
|
|
218
|
+
// Create isolated scheduler handle for this directive instance
|
|
219
|
+
this.schedulerHandle = this.scheduler.createHandle(() => this.showTooltip(), () => this.hideTooltip());
|
|
220
|
+
// Cleanup on destroy
|
|
221
|
+
this.destroyRef.onDestroy(() => {
|
|
222
|
+
this.isDestroyed = true;
|
|
223
|
+
this.schedulerHandle?.destroy();
|
|
224
|
+
this.hideTooltip();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Event Handlers
|
|
229
|
+
// ============================================================================
|
|
230
|
+
/**
|
|
231
|
+
* Shows tooltip on mouse enter (desktop only).
|
|
232
|
+
* Adds a 500ms delay to avoid showing tooltips during quick mouse movements.
|
|
233
|
+
*/
|
|
234
|
+
onMouseEnter() {
|
|
235
|
+
if (!this.canShowTooltip())
|
|
236
|
+
return;
|
|
237
|
+
this.schedulerHandle?.onTriggerEnter();
|
|
238
|
+
}
|
|
239
|
+
onMouseLeave() {
|
|
240
|
+
this.schedulerHandle?.onTriggerLeave();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Toggles tooltip on click (all devices).
|
|
244
|
+
* On mobile, skips interactive elements. On desktop, acts as a toggle.
|
|
245
|
+
*/
|
|
246
|
+
onClick() {
|
|
247
|
+
if (!this.canShowTooltip()) {
|
|
248
|
+
this.hideTooltip();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// En mobile, activar touch state
|
|
252
|
+
this.schedulerHandle?.onClick(this.deviceDetection.isMobile());
|
|
253
|
+
}
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// Private Methods
|
|
256
|
+
// ============================================================================
|
|
257
|
+
/**
|
|
258
|
+
* Checks if tooltip can be shown using TooltipService logic.
|
|
259
|
+
*
|
|
260
|
+
* @returns `true` if tooltip can be shown, `false` otherwise
|
|
261
|
+
*/
|
|
262
|
+
canShowTooltip() {
|
|
263
|
+
const elementTag = this.el.nativeElement.tagName.toLowerCase();
|
|
264
|
+
const hasContent = !!this.content();
|
|
265
|
+
return this.tooltipService.shouldShowTooltip(elementTag, this.deviceDetection.isMobile(), hasContent);
|
|
266
|
+
}
|
|
267
|
+
resolveTemplateContext() {
|
|
268
|
+
const rawContext = this.context();
|
|
269
|
+
const resolvedContext = this.unwrapSignal(rawContext);
|
|
270
|
+
if (resolvedContext && typeof resolvedContext === 'object') {
|
|
271
|
+
return {
|
|
272
|
+
...resolvedContext,
|
|
273
|
+
$implicit: resolvedContext
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return { $implicit: resolvedContext };
|
|
277
|
+
}
|
|
278
|
+
unwrapSignal(value) {
|
|
279
|
+
if (isSignal(value)) {
|
|
280
|
+
return value();
|
|
281
|
+
}
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Clears the hover timeout if it exists.
|
|
286
|
+
*/
|
|
287
|
+
/**
|
|
288
|
+
* Creates and displays the tooltip element.
|
|
289
|
+
*
|
|
290
|
+
* The tooltip is created as a fixed-position element appended to the document body.
|
|
291
|
+
* Content can be either plain text or a rendered template.
|
|
292
|
+
*/
|
|
293
|
+
showTooltip() {
|
|
294
|
+
if (this.tooltip || !this.content())
|
|
295
|
+
return;
|
|
296
|
+
// Create tooltip element
|
|
297
|
+
this.tooltip = this.renderer.createElement('div');
|
|
298
|
+
this.renderer.addClass(this.tooltip, 'maki-tooltip');
|
|
299
|
+
if (!this.tooltip)
|
|
300
|
+
return;
|
|
301
|
+
// Ensure tooltip is not visible or interactive while we measure and position it
|
|
302
|
+
this.tooltip.style.position = 'fixed';
|
|
303
|
+
this.tooltip.style.top = '-9999px';
|
|
304
|
+
this.tooltip.style.left = '-9999px';
|
|
305
|
+
this.tooltip.style.visibility = 'hidden';
|
|
306
|
+
this.tooltip.style.pointerEvents = 'none';
|
|
307
|
+
this.tooltip.style.opacity = '0';
|
|
308
|
+
this.tooltip.style.transition = `opacity ${this.tooltipService.getFadeDurationMs()}ms ease`;
|
|
309
|
+
// Apply color attribute if specified
|
|
310
|
+
const colorValue = this.color();
|
|
311
|
+
if (colorValue) {
|
|
312
|
+
this.renderer.setAttribute(this.tooltip, 'data-color', colorValue);
|
|
313
|
+
}
|
|
314
|
+
// Set content (template or string)
|
|
315
|
+
const contentValue = this.content();
|
|
316
|
+
this.updateTooltipContent(contentValue);
|
|
317
|
+
// Append hidden tooltip to body so we can measure it without visual jump
|
|
318
|
+
this.renderer.appendChild(document.body, this.tooltip);
|
|
319
|
+
// If there was a pending fade removal, cancel it (we're re-showing)
|
|
320
|
+
this.clearFadeTimeout();
|
|
321
|
+
// Immediately position the tooltip while hidden to avoid a visual jump
|
|
322
|
+
this.positionTooltip();
|
|
323
|
+
// Reveal and make interactive with fade-in
|
|
324
|
+
// Make visible before starting opacity animation so it's measured properly
|
|
325
|
+
this.tooltip.style.visibility = 'visible';
|
|
326
|
+
// Allow pointer events after showing
|
|
327
|
+
this.tooltip.style.pointerEvents = 'auto';
|
|
328
|
+
// Trigger fade-in
|
|
329
|
+
// Force a reflow to ensure transition runs
|
|
330
|
+
this.tooltip.getBoundingClientRect();
|
|
331
|
+
this.tooltip.style.opacity = '1';
|
|
332
|
+
// Añadir listeners para interacción (desktop y mobile)
|
|
333
|
+
this.addTooltipInteractionListeners();
|
|
334
|
+
// Start animation frame loop to follow trigger
|
|
335
|
+
this.startTooltipFollowLoop();
|
|
336
|
+
// Add document listeners on mobile
|
|
337
|
+
if (this.deviceDetection.isMobile()) {
|
|
338
|
+
this.addDocumentTouchListener();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Positions the tooltip using TooltipService calculations.
|
|
343
|
+
*/
|
|
344
|
+
positionTooltip() {
|
|
345
|
+
if (!this.tooltip)
|
|
346
|
+
return;
|
|
347
|
+
const triggerRect = this.el.nativeElement.getBoundingClientRect();
|
|
348
|
+
// Hide tooltip if element is not visible
|
|
349
|
+
if (!this.tooltipService.isElementVisible(triggerRect)) {
|
|
350
|
+
this.hideTooltip();
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const tooltipRect = this.tooltip.getBoundingClientRect();
|
|
354
|
+
// Calculate position using service
|
|
355
|
+
const position = this.tooltipService.calculatePosition(triggerRect, tooltipRect, window.innerWidth, window.innerHeight, this.position());
|
|
356
|
+
// Apply position
|
|
357
|
+
this.tooltip.style.position = 'fixed';
|
|
358
|
+
this.tooltip.style.zIndex = '10000';
|
|
359
|
+
this.tooltip.style.top = `${position.top}px`;
|
|
360
|
+
this.tooltip.style.left = `${position.left}px`;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Adds a document-level touch listener to hide tooltip on any touch outside.
|
|
364
|
+
* Only used on mobile devices.
|
|
365
|
+
*/
|
|
366
|
+
addDocumentTouchListener() {
|
|
367
|
+
this.removeDocumentTouchListener();
|
|
368
|
+
this.touchListenerCleanup = this.renderer.listen('document', 'touchstart', (event) => {
|
|
369
|
+
if (!this.tooltip)
|
|
370
|
+
return;
|
|
371
|
+
// Allow touches inside the tooltip or the trigger element to keep it open
|
|
372
|
+
const target = event.target;
|
|
373
|
+
if (target && (this.tooltip.contains(target) || this.el.nativeElement.contains(target))) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
// Any touch outside should hide the tooltip
|
|
377
|
+
this.hideTooltip();
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Removes the document-level touch listener.
|
|
382
|
+
*/
|
|
383
|
+
removeDocumentTouchListener() {
|
|
384
|
+
if (this.touchListenerCleanup) {
|
|
385
|
+
this.touchListenerCleanup();
|
|
386
|
+
this.touchListenerCleanup = null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Removes the tooltip from the DOM and cleans up resources.
|
|
391
|
+
*/
|
|
392
|
+
hideTooltip() {
|
|
393
|
+
this.removeDocumentTouchListener();
|
|
394
|
+
this.removeTooltipInteractionListeners();
|
|
395
|
+
this.stopTooltipFollowLoop();
|
|
396
|
+
// If there's no tooltip (already removed), nothing to do
|
|
397
|
+
if (!this.tooltip)
|
|
398
|
+
return;
|
|
399
|
+
// Begin fade-out and removal
|
|
400
|
+
this.tooltip.style.opacity = '0';
|
|
401
|
+
this.tooltip.style.pointerEvents = 'none';
|
|
402
|
+
// Schedule removal after fade duration.
|
|
403
|
+
this.clearFadeTimeout();
|
|
404
|
+
this.fadeTimeout = setTimeout(() => {
|
|
405
|
+
if (this.tooltip) {
|
|
406
|
+
// Avoid leaving the tooltip empty during the fade animation.
|
|
407
|
+
if (this.viewRef) {
|
|
408
|
+
if (!this.isDestroyed) {
|
|
409
|
+
try {
|
|
410
|
+
this.appRef.detachView(this.viewRef);
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
// ignore detach errors
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
this.viewRef.destroy();
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// ignore destroy errors
|
|
421
|
+
}
|
|
422
|
+
this.viewRef = null;
|
|
423
|
+
this.currentTemplateRef = null;
|
|
424
|
+
}
|
|
425
|
+
this.renderer.removeChild(document.body, this.tooltip);
|
|
426
|
+
this.tooltip = null;
|
|
427
|
+
}
|
|
428
|
+
this.fadeTimeout = null;
|
|
429
|
+
}, this.tooltipService.getFadeDurationMs());
|
|
430
|
+
}
|
|
431
|
+
clearFadeTimeout() {
|
|
432
|
+
if (this.fadeTimeout) {
|
|
433
|
+
clearTimeout(this.fadeTimeout);
|
|
434
|
+
this.fadeTimeout = null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
updateTooltipContent(contentValue) {
|
|
438
|
+
if (!this.tooltip)
|
|
439
|
+
return;
|
|
440
|
+
if (contentValue instanceof TemplateRef) {
|
|
441
|
+
this.updateTemplateContent(contentValue);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.updateStringContent(contentValue);
|
|
445
|
+
}
|
|
446
|
+
updateTemplateContent(template) {
|
|
447
|
+
if (!this.tooltip)
|
|
448
|
+
return;
|
|
449
|
+
const shouldRecreate = this.currentTemplateRef !== template || !this.viewRef;
|
|
450
|
+
if (shouldRecreate) {
|
|
451
|
+
this.clearTooltipContent();
|
|
452
|
+
this.viewRef = template.createEmbeddedView(this.latestTemplateContext);
|
|
453
|
+
// Avoid calling attachView after destroy; it can log NG0406 warnings.
|
|
454
|
+
if (!this.isDestroyed) {
|
|
455
|
+
this.appRef.attachView(this.viewRef);
|
|
456
|
+
}
|
|
457
|
+
this.viewRef.rootNodes.forEach(node => this.renderer.appendChild(this.tooltip, node));
|
|
458
|
+
this.currentTemplateRef = template;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
updateStringContent(contentValue) {
|
|
462
|
+
if (!this.tooltip)
|
|
463
|
+
return;
|
|
464
|
+
if (this.viewRef || this.currentTemplateRef) {
|
|
465
|
+
this.clearTooltipContent();
|
|
466
|
+
}
|
|
467
|
+
this.tooltip.textContent = contentValue;
|
|
468
|
+
this.currentTemplateRef = null;
|
|
469
|
+
}
|
|
470
|
+
clearTooltipContent() {
|
|
471
|
+
if (!this.tooltip)
|
|
472
|
+
return;
|
|
473
|
+
if (this.viewRef) {
|
|
474
|
+
if (!this.isDestroyed) {
|
|
475
|
+
try {
|
|
476
|
+
this.appRef.detachView(this.viewRef);
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
// ignore detach errors
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
this.viewRef.destroy();
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// ignore destroy errors
|
|
487
|
+
}
|
|
488
|
+
this.viewRef = null;
|
|
489
|
+
}
|
|
490
|
+
while (this.tooltip.firstChild) {
|
|
491
|
+
this.renderer.removeChild(this.tooltip, this.tooltip.firstChild);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Añade listeners de interacción al tooltip para mantenerlo abierto mientras se interactúa.
|
|
496
|
+
*/
|
|
497
|
+
addTooltipInteractionListeners() {
|
|
498
|
+
if (!this.tooltip)
|
|
499
|
+
return;
|
|
500
|
+
// Siempre elimina antes por si acaso (defensivo)
|
|
501
|
+
this.removeTooltipInteractionListeners();
|
|
502
|
+
// Desktop: mouseenter/mouseleave
|
|
503
|
+
this.tooltip.addEventListener('mouseenter', this.onTooltipMouseEnter);
|
|
504
|
+
this.tooltip.addEventListener('mouseleave', this.onTooltipMouseLeave);
|
|
505
|
+
// Mobile: touchstart/touchend
|
|
506
|
+
this.tooltip.addEventListener('touchstart', this.onTooltipTouchStart, { passive: true });
|
|
507
|
+
this.tooltip.addEventListener('touchend', this.onTooltipTouchEnd, { passive: true });
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Elimina listeners de interacción del tooltip.
|
|
511
|
+
*/
|
|
512
|
+
removeTooltipInteractionListeners() {
|
|
513
|
+
if (!this.tooltip)
|
|
514
|
+
return;
|
|
515
|
+
this.tooltip.removeEventListener('mouseenter', this.onTooltipMouseEnter);
|
|
516
|
+
this.tooltip.removeEventListener('mouseleave', this.onTooltipMouseLeave);
|
|
517
|
+
this.tooltip.removeEventListener('touchstart', this.onTooltipTouchStart);
|
|
518
|
+
this.tooltip.removeEventListener('touchend', this.onTooltipTouchEnd);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Handler: mouse entra en el tooltip (desktop).
|
|
522
|
+
*/
|
|
523
|
+
onTooltipMouseEnter = () => {
|
|
524
|
+
this.schedulerHandle?.onTooltipEnter();
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Handler: mouse sale del tooltip (desktop).
|
|
528
|
+
*/
|
|
529
|
+
onTooltipMouseLeave = () => {
|
|
530
|
+
this.schedulerHandle?.onTooltipLeave();
|
|
531
|
+
};
|
|
532
|
+
/**
|
|
533
|
+
* Handler: touchstart en el tooltip (mobile).
|
|
534
|
+
*/
|
|
535
|
+
onTooltipTouchStart = () => {
|
|
536
|
+
this.schedulerHandle?.onTooltipTouchStart();
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Handler: touchend en el tooltip (mobile).
|
|
540
|
+
*/
|
|
541
|
+
onTooltipTouchEnd = () => {
|
|
542
|
+
this.schedulerHandle?.onTooltipTouchEnd();
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* Starts a high-performance animation frame loop to keep the tooltip positioned with its trigger.
|
|
546
|
+
*/
|
|
547
|
+
startTooltipFollowLoop() {
|
|
548
|
+
this.stopTooltipFollowLoop();
|
|
549
|
+
const loop = () => {
|
|
550
|
+
if (!this.tooltip)
|
|
551
|
+
return;
|
|
552
|
+
this.positionTooltip();
|
|
553
|
+
this.positionAnimationFrame = requestAnimationFrame(loop);
|
|
554
|
+
};
|
|
555
|
+
this.positionAnimationFrame = requestAnimationFrame(loop);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Stops the animation frame loop for tooltip positioning.
|
|
559
|
+
*/
|
|
560
|
+
stopTooltipFollowLoop() {
|
|
561
|
+
if (this.positionAnimationFrame !== null) {
|
|
562
|
+
cancelAnimationFrame(this.positionAnimationFrame);
|
|
563
|
+
this.positionAnimationFrame = null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Cleanup on directive destruction.
|
|
568
|
+
*/
|
|
569
|
+
ngOnDestroy() {
|
|
570
|
+
this.hideTooltip();
|
|
571
|
+
}
|
|
572
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MakiTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
573
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: MakiTooltipDirective, isStandalone: true, selector: "[makiTooltip]", inputs: { content: { classPropertyName: "content", publicName: "makiTooltip", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "makiTooltipColor", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "makiTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "makiTooltipContext", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "click": "onClick()" } }, ngImport: i0 });
|
|
574
|
+
}
|
|
575
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MakiTooltipDirective, decorators: [{
|
|
576
|
+
type: Directive,
|
|
577
|
+
args: [{
|
|
578
|
+
selector: '[makiTooltip]',
|
|
579
|
+
standalone: true
|
|
580
|
+
}]
|
|
581
|
+
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "makiTooltip", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "makiTooltipColor", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "makiTooltipPosition", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "makiTooltipContext", required: false }] }], onMouseEnter: [{
|
|
582
|
+
type: HostListener,
|
|
583
|
+
args: ['mouseenter']
|
|
584
|
+
}], onMouseLeave: [{
|
|
585
|
+
type: HostListener,
|
|
586
|
+
args: ['mouseleave']
|
|
587
|
+
}], onClick: [{
|
|
588
|
+
type: HostListener,
|
|
589
|
+
args: ['click']
|
|
590
|
+
}] } });
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @file Directives Barrel Export
|
|
594
|
+
* @description Exports all directives from the ui-ionic library.
|
|
595
|
+
*/
|
|
596
|
+
|
|
8
597
|
/**
|
|
9
598
|
* @file Action Button List Component
|
|
10
599
|
* @description Ionic implementation of action button list for popovers/dropdowns.
|
|
@@ -44,10 +633,10 @@ import { chevronDownOutline } from 'ionicons/icons';
|
|
|
44
633
|
*
|
|
45
634
|
* @usageNotes
|
|
46
635
|
* ### Inputs
|
|
47
|
-
* - `buttons` (required): Array of `
|
|
636
|
+
* - `buttons` (required): Array of `IonicActionButton` objects to display
|
|
48
637
|
*
|
|
49
638
|
* ### Outputs
|
|
50
|
-
* - `buttonSelect`: Emits the selected `
|
|
639
|
+
* - `buttonSelect`: Emits the selected `IonicActionButton` when clicked
|
|
51
640
|
*/
|
|
52
641
|
class ActionButtonListComponent {
|
|
53
642
|
/** Reference to ActionButtonType.Dropdown for template comparison. */
|
|
@@ -110,6 +699,10 @@ class ActionButtonListComponent {
|
|
|
110
699
|
<ion-item
|
|
111
700
|
[button]="true"
|
|
112
701
|
[disabled]="!canSelectButton(button)"
|
|
702
|
+
[attr.aria-label]="button.ariaLabel"
|
|
703
|
+
[makiTooltip]="button.tooltip || ''"
|
|
704
|
+
[makiTooltipColor]="button.tooltipColor ?? null"
|
|
705
|
+
makiTooltipPosition="left"
|
|
113
706
|
[detail]="button.type === ActionButtonType.Dropdown"
|
|
114
707
|
(click)="onButtonClick(button)"
|
|
115
708
|
>
|
|
@@ -128,16 +721,20 @@ class ActionButtonListComponent {
|
|
|
128
721
|
</ion-item>
|
|
129
722
|
}
|
|
130
723
|
</ion-list>
|
|
131
|
-
`, isInline: true, styles: [":host{display:block}::ng-deep ion-popover::part(content){width:fit-content}ion-list{padding:0}ion-item{--padding-start: var(--maki-action-button-list-padding, 16px);--padding-end: var(--maki-action-button-list-padding, 16px);--min-height: var(--maki-action-button-list-item-height, 44px);cursor:pointer;width:100%}ion-item[disabled]{cursor:not-allowed}ion-label{white-space:nowrap}ion-icon{font-size:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}ion-spinner{width:var(--maki-action-button-list-icon-size, 20px);height:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}\n"], dependencies: [{ kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
724
|
+
`, isInline: true, styles: [":host{display:block}::ng-deep ion-popover::part(content){width:fit-content}ion-list{padding:0}ion-item{--padding-start: var(--maki-action-button-list-padding, 16px);--padding-end: var(--maki-action-button-list-padding, 16px);--min-height: var(--maki-action-button-list-item-height, 44px);cursor:pointer;width:100%}ion-item[disabled]{cursor:not-allowed}ion-label{white-space:nowrap}ion-icon{font-size:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}ion-spinner{width:var(--maki-action-button-list-icon-size, 20px);height:var(--maki-action-button-list-icon-size, 20px);margin-inline-end:var(--maki-action-button-list-icon-gap, 12px)}\n"], dependencies: [{ kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "directive", type: MakiTooltipDirective, selector: "[makiTooltip]", inputs: ["makiTooltip", "makiTooltipColor", "makiTooltipPosition", "makiTooltipContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
132
725
|
}
|
|
133
726
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ActionButtonListComponent, decorators: [{
|
|
134
727
|
type: Component,
|
|
135
|
-
args: [{ selector: 'maki-action-button-list', standalone: true, imports: [IonList, IonItem, IonIcon, IonLabel, IonSpinner], providers: [ActionButtonListService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
728
|
+
args: [{ selector: 'maki-action-button-list', standalone: true, imports: [IonList, IonItem, IonIcon, IonLabel, IonSpinner, MakiTooltipDirective], providers: [ActionButtonListService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
136
729
|
<ion-list lines="none">
|
|
137
730
|
@for (button of buttonList(); track button.id) {
|
|
138
731
|
<ion-item
|
|
139
732
|
[button]="true"
|
|
140
733
|
[disabled]="!canSelectButton(button)"
|
|
734
|
+
[attr.aria-label]="button.ariaLabel"
|
|
735
|
+
[makiTooltip]="button.tooltip || ''"
|
|
736
|
+
[makiTooltipColor]="button.tooltipColor ?? null"
|
|
737
|
+
makiTooltipPosition="left"
|
|
141
738
|
[detail]="button.type === ActionButtonType.Dropdown"
|
|
142
739
|
(click)="onButtonClick(button)"
|
|
143
740
|
>
|
|
@@ -174,7 +771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
174
771
|
*/
|
|
175
772
|
/**
|
|
176
773
|
* A configurable button component that renders an `ion-button` based on
|
|
177
|
-
* an `
|
|
774
|
+
* an `IonicActionButton` configuration object.
|
|
178
775
|
*
|
|
179
776
|
* Features:
|
|
180
777
|
* - Two button types: Default and Dropdown
|
|
@@ -197,26 +794,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
197
794
|
*
|
|
198
795
|
* <!-- Dropdown button -->
|
|
199
796
|
* <maki-button [button]="menuButton" />
|
|
797
|
+
*
|
|
798
|
+
* <!-- Button with tooltip -->
|
|
799
|
+
* <maki-button [button]="buttonWithTooltip" />
|
|
200
800
|
* ```
|
|
201
801
|
*
|
|
202
802
|
* @example
|
|
203
803
|
* ```typescript
|
|
204
804
|
* // In your component
|
|
205
|
-
* import {
|
|
805
|
+
* import { ActionButtonType } from '@makigamestudio/ui-core';
|
|
206
806
|
* import { IonicActionButton } from '@makigamestudio/ui-ionic';
|
|
807
|
+
* import { TemplateRef, ViewChild } from '@angular/core';
|
|
207
808
|
*
|
|
208
|
-
* //
|
|
209
|
-
* saveButton:
|
|
809
|
+
* // Button with string tooltip
|
|
810
|
+
* saveButton: IonicActionButton = {
|
|
210
811
|
* id: 'save',
|
|
211
812
|
* label: 'Save',
|
|
212
813
|
* icon: 'save-outline',
|
|
213
814
|
* type: ActionButtonType.Default,
|
|
815
|
+
* tooltip: 'Save your changes',
|
|
214
816
|
* config: { fill: 'solid', color: 'primary' },
|
|
215
817
|
* handler: async () => {
|
|
216
818
|
* await this.saveData();
|
|
217
819
|
* }
|
|
218
820
|
* };
|
|
219
821
|
*
|
|
822
|
+
* // Button with colored tooltip
|
|
823
|
+
* deleteButton: IonicActionButton = {
|
|
824
|
+
* id: 'delete',
|
|
825
|
+
* icon: 'trash-outline',
|
|
826
|
+
* type: ActionButtonType.Default,
|
|
827
|
+
* tooltip: 'Permanently delete this item',
|
|
828
|
+
* tooltipColor: 'danger',
|
|
829
|
+
* config: { fill: 'clear', color: 'danger' },
|
|
830
|
+
* handler: () => this.deleteItem()
|
|
831
|
+
* };
|
|
832
|
+
*
|
|
833
|
+
* // Button with template tooltip
|
|
834
|
+
* @ViewChild('richTooltip') richTooltipTemplate!: TemplateRef<unknown>;
|
|
835
|
+
*
|
|
836
|
+
* infoButton: IonicActionButton = {
|
|
837
|
+
* id: 'info',
|
|
838
|
+
* icon: 'information-circle-outline',
|
|
839
|
+
* type: ActionButtonType.Default,
|
|
840
|
+
* tooltip: this.richTooltipTemplate,
|
|
841
|
+
* tooltipColor: 'primary',
|
|
842
|
+
* handler: () => {}
|
|
843
|
+
* };
|
|
844
|
+
*
|
|
220
845
|
* // IonicActionButton (strict typing for Ionic-specific config)
|
|
221
846
|
* ionicButton: IonicActionButton = {
|
|
222
847
|
* id: 'ionic',
|
|
@@ -229,7 +854,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
229
854
|
* }
|
|
230
855
|
* };
|
|
231
856
|
*
|
|
232
|
-
* menuButton:
|
|
857
|
+
* menuButton: IonicActionButton = {
|
|
233
858
|
* id: 'menu',
|
|
234
859
|
* label: 'Actions',
|
|
235
860
|
* type: ActionButtonType.Dropdown,
|
|
@@ -243,7 +868,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
243
868
|
*
|
|
244
869
|
* @usageNotes
|
|
245
870
|
* ### Inputs
|
|
246
|
-
* - `button` (required): The `
|
|
871
|
+
* - `button` (required): The `IonicActionButton` configuration object
|
|
247
872
|
*
|
|
248
873
|
* ### Outputs
|
|
249
874
|
* - `buttonClick`: Emits the button configuration when clicked (for non-dropdown buttons)
|
|
@@ -318,9 +943,6 @@ class ButtonComponent {
|
|
|
318
943
|
async openDropdown(event) {
|
|
319
944
|
const btn = this.button();
|
|
320
945
|
const children = btn.children ?? [];
|
|
321
|
-
if (children.length === 0) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
946
|
const alignment = (btn.config?.dropdownAlignment ?? 'end');
|
|
325
947
|
const popover = await this.popoverCtrl.create({
|
|
326
948
|
component: ActionButtonListComponent,
|
|
@@ -360,7 +982,8 @@ class ButtonComponent {
|
|
|
360
982
|
[strong]="button().config?.strong"
|
|
361
983
|
[disabled]="isDisabled()"
|
|
362
984
|
[attr.aria-label]="button().ariaLabel"
|
|
363
|
-
[
|
|
985
|
+
[makiTooltip]="button().tooltip || ''"
|
|
986
|
+
[makiTooltipColor]="button().tooltipColor ?? null"
|
|
364
987
|
(click)="onClick($event)"
|
|
365
988
|
>
|
|
366
989
|
@if (showLoadingSpinner()) {
|
|
@@ -375,11 +998,11 @@ class ButtonComponent {
|
|
|
375
998
|
<ion-icon name="chevron-down-outline" slot="end" class="dropdown-icon" />
|
|
376
999
|
}
|
|
377
1000
|
</ion-button>
|
|
378
|
-
`, isInline: true, styles: ["ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end);text-transform:none}ion-button.button-has-icon-only::part(native){padding:0}ion-spinner{width:var(--maki-button-spinner-size, 16px);height:var(--maki-button-spinner-size, 16px);margin:0 4px 0 0}.button-icon{font-size:var(--maki-button-icon-size, 16px);margin:0 4px 0 0}.button-icon[slot=icon-only]{margin:0}.dropdown-icon{font-size:var(--maki-button-dropdown-icon-size, 16px);margin-inline-start:var(--maki-button-dropdown-icon-gap, 4px)}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1001
|
+
`, isInline: true, styles: ["ion-button{--padding-start: var(--maki-button-padding-start);--padding-end: var(--maki-button-padding-end);text-transform:none}ion-button.button-has-icon-only::part(native){padding:0}ion-spinner{width:var(--maki-button-spinner-size, 16px);height:var(--maki-button-spinner-size, 16px);margin:0 4px 0 0}.button-icon{font-size:var(--maki-button-icon-size, 16px);margin:0 4px 0 0}.button-icon[slot=icon-only]{margin:0}.dropdown-icon{font-size:var(--maki-button-dropdown-icon-size, 16px);margin-inline-start:var(--maki-button-dropdown-icon-gap, 4px)}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "directive", type: MakiTooltipDirective, selector: "[makiTooltip]", inputs: ["makiTooltip", "makiTooltipColor", "makiTooltipPosition", "makiTooltipContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
379
1002
|
}
|
|
380
1003
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
381
1004
|
type: Component,
|
|
382
|
-
args: [{ selector: 'maki-button', standalone: true, imports: [IonButton, IonIcon, IonSpinner], providers: [ButtonStateService, ButtonDisplayService, ButtonHandlerService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1005
|
+
args: [{ selector: 'maki-button', standalone: true, imports: [IonButton, IonIcon, IonSpinner, MakiTooltipDirective], providers: [ButtonStateService, ButtonDisplayService, ButtonHandlerService], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
383
1006
|
<ion-button
|
|
384
1007
|
[fill]="button().config?.fill"
|
|
385
1008
|
[size]="button().config?.size"
|
|
@@ -389,7 +1012,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
389
1012
|
[strong]="button().config?.strong"
|
|
390
1013
|
[disabled]="isDisabled()"
|
|
391
1014
|
[attr.aria-label]="button().ariaLabel"
|
|
392
|
-
[
|
|
1015
|
+
[makiTooltip]="button().tooltip || ''"
|
|
1016
|
+
[makiTooltipColor]="button().tooltipColor ?? null"
|
|
393
1017
|
(click)="onClick($event)"
|
|
394
1018
|
>
|
|
395
1019
|
@if (showLoadingSpinner()) {
|
|
@@ -426,5 +1050,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
426
1050
|
* Generated bundle index. Do not edit.
|
|
427
1051
|
*/
|
|
428
1052
|
|
|
429
|
-
export { ActionButtonListComponent, ButtonComponent };
|
|
1053
|
+
export { ActionButtonListComponent, ButtonComponent, MakiTooltipDirective };
|
|
430
1054
|
//# sourceMappingURL=makigamestudio-ui-ionic.mjs.map
|