@bendyline/squisq-react 2.11.4 → 2.11.6
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.
- package/dist/chunk-7QA6EW6Y.js +240 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -0
- package/dist/page/index.d.ts +36 -1
- package/dist/page/index.js +4 -0
- package/dist/squisq-player.full.global.js +308 -308
- package/dist/squisq-player.global.js +57 -57
- package/dist/standalone-source.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LinearDocView
|
|
3
|
+
} from "./chunk-5UGIE2QO.js";
|
|
4
|
+
|
|
5
|
+
// src/PaginatedDocView.tsx
|
|
6
|
+
import {
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState
|
|
12
|
+
} from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function PaginatedDocView({
|
|
15
|
+
doc,
|
|
16
|
+
pages,
|
|
17
|
+
linearViewProps,
|
|
18
|
+
className = "",
|
|
19
|
+
scrollerClassName = "",
|
|
20
|
+
pageClassName = "",
|
|
21
|
+
previousButtonClassName = "",
|
|
22
|
+
nextButtonClassName = "",
|
|
23
|
+
indicatorClassName = "",
|
|
24
|
+
indicatorTextClassName = "",
|
|
25
|
+
renderPreviousIcon,
|
|
26
|
+
renderNextIcon,
|
|
27
|
+
ariaLabel = "Document pages",
|
|
28
|
+
initialPage = 0,
|
|
29
|
+
focusOnMount = true,
|
|
30
|
+
einkMode = false,
|
|
31
|
+
onPageChange,
|
|
32
|
+
style,
|
|
33
|
+
scrollerStyle,
|
|
34
|
+
pageStyle: pageStyleOverride
|
|
35
|
+
}) {
|
|
36
|
+
const resolvedPages = useMemo(() => {
|
|
37
|
+
if (pages) return pages;
|
|
38
|
+
if (!doc) return [];
|
|
39
|
+
return doc.blocks.map((block, index) => ({
|
|
40
|
+
key: block.id || `page-${index}`,
|
|
41
|
+
label: block.sourceHeading ? `Document section ${index + 1}` : `Document page ${index + 1}`,
|
|
42
|
+
content: /* @__PURE__ */ jsx(
|
|
43
|
+
LinearDocView,
|
|
44
|
+
{
|
|
45
|
+
...linearViewProps,
|
|
46
|
+
doc: {
|
|
47
|
+
...doc,
|
|
48
|
+
blocks: [block],
|
|
49
|
+
startBlock: index === 0 ? doc.startBlock : void 0
|
|
50
|
+
},
|
|
51
|
+
thinMargins: linearViewProps?.thinMargins ?? true
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
}));
|
|
55
|
+
}, [doc, linearViewProps, pages]);
|
|
56
|
+
const lastPage = Math.max(0, resolvedPages.length - 1);
|
|
57
|
+
const [currentPage, setCurrentPage] = useState(
|
|
58
|
+
() => Math.min(Math.max(0, initialPage), lastPage)
|
|
59
|
+
);
|
|
60
|
+
const scrollRef = useRef(null);
|
|
61
|
+
const pendingPageRef = useRef(null);
|
|
62
|
+
const pendingPageTimeoutRef = useRef(null);
|
|
63
|
+
const commitPage = useCallback(
|
|
64
|
+
(next) => {
|
|
65
|
+
setCurrentPage((current) => {
|
|
66
|
+
if (current !== next) onPageChange?.(next);
|
|
67
|
+
return next;
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
[onPageChange]
|
|
71
|
+
);
|
|
72
|
+
const updatePage = useCallback(
|
|
73
|
+
(index) => {
|
|
74
|
+
const next = Math.min(Math.max(0, index), lastPage);
|
|
75
|
+
pendingPageRef.current = next;
|
|
76
|
+
if (pendingPageTimeoutRef.current !== null) {
|
|
77
|
+
clearTimeout(pendingPageTimeoutRef.current);
|
|
78
|
+
}
|
|
79
|
+
pendingPageTimeoutRef.current = setTimeout(() => {
|
|
80
|
+
pendingPageRef.current = null;
|
|
81
|
+
pendingPageTimeoutRef.current = null;
|
|
82
|
+
commitPage(next);
|
|
83
|
+
}, 750);
|
|
84
|
+
const container = scrollRef.current;
|
|
85
|
+
const pageNodes = container?.querySelectorAll(":scope > .squisq-paginated-page");
|
|
86
|
+
const target = pageNodes?.[next];
|
|
87
|
+
if (container && target) {
|
|
88
|
+
const origin = pageNodes?.[0]?.offsetLeft ?? 0;
|
|
89
|
+
container.scrollTo({
|
|
90
|
+
left: target.offsetLeft - origin,
|
|
91
|
+
behavior: einkMode ? "auto" : "smooth"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[commitPage, einkMode, lastPage]
|
|
96
|
+
);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const container = scrollRef.current;
|
|
99
|
+
if (!container) return;
|
|
100
|
+
const initial = Math.min(Math.max(0, initialPage), lastPage);
|
|
101
|
+
pendingPageRef.current = null;
|
|
102
|
+
container.scrollLeft = initial * container.clientWidth;
|
|
103
|
+
setCurrentPage(initial);
|
|
104
|
+
if (focusOnMount) container.focus({ preventScroll: true });
|
|
105
|
+
}, [focusOnMount, initialPage, lastPage, resolvedPages]);
|
|
106
|
+
useEffect(
|
|
107
|
+
() => () => {
|
|
108
|
+
if (pendingPageTimeoutRef.current !== null) {
|
|
109
|
+
clearTimeout(pendingPageTimeoutRef.current);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[]
|
|
113
|
+
);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
const container = scrollRef.current;
|
|
116
|
+
if (!container) return;
|
|
117
|
+
let frame = 0;
|
|
118
|
+
const onScroll = () => {
|
|
119
|
+
cancelAnimationFrame(frame);
|
|
120
|
+
frame = requestAnimationFrame(() => {
|
|
121
|
+
if (container.clientWidth <= 0) return;
|
|
122
|
+
const pageNodes = Array.from(
|
|
123
|
+
container.querySelectorAll(":scope > .squisq-paginated-page")
|
|
124
|
+
);
|
|
125
|
+
const origin = pageNodes[0]?.offsetLeft ?? 0;
|
|
126
|
+
const pendingPage = pendingPageRef.current;
|
|
127
|
+
if (pendingPage !== null) {
|
|
128
|
+
const pendingTarget = pageNodes[pendingPage];
|
|
129
|
+
const pendingLeft = pendingTarget ? pendingTarget.offsetLeft - origin : 0;
|
|
130
|
+
if (Math.abs(container.scrollLeft - pendingLeft) > 1) return;
|
|
131
|
+
pendingPageRef.current = null;
|
|
132
|
+
if (pendingPageTimeoutRef.current !== null) {
|
|
133
|
+
clearTimeout(pendingPageTimeoutRef.current);
|
|
134
|
+
pendingPageTimeoutRef.current = null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const next = pageNodes.reduce(
|
|
138
|
+
(nearest, page, index) => Math.abs(container.scrollLeft - (page.offsetLeft - origin)) < Math.abs(container.scrollLeft - (pageNodes[nearest]?.offsetLeft ?? origin) + origin) ? index : nearest,
|
|
139
|
+
0
|
|
140
|
+
);
|
|
141
|
+
commitPage(next);
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
container.addEventListener("scroll", onScroll, { passive: true });
|
|
145
|
+
return () => {
|
|
146
|
+
cancelAnimationFrame(frame);
|
|
147
|
+
container.removeEventListener("scroll", onScroll);
|
|
148
|
+
};
|
|
149
|
+
}, [commitPage, lastPage]);
|
|
150
|
+
const onKeyDown = (event) => {
|
|
151
|
+
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
updatePage((pendingPageRef.current ?? currentPage) + 1);
|
|
154
|
+
} else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
updatePage((pendingPageRef.current ?? currentPage) - 1);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const outerStyle = { position: "relative", width: "100%", height: "100%" };
|
|
160
|
+
const scrollStyle = {
|
|
161
|
+
display: "flex",
|
|
162
|
+
width: "100%",
|
|
163
|
+
height: "100%",
|
|
164
|
+
overflowX: "auto",
|
|
165
|
+
overflowY: "hidden",
|
|
166
|
+
scrollSnapType: "x mandatory"
|
|
167
|
+
};
|
|
168
|
+
const pageStyle = {
|
|
169
|
+
flex: "0 0 100%",
|
|
170
|
+
minWidth: "100%",
|
|
171
|
+
height: "100%",
|
|
172
|
+
overflowY: "auto",
|
|
173
|
+
overflowX: "hidden",
|
|
174
|
+
scrollSnapAlign: "start",
|
|
175
|
+
scrollSnapStop: "always",
|
|
176
|
+
boxSizing: "border-box"
|
|
177
|
+
};
|
|
178
|
+
return /* @__PURE__ */ jsxs("div", { className: `squisq-paginated ${className}`.trim(), style: { ...outerStyle, ...style }, children: [
|
|
179
|
+
/* @__PURE__ */ jsx(
|
|
180
|
+
"div",
|
|
181
|
+
{
|
|
182
|
+
ref: scrollRef,
|
|
183
|
+
className: `squisq-paginated-scroll ${scrollerClassName}`.trim(),
|
|
184
|
+
style: { ...scrollStyle, ...scrollerStyle },
|
|
185
|
+
tabIndex: 0,
|
|
186
|
+
role: "region",
|
|
187
|
+
"aria-label": ariaLabel,
|
|
188
|
+
"aria-roledescription": "paginated reader",
|
|
189
|
+
onKeyDown,
|
|
190
|
+
children: resolvedPages.map((page) => /* @__PURE__ */ jsx(
|
|
191
|
+
"div",
|
|
192
|
+
{
|
|
193
|
+
className: `squisq-paginated-page ${pageClassName} ${page.className ?? ""}`.trim(),
|
|
194
|
+
style: { ...pageStyle, ...pageStyleOverride },
|
|
195
|
+
role: "group",
|
|
196
|
+
"aria-label": page.label,
|
|
197
|
+
children: page.content
|
|
198
|
+
},
|
|
199
|
+
page.key
|
|
200
|
+
))
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
currentPage > 0 && /* @__PURE__ */ jsx(
|
|
204
|
+
"button",
|
|
205
|
+
{
|
|
206
|
+
type: "button",
|
|
207
|
+
className: `squisq-paginated-nav squisq-paginated-nav--previous ${previousButtonClassName}`.trim(),
|
|
208
|
+
"aria-label": "Previous page",
|
|
209
|
+
onClick: () => updatePage((pendingPageRef.current ?? currentPage) - 1),
|
|
210
|
+
children: renderPreviousIcon?.() ?? "\u2039"
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
currentPage < lastPage && /* @__PURE__ */ jsx(
|
|
214
|
+
"button",
|
|
215
|
+
{
|
|
216
|
+
type: "button",
|
|
217
|
+
className: `squisq-paginated-nav squisq-paginated-nav--next ${nextButtonClassName}`.trim(),
|
|
218
|
+
"aria-label": "Next page",
|
|
219
|
+
onClick: () => updatePage((pendingPageRef.current ?? currentPage) + 1),
|
|
220
|
+
children: renderNextIcon?.() ?? "\u203A"
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
resolvedPages.length > 0 && /* @__PURE__ */ jsx(
|
|
224
|
+
"div",
|
|
225
|
+
{
|
|
226
|
+
className: `squisq-paginated-indicator ${indicatorClassName}`.trim(),
|
|
227
|
+
"aria-live": "polite",
|
|
228
|
+
children: /* @__PURE__ */ jsxs("span", { className: indicatorTextClassName, children: [
|
|
229
|
+
currentPage + 1,
|
|
230
|
+
" / ",
|
|
231
|
+
resolvedPages.length
|
|
232
|
+
] })
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
] });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export {
|
|
239
|
+
PaginatedDocView
|
|
240
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { getAnimationStyle, getTransitionClass } from '@bendyline/squisq/doc';
|
|
|
6
6
|
import { C as CodeBlockCopyHandler } from './MarkdownRenderer-CC9dOc6b.js';
|
|
7
7
|
export { a as CodeBlockCopyContext, M as MarkdownRenderer, b as MarkdownRendererProps } from './MarkdownRenderer-CC9dOc6b.js';
|
|
8
8
|
export { FenceRendererContext, MermaidDiagram, MermaidDiagramProps, useFenceRenderers } from './markdown/index.js';
|
|
9
|
-
export { CanvasSection, CanvasSectionProps, ImageDisplayMode, LinearDocView, LinearDocViewProps, PageSectionView, PageSectionViewProps, PageViewContext, PageViewContextValue, usePageView } from './page/index.js';
|
|
9
|
+
export { CanvasSection, CanvasSectionProps, ImageDisplayMode, LinearDocView, LinearDocViewProps, PageSectionView, PageSectionViewProps, PageViewContext, PageViewContextValue, PaginatedDocPage, PaginatedDocView, PaginatedDocViewProps, usePageView } from './page/index.js';
|
|
10
10
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
11
11
|
export { ImageLayer, MapLayer, MermaidLayer, PathLayer, ShapeLayer, TextLayer, TreeLayer, VideoLayer } from './layers/index.js';
|
|
12
12
|
export { T as TableLayer, a as TableLayerContentRenderer, b as TableLayerContentRendererProps } from './TableLayer-B7ADuUGc.js';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
resolveDocPlayerAppearance,
|
|
17
17
|
useMediaClipDurations
|
|
18
18
|
} from "./chunk-KGWT454C.js";
|
|
19
|
+
import {
|
|
20
|
+
PaginatedDocView
|
|
21
|
+
} from "./chunk-7QA6EW6Y.js";
|
|
19
22
|
import {
|
|
20
23
|
BlockRenderer,
|
|
21
24
|
CanvasSection,
|
|
@@ -98,6 +101,7 @@ export {
|
|
|
98
101
|
MermaidLayer,
|
|
99
102
|
PageSectionView,
|
|
100
103
|
PageViewContext,
|
|
104
|
+
PaginatedDocView,
|
|
101
105
|
PathLayer,
|
|
102
106
|
ResourcePolicyContext,
|
|
103
107
|
ShapeLayer,
|
package/dist/page/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { FenceRendererMap } from '@bendyline/squisq/fence';
|
|
|
6
6
|
import { C as CodeBlockCopyHandler } from '../MarkdownRenderer-CC9dOc6b.js';
|
|
7
7
|
export { a as CodeBlockCopyContext } from '../MarkdownRenderer-CC9dOc6b.js';
|
|
8
8
|
import * as react from 'react';
|
|
9
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
9
10
|
|
|
10
11
|
interface LinearDocViewProps {
|
|
11
12
|
/**
|
|
@@ -95,6 +96,40 @@ type ImageDisplayMode = 'inline' | 'thumbnail';
|
|
|
95
96
|
*/
|
|
96
97
|
declare function LinearDocView({ doc, markdown, basePath, viewport, className, theme, surface, animationsEnabled, thinMargins, imageDisplayMode, globalKeyboardShortcuts, showCover, transformPage, showCodeCopyButton, onCopyCode, fenceRenderers, htmlPolicy, linkSchemes, }: LinearDocViewProps): react_jsx_runtime.JSX.Element;
|
|
97
98
|
|
|
99
|
+
interface PaginatedDocPage {
|
|
100
|
+
key: string;
|
|
101
|
+
label: string;
|
|
102
|
+
/** React or react-compatible host node (for example Preact compat). */
|
|
103
|
+
content: ReactNode | object | bigint;
|
|
104
|
+
className?: string;
|
|
105
|
+
}
|
|
106
|
+
interface PaginatedDocViewProps {
|
|
107
|
+
/** A Doc to split into one page per top-level block. */
|
|
108
|
+
doc?: Doc;
|
|
109
|
+
/** Host-composed pages. Wins over doc when supplied. */
|
|
110
|
+
pages?: readonly PaginatedDocPage[];
|
|
111
|
+
/** Props applied to Doc pages rendered with LinearDocView. */
|
|
112
|
+
linearViewProps?: Omit<LinearDocViewProps, 'doc'>;
|
|
113
|
+
className?: string;
|
|
114
|
+
scrollerClassName?: string;
|
|
115
|
+
pageClassName?: string;
|
|
116
|
+
previousButtonClassName?: string;
|
|
117
|
+
nextButtonClassName?: string;
|
|
118
|
+
indicatorClassName?: string;
|
|
119
|
+
indicatorTextClassName?: string;
|
|
120
|
+
renderPreviousIcon?: () => ReactNode | object | bigint;
|
|
121
|
+
renderNextIcon?: () => ReactNode | object | bigint;
|
|
122
|
+
ariaLabel?: string;
|
|
123
|
+
initialPage?: number;
|
|
124
|
+
focusOnMount?: boolean;
|
|
125
|
+
einkMode?: boolean;
|
|
126
|
+
onPageChange?: (pageIndex: number) => void;
|
|
127
|
+
style?: CSSProperties;
|
|
128
|
+
scrollerStyle?: CSSProperties;
|
|
129
|
+
pageStyle?: CSSProperties;
|
|
130
|
+
}
|
|
131
|
+
declare function PaginatedDocView({ doc, pages, linearViewProps, className, scrollerClassName, pageClassName, previousButtonClassName, nextButtonClassName, indicatorClassName, indicatorTextClassName, renderPreviousIcon, renderNextIcon, ariaLabel, initialPage, focusOnMount, einkMode, onPageChange, style, scrollerStyle, pageStyle: pageStyleOverride, }: PaginatedDocViewProps): react_jsx_runtime.JSX.Element;
|
|
132
|
+
|
|
98
133
|
interface PageSectionViewProps {
|
|
99
134
|
entry: PageSectionMaterialization;
|
|
100
135
|
/** Alternation flip for feature-split sections (theme `alternate` hint). */
|
|
@@ -130,4 +165,4 @@ interface PageViewContextValue {
|
|
|
130
165
|
declare const PageViewContext: react.Context<PageViewContextValue>;
|
|
131
166
|
declare function usePageView(): PageViewContextValue;
|
|
132
167
|
|
|
133
|
-
export { CanvasSection, type CanvasSectionProps, CodeBlockCopyHandler, type ImageDisplayMode, LinearDocView, type LinearDocViewProps, PageSectionView, type PageSectionViewProps, PageViewContext, type PageViewContextValue, usePageView };
|
|
168
|
+
export { CanvasSection, type CanvasSectionProps, CodeBlockCopyHandler, type ImageDisplayMode, LinearDocView, type LinearDocViewProps, PageSectionView, type PageSectionViewProps, PageViewContext, type PageViewContextValue, type PaginatedDocPage, PaginatedDocView, type PaginatedDocViewProps, usePageView };
|
package/dist/page/index.js
CHANGED