@adatechnology/conversations-ui 0.8.0 → 0.9.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 (49) hide show
  1. package/dist/{chunk-HMCOTZAX.js → chunk-NSOAUYNE.js} +33 -28
  2. package/dist/chunk-ZSOD5AQT.js +19 -0
  3. package/dist/flows/index.js +4 -2
  4. package/dist/index.js +5 -3
  5. package/dist/participant/index.d.ts +3 -0
  6. package/dist/participant/index.js +536 -276
  7. package/dist/styles.css +99 -23
  8. package/package.json +2 -1
  9. package/src/Tooltip.tsx +5 -12
  10. package/src/TooltipDom.test.tsx +78 -0
  11. package/src/domHarness.test-helper.ts +53 -0
  12. package/src/participant/ParticipantBackButton.tsx +1 -1
  13. package/src/participant/ParticipantBubbleBody.tsx +54 -0
  14. package/src/participant/ParticipantBubbleStatus.tsx +22 -28
  15. package/src/participant/ParticipantChannelBadges.tsx +10 -2
  16. package/src/participant/ParticipantComposer.tsx +1 -1
  17. package/src/participant/ParticipantComposerControls.tsx +2 -0
  18. package/src/participant/ParticipantCopyableToken.tsx +1 -0
  19. package/src/participant/ParticipantFailedMenu.tsx +40 -0
  20. package/src/participant/ParticipantFailedMenuList.tsx +41 -0
  21. package/src/participant/ParticipantMessageBubble.test.tsx +6 -6
  22. package/src/participant/ParticipantMessageBubble.tsx +20 -52
  23. package/src/participant/ParticipantMessageText.test.tsx +1 -1
  24. package/src/participant/ParticipantMessageText.tsx +4 -2
  25. package/src/participant/ParticipantProtocolBadge.tsx +1 -0
  26. package/src/participant/ParticipantScrollToLatest.tsx +1 -1
  27. package/src/participant/ParticipantThread.tsx +23 -12
  28. package/src/participant/ParticipantThreadHeader.test.tsx +1 -1
  29. package/src/participant/failedMenuKeyboard.ts +29 -0
  30. package/src/participant/participantFailedMenuDom.test.tsx +154 -0
  31. package/src/participant/participantFailedMenuState.test.ts +61 -0
  32. package/src/participant/participantFailedMenuState.ts +57 -0
  33. package/src/participant/participantFailedMessage.test.tsx +83 -0
  34. package/src/participant/participantLabels.ts +6 -0
  35. package/src/participant/participantThemeContrast.test.ts +8 -0
  36. package/src/participant/participantTooltips.test.tsx +74 -0
  37. package/src/participant/test-fixtures/thread-avatars-initials.golden.html +1 -1
  38. package/src/participant/test-fixtures/thread-closed.golden.html +1 -1
  39. package/src/participant/test-fixtures/thread-default.golden.html +1 -1
  40. package/src/participant/test-fixtures/thread-error.golden.html +1 -1
  41. package/src/participant/test-fixtures/thread-loading.golden.html +1 -1
  42. package/src/participant/test-fixtures/thread-plain.golden.html +1 -1
  43. package/src/participant/test-fixtures/thread-tail-off.golden.html +1 -1
  44. package/src/participant/useFailedMenuEffects.ts +59 -0
  45. package/src/participant/useParticipantFailedMenu.ts +60 -0
  46. package/src/participant/useSuppressTouchTooltips.ts +38 -0
  47. package/src/styles.css +19 -5
  48. package/src/tooltipLayerRegistry.test.ts +56 -0
  49. package/src/tooltipLayerRegistry.ts +34 -0
package/src/styles.css CHANGED
@@ -1188,7 +1188,7 @@
1188
1188
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
1189
1189
  }
1190
1190
 
1191
- .cv-p-button, .cv-p-chip, .cv-p-row, .cv-p-thread__older, .cv-p-bubble__retry, .cv-p-bubble__action {
1191
+ .cv-p-button, .cv-p-chip, .cv-p-row, .cv-p-thread__older, .cv-p-failed-menu__trigger, .cv-p-failed-menu__item {
1192
1192
  font: inherit; color: inherit; cursor: pointer;
1193
1193
  }
1194
1194
  .cv-p-button {
@@ -1300,15 +1300,27 @@
1300
1300
  }
1301
1301
  .cv-p-bubble__author { display: block; flex: 1 0 100%; margin-bottom: 0.15rem; font: 0.65rem 'SFMono-Regular', Consolas, monospace; text-transform: uppercase; color: var(--cv-p-i-accent); }
1302
1302
  .cv-p-bubble > .cv-p-text { flex: 0 1 auto; min-width: 0; margin-inline-end: auto; }
1303
- .cv-p-bubble > .cv-p-attachment, .cv-p-bubble > .cv-p-bubble__actions { flex: 1 0 100%; }
1303
+ .cv-p-bubble > .cv-p-attachment { flex: 1 0 100%; }
1304
1304
  .cv-p-bubble__meta { display: flex; flex: 0 0 auto; justify-content: flex-end; align-items: center; gap: 0.35rem; margin-inline-start: auto; margin-top: 0.25rem; font: 0.65rem 'SFMono-Regular', Consolas, monospace; color: var(--cv-p-i-text-muted); }
1305
1305
  .cv-p-bubble__status { display: inline-flex; align-items: center; gap: 0.25rem; }
1306
1306
  .cv-p-bubble .cv-status-ticks--queued, .cv-p-bubble .cv-status-ticks--sent, .cv-p-bubble .cv-status-ticks--delivered { color: var(--cv-p-i-tick); }
1307
1307
  .cv-p-bubble .cv-status-ticks--read { color: var(--cv-p-i-tick-read); }
1308
1308
  .cv-p-bubble .cv-status-ticks--failed { color: var(--cv-p-i-danger); }
1309
- .cv-p-bubble__retry { min-height: var(--cv-p-i-touch); display: inline-flex; align-items: center; gap: 0.25rem; padding: 0 0.5rem; background: transparent; border: 1px solid var(--cv-p-i-danger); color: var(--cv-p-i-danger); border-radius: var(--cv-p-i-radius); font-size: 0.7rem; }
1310
- .cv-p-bubble__actions { display: inline-flex; gap: 0.5rem; margin-top: 0.25rem; }
1311
- .cv-p-bubble__action { min-height: var(--cv-p-i-touch); display: inline-flex; align-items: center; padding: 0 0.75rem; background: transparent; border: 1px solid var(--cv-p-i-border); color: inherit; border-radius: var(--cv-p-i-radius); font-size: 0.7rem; }
1309
+ @supports (color: color-mix(in srgb, red, blue)) {
1310
+ .cv-p-bubble--failed { border-color: color-mix(in srgb, var(--cv-p-i-danger) 55%, var(--cv-p-i-border)); --cv-p-tail-edge: color-mix(in srgb, var(--cv-p-i-danger) 55%, var(--cv-p-i-border)); }
1311
+ .cv-p-bubble--mine.cv-p-bubble--failed { border-color: color-mix(in srgb, var(--cv-p-i-danger) 55%, var(--cv-p-i-accent)); --cv-p-tail-edge: color-mix(in srgb, var(--cv-p-i-danger) 55%, var(--cv-p-i-accent)); }
1312
+ }
1313
+ .cv-p-failed-retry { position: absolute; top: 50%; right: calc(100% + 0.125rem); transform: translateY(-50%); display: grid; place-items: center; width: var(--cv-p-i-touch); height: var(--cv-p-i-touch); padding: 0; background: transparent; border: 0; color: var(--cv-p-i-danger); font: inherit; cursor: pointer; }
1314
+ .cv-p-failed-retry__icon { display: grid; place-items: center; width: 2rem; height: 2rem; border: 1px solid var(--cv-p-i-danger); border-radius: 50%; }
1315
+ .cv-p-failed-retry:hover .cv-p-failed-retry__icon, .cv-p-failed-retry:focus-visible .cv-p-failed-retry__icon { background: var(--cv-p-i-surface-raised); }
1316
+ .cv-p-failed-menu { display: inline-flex; }
1317
+ .cv-p-failed-menu__trigger { position: relative; display: inline-grid; place-items: center; min-width: 1.25rem; height: 0.75rem; padding: 0; background: transparent; border: 0; color: var(--cv-p-i-text-muted); font: inherit; line-height: 0.75rem; cursor: pointer; }
1318
+ .cv-p-failed-menu__trigger::after { content: ''; position: absolute; inset: -0.5rem -0.4rem; }
1319
+ .cv-p-failed-menu__list { position: absolute; right: 0; bottom: calc(100% + 0.25rem); z-index: 2; display: flex; flex-direction: column; min-width: 8rem; background: var(--cv-p-i-surface-raised); border: 1px solid var(--cv-p-i-border); border-radius: var(--cv-p-i-radius); }
1320
+ .cv-p-failed-menu__item { min-height: var(--cv-p-i-touch); padding: 0 0.75rem; background: transparent; border: 0; color: var(--cv-p-i-text); font: 0.7rem 'SFMono-Regular', Consolas, monospace; text-align: start; text-transform: uppercase; letter-spacing: 0.04em; cursor: pointer; }
1321
+ .cv-p-failed-menu__list--below { bottom: auto; top: calc(100% + 0.25rem); }
1322
+ .cv-p-failed-menu__item--danger { color: var(--cv-p-i-danger); }
1323
+ .cv-p-failed-menu__item:hover, .cv-p-failed-menu__item:focus-visible { background: var(--cv-p-i-highlight); }
1312
1324
  .cv-p-attachment { display: flex; flex-direction: column; gap: 0.25rem; margin-top: 0.35rem; color: inherit; }
1313
1325
  .cv-p-attachment--document { flex-direction: row; justify-content: space-between; min-height: var(--cv-p-i-touch); align-items: center; text-decoration: underline; }
1314
1326
  .cv-p-attachment__image { display: block; max-width: 100%; max-height: 16rem; object-fit: contain; }
@@ -1417,6 +1429,8 @@
1417
1429
  .cv-p-filters, .cv-p-quick { mask-image: none; -webkit-mask-image: none; }
1418
1430
  .cv-p-thread__latest-badge { border: 1px solid ButtonText; }
1419
1431
  .cv-p-bubble .cv-status-ticks, .cv-p-copyable__done { color: CanvasText; }
1432
+ .cv-p-failed-menu__list { border-color: CanvasText; }
1433
+ .cv-p-failed-menu__item:focus-visible, .cv-p-failed-menu__item:hover { outline: 2px solid Highlight; }
1420
1434
  }
1421
1435
  @media (forced-colors: active) {
1422
1436
  .cv-p-text__code, .cv-p-text__pre, .cv-p-copyable { border: 1px solid; background: none; }
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+
3
+ import { createTooltipLayerRegistry } from './tooltipLayerRegistry'
4
+
5
+ function track() {
6
+ const events: boolean[] = []
7
+ return { events, notify: (isOwner: boolean) => void events.push(isOwner) }
8
+ }
9
+
10
+ describe('createTooltipLayerRegistry', () => {
11
+ it('makes the first mounted layer the owner and keeps the others quiet', () => {
12
+ const registry = createTooltipLayerRegistry()
13
+ const first = track()
14
+ const second = track()
15
+ registry.register({}, first.notify)
16
+ registry.register({}, second.notify)
17
+
18
+ expect(first.events).toEqual([true])
19
+ expect(second.events).toEqual([])
20
+ })
21
+
22
+ it('hands the ownership to the next mounted layer when the owner unmounts', () => {
23
+ const registry = createTooltipLayerRegistry()
24
+ const first = track()
25
+ const second = track()
26
+ const third = track()
27
+ const unmountFirst = registry.register({}, first.notify)
28
+ registry.register({}, second.notify)
29
+ registry.register({}, third.notify)
30
+
31
+ unmountFirst()
32
+
33
+ expect(second.events).toEqual([true])
34
+ expect(third.events).toEqual([])
35
+ })
36
+
37
+ it('does not disturb the owner when a waiting layer unmounts', () => {
38
+ const registry = createTooltipLayerRegistry()
39
+ const first = track()
40
+ const second = track()
41
+ registry.register({}, first.notify)
42
+ registry.register({}, second.notify)()
43
+
44
+ expect(first.events).toEqual([true])
45
+ expect(second.events).toEqual([])
46
+ })
47
+
48
+ it('lets a layer mounted after everyone left become the owner again', () => {
49
+ const registry = createTooltipLayerRegistry()
50
+ registry.register({}, () => undefined)()
51
+ const late = track()
52
+ registry.register({}, late.notify)
53
+
54
+ expect(late.events).toEqual([true])
55
+ })
56
+ })
@@ -0,0 +1,34 @@
1
+ export type TooltipLayerRegistry = {
2
+ /** Mounts a layer; the callback hears when it becomes (true) or stops being (false) the one that draws. */
3
+ readonly register: (layer: object, onOwnershipChange: (isOwner: boolean) => void) => () => void
4
+ }
5
+
6
+ /** Layers in mount order: one draws at a time, and when it unmounts the next mounted one takes over. */
7
+ export function createTooltipLayerRegistry(): TooltipLayerRegistry {
8
+ const layers = new Map<object, (isOwner: boolean) => void>()
9
+ let owner: object | undefined
10
+
11
+ function handOff(): void {
12
+ const next = layers.entries().next()
13
+ if (next.done) {
14
+ owner = undefined
15
+ return
16
+ }
17
+ const [layer, onOwnershipChange] = next.value
18
+ owner = layer
19
+ onOwnershipChange(true)
20
+ }
21
+
22
+ return {
23
+ register(layer, onOwnershipChange) {
24
+ layers.set(layer, onOwnershipChange)
25
+ if (owner === undefined) handOff()
26
+ return () => {
27
+ layers.delete(layer)
28
+ if (owner !== layer) return
29
+ owner = undefined
30
+ handOff()
31
+ }
32
+ },
33
+ }
34
+ }