ngx-com 0.0.1 → 0.0.4

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 (82) hide show
  1. package/fesm2022/ngx-com-components-avatar.mjs +772 -0
  2. package/fesm2022/ngx-com-components-avatar.mjs.map +1 -0
  3. package/fesm2022/ngx-com-components-badge.mjs +138 -0
  4. package/fesm2022/ngx-com-components-badge.mjs.map +1 -0
  5. package/fesm2022/ngx-com-components-button.mjs +146 -0
  6. package/fesm2022/ngx-com-components-button.mjs.map +1 -0
  7. package/fesm2022/ngx-com-components-calendar.mjs +5046 -0
  8. package/fesm2022/ngx-com-components-calendar.mjs.map +1 -0
  9. package/fesm2022/ngx-com-components-card.mjs +590 -0
  10. package/fesm2022/ngx-com-components-card.mjs.map +1 -0
  11. package/fesm2022/ngx-com-components-checkbox.mjs +344 -0
  12. package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -0
  13. package/fesm2022/ngx-com-components-collapsible.mjs +612 -0
  14. package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -0
  15. package/fesm2022/ngx-com-components-confirm.mjs +562 -0
  16. package/fesm2022/ngx-com-components-confirm.mjs.map +1 -0
  17. package/fesm2022/ngx-com-components-dropdown-testing.mjs +255 -0
  18. package/fesm2022/ngx-com-components-dropdown-testing.mjs.map +1 -0
  19. package/fesm2022/ngx-com-components-dropdown.mjs +2692 -0
  20. package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -0
  21. package/fesm2022/ngx-com-components-empty-state.mjs +382 -0
  22. package/fesm2022/ngx-com-components-empty-state.mjs.map +1 -0
  23. package/fesm2022/ngx-com-components-form-field.mjs +924 -0
  24. package/fesm2022/ngx-com-components-form-field.mjs.map +1 -0
  25. package/fesm2022/ngx-com-components-icon.mjs +183 -0
  26. package/fesm2022/ngx-com-components-icon.mjs.map +1 -0
  27. package/fesm2022/ngx-com-components-item.mjs +578 -0
  28. package/fesm2022/ngx-com-components-item.mjs.map +1 -0
  29. package/fesm2022/ngx-com-components-menu.mjs +1200 -0
  30. package/fesm2022/ngx-com-components-menu.mjs.map +1 -0
  31. package/fesm2022/ngx-com-components-paginator.mjs +823 -0
  32. package/fesm2022/ngx-com-components-paginator.mjs.map +1 -0
  33. package/fesm2022/ngx-com-components-popover.mjs +901 -0
  34. package/fesm2022/ngx-com-components-popover.mjs.map +1 -0
  35. package/fesm2022/ngx-com-components-radio.mjs +621 -0
  36. package/fesm2022/ngx-com-components-radio.mjs.map +1 -0
  37. package/fesm2022/ngx-com-components-segmented-control.mjs +538 -0
  38. package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -0
  39. package/fesm2022/ngx-com-components-sort.mjs +368 -0
  40. package/fesm2022/ngx-com-components-sort.mjs.map +1 -0
  41. package/fesm2022/ngx-com-components-spinner.mjs +189 -0
  42. package/fesm2022/ngx-com-components-spinner.mjs.map +1 -0
  43. package/fesm2022/ngx-com-components-tabs.mjs +1522 -0
  44. package/fesm2022/ngx-com-components-tabs.mjs.map +1 -0
  45. package/fesm2022/ngx-com-components-tooltip.mjs +625 -0
  46. package/fesm2022/ngx-com-components-tooltip.mjs.map +1 -0
  47. package/fesm2022/ngx-com-components.mjs +17 -0
  48. package/fesm2022/ngx-com-components.mjs.map +1 -0
  49. package/fesm2022/ngx-com-tokens.mjs +12 -0
  50. package/fesm2022/ngx-com-tokens.mjs.map +1 -0
  51. package/fesm2022/ngx-com-utils.mjs +601 -0
  52. package/fesm2022/ngx-com-utils.mjs.map +1 -0
  53. package/fesm2022/ngx-com.mjs +9 -23
  54. package/fesm2022/ngx-com.mjs.map +1 -1
  55. package/package.json +105 -1
  56. package/types/ngx-com-components-avatar.d.ts +409 -0
  57. package/types/ngx-com-components-badge.d.ts +97 -0
  58. package/types/ngx-com-components-button.d.ts +69 -0
  59. package/types/ngx-com-components-calendar.d.ts +1665 -0
  60. package/types/ngx-com-components-card.d.ts +373 -0
  61. package/types/ngx-com-components-checkbox.d.ts +116 -0
  62. package/types/ngx-com-components-collapsible.d.ts +379 -0
  63. package/types/ngx-com-components-confirm.d.ts +160 -0
  64. package/types/ngx-com-components-dropdown-testing.d.ts +116 -0
  65. package/types/ngx-com-components-dropdown.d.ts +938 -0
  66. package/types/ngx-com-components-empty-state.d.ts +269 -0
  67. package/types/ngx-com-components-form-field.d.ts +531 -0
  68. package/types/ngx-com-components-icon.d.ts +94 -0
  69. package/types/ngx-com-components-item.d.ts +336 -0
  70. package/types/ngx-com-components-menu.d.ts +479 -0
  71. package/types/ngx-com-components-paginator.d.ts +265 -0
  72. package/types/ngx-com-components-popover.d.ts +309 -0
  73. package/types/ngx-com-components-radio.d.ts +258 -0
  74. package/types/ngx-com-components-segmented-control.d.ts +274 -0
  75. package/types/ngx-com-components-sort.d.ts +133 -0
  76. package/types/ngx-com-components-spinner.d.ts +120 -0
  77. package/types/ngx-com-components-tabs.d.ts +396 -0
  78. package/types/ngx-com-components-tooltip.d.ts +200 -0
  79. package/types/ngx-com-components.d.ts +12 -0
  80. package/types/ngx-com-tokens.d.ts +7 -0
  81. package/types/ngx-com-utils.d.ts +424 -0
  82. package/types/ngx-com.d.ts +10 -7
@@ -0,0 +1,625 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, ChangeDetectionStrategy, Component, inject, ElementRef, ViewContainerRef, Injector, DestroyRef, Renderer2, PLATFORM_ID, input, numberAttribute, booleanAttribute, output, Directive } from '@angular/core';
3
+ import { NgTemplateOutlet, DOCUMENT, isPlatformBrowser } from '@angular/common';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import { Overlay } from '@angular/cdk/overlay';
6
+ import { ComponentPortal } from '@angular/cdk/portal';
7
+ import { cva } from 'class-variance-authority';
8
+ import { mergeClasses } from 'ngx-com/utils';
9
+
10
+ /**
11
+ * CVA variants for the tooltip panel wrapper.
12
+ *
13
+ * @tokens `--color-tooltip`, `--color-tooltip-foreground`, `--color-primary`, `--color-primary-foreground`,
14
+ * `--color-accent`, `--color-accent-foreground`, `--color-warn`, `--color-warn-foreground`,
15
+ * `--color-popover`, `--color-popover-foreground`, `--color-border`
16
+ */
17
+ const tooltipPanelVariants = cva([
18
+ 'com-tooltip-panel',
19
+ 'z-50',
20
+ 'pointer-events-auto',
21
+ 'select-none',
22
+ 'leading-normal',
23
+ ], {
24
+ variants: {
25
+ color: {
26
+ default: 'bg-tooltip text-tooltip-foreground',
27
+ primary: 'bg-primary text-primary-foreground',
28
+ accent: 'bg-accent text-accent-foreground',
29
+ warn: 'bg-warn text-warn-foreground',
30
+ invert: 'bg-popover text-popover-foreground border border-border shadow-md',
31
+ },
32
+ size: {
33
+ sm: 'px-2 py-1 text-xs max-w-48 rounded-md',
34
+ md: 'px-3 py-1.5 text-sm max-w-64 rounded-lg',
35
+ lg: 'px-4 py-2.5 text-sm max-w-80 rounded-xl',
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ color: 'default',
40
+ size: 'md',
41
+ },
42
+ });
43
+ /**
44
+ * CVA variants for the tooltip arrow element.
45
+ * The arrow points toward the trigger element.
46
+ *
47
+ * @tokens `--color-tooltip`, `--color-primary`, `--color-accent`, `--color-warn`,
48
+ * `--color-popover`, `--color-border`
49
+ */
50
+ const tooltipArrowVariants = cva('absolute size-2 rotate-45', {
51
+ variants: {
52
+ color: {
53
+ default: 'bg-tooltip',
54
+ primary: 'bg-primary',
55
+ accent: 'bg-accent',
56
+ warn: 'bg-warn',
57
+ invert: 'bg-popover border-l border-t border-border',
58
+ },
59
+ side: {
60
+ // Arrow points DOWN (tooltip is above trigger)
61
+ top: 'left-1/2 -translate-x-1/2 bottom-0 translate-y-1/2',
62
+ // Arrow points UP (tooltip is below trigger)
63
+ bottom: 'left-1/2 -translate-x-1/2 top-0 -translate-y-1/2',
64
+ // Arrow points RIGHT (tooltip is left of trigger)
65
+ left: 'top-1/2 -translate-y-1/2 right-0 translate-x-1/2',
66
+ // Arrow points LEFT (tooltip is right of trigger)
67
+ right: 'top-1/2 -translate-y-1/2 left-0 -translate-x-1/2',
68
+ },
69
+ },
70
+ compoundVariants: [
71
+ // Invert arrow needs different rotation for border visibility
72
+ { color: 'invert', side: 'top', class: 'rotate-[225deg]' },
73
+ { color: 'invert', side: 'bottom', class: 'rotate-45' },
74
+ { color: 'invert', side: 'left', class: 'rotate-[135deg]' },
75
+ { color: 'invert', side: 'right', class: '-rotate-45' },
76
+ ],
77
+ defaultVariants: {
78
+ color: 'default',
79
+ side: 'top',
80
+ },
81
+ });
82
+
83
+ let tooltipIdCounter = 0;
84
+ /**
85
+ * Generate a unique ID for a tooltip instance.
86
+ */
87
+ function generateTooltipId() {
88
+ return `tooltip-${++tooltipIdCounter}`;
89
+ }
90
+
91
+ /**
92
+ * Internal tooltip panel component rendered inside the CDK overlay.
93
+ * Receives content (string or template) and variant inputs from the directive.
94
+ *
95
+ * @internal Not exported in public API
96
+ *
97
+ * @tokens `--color-tooltip`, `--color-tooltip-foreground`, `--color-primary`, `--color-primary-foreground`,
98
+ * `--color-accent`, `--color-accent-foreground`, `--color-warn`, `--color-warn-foreground`,
99
+ * `--color-popover`, `--color-popover-foreground`, `--color-border`
100
+ */
101
+ class TooltipPanelComponent {
102
+ /** Plain text content to display. */
103
+ textContent = signal('', ...(ngDevMode ? [{ debugName: "textContent" }] : []));
104
+ /** Optional template for rich content. */
105
+ templateRef = signal(null, ...(ngDevMode ? [{ debugName: "templateRef" }] : []));
106
+ /** Color variant. */
107
+ color = signal('default', ...(ngDevMode ? [{ debugName: "color" }] : []));
108
+ /** Size variant. */
109
+ size = signal('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
110
+ /** Whether to show the arrow. */
111
+ showArrow = signal(true, ...(ngDevMode ? [{ debugName: "showArrow" }] : []));
112
+ /** Which side the tooltip is positioned on. */
113
+ activeSide = signal('top', ...(ngDevMode ? [{ debugName: "activeSide" }] : []));
114
+ /** Unique ID for accessibility. */
115
+ tooltipId = signal('', ...(ngDevMode ? [{ debugName: "tooltipId" }] : []));
116
+ /** Whether the tooltip is visible (for animation state). */
117
+ visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
118
+ /** Function to hide the tooltip (passed to template context). */
119
+ hideFn = signal(() => { }, ...(ngDevMode ? [{ debugName: "hideFn" }] : []));
120
+ /** Computed template context. */
121
+ templateContext = computed(() => ({
122
+ $implicit: this.textContent(),
123
+ hide: this.hideFn(),
124
+ }), ...(ngDevMode ? [{ debugName: "templateContext" }] : []));
125
+ /** Computed panel CSS classes. */
126
+ panelClasses = computed(() => mergeClasses(tooltipPanelVariants({
127
+ color: this.color(),
128
+ size: this.size(),
129
+ })), ...(ngDevMode ? [{ debugName: "panelClasses" }] : []));
130
+ /** Computed arrow CSS classes. */
131
+ arrowClasses = computed(() => mergeClasses(tooltipArrowVariants({
132
+ color: this.color(),
133
+ side: this.activeSide(),
134
+ })), ...(ngDevMode ? [{ debugName: "arrowClasses" }] : []));
135
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TooltipPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
136
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: TooltipPanelComponent, isStandalone: true, selector: "com-tooltip-panel", ngImport: i0, template: `
137
+ <div
138
+ class="relative"
139
+ [attr.data-state]="visible() ? 'visible' : 'hidden'"
140
+ [attr.data-side]="activeSide()"
141
+ >
142
+ @if (showArrow()) {
143
+ <span [class]="arrowClasses()" aria-hidden="true"></span>
144
+ }
145
+ <div
146
+ [class]="panelClasses()"
147
+ role="tooltip"
148
+ [attr.id]="tooltipId()"
149
+ >
150
+ @if (templateRef()) {
151
+ <ng-container
152
+ [ngTemplateOutlet]="templateRef()!"
153
+ [ngTemplateOutletContext]="templateContext()"
154
+ />
155
+ } @else {
156
+ {{ textContent() }}
157
+ }
158
+ </div>
159
+ </div>
160
+ `, isInline: true, styles: [":host{display:contents}[data-state=visible]{animation:tooltip-in .12s ease-out}[data-state=hidden]{animation:tooltip-out 80ms ease-in forwards}[data-side=top]{transform-origin:bottom center}[data-side=bottom]{transform-origin:top center}[data-side=left]{transform-origin:right center}[data-side=right]{transform-origin:left center}@keyframes tooltip-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes tooltip-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@media(prefers-reduced-motion:reduce){[data-state=visible],[data-state=hidden]{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
161
+ }
162
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TooltipPanelComponent, decorators: [{
163
+ type: Component,
164
+ args: [{ selector: 'com-tooltip-panel', template: `
165
+ <div
166
+ class="relative"
167
+ [attr.data-state]="visible() ? 'visible' : 'hidden'"
168
+ [attr.data-side]="activeSide()"
169
+ >
170
+ @if (showArrow()) {
171
+ <span [class]="arrowClasses()" aria-hidden="true"></span>
172
+ }
173
+ <div
174
+ [class]="panelClasses()"
175
+ role="tooltip"
176
+ [attr.id]="tooltipId()"
177
+ >
178
+ @if (templateRef()) {
179
+ <ng-container
180
+ [ngTemplateOutlet]="templateRef()!"
181
+ [ngTemplateOutletContext]="templateContext()"
182
+ />
183
+ } @else {
184
+ {{ textContent() }}
185
+ }
186
+ </div>
187
+ </div>
188
+ `, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:contents}[data-state=visible]{animation:tooltip-in .12s ease-out}[data-state=hidden]{animation:tooltip-out 80ms ease-in forwards}[data-side=top]{transform-origin:bottom center}[data-side=bottom]{transform-origin:top center}[data-side=left]{transform-origin:right center}[data-side=right]{transform-origin:left center}@keyframes tooltip-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes tooltip-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@media(prefers-reduced-motion:reduce){[data-state=visible],[data-state=hidden]{animation:none}}\n"] }]
189
+ }] });
190
+
191
+ /** Default show delay in milliseconds. */
192
+ const DEFAULT_SHOW_DELAY = 200;
193
+ /** Default hide delay in milliseconds. */
194
+ const DEFAULT_HIDE_DELAY = 100;
195
+ /** Offset between trigger and tooltip in pixels. */
196
+ const TOOLTIP_OFFSET = 8;
197
+ /** Viewport margin in pixels. */
198
+ const VIEWPORT_MARGIN = 8;
199
+ /**
200
+ * Build position strategy entries for the tooltip.
201
+ * Primary position is the preferred direction, with fallbacks.
202
+ */
203
+ function buildTooltipPositions(position) {
204
+ const positions = {
205
+ top: [
206
+ // Top center (preferred)
207
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_OFFSET },
208
+ // Bottom center (flip)
209
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_OFFSET },
210
+ // Right center (fallback)
211
+ { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_OFFSET },
212
+ // Left center (fallback)
213
+ { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_OFFSET },
214
+ ],
215
+ bottom: [
216
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_OFFSET },
217
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_OFFSET },
218
+ { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_OFFSET },
219
+ { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_OFFSET },
220
+ ],
221
+ left: [
222
+ { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_OFFSET },
223
+ { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_OFFSET },
224
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_OFFSET },
225
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_OFFSET },
226
+ ],
227
+ right: [
228
+ { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: TOOLTIP_OFFSET },
229
+ { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -TOOLTIP_OFFSET },
230
+ { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -TOOLTIP_OFFSET },
231
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: TOOLTIP_OFFSET },
232
+ ],
233
+ };
234
+ return positions[position];
235
+ }
236
+ /**
237
+ * Derive the tooltip side from a position change event.
238
+ */
239
+ function deriveSideFromPosition(pair) {
240
+ if (pair.originY === 'top' && pair.overlayY === 'bottom')
241
+ return 'top';
242
+ if (pair.originY === 'bottom' && pair.overlayY === 'top')
243
+ return 'bottom';
244
+ if (pair.originX === 'start' && pair.overlayX === 'end')
245
+ return 'left';
246
+ if (pair.originX === 'end' && pair.overlayX === 'start')
247
+ return 'right';
248
+ return 'top';
249
+ }
250
+ /**
251
+ * Tooltip directive — displays supplementary information on hover/focus.
252
+ *
253
+ * Applied as an attribute directive to any trigger element. The tooltip panel
254
+ * is rendered via CDK Overlay when triggered by mouse hover, keyboard focus,
255
+ * or programmatically.
256
+ *
257
+ * @tokens `--color-tooltip`, `--color-tooltip-foreground`, `--color-primary`, `--color-primary-foreground`,
258
+ * `--color-accent`, `--color-accent-foreground`, `--color-warn`, `--color-warn-foreground`,
259
+ * `--color-popover`, `--color-popover-foreground`, `--color-border`
260
+ *
261
+ * @example Simple text tooltip
262
+ * ```html
263
+ * <button comTooltip="Save your changes">
264
+ * <com-icon name="save" />
265
+ * </button>
266
+ * ```
267
+ *
268
+ * @example Positioned
269
+ * ```html
270
+ * <button comTooltip="Settings" comTooltipPosition="right">
271
+ * <com-icon name="settings" />
272
+ * </button>
273
+ * ```
274
+ *
275
+ * @example Color variants
276
+ * ```html
277
+ * <com-icon name="alert-triangle" comTooltip="3 warnings found" comTooltipColor="warn" />
278
+ * ```
279
+ *
280
+ * @example Custom template
281
+ * ```html
282
+ * <button
283
+ * comTooltip="Keyboard shortcuts"
284
+ * [comTooltipTpl]="shortcutsTpl"
285
+ * comTooltipSize="lg"
286
+ * >
287
+ * <com-icon name="keyboard" />
288
+ * </button>
289
+ *
290
+ * <ng-template #shortcutsTpl let-hide="hide">
291
+ * <div class="flex flex-col gap-1">
292
+ * <span>Press Ctrl+S to save</span>
293
+ * <button (click)="hide()">Got it</button>
294
+ * </div>
295
+ * </ng-template>
296
+ * ```
297
+ *
298
+ * @example Programmatic control
299
+ * ```html
300
+ * <input
301
+ * #tooltipRef="comTooltip"
302
+ * comTooltip="Invalid email"
303
+ * comTooltipColor="warn"
304
+ * [comTooltipDisabled]="true"
305
+ * />
306
+ * ```
307
+ *
308
+ * ```ts
309
+ * // Show tooltip on validation error
310
+ * if (emailInvalid) {
311
+ * this.tooltipRef.show();
312
+ * }
313
+ * ```
314
+ */
315
+ class ComTooltip {
316
+ overlay = inject(Overlay);
317
+ elementRef = inject(ElementRef);
318
+ viewContainerRef = inject(ViewContainerRef);
319
+ injector = inject(Injector);
320
+ destroyRef = inject(DestroyRef);
321
+ renderer = inject(Renderer2);
322
+ platformId = inject(PLATFORM_ID);
323
+ document = inject(DOCUMENT);
324
+ overlayRef = null;
325
+ panelInstance = null;
326
+ showTimeoutId = null;
327
+ hideTimeoutId = null;
328
+ touchTimeoutId = null;
329
+ tooltipId = generateTooltipId();
330
+ /** Current active side (updated from position changes). */
331
+ activeSide = signal('top', ...(ngDevMode ? [{ debugName: "activeSide" }] : []));
332
+ /** Whether the tooltip is currently visible. */
333
+ isVisible = signal(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
334
+ // ─── Content Inputs ───
335
+ /** Simple text content for the tooltip. Also serves as the directive selector. */
336
+ comTooltip = input.required(...(ngDevMode ? [{ debugName: "comTooltip" }] : []));
337
+ /** Custom template for rich tooltip content. Takes precedence over text when provided. */
338
+ comTooltipTpl = input(null, ...(ngDevMode ? [{ debugName: "comTooltipTpl" }] : []));
339
+ // ─── Positioning Inputs ───
340
+ /** Preferred position direction. Default: 'top'. */
341
+ comTooltipPosition = input('top', ...(ngDevMode ? [{ debugName: "comTooltipPosition" }] : []));
342
+ // ─── Behavior Inputs ───
343
+ /** Delay in ms before showing the tooltip. Default: 200. */
344
+ comTooltipShowDelay = input(DEFAULT_SHOW_DELAY, { ...(ngDevMode ? { debugName: "comTooltipShowDelay" } : {}), transform: numberAttribute });
345
+ /** Delay in ms before hiding the tooltip. Default: 100. */
346
+ comTooltipHideDelay = input(DEFAULT_HIDE_DELAY, { ...(ngDevMode ? { debugName: "comTooltipHideDelay" } : {}), transform: numberAttribute });
347
+ /** Disable the tooltip entirely. Default: false. */
348
+ comTooltipDisabled = input(false, { ...(ngDevMode ? { debugName: "comTooltipDisabled" } : {}), transform: booleanAttribute });
349
+ /** Touch device handling. Default: 'auto'. */
350
+ comTooltipTouchGestures = input('auto', ...(ngDevMode ? [{ debugName: "comTooltipTouchGestures" }] : []));
351
+ // ─── Variant Inputs ───
352
+ /** Color variant. Default: 'default'. */
353
+ comTooltipColor = input('default', ...(ngDevMode ? [{ debugName: "comTooltipColor" }] : []));
354
+ /** Size variant. Default: 'md'. */
355
+ comTooltipSize = input('md', ...(ngDevMode ? [{ debugName: "comTooltipSize" }] : []));
356
+ /** Whether to show the arrow/caret. Default: true. */
357
+ comTooltipHasArrow = input(true, { ...(ngDevMode ? { debugName: "comTooltipHasArrow" } : {}), transform: booleanAttribute });
358
+ // ─── Outputs ───
359
+ /** Emitted when the tooltip becomes visible (after delay + animation). */
360
+ comTooltipShown = output();
361
+ /** Emitted when the tooltip is fully hidden. */
362
+ comTooltipHidden = output();
363
+ constructor() {
364
+ // Cleanup on destroy
365
+ this.destroyRef.onDestroy(() => {
366
+ this.clearTimers();
367
+ this.disposeOverlay();
368
+ });
369
+ }
370
+ // ─── Public API ───
371
+ /** Programmatically show the tooltip (ignores disabled state for error validation use cases). */
372
+ show() {
373
+ if (this.isVisible())
374
+ return;
375
+ this.clearTimers();
376
+ this.createOverlay();
377
+ this.attachPanel();
378
+ }
379
+ /** Programmatically hide the tooltip. */
380
+ hide() {
381
+ if (!this.isVisible())
382
+ return;
383
+ this.clearTimers();
384
+ this.detachPanel();
385
+ }
386
+ // ─── Event Handlers ───
387
+ onMouseEnter() {
388
+ if (this.comTooltipDisabled())
389
+ return;
390
+ this.scheduleShow(this.comTooltipShowDelay());
391
+ }
392
+ onMouseLeave() {
393
+ this.scheduleHide(this.comTooltipHideDelay());
394
+ }
395
+ onFocusIn() {
396
+ if (this.comTooltipDisabled())
397
+ return;
398
+ // Keyboard users get instant feedback
399
+ this.scheduleShow(0);
400
+ }
401
+ onFocusOut() {
402
+ this.scheduleHide(this.comTooltipHideDelay());
403
+ }
404
+ onEscapeKey() {
405
+ if (this.isVisible()) {
406
+ this.hide();
407
+ }
408
+ }
409
+ onTouchStart(event) {
410
+ const touchMode = this.comTooltipTouchGestures();
411
+ if (touchMode === 'off' || this.comTooltipDisabled())
412
+ return;
413
+ // Long-press to show (500ms)
414
+ this.clearTimers();
415
+ this.touchTimeoutId = setTimeout(() => {
416
+ this.show();
417
+ // Set up tap-elsewhere to hide
418
+ const touchEndHandler = (e) => {
419
+ const touch = e.changedTouches?.[0];
420
+ if (touch) {
421
+ const target = this.document.elementFromPoint(touch.clientX, touch.clientY);
422
+ if (!this.overlayRef?.overlayElement.contains(target)) {
423
+ this.hide();
424
+ }
425
+ }
426
+ this.document.removeEventListener('touchend', touchEndHandler);
427
+ };
428
+ this.document.addEventListener('touchend', touchEndHandler);
429
+ }, 500);
430
+ // Cancel long-press if finger moves
431
+ const touchMoveHandler = () => {
432
+ this.clearTouchTimeout();
433
+ this.document.removeEventListener('touchmove', touchMoveHandler);
434
+ };
435
+ this.document.addEventListener('touchmove', touchMoveHandler);
436
+ // Cancel long-press if finger lifts before timeout
437
+ const touchEndCancelHandler = () => {
438
+ this.clearTouchTimeout();
439
+ this.document.removeEventListener('touchend', touchEndCancelHandler);
440
+ };
441
+ this.document.addEventListener('touchend', touchEndCancelHandler);
442
+ }
443
+ // ─── Panel Mouse Events ───
444
+ /** Called when mouse enters the tooltip panel. */
445
+ onPanelMouseEnter() {
446
+ // Cancel hide timer when hovering over panel (for interactive templates)
447
+ this.clearHideTimeout();
448
+ }
449
+ /** Called when mouse leaves the tooltip panel. */
450
+ onPanelMouseLeave() {
451
+ this.scheduleHide(this.comTooltipHideDelay());
452
+ }
453
+ // ─── Timer Management ───
454
+ scheduleShow(delay) {
455
+ this.clearTimers();
456
+ if (delay === 0) {
457
+ this.createOverlay();
458
+ this.attachPanel();
459
+ }
460
+ else {
461
+ this.showTimeoutId = setTimeout(() => {
462
+ this.createOverlay();
463
+ this.attachPanel();
464
+ }, delay);
465
+ }
466
+ }
467
+ scheduleHide(delay) {
468
+ this.clearShowTimeout();
469
+ if (!this.isVisible())
470
+ return;
471
+ if (delay === 0) {
472
+ this.detachPanel();
473
+ }
474
+ else {
475
+ this.hideTimeoutId = setTimeout(() => {
476
+ this.detachPanel();
477
+ }, delay);
478
+ }
479
+ }
480
+ clearTimers() {
481
+ this.clearShowTimeout();
482
+ this.clearHideTimeout();
483
+ this.clearTouchTimeout();
484
+ }
485
+ clearShowTimeout() {
486
+ if (this.showTimeoutId !== null) {
487
+ clearTimeout(this.showTimeoutId);
488
+ this.showTimeoutId = null;
489
+ }
490
+ }
491
+ clearHideTimeout() {
492
+ if (this.hideTimeoutId !== null) {
493
+ clearTimeout(this.hideTimeoutId);
494
+ this.hideTimeoutId = null;
495
+ }
496
+ }
497
+ clearTouchTimeout() {
498
+ if (this.touchTimeoutId !== null) {
499
+ clearTimeout(this.touchTimeoutId);
500
+ this.touchTimeoutId = null;
501
+ }
502
+ }
503
+ // ─── Overlay Management ───
504
+ createOverlay() {
505
+ if (this.overlayRef)
506
+ return;
507
+ if (!isPlatformBrowser(this.platformId))
508
+ return;
509
+ const positionStrategy = this.overlay
510
+ .position()
511
+ .flexibleConnectedTo(this.elementRef)
512
+ .withPositions(buildTooltipPositions(this.comTooltipPosition()))
513
+ .withFlexibleDimensions(false)
514
+ .withPush(true)
515
+ .withViewportMargin(VIEWPORT_MARGIN);
516
+ const scrollStrategy = this.overlay.scrollStrategies.reposition({ autoClose: true });
517
+ this.overlayRef = this.overlay.create({
518
+ positionStrategy,
519
+ scrollStrategy,
520
+ panelClass: 'com-tooltip-overlay',
521
+ hasBackdrop: false,
522
+ });
523
+ // Track position changes for arrow orientation
524
+ positionStrategy.positionChanges
525
+ .pipe(takeUntilDestroyed(this.destroyRef))
526
+ .subscribe((change) => {
527
+ const side = deriveSideFromPosition(change.connectionPair);
528
+ this.activeSide.set(side);
529
+ if (this.panelInstance) {
530
+ this.panelInstance.activeSide.set(side);
531
+ }
532
+ });
533
+ // Close when overlay detaches
534
+ this.overlayRef
535
+ .detachments()
536
+ .pipe(takeUntilDestroyed(this.destroyRef))
537
+ .subscribe(() => {
538
+ this.handleDetachment();
539
+ });
540
+ }
541
+ attachPanel() {
542
+ if (!this.overlayRef || this.overlayRef.hasAttached())
543
+ return;
544
+ const portal = new ComponentPortal(TooltipPanelComponent, this.viewContainerRef, this.injector);
545
+ const componentRef = this.overlayRef.attach(portal);
546
+ this.panelInstance = componentRef.instance;
547
+ // Configure panel inputs
548
+ this.panelInstance.textContent.set(this.comTooltip());
549
+ this.panelInstance.templateRef.set(this.comTooltipTpl());
550
+ this.panelInstance.color.set(this.comTooltipColor());
551
+ this.panelInstance.size.set(this.comTooltipSize());
552
+ this.panelInstance.showArrow.set(this.comTooltipHasArrow());
553
+ this.panelInstance.activeSide.set(this.activeSide());
554
+ this.panelInstance.tooltipId.set(this.tooltipId);
555
+ this.panelInstance.hideFn.set(() => this.hide());
556
+ // Set up panel mouse events for interactive templates
557
+ const overlayElement = this.overlayRef.overlayElement;
558
+ this.renderer.listen(overlayElement, 'mouseenter', () => this.onPanelMouseEnter());
559
+ this.renderer.listen(overlayElement, 'mouseleave', () => this.onPanelMouseLeave());
560
+ // Set ARIA on trigger
561
+ this.renderer.setAttribute(this.elementRef.nativeElement, 'aria-describedby', this.tooltipId);
562
+ // Trigger show animation after a microtask (allows initial styles to apply)
563
+ requestAnimationFrame(() => {
564
+ if (this.panelInstance) {
565
+ this.panelInstance.visible.set(true);
566
+ }
567
+ });
568
+ this.isVisible.set(true);
569
+ this.comTooltipShown.emit();
570
+ }
571
+ detachPanel() {
572
+ if (!this.panelInstance)
573
+ return;
574
+ // Trigger hide animation
575
+ this.panelInstance.visible.set(false);
576
+ // Wait for animation to complete before detaching
577
+ setTimeout(() => {
578
+ this.disposeOverlay();
579
+ }, 80); // Match animation duration
580
+ }
581
+ handleDetachment() {
582
+ // Remove ARIA from trigger
583
+ this.renderer.removeAttribute(this.elementRef.nativeElement, 'aria-describedby');
584
+ this.isVisible.set(false);
585
+ this.panelInstance = null;
586
+ this.comTooltipHidden.emit();
587
+ }
588
+ disposeOverlay() {
589
+ if (this.overlayRef) {
590
+ this.overlayRef.dispose();
591
+ this.overlayRef = null;
592
+ }
593
+ this.panelInstance = null;
594
+ this.isVisible.set(false);
595
+ // Remove ARIA from trigger
596
+ this.renderer.removeAttribute(this.elementRef.nativeElement, 'aria-describedby');
597
+ }
598
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTooltip, deps: [], target: i0.ɵɵFactoryTarget.Directive });
599
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.0", type: ComTooltip, isStandalone: true, selector: "[comTooltip]", inputs: { comTooltip: { classPropertyName: "comTooltip", publicName: "comTooltip", isSignal: true, isRequired: true, transformFunction: null }, comTooltipTpl: { classPropertyName: "comTooltipTpl", publicName: "comTooltipTpl", isSignal: true, isRequired: false, transformFunction: null }, comTooltipPosition: { classPropertyName: "comTooltipPosition", publicName: "comTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, comTooltipShowDelay: { classPropertyName: "comTooltipShowDelay", publicName: "comTooltipShowDelay", isSignal: true, isRequired: false, transformFunction: null }, comTooltipHideDelay: { classPropertyName: "comTooltipHideDelay", publicName: "comTooltipHideDelay", isSignal: true, isRequired: false, transformFunction: null }, comTooltipDisabled: { classPropertyName: "comTooltipDisabled", publicName: "comTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, comTooltipTouchGestures: { classPropertyName: "comTooltipTouchGestures", publicName: "comTooltipTouchGestures", isSignal: true, isRequired: false, transformFunction: null }, comTooltipColor: { classPropertyName: "comTooltipColor", publicName: "comTooltipColor", isSignal: true, isRequired: false, transformFunction: null }, comTooltipSize: { classPropertyName: "comTooltipSize", publicName: "comTooltipSize", isSignal: true, isRequired: false, transformFunction: null }, comTooltipHasArrow: { classPropertyName: "comTooltipHasArrow", publicName: "comTooltipHasArrow", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { comTooltipShown: "comTooltipShown", comTooltipHidden: "comTooltipHidden" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocusIn()", "focusout": "onFocusOut()", "keydown.escape": "onEscapeKey()", "touchstart": "onTouchStart($event)" } }, exportAs: ["comTooltip"], ngImport: i0 });
600
+ }
601
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTooltip, decorators: [{
602
+ type: Directive,
603
+ args: [{
604
+ selector: '[comTooltip]',
605
+ exportAs: 'comTooltip',
606
+ host: {
607
+ '(mouseenter)': 'onMouseEnter()',
608
+ '(mouseleave)': 'onMouseLeave()',
609
+ '(focusin)': 'onFocusIn()',
610
+ '(focusout)': 'onFocusOut()',
611
+ '(keydown.escape)': 'onEscapeKey()',
612
+ '(touchstart)': 'onTouchStart($event)',
613
+ },
614
+ }]
615
+ }], ctorParameters: () => [], propDecorators: { comTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltip", required: true }] }], comTooltipTpl: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipTpl", required: false }] }], comTooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipPosition", required: false }] }], comTooltipShowDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipShowDelay", required: false }] }], comTooltipHideDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipHideDelay", required: false }] }], comTooltipDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipDisabled", required: false }] }], comTooltipTouchGestures: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipTouchGestures", required: false }] }], comTooltipColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipColor", required: false }] }], comTooltipSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipSize", required: false }] }], comTooltipHasArrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "comTooltipHasArrow", required: false }] }], comTooltipShown: [{ type: i0.Output, args: ["comTooltipShown"] }], comTooltipHidden: [{ type: i0.Output, args: ["comTooltipHidden"] }] } });
616
+
617
+ // Public API for the tooltip directive
618
+ // Main directive
619
+
620
+ /**
621
+ * Generated bundle index. Do not edit.
622
+ */
623
+
624
+ export { ComTooltip, tooltipArrowVariants, tooltipPanelVariants };
625
+ //# sourceMappingURL=ngx-com-components-tooltip.mjs.map