@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.
Files changed (173) hide show
  1. package/README.md +3 -3
  2. package/dist/README.md +3 -3
  3. package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
  4. package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
  5. package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
  6. package/dist/core/copilotkit.providers.d.ts +1 -1
  7. package/dist/core/copilotkit.service.d.ts +5 -5
  8. package/dist/core/copilotkit.types.d.ts +8 -10
  9. package/dist/directives/copilotkit-frontend-tool.directive.d.ts +1 -1
  10. package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
  11. package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
  12. package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
  13. package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
  14. package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
  15. package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
  16. package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
  17. package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
  18. package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
  19. package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
  20. package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
  21. package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
  22. package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
  23. package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
  24. package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
  25. package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
  26. package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
  27. package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
  28. package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
  29. package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
  30. package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
  31. package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
  32. package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
  33. package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
  34. package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
  35. package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
  36. package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
  37. package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
  38. package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
  39. package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
  40. package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
  41. package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
  42. package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
  43. package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
  44. package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
  45. package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
  46. package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
  47. package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
  48. package/dist/esm2022/core/copilotkit.service.mjs +426 -0
  49. package/dist/esm2022/core/copilotkit.types.mjs +13 -0
  50. package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
  51. package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
  52. package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
  53. package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
  54. package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +128 -0
  55. package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +265 -0
  56. package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
  57. package/dist/esm2022/index.mjs +70 -0
  58. package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
  59. package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
  60. package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
  61. package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
  62. package/dist/esm2022/lib/utils.mjs +10 -0
  63. package/dist/esm2022/services/resize-observer.service.mjs +152 -0
  64. package/dist/esm2022/services/scroll-position.service.mjs +124 -0
  65. package/dist/esm2022/types/frontend-tool.mjs +2 -0
  66. package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
  67. package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
  68. package/dist/esm2022/utils/agent.utils.mjs +204 -0
  69. package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
  70. package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
  71. package/dist/esm2022/utils/frontend-tool.utils.mjs +224 -0
  72. package/dist/esm2022/utils/human-in-the-loop.utils.mjs +293 -0
  73. package/dist/fesm2022/copilotkitnext-angular.mjs +174 -187
  74. package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
  75. package/dist/utils/frontend-tool.utils.d.ts +1 -1
  76. package/package.json +23 -20
  77. package/vitest.config.mts +32 -21
  78. package/.turbo/turbo-build.log +0 -38
  79. package/.turbo/turbo-check-types.log +0 -0
  80. package/.turbo/turbo-test.log +0 -71
  81. package/ng-package.json +0 -19
  82. package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
  83. package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
  84. package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
  85. package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
  86. package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
  87. package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
  88. package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
  89. package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
  90. package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
  91. package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
  92. package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
  93. package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
  94. package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
  95. package/src/components/chat/copilot-chat-input.component.ts +0 -512
  96. package/src/components/chat/copilot-chat-input.types.ts +0 -148
  97. package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
  98. package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
  99. package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
  100. package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
  101. package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
  102. package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
  103. package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
  104. package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
  105. package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
  106. package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
  107. package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
  108. package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
  109. package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
  110. package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
  111. package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
  112. package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
  113. package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
  114. package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
  115. package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
  116. package/src/components/chat/copilot-chat-view.component.ts +0 -420
  117. package/src/components/chat/copilot-chat-view.types.ts +0 -52
  118. package/src/components/chat/copilot-chat.component.ts +0 -232
  119. package/src/components/copilotkit-tool-render.component.ts +0 -169
  120. package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
  121. package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
  122. package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
  123. package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
  124. package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
  125. package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
  126. package/src/core/copilotkit.providers.ts +0 -59
  127. package/src/core/copilotkit.service.ts +0 -542
  128. package/src/core/copilotkit.types.ts +0 -132
  129. package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
  130. package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
  131. package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
  132. package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
  133. package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
  134. package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
  135. package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
  136. package/src/directives/copilotkit-agent-context.directive.ts +0 -138
  137. package/src/directives/copilotkit-agent.directive.ts +0 -225
  138. package/src/directives/copilotkit-chat-config.directive.ts +0 -241
  139. package/src/directives/copilotkit-config.directive.ts +0 -81
  140. package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
  141. package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
  142. package/src/directives/stick-to-bottom.directive.ts +0 -204
  143. package/src/index.ts +0 -105
  144. package/src/lib/directives/tooltip.directive.ts +0 -292
  145. package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
  146. package/src/lib/slots/copilot-slot.component.ts +0 -135
  147. package/src/lib/slots/index.ts +0 -3
  148. package/src/lib/slots/slot.types.ts +0 -64
  149. package/src/lib/slots/slot.utils.ts +0 -289
  150. package/src/lib/utils.ts +0 -10
  151. package/src/public-api.ts +0 -1
  152. package/src/services/resize-observer.service.ts +0 -181
  153. package/src/services/scroll-position.service.ts +0 -169
  154. package/src/styles/globals.css +0 -266
  155. package/src/styles/index.css +0 -3
  156. package/src/test-setup.ts +0 -15
  157. package/src/testing/index.ts +0 -3
  158. package/src/testing/testing.utils.ts +0 -248
  159. package/src/types/frontend-tool.ts +0 -44
  160. package/src/types/human-in-the-loop.ts +0 -52
  161. package/src/utils/__tests__/agent.utils.spec.ts +0 -234
  162. package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
  163. package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
  164. package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
  165. package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
  166. package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
  167. package/src/utils/agent-context.utils.ts +0 -133
  168. package/src/utils/agent.utils.ts +0 -239
  169. package/src/utils/chat-config.utils.ts +0 -221
  170. package/src/utils/copilotkit.utils.ts +0 -20
  171. package/src/utils/frontend-tool.utils.ts +0 -266
  172. package/src/utils/human-in-the-loop.utils.ts +0 -359
  173. package/tsconfig.spec.json +0 -12
@@ -1,135 +0,0 @@
1
- import {
2
- Component,
3
- Input,
4
- TemplateRef,
5
- ViewContainerRef,
6
- OnInit,
7
- OnChanges,
8
- SimpleChanges,
9
- Inject,
10
- ChangeDetectionStrategy,
11
- ChangeDetectorRef,
12
- ViewChild
13
- } from '@angular/core';
14
- import { CommonModule } from '@angular/common';
15
- import { renderSlot } from './slot.utils';
16
- import { Type } from '@angular/core';
17
-
18
- /**
19
- * @internal - This component is for internal use only.
20
- * Simple slot component for rendering custom content or defaults.
21
- * Supports templates and components only.
22
- *
23
- * @example
24
- * ```html
25
- * <!-- With template -->
26
- * <copilot-slot [slot]="sendButtonTemplate" [context]="buttonContext">
27
- * <button class="default-btn">Default</button>
28
- * </copilot-slot>
29
- * ```
30
- */
31
- @Component({
32
- selector: 'copilot-slot',
33
- standalone: true,
34
- imports: [CommonModule],
35
- template: `
36
- <!-- If slot template provided, render it -->
37
- <ng-container *ngIf="slot && isTemplate(slot)"
38
- [ngTemplateOutlet]="slot"
39
- [ngTemplateOutletContext]="context || {}">
40
- </ng-container>
41
-
42
- <!-- If not a template, we'll handle in code -->
43
- <ng-container #slotContainer></ng-container>
44
-
45
- <!-- Default content (only shown if no slot) -->
46
- <ng-content *ngIf="!slot && !defaultComponent"></ng-content>
47
- `,
48
- changeDetection: ChangeDetectionStrategy.OnPush
49
- })
50
- export class CopilotSlotComponent implements OnInit, OnChanges {
51
- @Input() slot?: TemplateRef<any> | Type<any>;
52
- @Input() context?: any;
53
- @Input() defaultComponent?: Type<any>;
54
- @Input() outputs?: Record<string, (event: any) => void>;
55
-
56
- @ViewChild('slotContainer', { read: ViewContainerRef, static: true })
57
- private slotContainer!: ViewContainerRef;
58
-
59
- private componentRef?: any;
60
-
61
- constructor(
62
- @Inject(ViewContainerRef) private viewContainer: ViewContainerRef,
63
- private cdr: ChangeDetectorRef
64
- ) {}
65
-
66
- ngOnInit(): void {
67
- this.renderSlot();
68
- }
69
-
70
- ngOnChanges(changes: SimpleChanges): void {
71
- if (changes['slot']) {
72
- // Slot changed, need to re-render completely
73
- this.renderSlot();
74
- } else if (changes['context'] && this.componentRef) {
75
- // Just context changed, update existing component
76
- this.updateComponentProps();
77
- this.cdr.detectChanges();
78
- } else if (changes['context']) {
79
- // No component ref yet, render the slot
80
- this.renderSlot();
81
- }
82
- }
83
-
84
- isTemplate(value: any): value is TemplateRef<any> {
85
- return value instanceof TemplateRef;
86
- }
87
-
88
- private renderSlot(): void {
89
- // Skip if it's a template (handled by ngTemplateOutlet)
90
- if (this.slot && this.isTemplate(this.slot)) {
91
- this.componentRef = null;
92
- return;
93
- }
94
-
95
- // Clear previous content
96
- this.slotContainer.clear();
97
- this.componentRef = null;
98
-
99
- // Skip if no slot and no default component
100
- if (!this.slot && !this.defaultComponent) {
101
- return;
102
- }
103
-
104
- // Use the utility to render other slot types
105
- if (this.slot || this.defaultComponent) {
106
- this.componentRef = renderSlot(this.slotContainer, {
107
- slot: this.slot,
108
- defaultComponent: this.defaultComponent!,
109
- props: this.context,
110
- outputs: this.outputs
111
- });
112
- }
113
- }
114
-
115
- private updateComponentProps(): void {
116
- if (!this.componentRef || !this.componentRef.instance) {
117
- return;
118
- }
119
-
120
- const props = this.context;
121
-
122
- // Update props using setInput
123
- if (props) {
124
- for (const key in props) {
125
- const value = props[key];
126
- this.componentRef.setInput(key, value);
127
- }
128
- }
129
-
130
- // Trigger change detection
131
- if (this.componentRef.changeDetectorRef) {
132
- this.componentRef.changeDetectorRef.detectChanges();
133
- }
134
- }
135
- }
@@ -1,3 +0,0 @@
1
- export * from './slot.types';
2
- export * from './slot.utils';
3
- export { CopilotSlotComponent } from './copilot-slot.component';
@@ -1,64 +0,0 @@
1
- import { Type, TemplateRef, InjectionToken } from '@angular/core';
2
-
3
- /**
4
- * Represents a value that can be used as a slot override.
5
- * Can be a component type or template reference only.
6
- * @internal - This type is for internal use only
7
- */
8
- export type SlotValue<T = any> =
9
- | Type<T>
10
- | TemplateRef<T>;
11
-
12
- /**
13
- * Configuration for a slot
14
- * @internal - This interface is for internal use only
15
- */
16
- export interface SlotConfig<T = any> {
17
- value?: SlotValue<T>;
18
- default?: Type<T>;
19
- }
20
-
21
- /**
22
- * Context passed to slot templates
23
- */
24
- export interface SlotContext<T = any> {
25
- $implicit: T;
26
- props?: Partial<T>;
27
- [key: string]: any;
28
- }
29
-
30
- /**
31
- * Slot registry entry
32
- * @internal - This interface is for internal use only
33
- */
34
- export interface SlotRegistryEntry<T = any> {
35
- component?: Type<T>;
36
- template?: TemplateRef<T>;
37
- }
38
-
39
- /**
40
- * Options for rendering a slot
41
- */
42
- export interface RenderSlotOptions<T = any> {
43
- slot?: SlotValue<T>;
44
- defaultComponent: Type<T>;
45
- props?: Partial<T>;
46
- injector?: any;
47
- outputs?: Record<string, (event: any) => void>;
48
- }
49
-
50
- /**
51
- * Injection token for slot configuration
52
- */
53
- export const SLOT_CONFIG = new InjectionToken<ReadonlyMap<string, SlotRegistryEntry>>('SLOT_CONFIG');
54
-
55
- /**
56
- * Type for components with slots
57
- */
58
- export type WithSlots<S extends Record<string, Type<any>>, Rest = object> = {
59
- [K in keyof S as `${string & K}Component`]?: Type<any>;
60
- } & {
61
- [K in keyof S as `${string & K}Template`]?: TemplateRef<any>;
62
- } & {
63
- [K in keyof S as `${string & K}Class`]?: string;
64
- } & Rest;
@@ -1,289 +0,0 @@
1
- import {
2
- Type,
3
- TemplateRef,
4
- ViewContainerRef,
5
- ComponentRef,
6
- EmbeddedViewRef,
7
- Injector,
8
- inject
9
- } from '@angular/core';
10
- import { SlotValue, RenderSlotOptions, SlotRegistryEntry, SLOT_CONFIG } from './slot.types';
11
-
12
- /**
13
- * Renders a slot value into a ViewContainerRef.
14
- * This is the core utility for slot rendering.
15
- *
16
- * @param viewContainer - The ViewContainerRef to render into
17
- * @param options - Options for rendering the slot
18
- * @returns The created component or embedded view reference
19
- *
20
- * @example
21
- * ```typescript
22
- * export class MyComponent {
23
- * @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
24
- *
25
- * renderButton() {
26
- * renderSlot(this.container, {
27
- * slot: this.buttonOverride,
28
- * defaultComponent: DefaultButton,
29
- * props: { text: 'Click me' },
30
- * outputs: { click: (event) => this.handleClick(event) }
31
- * });
32
- * }
33
- * }
34
- * ```
35
- */
36
- export function renderSlot<T = any>(
37
- viewContainer: ViewContainerRef,
38
- options: RenderSlotOptions<T>
39
- ): ComponentRef<T> | EmbeddedViewRef<T> | null {
40
- const { slot, defaultComponent, props, injector, outputs } = options;
41
-
42
- viewContainer.clear();
43
-
44
- const effectiveSlot = slot ?? defaultComponent;
45
- const effectiveInjector = injector ?? viewContainer.injector;
46
-
47
- if (effectiveSlot instanceof TemplateRef) {
48
- // TemplateRef: render template
49
- return viewContainer.createEmbeddedView(effectiveSlot, {
50
- $implicit: props ?? {},
51
- props: props ?? {}
52
- } as any);
53
- } else if (isComponentType(effectiveSlot)) {
54
- // Component type - wrap in try/catch for safety
55
- try {
56
- return createComponent(
57
- viewContainer,
58
- effectiveSlot as Type<T>,
59
- props,
60
- effectiveInjector,
61
- outputs
62
- );
63
- } catch (error) {
64
- console.warn('Failed to create component:', effectiveSlot, error);
65
- // Fall through to default component
66
- }
67
- }
68
-
69
- // Default: render default component if provided
70
- return defaultComponent ? createComponent(
71
- viewContainer,
72
- defaultComponent,
73
- props,
74
- effectiveInjector,
75
- outputs
76
- ) : null;
77
- }
78
-
79
- /**
80
- * Creates a component and applies properties.
81
- */
82
- function createComponent<T>(
83
- viewContainer: ViewContainerRef,
84
- component: Type<T>,
85
- props?: Partial<T>,
86
- injector?: Injector,
87
- outputs?: Record<string, (event: any) => void>
88
- ): ComponentRef<T> {
89
- const componentRef = viewContainer.createComponent(component, {
90
- injector
91
- });
92
-
93
- if (props) {
94
- // Apply props using setInput
95
- for (const key in props) {
96
- const value = props[key];
97
- componentRef.setInput(key, value);
98
- }
99
- }
100
-
101
- if (outputs) {
102
- // Wire up output event handlers with proper cleanup
103
- const instance = componentRef.instance as any;
104
- const subscriptions: any[] = [];
105
-
106
- for (const [eventName, handler] of Object.entries(outputs)) {
107
- if (instance[eventName]?.subscribe) {
108
- const subscription = instance[eventName].subscribe(handler);
109
- subscriptions.push(subscription);
110
- }
111
- }
112
-
113
- // Register cleanup on component destroy
114
- componentRef.onDestroy(() => {
115
- subscriptions.forEach(sub => sub.unsubscribe());
116
- });
117
- }
118
-
119
- // Trigger change detection
120
- componentRef.changeDetectorRef.detectChanges();
121
-
122
- return componentRef;
123
- }
124
-
125
-
126
- /**
127
- * Checks if a value is a component type.
128
- * Simplified check - rely on try/catch for actual validation.
129
- */
130
- export function isComponentType(value: any): boolean {
131
- // Arrow functions and regular functions without a prototype are not components
132
- return typeof value === 'function' && !!value.prototype;
133
- }
134
-
135
- /**
136
- * Checks if a value is a valid slot value.
137
- */
138
- export function isSlotValue(value: any): value is SlotValue {
139
- return value instanceof TemplateRef || isComponentType(value);
140
- }
141
-
142
- /**
143
- * Normalizes a slot value to a consistent format.
144
- */
145
- export function normalizeSlotValue<T = any>(
146
- value: SlotValue<T> | undefined,
147
- defaultComponent: Type<T> | undefined
148
- ): SlotRegistryEntry<T> {
149
- if (!value) {
150
- return { component: defaultComponent };
151
- }
152
-
153
- if (value instanceof TemplateRef) {
154
- return { template: value };
155
- }
156
-
157
- if (isComponentType(value)) {
158
- return { component: value as Type<T> };
159
- }
160
-
161
- return { component: defaultComponent };
162
- }
163
-
164
- /**
165
- * Creates a slot configuration map for a component.
166
- *
167
- * @example
168
- * ```typescript
169
- * const slots = createSlotConfig({
170
- * sendButton: CustomSendButton,
171
- * toolbar: 'custom-toolbar-class',
172
- * footer: footerTemplate
173
- * }, {
174
- * sendButton: DefaultSendButton,
175
- * toolbar: DefaultToolbar,
176
- * footer: DefaultFooter
177
- * });
178
- * ```
179
- */
180
- export function createSlotConfig<T extends Record<string, Type<any>>>(
181
- overrides: Partial<Record<keyof T, SlotValue>>,
182
- defaults: T
183
- ): Map<keyof T, SlotRegistryEntry> {
184
- const config = new Map<keyof T, SlotRegistryEntry>();
185
-
186
- for (const key in defaults) {
187
- const override = overrides[key];
188
- const defaultComponent = defaults[key];
189
- config.set(key, normalizeSlotValue(override, defaultComponent));
190
- }
191
-
192
- return config;
193
- }
194
-
195
- /**
196
- * Provides slot configuration to child components via DI.
197
- *
198
- * @example
199
- * ```typescript
200
- * @Component({
201
- * providers: [
202
- * provideSlots({
203
- * sendButton: CustomSendButton,
204
- * toolbar: CustomToolbar
205
- * })
206
- * ]
207
- * })
208
- * ```
209
- */
210
- export function provideSlots(slots: Record<string, Type<any>>) {
211
- const slotMap = new Map<string, SlotRegistryEntry>();
212
-
213
- // Only accept component types in DI (templates lack view context)
214
- for (const [key, value] of Object.entries(slots)) {
215
- if (isComponentType(value)) {
216
- slotMap.set(key, { component: value as Type<any> });
217
- }
218
- }
219
-
220
- return {
221
- provide: SLOT_CONFIG,
222
- useValue: slotMap
223
- };
224
- }
225
-
226
- /**
227
- * Gets slot configuration from DI.
228
- * Must be called within an injection context.
229
- *
230
- * @example
231
- * ```typescript
232
- * export class MyComponent {
233
- * slots = getSlotConfig();
234
- *
235
- * ngOnInit() {
236
- * const sendButton = this.slots?.get('sendButton');
237
- * }
238
- * }
239
- * ```
240
- */
241
- export function getSlotConfig(): ReadonlyMap<string, SlotRegistryEntry> | null {
242
- return inject(SLOT_CONFIG, { optional: true });
243
- }
244
-
245
- /**
246
- * Creates a render function for a specific slot.
247
- * Useful for creating reusable slot renderers.
248
- *
249
- * @example
250
- * ```typescript
251
- * const renderSendButton = createSlotRenderer(
252
- * DefaultSendButton,
253
- * 'sendButton'
254
- * );
255
- *
256
- * // Later in template
257
- * renderSendButton(this.viewContainer, this.sendButtonOverride);
258
- * ```
259
- */
260
- export function createSlotRenderer<T>(
261
- defaultComponent: Type<T>,
262
- slotName?: string
263
- ) {
264
- // Get config in the injection context when the renderer is created
265
- const config = slotName ? getSlotConfig() : null;
266
-
267
- return (
268
- viewContainer: ViewContainerRef,
269
- slot?: SlotValue<T>,
270
- props?: Partial<T>,
271
- outputs?: Record<string, (event: any) => void>
272
- ) => {
273
- // Check DI for overrides if slot name provided
274
- if (slotName && !slot && config) {
275
- const entry = config.get(slotName);
276
- if (entry) {
277
- if (entry.component) slot = entry.component;
278
- else if (entry.template) slot = entry.template;
279
- }
280
- }
281
-
282
- return renderSlot(viewContainer, {
283
- slot,
284
- defaultComponent,
285
- props,
286
- outputs
287
- });
288
- };
289
- }
package/src/lib/utils.ts DELETED
@@ -1,10 +0,0 @@
1
- import { clsx, type ClassValue } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
-
4
- /**
5
- * Utility function to merge Tailwind CSS classes
6
- * Combines clsx for conditional classes and tailwind-merge for proper Tailwind class merging
7
- */
8
- export function cn(...inputs: ClassValue[]) {
9
- return twMerge(clsx(inputs));
10
- }
package/src/public-api.ts DELETED
@@ -1 +0,0 @@
1
- export * from './index';
@@ -1,181 +0,0 @@
1
- import { Injectable, ElementRef, NgZone, OnDestroy } from '@angular/core';
2
- import { Observable, Subject, BehaviorSubject } from 'rxjs';
3
- import { debounceTime, takeUntil, distinctUntilChanged } from 'rxjs/operators';
4
-
5
- export interface ResizeState {
6
- width: number;
7
- height: number;
8
- isResizing: boolean;
9
- }
10
-
11
- @Injectable({
12
- providedIn: 'root'
13
- })
14
- export class ResizeObserverService implements OnDestroy {
15
- private destroy$ = new Subject<void>();
16
- private observers = new Map<HTMLElement, ResizeObserver>();
17
- private resizeStates = new Map<HTMLElement, BehaviorSubject<ResizeState>>();
18
- private resizeTimeouts = new Map<HTMLElement, number>();
19
-
20
- constructor(private ngZone: NgZone) {}
21
-
22
- /**
23
- * Observe element resize with debouncing and resizing state
24
- * @param element Element to observe
25
- * @param debounceMs Debounce time (default 250ms)
26
- * @param resizingDurationMs How long to show "isResizing" state (default 250ms)
27
- */
28
- observeElement(
29
- element: ElementRef<HTMLElement> | HTMLElement,
30
- debounceMs: number = 0,
31
- resizingDurationMs: number = 250
32
- ): Observable<ResizeState> {
33
- const el = element instanceof ElementRef ? element.nativeElement : element;
34
-
35
- // Return existing observer if already observing
36
- if (this.resizeStates.has(el)) {
37
- return this.resizeStates.get(el)!.asObservable();
38
- }
39
-
40
- // Create new subject for this element
41
- const resizeState$ = new BehaviorSubject<ResizeState>({
42
- width: el.offsetWidth,
43
- height: el.offsetHeight,
44
- isResizing: false
45
- });
46
-
47
- this.resizeStates.set(el, resizeState$);
48
-
49
- // Create ResizeObserver
50
- const resizeObserver = new ResizeObserver((entries) => {
51
- if (entries.length === 0) return;
52
-
53
- const entry = entries[0];
54
- if (!entry) return;
55
-
56
- const { width, height } = entry.contentRect;
57
-
58
- this.ngZone.run(() => {
59
- // Clear existing timeout
60
- const existingTimeout = this.resizeTimeouts.get(el);
61
- if (existingTimeout) {
62
- clearTimeout(existingTimeout);
63
- }
64
-
65
- // Update state with isResizing = true
66
- resizeState$.next({
67
- width,
68
- height,
69
- isResizing: true
70
- });
71
-
72
- // Set timeout to clear isResizing flag
73
- if (resizingDurationMs > 0) {
74
- const timeout = window.setTimeout(() => {
75
- resizeState$.next({
76
- width,
77
- height,
78
- isResizing: false
79
- });
80
- this.resizeTimeouts.delete(el);
81
- }, resizingDurationMs);
82
-
83
- this.resizeTimeouts.set(el, timeout);
84
- } else {
85
- // If no duration, immediately set isResizing to false
86
- resizeState$.next({
87
- width,
88
- height,
89
- isResizing: false
90
- });
91
- }
92
- });
93
- });
94
-
95
- // Start observing
96
- resizeObserver.observe(el);
97
- this.observers.set(el, resizeObserver);
98
-
99
- // Return observable with debouncing if specified
100
- const observable = resizeState$.asObservable().pipe(
101
- debounceMs > 0 ? debounceTime(debounceMs) : (source) => source,
102
- distinctUntilChanged((a, b) =>
103
- a.width === b.width &&
104
- a.height === b.height &&
105
- a.isResizing === b.isResizing
106
- ),
107
- takeUntil(this.destroy$)
108
- );
109
-
110
- return observable;
111
- }
112
-
113
- /**
114
- * Stop observing an element
115
- * @param element Element to stop observing
116
- */
117
- unobserve(element: ElementRef<HTMLElement> | HTMLElement): void {
118
- const el = element instanceof ElementRef ? element.nativeElement : element;
119
-
120
- // Clear timeout if exists
121
- const timeout = this.resizeTimeouts.get(el);
122
- if (timeout) {
123
- clearTimeout(timeout);
124
- this.resizeTimeouts.delete(el);
125
- }
126
-
127
- // Disconnect observer
128
- const observer = this.observers.get(el);
129
- if (observer) {
130
- observer.disconnect();
131
- this.observers.delete(el);
132
- }
133
-
134
- // Complete and remove subject
135
- const subject = this.resizeStates.get(el);
136
- if (subject) {
137
- subject.complete();
138
- this.resizeStates.delete(el);
139
- }
140
- }
141
-
142
- /**
143
- * Get current size of element
144
- * @param element Element to measure
145
- */
146
- getCurrentSize(element: ElementRef<HTMLElement> | HTMLElement): { width: number; height: number } {
147
- const el = element instanceof ElementRef ? element.nativeElement : element;
148
- return {
149
- width: el.offsetWidth,
150
- height: el.offsetHeight
151
- };
152
- }
153
-
154
- /**
155
- * Get current resize state of element
156
- * @param element Element to check
157
- */
158
- getCurrentState(element: ElementRef<HTMLElement> | HTMLElement): ResizeState | null {
159
- const el = element instanceof ElementRef ? element.nativeElement : element;
160
- const subject = this.resizeStates.get(el);
161
- return subject ? subject.value : null;
162
- }
163
-
164
- ngOnDestroy(): void {
165
- // Clear all timeouts
166
- this.resizeTimeouts.forEach(timeout => clearTimeout(timeout));
167
- this.resizeTimeouts.clear();
168
-
169
- // Disconnect all observers
170
- this.observers.forEach(observer => observer.disconnect());
171
- this.observers.clear();
172
-
173
- // Complete all subjects
174
- this.resizeStates.forEach(subject => subject.complete());
175
- this.resizeStates.clear();
176
-
177
- // Complete destroy subject
178
- this.destroy$.next();
179
- this.destroy$.complete();
180
- }
181
- }