@marianmeres/stuic 3.31.1 → 3.32.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.
|
@@ -741,6 +741,7 @@
|
|
|
741
741
|
<!-- Sheets (3D flippable elements) — no static pages needed -->
|
|
742
742
|
{#each sheets as sheet (sheet.id)}
|
|
743
743
|
{@const flipped = sheet.id < activeSpread}
|
|
744
|
+
{@const isNearby = Math.abs(sheet.id - activeSpread) <= 2}
|
|
744
745
|
<div
|
|
745
746
|
class={twMerge(!unstyled && "stuic-book-sheet")}
|
|
746
747
|
data-flipped={flipped ? "true" : undefined}
|
|
@@ -755,7 +756,7 @@
|
|
|
755
756
|
data-placeholder={!sheet.frontPage && sheet.backPage ? "" : undefined}
|
|
756
757
|
onclick={(e) => handlePageClick(e, sheet.frontPage)}
|
|
757
758
|
>
|
|
758
|
-
{#if sheet.frontPage}
|
|
759
|
+
{#if isNearby && sheet.frontPage}
|
|
759
760
|
{#if renderPage}
|
|
760
761
|
{@render renderPage({
|
|
761
762
|
page: sheet.frontPage,
|
|
@@ -844,7 +845,7 @@
|
|
|
844
845
|
data-placeholder={!sheet.backPage && sheet.frontPage ? "" : undefined}
|
|
845
846
|
onclick={(e) => handlePageClick(e, sheet.backPage)}
|
|
846
847
|
>
|
|
847
|
-
{#if sheet.backPage}
|
|
848
|
+
{#if isNearby && sheet.backPage}
|
|
848
849
|
{#if renderPage}
|
|
849
850
|
{@render renderPage({
|
|
850
851
|
page: sheet.backPage,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Props as BookProps } from "./Book.svelte";
|
|
3
|
+
|
|
4
|
+
export interface Props extends Omit<BookProps, "responsive" | "singlePage"> {
|
|
5
|
+
/** Minimum page width (px) before switching to single-page mode (default: 150) */
|
|
6
|
+
minPageWidth?: number;
|
|
7
|
+
/** Resize debounce delay in ms (default: 150) */
|
|
8
|
+
debounce?: number;
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import Book, { computeBookPageSize, type BookPage } from "./Book.svelte";
|
|
14
|
+
import { waitForTwoRepaints } from "../../utils/paint.js";
|
|
15
|
+
|
|
16
|
+
let bookRef: ReturnType<typeof Book> | undefined = $state();
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
pages,
|
|
20
|
+
minPageWidth = 150,
|
|
21
|
+
debounce: debounceMs = 150,
|
|
22
|
+
activeSpread = $bindable(0),
|
|
23
|
+
...rest
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
// derive aspect ratio from actual page dimensions
|
|
27
|
+
const ratio = $derived.by(() => {
|
|
28
|
+
const size = computeBookPageSize(pages);
|
|
29
|
+
return size.width / size.height;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
let containerEl: HTMLDivElement | undefined = $state();
|
|
33
|
+
let pageWidth = $state(0);
|
|
34
|
+
let pageHeight = $state(0);
|
|
35
|
+
let forceSingle = $state(false);
|
|
36
|
+
let resizing = $state(false);
|
|
37
|
+
let measured = $state(false);
|
|
38
|
+
|
|
39
|
+
// ---- Proxy API (safe no-ops during remount) ----
|
|
40
|
+
|
|
41
|
+
export function next() { bookRef?.next(); }
|
|
42
|
+
export function previous() { bookRef?.previous(); }
|
|
43
|
+
export function goTo(spreadIndex: number) { bookRef?.goTo(spreadIndex); }
|
|
44
|
+
export function goToPage(pageId: BookPage["id"]) { bookRef?.goToPage(pageId); }
|
|
45
|
+
export function zoomIn() { bookRef?.zoomIn(); }
|
|
46
|
+
export function zoomOut() { bookRef?.zoomOut(); }
|
|
47
|
+
export function resetZoom() { bookRef?.resetZoom(); }
|
|
48
|
+
export function getCollection() { return bookRef?.getCollection(); }
|
|
49
|
+
|
|
50
|
+
// Dynamically size the book to fill available container space.
|
|
51
|
+
// We own the single/dual page decision (responsive={false}) to avoid feedback loops —
|
|
52
|
+
// the Book's internal responsive detection uses bind:clientWidth which conflicts with
|
|
53
|
+
// our external sizing and can oscillate during flip animations.
|
|
54
|
+
// Uses window resize event instead of ResizeObserver for the same reason.
|
|
55
|
+
$effect(() => {
|
|
56
|
+
if (!containerEl) return;
|
|
57
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
58
|
+
|
|
59
|
+
const apply = () => {
|
|
60
|
+
const cw = containerEl!.clientWidth;
|
|
61
|
+
const ch = containerEl!.clientHeight;
|
|
62
|
+
if (!cw || !ch) return;
|
|
63
|
+
|
|
64
|
+
// suppress width/translate transitions during dimension changes,
|
|
65
|
+
// then restore so flip animations (stage translate) work normally
|
|
66
|
+
resizing = true;
|
|
67
|
+
|
|
68
|
+
// try dual-page first: 2 pages side by side
|
|
69
|
+
const dualHeight = Math.floor(Math.min(ch, cw / 2 / ratio));
|
|
70
|
+
const dualWidth = Math.floor(dualHeight * ratio);
|
|
71
|
+
|
|
72
|
+
if (dualWidth >= minPageWidth) {
|
|
73
|
+
// dual-page mode — pages are large enough
|
|
74
|
+
pageWidth = dualWidth;
|
|
75
|
+
pageHeight = dualHeight;
|
|
76
|
+
forceSingle = false;
|
|
77
|
+
} else {
|
|
78
|
+
// single-page mode — container too narrow for readable dual pages
|
|
79
|
+
const singleHeight = Math.floor(Math.min(ch, cw / ratio));
|
|
80
|
+
pageWidth = Math.floor(singleHeight * ratio);
|
|
81
|
+
pageHeight = singleHeight;
|
|
82
|
+
forceSingle = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// force-remount Book so 3D context reinitializes with new dimensions
|
|
86
|
+
// (overflow:hidden flattens preserve-3d when dimensions change mid-life)
|
|
87
|
+
measured = false;
|
|
88
|
+
waitForTwoRepaints().then(() => {
|
|
89
|
+
resizing = false;
|
|
90
|
+
measured = true;
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// initial measurement
|
|
95
|
+
apply();
|
|
96
|
+
|
|
97
|
+
// subsequent resizes — debounced to avoid flickering during drag
|
|
98
|
+
const onResize = () => {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
timer = setTimeout(apply, debounceMs);
|
|
101
|
+
};
|
|
102
|
+
window.addEventListener("resize", onResize);
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
window.removeEventListener("resize", onResize);
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<div
|
|
112
|
+
bind:this={containerEl}
|
|
113
|
+
class="stuic-book-responsive"
|
|
114
|
+
style="--stuic-book-page-width: {pageWidth}px; --stuic-book-page-height: {pageHeight}px;{resizing
|
|
115
|
+
? ' --stuic-book-duration: 0ms;'
|
|
116
|
+
: ''}"
|
|
117
|
+
>
|
|
118
|
+
{#if measured}
|
|
119
|
+
<Book
|
|
120
|
+
bind:this={bookRef}
|
|
121
|
+
{pages}
|
|
122
|
+
responsive={false}
|
|
123
|
+
singlePage={forceSingle}
|
|
124
|
+
bind:activeSpread
|
|
125
|
+
{...rest}
|
|
126
|
+
/>
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<style>
|
|
131
|
+
.stuic-book-responsive {
|
|
132
|
+
flex: 1;
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
overflow: hidden;
|
|
137
|
+
}
|
|
138
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Props as BookProps } from "./Book.svelte";
|
|
2
|
+
export interface Props extends Omit<BookProps, "responsive" | "singlePage"> {
|
|
3
|
+
/** Minimum page width (px) before switching to single-page mode (default: 150) */
|
|
4
|
+
minPageWidth?: number;
|
|
5
|
+
/** Resize debounce delay in ms (default: 150) */
|
|
6
|
+
debounce?: number;
|
|
7
|
+
}
|
|
8
|
+
import { type BookPage } from "./Book.svelte";
|
|
9
|
+
declare const BookResponsive: import("svelte").Component<Props, {
|
|
10
|
+
next: () => void;
|
|
11
|
+
previous: () => void;
|
|
12
|
+
goTo: (spreadIndex: number) => void;
|
|
13
|
+
goToPage: (pageId: BookPage["id"]) => void;
|
|
14
|
+
zoomIn: () => void;
|
|
15
|
+
zoomOut: () => void;
|
|
16
|
+
resetZoom: () => void;
|
|
17
|
+
getCollection: () => import("./Book.svelte").BookCollection | undefined;
|
|
18
|
+
}, "activeSpread">;
|
|
19
|
+
type BookResponsive = ReturnType<typeof BookResponsive>;
|
|
20
|
+
export default BookResponsive;
|
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { default as Book, type Props as BookProps, type BookPage, type BookPageArea, type BookSpread, type BookSheet, type BookCollection, buildSpreads, buildSinglePageSpreads, buildSheets, computeBookPageSize, } from "./Book.svelte";
|
|
2
|
+
export { default as BookResponsive, type Props as BookResponsiveProps, } from "./BookResponsive.svelte";
|