@k2b/ui 0.18.0 → 0.19.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 (178) hide show
  1. package/README.md +3 -2
  2. package/dist/browser/actions/ContextMenu.js +3 -1
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/actions/Dropdown.js +91 -46
  5. package/dist/browser/actions/Dropdown.js.map +1 -1
  6. package/dist/browser/actions/FilterChip.js +4 -1
  7. package/dist/browser/actions/FilterChip.js.map +1 -1
  8. package/dist/browser/chat/ChatComposer.js +1293 -534
  9. package/dist/browser/chat/ChatComposer.js.map +1 -1
  10. package/dist/browser/chat/chat-behavior.js +9 -0
  11. package/dist/browser/chat/chat-behavior.js.map +1 -1
  12. package/dist/browser/chat/index.js.map +1 -1
  13. package/dist/browser/chat/types.js.map +1 -1
  14. package/dist/browser/content/Calendar.js +579 -533
  15. package/dist/browser/content/Calendar.js.map +1 -1
  16. package/dist/browser/content/Lightbox.js +76 -40
  17. package/dist/browser/content/Lightbox.js.map +1 -1
  18. package/dist/browser/content/PdfPreview.js +146 -79
  19. package/dist/browser/content/PdfPreview.js.map +1 -1
  20. package/dist/browser/content/Timeline.js +1038 -0
  21. package/dist/browser/content/Timeline.js.map +1 -0
  22. package/dist/browser/content/VideoPlayer.js +29 -9
  23. package/dist/browser/content/VideoPlayer.js.map +1 -1
  24. package/dist/browser/content/chart-inspection.js +7 -2
  25. package/dist/browser/content/chart-inspection.js.map +1 -1
  26. package/dist/browser/content/index.js +1 -0
  27. package/dist/browser/content/index.js.map +1 -1
  28. package/dist/browser/content/timeline-model.js +579 -0
  29. package/dist/browser/content/timeline-model.js.map +1 -0
  30. package/dist/browser/feedback/dialog-core.js +26 -14
  31. package/dist/browser/feedback/dialog-core.js.map +1 -1
  32. package/dist/browser/inputs/ChoiceChips.js +66 -0
  33. package/dist/browser/inputs/ChoiceChips.js.map +1 -0
  34. package/dist/browser/inputs/ChoiceGroups.js +53 -12
  35. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  36. package/dist/browser/inputs/FileInputs.js +48 -33
  37. package/dist/browser/inputs/FileInputs.js.map +1 -1
  38. package/dist/browser/inputs/SignatureInput.js +558 -0
  39. package/dist/browser/inputs/SignatureInput.js.map +1 -0
  40. package/dist/browser/inputs/emoji/EmojiPicker.js +841 -0
  41. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -0
  42. package/dist/browser/inputs/emoji/emoji-data.js +1903 -0
  43. package/dist/browser/inputs/emoji/emoji-data.js.map +1 -0
  44. package/dist/browser/inputs/emoji/emoji-index.js +209 -0
  45. package/dist/browser/inputs/emoji/emoji-index.js.map +1 -0
  46. package/dist/browser/inputs/emoji/emoji-messages.js +64 -0
  47. package/dist/browser/inputs/emoji/emoji-messages.js.map +1 -0
  48. package/dist/browser/inputs/index.js +5 -0
  49. package/dist/browser/inputs/index.js.map +1 -1
  50. package/dist/browser/inputs/markdown/actions.js +48 -2
  51. package/dist/browser/inputs/markdown/actions.js.map +1 -1
  52. package/dist/browser/inputs/markdown/code-zone.js +38 -2
  53. package/dist/browser/inputs/markdown/code-zone.js.map +1 -1
  54. package/dist/browser/inputs/signature-messages.js +39 -0
  55. package/dist/browser/inputs/signature-messages.js.map +1 -0
  56. package/dist/browser/inputs/signature.js +208 -0
  57. package/dist/browser/inputs/signature.js.map +1 -0
  58. package/dist/browser/internal/render-error.js +49 -0
  59. package/dist/browser/internal/render-error.js.map +1 -0
  60. package/dist/browser/intl/messages.js +108 -0
  61. package/dist/browser/intl/messages.js.map +1 -1
  62. package/dist/browser/layout/AppWorkspace.js +321 -222
  63. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  64. package/dist/browser/layout/FloatingWindow.js +4 -1
  65. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  66. package/dist/browser/layout/SidebarItemPreview.js +5 -1
  67. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  68. package/dist/browser/layout/index.js.map +1 -1
  69. package/dist/browser/surfaces/ResourceCard.js +212 -0
  70. package/dist/browser/surfaces/ResourceCard.js.map +1 -0
  71. package/dist/browser/surfaces/index.js +1 -0
  72. package/dist/browser/surfaces/index.js.map +1 -1
  73. package/dist/licenses/emojibase-data.txt +21 -0
  74. package/dist/ssr/actions/ContextMenu.js +3 -1
  75. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  76. package/dist/ssr/actions/Dropdown.js +49 -12
  77. package/dist/ssr/actions/Dropdown.js.map +1 -1
  78. package/dist/ssr/actions/FilterChip.js +4 -1
  79. package/dist/ssr/actions/FilterChip.js.map +1 -1
  80. package/dist/ssr/chat/ChatComposer.js +805 -214
  81. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  82. package/dist/ssr/chat/chat-behavior.js +9 -0
  83. package/dist/ssr/chat/chat-behavior.js.map +1 -1
  84. package/dist/ssr/chat/index.js.map +1 -1
  85. package/dist/ssr/chat/types.js.map +1 -1
  86. package/dist/ssr/content/Calendar.js +183 -142
  87. package/dist/ssr/content/Calendar.js.map +1 -1
  88. package/dist/ssr/content/Lightbox.js +19 -5
  89. package/dist/ssr/content/Lightbox.js.map +1 -1
  90. package/dist/ssr/content/PdfPreview.js +77 -36
  91. package/dist/ssr/content/PdfPreview.js.map +1 -1
  92. package/dist/ssr/content/Timeline.js +804 -0
  93. package/dist/ssr/content/Timeline.js.map +1 -0
  94. package/dist/ssr/content/VideoPlayer.js +29 -9
  95. package/dist/ssr/content/VideoPlayer.js.map +1 -1
  96. package/dist/ssr/content/chart-inspection.js +7 -2
  97. package/dist/ssr/content/chart-inspection.js.map +1 -1
  98. package/dist/ssr/content/index.js +1 -0
  99. package/dist/ssr/content/index.js.map +1 -1
  100. package/dist/ssr/content/timeline-model.js +579 -0
  101. package/dist/ssr/content/timeline-model.js.map +1 -0
  102. package/dist/ssr/feedback/dialog-core.js +26 -14
  103. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  104. package/dist/ssr/inputs/ChoiceChips.js +66 -0
  105. package/dist/ssr/inputs/ChoiceChips.js.map +1 -0
  106. package/dist/ssr/inputs/ChoiceGroups.js +12 -4
  107. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  108. package/dist/ssr/inputs/FileInputs.js +19 -4
  109. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  110. package/dist/ssr/inputs/SignatureInput.js +404 -0
  111. package/dist/ssr/inputs/SignatureInput.js.map +1 -0
  112. package/dist/ssr/inputs/emoji/EmojiPicker.js +551 -0
  113. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -0
  114. package/dist/ssr/inputs/emoji/emoji-data.js +1903 -0
  115. package/dist/ssr/inputs/emoji/emoji-data.js.map +1 -0
  116. package/dist/ssr/inputs/emoji/emoji-index.js +209 -0
  117. package/dist/ssr/inputs/emoji/emoji-index.js.map +1 -0
  118. package/dist/ssr/inputs/emoji/emoji-messages.js +64 -0
  119. package/dist/ssr/inputs/emoji/emoji-messages.js.map +1 -0
  120. package/dist/ssr/inputs/index.js +5 -0
  121. package/dist/ssr/inputs/index.js.map +1 -1
  122. package/dist/ssr/inputs/markdown/actions.js +48 -2
  123. package/dist/ssr/inputs/markdown/actions.js.map +1 -1
  124. package/dist/ssr/inputs/markdown/code-zone.js +38 -2
  125. package/dist/ssr/inputs/markdown/code-zone.js.map +1 -1
  126. package/dist/ssr/inputs/signature-messages.js +39 -0
  127. package/dist/ssr/inputs/signature-messages.js.map +1 -0
  128. package/dist/ssr/inputs/signature.js +208 -0
  129. package/dist/ssr/inputs/signature.js.map +1 -0
  130. package/dist/ssr/internal/render-error.js +49 -0
  131. package/dist/ssr/internal/render-error.js.map +1 -0
  132. package/dist/ssr/intl/messages.js +108 -0
  133. package/dist/ssr/intl/messages.js.map +1 -1
  134. package/dist/ssr/layout/AppWorkspace.js +112 -41
  135. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  136. package/dist/ssr/layout/FloatingWindow.js +4 -1
  137. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  138. package/dist/ssr/layout/SidebarItemPreview.js +5 -1
  139. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  140. package/dist/ssr/layout/index.js.map +1 -1
  141. package/dist/ssr/surfaces/ResourceCard.js +115 -0
  142. package/dist/ssr/surfaces/ResourceCard.js.map +1 -0
  143. package/dist/ssr/surfaces/index.js +1 -0
  144. package/dist/ssr/surfaces/index.js.map +1 -1
  145. package/dist/styles.css +1 -1
  146. package/dist/types/actions/Dropdown.d.ts +13 -1
  147. package/dist/types/actions/FilterChip.d.ts +5 -0
  148. package/dist/types/chat/ChatComposer.d.ts +24 -1
  149. package/dist/types/chat/chat-behavior.d.ts +15 -0
  150. package/dist/types/chat/index.d.ts +1 -1
  151. package/dist/types/chat/types.d.ts +34 -0
  152. package/dist/types/content/Calendar.d.ts +28 -8
  153. package/dist/types/content/Lightbox.d.ts +8 -0
  154. package/dist/types/content/Timeline.d.ts +49 -0
  155. package/dist/types/content/index.d.ts +4 -2
  156. package/dist/types/content/timeline-model.d.ts +148 -0
  157. package/dist/types/feedback/dialog-core.d.ts +1 -1
  158. package/dist/types/inputs/ChoiceChips.d.ts +20 -0
  159. package/dist/types/inputs/ChoiceGroups.d.ts +11 -2
  160. package/dist/types/inputs/FileInputs.d.ts +6 -0
  161. package/dist/types/inputs/SignatureInput.d.ts +22 -0
  162. package/dist/types/inputs/emoji/EmojiPicker.d.ts +42 -0
  163. package/dist/types/inputs/emoji/emoji-data.d.ts +2 -0
  164. package/dist/types/inputs/emoji/emoji-index.d.ts +109 -0
  165. package/dist/types/inputs/emoji/emoji-messages.d.ts +30 -0
  166. package/dist/types/inputs/index.d.ts +10 -0
  167. package/dist/types/inputs/markdown/actions.d.ts +4 -0
  168. package/dist/types/inputs/markdown/code-zone.d.ts +7 -0
  169. package/dist/types/inputs/signature-messages.d.ts +11 -0
  170. package/dist/types/inputs/signature.d.ts +72 -0
  171. package/dist/types/internal/render-error.d.ts +13 -0
  172. package/dist/types/intl/messages.d.ts +54 -0
  173. package/dist/types/layout/AppWorkspace.d.ts +17 -0
  174. package/dist/types/layout/SidebarItemPreview.d.ts +2 -0
  175. package/dist/types/layout/index.d.ts +1 -1
  176. package/dist/types/surfaces/ResourceCard.d.ts +31 -0
  177. package/dist/types/surfaces/index.d.ts +2 -0
  178. package/package.json +5 -4
@@ -1,9 +1,10 @@
1
1
  import { template as _$template } from "solid-js/web";
2
2
  import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
- import { memo as _$memo } from "solid-js/web";
3
+ import { className as _$className } from "solid-js/web";
4
4
  import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
5
5
  import { setAttribute as _$setAttribute } from "solid-js/web";
6
6
  import { effect as _$effect } from "solid-js/web";
7
+ import { memo as _$memo } from "solid-js/web";
7
8
  import { createComponent as _$createComponent } from "solid-js/web";
8
9
  import { getNextElement as _$getNextElement } from "solid-js/web";
9
10
  import { getNextMarker as _$getNextMarker } from "solid-js/web";
@@ -15,12 +16,16 @@ var _tmpl$ = /*#__PURE__*/_$template(`<span class=k2b-content-lightbox__counter
15
16
  _tmpl$4 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__nav data-direction=previous><i class="ti ti-chevron-left"aria-hidden=true>`),
16
17
  _tmpl$5 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__nav data-direction=next><i class="ti ti-chevron-right"aria-hidden=true>`),
17
18
  _tmpl$6 = /*#__PURE__*/_$template(`<div class=k2b-content-lightbox__dots role=group>`),
18
- _tmpl$7 = /*#__PURE__*/_$template(`<dialog class=k2b-content-lightbox><div class=k2b-content-lightbox__bar><div class=k2b-content-lightbox__meta><!$><!/><!$><!/></div><div class=k2b-content-lightbox__actions><!$><!/><button type=button class=k2b-content-lightbox__button><i class="ti ti-x"aria-hidden=true></i><span class=k2b-content-lightbox__button-label></span></button></div></div><div class=k2b-content-lightbox__stage></div><!$><!/><!$><!/>`),
19
- _tmpl$8 = /*#__PURE__*/_$template(`<img class=k2b-content-lightbox__image>`),
20
- _tmpl$9 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__dot>`);
19
+ _tmpl$7 = /*#__PURE__*/_$template(`<dialog class=k2b-content-lightbox><div class=k2b-content-lightbox__bar><div class=k2b-content-lightbox__meta><!$><!/><!$><!/></div><div class=k2b-content-lightbox__actions><!$><!/><!$><!/><button type=button class=k2b-content-lightbox__button><i class="ti ti-x"aria-hidden=true></i><span class=k2b-content-lightbox__button-label></span></button></div></div><div class=k2b-content-lightbox__stage></div><!$><!/><!$><!/>`),
20
+ _tmpl$8 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__button><i aria-hidden=true></i><span class=k2b-content-lightbox__button-label>`),
21
+ _tmpl$9 = /*#__PURE__*/_$template(`<img class=k2b-content-lightbox__image>`),
22
+ _tmpl$0 = /*#__PURE__*/_$template(`<button type=button class=k2b-content-lightbox__dot>`);
21
23
  import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js";
22
24
  import { returnFocus, ringOnReturn } from "../internal/focus-return";
23
25
  import { useUiMessages } from "../intl/messages";
26
+
27
+ /** One more thing to do with the shown image, beside Download: a short label, its icon class, and what it does. */
28
+
24
29
  /**
25
30
  * Minimal, accessible lightbox using native <dialog>.
26
31
  * Supports keyboard navigation, touch swipe gestures, and screen readers.
@@ -70,8 +75,13 @@ export default function Lightbox(props) {
70
75
  props.onClose();
71
76
  };
72
77
 
78
+ // A key belongs to the Lightbox while focus is inside it, or on the page with no other dialog open. A dialog opened
79
+ // from one of its actions keeps its own keys: Escape closes that dialog, and arrows stay in its fields.
80
+ const ownsKey = target => target instanceof Node && (dialogRef.contains(target) || target === document.body && Array.from(document.querySelectorAll("dialog[open]")).every(dialog => dialog === dialogRef));
81
+
73
82
  // Keyboard navigation
74
83
  const handleKeyDown = e => {
84
+ if (!ownsKey(e.target)) return;
75
85
  switch (e.key) {
76
86
  case "Escape":
77
87
  // Handled here, so WebKit does not light the returned focus for this key.
@@ -147,14 +157,16 @@ export default function Lightbox(props) {
147
157
  _el$14 = _el$3.nextSibling,
148
158
  _el$21 = _el$14.firstChild,
149
159
  [_el$22, _co$5] = _$getNextMarker(_el$21.nextSibling),
150
- _el$18 = _el$22.nextSibling,
160
+ _el$23 = _el$22.nextSibling,
161
+ [_el$24, _co$6] = _$getNextMarker(_el$23.nextSibling),
162
+ _el$18 = _el$24.nextSibling,
151
163
  _el$19 = _el$18.firstChild,
152
164
  _el$20 = _el$19.nextSibling,
153
- _el$23 = _el$2.nextSibling,
154
- _el$27 = _el$23.nextSibling,
155
- [_el$28, _co$6] = _$getNextMarker(_el$27.nextSibling),
156
- _el$29 = _el$28.nextSibling,
157
- [_el$30, _co$7] = _$getNextMarker(_el$29.nextSibling);
165
+ _el$25 = _el$2.nextSibling,
166
+ _el$29 = _el$25.nextSibling,
167
+ [_el$30, _co$7] = _$getNextMarker(_el$29.nextSibling),
168
+ _el$31 = _el$30.nextSibling,
169
+ [_el$32, _co$8] = _$getNextMarker(_el$31.nextSibling);
158
170
  _el$.$$touchend = handleTouchEnd;
159
171
  _el$.$$touchstart = handleTouchStart;
160
172
  _el$.addEventListener("cancel", handleCancel);
@@ -188,6 +200,30 @@ export default function Lightbox(props) {
188
200
  return _el$0;
189
201
  }
190
202
  }), _el$13, _co$4);
203
+ _$insert(_el$14, _$createComponent(For, {
204
+ get each() {
205
+ return current()?.actions ?? [];
206
+ },
207
+ children: action => (() => {
208
+ var _el$33 = _$getNextElement(_tmpl$8),
209
+ _el$34 = _el$33.firstChild,
210
+ _el$35 = _el$34.nextSibling;
211
+ _el$33.$$click = () => action.onClick();
212
+ _$insert(_el$35, () => action.label);
213
+ _$effect(_p$ => {
214
+ var _v$5 = action.label,
215
+ _v$6 = action.icon;
216
+ _v$5 !== _p$.e && _$setAttribute(_el$33, "aria-label", _p$.e = _v$5);
217
+ _v$6 !== _p$.t && _$className(_el$34, _p$.t = _v$6);
218
+ return _p$;
219
+ }, {
220
+ e: undefined,
221
+ t: undefined
222
+ });
223
+ _$runHydrationEvents();
224
+ return _el$33;
225
+ })()
226
+ }), _el$22, _co$5);
191
227
  _$insert(_el$14, _$createComponent(Show, {
192
228
  get when() {
193
229
  return current()?.downloadUrl;
@@ -209,27 +245,27 @@ export default function Lightbox(props) {
209
245
  });
210
246
  return _el$15;
211
247
  }
212
- }), _el$22, _co$5);
248
+ }), _el$24, _co$6);
213
249
  _el$18.$$click = close;
214
250
  _$insert(_el$20, () => messages().close);
215
- _$insert(_el$23, _$createComponent(Show, {
251
+ _$insert(_el$25, _$createComponent(Show, {
216
252
  get when() {
217
253
  return current();
218
254
  },
219
255
  children: image => (() => {
220
- var _el$31 = _$getNextElement(_tmpl$8);
221
- _$setAttribute(_el$31, "draggable", false);
256
+ var _el$36 = _$getNextElement(_tmpl$9);
257
+ _$setAttribute(_el$36, "draggable", false);
222
258
  _$effect(_p$ => {
223
- var _v$5 = image().src,
224
- _v$6 = image().alt ?? "";
225
- _v$5 !== _p$.e && _$setAttribute(_el$31, "src", _p$.e = _v$5);
226
- _v$6 !== _p$.t && _$setAttribute(_el$31, "alt", _p$.t = _v$6);
259
+ var _v$7 = image().src,
260
+ _v$8 = image().alt ?? "";
261
+ _v$7 !== _p$.e && _$setAttribute(_el$36, "src", _p$.e = _v$7);
262
+ _v$8 !== _p$.t && _$setAttribute(_el$36, "alt", _p$.t = _v$8);
227
263
  return _p$;
228
264
  }, {
229
265
  e: undefined,
230
266
  t: undefined
231
267
  });
232
- return _el$31;
268
+ return _el$36;
233
269
  })()
234
270
  }));
235
271
  _$insert(_el$, _$createComponent(Show, {
@@ -238,53 +274,53 @@ export default function Lightbox(props) {
238
274
  },
239
275
  get children() {
240
276
  return [(() => {
241
- var _el$24 = _$getNextElement(_tmpl$4);
242
- _el$24.$$click = prev;
243
- _$effect(() => _$setAttribute(_el$24, "aria-label", messages().previousImage));
277
+ var _el$26 = _$getNextElement(_tmpl$4);
278
+ _el$26.$$click = prev;
279
+ _$effect(() => _$setAttribute(_el$26, "aria-label", messages().previousImage));
244
280
  _$runHydrationEvents();
245
- return _el$24;
281
+ return _el$26;
246
282
  })(), (() => {
247
- var _el$25 = _$getNextElement(_tmpl$5);
248
- _el$25.$$click = next;
249
- _$effect(() => _$setAttribute(_el$25, "aria-label", messages().nextImage));
283
+ var _el$27 = _$getNextElement(_tmpl$5);
284
+ _el$27.$$click = next;
285
+ _$effect(() => _$setAttribute(_el$27, "aria-label", messages().nextImage));
250
286
  _$runHydrationEvents();
251
- return _el$25;
287
+ return _el$27;
252
288
  })()];
253
289
  }
254
- }), _el$28, _co$6);
290
+ }), _el$30, _co$7);
255
291
  _$insert(_el$, _$createComponent(Show, {
256
292
  get when() {
257
293
  return _$memo(() => !!isMultiple())() && props.images.length <= 10;
258
294
  },
259
295
  get children() {
260
- var _el$26 = _$getNextElement(_tmpl$6);
261
- _$insert(_el$26, _$createComponent(For, {
296
+ var _el$28 = _$getNextElement(_tmpl$6);
297
+ _$insert(_el$28, _$createComponent(For, {
262
298
  get each() {
263
299
  return props.images;
264
300
  },
265
301
  children: (_, i) => (() => {
266
- var _el$32 = _$getNextElement(_tmpl$9);
267
- _el$32.$$click = () => setIndex(i());
302
+ var _el$37 = _$getNextElement(_tmpl$0);
303
+ _el$37.$$click = () => setIndex(i());
268
304
  _$effect(_p$ => {
269
- var _v$7 = index() === i() ? "true" : undefined,
270
- _v$8 = messages().goToImage({
305
+ var _v$9 = index() === i() ? "true" : undefined,
306
+ _v$0 = messages().goToImage({
271
307
  index: i() + 1
272
308
  });
273
- _v$7 !== _p$.e && _$setAttribute(_el$32, "aria-current", _p$.e = _v$7);
274
- _v$8 !== _p$.t && _$setAttribute(_el$32, "aria-label", _p$.t = _v$8);
309
+ _v$9 !== _p$.e && _$setAttribute(_el$37, "aria-current", _p$.e = _v$9);
310
+ _v$0 !== _p$.t && _$setAttribute(_el$37, "aria-label", _p$.t = _v$0);
275
311
  return _p$;
276
312
  }, {
277
313
  e: undefined,
278
314
  t: undefined
279
315
  });
280
316
  _$runHydrationEvents();
281
- return _el$32;
317
+ return _el$37;
282
318
  })()
283
319
  }));
284
- _$effect(() => _$setAttribute(_el$26, "aria-label", messages().imageNavigation));
285
- return _el$26;
320
+ _$effect(() => _$setAttribute(_el$28, "aria-label", messages().imageNavigation));
321
+ return _el$28;
286
322
  }
287
- }), _el$30, _co$7);
323
+ }), _el$32, _co$8);
288
324
  _$effect(_p$ => {
289
325
  var _v$3 = messages().imageLightbox,
290
326
  _v$4 = messages().closeLightbox;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/Lightbox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, onCleanup, onMount, Show } from \"solid-js\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\nexport type LightboxImage = {\n src: string;\n alt?: string;\n downloadUrl?: string;\n};\n\ntype LightboxProps = {\n images: LightboxImage[];\n initialIndex?: number;\n onClose: () => void;\n};\n\n/**\n * Minimal, accessible lightbox using native <dialog>.\n * Supports keyboard navigation, touch swipe gestures, and screen readers.\n */\nexport default function Lightbox(props: LightboxProps) {\n const messages = useUiMessages();\n const clampIndex = (value: number, images = props.images): number =>\n images.length === 0 ? 0 : Math.max(0, Math.min(value, images.length - 1));\n const [index, setIndex] = createSignal(clampIndex(props.initialIndex ?? 0));\n let dialogRef!: HTMLDialogElement;\n let opener: HTMLElement | null = null;\n let openerRing = true;\n\n createEffect(\n (previous: { images: LightboxImage[]; initialIndex: number | undefined }) => {\n const images = props.images;\n const initialIndex = props.initialIndex;\n setIndex((currentIndex) => {\n if (initialIndex !== previous.initialIndex) return clampIndex(initialIndex ?? 0, images);\n const previousImage = previous.images[currentIndex];\n const retained = previousImage ? images.findIndex((image) => image === previousImage || image.src === previousImage.src) : -1;\n return retained >= 0 ? retained : clampIndex(currentIndex, images);\n });\n return { images, initialIndex };\n },\n { images: props.images, initialIndex: props.initialIndex },\n );\n\n // Touch gesture tracking\n let touchStartX = 0;\n let touchStartY = 0;\n const SWIPE_THRESHOLD = 50;\n\n const current = () => props.images[index()];\n const isMultiple = () => props.images.length > 1;\n const prev = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i - 1 + props.images.length) % props.images.length);\n };\n const next = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i + 1) % props.images.length);\n };\n\n const close = () => {\n dialogRef.close();\n returnFocus(opener, openerRing);\n props.onClose();\n };\n\n // Keyboard navigation\n const handleKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case \"Escape\":\n // Handled here, so WebKit does not light the returned focus for this key.\n e.preventDefault();\n close();\n break;\n case \"ArrowLeft\":\n prev();\n break;\n case \"ArrowRight\":\n next();\n break;\n }\n };\n\n // Touch handlers for swipe gestures\n const handleTouchStart = (e: TouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n }\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n const touch = e.changedTouches[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n\n // Only trigger swipe if horizontal movement is dominant\n if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > SWIPE_THRESHOLD) {\n if (deltaX > 0) {\n prev();\n } else {\n next();\n }\n }\n };\n\n // Click on backdrop closes lightbox\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === dialogRef) {\n close();\n }\n };\n\n // The browser cancels the dialog itself, as on the Android back gesture;\n // closing here still returns focus and tells the owner.\n const handleCancel = (e: Event) => {\n e.preventDefault();\n close();\n };\n\n // Registration and teardown both live in onMount: onCleanup also runs when an\n // SSR render is disposed, where `document` does not exist.\n onMount(() => {\n opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n openerRing = ringOnReturn(opener);\n // The native dialog would return focus with a ring of the browser's choosing.\n opener?.blur();\n dialogRef.showModal();\n document.addEventListener(\"keydown\", handleKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", handleKeyDown));\n });\n\n return (\n <dialog\n ref={dialogRef}\n class=\"k2b-content-lightbox\"\n onMouseDown={handleBackdropClick}\n onCancel={handleCancel}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n aria-label={messages().imageLightbox}\n >\n {/* Top bar */}\n <div class=\"k2b-content-lightbox__bar\">\n <div class=\"k2b-content-lightbox__meta\">\n <Show when={isMultiple()}>\n <span class=\"k2b-content-lightbox__counter\" aria-live=\"polite\">\n {index() + 1} / {props.images.length}\n </span>\n </Show>\n <Show when={current()?.alt}>\n <div class=\"k2b-content-lightbox__caption\">\n <div class=\"k2b-content-lightbox__caption-text\">{current()?.alt}</div>\n </div>\n </Show>\n </div>\n\n <div class=\"k2b-content-lightbox__actions\">\n <Show when={current()?.downloadUrl}>\n <a href={current()!.downloadUrl} download=\"\" class=\"k2b-content-lightbox__button\" aria-label={messages().downloadImage}>\n <i class=\"ti ti-download\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().download}</span>\n </a>\n </Show>\n <button type=\"button\" onClick={close} class=\"k2b-content-lightbox__button\" aria-label={messages().closeLightbox}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().close}</span>\n </button>\n </div>\n </div>\n\n <div class=\"k2b-content-lightbox__stage\">\n <Show when={current()}>\n {(image) => <img src={image().src} alt={image().alt ?? \"\"} class=\"k2b-content-lightbox__image\" draggable={false} />}\n </Show>\n </div>\n\n <Show when={isMultiple()}>\n <button\n type=\"button\"\n onClick={prev}\n class=\"k2b-content-lightbox__nav\"\n data-direction=\"previous\"\n aria-label={messages().previousImage}\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </button>\n <button type=\"button\" onClick={next} class=\"k2b-content-lightbox__nav\" data-direction=\"next\" aria-label={messages().nextImage}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </button>\n </Show>\n\n <Show when={isMultiple() && props.images.length <= 10}>\n <div class=\"k2b-content-lightbox__dots\" role=\"group\" aria-label={messages().imageNavigation}>\n <For each={props.images}>\n {(_, i) => (\n <button\n type=\"button\"\n onClick={() => setIndex(i())}\n class=\"k2b-content-lightbox__dot\"\n aria-current={index() === i() ? \"true\" : undefined}\n aria-label={messages().goToImage({ index: i() + 1 })}\n />\n )}\n </For>\n </div>\n </Show>\n </dialog>\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/Lightbox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, onCleanup, onMount, Show } from \"solid-js\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\n\n/** One more thing to do with the shown image, beside Download: a short label, its icon class, and what it does. */\nexport type LightboxAction = {\n label: string;\n icon: string;\n onClick: () => void;\n};\n\nexport type LightboxImage = {\n src: string;\n alt?: string;\n downloadUrl?: string;\n /** Actions for this image, shown before Download in the same style. */\n actions?: readonly LightboxAction[];\n};\n\ntype LightboxProps = {\n images: LightboxImage[];\n initialIndex?: number;\n onClose: () => void;\n};\n\n/**\n * Minimal, accessible lightbox using native <dialog>.\n * Supports keyboard navigation, touch swipe gestures, and screen readers.\n */\nexport default function Lightbox(props: LightboxProps) {\n const messages = useUiMessages();\n const clampIndex = (value: number, images = props.images): number =>\n images.length === 0 ? 0 : Math.max(0, Math.min(value, images.length - 1));\n const [index, setIndex] = createSignal(clampIndex(props.initialIndex ?? 0));\n let dialogRef!: HTMLDialogElement;\n let opener: HTMLElement | null = null;\n let openerRing = true;\n\n createEffect(\n (previous: { images: LightboxImage[]; initialIndex: number | undefined }) => {\n const images = props.images;\n const initialIndex = props.initialIndex;\n setIndex((currentIndex) => {\n if (initialIndex !== previous.initialIndex) return clampIndex(initialIndex ?? 0, images);\n const previousImage = previous.images[currentIndex];\n const retained = previousImage ? images.findIndex((image) => image === previousImage || image.src === previousImage.src) : -1;\n return retained >= 0 ? retained : clampIndex(currentIndex, images);\n });\n return { images, initialIndex };\n },\n { images: props.images, initialIndex: props.initialIndex },\n );\n\n // Touch gesture tracking\n let touchStartX = 0;\n let touchStartY = 0;\n const SWIPE_THRESHOLD = 50;\n\n const current = () => props.images[index()];\n const isMultiple = () => props.images.length > 1;\n const prev = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i - 1 + props.images.length) % props.images.length);\n };\n const next = () => {\n if (!isMultiple()) return;\n setIndex((i) => (i + 1) % props.images.length);\n };\n\n const close = () => {\n dialogRef.close();\n returnFocus(opener, openerRing);\n props.onClose();\n };\n\n // A key belongs to the Lightbox while focus is inside it, or on the page with no other dialog open. A dialog opened\n // from one of its actions keeps its own keys: Escape closes that dialog, and arrows stay in its fields.\n const ownsKey = (target: EventTarget | null) =>\n target instanceof Node &&\n (dialogRef.contains(target) ||\n (target === document.body && Array.from(document.querySelectorAll(\"dialog[open]\")).every((dialog) => dialog === dialogRef)));\n\n // Keyboard navigation\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!ownsKey(e.target)) return;\n switch (e.key) {\n case \"Escape\":\n // Handled here, so WebKit does not light the returned focus for this key.\n e.preventDefault();\n close();\n break;\n case \"ArrowLeft\":\n prev();\n break;\n case \"ArrowRight\":\n next();\n break;\n }\n };\n\n // Touch handlers for swipe gestures\n const handleTouchStart = (e: TouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n touchStartX = touch.clientX;\n touchStartY = touch.clientY;\n }\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n const touch = e.changedTouches[0];\n if (!touch) return;\n\n const deltaX = touch.clientX - touchStartX;\n const deltaY = touch.clientY - touchStartY;\n\n // Only trigger swipe if horizontal movement is dominant\n if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > SWIPE_THRESHOLD) {\n if (deltaX > 0) {\n prev();\n } else {\n next();\n }\n }\n };\n\n // Click on backdrop closes lightbox\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === dialogRef) {\n close();\n }\n };\n\n // The browser cancels the dialog itself, as on the Android back gesture;\n // closing here still returns focus and tells the owner.\n const handleCancel = (e: Event) => {\n e.preventDefault();\n close();\n };\n\n // Registration and teardown both live in onMount: onCleanup also runs when an\n // SSR render is disposed, where `document` does not exist.\n onMount(() => {\n opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n openerRing = ringOnReturn(opener);\n // The native dialog would return focus with a ring of the browser's choosing.\n opener?.blur();\n dialogRef.showModal();\n document.addEventListener(\"keydown\", handleKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", handleKeyDown));\n });\n\n return (\n <dialog\n ref={dialogRef}\n class=\"k2b-content-lightbox\"\n onMouseDown={handleBackdropClick}\n onCancel={handleCancel}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n aria-label={messages().imageLightbox}\n >\n {/* Top bar */}\n <div class=\"k2b-content-lightbox__bar\">\n <div class=\"k2b-content-lightbox__meta\">\n <Show when={isMultiple()}>\n <span class=\"k2b-content-lightbox__counter\" aria-live=\"polite\">\n {index() + 1} / {props.images.length}\n </span>\n </Show>\n <Show when={current()?.alt}>\n <div class=\"k2b-content-lightbox__caption\">\n <div class=\"k2b-content-lightbox__caption-text\">{current()?.alt}</div>\n </div>\n </Show>\n </div>\n\n <div class=\"k2b-content-lightbox__actions\">\n <For each={current()?.actions ?? []}>\n {(action) => (\n <button type=\"button\" onClick={() => action.onClick()} class=\"k2b-content-lightbox__button\" aria-label={action.label}>\n <i class={action.icon} aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{action.label}</span>\n </button>\n )}\n </For>\n <Show when={current()?.downloadUrl}>\n <a href={current()!.downloadUrl} download=\"\" class=\"k2b-content-lightbox__button\" aria-label={messages().downloadImage}>\n <i class=\"ti ti-download\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().download}</span>\n </a>\n </Show>\n <button type=\"button\" onClick={close} class=\"k2b-content-lightbox__button\" aria-label={messages().closeLightbox}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n <span class=\"k2b-content-lightbox__button-label\">{messages().close}</span>\n </button>\n </div>\n </div>\n\n <div class=\"k2b-content-lightbox__stage\">\n <Show when={current()}>\n {(image) => <img src={image().src} alt={image().alt ?? \"\"} class=\"k2b-content-lightbox__image\" draggable={false} />}\n </Show>\n </div>\n\n <Show when={isMultiple()}>\n <button\n type=\"button\"\n onClick={prev}\n class=\"k2b-content-lightbox__nav\"\n data-direction=\"previous\"\n aria-label={messages().previousImage}\n >\n <i class=\"ti ti-chevron-left\" aria-hidden=\"true\" />\n </button>\n <button type=\"button\" onClick={next} class=\"k2b-content-lightbox__nav\" data-direction=\"next\" aria-label={messages().nextImage}>\n <i class=\"ti ti-chevron-right\" aria-hidden=\"true\" />\n </button>\n </Show>\n\n <Show when={isMultiple() && props.images.length <= 10}>\n <div class=\"k2b-content-lightbox__dots\" role=\"group\" aria-label={messages().imageNavigation}>\n <For each={props.images}>\n {(_, i) => (\n <button\n type=\"button\"\n onClick={() => setIndex(i())}\n class=\"k2b-content-lightbox__dot\"\n aria-current={index() === i() ? \"true\" : undefined}\n aria-label={messages().goToImage({ index: i() + 1 })}\n />\n )}\n </For>\n </div>\n </Show>\n </dialog>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -16,11 +16,13 @@ var _tmpl$ = /*#__PURE__*/_$template(`<button type=button class=k2b-button data-
16
16
  _tmpl$3 = /*#__PURE__*/_$template(`<span role=alert class=k2b-content-pdf-preview__notice>`),
17
17
  _tmpl$4 = /*#__PURE__*/_$template(`<div class=k2b-content-pdf-preview__actions><!$><!/><!$><!/><!$><!/><!$><!/>`),
18
18
  _tmpl$5 = /*#__PURE__*/_$template(`<a class=k2b-button data-variant=secondary data-size=sm target=_blank rel=noopener><i class="ti ti-external-link"aria-hidden=true></i><!$><!/>`),
19
- _tmpl$6 = /*#__PURE__*/_$template(`<iframe class=k2b-content-pdf-preview__frame>`),
20
- _tmpl$7 = /*#__PURE__*/_$template(`<button type=button class=k2b-button data-variant=secondary data-size=sm>`),
21
- _tmpl$8 = /*#__PURE__*/_$template(`<h2 class=k2b-content-pdf-preview__title>`),
22
- _tmpl$9 = /*#__PURE__*/_$template(`<section><div class=k2b-content-pdf-preview__toolbar><div class=k2b-content-pdf-preview__heading></div><!$><!/></div><!$><!/>`);
19
+ _tmpl$6 = /*#__PURE__*/_$template(`<div class=k2b-content-pdf-preview__viewer><!$><!/><!$><!/>`),
20
+ _tmpl$7 = /*#__PURE__*/_$template(`<iframe class=k2b-content-pdf-preview__frame>`),
21
+ _tmpl$8 = /*#__PURE__*/_$template(`<button type=button class=k2b-button data-variant=secondary data-size=sm>`),
22
+ _tmpl$9 = /*#__PURE__*/_$template(`<h2 class=k2b-content-pdf-preview__title>`),
23
+ _tmpl$0 = /*#__PURE__*/_$template(`<section><div class=k2b-content-pdf-preview__toolbar><div class=k2b-content-pdf-preview__heading></div><!$><!/></div><!$><!/>`);
23
24
  import { createSignal, onCleanup, onMount, Show } from "solid-js";
25
+ import { announce } from "../feedback/announce";
24
26
  import { resolveUiMessages, useUiMessages } from "../intl/messages";
25
27
  import Placeholder from "../surfaces/Placeholder";
26
28
  const readErrorMessage = async response => {
@@ -44,15 +46,26 @@ export default function PdfPreview(props) {
44
46
  const [error, setError] = createSignal(null);
45
47
  const [failed, setFailed] = createSignal(false);
46
48
  const [tabBlocked, setTabBlocked] = createSignal(false);
49
+ // The frame has its document only once it fires `load`; until then the loading state covers it.
50
+ const [drawn, setDrawn] = createSignal(false);
47
51
  // An automatic preview shows one fixed document; the open action reuses it instead of requesting it again.
48
52
  let shownBlob = null;
49
53
  let openButton;
50
54
  let frame;
55
+ let viewer;
51
56
  let renderButton;
52
57
  let errorRetryButton;
53
58
  let disposed = false;
54
59
  let loadGeneration = 0;
55
60
  let openGeneration = 0;
61
+
62
+ // Without an inline PDF viewer (`navigator.pdfViewerEnabled === false`, as in Chrome on Android) a frame stays empty
63
+ // or downloads the file, so the viewer explains that instead and leaves the document to the open and download actions.
64
+ const inlineViewer = () => typeof navigator === "undefined" || navigator.pdfViewerEnabled !== false;
65
+ // There an automatic preview could show only that hint, so it requests nothing: the open action fetches the document
66
+ // when someone asks for it, and is also the retry after it fails. An on-demand preview still renders, because its
67
+ // request also checks the caller's input.
68
+ const openOnly = () => Boolean(props.autoLoad) && !inlineViewer();
56
69
  const revokeCurrent = () => {
57
70
  const current = url();
58
71
  if (current) URL.revokeObjectURL(current);
@@ -88,12 +101,18 @@ export default function PdfPreview(props) {
88
101
  const previousUrl = url();
89
102
  shownBlob = blob;
90
103
  setFailed(false);
104
+ setDrawn(false);
91
105
  setUrl(nextUrl);
92
106
  setLoading(false);
93
107
  if (previousUrl) URL.revokeObjectURL(previousUrl);
94
- // The shown document removes the retry action; keep keyboard focus in the actions instead of the page, or on the
95
- // document itself when the host shows only the content.
96
- if (retryFocused && document.activeElement === document.body) (openButton?.isConnected ? openButton : frame)?.focus();
108
+ // The loading status turns into the missing-viewer hint in place and stops being a live region, so say it here.
109
+ if (!inlineViewer()) {
110
+ announce(messages().pdfPreviewNoViewer);
111
+ announce(messages().pdfPreviewNoViewerDescription);
112
+ }
113
+ // The shown document removes the retry action; keep keyboard focus in the actions instead of the page, or, when
114
+ // the host shows only the content, in the viewer, which hands it on to the document once the frame has loaded it.
115
+ if (retryFocused && document.activeElement === document.body) (openButton?.isConnected ? openButton : viewer)?.focus();
97
116
  } catch (e) {
98
117
  if (disposed || generation !== loadGeneration) return;
99
118
  shownBlob = null;
@@ -105,9 +124,10 @@ export default function PdfPreview(props) {
105
124
  }
106
125
  };
107
126
  onMount(() => {
108
- // Hand the initial loading state to the request itself, which also covers a preview disabled by now.
127
+ // Hand the initial loading state to the request itself, which also covers a preview disabled by now or one that
128
+ // leaves the document to the open action.
109
129
  setLoading(false);
110
- if (props.autoLoad) void load();
130
+ if (props.autoLoad && !openOnly()) void load();
111
131
  });
112
132
  const openInNewTab = async () => {
113
133
  if (loading() || opening() || props.disabled?.()) return;
@@ -140,7 +160,7 @@ export default function PdfPreview(props) {
140
160
 
141
161
  // With autoLoad, rendering again would show the same document: offer it only until one is shown. After a failure the
142
162
  // default error state carries it as the retry; beside a caller's error content it stays in place while a retry loads.
143
- const renderable = () => !props.autoLoad || (failed() ? Boolean(props.renderError) : !url() && !loading());
163
+ const renderable = () => !openOnly() && (!props.autoLoad || (failed() ? Boolean(props.renderError) : !url() && !loading()));
144
164
  const actions = () => (() => {
145
165
  var _el$ = _$getNextElement(_tmpl$4),
146
166
  _el$1 = _el$.firstChild,
@@ -245,71 +265,118 @@ export default function PdfPreview(props) {
245
265
  }), _el$16, _co$6);
246
266
  return _el$;
247
267
  })();
248
- // Every state before a document is shown uses one placeholder in the viewer's box, so loading changes no layout.
249
- const content = () => _$createComponent(Show, {
250
- get when() {
251
- return error();
252
- },
253
- get fallback() {
254
- return _$createComponent(Show, {
255
- get when() {
256
- return url();
257
- },
258
- get fallback() {
259
- return _$createComponent(Placeholder, {
268
+ // Every state uses one placeholder in the viewer's box, so loading changes no layout: idle and loading before a
269
+ // document arrives, loading over the frame until it has loaded the document, and the hint where no frame can.
270
+ const status = () => {
271
+ if (error()) return null;
272
+ if (!inlineViewer() && (url() || openOnly() && !loading())) return "no-viewer";
273
+ if (!url()) return loading() ? "loading" : "idle";
274
+ return drawn() ? null : "loading";
275
+ };
276
+ const viewerBox = () => // While it shows a placeholder, the viewer itself holds keyboard focus that has nowhere else to go.
277
+ (() => {
278
+ var _el$25 = _$getNextElement(_tmpl$6),
279
+ _el$26 = _el$25.firstChild,
280
+ [_el$27, _co$9] = _$getNextMarker(_el$26.nextSibling),
281
+ _el$28 = _el$27.nextSibling,
282
+ [_el$29, _co$0] = _$getNextMarker(_el$28.nextSibling);
283
+ _$use(element => viewer = element, _el$25);
284
+ _$insert(_el$25, _$createComponent(Show, {
285
+ get when() {
286
+ return _$memo(() => !!(!error() && inlineViewer()))() && url();
287
+ },
288
+ children: currentUrl => (() => {
289
+ var _el$30 = _$getNextElement(_tmpl$7);
290
+ _el$30.addEventListener("load", () => {
291
+ const waiting = document.activeElement === viewer;
292
+ setDrawn(true);
293
+ if (waiting) frame?.focus();
294
+ });
295
+ _$use(element => {
296
+ frame = element;
297
+ // A new frame loads its document again, also one that was shown before a failed reload.
298
+ setDrawn(false);
299
+ }, _el$30);
300
+ _$effect(_p$ => {
301
+ var _v$5 = drawn() ? "" : undefined,
302
+ _v$6 = !drawn(),
303
+ _v$7 = currentUrl(),
304
+ _v$8 = props.title ?? messages().pdfPreview;
305
+ _v$5 !== _p$.e && _$setAttribute(_el$30, "data-drawn", _p$.e = _v$5);
306
+ _v$6 !== _p$.t && _$setProperty(_el$30, "inert", _p$.t = _v$6);
307
+ _v$7 !== _p$.a && _$setAttribute(_el$30, "src", _p$.a = _v$7);
308
+ _v$8 !== _p$.o && _$setAttribute(_el$30, "title", _p$.o = _v$8);
309
+ return _p$;
310
+ }, {
311
+ e: undefined,
312
+ t: undefined,
313
+ a: undefined,
314
+ o: undefined
315
+ });
316
+ return _el$30;
317
+ })()
318
+ }), _el$27, _co$9);
319
+ _$insert(_el$25, _$createComponent(Show, {
320
+ get when() {
321
+ return error();
322
+ },
323
+ get fallback() {
324
+ return _$createComponent(Show, {
325
+ get when() {
326
+ return status();
327
+ },
328
+ children: state => _$createComponent(Placeholder, {
260
329
  "class": "k2b-content-pdf-preview__placeholder",
261
330
  get state() {
262
- return loading() ? "loading" : "empty";
331
+ return state() === "loading" ? "loading" : "empty";
263
332
  },
264
333
  get icon() {
265
- return loading() ? undefined : "ti ti-file-type-pdf";
334
+ return state() === "loading" ? undefined : "ti ti-file-type-pdf";
266
335
  },
267
336
  get title() {
268
- return _$memo(() => !!loading())() ? messages().loading : undefined;
337
+ return _$memo(() => state() === "loading")() ? messages().loading : _$memo(() => state() === "no-viewer")() ? messages().pdfPreviewNoViewer : undefined;
269
338
  },
270
339
  get description() {
271
- return _$memo(() => !!loading())() ? undefined : props.emptyText ?? messages().renderPdfPreview;
340
+ return _$memo(() => state() === "idle")() ? props.emptyText ?? messages().renderPdfPreview : _$memo(() => state() === "no-viewer")() ? messages().pdfPreviewNoViewerDescription : undefined;
272
341
  }
273
- });
342
+ })
343
+ });
344
+ },
345
+ children: message => _$createComponent(Placeholder, {
346
+ "class": "k2b-content-pdf-preview__placeholder",
347
+ state: "error",
348
+ get description() {
349
+ return message();
274
350
  },
275
- children: currentUrl => (() => {
276
- var _el$25 = _$getNextElement(_tmpl$6);
277
- _$use(element => frame = element, _el$25);
278
- _$effect(_p$ => {
279
- var _v$5 = currentUrl(),
280
- _v$6 = props.title ?? messages().pdfPreview;
281
- _v$5 !== _p$.e && _$setAttribute(_el$25, "src", _p$.e = _v$5);
282
- _v$6 !== _p$.t && _$setAttribute(_el$25, "title", _p$.t = _v$6);
283
- return _p$;
284
- }, {
285
- e: undefined,
286
- t: undefined
287
- });
288
- return _el$25;
289
- })()
290
- });
351
+ get action() {
352
+ return (
353
+ // One retry at a time: the render action moves here only where the toolbar no longer offers it, and the
354
+ // open action is the retry of a preview that leaves the document to it.
355
+ _$memo(() => !!(renderable() || openOnly()))() ? undefined : (() => {
356
+ var _el$31 = _$getNextElement(_tmpl$8);
357
+ _el$31.$$click = () => void load();
358
+ _$use(element => errorRetryButton = element, _el$31);
359
+ _$insert(_el$31, () => props.buttonLabel ?? messages().retry);
360
+ _$effect(() => _$setProperty(_el$31, "disabled", opening() || props.disabled?.()));
361
+ _$runHydrationEvents();
362
+ return _el$31;
363
+ })()
364
+ );
365
+ }
366
+ })
367
+ }), _el$29, _co$0);
368
+ _$effect(() => _$setAttribute(_el$25, "tabindex", status() ? -1 : undefined));
369
+ return _el$25;
370
+ })();
371
+ // A caller's error content replaces the whole viewer, as it brings its own layout.
372
+ const content = () => _$createComponent(Show, {
373
+ get when() {
374
+ return _$memo(() => !!props.renderError)() ? error() : null;
291
375
  },
292
- children: message => props.renderError?.(message()) ?? _$createComponent(Placeholder, {
293
- "class": "k2b-content-pdf-preview__placeholder",
294
- state: "error",
295
- get description() {
296
- return message();
297
- },
298
- get action() {
299
- return (
300
- // One retry at a time: the render action moves here only where the toolbar no longer offers it.
301
- _$memo(() => !!renderable())() ? undefined : (() => {
302
- var _el$26 = _$getNextElement(_tmpl$7);
303
- _el$26.$$click = () => void load();
304
- _$use(element => errorRetryButton = element, _el$26);
305
- _$insert(_el$26, () => props.buttonLabel ?? messages().retry);
306
- _$effect(() => _$setProperty(_el$26, "disabled", opening() || props.disabled?.()));
307
- _$runHydrationEvents();
308
- return _el$26;
309
- })()
310
- );
311
- }
312
- })
376
+ get fallback() {
377
+ return viewerBox();
378
+ },
379
+ children: message => props.renderError?.(message())
313
380
  });
314
381
  if (props.children) return props.children({
315
382
  get actions() {
@@ -320,27 +387,27 @@ export default function PdfPreview(props) {
320
387
  }
321
388
  });
322
389
  return (() => {
323
- var _el$27 = _$getNextElement(_tmpl$9),
324
- _el$28 = _el$27.firstChild,
325
- _el$29 = _el$28.firstChild,
326
- _el$31 = _el$29.nextSibling,
327
- [_el$32, _co$9] = _$getNextMarker(_el$31.nextSibling),
328
- _el$33 = _el$28.nextSibling,
329
- [_el$34, _co$0] = _$getNextMarker(_el$33.nextSibling);
330
- _$insert(_el$29, _$createComponent(Show, {
390
+ var _el$32 = _$getNextElement(_tmpl$0),
391
+ _el$33 = _el$32.firstChild,
392
+ _el$34 = _el$33.firstChild,
393
+ _el$36 = _el$34.nextSibling,
394
+ [_el$37, _co$1] = _$getNextMarker(_el$36.nextSibling),
395
+ _el$38 = _el$33.nextSibling,
396
+ [_el$39, _co$10] = _$getNextMarker(_el$38.nextSibling);
397
+ _$insert(_el$34, _$createComponent(Show, {
331
398
  get when() {
332
399
  return props.title;
333
400
  },
334
401
  get children() {
335
- var _el$30 = _$getNextElement(_tmpl$8);
336
- _$insert(_el$30, () => props.title);
337
- return _el$30;
402
+ var _el$35 = _$getNextElement(_tmpl$9);
403
+ _$insert(_el$35, () => props.title);
404
+ return _el$35;
338
405
  }
339
406
  }));
340
- _$insert(_el$28, actions, _el$32, _co$9);
341
- _$insert(_el$27, content, _el$34, _co$0);
342
- _$effect(() => _$className(_el$27, `k2b-content-pdf-preview ${props.class ?? ""}`));
343
- return _el$27;
407
+ _$insert(_el$33, actions, _el$37, _co$1);
408
+ _$insert(_el$32, content, _el$39, _co$10);
409
+ _$effect(() => _$className(_el$32, `k2b-content-pdf-preview ${props.class ?? ""}`));
410
+ return _el$32;
344
411
  })();
345
412
  }
346
413
  _$delegateEvents(["click"]);