forty-cdk 0.2.0 → 0.3.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/accordion/README.md +122 -0
- package/aspect-ratio/README.md +76 -0
- package/avatar/README.md +100 -0
- package/breadcrumbs/README.md +49 -0
- package/breakpoints/README.md +81 -0
- package/button/README.md +49 -0
- package/calendar/README.md +458 -0
- package/carousel/README.md +358 -0
- package/checkbox/README.md +146 -0
- package/combobox/README.md +535 -0
- package/context-menu/README.md +139 -0
- package/date-field/README.md +184 -0
- package/date-picker/README.md +338 -0
- package/dialog/README.md +388 -0
- package/disclosure/README.md +114 -0
- package/drag-drop/README.md +359 -0
- package/drawer/README.md +560 -0
- package/dropdown-menu/README.md +176 -0
- package/fesm2022/forty-cdk-accordion.mjs +348 -0
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
- package/fesm2022/forty-cdk-avatar.mjs +308 -0
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
- package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
- package/fesm2022/forty-cdk-button.mjs +134 -0
- package/fesm2022/forty-cdk-button.mjs.map +1 -0
- package/fesm2022/forty-cdk-calendar.mjs +2034 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
- package/fesm2022/forty-cdk-carousel.mjs +968 -0
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
- package/fesm2022/forty-cdk-checkbox.mjs +226 -0
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-combobox.mjs +2596 -0
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
- package/fesm2022/forty-cdk-context-menu.mjs +413 -0
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-core.mjs +9022 -0
- package/fesm2022/forty-cdk-core.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-field.mjs +744 -0
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-dialog.mjs +707 -0
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
- package/fesm2022/forty-cdk-disclosure.mjs +190 -0
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
- package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/forty-cdk-drawer.mjs +1641 -0
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-field.mjs +425 -0
- package/fesm2022/forty-cdk-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-fieldset.mjs +164 -0
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
- package/fesm2022/forty-cdk-file-upload.mjs +221 -0
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
- package/fesm2022/forty-cdk-hover-card.mjs +496 -0
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
- package/fesm2022/forty-cdk-input.mjs +274 -0
- package/fesm2022/forty-cdk-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +1279 -0
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-menu.mjs +1439 -0
- package/fesm2022/forty-cdk-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-menubar.mjs +787 -0
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
- package/fesm2022/forty-cdk-meter.mjs +211 -0
- package/fesm2022/forty-cdk-meter.mjs.map +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-number-input.mjs +559 -0
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-otp-input.mjs +527 -0
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-pagination.mjs +323 -0
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
- package/fesm2022/forty-cdk-popover.mjs +698 -0
- package/fesm2022/forty-cdk-popover.mjs.map +1 -0
- package/fesm2022/forty-cdk-progress.mjs +226 -0
- package/fesm2022/forty-cdk-progress.mjs.map +1 -0
- package/fesm2022/forty-cdk-radio-group.mjs +378 -0
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
- package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
- package/fesm2022/forty-cdk-search.mjs +205 -0
- package/fesm2022/forty-cdk-search.mjs.map +1 -0
- package/fesm2022/forty-cdk-select.mjs +1661 -0
- package/fesm2022/forty-cdk-select.mjs.map +1 -0
- package/fesm2022/forty-cdk-separator.mjs +82 -0
- package/fesm2022/forty-cdk-separator.mjs.map +1 -0
- package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
- package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
- package/fesm2022/forty-cdk-slider.mjs +803 -0
- package/fesm2022/forty-cdk-slider.mjs.map +1 -0
- package/fesm2022/forty-cdk-stepper.mjs +886 -0
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
- package/fesm2022/forty-cdk-switch.mjs +137 -0
- package/fesm2022/forty-cdk-switch.mjs.map +1 -0
- package/fesm2022/forty-cdk-table.mjs +1518 -0
- package/fesm2022/forty-cdk-table.mjs.map +1 -0
- package/fesm2022/forty-cdk-tabs.mjs +400 -0
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-field.mjs +593 -0
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-toast.mjs +1153 -0
- package/fesm2022/forty-cdk-toast.mjs.map +1 -0
- package/fesm2022/forty-cdk-toggle.mjs +516 -0
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
- package/fesm2022/forty-cdk-toolbar.mjs +374 -0
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
- package/fesm2022/forty-cdk-tooltip.mjs +672 -0
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
- package/fesm2022/forty-cdk-tree.mjs +2007 -0
- package/fesm2022/forty-cdk-tree.mjs.map +1 -0
- package/fesm2022/forty-cdk-virtualization.mjs +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk.mjs +0 -43310
- package/fesm2022/forty-cdk.mjs.map +1 -1
- package/field/README.md +97 -0
- package/fieldset/README.md +86 -0
- package/file-upload/README.md +73 -0
- package/hover-card/README.md +171 -0
- package/input/README.md +156 -0
- package/listbox/README.md +424 -0
- package/menu/README.md +181 -0
- package/menubar/README.md +140 -0
- package/meter/README.md +128 -0
- package/navigation-menu/README.md +253 -0
- package/number-input/README.md +171 -0
- package/otp-input/README.md +198 -0
- package/package.json +213 -1
- package/pagination/README.md +61 -0
- package/pane-resizer/README.md +136 -0
- package/popover/README.md +262 -0
- package/progress/README.md +115 -0
- package/radio-group/README.md +129 -0
- package/scroll-area/README.md +184 -0
- package/search/README.md +42 -0
- package/select/README.md +488 -0
- package/separator/README.md +84 -0
- package/signal-forms/README.md +72 -0
- package/slider/README.md +152 -0
- package/stepper/README.md +292 -0
- package/switch/README.md +116 -0
- package/table/README.md +769 -0
- package/tabs/README.md +130 -0
- package/time-field/README.md +157 -0
- package/time-picker/README.md +172 -0
- package/toast/README.md +398 -0
- package/toggle/README.md +224 -0
- package/toolbar/README.md +109 -0
- package/tooltip/README.md +274 -0
- package/tree/README.md +708 -0
- package/types/forty-cdk-accordion.d.ts +242 -0
- package/types/forty-cdk-aspect-ratio.d.ts +59 -0
- package/types/forty-cdk-avatar.d.ts +133 -0
- package/types/forty-cdk-breadcrumbs.d.ts +92 -0
- package/types/forty-cdk-breakpoints.d.ts +141 -0
- package/types/forty-cdk-button.d.ts +80 -0
- package/types/forty-cdk-calendar.d.ts +914 -0
- package/types/forty-cdk-carousel.d.ts +530 -0
- package/types/forty-cdk-checkbox.d.ts +141 -0
- package/types/forty-cdk-combobox.d.ts +1259 -0
- package/types/forty-cdk-context-menu.d.ts +313 -0
- package/types/forty-cdk-core.d.ts +5774 -0
- package/types/forty-cdk-date-field.d.ts +307 -0
- package/types/forty-cdk-date-picker.d.ts +622 -0
- package/types/forty-cdk-dialog.d.ts +546 -0
- package/types/forty-cdk-disclosure.d.ts +127 -0
- package/types/forty-cdk-drag-drop.d.ts +456 -0
- package/types/forty-cdk-drawer.d.ts +871 -0
- package/types/forty-cdk-dropdown-menu.d.ts +242 -0
- package/types/forty-cdk-field.d.ts +236 -0
- package/types/forty-cdk-fieldset.d.ts +119 -0
- package/types/forty-cdk-file-upload.d.ts +124 -0
- package/types/forty-cdk-hover-card.d.ts +320 -0
- package/types/forty-cdk-input.d.ts +169 -0
- package/types/forty-cdk-internationalized-date.d.ts +1 -1
- package/types/forty-cdk-listbox.d.ts +513 -0
- package/types/forty-cdk-menu.d.ts +629 -0
- package/types/forty-cdk-menubar.d.ts +451 -0
- package/types/forty-cdk-meter.d.ts +122 -0
- package/types/forty-cdk-navigation-menu.d.ts +514 -0
- package/types/forty-cdk-number-input.d.ts +319 -0
- package/types/forty-cdk-otp-input.d.ts +248 -0
- package/types/forty-cdk-pagination.d.ts +214 -0
- package/types/forty-cdk-pane-resizer.d.ts +145 -0
- package/types/forty-cdk-popover.d.ts +509 -0
- package/types/forty-cdk-progress.d.ts +143 -0
- package/types/forty-cdk-radio-group.d.ts +222 -0
- package/types/forty-cdk-scroll-area.d.ts +258 -0
- package/types/forty-cdk-search.d.ts +142 -0
- package/types/forty-cdk-select.d.ts +899 -0
- package/types/forty-cdk-separator.d.ts +59 -0
- package/types/forty-cdk-signal-forms.d.ts +58 -0
- package/types/forty-cdk-slider.d.ts +379 -0
- package/types/forty-cdk-stepper.d.ts +650 -0
- package/types/forty-cdk-switch.d.ts +87 -0
- package/types/forty-cdk-table.d.ts +723 -0
- package/types/forty-cdk-tabs.d.ts +235 -0
- package/types/forty-cdk-time-field.d.ts +307 -0
- package/types/forty-cdk-time-picker.d.ts +578 -0
- package/types/forty-cdk-toast.d.ts +598 -0
- package/types/forty-cdk-toggle.d.ts +310 -0
- package/types/forty-cdk-toolbar.d.ts +217 -0
- package/types/forty-cdk-tooltip.d.ts +436 -0
- package/types/forty-cdk-tree.d.ts +688 -0
- package/types/forty-cdk.d.ts +1 -19743
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, computed, Injectable, model, input, numberAttribute, booleanAttribute, signal, DestroyRef, Directive, ElementRef, effect } from '@angular/core';
|
|
3
|
+
import { createDefaults, SkipDelayCoordinator, IdGenerator, ANCHORED_POSITIONING_DEFAULTS, injectPrefersReducedMotion, forceCloseWhenDisabled, createHoverIntent, adoptHostId, resolveGraceSide, buildSubmenuGracePolygon, attachPointerGrace, injectOverlayShell, toFloatingPositioner, registerHandle } from 'forty-cdk/core';
|
|
4
|
+
|
|
5
|
+
const FOR_TOOLTIP_CONTEXT = new InjectionToken('FOR_TOOLTIP_CONTEXT');
|
|
6
|
+
function injectTooltipContext(piece) {
|
|
7
|
+
const ctx = inject(FOR_TOOLTIP_CONTEXT, { optional: true });
|
|
8
|
+
if (!ctx) {
|
|
9
|
+
throw new Error(`[forty-cdk/tooltip] ${piece} must be used inside a [forTooltip] element. ` +
|
|
10
|
+
"If it is declared inside an ng-template, DI resolves at the template's declaration site — " +
|
|
11
|
+
'not where it is stamped (e.g. via ngTemplateOutlet) — so declare the template inside the ' +
|
|
12
|
+
'[forTooltip] root.');
|
|
13
|
+
}
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the trigger's root context: the explicit reference when the
|
|
18
|
+
* `[forTooltipTrigger]` input carries one, the injected `FOR_TOOLTIP_CONTEXT`
|
|
19
|
+
* otherwise. The orphan error only fires when neither resolves, on first read
|
|
20
|
+
* of the returned signal. Must be called in an injection context.
|
|
21
|
+
*/
|
|
22
|
+
function injectTooltipTriggerContext(explicitRoot) {
|
|
23
|
+
const injected = inject(FOR_TOOLTIP_CONTEXT, { optional: true });
|
|
24
|
+
return computed(() => {
|
|
25
|
+
const explicit = explicitRoot();
|
|
26
|
+
if (explicit !== '') {
|
|
27
|
+
return explicit;
|
|
28
|
+
}
|
|
29
|
+
if (injected) {
|
|
30
|
+
return injected;
|
|
31
|
+
}
|
|
32
|
+
throw new Error('[forty-cdk/tooltip] ForTooltipTrigger could not resolve its [forTooltip] root: ' +
|
|
33
|
+
'no FOR_TOOLTIP_CONTEXT provider is visible and no explicit root reference was passed. ' +
|
|
34
|
+
"If this trigger is declared inside an ng-template, DI resolves at the template's declaration " +
|
|
35
|
+
'site — not where it is stamped — so either declare the template inside the root or pass the ' +
|
|
36
|
+
'root explicitly: [forTooltipTrigger]="root" with #root="forTooltip".');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Library fallback for tooltip defaults, read at the root injector when no
|
|
42
|
+
* consumer has called `provideForTooltipDefaults`. Exported for the shared
|
|
43
|
+
* defaults contract spec; not re-exported from the primitive's public entry.
|
|
44
|
+
*/
|
|
45
|
+
const FOR_TOOLTIP_FALLBACK_DEFAULTS = {
|
|
46
|
+
openDelay: 700,
|
|
47
|
+
closeDelay: 300,
|
|
48
|
+
skipDelayDuration: 300,
|
|
49
|
+
side: 'top',
|
|
50
|
+
align: 'center',
|
|
51
|
+
sideOffset: 8,
|
|
52
|
+
collisionPadding: 8,
|
|
53
|
+
showOnOverflow: false,
|
|
54
|
+
hoverableContent: false,
|
|
55
|
+
};
|
|
56
|
+
const { token, provideDefaults } = createDefaults('FOR_TOOLTIP_DEFAULTS', FOR_TOOLTIP_FALLBACK_DEFAULTS);
|
|
57
|
+
/** Token holding the resolved tooltip defaults for the current scope. */
|
|
58
|
+
const FOR_TOOLTIP_DEFAULTS = token;
|
|
59
|
+
/**
|
|
60
|
+
* Per-injector-scope state owned by forty-cdk tooltip. Thin subclass of the
|
|
61
|
+
* shared `SkipDelayCoordinator` bound to this primitive's own DI token, so
|
|
62
|
+
* each call to `provideForTooltipDefaults` re-provides it and the
|
|
63
|
+
* corresponding subtree gets its own skip-delay window, independent from any
|
|
64
|
+
* hover-card scope. Tooltips inject it on construction.
|
|
65
|
+
*/
|
|
66
|
+
class TooltipCoordinator extends SkipDelayCoordinator {
|
|
67
|
+
constructor() {
|
|
68
|
+
super(inject(FOR_TOOLTIP_DEFAULTS));
|
|
69
|
+
}
|
|
70
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TooltipCoordinator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
71
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TooltipCoordinator, providedIn: 'root' });
|
|
72
|
+
}
|
|
73
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TooltipCoordinator, decorators: [{
|
|
74
|
+
type: Injectable,
|
|
75
|
+
args: [{ providedIn: 'root' }]
|
|
76
|
+
}], ctorParameters: () => [] });
|
|
77
|
+
/**
|
|
78
|
+
* Configures forty-cdk tooltip defaults for this injector scope.
|
|
79
|
+
* Partial overrides inherit unspecified keys from the parent scope (or
|
|
80
|
+
* library defaults at the root). Each call establishes a new
|
|
81
|
+
* coordinator scope: peer tooltips inside the scope share a skip-delay
|
|
82
|
+
* window; tooltips in other scopes don't.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* // application-level
|
|
87
|
+
* bootstrapApplication(App, {
|
|
88
|
+
* providers: [provideForTooltipDefaults({ openDelay: 500 })],
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* // component-level override (e.g. a toolbar with its own cadence)
|
|
92
|
+
* @Component({
|
|
93
|
+
* providers: [provideForTooltipDefaults({ skipDelayDuration: 100 })],
|
|
94
|
+
* ...
|
|
95
|
+
* })
|
|
96
|
+
* class Toolbar {}
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
function provideForTooltipDefaults(defaults = {}) {
|
|
100
|
+
return [...provideDefaults(defaults), TooltipCoordinator];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Headless implementation of the [WAI-ARIA Tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/).
|
|
105
|
+
*
|
|
106
|
+
* Wrapper directive that owns open / closed state, hover / focus delays, and
|
|
107
|
+
* placement. Provides the shared context to `ForTooltipTrigger`,
|
|
108
|
+
* `ForTooltipContent`, and the optional `ForTooltipArrow`.
|
|
109
|
+
*
|
|
110
|
+
* Tooltip content is portaled to `document.body` and positioned via
|
|
111
|
+
* `@floating-ui/dom`. Per APG, content must NOT be interactive — for
|
|
112
|
+
* interactive popups use a Popover primitive (not yet shipped).
|
|
113
|
+
*/
|
|
114
|
+
class ForTooltip {
|
|
115
|
+
#idGen = inject(IdGenerator);
|
|
116
|
+
#defaults = inject(FOR_TOOLTIP_DEFAULTS);
|
|
117
|
+
/**
|
|
118
|
+
* Two-way bindable. Whether the tooltip is currently shown. The `model()`
|
|
119
|
+
* change emitter (`(openChange)`) fires only on internal transitions
|
|
120
|
+
* (hover/focus delays, Escape, and the force-close that runs when `disabled`
|
|
121
|
+
* flips to true), never on consumer writes through `[(open)]` — observe
|
|
122
|
+
* state changes without binding back.
|
|
123
|
+
*/
|
|
124
|
+
open = model(false, /* @ts-ignore */
|
|
125
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
126
|
+
/**
|
|
127
|
+
* Per-tooltip override for the side the tooltip is anchored to. Pair with
|
|
128
|
+
* `align` for the full positioning API. When `undefined` (default), falls
|
|
129
|
+
* back to `ForTooltipDefaults.side` from the surrounding
|
|
130
|
+
* `provideForTooltipDefaults` scope (`'top'` unless configured).
|
|
131
|
+
*
|
|
132
|
+
* The input is aliased to `side`; consumers bind `[side]="..."` and read
|
|
133
|
+
* the effective value via the public `side` computed below.
|
|
134
|
+
*/
|
|
135
|
+
_sideInput = input(undefined, { ...(ngDevMode ? { debugName: "_sideInput" } : /* istanbul ignore next */ {}), alias: 'side' });
|
|
136
|
+
/** Effective anchor side: the `side` input when set, else the scope default. */
|
|
137
|
+
side = computed(() => this._sideInput() ?? this.#defaults.side, /* @ts-ignore */
|
|
138
|
+
...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
139
|
+
/**
|
|
140
|
+
* Per-tooltip override for the alignment along the chosen `side`. When
|
|
141
|
+
* `undefined` (default), falls back to `ForTooltipDefaults.align` from the
|
|
142
|
+
* surrounding `provideForTooltipDefaults` scope (`'center'` unless
|
|
143
|
+
* configured).
|
|
144
|
+
*
|
|
145
|
+
* The input is aliased to `align`; consumers bind `[align]="..."` and read
|
|
146
|
+
* the effective value via the public `align` computed below.
|
|
147
|
+
*/
|
|
148
|
+
_alignInput = input(undefined, { ...(ngDevMode ? { debugName: "_alignInput" } : /* istanbul ignore next */ {}), alias: 'align' });
|
|
149
|
+
/** Effective alignment: the `align` input when set, else the scope default. */
|
|
150
|
+
align = computed(() => this._alignInput() ?? this.#defaults.align, /* @ts-ignore */
|
|
151
|
+
...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
152
|
+
/**
|
|
153
|
+
* Per-tooltip override for the gap (px) between trigger and content along
|
|
154
|
+
* the main axis. When `undefined` (default),
|
|
155
|
+
* falls back to `ForTooltipDefaults.sideOffset` from the surrounding
|
|
156
|
+
* `provideForTooltipDefaults` scope (`8` unless configured).
|
|
157
|
+
*
|
|
158
|
+
* The input is aliased to `sideOffset`; consumers bind `[sideOffset]="..."`
|
|
159
|
+
* and read the effective value via the public `sideOffset` computed below.
|
|
160
|
+
*/
|
|
161
|
+
_sideOffsetInput = input(undefined, { ...(ngDevMode ? { debugName: "_sideOffsetInput" } : /* istanbul ignore next */ {}), alias: 'sideOffset',
|
|
162
|
+
transform: (v) => (v == null ? undefined : numberAttribute(v)) });
|
|
163
|
+
/** Effective main-axis gap (px): the `sideOffset` input when set, else the scope default. */
|
|
164
|
+
sideOffset = computed(() => this._sideOffsetInput() ?? this.#defaults.sideOffset, /* @ts-ignore */
|
|
165
|
+
...(ngDevMode ? [{ debugName: "sideOffset" }] : /* istanbul ignore next */ []));
|
|
166
|
+
/** Gap (px) along the cross axis (parallel to `side`). Default `0`. */
|
|
167
|
+
alignOffset = input(ANCHORED_POSITIONING_DEFAULTS.alignOffset, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
168
|
+
/**
|
|
169
|
+
* When `true` (default), `flip` and `shift` keep the tooltip inside the
|
|
170
|
+
* viewport. Disable for strict positioning where overflow is acceptable.
|
|
171
|
+
*/
|
|
172
|
+
avoidCollisions = input(ANCHORED_POSITIONING_DEFAULTS.avoidCollisions, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
173
|
+
/**
|
|
174
|
+
* Per-tooltip override for the padding (px) applied uniformly to the
|
|
175
|
+
* `flip`, `shift`, and `size` middlewares. When `undefined` (default),
|
|
176
|
+
* falls back to `ForTooltipDefaults.collisionPadding` from the surrounding
|
|
177
|
+
* `provideForTooltipDefaults` scope (`8` unless configured).
|
|
178
|
+
*
|
|
179
|
+
* The input is aliased to `collisionPadding`; consumers bind
|
|
180
|
+
* `[collisionPadding]="..."` and read the effective value via the public
|
|
181
|
+
* `collisionPadding` computed below.
|
|
182
|
+
*/
|
|
183
|
+
_collisionPaddingInput = input(undefined, { ...(ngDevMode ? { debugName: "_collisionPaddingInput" } : /* istanbul ignore next */ {}), alias: 'collisionPadding',
|
|
184
|
+
transform: (v) => (v == null ? undefined : numberAttribute(v)) });
|
|
185
|
+
/** Effective collision padding (px): the `collisionPadding` input when set, else the scope default. */
|
|
186
|
+
collisionPadding = computed(() => this._collisionPaddingInput() ?? this.#defaults.collisionPadding, /* @ts-ignore */
|
|
187
|
+
...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
|
|
188
|
+
/** Padding (px) for the `arrow` middleware. Default `0`. */
|
|
189
|
+
arrowPadding = input(ANCHORED_POSITIONING_DEFAULTS.arrowPadding, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
190
|
+
/**
|
|
191
|
+
* Stickiness behaviour for `shift`. `'partial'` (default) lets the
|
|
192
|
+
* tooltip shift to stay visible. `'always'` keeps the requested
|
|
193
|
+
* placement even off-screen.
|
|
194
|
+
*/
|
|
195
|
+
sticky = input(ANCHORED_POSITIONING_DEFAULTS.sticky, /* @ts-ignore */
|
|
196
|
+
...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
|
|
197
|
+
/**
|
|
198
|
+
* When `true`, sets `data-detached=""` while the trigger has scrolled
|
|
199
|
+
* off all clipping ancestors.
|
|
200
|
+
*/
|
|
201
|
+
hideWhenDetached = input(ANCHORED_POSITIONING_DEFAULTS.hideWhenDetached, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
202
|
+
/**
|
|
203
|
+
* When `true` (default), the content is clipped until floating-ui resolves
|
|
204
|
+
* its first position, preventing a flash at the viewport corner. Set to
|
|
205
|
+
* `false` so a dramatic `animate.enter` plays from its first frame (the
|
|
206
|
+
* surface may flash briefly at the unresolved position while positioning
|
|
207
|
+
* computes).
|
|
208
|
+
*/
|
|
209
|
+
clipUntilPositioned = input(ANCHORED_POSITIONING_DEFAULTS.clipUntilPositioned, { ...(ngDevMode ? { debugName: "clipUntilPositioned" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
210
|
+
/**
|
|
211
|
+
* Per-tooltip override for the open delay (ms). When `undefined`
|
|
212
|
+
* (default), falls back to `ForTooltipDefaults.openDelay` from the
|
|
213
|
+
* surrounding `provideForTooltipDefaults` scope (700ms unless configured).
|
|
214
|
+
*/
|
|
215
|
+
openDelay = input(undefined, /* @ts-ignore */
|
|
216
|
+
...(ngDevMode ? [{ debugName: "openDelay" }] : /* istanbul ignore next */ []));
|
|
217
|
+
/**
|
|
218
|
+
* Per-tooltip override for the close delay (ms) after hover or focus
|
|
219
|
+
* leaves. `Escape` ignores this. When `undefined` (default), falls back to
|
|
220
|
+
* `ForTooltipDefaults.closeDelay` from the surrounding
|
|
221
|
+
* `provideForTooltipDefaults` scope (300ms unless configured).
|
|
222
|
+
*/
|
|
223
|
+
closeDelay = input(undefined, /* @ts-ignore */
|
|
224
|
+
...(ngDevMode ? [{ debugName: "closeDelay" }] : /* istanbul ignore next */ []));
|
|
225
|
+
/** When true, all hover / focus interaction is ignored and any open tooltip is forced closed. */
|
|
226
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
227
|
+
/**
|
|
228
|
+
* Per-tooltip override for whether the tooltip shows only when the
|
|
229
|
+
* trigger's own text is truncated (`scrollWidth > clientWidth`) — the
|
|
230
|
+
* common pattern for ellipsized labels where the tooltip adds nothing once
|
|
231
|
+
* the full text is visible. When `undefined` (default), falls back to
|
|
232
|
+
* `ForTooltipDefaults.showOnOverflow` from the surrounding
|
|
233
|
+
* `provideForTooltipDefaults` scope (`false` unless configured).
|
|
234
|
+
*
|
|
235
|
+
* The input is aliased to `showOnOverflow`; consumers bind
|
|
236
|
+
* `[showOnOverflow]="..."` (or the bare attribute) and read the effective
|
|
237
|
+
* value via the public `showOnOverflow` computed below.
|
|
238
|
+
*/
|
|
239
|
+
_showOnOverflowInput = input(undefined, { ...(ngDevMode ? { debugName: "_showOnOverflowInput" } : /* istanbul ignore next */ {}), alias: 'showOnOverflow',
|
|
240
|
+
transform: (v) => (v == null ? undefined : booleanAttribute(v)) });
|
|
241
|
+
/** Effective overflow gate: the `showOnOverflow` input when set, else the scope default. */
|
|
242
|
+
showOnOverflow = computed(() => this._showOnOverflowInput() ?? this.#defaults.showOnOverflow, /* @ts-ignore */
|
|
243
|
+
...(ngDevMode ? [{ debugName: "showOnOverflow" }] : /* istanbul ignore next */ []));
|
|
244
|
+
/**
|
|
245
|
+
* Per-tooltip override for whether the pointer may move into the content
|
|
246
|
+
* without dismissing the tooltip. When `true`, the content drops its
|
|
247
|
+
* default `pointer-events: none` while open and a pointer-grace "safe
|
|
248
|
+
* triangle" bridges the gap between trigger and content so a slow diagonal
|
|
249
|
+
* traversal doesn't close it. When `undefined` (default), falls back to
|
|
250
|
+
* `ForTooltipDefaults.hoverableContent` from the surrounding
|
|
251
|
+
* `provideForTooltipDefaults` scope (`false` unless configured).
|
|
252
|
+
*
|
|
253
|
+
* Per APG the content must stay non-interactive; this only allows the
|
|
254
|
+
* pointer to rest over descriptive text (e.g. to select it).
|
|
255
|
+
*
|
|
256
|
+
* The input is aliased to `hoverableContent`; consumers bind
|
|
257
|
+
* `[hoverableContent]="..."` (or the bare attribute) and read the effective
|
|
258
|
+
* value via the public `hoverableContent` computed below.
|
|
259
|
+
*/
|
|
260
|
+
_hoverableContentInput = input(undefined, { ...(ngDevMode ? { debugName: "_hoverableContentInput" } : /* istanbul ignore next */ {}), alias: 'hoverableContent',
|
|
261
|
+
transform: (v) => (v == null ? undefined : booleanAttribute(v)) });
|
|
262
|
+
/** Effective hoverable-content flag: the `hoverableContent` input when set, else the scope default. */
|
|
263
|
+
hoverableContent = computed(() => this._hoverableContentInput() ?? this.#defaults.hoverableContent, /* @ts-ignore */
|
|
264
|
+
...(ngDevMode ? [{ debugName: "hoverableContent" }] : /* istanbul ignore next */ []));
|
|
265
|
+
/**
|
|
266
|
+
* Whether the user has requested reduced motion via the OS
|
|
267
|
+
* `prefers-reduced-motion: reduce` media query. Reflected as the boolean
|
|
268
|
+
* `data-reduced-motion` attribute on the root and content so consumers can
|
|
269
|
+
* disable their own `animate.enter` / `animate.leave` and CSS transitions
|
|
270
|
+
* without re-deriving the media query. Tooltip's JS-coordinated timing (the
|
|
271
|
+
* open / close hover-intent delays) is intent debouncing, not motion, so it
|
|
272
|
+
* is deliberately unchanged under reduced motion.
|
|
273
|
+
*/
|
|
274
|
+
reducedMotion = injectPrefersReducedMotion();
|
|
275
|
+
#generatedTriggerId = this.#idGen.next('for-tooltip-trigger');
|
|
276
|
+
/**
|
|
277
|
+
* Id of the trigger element. A consumer-set `id` on the trigger host is
|
|
278
|
+
* adopted at registration and preserved; the generated
|
|
279
|
+
* `for-tooltip-trigger-*` id is only assigned when the host has none.
|
|
280
|
+
*/
|
|
281
|
+
triggerId = signal(this.#generatedTriggerId, /* @ts-ignore */
|
|
282
|
+
...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
|
|
283
|
+
/** Generated id of the content element, wired to the trigger's `aria-describedby` while open. */
|
|
284
|
+
contentId = signal(this.#idGen.next('for-tooltip-content'), /* @ts-ignore */
|
|
285
|
+
...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
|
|
286
|
+
#triggerEl = signal(null, /* @ts-ignore */
|
|
287
|
+
...(ngDevMode ? [{ debugName: "#triggerEl" }] : /* istanbul ignore next */ []));
|
|
288
|
+
trigger = this.#triggerEl.asReadonly();
|
|
289
|
+
#contentEl = signal(null, /* @ts-ignore */
|
|
290
|
+
...(ngDevMode ? [{ debugName: "#contentEl" }] : /* istanbul ignore next */ []));
|
|
291
|
+
#arrowEl = signal(null, /* @ts-ignore */
|
|
292
|
+
...(ngDevMode ? [{ debugName: "#arrowEl" }] : /* istanbul ignore next */ []));
|
|
293
|
+
arrow = this.#arrowEl.asReadonly();
|
|
294
|
+
#coordinator = inject(TooltipCoordinator);
|
|
295
|
+
#hoverIntent;
|
|
296
|
+
#triggerHovered = false;
|
|
297
|
+
#triggerFocused = false;
|
|
298
|
+
#contentHovered = false;
|
|
299
|
+
#detachGrace = null;
|
|
300
|
+
constructor() {
|
|
301
|
+
forceCloseWhenDisabled({
|
|
302
|
+
open: this.open,
|
|
303
|
+
disabled: this.disabled,
|
|
304
|
+
onForceClose: () => this.cancelPending(),
|
|
305
|
+
});
|
|
306
|
+
this.#hoverIntent = createHoverIntent({
|
|
307
|
+
open: this.open,
|
|
308
|
+
isDisabled: () => this.disabled(),
|
|
309
|
+
openDelay: () => this.openDelay() ?? this.#coordinator.openDelay,
|
|
310
|
+
closeDelay: () => this.closeDelay() ?? this.#coordinator.closeDelay,
|
|
311
|
+
coordinator: this.#coordinator,
|
|
312
|
+
});
|
|
313
|
+
inject(DestroyRef).onDestroy(() => this.cancelPending());
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Registers the trigger host element. Adopts a pre-existing consumer-set
|
|
317
|
+
* `id` as the trigger id so external references (anchors, `aria-labelledby`,
|
|
318
|
+
* label `for`) keep resolving; falls back to the generated id otherwise.
|
|
319
|
+
*/
|
|
320
|
+
registerTrigger(el) {
|
|
321
|
+
adoptHostId(el, this.triggerId);
|
|
322
|
+
this.#triggerEl.set(el);
|
|
323
|
+
}
|
|
324
|
+
unregisterTrigger(el) {
|
|
325
|
+
if (this.#triggerEl() === el) {
|
|
326
|
+
this.#triggerEl.set(null);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/** Registers the content host element so the hoverable-content grace polygon can measure it. */
|
|
330
|
+
registerContent(el) {
|
|
331
|
+
this.#contentEl.set(el);
|
|
332
|
+
}
|
|
333
|
+
unregisterContent(el) {
|
|
334
|
+
if (this.#contentEl() === el) {
|
|
335
|
+
this.#contentEl.set(null);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** Adopts a consumer-set static `id` on the content host into `contentId`. */
|
|
339
|
+
adoptContentId(el) {
|
|
340
|
+
adoptHostId(el, this.contentId);
|
|
341
|
+
}
|
|
342
|
+
registerArrow(el) {
|
|
343
|
+
this.#arrowEl.set(el);
|
|
344
|
+
}
|
|
345
|
+
unregisterArrow(el) {
|
|
346
|
+
if (this.#arrowEl() === el) {
|
|
347
|
+
this.#arrowEl.set(null);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
pointerEnterTrigger() {
|
|
351
|
+
this.#triggerHovered = true;
|
|
352
|
+
this.#disarmContentGrace();
|
|
353
|
+
if (this.#suppressedByOverflow()) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.#hoverIntent.scheduleOpen();
|
|
357
|
+
}
|
|
358
|
+
pointerLeaveTrigger(cursor) {
|
|
359
|
+
this.#triggerHovered = false;
|
|
360
|
+
if (this.#triggerFocused) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (this.hoverableContent() && this.open() && this.#contentEl()) {
|
|
364
|
+
this.#armContentGrace(cursor);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
this.#scheduleCloseIfInactive();
|
|
368
|
+
}
|
|
369
|
+
focusTrigger() {
|
|
370
|
+
this.#triggerFocused = true;
|
|
371
|
+
if (this.#suppressedByOverflow()) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
this.#hoverIntent.scheduleOpen();
|
|
375
|
+
}
|
|
376
|
+
blurTrigger() {
|
|
377
|
+
this.#triggerFocused = false;
|
|
378
|
+
this.#scheduleCloseIfInactive();
|
|
379
|
+
}
|
|
380
|
+
pointerEnterContent() {
|
|
381
|
+
if (!this.hoverableContent()) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
this.#contentHovered = true;
|
|
385
|
+
this.#disarmContentGrace();
|
|
386
|
+
this.#hoverIntent.cancelPending();
|
|
387
|
+
}
|
|
388
|
+
pointerLeaveContent() {
|
|
389
|
+
if (!this.hoverableContent()) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
this.#contentHovered = false;
|
|
393
|
+
this.#scheduleCloseIfInactive();
|
|
394
|
+
}
|
|
395
|
+
scheduleOpen(_reason) {
|
|
396
|
+
this.#hoverIntent.scheduleOpen();
|
|
397
|
+
}
|
|
398
|
+
scheduleClose(reason) {
|
|
399
|
+
this.#disarmContentGrace();
|
|
400
|
+
this.#hoverIntent.scheduleClose(reason === 'escape');
|
|
401
|
+
}
|
|
402
|
+
cancelPending() {
|
|
403
|
+
this.#disarmContentGrace();
|
|
404
|
+
this.#hoverIntent.cancelPending();
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Imperatively opens the tooltip — for programmatic control beyond hover and
|
|
408
|
+
* focus (e.g. a design-system wrapper driving the tooltip from a
|
|
409
|
+
* text-truncation observer). Schedules the show after the resolved
|
|
410
|
+
* `openDelay` (instant when the delay is `0` or the scope's skip-delay window
|
|
411
|
+
* is active) and applies the same gates as a hover / focus open: a no-op
|
|
412
|
+
* while `disabled`, and a no-op under `showOnOverflow` when the trigger's own
|
|
413
|
+
* text is not truncated. For an instant, unconditional open that bypasses the
|
|
414
|
+
* delay and both gates, write the `[(open)]` model directly (`open.set(true)`).
|
|
415
|
+
*/
|
|
416
|
+
show() {
|
|
417
|
+
if (this.#suppressedByOverflow()) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
this.#hoverIntent.scheduleOpen();
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Imperatively closes the tooltip, mirroring a hover-leave / blur close:
|
|
424
|
+
* schedules the hide after the resolved `closeDelay` (instant when the delay
|
|
425
|
+
* is `0`) and disarms the hoverable-content grace bridge. For an instant
|
|
426
|
+
* close that ignores `closeDelay`, write the `[(open)]` model directly
|
|
427
|
+
* (`open.set(false)`).
|
|
428
|
+
*/
|
|
429
|
+
hide() {
|
|
430
|
+
this.scheduleClose('hover');
|
|
431
|
+
}
|
|
432
|
+
/** Close only when no keep-alive source (trigger hover/focus, content hover) is active. */
|
|
433
|
+
#scheduleCloseIfInactive() {
|
|
434
|
+
if (this.#triggerHovered || this.#triggerFocused || this.#contentHovered) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this.#hoverIntent.scheduleClose(false);
|
|
438
|
+
}
|
|
439
|
+
/** True when `showOnOverflow` is on and the trigger's text is NOT truncated. */
|
|
440
|
+
#suppressedByOverflow() {
|
|
441
|
+
if (!this.showOnOverflow()) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
const el = this.#triggerEl();
|
|
445
|
+
return el !== null && el.scrollWidth <= el.clientWidth;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Arms a pointer-grace "safe triangle" from `cursor` toward the content so
|
|
449
|
+
* the pointer can travel across the trigger / content gap without closing.
|
|
450
|
+
* On exit (or when content hover takes over) the grace disarms and a close
|
|
451
|
+
* is scheduled if nothing else keeps the tooltip alive.
|
|
452
|
+
*/
|
|
453
|
+
#armContentGrace(cursor) {
|
|
454
|
+
const content = this.#contentEl();
|
|
455
|
+
if (!content) {
|
|
456
|
+
this.#scheduleCloseIfInactive();
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const rect = content.getBoundingClientRect();
|
|
460
|
+
const trigger = this.#triggerEl();
|
|
461
|
+
const side = trigger ? resolveGraceSide(trigger.getBoundingClientRect(), rect) : this.side();
|
|
462
|
+
const polygon = buildSubmenuGracePolygon(cursor, rect, side);
|
|
463
|
+
this.#disarmContentGrace();
|
|
464
|
+
this.#detachGrace = attachPointerGrace(content.ownerDocument, polygon, () => {
|
|
465
|
+
this.#disarmContentGrace();
|
|
466
|
+
this.#scheduleCloseIfInactive();
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
#disarmContentGrace() {
|
|
470
|
+
if (this.#detachGrace) {
|
|
471
|
+
this.#detachGrace();
|
|
472
|
+
this.#detachGrace = null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltip, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
476
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTooltip, isStandalone: true, selector: "[forTooltip]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, _sideInput: { classPropertyName: "_sideInput", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, _alignInput: { classPropertyName: "_alignInput", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, _sideOffsetInput: { classPropertyName: "_sideOffsetInput", publicName: "sideOffset", isSignal: true, isRequired: false, transformFunction: null }, alignOffset: { classPropertyName: "alignOffset", publicName: "alignOffset", isSignal: true, isRequired: false, transformFunction: null }, avoidCollisions: { classPropertyName: "avoidCollisions", publicName: "avoidCollisions", isSignal: true, isRequired: false, transformFunction: null }, _collisionPaddingInput: { classPropertyName: "_collisionPaddingInput", publicName: "collisionPadding", isSignal: true, isRequired: false, transformFunction: null }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, hideWhenDetached: { classPropertyName: "hideWhenDetached", publicName: "hideWhenDetached", isSignal: true, isRequired: false, transformFunction: null }, clipUntilPositioned: { classPropertyName: "clipUntilPositioned", publicName: "clipUntilPositioned", isSignal: true, isRequired: false, transformFunction: null }, openDelay: { classPropertyName: "openDelay", publicName: "openDelay", isSignal: true, isRequired: false, transformFunction: null }, closeDelay: { classPropertyName: "closeDelay", publicName: "closeDelay", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _showOnOverflowInput: { classPropertyName: "_showOnOverflowInput", publicName: "showOnOverflow", isSignal: true, isRequired: false, transformFunction: null }, _hoverableContentInput: { classPropertyName: "_hoverableContentInput", publicName: "hoverableContent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { properties: { "attr.data-state": "open() ? \"open\" : \"closed\"", "attr.data-disabled": "disabled() ? \"\" : null", "attr.data-reduced-motion": "reducedMotion() ? \"\" : null" } }, providers: [{ provide: FOR_TOOLTIP_CONTEXT, useExisting: ForTooltip }], exportAs: ["forTooltip"], ngImport: i0 });
|
|
477
|
+
}
|
|
478
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltip, decorators: [{
|
|
479
|
+
type: Directive,
|
|
480
|
+
args: [{
|
|
481
|
+
selector: '[forTooltip]',
|
|
482
|
+
exportAs: 'forTooltip',
|
|
483
|
+
host: {
|
|
484
|
+
'[attr.data-state]': 'open() ? "open" : "closed"',
|
|
485
|
+
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
486
|
+
'[attr.data-reduced-motion]': 'reducedMotion() ? "" : null',
|
|
487
|
+
},
|
|
488
|
+
providers: [{ provide: FOR_TOOLTIP_CONTEXT, useExisting: ForTooltip }],
|
|
489
|
+
}]
|
|
490
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], _sideInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], _alignInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], _sideOffsetInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOffset", required: false }] }], alignOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignOffset", required: false }] }], avoidCollisions: [{ type: i0.Input, args: [{ isSignal: true, alias: "avoidCollisions", required: false }] }], _collisionPaddingInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], hideWhenDetached: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideWhenDetached", required: false }] }], clipUntilPositioned: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipUntilPositioned", required: false }] }], openDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "openDelay", required: false }] }], closeDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeDelay", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], _showOnOverflowInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnOverflow", required: false }] }], _hoverableContentInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverableContent", required: false }] }] } });
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Element that activates the tooltip on mouse hover or keyboard focus. Apply on
|
|
494
|
+
* a focusable element — preferably a `<button>` so keyboard users can reach it.
|
|
495
|
+
* Receives `aria-describedby` only while the tooltip is open, per APG.
|
|
496
|
+
*
|
|
497
|
+
* A touch tap does NOT open the tooltip: touch pointers are filtered out of
|
|
498
|
+
* both the hover-open path and the focus-open path, because a tap is not a
|
|
499
|
+
* hover and the APG flags hover-tooltips as problematic on touch (no hover, no
|
|
500
|
+
* separate focus affordance, no obvious dismiss). Keyboard focus stays the
|
|
501
|
+
* touch-accessible fallback for descriptive content.
|
|
502
|
+
*
|
|
503
|
+
* The root is normally resolved via DI from the enclosing `[forTooltip]`.
|
|
504
|
+
* When the trigger is declared inside an `ng-template` stamped into the root
|
|
505
|
+
* (e.g. via `ngTemplateOutlet`), DI resolves at the template's declaration
|
|
506
|
+
* site and misses the root — pass it explicitly through the selector input,
|
|
507
|
+
* `routerLink`-style: `[forTooltipTrigger]="root"` with `#root="forTooltip"`.
|
|
508
|
+
*/
|
|
509
|
+
class ForTooltipTrigger {
|
|
510
|
+
#host = inject(ElementRef);
|
|
511
|
+
/**
|
|
512
|
+
* Optional explicit reference to the `[forTooltip]` root, named after the
|
|
513
|
+
* selector `routerLink`-style. The bare valueless attribute keeps resolving
|
|
514
|
+
* the enclosing root via DI; pass the root explicitly
|
|
515
|
+
* (`[forTooltipTrigger]="root"`, with `#root="forTooltip"`) when the trigger
|
|
516
|
+
* is declared in an `ng-template` stamped inside the root — DI resolves at
|
|
517
|
+
* the template's declaration site, so the enclosing root is invisible there.
|
|
518
|
+
* The empty string (what the valueless attribute yields) is treated as unset.
|
|
519
|
+
*/
|
|
520
|
+
forTooltipTrigger = input('', /* @ts-ignore */
|
|
521
|
+
...(ngDevMode ? [{ debugName: "forTooltipTrigger" }] : /* istanbul ignore next */ []));
|
|
522
|
+
ctx = injectTooltipTriggerContext(this.forTooltipTrigger);
|
|
523
|
+
#lastPointerType = null;
|
|
524
|
+
constructor() {
|
|
525
|
+
const el = this.#host.nativeElement;
|
|
526
|
+
// Registration is an imperative call into the resolved root's registry,
|
|
527
|
+
// not state derivation — the effect only re-registers the element when the
|
|
528
|
+
// resolved root changes (explicit reference swapped at runtime).
|
|
529
|
+
effect((onCleanup) => {
|
|
530
|
+
const ctx = this.ctx();
|
|
531
|
+
ctx.registerTrigger(el);
|
|
532
|
+
onCleanup(() => ctx.unregisterTrigger(el));
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
onPointerEnter(event) {
|
|
536
|
+
if (event.pointerType === 'touch') {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
this.ctx().pointerEnterTrigger();
|
|
540
|
+
}
|
|
541
|
+
onPointerDown(event) {
|
|
542
|
+
this.#lastPointerType = event.pointerType;
|
|
543
|
+
}
|
|
544
|
+
onPointerLeave(event) {
|
|
545
|
+
this.ctx().pointerLeaveTrigger({ x: event.clientX, y: event.clientY });
|
|
546
|
+
}
|
|
547
|
+
onFocus() {
|
|
548
|
+
const touchInduced = this.#lastPointerType === 'touch';
|
|
549
|
+
this.#lastPointerType = null;
|
|
550
|
+
if (touchInduced) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
this.ctx().focusTrigger();
|
|
554
|
+
}
|
|
555
|
+
onBlur() {
|
|
556
|
+
this.#lastPointerType = null;
|
|
557
|
+
this.ctx().blurTrigger();
|
|
558
|
+
}
|
|
559
|
+
onEscape(event) {
|
|
560
|
+
if (this.ctx().open()) {
|
|
561
|
+
event.preventDefault();
|
|
562
|
+
event.stopPropagation();
|
|
563
|
+
this.ctx().scheduleClose('escape');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
567
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTooltipTrigger, isStandalone: true, selector: "[forTooltipTrigger]", inputs: { forTooltipTrigger: { classPropertyName: "forTooltipTrigger", publicName: "forTooltipTrigger", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerenter": "onPointerEnter($event)", "pointerdown": "onPointerDown($event)", "pointerleave": "onPointerLeave($event)", "focus": "onFocus()", "blur": "onBlur()", "keydown.escape": "onEscape($event)" }, properties: { "id": "ctx().triggerId()", "attr.aria-describedby": "ctx().open() ? ctx().contentId() : null", "attr.data-state": "ctx().open() ? \"open\" : \"closed\"" } }, exportAs: ["forTooltipTrigger"], ngImport: i0 });
|
|
568
|
+
}
|
|
569
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipTrigger, decorators: [{
|
|
570
|
+
type: Directive,
|
|
571
|
+
args: [{
|
|
572
|
+
selector: '[forTooltipTrigger]',
|
|
573
|
+
exportAs: 'forTooltipTrigger',
|
|
574
|
+
host: {
|
|
575
|
+
'[id]': 'ctx().triggerId()',
|
|
576
|
+
'[attr.aria-describedby]': 'ctx().open() ? ctx().contentId() : null',
|
|
577
|
+
'[attr.data-state]': 'ctx().open() ? "open" : "closed"',
|
|
578
|
+
'(pointerenter)': 'onPointerEnter($event)',
|
|
579
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
580
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
581
|
+
'(focus)': 'onFocus()',
|
|
582
|
+
'(blur)': 'onBlur()',
|
|
583
|
+
'(keydown.escape)': 'onEscape($event)',
|
|
584
|
+
},
|
|
585
|
+
}]
|
|
586
|
+
}], ctorParameters: () => [], propDecorators: { forTooltipTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "forTooltipTrigger", required: false }] }] } });
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* The tooltip bubble. Carries `role="tooltip"`, is portaled to
|
|
590
|
+
* `document.body`, and is positioned by `@floating-ui/dom` (via the shared
|
|
591
|
+
* `injectOverlayShell` helper) while mounted.
|
|
592
|
+
*
|
|
593
|
+
* Default `pointer-events: none` is applied via host styles so the bubble
|
|
594
|
+
* layers above content without intercepting hover. When the root opts in with
|
|
595
|
+
* `hoverableContent`, `pointer-events` is dropped while open so the pointer
|
|
596
|
+
* can rest over the bubble without dismissing it. Override with your own CSS
|
|
597
|
+
* if needed — but per APG, do not put interactive elements inside.
|
|
598
|
+
*
|
|
599
|
+
* The directive does not manage DOM presence — wrap with
|
|
600
|
+
* `@if (tip.open())` (using a template ref on `[forTooltip]`) so the
|
|
601
|
+
* bubble mounts and unmounts with the open state and `animate.enter` /
|
|
602
|
+
* `animate.leave` work natively.
|
|
603
|
+
*
|
|
604
|
+
* Tooltips have no dismissable layer (Escape is a tooltip controller
|
|
605
|
+
* concern, not an outside-pointer/focus one), no initial focus move, and
|
|
606
|
+
* no return focus on destroy — only the floating positioner is wired.
|
|
607
|
+
*/
|
|
608
|
+
class ForTooltipContent {
|
|
609
|
+
ctx = injectTooltipContext('ForTooltipContent');
|
|
610
|
+
constructor() {
|
|
611
|
+
const el = inject(ElementRef).nativeElement;
|
|
612
|
+
this.ctx.adoptContentId(el);
|
|
613
|
+
this.ctx.registerContent(el);
|
|
614
|
+
inject(DestroyRef).onDestroy(() => this.ctx.unregisterContent(el));
|
|
615
|
+
injectOverlayShell({
|
|
616
|
+
positioner: toFloatingPositioner(this.ctx, this.ctx.trigger),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
620
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTooltipContent, isStandalone: true, selector: "[forTooltipContent]", host: { attributes: { "role": "tooltip" }, listeners: { "pointerenter": "ctx.pointerEnterContent()", "pointerleave": "ctx.pointerLeaveContent()" }, properties: { "id": "ctx.contentId()", "attr.data-state": "ctx.open() ? \"open\" : \"closed\"", "attr.data-reduced-motion": "ctx.reducedMotion() ? \"\" : null", "style.pointer-events": "ctx.hoverableContent() ? null : \"none\"" } }, exportAs: ["forTooltipContent"], ngImport: i0 });
|
|
621
|
+
}
|
|
622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipContent, decorators: [{
|
|
623
|
+
type: Directive,
|
|
624
|
+
args: [{
|
|
625
|
+
selector: '[forTooltipContent]',
|
|
626
|
+
exportAs: 'forTooltipContent',
|
|
627
|
+
host: {
|
|
628
|
+
role: 'tooltip',
|
|
629
|
+
'[id]': 'ctx.contentId()',
|
|
630
|
+
'[attr.data-state]': 'ctx.open() ? "open" : "closed"',
|
|
631
|
+
'[attr.data-reduced-motion]': 'ctx.reducedMotion() ? "" : null',
|
|
632
|
+
'[style.pointer-events]': 'ctx.hoverableContent() ? null : "none"',
|
|
633
|
+
'(pointerenter)': 'ctx.pointerEnterContent()',
|
|
634
|
+
'(pointerleave)': 'ctx.pointerLeaveContent()',
|
|
635
|
+
},
|
|
636
|
+
}]
|
|
637
|
+
}], ctorParameters: () => [] });
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Optional visual arrow inside `ForTooltipContent`. Registers itself with
|
|
641
|
+
* the tooltip context so floating-ui's `arrow` middleware can position it
|
|
642
|
+
* along the bubble edge that points at the trigger. Style size and color
|
|
643
|
+
* yourself — the directive only sets `position`, `left`/`top`, and the
|
|
644
|
+
* opposite-side offset.
|
|
645
|
+
*/
|
|
646
|
+
class ForTooltipArrow {
|
|
647
|
+
#ctx = injectTooltipContext('ForTooltipArrow');
|
|
648
|
+
#host = inject(ElementRef);
|
|
649
|
+
constructor() {
|
|
650
|
+
registerHandle(this.#host.nativeElement, (el) => this.#ctx.registerArrow(el), (el) => this.#ctx.unregisterArrow(el));
|
|
651
|
+
}
|
|
652
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipArrow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
653
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTooltipArrow, isStandalone: true, selector: "[forTooltipArrow]", host: { attributes: { "aria-hidden": "true", "data-tooltip-arrow": "" } }, exportAs: ["forTooltipArrow"], ngImport: i0 });
|
|
654
|
+
}
|
|
655
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTooltipArrow, decorators: [{
|
|
656
|
+
type: Directive,
|
|
657
|
+
args: [{
|
|
658
|
+
selector: '[forTooltipArrow]',
|
|
659
|
+
exportAs: 'forTooltipArrow',
|
|
660
|
+
host: {
|
|
661
|
+
'aria-hidden': 'true',
|
|
662
|
+
'data-tooltip-arrow': '',
|
|
663
|
+
},
|
|
664
|
+
}]
|
|
665
|
+
}], ctorParameters: () => [] });
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Generated bundle index. Do not edit.
|
|
669
|
+
*/
|
|
670
|
+
|
|
671
|
+
export { FOR_TOOLTIP_CONTEXT, FOR_TOOLTIP_DEFAULTS, ForTooltip, ForTooltipArrow, ForTooltipContent, ForTooltipTrigger, provideForTooltipDefaults };
|
|
672
|
+
//# sourceMappingURL=forty-cdk-tooltip.mjs.map
|