@edrlab/thorium-web 1.2.1 → 1.3.1

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 (100) hide show
  1. package/dist/{ThPreferencesAdapter-DrZ5_6Dv.d.mts → ThPreferencesAdapter-D0rzsGRl.d.mts} +50 -13
  2. package/dist/{ThSettingsWrapper-8Kx0SnH4.d.mts → ThSettingsWrapper-BXuRgdqp.d.mts} +42 -4
  3. package/dist/{actions-D2CHvCHu.d.mts → actions-BLAr0oaM.d.mts} +16 -4
  4. package/dist/{actionsReducer-kc-S130w.d.mts → actionsReducer-XWTGGNUd.d.mts} +46 -14
  5. package/dist/chunk-2ORXUOH3.mjs +134 -0
  6. package/dist/chunk-2ORXUOH3.mjs.map +1 -0
  7. package/dist/chunk-3GDQP6AS.mjs +14 -0
  8. package/dist/chunk-3GDQP6AS.mjs.map +1 -0
  9. package/dist/chunk-6BUN7DEA.mjs +854 -0
  10. package/dist/chunk-6BUN7DEA.mjs.map +1 -0
  11. package/dist/{chunk-IYAFKTPL.mjs → chunk-6EHFW43Y.mjs} +5 -4
  12. package/dist/chunk-6EHFW43Y.mjs.map +1 -0
  13. package/dist/chunk-7CGMWOZN.mjs +20 -0
  14. package/dist/chunk-7CGMWOZN.mjs.map +1 -0
  15. package/dist/{chunk-4VHEHMJN.mjs → chunk-A3FZBEUL.mjs} +228 -94
  16. package/dist/chunk-A3FZBEUL.mjs.map +1 -0
  17. package/dist/{chunk-NYZBHYW2.mjs → chunk-DETZMFZ7.mjs} +366 -61
  18. package/dist/chunk-DETZMFZ7.mjs.map +1 -0
  19. package/dist/{chunk-QPE574OW.mjs → chunk-DMZFSOHK.mjs} +28 -36
  20. package/dist/chunk-DMZFSOHK.mjs.map +1 -0
  21. package/dist/{chunk-7NEQAW7J.mjs → chunk-DTPO3J2C.mjs} +676 -930
  22. package/dist/chunk-DTPO3J2C.mjs.map +1 -0
  23. package/dist/{chunk-K3K7TUWM.mjs → chunk-EZG6SBSO.mjs} +358 -94
  24. package/dist/chunk-EZG6SBSO.mjs.map +1 -0
  25. package/dist/chunk-GPWW5OML.mjs +1955 -0
  26. package/dist/chunk-GPWW5OML.mjs.map +1 -0
  27. package/dist/{chunk-P4V3LA5R.mjs → chunk-I4BKU5NN.mjs} +13 -9
  28. package/dist/chunk-I4BKU5NN.mjs.map +1 -0
  29. package/dist/{chunk-47AIIJFO.mjs → chunk-ITDBOMY5.mjs} +3 -3
  30. package/dist/{chunk-47AIIJFO.mjs.map → chunk-ITDBOMY5.mjs.map} +1 -1
  31. package/dist/{chunk-XVSFXHYB.mjs → chunk-L4XGZAZ5.mjs} +23 -20
  32. package/dist/chunk-L4XGZAZ5.mjs.map +1 -0
  33. package/dist/{chunk-PXAUQJEU.mjs → chunk-LP3JFZ4A.mjs} +2425 -1634
  34. package/dist/chunk-LP3JFZ4A.mjs.map +1 -0
  35. package/dist/{chunk-72XCX5TD.mjs → chunk-NKO3K3QS.mjs} +14 -9
  36. package/dist/chunk-NKO3K3QS.mjs.map +1 -0
  37. package/dist/chunk-SAUOY37Q.mjs +862 -0
  38. package/dist/chunk-SAUOY37Q.mjs.map +1 -0
  39. package/dist/chunk-TEZB4ULX.mjs +57 -0
  40. package/dist/chunk-TEZB4ULX.mjs.map +1 -0
  41. package/dist/components/Audio/index.css +1858 -0
  42. package/dist/components/Audio/index.css.map +1 -0
  43. package/dist/components/Audio/index.d.mts +103 -0
  44. package/dist/components/Audio/index.mjs +23 -0
  45. package/dist/components/Audio/index.mjs.map +1 -0
  46. package/dist/components/Epub/index.css +365 -9
  47. package/dist/components/Epub/index.css.map +1 -1
  48. package/dist/components/Epub/index.d.mts +17 -19
  49. package/dist/components/Epub/index.mjs +18 -13
  50. package/dist/components/Misc/index.css +7 -4
  51. package/dist/components/Misc/index.css.map +1 -1
  52. package/dist/components/Misc/index.mjs +5 -133
  53. package/dist/components/Misc/index.mjs.map +1 -1
  54. package/dist/components/Reader/index.css +1022 -183
  55. package/dist/components/Reader/index.css.map +1 -1
  56. package/dist/components/Reader/index.d.mts +16 -16
  57. package/dist/components/Reader/index.mjs +124 -25
  58. package/dist/components/Reader/index.mjs.map +1 -1
  59. package/dist/components/WebPub/index.css +365 -9
  60. package/dist/components/WebPub/index.css.map +1 -1
  61. package/dist/components/WebPub/index.d.mts +16 -16
  62. package/dist/components/WebPub/index.mjs +18 -13
  63. package/dist/core/Components/index.d.mts +64 -15
  64. package/dist/core/Components/index.mjs +2 -1
  65. package/dist/core/Helpers/index.d.mts +2 -2
  66. package/dist/core/Helpers/index.mjs +4 -2
  67. package/dist/core/Hooks/index.d.mts +7 -8
  68. package/dist/core/Hooks/index.mjs +3 -1
  69. package/dist/i18n/index.mjs +6 -7
  70. package/dist/lib/index.d.mts +159 -15
  71. package/dist/lib/index.mjs +4 -2
  72. package/dist/lib-M3PPQDJJ.mjs +6548 -0
  73. package/dist/lib-M3PPQDJJ.mjs.map +1 -0
  74. package/dist/locales/en/thorium-web.json +22 -0
  75. package/dist/next-lib/index.mjs +2 -0
  76. package/dist/next-lib/index.mjs.map +1 -1
  77. package/dist/preferences/index.d.mts +111 -13
  78. package/dist/preferences/index.mjs +6 -3
  79. package/dist/{settingsReducer-C1wwCAMv.d.mts → settingsReducer-Bu1zeveu.d.mts} +1 -1
  80. package/dist/{ui-CamWuqOo.d.mts → ui-nBv8gfr0.d.mts} +20 -1
  81. package/dist/useAudioNavigator-C5aW4-eT.d.mts +133 -0
  82. package/dist/{useContrast-D6sjPjxy.d.mts → useContrast-2t429O9O.d.mts} +16 -8
  83. package/dist/usePreferences-VaBf46eP.d.mts +230 -0
  84. package/dist/useReaderTransitions-IBGdE7qi.d.mts +530 -0
  85. package/dist/{useTimeline-DyMx_aWY.d.mts → useTimeline-DCZ1qoCO.d.mts} +4 -2
  86. package/package.json +17 -13
  87. package/dist/chunk-4VHEHMJN.mjs.map +0 -1
  88. package/dist/chunk-72XCX5TD.mjs.map +0 -1
  89. package/dist/chunk-7NEQAW7J.mjs.map +0 -1
  90. package/dist/chunk-IYAFKTPL.mjs.map +0 -1
  91. package/dist/chunk-K3K7TUWM.mjs.map +0 -1
  92. package/dist/chunk-NYZBHYW2.mjs.map +0 -1
  93. package/dist/chunk-P4V3LA5R.mjs.map +0 -1
  94. package/dist/chunk-PXAUQJEU.mjs.map +0 -1
  95. package/dist/chunk-QPE574OW.mjs.map +0 -1
  96. package/dist/chunk-XVSFXHYB.mjs.map +0 -1
  97. package/dist/useEpubNavigator-CwHJfoiV.d.mts +0 -42
  98. package/dist/usePreferences-BXFJbval.d.mts +0 -43
  99. package/dist/useReaderTransitions-guT-eA-Q.d.mts +0 -365
  100. package/dist/useWebPubNavigator-CuSNQKMw.d.mts +0 -39
@@ -0,0 +1,854 @@
1
+ import { useReaderHeaderBase, thorium_web_reader_app_default, StatefulBackLink, thorium_web_reader_header_default, StatefulCollapsibleActionsBar, thorium_web_overflow_default, useNavigator } from './chunk-LP3JFZ4A.mjs';
2
+ import { useAppSelector, useAppDispatch, setHovering } from './chunk-EZG6SBSO.mjs';
3
+ import { usePreferences } from './chunk-GPWW5OML.mjs';
4
+ import { useI18n } from './chunk-6EHFW43Y.mjs';
5
+ import { ThInteractiveOverlay, ThHeader, ThFooter, ThRunningHead, ThPagination } from './chunk-DETZMFZ7.mjs';
6
+ import { isInteractiveElement, makeBreakpointsMap, getBestMatchingProgressionFormat } from './chunk-ITDBOMY5.mjs';
7
+ import { ThRunningHeadFormat, ThProgressionFormat } from './chunk-L4XGZAZ5.mjs';
8
+ import { getPlatform, buildShortcut } from './chunk-5LUMM7FW.mjs';
9
+ import { useRef, useCallback, useEffect, useMemo } from 'react';
10
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
11
+ import classNames from 'classnames';
12
+ import { useFocusWithin } from 'react-aria';
13
+
14
+ var StatefulReaderRunningHead = ({
15
+ formatPref
16
+ }) => {
17
+ const { t } = useI18n();
18
+ const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
19
+ const isImmersive = useAppSelector((state) => state.reader.isImmersive);
20
+ const isHovering = useAppSelector((state) => state.reader.isHovering);
21
+ const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
22
+ const breakpoint = useAppSelector((state) => state.theming.breakpoint);
23
+ const fallbackFormat = useMemo(() => ({
24
+ variants: "title" /* title */,
25
+ displayInImmersive: true,
26
+ displayInFullscreen: true
27
+ }), []);
28
+ const breakpointsMap = useMemo(() => {
29
+ return makeBreakpointsMap({
30
+ defaultValue: formatPref?.default || fallbackFormat,
31
+ fromEnum: ThRunningHeadFormat,
32
+ pref: formatPref?.breakpoints,
33
+ validateKey: "variants"
34
+ });
35
+ }, [formatPref, fallbackFormat]);
36
+ const currentPrefs = useMemo(() => {
37
+ if (!breakpoint) return formatPref?.default || fallbackFormat;
38
+ return breakpointsMap[breakpoint] || formatPref?.default || fallbackFormat;
39
+ }, [breakpoint, breakpointsMap, formatPref?.default, fallbackFormat]);
40
+ const { variants, displayInImmersive, displayInFullscreen } = currentPrefs;
41
+ const displayFormat = useMemo(() => {
42
+ if (!variants) return "title" /* title */;
43
+ if (isImmersive && displayInImmersive === false && !isHovering) {
44
+ return "none" /* none */;
45
+ }
46
+ if (isImmersive && isFullscreen && displayInFullscreen === false && !isHovering) {
47
+ return "none" /* none */;
48
+ }
49
+ return variants;
50
+ }, [variants, isImmersive, displayInImmersive, isHovering, isFullscreen, displayInFullscreen]);
51
+ const runningHead = useMemo(() => {
52
+ if (displayFormat === "title" /* title */) {
53
+ return unstableTimeline?.title || "";
54
+ } else if (displayFormat === "chapter" /* chapter */) {
55
+ return unstableTimeline?.progression?.currentChapter || unstableTimeline?.title || "";
56
+ }
57
+ return "";
58
+ }, [displayFormat, unstableTimeline]);
59
+ if (!runningHead || displayFormat === "none" /* none */) return null;
60
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
61
+ ThRunningHead,
62
+ {
63
+ label: runningHead,
64
+ "aria-label": t("reader.app.header.runningHead")
65
+ }
66
+ ) });
67
+ };
68
+ var StatefulReaderHeader = ({
69
+ actionKeys,
70
+ actionsOrder,
71
+ layout,
72
+ runningHeadFormatPref
73
+ }) => {
74
+ const {
75
+ headerRef,
76
+ focusWithinProps,
77
+ setHover,
78
+ removeHover,
79
+ listActionItems,
80
+ isImmersive,
81
+ isHovering,
82
+ isScroll,
83
+ t
84
+ } = useReaderHeaderBase(actionKeys);
85
+ const { preferences } = usePreferences();
86
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
87
+ /* @__PURE__ */ jsx(
88
+ ThInteractiveOverlay,
89
+ {
90
+ className: classNames(thorium_web_reader_app_default.barOverlay, thorium_web_reader_app_default.headerOverlay),
91
+ isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
92
+ onMouseEnter: setHover,
93
+ onMouseLeave: removeHover
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsxs(
97
+ ThHeader,
98
+ {
99
+ ref: headerRef,
100
+ className: classNames(thorium_web_reader_app_default.topBar, thorium_web_reader_header_default.header),
101
+ "aria-label": t("reader.app.header.label"),
102
+ onMouseEnter: setHover,
103
+ onMouseLeave: removeHover,
104
+ ...focusWithinProps,
105
+ children: [
106
+ preferences.theming.header?.backLink && /* @__PURE__ */ jsx(StatefulBackLink, { className: thorium_web_reader_header_default.backlinkWrapper }),
107
+ /* @__PURE__ */ jsx(StatefulReaderRunningHead, { formatPref: runningHeadFormatPref }),
108
+ /* @__PURE__ */ jsx(
109
+ StatefulCollapsibleActionsBar,
110
+ {
111
+ id: "reader-header-overflowMenu",
112
+ items: listActionItems(),
113
+ prefs: { ...preferences.actions, displayOrder: actionsOrder },
114
+ className: thorium_web_reader_header_default.actionsWrapper,
115
+ "aria-label": t("reader.app.header.actions"),
116
+ overflowMenuClassName: !isScroll || preferences.affordances.scroll.hintInImmersive ? thorium_web_overflow_default.hint : void 0
117
+ }
118
+ )
119
+ ]
120
+ }
121
+ )
122
+ ] });
123
+ };
124
+
125
+ // src/components/assets/styles/thorium-web.reader.pagination.module.css
126
+ var thorium_web_reader_pagination_default = {
127
+ wrapper: "thorium_web_reader_pagination_wrapper",
128
+ listItem: "thorium_web_reader_pagination_listItem",
129
+ previousButton: "thorium_web_reader_pagination_previousButton",
130
+ progression: "thorium_web_reader_pagination_progression",
131
+ nextButton: "thorium_web_reader_pagination_nextButton",
132
+ label: "thorium_web_reader_pagination_label"
133
+ };
134
+
135
+ // src/components/assets/styles/thorium-web.reader.progression.module.css
136
+ var thorium_web_reader_progression_default = {
137
+ wrapper: "thorium_web_reader_progression_wrapper"
138
+ };
139
+ var ThProgression = ({
140
+ ref,
141
+ children,
142
+ ...props
143
+ }) => {
144
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
145
+ "div",
146
+ {
147
+ ref,
148
+ ...props,
149
+ children
150
+ }
151
+ ) });
152
+ };
153
+ var StatefulReaderProgression = ({
154
+ className,
155
+ formatPref,
156
+ fallbackVariant
157
+ }) => {
158
+ const { t } = useI18n();
159
+ const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
160
+ const isImmersive = useAppSelector((state) => state.reader.isImmersive);
161
+ const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
162
+ const isHovering = useAppSelector((state) => state.reader.isHovering);
163
+ const breakpoint = useAppSelector((state) => state.theming.breakpoint);
164
+ useMemo(() => {
165
+ if (!unstableTimeline?.progression) return "";
166
+ const {
167
+ currentPositions = [],
168
+ totalPositions,
169
+ relativeProgression,
170
+ totalProgression,
171
+ currentChapter,
172
+ positionsLeft,
173
+ totalItems,
174
+ currentIndex
175
+ } = unstableTimeline.progression;
176
+ return JSON.stringify({
177
+ currentPositions,
178
+ totalPositions,
179
+ relativeProgression,
180
+ totalProgression,
181
+ currentChapter,
182
+ positionsLeft,
183
+ totalItems,
184
+ currentIndex
185
+ });
186
+ }, [unstableTimeline?.progression]);
187
+ const fallbackFormat = useMemo(() => {
188
+ return {
189
+ variants: fallbackVariant,
190
+ displayInImmersive: true,
191
+ displayInFullscreen: true
192
+ };
193
+ }, [fallbackVariant]);
194
+ const breakpointsMap = useMemo(() => {
195
+ return makeBreakpointsMap({
196
+ defaultValue: formatPref?.default || fallbackFormat,
197
+ fromEnum: ThProgressionFormat,
198
+ pref: formatPref?.breakpoints,
199
+ validateKey: "variants"
200
+ });
201
+ }, [formatPref, fallbackFormat]);
202
+ const currentPrefs = useMemo(() => {
203
+ if (!breakpoint) return formatPref?.default || fallbackFormat;
204
+ return breakpointsMap[breakpoint] || formatPref?.default || fallbackFormat;
205
+ }, [breakpoint, breakpointsMap, formatPref?.default, fallbackFormat]);
206
+ const { variants, displayInImmersive, displayInFullscreen } = currentPrefs;
207
+ const displayFormat = useMemo(() => {
208
+ if (!variants) return fallbackFormat.variants;
209
+ if (isImmersive && displayInImmersive === false && !isHovering) {
210
+ return "none" /* none */;
211
+ }
212
+ if (isImmersive && isFullscreen && displayInFullscreen === false && !isHovering) {
213
+ return "none" /* none */;
214
+ }
215
+ if (Array.isArray(variants)) {
216
+ return getBestMatchingProgressionFormat(variants, unstableTimeline?.progression) || fallbackFormat.variants;
217
+ }
218
+ return variants;
219
+ }, [variants, unstableTimeline?.progression, fallbackFormat, isImmersive, isHovering, isFullscreen, displayInImmersive, displayInFullscreen]);
220
+ const displayText = useMemo(() => {
221
+ if (displayFormat === "none" /* none */ || !unstableTimeline?.progression) {
222
+ return "";
223
+ }
224
+ const {
225
+ currentPositions = [],
226
+ totalPositions,
227
+ relativeProgression,
228
+ totalProgression,
229
+ currentChapter,
230
+ positionsLeft,
231
+ totalItems,
232
+ currentIndex
233
+ } = unstableTimeline.progression;
234
+ let text = "";
235
+ const formatPositions = (positions) => {
236
+ if (positions.length === 2) {
237
+ return positions.join("\u2013");
238
+ }
239
+ return positions[0]?.toString() || "";
240
+ };
241
+ switch (displayFormat) {
242
+ case "positions" /* positions */:
243
+ if (currentPositions.length > 0) {
244
+ text = formatPositions(currentPositions);
245
+ }
246
+ break;
247
+ case "positionsOfTotal" /* positionsOfTotal */:
248
+ if (currentPositions.length > 0 && totalPositions) {
249
+ text = t("reader.progression.xOfY.compact", {
250
+ x: formatPositions(currentPositions),
251
+ y: totalPositions
252
+ });
253
+ }
254
+ break;
255
+ case "positionsPercentOfTotal" /* positionsPercentOfTotal */:
256
+ if (currentPositions.length > 0 && totalPositions) {
257
+ const percentage = Math.round((totalProgression || 0) * 100);
258
+ text = t("reader.progression.xOfY.descriptive", {
259
+ x: formatPositions(currentPositions),
260
+ y: totalPositions,
261
+ z: `${percentage}%`
262
+ });
263
+ }
264
+ break;
265
+ case "positionsLeft" /* positionsLeft */:
266
+ if (positionsLeft !== void 0) {
267
+ text = t(`reader.progression.positionsLeftInChapter.descriptive`, {
268
+ count: positionsLeft
269
+ });
270
+ }
271
+ break;
272
+ case "overallProgression" /* overallProgression */:
273
+ if (totalProgression !== void 0) {
274
+ const percentage = Math.round(totalProgression * 100);
275
+ text = `${percentage}%`;
276
+ }
277
+ break;
278
+ case "resourceProgression" /* resourceProgression */:
279
+ if (relativeProgression !== void 0) {
280
+ const percentage = Math.round(relativeProgression * 100);
281
+ text = `${percentage}%`;
282
+ }
283
+ break;
284
+ case "progressionOfResource" /* progressionOfResource */:
285
+ if (relativeProgression !== void 0) {
286
+ const percentage = Math.round(relativeProgression * 100);
287
+ text = t("reader.progression.xOfY.compact", {
288
+ x: `${percentage}%`,
289
+ y: currentChapter || t("reader.app.progression.referenceFallback")
290
+ });
291
+ }
292
+ break;
293
+ case "readingOrderIndex" /* readingOrderIndex */:
294
+ if (currentIndex !== void 0 && totalItems !== void 0) {
295
+ text = t("reader.progression.xOfY.compact", {
296
+ x: currentIndex,
297
+ y: totalItems
298
+ });
299
+ }
300
+ break;
301
+ }
302
+ return text;
303
+ }, [displayFormat, unstableTimeline, t]);
304
+ if (!displayText || displayFormat === "none" /* none */) {
305
+ return null;
306
+ }
307
+ return /* @__PURE__ */ jsx(
308
+ ThProgression,
309
+ {
310
+ id: "current-progression",
311
+ className: classNames(thorium_web_reader_progression_default.wrapper, className),
312
+ "aria-label": t("reader.app.progression.wrapper"),
313
+ children: displayText
314
+ }
315
+ );
316
+ };
317
+ var StatefulReaderPagination = ({
318
+ ref,
319
+ links,
320
+ compounds,
321
+ children,
322
+ ...props
323
+ }) => {
324
+ const previousButtonRef = useRef(null);
325
+ const nextButtonRef = useRef(null);
326
+ const updatedCompounds = {
327
+ ...compounds,
328
+ previousButton: {
329
+ ...compounds?.previousButton,
330
+ ref: previousButtonRef,
331
+ onKeyDown: (e) => {
332
+ if (e.key === "Escape") {
333
+ previousButtonRef.current?.blur();
334
+ }
335
+ }
336
+ },
337
+ nextButton: {
338
+ ...compounds?.nextButton,
339
+ ref: nextButtonRef,
340
+ onKeyDown: (e) => {
341
+ if (e.key === "Escape") {
342
+ nextButtonRef.current?.blur();
343
+ }
344
+ }
345
+ }
346
+ };
347
+ return /* @__PURE__ */ jsx(
348
+ ThPagination,
349
+ {
350
+ ref,
351
+ className: thorium_web_reader_pagination_default.wrapper,
352
+ links,
353
+ compounds: updatedCompounds,
354
+ ...props,
355
+ children
356
+ }
357
+ );
358
+ };
359
+ var StatefulReaderFooter = ({
360
+ layout,
361
+ progressionFormatPref,
362
+ progressionFormatFallback
363
+ }) => {
364
+ const { t } = useI18n();
365
+ const footerRef = useRef(null);
366
+ const readerProfile = useAppSelector((state) => state.reader.profile);
367
+ const isImmersive = useAppSelector((state) => state.reader.isImmersive);
368
+ const isHovering = useAppSelector((state) => state.reader.isHovering);
369
+ const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
370
+ const scroll = useAppSelector((state) => state.settings.scroll);
371
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
372
+ const isScroll = scroll && !isFXL;
373
+ const breakpoint = useAppSelector((state) => state.theming.breakpoint);
374
+ const reducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
375
+ const timeline = useAppSelector((state) => state.publication.unstableTimeline);
376
+ const dispatch = useAppDispatch();
377
+ const { focusWithinProps } = useFocusWithin({
378
+ onFocusWithin() {
379
+ dispatch(setHovering(true));
380
+ },
381
+ onBlurWithin() {
382
+ dispatch(setHovering(false));
383
+ }
384
+ });
385
+ const setHover = () => {
386
+ if (!hasScrollAffordance) {
387
+ dispatch(setHovering(true));
388
+ }
389
+ };
390
+ const removeHover = () => {
391
+ if (!hasScrollAffordance) {
392
+ dispatch(setHovering(false));
393
+ }
394
+ };
395
+ const { previousLocator, nextLocator, go } = useNavigator().unified;
396
+ const updateLinks = useCallback(() => {
397
+ const links = {
398
+ previous: void 0,
399
+ next: void 0
400
+ };
401
+ const previous = previousLocator();
402
+ const next = nextLocator();
403
+ if (previous) {
404
+ links.previous = {
405
+ node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
406
+ /* @__PURE__ */ jsx("span", { className: thorium_web_reader_app_default.srOnly, children: t(isFXL ? "reader.actions.goToPreviousPage.descriptive" : "reader.actions.goToPreviousChapter.descriptive") }),
407
+ /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: timeline?.previousItem?.title || previous.title || t(isFXL ? "reader.actions.goToPreviousPage.compact" : "reader.actions.goToPreviousChapter.compact") })
408
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: t(isFXL ? "reader.actions.goToPreviousPage.compact" : "reader.actions.goToPreviousChapter.compact") }) }),
409
+ onPress: () => go(previous, !reducedMotion, () => {
410
+ })
411
+ };
412
+ }
413
+ if (next) {
414
+ links.next = {
415
+ node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
416
+ /* @__PURE__ */ jsx("span", { className: thorium_web_reader_app_default.srOnly, children: t(isFXL ? "reader.actions.goToNextPage.descriptive" : "reader.actions.goToNextChapter.descriptive") }),
417
+ /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: timeline?.nextItem?.title || next.title || t(isFXL ? "reader.actions.goToNextPage.compact" : "reader.actions.goToNextChapter.compact") })
418
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: t(isFXL ? "reader.actions.goToNextPage.compact" : "reader.actions.goToNextChapter.compact") }) }),
419
+ onPress: () => go(next, !reducedMotion, () => {
420
+ })
421
+ };
422
+ }
423
+ return links;
424
+ }, [go, previousLocator, nextLocator, t, timeline, breakpoint, reducedMotion, isFXL]);
425
+ useEffect(() => {
426
+ updateLinks();
427
+ }, [timeline, updateLinks]);
428
+ useEffect(() => {
429
+ if (isImmersive) {
430
+ const focusElement = document.activeElement;
431
+ if (focusElement && footerRef.current?.contains(focusElement)) {
432
+ focusElement.blur();
433
+ }
434
+ }
435
+ }, [isImmersive]);
436
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
437
+ /* @__PURE__ */ jsx(
438
+ ThInteractiveOverlay,
439
+ {
440
+ className: classNames(thorium_web_reader_app_default.barOverlay, thorium_web_reader_app_default.footerOverlay),
441
+ isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
442
+ onMouseEnter: setHover,
443
+ onMouseLeave: removeHover
444
+ }
445
+ ),
446
+ /* @__PURE__ */ jsx(
447
+ ThFooter,
448
+ {
449
+ ref: footerRef,
450
+ className: thorium_web_reader_app_default.bottomBar,
451
+ "aria-label": t("reader.app.footer.label"),
452
+ onMouseEnter: setHover,
453
+ onMouseLeave: removeHover,
454
+ ...focusWithinProps,
455
+ children: isScroll || readerProfile === "webPub" ? /* @__PURE__ */ jsx(
456
+ StatefulReaderPagination,
457
+ {
458
+ "aria-label": t("reader.navigation.scroll.wrapper"),
459
+ links: updateLinks(),
460
+ compounds: {
461
+ listItem: {
462
+ className: thorium_web_reader_pagination_default.listItem
463
+ },
464
+ previousButton: {
465
+ className: thorium_web_reader_pagination_default.previousButton,
466
+ preventFocusOnPress: true
467
+ },
468
+ nextButton: {
469
+ className: thorium_web_reader_pagination_default.nextButton,
470
+ preventFocusOnPress: true
471
+ }
472
+ },
473
+ children: /* @__PURE__ */ jsx(
474
+ StatefulReaderProgression,
475
+ {
476
+ className: thorium_web_reader_pagination_default.progression,
477
+ formatPref: progressionFormatPref,
478
+ fallbackVariant: progressionFormatFallback
479
+ }
480
+ )
481
+ }
482
+ ) : /* @__PURE__ */ jsx(
483
+ StatefulReaderProgression,
484
+ {
485
+ formatPref: progressionFormatPref,
486
+ fallbackVariant: progressionFormatFallback
487
+ }
488
+ )
489
+ }
490
+ )
491
+ ] });
492
+ };
493
+
494
+ // src/core/Hooks/fonts/androidPatchCss.ts
495
+ var getAndroidPatchCss = () => {
496
+ if (typeof window === "undefined") {
497
+ return "";
498
+ }
499
+ const origin = window.location.origin;
500
+ return `/* Readium CSS
501
+ Android Fonts Patch module
502
+
503
+ A stylesheet aligning Android generic serif and sans-serif fonts on other platforms
504
+
505
+ Repo: https://github.com/readium/css */
506
+
507
+ /* Android resolves sans-serif to Roboto, and serif to Droid Serif
508
+ This created issues for FXL EPUBs relying on Times (New Roman),
509
+ Helvetica or Arial while not embedding the font files in the package.
510
+
511
+ See https://github.com/readium/css/issues/149
512
+
513
+ Unfortunately it is no possible to target generic family using @font-face,
514
+ we have to target specific font-family names e.g. Times, Arial, etc.
515
+
516
+ This stylesheet/patch should be loaded only for this case i.e.
517
+ a Fixed-Layout EPUB with text but no embedded font on an Android device.
518
+ The logic for checking these conditions are up to implementers.
519
+ */
520
+
521
+ /* Serif (Times + Times New Roman) */
522
+
523
+ @font-face {
524
+ font-family: Times;
525
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman.woff", origin).toString()}") format("woff");
526
+ font-weight: normal;
527
+ font-style: normal;
528
+ }
529
+
530
+ @font-face {
531
+ font-family: "Times New Roman";
532
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman.woff", origin).toString()}") format("woff");
533
+ font-weight: normal;
534
+ font-style: normal;
535
+ }
536
+
537
+ @font-face {
538
+ font-family: Times;
539
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Italic.woff", origin).toString()}") format("woff");
540
+ font-weight: normal;
541
+ font-style: italic;
542
+ }
543
+
544
+ @font-face {
545
+ font-family: "Times New Roman";
546
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Italic.woff", origin).toString()}") format("woff");
547
+ font-weight: normal;
548
+ font-style: italic;
549
+ }
550
+
551
+ @font-face {
552
+ font-family: Times;
553
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Bold.woff", origin).toString()}") format("woff");
554
+ font-weight: bold;
555
+ font-style: normal;
556
+ }
557
+
558
+ @font-face {
559
+ font-family: "Times New Roman";
560
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Bold.woff", origin).toString()}") format("woff");
561
+ font-weight: bold;
562
+ font-style: normal;
563
+ }
564
+
565
+ @font-face {
566
+ font-family: Times;
567
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-BoldItalic.woff", origin).toString()}") format("woff");
568
+ font-weight: bold;
569
+ font-style: italic;
570
+ }
571
+
572
+ @font-face {
573
+ font-family: "Times New Roman";
574
+ src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-BoldItalic.woff", origin).toString()}") format("woff");
575
+ font-weight: bold;
576
+ font-style: italic;
577
+ }
578
+
579
+ /* Sans-serif (Helvetica + Arial) */
580
+
581
+ @font-face {
582
+ font-family: Helvetica;
583
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans.woff", origin).toString()}") format("woff");
584
+ font-weight: normal;
585
+ font-style: normal;
586
+ }
587
+
588
+ @font-face {
589
+ font-family: Arial;
590
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans.woff", origin).toString()}") format("woff");
591
+ font-weight: normal;
592
+ font-style: normal;
593
+ }
594
+
595
+ @font-face {
596
+ font-family: Helvetica;
597
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Italic.woff", origin).toString()}") format("woff");
598
+ font-weight: normal;
599
+ font-style: italic;
600
+ }
601
+
602
+ @font-face {
603
+ font-family: Arial;
604
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Italic.woff", origin).toString()}") format("woff");
605
+ font-weight: normal;
606
+ font-style: italic;
607
+ }
608
+
609
+ @font-face {
610
+ font-family: Helvetica;
611
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Bold.woff", origin).toString()}") format("woff");
612
+ font-weight: bold;
613
+ font-style: normal;
614
+ }
615
+
616
+ @font-face {
617
+ font-family: Arial;
618
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Bold.woff", origin).toString()}") format("woff");
619
+ font-weight: bold;
620
+ font-style: normal;
621
+ }
622
+
623
+ @font-face {
624
+ font-family: Helvetica;
625
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-BoldItalic.woff", origin).toString()}") format("woff");
626
+ font-weight: bold;
627
+ font-style: italic;
628
+ }
629
+
630
+ @font-face {
631
+ font-family: Arial;
632
+ src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-BoldItalic.woff", origin).toString()}") format("woff");
633
+ font-weight: bold;
634
+ font-style: italic;
635
+ }`;
636
+ };
637
+
638
+ // src/core/Hooks/fonts/useFonts.ts
639
+ var useFonts = (fontResources) => {
640
+ const injectedElementsRef = useRef({
641
+ prepend: [],
642
+ append: []
643
+ });
644
+ const createLinkElement = useCallback((resource) => {
645
+ const link = document.createElement("link");
646
+ if ("attributes" in resource && resource.attributes) {
647
+ Object.entries(resource.attributes).forEach(([key, value]) => {
648
+ link.setAttribute(key, value);
649
+ });
650
+ }
651
+ link.rel = resource.rel;
652
+ link.as = resource.as;
653
+ if ("url" in resource) {
654
+ link.href = resource.url;
655
+ } else if ("blob" in resource && resource.blob) {
656
+ link.href = URL.createObjectURL(resource.blob);
657
+ }
658
+ return link;
659
+ }, []);
660
+ const removeInjectedElements = useCallback(() => {
661
+ const { prepend, append } = injectedElementsRef.current;
662
+ [...prepend, ...append].forEach((element) => {
663
+ if (element.parentNode) {
664
+ element.parentNode.removeChild(element);
665
+ }
666
+ if (element instanceof HTMLLinkElement && element.href.startsWith("blob:")) {
667
+ URL.revokeObjectURL(element.href);
668
+ }
669
+ });
670
+ injectedElementsRef.current = {
671
+ prepend: [],
672
+ append: []
673
+ };
674
+ }, []);
675
+ const injectFontResources = useCallback((resources) => {
676
+ if (typeof document === "undefined") return;
677
+ removeInjectedElements();
678
+ if (!resources) return;
679
+ const { prepend, append } = resources;
680
+ const injectedElements = injectedElementsRef.current;
681
+ prepend.forEach((resource) => {
682
+ const element = createLinkElement(resource);
683
+ document.head.insertBefore(element, document.head.firstChild);
684
+ injectedElements.prepend.push(element);
685
+ });
686
+ append.forEach((resource) => {
687
+ const element = createLinkElement(resource);
688
+ document.head.appendChild(element);
689
+ injectedElements.append.push(element);
690
+ });
691
+ }, [createLinkElement, removeInjectedElements]);
692
+ const getAndroidFXLPatch = useCallback(() => {
693
+ const platform = getPlatform();
694
+ const isAndroid = platform === "android";
695
+ if (!isAndroid) {
696
+ return null;
697
+ }
698
+ const cssContent = getAndroidPatchCss();
699
+ const blob = new Blob([cssContent], { type: "text/css" });
700
+ return {
701
+ as: "link",
702
+ rel: "stylesheet",
703
+ blob
704
+ };
705
+ }, []);
706
+ useEffect(() => {
707
+ injectFontResources(fontResources || null);
708
+ return () => {
709
+ removeInjectedElements();
710
+ };
711
+ }, [fontResources, injectFontResources, removeInjectedElements]);
712
+ return {
713
+ injectFontResources,
714
+ removeFontResources: removeInjectedElements,
715
+ getAndroidFXLPatch
716
+ };
717
+ };
718
+
719
+ // src/helpers/peripherals.ts
720
+ var Peripherals = class {
721
+ observers = ["keydown"];
722
+ targets = [];
723
+ callbacks;
724
+ store;
725
+ actionsPref;
726
+ shortcuts;
727
+ constructor(store, actionsPref, callbacks) {
728
+ this.observers.forEach((method) => {
729
+ this["on" + method] = this["on" + method].bind(this);
730
+ });
731
+ this.store = store;
732
+ this.actionsPref = actionsPref;
733
+ this.callbacks = callbacks;
734
+ this.shortcuts = this.retrieveShortcuts();
735
+ }
736
+ getPlatformModifier() {
737
+ return this.store.getState().reader.platformModifier.modifier;
738
+ }
739
+ retrieveShortcuts() {
740
+ if (!this.actionsPref) return {};
741
+ const shortcutsObj = {};
742
+ const displayOrder = this.store.getState().publication.isFXL ? this.actionsPref.fxlOrder : this.actionsPref.reflowOrder;
743
+ for (const actionKey of displayOrder) {
744
+ const shortcutString = this.actionsPref.keys[actionKey].shortcut;
745
+ if (shortcutString) {
746
+ const shortcutObj = buildShortcut(shortcutString);
747
+ if (shortcutObj?.key) {
748
+ Object.defineProperty(shortcutsObj, shortcutObj.key, {
749
+ value: {
750
+ actionKey,
751
+ modifiers: shortcutObj.modifiers
752
+ },
753
+ writable: false,
754
+ enumerable: true
755
+ });
756
+ }
757
+ }
758
+ }
759
+ return shortcutsObj;
760
+ }
761
+ destroy() {
762
+ this.targets.forEach((t) => this.unobserve(t));
763
+ }
764
+ unobserve(item) {
765
+ if (!item) return;
766
+ this.observers.forEach((EventName) => {
767
+ item.removeEventListener(
768
+ EventName,
769
+ this["on" + EventName],
770
+ false
771
+ );
772
+ });
773
+ this.targets = this.targets.filter((t) => t !== item);
774
+ }
775
+ observe(item) {
776
+ if (!item) return;
777
+ if (this.targets.includes(item)) return;
778
+ this.observers.forEach((EventName) => {
779
+ item.addEventListener(EventName, this["on" + EventName], false);
780
+ });
781
+ this.targets.push(item);
782
+ }
783
+ onkeydown(e) {
784
+ const focusIsSafe = !isInteractiveElement(document.activeElement);
785
+ switch (e.code) {
786
+ case "Space":
787
+ focusIsSafe && this.callbacks.goProgression(e.shiftKey);
788
+ break;
789
+ case "ArrowRight":
790
+ focusIsSafe && this.callbacks.moveTo("right");
791
+ break;
792
+ case "ArrowLeft":
793
+ focusIsSafe && this.callbacks.moveTo("left");
794
+ break;
795
+ case "ArrowUp":
796
+ case "PageUp":
797
+ focusIsSafe && this.callbacks.moveTo("up");
798
+ break;
799
+ case "ArrowDown":
800
+ case "PageDown":
801
+ focusIsSafe && this.callbacks.moveTo("down");
802
+ break;
803
+ case "Home":
804
+ focusIsSafe && this.callbacks.moveTo("home");
805
+ break;
806
+ case "End":
807
+ focusIsSafe && this.callbacks.moveTo("end");
808
+ break;
809
+ default:
810
+ if (this.shortcuts.hasOwnProperty(e.code)) {
811
+ const customShortcutObj = this.shortcuts[e.code];
812
+ const sendCallback = Object.entries(customShortcutObj.modifiers).every(([modifier, value]) => {
813
+ if (modifier === "platformKey") {
814
+ return e[this.getPlatformModifier()] === value;
815
+ } else {
816
+ return e[modifier] === value;
817
+ }
818
+ });
819
+ if (sendCallback) {
820
+ e.preventDefault();
821
+ this.callbacks.toggleAction(customShortcutObj.actionKey);
822
+ }
823
+ }
824
+ break;
825
+ }
826
+ }
827
+ };
828
+ var LAYOUT_CLASSES = {
829
+ ["stacked-ui" /* stacked */]: "thorium_web_stackedUI",
830
+ ["layered-ui" /* layered */]: "thorium_web_layeredUI"
831
+ };
832
+ function getReaderClassNames(options) {
833
+ const {
834
+ layoutUI,
835
+ isScroll,
836
+ isImmersive = false,
837
+ isHovering = false,
838
+ isFXL = false,
839
+ breakpoint
840
+ } = options;
841
+ return classNames(
842
+ thorium_web_reader_app_default.shell,
843
+ isScroll ? "thorium_web_isScroll" : "thorium_web_isPaged",
844
+ isImmersive && "thorium_web_isImmersive",
845
+ isHovering && "thorium_web_isHovering",
846
+ isFXL ? "thorium_web_isFXL" : "thorium_web_isReflow",
847
+ LAYOUT_CLASSES[layoutUI],
848
+ breakpoint ? `thorium_web_is${breakpoint.charAt(0).toUpperCase() + breakpoint.slice(1)}` : void 0
849
+ );
850
+ }
851
+
852
+ export { Peripherals, StatefulReaderFooter, StatefulReaderHeader, getReaderClassNames, useFonts };
853
+ //# sourceMappingURL=chunk-6BUN7DEA.mjs.map
854
+ //# sourceMappingURL=chunk-6BUN7DEA.mjs.map