@copilotkitnext/angular 0.0.2 → 0.0.5
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 +3 -3
- package/dist/README.md +3 -3
- package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
- package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
- package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
- package/dist/core/copilotkit.providers.d.ts +1 -1
- package/dist/core/copilotkit.service.d.ts +5 -5
- package/dist/core/copilotkit.types.d.ts +8 -10
- package/dist/directives/copilotkit-frontend-tool.directive.d.ts +1 -1
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
- package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
- package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
- package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
- package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
- package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
- package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
- package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
- package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
- package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
- package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
- package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
- package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
- package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
- package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
- package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
- package/dist/esm2022/core/copilotkit.service.mjs +426 -0
- package/dist/esm2022/core/copilotkit.types.mjs +13 -0
- package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
- package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
- package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
- package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
- package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +128 -0
- package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +265 -0
- package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
- package/dist/esm2022/index.mjs +70 -0
- package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
- package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
- package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
- package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
- package/dist/esm2022/lib/utils.mjs +10 -0
- package/dist/esm2022/services/resize-observer.service.mjs +152 -0
- package/dist/esm2022/services/scroll-position.service.mjs +124 -0
- package/dist/esm2022/types/frontend-tool.mjs +2 -0
- package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
- package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
- package/dist/esm2022/utils/agent.utils.mjs +204 -0
- package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
- package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
- package/dist/esm2022/utils/frontend-tool.utils.mjs +224 -0
- package/dist/esm2022/utils/human-in-the-loop.utils.mjs +293 -0
- package/dist/fesm2022/copilotkitnext-angular.mjs +174 -187
- package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
- package/dist/utils/frontend-tool.utils.d.ts +1 -1
- package/package.json +23 -20
- package/vitest.config.mts +32 -21
- package/.turbo/turbo-build.log +0 -38
- package/.turbo/turbo-check-types.log +0 -0
- package/.turbo/turbo-test.log +0 -71
- package/ng-package.json +0 -19
- package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
- package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
- package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
- package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
- package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
- package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
- package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
- package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
- package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
- package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
- package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
- package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
- package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
- package/src/components/chat/copilot-chat-input.component.ts +0 -512
- package/src/components/chat/copilot-chat-input.types.ts +0 -148
- package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
- package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
- package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
- package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
- package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
- package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
- package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
- package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
- package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
- package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
- package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
- package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
- package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
- package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
- package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
- package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
- package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
- package/src/components/chat/copilot-chat-view.component.ts +0 -420
- package/src/components/chat/copilot-chat-view.types.ts +0 -52
- package/src/components/chat/copilot-chat.component.ts +0 -232
- package/src/components/copilotkit-tool-render.component.ts +0 -169
- package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
- package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
- package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
- package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
- package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
- package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
- package/src/core/copilotkit.providers.ts +0 -59
- package/src/core/copilotkit.service.ts +0 -542
- package/src/core/copilotkit.types.ts +0 -132
- package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
- package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
- package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
- package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
- package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
- package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
- package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
- package/src/directives/copilotkit-agent-context.directive.ts +0 -138
- package/src/directives/copilotkit-agent.directive.ts +0 -225
- package/src/directives/copilotkit-chat-config.directive.ts +0 -241
- package/src/directives/copilotkit-config.directive.ts +0 -81
- package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
- package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
- package/src/directives/stick-to-bottom.directive.ts +0 -204
- package/src/index.ts +0 -105
- package/src/lib/directives/tooltip.directive.ts +0 -292
- package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
- package/src/lib/slots/copilot-slot.component.ts +0 -135
- package/src/lib/slots/index.ts +0 -3
- package/src/lib/slots/slot.types.ts +0 -64
- package/src/lib/slots/slot.utils.ts +0 -289
- package/src/lib/utils.ts +0 -10
- package/src/public-api.ts +0 -1
- package/src/services/resize-observer.service.ts +0 -181
- package/src/services/scroll-position.service.ts +0 -169
- package/src/styles/globals.css +0 -266
- package/src/styles/index.css +0 -3
- package/src/test-setup.ts +0 -15
- package/src/testing/index.ts +0 -3
- package/src/testing/testing.utils.ts +0 -248
- package/src/types/frontend-tool.ts +0 -44
- package/src/types/human-in-the-loop.ts +0 -52
- package/src/utils/__tests__/agent.utils.spec.ts +0 -234
- package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
- package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
- package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
- package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
- package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
- package/src/utils/agent-context.utils.ts +0 -133
- package/src/utils/agent.utils.ts +0 -239
- package/src/utils/chat-config.utils.ts +0 -221
- package/src/utils/copilotkit.utils.ts +0 -20
- package/src/utils/frontend-tool.utils.ts +0 -266
- package/src/utils/human-in-the-loop.utils.ts +0 -359
- package/tsconfig.spec.json +0 -12
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Directive,
|
|
3
|
-
Input,
|
|
4
|
-
ElementRef,
|
|
5
|
-
HostListener,
|
|
6
|
-
OnDestroy,
|
|
7
|
-
inject,
|
|
8
|
-
ViewContainerRef
|
|
9
|
-
} from '@angular/core';
|
|
10
|
-
import {
|
|
11
|
-
Overlay,
|
|
12
|
-
OverlayRef,
|
|
13
|
-
OverlayPositionBuilder,
|
|
14
|
-
ConnectedPosition
|
|
15
|
-
} from '@angular/cdk/overlay';
|
|
16
|
-
import { ComponentPortal } from '@angular/cdk/portal';
|
|
17
|
-
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
|
18
|
-
|
|
19
|
-
@Component({
|
|
20
|
-
selector: 'copilot-tooltip-content',
|
|
21
|
-
template: `
|
|
22
|
-
<div class="copilot-tooltip-wrapper" [attr.data-position]="position">
|
|
23
|
-
<div class="copilot-tooltip">
|
|
24
|
-
{{ text }}
|
|
25
|
-
</div>
|
|
26
|
-
<div class="copilot-tooltip-arrow"></div>
|
|
27
|
-
</div>
|
|
28
|
-
`,
|
|
29
|
-
styles: [`
|
|
30
|
-
.copilot-tooltip-wrapper {
|
|
31
|
-
position: relative;
|
|
32
|
-
display: inline-block;
|
|
33
|
-
animation: fadeIn 0.15s ease-in-out;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.copilot-tooltip {
|
|
37
|
-
background-color: #1a1a1a;
|
|
38
|
-
color: white;
|
|
39
|
-
padding: 6px 10px;
|
|
40
|
-
border-radius: 6px;
|
|
41
|
-
font-size: 12px;
|
|
42
|
-
font-weight: 500;
|
|
43
|
-
white-space: nowrap;
|
|
44
|
-
max-width: 200px;
|
|
45
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.copilot-tooltip-arrow {
|
|
49
|
-
position: absolute;
|
|
50
|
-
width: 0;
|
|
51
|
-
height: 0;
|
|
52
|
-
border-style: solid;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* Arrow for tooltip below element (arrow points up to tooltip) */
|
|
56
|
-
.copilot-tooltip-wrapper[data-position="below"] .copilot-tooltip-arrow {
|
|
57
|
-
top: -4px;
|
|
58
|
-
left: 50%;
|
|
59
|
-
transform: translateX(-50%);
|
|
60
|
-
border-width: 0 4px 4px 4px;
|
|
61
|
-
border-color: transparent transparent #1a1a1a transparent;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* Arrow for tooltip above element (arrow points down to element) */
|
|
65
|
-
.copilot-tooltip-wrapper[data-position="above"] .copilot-tooltip-arrow {
|
|
66
|
-
bottom: -4px;
|
|
67
|
-
left: 50%;
|
|
68
|
-
transform: translateX(-50%);
|
|
69
|
-
border-width: 4px 4px 0 4px;
|
|
70
|
-
border-color: #1a1a1a transparent transparent transparent;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/* Arrow for tooltip to the left */
|
|
74
|
-
.copilot-tooltip-wrapper[data-position="left"] .copilot-tooltip-arrow {
|
|
75
|
-
right: -4px;
|
|
76
|
-
top: 50%;
|
|
77
|
-
transform: translateY(-50%);
|
|
78
|
-
border-width: 4px 0 4px 4px;
|
|
79
|
-
border-color: transparent transparent transparent #1a1a1a;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Arrow for tooltip to the right */
|
|
83
|
-
.copilot-tooltip-wrapper[data-position="right"] .copilot-tooltip-arrow {
|
|
84
|
-
left: -4px;
|
|
85
|
-
top: 50%;
|
|
86
|
-
transform: translateY(-50%);
|
|
87
|
-
border-width: 4px 4px 4px 0;
|
|
88
|
-
border-color: transparent #1a1a1a transparent transparent;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
@keyframes fadeIn {
|
|
92
|
-
from {
|
|
93
|
-
opacity: 0;
|
|
94
|
-
transform: translateY(2px);
|
|
95
|
-
}
|
|
96
|
-
to {
|
|
97
|
-
opacity: 1;
|
|
98
|
-
transform: translateY(0);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
`],
|
|
102
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
103
|
-
standalone: true
|
|
104
|
-
})
|
|
105
|
-
export class TooltipContentComponent {
|
|
106
|
-
text = '';
|
|
107
|
-
private _position: 'above' | 'below' | 'left' | 'right' = 'below';
|
|
108
|
-
|
|
109
|
-
get position(): 'above' | 'below' | 'left' | 'right' {
|
|
110
|
-
return this._position;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
set position(value: 'above' | 'below' | 'left' | 'right') {
|
|
114
|
-
this._position = value;
|
|
115
|
-
this.cdr.markForCheck();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
constructor(private cdr: ChangeDetectorRef) {}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@Directive({
|
|
122
|
-
selector: '[copilotTooltip]',
|
|
123
|
-
standalone: true
|
|
124
|
-
})
|
|
125
|
-
export class CopilotTooltipDirective implements OnDestroy {
|
|
126
|
-
@Input('copilotTooltip') tooltipText = '';
|
|
127
|
-
@Input() tooltipPosition: 'above' | 'below' | 'left' | 'right' = 'below';
|
|
128
|
-
@Input() tooltipDelay = 500; // milliseconds
|
|
129
|
-
|
|
130
|
-
private overlay = inject(Overlay);
|
|
131
|
-
private overlayPositionBuilder = inject(OverlayPositionBuilder);
|
|
132
|
-
private elementRef = inject(ElementRef);
|
|
133
|
-
private viewContainerRef = inject(ViewContainerRef);
|
|
134
|
-
|
|
135
|
-
private overlayRef?: OverlayRef;
|
|
136
|
-
private tooltipTimeout?: number;
|
|
137
|
-
private originalTitle?: string;
|
|
138
|
-
|
|
139
|
-
@HostListener('mouseenter')
|
|
140
|
-
onMouseEnter(): void {
|
|
141
|
-
if (!this.tooltipText) return;
|
|
142
|
-
|
|
143
|
-
// Store and remove native title to prevent OS tooltip
|
|
144
|
-
const element = this.elementRef.nativeElement;
|
|
145
|
-
if (element.hasAttribute('title')) {
|
|
146
|
-
this.originalTitle = element.getAttribute('title');
|
|
147
|
-
element.removeAttribute('title');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Clear any existing timeout
|
|
151
|
-
if (this.tooltipTimeout) {
|
|
152
|
-
clearTimeout(this.tooltipTimeout);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Set timeout to show tooltip after delay
|
|
156
|
-
this.tooltipTimeout = window.setTimeout(() => {
|
|
157
|
-
this.show();
|
|
158
|
-
}, this.tooltipDelay);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@HostListener('mouseleave')
|
|
162
|
-
onMouseLeave(): void {
|
|
163
|
-
// Clear timeout if mouse leaves before tooltip shows
|
|
164
|
-
if (this.tooltipTimeout) {
|
|
165
|
-
clearTimeout(this.tooltipTimeout);
|
|
166
|
-
this.tooltipTimeout = undefined;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Restore original title if it existed
|
|
170
|
-
if (this.originalTitle !== undefined) {
|
|
171
|
-
this.elementRef.nativeElement.setAttribute('title', this.originalTitle);
|
|
172
|
-
this.originalTitle = undefined;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Hide tooltip if it's showing
|
|
176
|
-
this.hide();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private show(): void {
|
|
180
|
-
if (this.overlayRef) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Create overlay
|
|
185
|
-
const positionStrategy = this.overlayPositionBuilder
|
|
186
|
-
.flexibleConnectedTo(this.elementRef)
|
|
187
|
-
.withPositions(this.getPositions())
|
|
188
|
-
.withPush(false);
|
|
189
|
-
|
|
190
|
-
this.overlayRef = this.overlay.create({
|
|
191
|
-
positionStrategy,
|
|
192
|
-
scrollStrategy: this.overlay.scrollStrategies.close(),
|
|
193
|
-
hasBackdrop: false
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Create component portal and attach
|
|
197
|
-
const portal = new ComponentPortal(TooltipContentComponent, this.viewContainerRef);
|
|
198
|
-
const componentRef = this.overlayRef.attach(portal);
|
|
199
|
-
componentRef.instance.text = this.tooltipText;
|
|
200
|
-
|
|
201
|
-
// Detect actual position after overlay is positioned
|
|
202
|
-
setTimeout(() => {
|
|
203
|
-
if (this.overlayRef && this.elementRef.nativeElement) {
|
|
204
|
-
const tooltipRect = this.overlayRef.overlayElement.getBoundingClientRect();
|
|
205
|
-
const elementRect = this.elementRef.nativeElement.getBoundingClientRect();
|
|
206
|
-
|
|
207
|
-
let actualPosition: 'above' | 'below' | 'left' | 'right' = 'below';
|
|
208
|
-
|
|
209
|
-
// Determine actual position based on relative positions
|
|
210
|
-
if (tooltipRect.bottom <= elementRect.top) {
|
|
211
|
-
actualPosition = 'above';
|
|
212
|
-
} else if (tooltipRect.top >= elementRect.bottom) {
|
|
213
|
-
actualPosition = 'below';
|
|
214
|
-
} else if (tooltipRect.right <= elementRect.left) {
|
|
215
|
-
actualPosition = 'left';
|
|
216
|
-
} else if (tooltipRect.left >= elementRect.right) {
|
|
217
|
-
actualPosition = 'right';
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
componentRef.instance.position = actualPosition;
|
|
221
|
-
}
|
|
222
|
-
}, 0);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private hide(): void {
|
|
226
|
-
if (this.overlayRef) {
|
|
227
|
-
this.overlayRef.dispose();
|
|
228
|
-
this.overlayRef = undefined;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
private getPositions(): ConnectedPosition[] {
|
|
233
|
-
const positions: Record<string, ConnectedPosition[]> = {
|
|
234
|
-
above: [
|
|
235
|
-
{
|
|
236
|
-
originX: 'center',
|
|
237
|
-
originY: 'top',
|
|
238
|
-
overlayX: 'center',
|
|
239
|
-
overlayY: 'bottom',
|
|
240
|
-
offsetY: -12
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
below: [
|
|
244
|
-
{
|
|
245
|
-
originX: 'center',
|
|
246
|
-
originY: 'bottom',
|
|
247
|
-
overlayX: 'center',
|
|
248
|
-
overlayY: 'top',
|
|
249
|
-
offsetY: 12
|
|
250
|
-
}
|
|
251
|
-
],
|
|
252
|
-
left: [
|
|
253
|
-
{
|
|
254
|
-
originX: 'start',
|
|
255
|
-
originY: 'center',
|
|
256
|
-
overlayX: 'end',
|
|
257
|
-
overlayY: 'center',
|
|
258
|
-
offsetX: -12
|
|
259
|
-
}
|
|
260
|
-
],
|
|
261
|
-
right: [
|
|
262
|
-
{
|
|
263
|
-
originX: 'end',
|
|
264
|
-
originY: 'center',
|
|
265
|
-
overlayX: 'start',
|
|
266
|
-
overlayY: 'center',
|
|
267
|
-
offsetX: 12
|
|
268
|
-
}
|
|
269
|
-
]
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
// Prefer below position, but use above as fallback
|
|
273
|
-
const primary = positions[this.tooltipPosition] || positions.below;
|
|
274
|
-
// For below position, add above as first fallback
|
|
275
|
-
const fallbacks = this.tooltipPosition === 'below'
|
|
276
|
-
? [...(positions.above || []), ...(positions.left || []), ...(positions.right || [])]
|
|
277
|
-
: Object.values(positions).filter(p => p !== primary).flat();
|
|
278
|
-
|
|
279
|
-
return [...(primary || []), ...fallbacks];
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
ngOnDestroy(): void {
|
|
283
|
-
if (this.tooltipTimeout) {
|
|
284
|
-
clearTimeout(this.tooltipTimeout);
|
|
285
|
-
}
|
|
286
|
-
// Restore original title if it existed
|
|
287
|
-
if (this.originalTitle !== undefined) {
|
|
288
|
-
this.elementRef.nativeElement.setAttribute('title', this.originalTitle);
|
|
289
|
-
}
|
|
290
|
-
this.hide();
|
|
291
|
-
}
|
|
292
|
-
}
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
4
|
-
import {
|
|
5
|
-
renderSlot,
|
|
6
|
-
isComponentType,
|
|
7
|
-
isSlotValue,
|
|
8
|
-
normalizeSlotValue,
|
|
9
|
-
createSlotConfig,
|
|
10
|
-
provideSlots,
|
|
11
|
-
getSlotConfig,
|
|
12
|
-
createSlotRenderer
|
|
13
|
-
} from '../slot.utils';
|
|
14
|
-
import { SLOT_CONFIG } from '../slot.types';
|
|
15
|
-
|
|
16
|
-
// Test components
|
|
17
|
-
@Component({
|
|
18
|
-
selector: 'default-component',
|
|
19
|
-
template: `<div class="default">{{ text }}</div>`,
|
|
20
|
-
standalone: true
|
|
21
|
-
})
|
|
22
|
-
class DefaultComponent {
|
|
23
|
-
@Input() text = 'Default';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'custom-component',
|
|
28
|
-
template: `<div class="custom">{{ text }}</div>`,
|
|
29
|
-
standalone: true
|
|
30
|
-
})
|
|
31
|
-
class CustomComponent {
|
|
32
|
-
@Input() text = 'Custom';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('Slot Utilities', () => {
|
|
36
|
-
describe('renderSlot', () => {
|
|
37
|
-
let viewContainer: ViewContainerRef;
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
@Component({
|
|
41
|
-
template: `<div #container></div>`,
|
|
42
|
-
standalone: true
|
|
43
|
-
})
|
|
44
|
-
class TestComponent {
|
|
45
|
-
@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
49
|
-
fixture.detectChanges();
|
|
50
|
-
viewContainer = fixture.componentInstance.container;
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should render default component when no slot provided', () => {
|
|
54
|
-
const ref = renderSlot(viewContainer, {
|
|
55
|
-
defaultComponent: DefaultComponent
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(ref).toBeTruthy();
|
|
59
|
-
expect(ref?.location.nativeElement.querySelector('.default')).toBeTruthy();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should render custom component when provided', () => {
|
|
63
|
-
const ref = renderSlot(viewContainer, {
|
|
64
|
-
slot: CustomComponent,
|
|
65
|
-
defaultComponent: DefaultComponent
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(ref).toBeTruthy();
|
|
69
|
-
expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should render default component when string slot is no longer supported', () => {
|
|
73
|
-
// String slots are no longer supported - should render default component
|
|
74
|
-
const ref = renderSlot(viewContainer, {
|
|
75
|
-
slot: 'fancy-style' as any, // Type assertion needed since strings are no longer valid
|
|
76
|
-
defaultComponent: DefaultComponent
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
expect(ref).toBeTruthy();
|
|
80
|
-
// Should render default component, not apply class
|
|
81
|
-
expect(ref?.location.nativeElement.querySelector('.default')).toBeTruthy();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should apply props to component using setInput', () => {
|
|
85
|
-
const ref = renderSlot(viewContainer, {
|
|
86
|
-
defaultComponent: DefaultComponent,
|
|
87
|
-
props: { text: 'Hello World' }
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
expect(ref).toBeTruthy();
|
|
91
|
-
if ('instance' in ref!) {
|
|
92
|
-
// Props should be set via setInput, which updates the instance
|
|
93
|
-
expect(ref.instance.text).toBe('Hello World');
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should render template when provided', () => {
|
|
98
|
-
@Component({
|
|
99
|
-
template: `
|
|
100
|
-
<div #container></div>
|
|
101
|
-
<ng-template #myTemplate let-props="props">
|
|
102
|
-
<span class="template">{{ props?.message }}</span>
|
|
103
|
-
</ng-template>
|
|
104
|
-
`,
|
|
105
|
-
standalone: true
|
|
106
|
-
})
|
|
107
|
-
class TestComponent {
|
|
108
|
-
@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
|
|
109
|
-
@ViewChild('myTemplate') template!: TemplateRef<any>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
113
|
-
fixture.detectChanges();
|
|
114
|
-
|
|
115
|
-
renderSlot(fixture.componentInstance.container, {
|
|
116
|
-
slot: fixture.componentInstance.template,
|
|
117
|
-
defaultComponent: DefaultComponent,
|
|
118
|
-
props: { message: 'Template Content' }
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
fixture.detectChanges();
|
|
122
|
-
|
|
123
|
-
const span = fixture.nativeElement.querySelector('.template');
|
|
124
|
-
expect(span).toBeTruthy();
|
|
125
|
-
expect(span.textContent).toBe('Template Content');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should render default component when object slot is no longer supported', () => {
|
|
129
|
-
// Object slots are no longer supported - should render default component
|
|
130
|
-
const ref = renderSlot(viewContainer, {
|
|
131
|
-
slot: { text: 'Overridden' } as any, // Type assertion needed since objects are no longer valid
|
|
132
|
-
defaultComponent: DefaultComponent
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
expect(ref).toBeTruthy();
|
|
136
|
-
if ('instance' in ref!) {
|
|
137
|
-
// Should use default text, not override
|
|
138
|
-
expect(ref.instance.text).toBe('Default');
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('isComponentType', () => {
|
|
144
|
-
it('should identify Angular components', () => {
|
|
145
|
-
// After removing ɵ checks, any function with prototype is considered a component
|
|
146
|
-
expect(isComponentType(DefaultComponent)).toBe(true);
|
|
147
|
-
expect(isComponentType(CustomComponent)).toBe(true);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should reject non-components', () => {
|
|
151
|
-
expect(isComponentType('string')).toBe(false);
|
|
152
|
-
expect(isComponentType(123)).toBe(false);
|
|
153
|
-
expect(isComponentType({})).toBe(false);
|
|
154
|
-
expect(isComponentType(null)).toBe(false);
|
|
155
|
-
expect(isComponentType(undefined)).toBe(false);
|
|
156
|
-
expect(isComponentType(() => {})).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('isSlotValue', () => {
|
|
161
|
-
it('should accept valid slot values', () => {
|
|
162
|
-
// Only components and templates are valid now
|
|
163
|
-
expect(isSlotValue(DefaultComponent)).toBe(true);
|
|
164
|
-
expect(isSlotValue(CustomComponent)).toBe(true);
|
|
165
|
-
// Strings and objects are no longer valid slot values
|
|
166
|
-
expect(isSlotValue('css-class')).toBe(false);
|
|
167
|
-
expect(isSlotValue({ prop: 'value' })).toBe(false);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should reject invalid slot values', () => {
|
|
171
|
-
expect(isSlotValue(null)).toBe(false);
|
|
172
|
-
expect(isSlotValue(undefined)).toBe(false);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('normalizeSlotValue', () => {
|
|
177
|
-
it('should return default component for string (no longer supported)', () => {
|
|
178
|
-
const result = normalizeSlotValue('custom-class' as any, DefaultComponent);
|
|
179
|
-
expect(result).toEqual({
|
|
180
|
-
component: DefaultComponent
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should normalize component type', () => {
|
|
185
|
-
const result = normalizeSlotValue(CustomComponent, DefaultComponent);
|
|
186
|
-
expect(result).toEqual({
|
|
187
|
-
component: CustomComponent
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should return default component for object (no longer supported)', () => {
|
|
192
|
-
const props = { text: 'Test' };
|
|
193
|
-
const result = normalizeSlotValue(props as any, DefaultComponent);
|
|
194
|
-
expect(result).toEqual({
|
|
195
|
-
component: DefaultComponent
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should handle undefined', () => {
|
|
200
|
-
const result = normalizeSlotValue(undefined, DefaultComponent);
|
|
201
|
-
expect(result).toEqual({
|
|
202
|
-
component: DefaultComponent
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('createSlotConfig', () => {
|
|
208
|
-
it('should create configuration map', () => {
|
|
209
|
-
const config = createSlotConfig(
|
|
210
|
-
{
|
|
211
|
-
button: CustomComponent,
|
|
212
|
-
toolbar: 'toolbar-class' as any // String no longer supported but test the behavior
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
button: DefaultComponent,
|
|
216
|
-
toolbar: DefaultComponent
|
|
217
|
-
}
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
expect(config.get('button')).toEqual({
|
|
221
|
-
component: CustomComponent
|
|
222
|
-
});
|
|
223
|
-
// String slots no longer supported - should use default
|
|
224
|
-
expect(config.get('toolbar')).toEqual({
|
|
225
|
-
component: DefaultComponent
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should use defaults when no overrides', () => {
|
|
230
|
-
const config = createSlotConfig(
|
|
231
|
-
{},
|
|
232
|
-
{
|
|
233
|
-
button: DefaultComponent,
|
|
234
|
-
toolbar: DefaultComponent
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
expect(config.get('button')).toEqual({
|
|
239
|
-
component: DefaultComponent
|
|
240
|
-
});
|
|
241
|
-
expect(config.get('toolbar')).toEqual({
|
|
242
|
-
component: DefaultComponent
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('provideSlots', () => {
|
|
248
|
-
it('should create provider configuration with Map', () => {
|
|
249
|
-
const provider = provideSlots({
|
|
250
|
-
button: CustomComponent
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expect(provider.provide).toBe(SLOT_CONFIG);
|
|
254
|
-
expect(provider.useValue).toBeInstanceOf(Map);
|
|
255
|
-
expect(provider.useValue.get('button')).toEqual({
|
|
256
|
-
component: CustomComponent
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
describe('getSlotConfig', () => {
|
|
262
|
-
it('should retrieve slot configuration from DI', () => {
|
|
263
|
-
const slots = new Map([
|
|
264
|
-
['button', { component: CustomComponent }]
|
|
265
|
-
]);
|
|
266
|
-
|
|
267
|
-
TestBed.configureTestingModule({
|
|
268
|
-
providers: [
|
|
269
|
-
{ provide: SLOT_CONFIG, useValue: slots }
|
|
270
|
-
]
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
@Component({
|
|
274
|
-
template: '',
|
|
275
|
-
standalone: true
|
|
276
|
-
})
|
|
277
|
-
class TestComponent {
|
|
278
|
-
slots = getSlotConfig();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
282
|
-
expect(fixture.componentInstance.slots).toBe(slots);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('should return null when no config provided', () => {
|
|
286
|
-
@Component({
|
|
287
|
-
template: '',
|
|
288
|
-
standalone: true
|
|
289
|
-
})
|
|
290
|
-
class TestComponent {
|
|
291
|
-
slots = getSlotConfig();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
295
|
-
expect(fixture.componentInstance.slots).toBe(null);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('createSlotRenderer', () => {
|
|
300
|
-
let viewContainer: ViewContainerRef;
|
|
301
|
-
|
|
302
|
-
beforeEach(() => {
|
|
303
|
-
@Component({
|
|
304
|
-
template: `<div #container></div>`,
|
|
305
|
-
standalone: true
|
|
306
|
-
})
|
|
307
|
-
class TestComponent {
|
|
308
|
-
@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
312
|
-
fixture.detectChanges();
|
|
313
|
-
viewContainer = fixture.componentInstance.container;
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('should create a renderer function', () => {
|
|
317
|
-
const renderer = createSlotRenderer(DefaultComponent);
|
|
318
|
-
expect(typeof renderer).toBe('function');
|
|
319
|
-
|
|
320
|
-
const ref = renderer(viewContainer, CustomComponent);
|
|
321
|
-
expect(ref).toBeTruthy();
|
|
322
|
-
expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('should use DI config when slot name provided', () => {
|
|
326
|
-
const slots = new Map([
|
|
327
|
-
['button', { component: CustomComponent }]
|
|
328
|
-
]);
|
|
329
|
-
|
|
330
|
-
TestBed.resetTestingModule();
|
|
331
|
-
TestBed.configureTestingModule({
|
|
332
|
-
providers: [
|
|
333
|
-
{ provide: SLOT_CONFIG, useValue: slots }
|
|
334
|
-
]
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
@Component({
|
|
338
|
-
template: `<div #container></div>`,
|
|
339
|
-
standalone: true
|
|
340
|
-
})
|
|
341
|
-
class TestComponent {
|
|
342
|
-
@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
|
|
343
|
-
renderButton = createSlotRenderer(DefaultComponent, 'button');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const fixture = TestBed.createComponent(TestComponent);
|
|
347
|
-
fixture.detectChanges();
|
|
348
|
-
|
|
349
|
-
const ref = fixture.componentInstance.renderButton(
|
|
350
|
-
fixture.componentInstance.container
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
expect(ref).toBeTruthy();
|
|
354
|
-
expect(ref?.location.nativeElement.querySelector('.custom')).toBeTruthy();
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should apply props from renderer', () => {
|
|
358
|
-
const renderer = createSlotRenderer(DefaultComponent);
|
|
359
|
-
const ref = renderer(viewContainer, undefined, { text: 'Rendered' });
|
|
360
|
-
|
|
361
|
-
expect(ref).toBeTruthy();
|
|
362
|
-
if ('instance' in ref!) {
|
|
363
|
-
// Props should be set via setInput
|
|
364
|
-
expect(ref.instance.text).toBe('Rendered');
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('should apply outputs when provided', () => {
|
|
369
|
-
const renderer = createSlotRenderer(DefaultComponent);
|
|
370
|
-
const clickHandler = vi.fn();
|
|
371
|
-
const ref = renderer(viewContainer, undefined, undefined, { click: clickHandler });
|
|
372
|
-
|
|
373
|
-
expect(ref).toBeTruthy();
|
|
374
|
-
// Outputs would be wired if the component has the corresponding EventEmitter
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
});
|