@internetarchive/elements 0.2.14-webdev-9022.0 → 0.4.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 (181) hide show
  1. package/dist/src/elements/ia-audio-element/ia-audio-element.d.ts +72 -0
  2. package/dist/src/elements/ia-audio-element/ia-audio-element.d.ts.map +1 -0
  3. package/dist/src/elements/ia-audio-element/ia-audio-element.js +173 -0
  4. package/dist/src/elements/ia-audio-element/ia-audio-element.js.map +1 -0
  5. package/dist/src/elements/ia-audio-element/models.d.ts +13 -0
  6. package/dist/src/elements/ia-audio-element/models.d.ts.map +1 -0
  7. package/dist/src/elements/ia-audio-element/models.js +2 -0
  8. package/dist/src/elements/ia-audio-element/models.js.map +1 -0
  9. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.d.ts +3 -0
  10. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.d.ts.map +1 -0
  11. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.js +28 -0
  12. package/dist/src/elements/ia-expandable-search-bar/assets/clear-results.js.map +1 -0
  13. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.d.ts +3 -0
  14. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.d.ts.map +1 -0
  15. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.js +26 -0
  16. package/dist/src/elements/ia-expandable-search-bar/assets/disclosure.js.map +1 -0
  17. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.d.ts +3 -0
  18. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.d.ts.map +1 -0
  19. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.js +22 -0
  20. package/dist/src/elements/ia-expandable-search-bar/assets/magnifying-glass.js.map +1 -0
  21. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.d.ts +53 -0
  22. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.d.ts.map +1 -0
  23. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.js +352 -0
  24. package/dist/src/elements/ia-expandable-search-bar/ia-expandable-search-bar.js.map +1 -0
  25. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.d.ts +19 -0
  26. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.d.ts.map +1 -0
  27. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.js +115 -0
  28. package/dist/src/elements/ia-expandable-search-bar/ia-quick-search.js.map +1 -0
  29. package/dist/src/elements/ia-expandable-search-bar/models.d.ts +13 -0
  30. package/dist/src/elements/ia-expandable-search-bar/models.d.ts.map +1 -0
  31. package/dist/src/elements/ia-expandable-search-bar/models.js +2 -0
  32. package/dist/src/elements/ia-expandable-search-bar/models.js.map +1 -0
  33. package/dist/src/elements/ia-playback-controls/assets/next-section.d.ts +3 -0
  34. package/dist/src/elements/ia-playback-controls/assets/next-section.d.ts.map +1 -0
  35. package/dist/src/elements/ia-playback-controls/assets/next-section.js +16 -0
  36. package/dist/src/elements/ia-playback-controls/assets/next-section.js.map +1 -0
  37. package/dist/src/elements/ia-playback-controls/assets/pause.d.ts +3 -0
  38. package/dist/src/elements/ia-playback-controls/assets/pause.d.ts.map +1 -0
  39. package/dist/src/elements/ia-playback-controls/assets/pause.js +17 -0
  40. package/dist/src/elements/ia-playback-controls/assets/pause.js.map +1 -0
  41. package/dist/src/elements/ia-playback-controls/assets/play.d.ts +3 -0
  42. package/dist/src/elements/ia-playback-controls/assets/play.d.ts.map +1 -0
  43. package/dist/src/elements/ia-playback-controls/assets/play.js +18 -0
  44. package/dist/src/elements/ia-playback-controls/assets/play.js.map +1 -0
  45. package/dist/src/elements/ia-playback-controls/assets/playback-speed.d.ts +3 -0
  46. package/dist/src/elements/ia-playback-controls/assets/playback-speed.d.ts.map +1 -0
  47. package/dist/src/elements/ia-playback-controls/assets/playback-speed.js +28 -0
  48. package/dist/src/elements/ia-playback-controls/assets/playback-speed.js.map +1 -0
  49. package/dist/src/elements/ia-playback-controls/assets/previous-section.d.ts +3 -0
  50. package/dist/src/elements/ia-playback-controls/assets/previous-section.d.ts.map +1 -0
  51. package/dist/src/elements/ia-playback-controls/assets/previous-section.js +16 -0
  52. package/dist/src/elements/ia-playback-controls/assets/previous-section.js.map +1 -0
  53. package/dist/src/elements/ia-playback-controls/assets/replay.d.ts +3 -0
  54. package/dist/src/elements/ia-playback-controls/assets/replay.d.ts.map +1 -0
  55. package/dist/src/elements/ia-playback-controls/assets/replay.js +28 -0
  56. package/dist/src/elements/ia-playback-controls/assets/replay.js.map +1 -0
  57. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.d.ts +3 -0
  58. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.d.ts.map +1 -0
  59. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.js +29 -0
  60. package/dist/src/elements/ia-playback-controls/assets/skip-ahead.js.map +1 -0
  61. package/dist/src/elements/ia-playback-controls/assets/volume-full.d.ts +3 -0
  62. package/dist/src/elements/ia-playback-controls/assets/volume-full.d.ts.map +1 -0
  63. package/dist/src/elements/ia-playback-controls/assets/volume-full.js +19 -0
  64. package/dist/src/elements/ia-playback-controls/assets/volume-full.js.map +1 -0
  65. package/dist/src/elements/ia-playback-controls/assets/volume-medium.d.ts +3 -0
  66. package/dist/src/elements/ia-playback-controls/assets/volume-medium.d.ts.map +1 -0
  67. package/dist/src/elements/ia-playback-controls/assets/volume-medium.js +24 -0
  68. package/dist/src/elements/ia-playback-controls/assets/volume-medium.js.map +1 -0
  69. package/dist/src/elements/ia-playback-controls/assets/volume-mute.d.ts +3 -0
  70. package/dist/src/elements/ia-playback-controls/assets/volume-mute.d.ts.map +1 -0
  71. package/dist/src/elements/ia-playback-controls/assets/volume-mute.js +35 -0
  72. package/dist/src/elements/ia-playback-controls/assets/volume-mute.js.map +1 -0
  73. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts +66 -0
  74. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts.map +1 -0
  75. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js +337 -0
  76. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js.map +1 -0
  77. package/dist/src/elements/ia-playback-controls/models.d.ts +13 -0
  78. package/dist/src/elements/ia-playback-controls/models.d.ts.map +1 -0
  79. package/dist/src/elements/ia-playback-controls/models.js +12 -0
  80. package/dist/src/elements/ia-playback-controls/models.js.map +1 -0
  81. package/dist/src/elements/ia-radio-player/assets/next-result.d.ts +3 -0
  82. package/dist/src/elements/ia-radio-player/assets/next-result.d.ts.map +1 -0
  83. package/dist/src/elements/ia-radio-player/assets/next-result.js +27 -0
  84. package/dist/src/elements/ia-radio-player/assets/next-result.js.map +1 -0
  85. package/dist/src/elements/ia-radio-player/assets/previous-result.d.ts +3 -0
  86. package/dist/src/elements/ia-radio-player/assets/previous-result.d.ts.map +1 -0
  87. package/dist/src/elements/ia-radio-player/assets/previous-result.js +27 -0
  88. package/dist/src/elements/ia-radio-player/assets/previous-result.js.map +1 -0
  89. package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts +130 -0
  90. package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts.map +1 -0
  91. package/dist/src/elements/ia-radio-player/ia-radio-player.js +748 -0
  92. package/dist/src/elements/ia-radio-player/ia-radio-player.js.map +1 -0
  93. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.d.ts +22 -0
  94. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.d.ts.map +1 -0
  95. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.js +137 -0
  96. package/dist/src/elements/ia-radio-player/ia-search-results-switcher.js.map +1 -0
  97. package/dist/src/elements/ia-radio-player/models.d.ts +29 -0
  98. package/dist/src/elements/ia-radio-player/models.d.ts.map +1 -0
  99. package/dist/src/elements/ia-radio-player/models.js +2 -0
  100. package/dist/src/elements/ia-radio-player/models.js.map +1 -0
  101. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.d.ts +38 -0
  102. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.d.ts.map +1 -0
  103. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.js +64 -0
  104. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-backend.js.map +1 -0
  105. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.d.ts +94 -0
  106. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.d.ts.map +1 -0
  107. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.js +56 -0
  108. package/dist/src/elements/ia-radio-player/search/backends/full-text-search-response.js.map +1 -0
  109. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.d.ts +15 -0
  110. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.d.ts.map +1 -0
  111. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.js +26 -0
  112. package/dist/src/elements/ia-radio-player/search/backends/local-search-backend.js.map +1 -0
  113. package/dist/src/elements/ia-radio-player/search/backends/search-backend.d.ts +12 -0
  114. package/dist/src/elements/ia-radio-player/search/backends/search-backend.d.ts.map +1 -0
  115. package/dist/src/elements/ia-radio-player/search/backends/search-backend.js +2 -0
  116. package/dist/src/elements/ia-radio-player/search/backends/search-backend.js.map +1 -0
  117. package/dist/src/elements/ia-radio-player/search/models.d.ts +38 -0
  118. package/dist/src/elements/ia-radio-player/search/models.d.ts.map +1 -0
  119. package/dist/src/elements/ia-radio-player/search/models.js +17 -0
  120. package/dist/src/elements/ia-radio-player/search/models.js.map +1 -0
  121. package/dist/src/elements/ia-radio-player/search/search-handler.d.ts +49 -0
  122. package/dist/src/elements/ia-radio-player/search/search-handler.d.ts.map +1 -0
  123. package/dist/src/elements/ia-radio-player/search/search-handler.js +128 -0
  124. package/dist/src/elements/ia-radio-player/search/search-handler.js.map +1 -0
  125. package/dist/src/elements/ia-radio-player/search/search-helper.d.ts +9 -0
  126. package/dist/src/elements/ia-radio-player/search/search-helper.d.ts.map +1 -0
  127. package/dist/src/elements/ia-radio-player/search/search-helper.js +15 -0
  128. package/dist/src/elements/ia-radio-player/search/search-helper.js.map +1 -0
  129. package/dist/src/elements/ia-radio-player/search/transcript-index.d.ts +45 -0
  130. package/dist/src/elements/ia-radio-player/search/transcript-index.d.ts.map +1 -0
  131. package/dist/src/elements/ia-radio-player/search/transcript-index.js +48 -0
  132. package/dist/src/elements/ia-radio-player/search/transcript-index.js.map +1 -0
  133. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.d.ts +3 -0
  134. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.d.ts.map +1 -0
  135. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.js +18 -0
  136. package/dist/src/elements/ia-scrubber-bar/assets/next-section-marker.js.map +1 -0
  137. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.d.ts +3 -0
  138. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.d.ts.map +1 -0
  139. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.js +18 -0
  140. package/dist/src/elements/ia-scrubber-bar/assets/previous-section-marker.js.map +1 -0
  141. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.d.ts +82 -0
  142. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.d.ts.map +1 -0
  143. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.js +387 -0
  144. package/dist/src/elements/ia-scrubber-bar/ia-scrubber-bar.js.map +1 -0
  145. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.d.ts +16 -0
  146. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.d.ts.map +1 -0
  147. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.js +109 -0
  148. package/dist/src/elements/ia-scrubber-bar/ia-section-marker.js.map +1 -0
  149. package/dist/src/elements/ia-scrubber-bar/models.d.ts +21 -0
  150. package/dist/src/elements/ia-scrubber-bar/models.d.ts.map +1 -0
  151. package/dist/src/elements/ia-scrubber-bar/models.js +20 -0
  152. package/dist/src/elements/ia-scrubber-bar/models.js.map +1 -0
  153. package/dist/src/elements/ia-transcript-view/duration-formatter.d.ts +13 -0
  154. package/dist/src/elements/ia-transcript-view/duration-formatter.d.ts.map +1 -0
  155. package/dist/src/elements/ia-transcript-view/duration-formatter.js +25 -0
  156. package/dist/src/elements/ia-transcript-view/duration-formatter.js.map +1 -0
  157. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.d.ts +25 -0
  158. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.d.ts.map +1 -0
  159. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.js +51 -0
  160. package/dist/src/elements/ia-transcript-view/ia-transcript-entry.js.map +1 -0
  161. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts +87 -0
  162. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts.map +1 -0
  163. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js +565 -0
  164. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js.map +1 -0
  165. package/dist/src/elements/ia-transcript-view/models.d.ts +34 -0
  166. package/dist/src/elements/ia-transcript-view/models.d.ts.map +1 -0
  167. package/dist/src/elements/ia-transcript-view/models.js +38 -0
  168. package/dist/src/elements/ia-transcript-view/models.js.map +1 -0
  169. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.d.ts +62 -0
  170. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.d.ts.map +1 -0
  171. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.js +217 -0
  172. package/dist/src/elements/ia-waveform-progress/ia-waveform-progress.js.map +1 -0
  173. package/dist/src/elements/ia-waveform-progress/models.d.ts +13 -0
  174. package/dist/src/elements/ia-waveform-progress/models.d.ts.map +1 -0
  175. package/dist/src/elements/ia-waveform-progress/models.js +2 -0
  176. package/dist/src/elements/ia-waveform-progress/models.js.map +1 -0
  177. package/dist/src/elements/index.d.ts +27 -0
  178. package/dist/src/elements/index.d.ts.map +1 -1
  179. package/dist/src/elements/index.js +27 -0
  180. package/dist/src/elements/index.js.map +1 -1
  181. package/package.json +1 -1
@@ -0,0 +1,565 @@
1
+ var IATranscriptView_1;
2
+ import { __decorate } from "tslib";
3
+ import { css, html, LitElement, nothing, } from 'lit';
4
+ import { customElement, property, query, state } from 'lit/decorators.js';
5
+ import { ifDefined } from 'lit/directives/if-defined.js';
6
+ import { msg } from '@lit/localize';
7
+ import { formatDuration } from './duration-formatter.js';
8
+ import './ia-transcript-entry.js';
9
+ /**
10
+ * Event names emitted by this component
11
+ */
12
+ const Events = {
13
+ TranscriptEntrySelected: 'transcriptEntrySelected',
14
+ CurrentEntriesUpdated: 'currentEntriesUpdated',
15
+ AutoScrollChanged: 'autoScrollChanged',
16
+ };
17
+ /** How long a scroll animation runs, in seconds */
18
+ const SCROLL_DURATION_SECONDS = 1;
19
+ /**
20
+ * A scrolling transcript that follows playback.
21
+ *
22
+ * It keeps itself scrolled to whatever is being spoken, steps aside when the
23
+ * listener scrolls by hand, and picks the job back up once they've stopped.
24
+ */
25
+ let IATranscriptView = IATranscriptView_1 = class IATranscriptView extends LitElement {
26
+ constructor() {
27
+ super(...arguments);
28
+ /** Playback position, in seconds into the track */
29
+ this.currentTime = 0;
30
+ /** How much room to leave above the entry being spoken, in pixels */
31
+ this.topContextHeight = 50;
32
+ /** How much room to leave below the entry being spoken, in pixels */
33
+ this.bottomContextHeight = 50;
34
+ /** Whether the transcript follows playback */
35
+ this.autoScroll = true;
36
+ /** Which search result is currently being looked at */
37
+ this.selectedSearchResultIndex = 0;
38
+ /** Whether to outline the context zones, for working on the scrolling */
39
+ this.showContextZones = false;
40
+ /** How long to wait after a manual scroll before following playback again */
41
+ this.scrollTimerDelay = 15000;
42
+ this.timeScrollTop = 0;
43
+ this.currentEntries = [];
44
+ }
45
+ disconnectedCallback() {
46
+ super.disconnectedCallback();
47
+ clearTimeout(this.scrollResumeTimerId);
48
+ this.cancelScrollAnimation();
49
+ }
50
+ render() {
51
+ return html `
52
+ <div class="container">
53
+ ${this.showContextZones ? this.contextZoneTemplates : nothing}
54
+
55
+ <div
56
+ class="scroll-container"
57
+ id="scroll-container"
58
+ @wheel=${this.didScroll}
59
+ @touchmove=${this.didScroll}
60
+ >
61
+ <div class="col time">${this.timeDisplayTemplate}</div>
62
+
63
+ <div class="col">
64
+ ${this.autoScrollButtonTemplate}
65
+ ${this.transcriptEntries.map((entry) => this.transcriptEntryTemplate(entry))}
66
+ </div>
67
+ </div>
68
+ </div>
69
+ `;
70
+ }
71
+ /** Scrolls the selected search result into view. */
72
+ scrollToSelectedSearchResult() {
73
+ const { selectedSearchResult } = this;
74
+ if (!selectedSearchResult)
75
+ return;
76
+ this.autoScroll = false;
77
+ this.scrollToElement(selectedSearchResult);
78
+ }
79
+ /** The id of the entry starting closest to a given time, if there is one. */
80
+ entryIdentifierClosestToTime(time) {
81
+ const entries = this.transcriptEntries;
82
+ if (entries.length === 0)
83
+ return null;
84
+ let closest = entries[0];
85
+ let smallestDelta = Math.abs(time - closest.start);
86
+ for (const entry of entries) {
87
+ const delta = Math.abs(time - entry.start);
88
+ if (delta < smallestDelta) {
89
+ smallestDelta = delta;
90
+ closest = entry;
91
+ }
92
+ }
93
+ return closest.id;
94
+ }
95
+ get autoScrollButtonTemplate() {
96
+ return html `
97
+ <button
98
+ type="button"
99
+ class="auto-scroll-button ${this.autoScroll ? 'hidden' : ''}"
100
+ @click=${this.enableAutoScroll}
101
+ >
102
+ ${msg('Scroll text with audio')}
103
+ </button>
104
+ `;
105
+ }
106
+ get timeDisplayTemplate() {
107
+ return html `
108
+ <div class="time-display" style="top: ${this.timeScrollTop}px">
109
+ ${formatDuration(this.currentTime)}
110
+ </div>
111
+ `;
112
+ }
113
+ transcriptEntryTemplate(entry) {
114
+ const isActive = this.currentEntries.some((currentEntry) => currentEntry.id === entry.id);
115
+ return html `
116
+ <ia-transcript-entry
117
+ .entry=${entry}
118
+ ?isActive=${isActive}
119
+ ?isSelected=${entry.searchMatchIndex === this.selectedSearchResultIndex}
120
+ ?isSearchResult=${entry.searchMatchIndex !== undefined}
121
+ ?isMusicEntry=${entry.isMusic}
122
+ isClickable
123
+ data-search-result-index=${ifDefined(entry.searchMatchIndex)}
124
+ data-identifier=${entry.id}
125
+ @click=${this.transcriptEntrySelected}
126
+ ></ia-transcript-entry>
127
+ `;
128
+ }
129
+ get contextZoneTemplates() {
130
+ return html `
131
+ <div
132
+ class="top context-overlay"
133
+ style="height: ${this.topContextHeight}px"
134
+ ></div>
135
+ <div
136
+ class="bottom context-overlay"
137
+ style="height: ${this.bottomContextHeight}px"
138
+ ></div>
139
+ `;
140
+ }
141
+ get transcriptEntries() {
142
+ return this.config?.entries ?? [];
143
+ }
144
+ updated(changedProperties) {
145
+ if (changedProperties.has('currentTime'))
146
+ this.handleCurrentTimeChange();
147
+ if (changedProperties.has('selectedSearchResultIndex')) {
148
+ this.scrollToSelectedSearchResult();
149
+ }
150
+ if (changedProperties.has('currentEntries')) {
151
+ this.scrollToClosestEntry();
152
+ this.updateTimePosition();
153
+ }
154
+ if (changedProperties.has('autoScroll')) {
155
+ this.dispatchEvent(new CustomEvent(Events.AutoScrollChanged, {
156
+ detail: { autoScroll: this.autoScroll },
157
+ }));
158
+ }
159
+ if (changedProperties.has('config')) {
160
+ this.selectedSearchResultIndex = 0;
161
+ this.scrollToSelectedSearchResult();
162
+ }
163
+ }
164
+ transcriptEntrySelected(e) {
165
+ const { entry } = e.currentTarget;
166
+ if (!entry)
167
+ return;
168
+ this.dispatchEvent(new CustomEvent(Events.TranscriptEntrySelected, { detail: { entry } }));
169
+ if (entry.searchMatchIndex !== undefined) {
170
+ this.selectedSearchResultIndex = entry.searchMatchIndex;
171
+ }
172
+ this.autoScroll = false;
173
+ }
174
+ /**
175
+ * Works out which entries are being spoken now.
176
+ *
177
+ * Runs on every time update, several times a second, so it only touches the
178
+ * rendered state when the set of entries has actually changed.
179
+ */
180
+ handleCurrentTimeChange() {
181
+ const entries = this.transcriptEntries;
182
+ if (entries.length === 0)
183
+ return;
184
+ const activeEntries = entries.filter((entry) => this.currentTime >= entry.start && this.currentTime <= entry.end);
185
+ if (IATranscriptView_1.entryArraysMatch(activeEntries, this.currentEntries)) {
186
+ return;
187
+ }
188
+ this.dispatchEvent(new Event(Events.CurrentEntriesUpdated));
189
+ this.currentEntries = activeEntries;
190
+ }
191
+ static entryArraysMatch(a, b) {
192
+ if (a.length !== b.length)
193
+ return false;
194
+ const aIds = a.map((entry) => entry.id).sort((x, y) => x - y);
195
+ const bIds = b.map((entry) => entry.id).sort((x, y) => x - y);
196
+ return aIds.every((id, index) => bIds[index] === id);
197
+ }
198
+ elementClosestToTime(time) {
199
+ const closestIdentifier = this.entryIdentifierClosestToTime(time);
200
+ // Compared against null rather than tested for truthiness, so an entry
201
+ // whose id is 0 is still found.
202
+ if (closestIdentifier === null)
203
+ return null;
204
+ return this.elementForIdentifier(closestIdentifier);
205
+ }
206
+ elementForIdentifier(identifier) {
207
+ return (this.shadowRoot?.querySelector(`ia-transcript-entry[data-identifier="${identifier}"]`) ?? null);
208
+ }
209
+ /**
210
+ * Steps auto-scroll aside when the listener scrolls by hand, and arranges to
211
+ * pick it back up once they've left it alone for a while.
212
+ *
213
+ * A wheel that the transcript cannot act on, such as a wheel-up when it is
214
+ * already at the top, is ignored: the listener sees nothing move, so taking
215
+ * auto-scroll away from them reads as the page misbehaving.
216
+ */
217
+ didScroll(event) {
218
+ if (event instanceof WheelEvent && !this.canScrollBy(event.deltaY))
219
+ return;
220
+ this.autoScroll = false;
221
+ clearTimeout(this.scrollResumeTimerId);
222
+ this.scrollResumeTimerId = setTimeout(() => {
223
+ this.autoScroll = true;
224
+ }, this.scrollTimerDelay);
225
+ }
226
+ /** Whether the transcript has anywhere left to go in `deltaY`'s direction. */
227
+ canScrollBy(deltaY) {
228
+ const { scrollView } = this;
229
+ if (!scrollView)
230
+ return false;
231
+ const { scrollTop, scrollHeight, clientHeight } = scrollView;
232
+ if (deltaY < 0)
233
+ return scrollTop > 0;
234
+ if (deltaY > 0)
235
+ return Math.ceil(scrollTop + clientHeight) < scrollHeight;
236
+ return false;
237
+ }
238
+ enableAutoScroll() {
239
+ this.autoScroll = true;
240
+ this.scrollToClosestEntry();
241
+ }
242
+ get activeTranscriptEntry() {
243
+ return (this.shadowRoot?.querySelector('ia-transcript-entry[isActive]') ?? null);
244
+ }
245
+ get selectedSearchResult() {
246
+ return (this.shadowRoot?.querySelector(`ia-transcript-entry[data-search-result-index="${this.selectedSearchResultIndex}"]`) ?? null);
247
+ }
248
+ get closestEntryToCurrentTime() {
249
+ return (this.activeTranscriptEntry ?? this.elementClosestToTime(this.currentTime));
250
+ }
251
+ scrollToClosestEntry() {
252
+ if (!this.autoScroll)
253
+ return;
254
+ const closestEntry = this.closestEntryToCurrentTime;
255
+ if (closestEntry)
256
+ this.scrollToElement(closestEntry);
257
+ }
258
+ /**
259
+ * Scrolls an entry to the top of the focus area, if it isn't in there
260
+ * already.
261
+ */
262
+ scrollToElement(element) {
263
+ const { scrollView } = this;
264
+ if (!scrollView)
265
+ return;
266
+ const containerRect = scrollView.getBoundingClientRect();
267
+ const entryRect = element.getBoundingClientRect();
268
+ const focusBottom = containerRect.height - this.bottomContextHeight;
269
+ const isBelowFocusArea = entryRect.bottom > containerRect.top + focusBottom;
270
+ const isAboveFocusArea = entryRect.top < containerRect.top;
271
+ if (!isBelowFocusArea && !isAboveFocusArea)
272
+ return;
273
+ this.scrollToOffset(entryRect.top -
274
+ containerRect.top +
275
+ scrollView.scrollTop -
276
+ this.topContextHeight);
277
+ }
278
+ updateTimePosition() {
279
+ const scrollToEntry = this.closestEntryToCurrentTime;
280
+ if (!scrollToEntry)
281
+ return;
282
+ const parent = scrollToEntry.parentNode;
283
+ if (!parent)
284
+ return;
285
+ this.timeScrollTop =
286
+ scrollToEntry.getBoundingClientRect().top -
287
+ parent.getBoundingClientRect().top;
288
+ }
289
+ /**
290
+ * Eases the scroll container to an offset.
291
+ *
292
+ * Only one animation runs at a time: playback ticks arrive faster than an
293
+ * animation finishes, and two loops writing `scrollTop` fight each other.
294
+ */
295
+ scrollToOffset(offset) {
296
+ const { scrollView } = this;
297
+ if (!scrollView)
298
+ return;
299
+ this.cancelScrollAnimation();
300
+ const start = scrollView.scrollTop;
301
+ const change = offset - start;
302
+ const startTime = performance.now();
303
+ const easeInOutQuad = (time) => time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time;
304
+ const animate = () => {
305
+ const elapsed = (performance.now() - startTime) / 1000;
306
+ // Clamped so the last frame lands exactly on the target rather than
307
+ // overshooting past it.
308
+ const progress = Math.min(elapsed / SCROLL_DURATION_SECONDS, 1);
309
+ scrollView.scrollTop = start + change * easeInOutQuad(progress);
310
+ if (progress < 1) {
311
+ this.scrollAnimationId = requestAnimationFrame(animate);
312
+ }
313
+ else {
314
+ this.scrollAnimationId = undefined;
315
+ }
316
+ };
317
+ animate();
318
+ }
319
+ cancelScrollAnimation() {
320
+ if (this.scrollAnimationId === undefined)
321
+ return;
322
+ cancelAnimationFrame(this.scrollAnimationId);
323
+ this.scrollAnimationId = undefined;
324
+ }
325
+ static get styles() {
326
+ return css `
327
+ :host {
328
+ --transcript-height--: var(--ia-theme-transcript-height, 200px);
329
+
330
+ --transcript-normal-text-color--: var(
331
+ --ia-theme-transcript-normal-text-color,
332
+ gray
333
+ );
334
+ --transcript-active-text-color--: var(
335
+ --ia-theme-transcript-active-text-color,
336
+ white
337
+ );
338
+ --transcript-hover-text-color--: var(
339
+ --ia-theme-transcript-hover-text-color,
340
+ silver
341
+ );
342
+
343
+ --transcript-music-normal-text-color--: var(
344
+ --ia-theme-transcript-music-normal-text-color,
345
+ gray
346
+ );
347
+ --transcript-music-active-text-color--: var(
348
+ --ia-theme-transcript-music-active-text-color,
349
+ white
350
+ );
351
+ --transcript-music-hover-text-color--: var(
352
+ --ia-theme-transcript-music-hover-text-color,
353
+ silver
354
+ );
355
+
356
+ --transcript-search-result-inactive-border-color--: var(
357
+ --ia-theme-transcript-search-result-inactive-border-color,
358
+ gray
359
+ );
360
+ --transcript-search-result-active-border-color--: var(
361
+ --ia-theme-transcript-search-result-active-border-color,
362
+ green
363
+ );
364
+
365
+ --transcript-time-color--: var(--ia-theme-transcript-time-color, white);
366
+ --transcript-time-column-width--: var(
367
+ --ia-theme-transcript-time-column-width,
368
+ 3rem
369
+ );
370
+ --transcript-time-font-size--: var(
371
+ --ia-theme-transcript-time-font-size,
372
+ 1em
373
+ );
374
+ --transcript-time-line-height--: var(
375
+ --ia-theme-transcript-time-line-height,
376
+ 1em
377
+ );
378
+ --transcript-time-display--: var(
379
+ --ia-theme-transcript-time-display,
380
+ block
381
+ );
382
+
383
+ --transcript-auto-scroll-button-color--: var(
384
+ --ia-theme-transcript-auto-scroll-button-color,
385
+ black
386
+ );
387
+ --transcript-auto-scroll-button-background--: var(
388
+ --ia-theme-transcript-auto-scroll-button-background,
389
+ white
390
+ );
391
+ --transcript-auto-scroll-button-width--: var(
392
+ --ia-theme-transcript-auto-scroll-button-width,
393
+ 12rem
394
+ );
395
+ --transcript-auto-scroll-button-font-size--: var(
396
+ --ia-theme-transcript-auto-scroll-button-font-size,
397
+ 1em
398
+ );
399
+
400
+ color: var(--transcript-normal-text-color--);
401
+ }
402
+
403
+ .container {
404
+ position: relative;
405
+ }
406
+
407
+ .auto-scroll-button {
408
+ position: absolute;
409
+ left: 0;
410
+ right: 0;
411
+ bottom: 1rem;
412
+ margin: auto;
413
+ width: var(--transcript-auto-scroll-button-width--);
414
+ border-radius: 1em;
415
+ border: 0;
416
+ display: inline-block;
417
+ color: var(--transcript-auto-scroll-button-color--);
418
+ background-color: var(--transcript-auto-scroll-button-background--);
419
+ font-size: var(--transcript-auto-scroll-button-font-size--);
420
+ cursor: pointer;
421
+ z-index: 10;
422
+ }
423
+
424
+ .auto-scroll-button.hidden {
425
+ display: none;
426
+ }
427
+
428
+ .context-overlay {
429
+ position: absolute;
430
+ left: 0;
431
+ width: 100%;
432
+ height: 0;
433
+ /* Purely a guide. It sits over the top and bottom of the scroll area,
434
+ so without this it would swallow the wheel and the entry clicks
435
+ there, breaking the scrolling it exists to help you look at. */
436
+ pointer-events: none;
437
+ }
438
+
439
+ .context-overlay.top {
440
+ top: 0;
441
+ border-bottom: 1px solid green;
442
+ }
443
+
444
+ .context-overlay.bottom {
445
+ bottom: 0;
446
+ border-top: 1px solid green;
447
+ }
448
+
449
+ .time {
450
+ display: var(--transcript-time-display--);
451
+ flex: 0 0 var(--transcript-time-column-width--);
452
+ color: var(--transcript-time-color--);
453
+ position: relative;
454
+ }
455
+
456
+ .time-display {
457
+ position: absolute;
458
+ top: 0;
459
+ font-size: var(--transcript-time-font-size--);
460
+ line-height: var(--transcript-time-line-height--);
461
+ transition: top 1s;
462
+ }
463
+
464
+ .scroll-container {
465
+ display: flex;
466
+ overflow-y: auto;
467
+ -ms-overflow-style: none;
468
+ scrollbar-width: none;
469
+ height: var(--transcript-height--);
470
+ }
471
+
472
+ .scroll-container::-webkit-scrollbar {
473
+ display: none;
474
+ }
475
+
476
+ ia-transcript-entry {
477
+ cursor: pointer;
478
+ }
479
+
480
+ ia-transcript-entry:hover {
481
+ color: var(--transcript-hover-text-color--);
482
+ }
483
+
484
+ ia-transcript-entry[ismusicentry] {
485
+ color: var(--transcript-music-normal-text-color--);
486
+ display: block;
487
+ font-style: italic;
488
+ }
489
+
490
+ ia-transcript-entry[ismusicentry]:hover {
491
+ color: var(--transcript-music-hover-text-color--);
492
+ }
493
+
494
+ ia-transcript-entry[ismusicentry][isactive] {
495
+ color: var(--transcript-music-active-text-color--);
496
+ }
497
+
498
+ ia-transcript-entry[isactive] {
499
+ color: var(--transcript-active-text-color--);
500
+ }
501
+
502
+ ia-transcript-entry[issearchresult] {
503
+ /* Inline-block, or the outline adds a space to the right of the text */
504
+ display: inline-block;
505
+ padding: 0 5px;
506
+ position: relative;
507
+ }
508
+
509
+ ia-transcript-entry[issearchresult]:after {
510
+ content: '';
511
+ display: block;
512
+ position: absolute;
513
+ top: 0;
514
+ bottom: 0;
515
+ left: 0;
516
+ right: 0;
517
+ border: 2px solid
518
+ var(--transcript-search-result-inactive-border-color--);
519
+ border-radius: 5px;
520
+ }
521
+
522
+ ia-transcript-entry[issearchresult][isselected]:after {
523
+ border: 2px solid var(--transcript-search-result-active-border-color--);
524
+ }
525
+ `;
526
+ }
527
+ };
528
+ __decorate([
529
+ property({ type: Object })
530
+ ], IATranscriptView.prototype, "config", void 0);
531
+ __decorate([
532
+ property({ type: Number })
533
+ ], IATranscriptView.prototype, "currentTime", void 0);
534
+ __decorate([
535
+ property({ type: Number })
536
+ ], IATranscriptView.prototype, "topContextHeight", void 0);
537
+ __decorate([
538
+ property({ type: Number })
539
+ ], IATranscriptView.prototype, "bottomContextHeight", void 0);
540
+ __decorate([
541
+ property({ type: Boolean })
542
+ ], IATranscriptView.prototype, "autoScroll", void 0);
543
+ __decorate([
544
+ property({ type: Number })
545
+ ], IATranscriptView.prototype, "selectedSearchResultIndex", void 0);
546
+ __decorate([
547
+ property({ type: Boolean })
548
+ ], IATranscriptView.prototype, "showContextZones", void 0);
549
+ __decorate([
550
+ property({ type: Number })
551
+ ], IATranscriptView.prototype, "scrollTimerDelay", void 0);
552
+ __decorate([
553
+ state()
554
+ ], IATranscriptView.prototype, "timeScrollTop", void 0);
555
+ __decorate([
556
+ state()
557
+ ], IATranscriptView.prototype, "currentEntries", void 0);
558
+ __decorate([
559
+ query('#scroll-container')
560
+ ], IATranscriptView.prototype, "scrollView", void 0);
561
+ IATranscriptView = IATranscriptView_1 = __decorate([
562
+ customElement('ia-transcript-view')
563
+ ], IATranscriptView);
564
+ export { IATranscriptView };
565
+ //# sourceMappingURL=ia-transcript-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ia-transcript-view.js","sourceRoot":"","sources":["../../../../src/elements/ia-transcript-view/ia-transcript-view.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n css,\n html,\n LitElement,\n nothing,\n type CSSResultGroup,\n type PropertyValues,\n type TemplateResult,\n} from 'lit';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { msg } from '@lit/localize';\n\nimport { formatDuration } from './duration-formatter';\nimport type { TranscriptConfig, TranscriptEntryConfig } from './models';\nimport type { IATranscriptEntry } from './ia-transcript-entry';\n\nimport './ia-transcript-entry';\n\n/**\n * Event names emitted by this component\n */\nconst Events = {\n TranscriptEntrySelected: 'transcriptEntrySelected',\n CurrentEntriesUpdated: 'currentEntriesUpdated',\n AutoScrollChanged: 'autoScrollChanged',\n};\n\n/** How long a scroll animation runs, in seconds */\nconst SCROLL_DURATION_SECONDS = 1;\n\n/**\n * A scrolling transcript that follows playback.\n *\n * It keeps itself scrolled to whatever is being spoken, steps aside when the\n * listener scrolls by hand, and picks the job back up once they've stopped.\n */\n@customElement('ia-transcript-view')\nexport class IATranscriptView extends LitElement {\n /** The transcript to render */\n @property({ type: Object }) config?: TranscriptConfig;\n\n /** Playback position, in seconds into the track */\n @property({ type: Number }) currentTime = 0;\n\n /** How much room to leave above the entry being spoken, in pixels */\n @property({ type: Number }) topContextHeight = 50;\n\n /** How much room to leave below the entry being spoken, in pixels */\n @property({ type: Number }) bottomContextHeight = 50;\n\n /** Whether the transcript follows playback */\n @property({ type: Boolean }) autoScroll = true;\n\n /** Which search result is currently being looked at */\n @property({ type: Number }) selectedSearchResultIndex = 0;\n\n /** Whether to outline the context zones, for working on the scrolling */\n @property({ type: Boolean }) showContextZones = false;\n\n /** How long to wait after a manual scroll before following playback again */\n @property({ type: Number }) scrollTimerDelay = 15000;\n\n @state() private timeScrollTop = 0;\n\n @state() private currentEntries: TranscriptEntryConfig[] = [];\n\n @query('#scroll-container') private scrollView?: HTMLElement | null;\n\n private scrollResumeTimerId?: ReturnType<typeof setTimeout>;\n\n private scrollAnimationId?: number;\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n clearTimeout(this.scrollResumeTimerId);\n this.cancelScrollAnimation();\n }\n\n render(): TemplateResult {\n return html`\n <div class=\"container\">\n ${this.showContextZones ? this.contextZoneTemplates : nothing}\n\n <div\n class=\"scroll-container\"\n id=\"scroll-container\"\n @wheel=${this.didScroll}\n @touchmove=${this.didScroll}\n >\n <div class=\"col time\">${this.timeDisplayTemplate}</div>\n\n <div class=\"col\">\n ${this.autoScrollButtonTemplate}\n ${this.transcriptEntries.map((entry) =>\n this.transcriptEntryTemplate(entry),\n )}\n </div>\n </div>\n </div>\n `;\n }\n\n /** Scrolls the selected search result into view. */\n scrollToSelectedSearchResult(): void {\n const { selectedSearchResult } = this;\n if (!selectedSearchResult) return;\n\n this.autoScroll = false;\n this.scrollToElement(selectedSearchResult);\n }\n\n /** The id of the entry starting closest to a given time, if there is one. */\n entryIdentifierClosestToTime(time: number): number | null {\n const entries = this.transcriptEntries;\n if (entries.length === 0) return null;\n\n let closest = entries[0];\n let smallestDelta = Math.abs(time - closest.start);\n\n for (const entry of entries) {\n const delta = Math.abs(time - entry.start);\n if (delta < smallestDelta) {\n smallestDelta = delta;\n closest = entry;\n }\n }\n\n return closest.id;\n }\n\n private get autoScrollButtonTemplate(): TemplateResult {\n return html`\n <button\n type=\"button\"\n class=\"auto-scroll-button ${this.autoScroll ? 'hidden' : ''}\"\n @click=${this.enableAutoScroll}\n >\n ${msg('Scroll text with audio')}\n </button>\n `;\n }\n\n private get timeDisplayTemplate(): TemplateResult {\n return html`\n <div class=\"time-display\" style=\"top: ${this.timeScrollTop}px\">\n ${formatDuration(this.currentTime)}\n </div>\n `;\n }\n\n private transcriptEntryTemplate(\n entry: TranscriptEntryConfig,\n ): TemplateResult {\n const isActive = this.currentEntries.some(\n (currentEntry) => currentEntry.id === entry.id,\n );\n\n return html`\n <ia-transcript-entry\n .entry=${entry}\n ?isActive=${isActive}\n ?isSelected=${entry.searchMatchIndex === this.selectedSearchResultIndex}\n ?isSearchResult=${entry.searchMatchIndex !== undefined}\n ?isMusicEntry=${entry.isMusic}\n isClickable\n data-search-result-index=${ifDefined(entry.searchMatchIndex)}\n data-identifier=${entry.id}\n @click=${this.transcriptEntrySelected}\n ></ia-transcript-entry>\n `;\n }\n\n private get contextZoneTemplates(): TemplateResult {\n return html`\n <div\n class=\"top context-overlay\"\n style=\"height: ${this.topContextHeight}px\"\n ></div>\n <div\n class=\"bottom context-overlay\"\n style=\"height: ${this.bottomContextHeight}px\"\n ></div>\n `;\n }\n\n private get transcriptEntries(): TranscriptEntryConfig[] {\n return this.config?.entries ?? [];\n }\n\n updated(changedProperties: PropertyValues): void {\n if (changedProperties.has('currentTime')) this.handleCurrentTimeChange();\n if (changedProperties.has('selectedSearchResultIndex')) {\n this.scrollToSelectedSearchResult();\n }\n if (changedProperties.has('currentEntries')) {\n this.scrollToClosestEntry();\n this.updateTimePosition();\n }\n if (changedProperties.has('autoScroll')) {\n this.dispatchEvent(\n new CustomEvent<{ autoScroll: boolean }>(Events.AutoScrollChanged, {\n detail: { autoScroll: this.autoScroll },\n }),\n );\n }\n if (changedProperties.has('config')) {\n this.selectedSearchResultIndex = 0;\n this.scrollToSelectedSearchResult();\n }\n }\n\n private transcriptEntrySelected(e: Event): void {\n const { entry } = e.currentTarget as IATranscriptEntry;\n if (!entry) return;\n\n this.dispatchEvent(\n new CustomEvent<{ entry: TranscriptEntryConfig }>(\n Events.TranscriptEntrySelected,\n { detail: { entry } },\n ),\n );\n\n if (entry.searchMatchIndex !== undefined) {\n this.selectedSearchResultIndex = entry.searchMatchIndex;\n }\n this.autoScroll = false;\n }\n\n /**\n * Works out which entries are being spoken now.\n *\n * Runs on every time update, several times a second, so it only touches the\n * rendered state when the set of entries has actually changed.\n */\n private handleCurrentTimeChange(): void {\n const entries = this.transcriptEntries;\n if (entries.length === 0) return;\n\n const activeEntries = entries.filter(\n (entry) =>\n this.currentTime >= entry.start && this.currentTime <= entry.end,\n );\n\n if (IATranscriptView.entryArraysMatch(activeEntries, this.currentEntries)) {\n return;\n }\n\n this.dispatchEvent(new Event(Events.CurrentEntriesUpdated));\n this.currentEntries = activeEntries;\n }\n\n private static entryArraysMatch(\n a: TranscriptEntryConfig[],\n b: TranscriptEntryConfig[],\n ): boolean {\n if (a.length !== b.length) return false;\n\n const aIds = a.map((entry) => entry.id).sort((x, y) => x - y);\n const bIds = b.map((entry) => entry.id).sort((x, y) => x - y);\n\n return aIds.every((id, index) => bIds[index] === id);\n }\n\n private elementClosestToTime(time: number): HTMLElement | null {\n const closestIdentifier = this.entryIdentifierClosestToTime(time);\n // Compared against null rather than tested for truthiness, so an entry\n // whose id is 0 is still found.\n if (closestIdentifier === null) return null;\n\n return this.elementForIdentifier(closestIdentifier);\n }\n\n private elementForIdentifier(identifier: number): HTMLElement | null {\n return (\n this.shadowRoot?.querySelector(\n `ia-transcript-entry[data-identifier=\"${identifier}\"]`,\n ) ?? null\n );\n }\n\n /**\n * Steps auto-scroll aside when the listener scrolls by hand, and arranges to\n * pick it back up once they've left it alone for a while.\n *\n * A wheel that the transcript cannot act on, such as a wheel-up when it is\n * already at the top, is ignored: the listener sees nothing move, so taking\n * auto-scroll away from them reads as the page misbehaving.\n */\n private didScroll(event: Event): void {\n if (event instanceof WheelEvent && !this.canScrollBy(event.deltaY)) return;\n\n this.autoScroll = false;\n\n clearTimeout(this.scrollResumeTimerId);\n this.scrollResumeTimerId = setTimeout(() => {\n this.autoScroll = true;\n }, this.scrollTimerDelay);\n }\n\n /** Whether the transcript has anywhere left to go in `deltaY`'s direction. */\n private canScrollBy(deltaY: number): boolean {\n const { scrollView } = this;\n if (!scrollView) return false;\n\n const { scrollTop, scrollHeight, clientHeight } = scrollView;\n if (deltaY < 0) return scrollTop > 0;\n if (deltaY > 0) return Math.ceil(scrollTop + clientHeight) < scrollHeight;\n return false;\n }\n\n private enableAutoScroll(): void {\n this.autoScroll = true;\n this.scrollToClosestEntry();\n }\n\n private get activeTranscriptEntry(): HTMLElement | null {\n return (\n this.shadowRoot?.querySelector('ia-transcript-entry[isActive]') ?? null\n );\n }\n\n private get selectedSearchResult(): HTMLElement | null {\n return (\n this.shadowRoot?.querySelector(\n `ia-transcript-entry[data-search-result-index=\"${this.selectedSearchResultIndex}\"]`,\n ) ?? null\n );\n }\n\n private get closestEntryToCurrentTime(): HTMLElement | null {\n return (\n this.activeTranscriptEntry ?? this.elementClosestToTime(this.currentTime)\n );\n }\n\n private scrollToClosestEntry(): void {\n if (!this.autoScroll) return;\n\n const closestEntry = this.closestEntryToCurrentTime;\n if (closestEntry) this.scrollToElement(closestEntry);\n }\n\n /**\n * Scrolls an entry to the top of the focus area, if it isn't in there\n * already.\n */\n private scrollToElement(element: HTMLElement): void {\n const { scrollView } = this;\n if (!scrollView) return;\n\n const containerRect = scrollView.getBoundingClientRect();\n const entryRect = element.getBoundingClientRect();\n const focusBottom = containerRect.height - this.bottomContextHeight;\n\n const isBelowFocusArea = entryRect.bottom > containerRect.top + focusBottom;\n const isAboveFocusArea = entryRect.top < containerRect.top;\n if (!isBelowFocusArea && !isAboveFocusArea) return;\n\n this.scrollToOffset(\n entryRect.top -\n containerRect.top +\n scrollView.scrollTop -\n this.topContextHeight,\n );\n }\n\n private updateTimePosition(): void {\n const scrollToEntry = this.closestEntryToCurrentTime;\n if (!scrollToEntry) return;\n\n const parent = scrollToEntry.parentNode as HTMLElement | null;\n if (!parent) return;\n\n this.timeScrollTop =\n scrollToEntry.getBoundingClientRect().top -\n parent.getBoundingClientRect().top;\n }\n\n /**\n * Eases the scroll container to an offset.\n *\n * Only one animation runs at a time: playback ticks arrive faster than an\n * animation finishes, and two loops writing `scrollTop` fight each other.\n */\n private scrollToOffset(offset: number): void {\n const { scrollView } = this;\n if (!scrollView) return;\n\n this.cancelScrollAnimation();\n\n const start = scrollView.scrollTop;\n const change = offset - start;\n const startTime = performance.now();\n\n const easeInOutQuad = (time: number): number =>\n time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time;\n\n const animate = (): void => {\n const elapsed = (performance.now() - startTime) / 1000;\n // Clamped so the last frame lands exactly on the target rather than\n // overshooting past it.\n const progress = Math.min(elapsed / SCROLL_DURATION_SECONDS, 1);\n\n scrollView.scrollTop = start + change * easeInOutQuad(progress);\n\n if (progress < 1) {\n this.scrollAnimationId = requestAnimationFrame(animate);\n } else {\n this.scrollAnimationId = undefined;\n }\n };\n\n animate();\n }\n\n private cancelScrollAnimation(): void {\n if (this.scrollAnimationId === undefined) return;\n\n cancelAnimationFrame(this.scrollAnimationId);\n this.scrollAnimationId = undefined;\n }\n\n static get styles(): CSSResultGroup {\n return css`\n :host {\n --transcript-height--: var(--ia-theme-transcript-height, 200px);\n\n --transcript-normal-text-color--: var(\n --ia-theme-transcript-normal-text-color,\n gray\n );\n --transcript-active-text-color--: var(\n --ia-theme-transcript-active-text-color,\n white\n );\n --transcript-hover-text-color--: var(\n --ia-theme-transcript-hover-text-color,\n silver\n );\n\n --transcript-music-normal-text-color--: var(\n --ia-theme-transcript-music-normal-text-color,\n gray\n );\n --transcript-music-active-text-color--: var(\n --ia-theme-transcript-music-active-text-color,\n white\n );\n --transcript-music-hover-text-color--: var(\n --ia-theme-transcript-music-hover-text-color,\n silver\n );\n\n --transcript-search-result-inactive-border-color--: var(\n --ia-theme-transcript-search-result-inactive-border-color,\n gray\n );\n --transcript-search-result-active-border-color--: var(\n --ia-theme-transcript-search-result-active-border-color,\n green\n );\n\n --transcript-time-color--: var(--ia-theme-transcript-time-color, white);\n --transcript-time-column-width--: var(\n --ia-theme-transcript-time-column-width,\n 3rem\n );\n --transcript-time-font-size--: var(\n --ia-theme-transcript-time-font-size,\n 1em\n );\n --transcript-time-line-height--: var(\n --ia-theme-transcript-time-line-height,\n 1em\n );\n --transcript-time-display--: var(\n --ia-theme-transcript-time-display,\n block\n );\n\n --transcript-auto-scroll-button-color--: var(\n --ia-theme-transcript-auto-scroll-button-color,\n black\n );\n --transcript-auto-scroll-button-background--: var(\n --ia-theme-transcript-auto-scroll-button-background,\n white\n );\n --transcript-auto-scroll-button-width--: var(\n --ia-theme-transcript-auto-scroll-button-width,\n 12rem\n );\n --transcript-auto-scroll-button-font-size--: var(\n --ia-theme-transcript-auto-scroll-button-font-size,\n 1em\n );\n\n color: var(--transcript-normal-text-color--);\n }\n\n .container {\n position: relative;\n }\n\n .auto-scroll-button {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 1rem;\n margin: auto;\n width: var(--transcript-auto-scroll-button-width--);\n border-radius: 1em;\n border: 0;\n display: inline-block;\n color: var(--transcript-auto-scroll-button-color--);\n background-color: var(--transcript-auto-scroll-button-background--);\n font-size: var(--transcript-auto-scroll-button-font-size--);\n cursor: pointer;\n z-index: 10;\n }\n\n .auto-scroll-button.hidden {\n display: none;\n }\n\n .context-overlay {\n position: absolute;\n left: 0;\n width: 100%;\n height: 0;\n /* Purely a guide. It sits over the top and bottom of the scroll area,\n so without this it would swallow the wheel and the entry clicks\n there, breaking the scrolling it exists to help you look at. */\n pointer-events: none;\n }\n\n .context-overlay.top {\n top: 0;\n border-bottom: 1px solid green;\n }\n\n .context-overlay.bottom {\n bottom: 0;\n border-top: 1px solid green;\n }\n\n .time {\n display: var(--transcript-time-display--);\n flex: 0 0 var(--transcript-time-column-width--);\n color: var(--transcript-time-color--);\n position: relative;\n }\n\n .time-display {\n position: absolute;\n top: 0;\n font-size: var(--transcript-time-font-size--);\n line-height: var(--transcript-time-line-height--);\n transition: top 1s;\n }\n\n .scroll-container {\n display: flex;\n overflow-y: auto;\n -ms-overflow-style: none;\n scrollbar-width: none;\n height: var(--transcript-height--);\n }\n\n .scroll-container::-webkit-scrollbar {\n display: none;\n }\n\n ia-transcript-entry {\n cursor: pointer;\n }\n\n ia-transcript-entry:hover {\n color: var(--transcript-hover-text-color--);\n }\n\n ia-transcript-entry[ismusicentry] {\n color: var(--transcript-music-normal-text-color--);\n display: block;\n font-style: italic;\n }\n\n ia-transcript-entry[ismusicentry]:hover {\n color: var(--transcript-music-hover-text-color--);\n }\n\n ia-transcript-entry[ismusicentry][isactive] {\n color: var(--transcript-music-active-text-color--);\n }\n\n ia-transcript-entry[isactive] {\n color: var(--transcript-active-text-color--);\n }\n\n ia-transcript-entry[issearchresult] {\n /* Inline-block, or the outline adds a space to the right of the text */\n display: inline-block;\n padding: 0 5px;\n position: relative;\n }\n\n ia-transcript-entry[issearchresult]:after {\n content: '';\n display: block;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n border: 2px solid\n var(--transcript-search-result-inactive-border-color--);\n border-radius: 5px;\n }\n\n ia-transcript-entry[issearchresult][isselected]:after {\n border: 2px solid var(--transcript-search-result-active-border-color--);\n }\n `;\n }\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * One line of a transcript: a stretch of time and the words spoken in it.
3
+ */
4
+ export declare class TranscriptEntryConfig {
5
+ /** Identifies this entry within its transcript. */
6
+ id: number;
7
+ /** When the entry starts, in seconds into the track. */
8
+ start: number;
9
+ /** When the entry ends, in seconds into the track. */
10
+ end: number;
11
+ /** Whether this stretch is music rather than speech. */
12
+ isMusic: boolean;
13
+ /** Where this entry falls in the search results, if it is one. */
14
+ searchMatchIndex?: number;
15
+ /** The transcribed words. */
16
+ rawText: string;
17
+ constructor(id: number, start: number, end: number, rawText: string, isMusic: boolean, searchMatchIndex?: number);
18
+ /**
19
+ * What to show for this entry.
20
+ *
21
+ * Music stretches carry no words worth reading, so they say so instead.
22
+ */
23
+ get displayText(): string;
24
+ }
25
+ /**
26
+ * A whole transcript, as an ordered list of entries.
27
+ */
28
+ export declare class TranscriptConfig {
29
+ entries: TranscriptEntryConfig[];
30
+ constructor(entries: TranscriptEntryConfig[]);
31
+ /** Just the entries that matched the current search. */
32
+ get searchResults(): TranscriptEntryConfig[];
33
+ }
34
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../src/elements/ia-transcript-view/models.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,qBAAqB;IAChC,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IAEX,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IAEd,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IAEZ,wDAAwD;IACxD,OAAO,EAAE,OAAO,CAAC;IAEjB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;gBAGd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,gBAAgB,CAAC,EAAE,MAAM;IAU3B;;;;OAIG;IACH,IAAI,WAAW,IAAI,MAAM,CAIxB;CACF;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,EAAE,qBAAqB,EAAE,CAAM;gBAE1B,OAAO,EAAE,qBAAqB,EAAE;IAI5C,wDAAwD;IACxD,IAAI,aAAa,IAAI,qBAAqB,EAAE,CAE3C;CACF"}