@copilotkitnext/angular 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) 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/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
  7. package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
  8. package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
  9. package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
  10. package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
  11. package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
  12. package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
  13. package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
  14. package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
  15. package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
  16. package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
  17. package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
  18. package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
  19. package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
  20. package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
  21. package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
  22. package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
  23. package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
  24. package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
  25. package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
  26. package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
  27. package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
  28. package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
  29. package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
  30. package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
  31. package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
  32. package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
  33. package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
  34. package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
  35. package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
  36. package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
  37. package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
  38. package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
  39. package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
  40. package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
  41. package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
  42. package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
  43. package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
  44. package/dist/esm2022/core/copilotkit.service.mjs +430 -0
  45. package/dist/esm2022/core/copilotkit.types.mjs +12 -0
  46. package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
  47. package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
  48. package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
  49. package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
  50. package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +130 -0
  51. package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +266 -0
  52. package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
  53. package/dist/esm2022/index.mjs +70 -0
  54. package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
  55. package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
  56. package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
  57. package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
  58. package/dist/esm2022/lib/utils.mjs +10 -0
  59. package/dist/esm2022/services/resize-observer.service.mjs +152 -0
  60. package/dist/esm2022/services/scroll-position.service.mjs +124 -0
  61. package/dist/esm2022/types/frontend-tool.mjs +2 -0
  62. package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
  63. package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
  64. package/dist/esm2022/utils/agent.utils.mjs +204 -0
  65. package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
  66. package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
  67. package/dist/esm2022/utils/frontend-tool.utils.mjs +228 -0
  68. package/dist/esm2022/utils/human-in-the-loop.utils.mjs +296 -0
  69. package/dist/fesm2022/copilotkitnext-angular.mjs +163 -164
  70. package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
  71. package/package.json +21 -18
  72. package/vitest.config.mts +32 -21
  73. package/.turbo/turbo-build.log +0 -38
  74. package/.turbo/turbo-check-types.log +0 -0
  75. package/.turbo/turbo-test.log +0 -71
  76. package/ng-package.json +0 -19
  77. package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
  78. package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
  79. package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
  80. package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
  81. package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
  82. package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
  83. package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
  84. package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
  85. package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
  86. package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
  87. package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
  88. package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
  89. package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
  90. package/src/components/chat/copilot-chat-input.component.ts +0 -512
  91. package/src/components/chat/copilot-chat-input.types.ts +0 -148
  92. package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
  93. package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
  94. package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
  95. package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
  96. package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
  97. package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
  98. package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
  99. package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
  100. package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
  101. package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
  102. package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
  103. package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
  104. package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
  105. package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
  106. package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
  107. package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
  108. package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
  109. package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
  110. package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
  111. package/src/components/chat/copilot-chat-view.component.ts +0 -420
  112. package/src/components/chat/copilot-chat-view.types.ts +0 -52
  113. package/src/components/chat/copilot-chat.component.ts +0 -232
  114. package/src/components/copilotkit-tool-render.component.ts +0 -169
  115. package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
  116. package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
  117. package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
  118. package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
  119. package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
  120. package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
  121. package/src/core/copilotkit.providers.ts +0 -59
  122. package/src/core/copilotkit.service.ts +0 -542
  123. package/src/core/copilotkit.types.ts +0 -132
  124. package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
  125. package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
  126. package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
  127. package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
  128. package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
  129. package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
  130. package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
  131. package/src/directives/copilotkit-agent-context.directive.ts +0 -138
  132. package/src/directives/copilotkit-agent.directive.ts +0 -225
  133. package/src/directives/copilotkit-chat-config.directive.ts +0 -241
  134. package/src/directives/copilotkit-config.directive.ts +0 -81
  135. package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
  136. package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
  137. package/src/directives/stick-to-bottom.directive.ts +0 -204
  138. package/src/index.ts +0 -105
  139. package/src/lib/directives/tooltip.directive.ts +0 -292
  140. package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
  141. package/src/lib/slots/copilot-slot.component.ts +0 -135
  142. package/src/lib/slots/index.ts +0 -3
  143. package/src/lib/slots/slot.types.ts +0 -64
  144. package/src/lib/slots/slot.utils.ts +0 -289
  145. package/src/lib/utils.ts +0 -10
  146. package/src/public-api.ts +0 -1
  147. package/src/services/resize-observer.service.ts +0 -181
  148. package/src/services/scroll-position.service.ts +0 -169
  149. package/src/styles/globals.css +0 -266
  150. package/src/styles/index.css +0 -3
  151. package/src/test-setup.ts +0 -15
  152. package/src/testing/index.ts +0 -3
  153. package/src/testing/testing.utils.ts +0 -248
  154. package/src/types/frontend-tool.ts +0 -44
  155. package/src/types/human-in-the-loop.ts +0 -52
  156. package/src/utils/__tests__/agent.utils.spec.ts +0 -234
  157. package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
  158. package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
  159. package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
  160. package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
  161. package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
  162. package/src/utils/agent-context.utils.ts +0 -133
  163. package/src/utils/agent.utils.ts +0 -239
  164. package/src/utils/chat-config.utils.ts +0 -221
  165. package/src/utils/copilotkit.utils.ts +0 -20
  166. package/src/utils/frontend-tool.utils.ts +0 -266
  167. package/src/utils/human-in-the-loop.utils.ts +0 -359
  168. 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
- }