@bbki.ng/bb-msg-history 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/components/bb-custom-avatar.d.ts +20 -0
  2. package/dist/components/bb-custom-avatar.js +145 -0
  3. package/dist/components/bb-letter-avatar.d.ts +14 -0
  4. package/dist/components/bb-letter-avatar.js +61 -0
  5. package/dist/components/bb-loading-overlay.d.ts +14 -0
  6. package/dist/components/bb-loading-overlay.js +89 -0
  7. package/dist/components/bb-message-bubble.d.ts +19 -0
  8. package/dist/components/bb-message-bubble.js +116 -0
  9. package/dist/components/bb-message.d.ts +27 -0
  10. package/dist/components/bb-message.js +174 -0
  11. package/dist/components/bb-msg-history.d.ts +111 -0
  12. package/dist/components/bb-msg-history.js +473 -0
  13. package/dist/components/bb-scroll-button.d.ts +16 -0
  14. package/dist/components/bb-scroll-button.js +161 -0
  15. package/dist/components/bb-timestamp.d.ts +15 -0
  16. package/dist/components/bb-timestamp.js +59 -0
  17. package/dist/components/index.d.ts +7 -0
  18. package/dist/components/index.js +7 -0
  19. package/dist/const/styles.js +0 -33
  20. package/dist/contexts/author-context.d.ts +8 -0
  21. package/dist/contexts/author-context.js +6 -0
  22. package/dist/controllers/scroll-controller.d.ts +52 -0
  23. package/dist/controllers/scroll-controller.js +138 -0
  24. package/dist/core/renderer.js +1 -9
  25. package/dist/parsers/base.d.ts +21 -0
  26. package/dist/parsers/base.js +1 -0
  27. package/dist/parsers/default-parser.d.ts +10 -0
  28. package/dist/parsers/default-parser.js +40 -0
  29. package/dist/parsers/index.d.ts +2 -0
  30. package/dist/parsers/index.js +1 -0
  31. package/dist/utils/message-builder.d.ts +0 -4
  32. package/dist/utils/message-builder.js +0 -15
  33. package/dist/utils/tooltip.d.ts +11 -2
  34. package/dist/utils/tooltip.js +56 -13
  35. package/package.json +1 -1
  36. package/src/const/styles.ts +0 -33
  37. package/src/core/renderer.ts +1 -11
  38. package/src/utils/message-builder.ts +0 -15
  39. package/src/utils/tooltip.ts +0 -16
@@ -1,17 +1,60 @@
1
1
  /**
2
- * Setup dynamic tooltip positioning
3
- * Tooltips are positioned fixed to avoid overflow clipping from parent containers
2
+ * Tooltip utilities for avatar hover effects
3
+ *
4
+ * Tooltips use position: fixed to escape overflow clipping from scrollable containers.
5
+ * Position is calculated on mouseenter and updated on scroll to prevent staleness.
6
+ */
7
+ /**
8
+ * Position a tooltip relative to its avatar wrapper
9
+ * Centers horizontally and places above the avatar
10
+ */
11
+ function positionTooltip(wrapper, tooltip) {
12
+ const rect = wrapper.getBoundingClientRect();
13
+ const tooltipRect = tooltip.getBoundingClientRect();
14
+ // Center horizontally relative to viewport
15
+ let left = rect.left + rect.width / 2 - tooltipRect.width / 2;
16
+ // Viewport boundary check - keep tooltip within viewport
17
+ const padding = 8;
18
+ left = Math.max(padding, Math.min(left, window.innerWidth - tooltipRect.width - padding));
19
+ // Position above avatar
20
+ const top = rect.top - tooltipRect.height - 8;
21
+ tooltip.style.left = `${left}px`;
22
+ tooltip.style.top = `${top}px`;
23
+ }
24
+ /**
25
+ * Setup tooltip positioning for a single avatar wrapper element
26
+ */
27
+ export function setupTooltipForElement(wrapper) {
28
+ const tooltip = wrapper.querySelector('.avatar-tooltip');
29
+ if (!tooltip)
30
+ return;
31
+ let scrollContainer = null;
32
+ const handleMouseEnter = () => {
33
+ // Find the scrollable container (if any)
34
+ scrollContainer = wrapper.closest('.history');
35
+ // Initial position
36
+ positionTooltip(wrapper, tooltip);
37
+ // Add scroll listener to update position during scroll
38
+ if (scrollContainer) {
39
+ scrollContainer.addEventListener('scroll', handleScroll, { passive: true });
40
+ }
41
+ };
42
+ const handleMouseLeave = () => {
43
+ // Remove scroll listener when not hovering
44
+ if (scrollContainer) {
45
+ scrollContainer.removeEventListener('scroll', handleScroll);
46
+ scrollContainer = null;
47
+ }
48
+ };
49
+ const handleScroll = () => {
50
+ positionTooltip(wrapper, tooltip);
51
+ };
52
+ wrapper.addEventListener('mouseenter', handleMouseEnter);
53
+ wrapper.addEventListener('mouseleave', handleMouseLeave);
54
+ }
55
+ /**
56
+ * Setup tooltips for all avatar wrappers in the shadow root
4
57
  */
5
58
  export function setupTooltips(shadowRoot) {
6
- shadowRoot.querySelectorAll('.avatar-wrapper').forEach(wrapper => {
7
- wrapper.addEventListener('mouseenter', () => {
8
- const tooltip = wrapper.querySelector('.avatar-tooltip');
9
- if (!tooltip)
10
- return;
11
- const rect = wrapper.getBoundingClientRect();
12
- const tooltipRect = tooltip.getBoundingClientRect();
13
- tooltip.style.left = `${rect.left + rect.width / 2 - tooltipRect.width / 2}px`;
14
- tooltip.style.top = `${rect.top - tooltipRect.height - 8}px`;
15
- });
16
- });
59
+ shadowRoot.querySelectorAll('.avatar-wrapper').forEach(setupTooltipForElement);
17
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/bb-msg-history",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "A chat-style message history web component",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -160,7 +160,6 @@ export const MAIN_STYLES = `
160
160
  background: #ffffff;
161
161
  border-radius: 50%;
162
162
  overflow: hidden;
163
- cursor: help;
164
163
  }
165
164
 
166
165
  .avatar-wrapper--hidden {
@@ -183,38 +182,6 @@ export const MAIN_STYLES = `
183
182
  height: 100%;
184
183
  }
185
184
 
186
- /* Hover tooltip */
187
- .avatar-tooltip {
188
- position: fixed;
189
- padding: 0.25rem 0.5rem;
190
- background: ${THEME.gray[800]};
191
- color: ${THEME.gray[50]};
192
- font-size: 0.75rem;
193
- border-radius: 0.25rem;
194
- white-space: nowrap;
195
- opacity: 0;
196
- visibility: hidden;
197
- pointer-events: none;
198
- z-index: 10;
199
- font-weight: 500;
200
- letter-spacing: 0.02em;
201
- }
202
-
203
- .avatar-tooltip::after {
204
- content: '';
205
- position: absolute;
206
- top: calc(100% - 1px);
207
- left: 50%;
208
- transform: translateX(-50%);
209
- border: 4px solid transparent;
210
- border-top-color: ${THEME.gray[800]};
211
- }
212
-
213
- .avatar-wrapper:hover .avatar-tooltip {
214
- opacity: 1;
215
- visibility: visible;
216
- }
217
-
218
185
  /* Message content area */
219
186
  .msg-content {
220
187
  display: flex;
@@ -2,9 +2,8 @@ import type { AuthorOptions, Message } from '../types/index.js';
2
2
  import type { ProcessedMessage } from './message-processor.js';
3
3
  import { EMPTY_STYLES, LOADING_STYLES, MAIN_STYLES } from '../const/styles.js';
4
4
  import { resolveAuthorConfig } from '../utils/author-resolver.js';
5
- import { buildMessageRowHtml, setupTooltipForElement } from '../utils/message-builder.js';
5
+ import { buildMessageRowHtml } from '../utils/message-builder.js';
6
6
  import { buildScrollButtonHtml } from '../utils/scroll-button.js';
7
- import { setupTooltips } from '../utils/tooltip.js';
8
7
 
9
8
  /**
10
9
  * State for incremental message appending
@@ -149,9 +148,6 @@ export class Renderer {
149
148
  scrollContainer.scrollTop = scrollContainer.scrollHeight;
150
149
  }
151
150
 
152
- // Re-setup tooltips for new content
153
- setupTooltips(this.shadowRoot);
154
-
155
151
  return { wasAtBottom };
156
152
  }
157
153
 
@@ -205,12 +201,6 @@ export class Renderer {
205
201
 
206
202
  container.insertAdjacentHTML('beforeend', msgHtml);
207
203
 
208
- // Setup tooltip for new element
209
- const newWrapper = container.lastElementChild?.querySelector('.avatar-wrapper');
210
- if (newWrapper) {
211
- setupTooltipForElement(newWrapper);
212
- }
213
-
214
204
  return {
215
205
  success: true,
216
206
  lastAuthor: message.author,
@@ -10,7 +10,6 @@ export function buildAvatarHtml(author: string, config: AuthorConfig, showAvatar
10
10
  <div class="avatar-wrapper ${showAvatar ? '' : 'avatar-wrapper--hidden'}"
11
11
  data-author="${escapeHtml(author)}">
12
12
  <div class="avatar">${config.avatar}</div>
13
- <div class="avatar-tooltip">${escapeHtml(author)}</div>
14
13
  </div>
15
14
  `;
16
15
  }
@@ -68,17 +67,3 @@ export function buildMessageRowHtml(
68
67
  </div>
69
68
  `;
70
69
  }
71
-
72
- /**
73
- * Setup tooltip for a single avatar wrapper element
74
- */
75
- export function setupTooltipForElement(wrapper: Element): void {
76
- wrapper.addEventListener('mouseenter', () => {
77
- const tooltip = wrapper.querySelector('.avatar-tooltip') as HTMLElement;
78
- if (!tooltip) return;
79
- const rect = wrapper.getBoundingClientRect();
80
- const tooltipRect = tooltip.getBoundingClientRect();
81
- tooltip.style.left = `${rect.left + rect.width / 2 - tooltipRect.width / 2}px`;
82
- tooltip.style.top = `${rect.top - tooltipRect.height - 8}px`;
83
- });
84
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Setup dynamic tooltip positioning
3
- * Tooltips are positioned fixed to avoid overflow clipping from parent containers
4
- */
5
- export function setupTooltips(shadowRoot: ShadowRoot): void {
6
- shadowRoot.querySelectorAll('.avatar-wrapper').forEach(wrapper => {
7
- wrapper.addEventListener('mouseenter', () => {
8
- const tooltip = wrapper.querySelector('.avatar-tooltip') as HTMLElement;
9
- if (!tooltip) return;
10
- const rect = wrapper.getBoundingClientRect();
11
- const tooltipRect = tooltip.getBoundingClientRect();
12
- tooltip.style.left = `${rect.left + rect.width / 2 - tooltipRect.width / 2}px`;
13
- tooltip.style.top = `${rect.top - tooltipRect.height - 8}px`;
14
- });
15
- });
16
- }