@openpageflip/core 0.1.0 → 0.2.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.
- package/README.md +42 -21
- package/dist/index.d.ts +10 -14
- package/dist/index.iife.js +1 -1
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +44 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ function startTween(clock, spec) {
|
|
|
20
20
|
const tick = (time) => {
|
|
21
21
|
handle = null;
|
|
22
22
|
if (done) return;
|
|
23
|
-
const t = spec.duration <= 0 ? 1 : Math.min(1, (time - startedAt) / spec.duration);
|
|
23
|
+
const t = spec.duration <= 0 ? 1 : Math.min(1, Math.max(0, (time - startedAt) / spec.duration));
|
|
24
24
|
if (t >= 1) {
|
|
25
25
|
end();
|
|
26
26
|
return;
|
|
@@ -51,11 +51,6 @@ const Layout = {
|
|
|
51
51
|
single: "single",
|
|
52
52
|
spread: "spread"
|
|
53
53
|
};
|
|
54
|
-
/** Reading direction. `rtl` flips the spine to the right for manga and Hebrew/Arabic books. */
|
|
55
|
-
const Direction = {
|
|
56
|
-
ltr: "ltr",
|
|
57
|
-
rtl: "rtl"
|
|
58
|
-
};
|
|
59
54
|
/** Which corner a programmatic flip lifts. */
|
|
60
55
|
const FlipCorner = {
|
|
61
56
|
top: "top",
|
|
@@ -120,7 +115,7 @@ const DEFAULTS = {
|
|
|
120
115
|
ignoreDragOn: "a, button, input, textarea, select, [data-opf-no-flip]"
|
|
121
116
|
};
|
|
122
117
|
function isOneOf(vocabulary, value) {
|
|
123
|
-
return Object.values(vocabulary).
|
|
118
|
+
return Object.values(vocabulary).some((allowed) => allowed === value);
|
|
124
119
|
}
|
|
125
120
|
/** Fill in defaults and reject options that could only produce a broken book. */
|
|
126
121
|
function resolveOptions(user) {
|
|
@@ -144,6 +139,11 @@ function resolveOptions(user) {
|
|
|
144
139
|
if (!isOneOf(ClickMode, options.click)) throw new TypeError(`@openpageflip/core: unknown "click" ${String(options.click)}`);
|
|
145
140
|
if (!(options.shadowOpacity >= 0 && options.shadowOpacity <= 1)) throw new TypeError(`@openpageflip/core: "shadowOpacity" must be within 0..1, got ${options.shadowOpacity}`);
|
|
146
141
|
if (!Number.isInteger(options.startPage) || options.startPage < 0) throw new TypeError(`@openpageflip/core: "startPage" must be a non-negative integer, got ${options.startPage}`);
|
|
142
|
+
if (options.ignoreDragOn !== false) try {
|
|
143
|
+
document.createElement("div").matches(options.ignoreDragOn);
|
|
144
|
+
} catch {
|
|
145
|
+
throw new TypeError(`@openpageflip/core: "ignoreDragOn" is not a valid selector: ${options.ignoreDragOn}`);
|
|
146
|
+
}
|
|
147
147
|
return options;
|
|
148
148
|
}
|
|
149
149
|
//#endregion
|
|
@@ -600,8 +600,10 @@ var FlipController = class {
|
|
|
600
600
|
}
|
|
601
601
|
/** Returns true when the orientation changed, which re-paginates the book. */
|
|
602
602
|
setLayout(layout) {
|
|
603
|
+
const resized = layout.rect.pageWidth !== this.rect.pageWidth || layout.rect.height !== this.rect.height;
|
|
603
604
|
this.rect = layout.rect;
|
|
604
605
|
const orientationChanged = layout.orientation !== this.orientation;
|
|
606
|
+
if (resized && !orientationChanged) this.endSession();
|
|
605
607
|
if (orientationChanged) {
|
|
606
608
|
this.endSession();
|
|
607
609
|
this.orientation = layout.orientation;
|
|
@@ -620,7 +622,7 @@ var FlipController = class {
|
|
|
620
622
|
}
|
|
621
623
|
showPage(page) {
|
|
622
624
|
const index = spreadIndexOfPage(this.spreads, page);
|
|
623
|
-
if (index === null)
|
|
625
|
+
if (index === null) throw new RangeError(`@openpageflip/core: page ${page} is out of range (0..${this.pages.length - 1})`);
|
|
624
626
|
this.spreadIndex = index;
|
|
625
627
|
this.showSpread();
|
|
626
628
|
}
|
|
@@ -664,6 +666,7 @@ var FlipController = class {
|
|
|
664
666
|
* The static pages keep showing the current spread until that turn lands.
|
|
665
667
|
*/
|
|
666
668
|
flipTo(page, corner) {
|
|
669
|
+
this.tween?.finish();
|
|
667
670
|
const target = spreadIndexOfPage(this.spreads, page);
|
|
668
671
|
if (target === null || target === this.spreadIndex) return Promise.resolve(false);
|
|
669
672
|
if (target > this.spreadIndex) {
|
|
@@ -790,14 +793,16 @@ var FlipController = class {
|
|
|
790
793
|
this.release();
|
|
791
794
|
}
|
|
792
795
|
pointerDown(containerPos) {
|
|
796
|
+
if (this.state === FlipState.flipping) this.tween?.finish();
|
|
793
797
|
this.pressStart = containerPos;
|
|
794
798
|
this.dragged = false;
|
|
795
799
|
}
|
|
796
800
|
/** A pressed pointer moved. Starts a drag once it travels past the click threshold. */
|
|
797
801
|
pointerDrag(containerPos) {
|
|
798
|
-
if (this.pressStart === null
|
|
802
|
+
if (this.pressStart === null) return;
|
|
799
803
|
if (!this.dragged && distance(this.pressStart, containerPos) <= DRAG_THRESHOLD) return;
|
|
800
804
|
this.dragged = true;
|
|
805
|
+
if (!this.options.drag) return;
|
|
801
806
|
const session = this.session ?? this.start(this.pressStart);
|
|
802
807
|
if (session === null) return;
|
|
803
808
|
this.setState(FlipState.userFold);
|
|
@@ -937,6 +942,10 @@ var FlipController = class {
|
|
|
937
942
|
} : null
|
|
938
943
|
};
|
|
939
944
|
}
|
|
945
|
+
/** Hand the current frame to the renderer again, after something else touched the DOM. */
|
|
946
|
+
redraw() {
|
|
947
|
+
this.render();
|
|
948
|
+
}
|
|
940
949
|
render() {
|
|
941
950
|
this.hooks.onFrame(this.frame());
|
|
942
951
|
}
|
|
@@ -959,8 +968,9 @@ function createEmitter() {
|
|
|
959
968
|
set = /* @__PURE__ */ new Set();
|
|
960
969
|
listeners.set(name, set);
|
|
961
970
|
}
|
|
962
|
-
set.add(listener);
|
|
963
971
|
const unsubscribe = () => off(name, listener);
|
|
972
|
+
if (options?.signal?.aborted) return unsubscribe;
|
|
973
|
+
set.add(listener);
|
|
964
974
|
options?.signal?.addEventListener("abort", unsubscribe, { once: true });
|
|
965
975
|
return unsubscribe;
|
|
966
976
|
},
|
|
@@ -991,7 +1001,7 @@ function attachInput(container, controller, options) {
|
|
|
991
1001
|
const onDown = (event) => {
|
|
992
1002
|
if (press !== null) return;
|
|
993
1003
|
if (event.pointerType === "mouse" && event.button !== 0) return;
|
|
994
|
-
if (event.target instanceof Element && event.target.closest(options.ignoreDragOn) !== null) return;
|
|
1004
|
+
if (options.ignoreDragOn !== false && event.target instanceof Element && event.target.closest(options.ignoreDragOn) !== null) return;
|
|
995
1005
|
const start = local(event);
|
|
996
1006
|
press = {
|
|
997
1007
|
id: event.pointerId,
|
|
@@ -1148,6 +1158,8 @@ var DomRenderer = class {
|
|
|
1148
1158
|
* over it. The copy is inert, has no ids, and lives only for the duration of the flip.
|
|
1149
1159
|
*/
|
|
1150
1160
|
clone = null;
|
|
1161
|
+
/** Pages currently hidden inline. A page is hidden once when it leaves the stage, not every frame. */
|
|
1162
|
+
hidden = /* @__PURE__ */ new Set();
|
|
1151
1163
|
container;
|
|
1152
1164
|
options;
|
|
1153
1165
|
constructor(container, options) {
|
|
@@ -1172,6 +1184,7 @@ var DomRenderer = class {
|
|
|
1172
1184
|
setPages(pages) {
|
|
1173
1185
|
for (const page of this.pages) if (!pages.some((next) => next.element === page.element)) this.restore(page.element);
|
|
1174
1186
|
this.pages = pages;
|
|
1187
|
+
this.hidden.clear();
|
|
1175
1188
|
for (const page of pages) {
|
|
1176
1189
|
if (this.saved.has(page.element)) continue;
|
|
1177
1190
|
this.saved.set(page.element, {
|
|
@@ -1186,25 +1199,27 @@ var DomRenderer = class {
|
|
|
1186
1199
|
applyContainerSizing(orientation = Orientation.landscape) {
|
|
1187
1200
|
const { autoSize, size, width, height, minWidth, maxWidth, layout } = this.options;
|
|
1188
1201
|
if (!autoSize) return;
|
|
1189
|
-
const
|
|
1202
|
+
const minAcross = layout === Layout.spread ? 2 : 1;
|
|
1203
|
+
const maxAcross = layout === Layout.single ? 1 : 2;
|
|
1190
1204
|
const style = this.container.style;
|
|
1191
1205
|
style.width = "100%";
|
|
1192
|
-
style.minWidth = `${(size ===
|
|
1193
|
-
style.maxWidth = `${(size ===
|
|
1206
|
+
style.minWidth = `${(size === SizeMode.fixed ? width : minWidth) * minAcross}px`;
|
|
1207
|
+
style.maxWidth = `${(size === SizeMode.fixed ? width : maxWidth) * maxAcross}px`;
|
|
1194
1208
|
style.aspectRatio = orientation === Orientation.portrait ? `${width} / ${height}` : `${width * 2} / ${height}`;
|
|
1195
1209
|
}
|
|
1196
1210
|
render(frame) {
|
|
1197
1211
|
const { rect, flip } = frame;
|
|
1198
|
-
const active = /* @__PURE__ */ new Set(
|
|
1212
|
+
const active = /* @__PURE__ */ new Set();
|
|
1213
|
+
for (const index of [
|
|
1199
1214
|
frame.left,
|
|
1200
1215
|
frame.right,
|
|
1201
1216
|
flip?.flipping,
|
|
1202
1217
|
flip?.bottom
|
|
1203
|
-
]);
|
|
1204
|
-
for (const [index, page] of this.pages.entries())
|
|
1205
|
-
|
|
1206
|
-
page.element
|
|
1207
|
-
|
|
1218
|
+
]) if (index !== void 0 && index !== null) active.add(index);
|
|
1219
|
+
for (const [index, page] of this.pages.entries()) if (active.has(index)) this.hidden.delete(index);
|
|
1220
|
+
else if (!this.hidden.has(index)) {
|
|
1221
|
+
applyPageStyle(page.element, { display: "none" });
|
|
1222
|
+
this.hidden.add(index);
|
|
1208
1223
|
}
|
|
1209
1224
|
const flippingHard = flip !== null && this.pages[flip.flipping]?.drawingDensity === PageDensity.hard;
|
|
1210
1225
|
if (frame.orientation !== Orientation.portrait && frame.left !== null) {
|
|
@@ -1243,6 +1258,9 @@ var DomRenderer = class {
|
|
|
1243
1258
|
const page = this.pages[index];
|
|
1244
1259
|
if (page === void 0) return null;
|
|
1245
1260
|
const el = asClone ? this.cloneOf(page.element) : page.element;
|
|
1261
|
+
el.classList.add(CLASS.page);
|
|
1262
|
+
el.classList.toggle(CLASS.hard, page.drawingDensity === PageDensity.hard);
|
|
1263
|
+
el.classList.toggle(CLASS.soft, page.drawingDensity === PageDensity.soft);
|
|
1246
1264
|
el.classList.toggle(CLASS.left, side === "left");
|
|
1247
1265
|
el.classList.toggle(CLASS.right, side === "right");
|
|
1248
1266
|
return el;
|
|
@@ -1251,6 +1269,7 @@ var DomRenderer = class {
|
|
|
1251
1269
|
if (this.clone?.source === source) return this.clone.element;
|
|
1252
1270
|
this.dropClone();
|
|
1253
1271
|
const element = source.cloneNode(true);
|
|
1272
|
+
if (!(element instanceof HTMLElement)) throw new TypeError("@openpageflip/core: a page clone is not an element");
|
|
1254
1273
|
element.removeAttribute("id");
|
|
1255
1274
|
for (const el of element.querySelectorAll("[id]")) el.removeAttribute("id");
|
|
1256
1275
|
element.setAttribute("aria-hidden", "true");
|
|
@@ -1410,6 +1429,7 @@ var DomRenderer = class {
|
|
|
1410
1429
|
/** Put the container and every page back the way they were found. */
|
|
1411
1430
|
destroy() {
|
|
1412
1431
|
this.dropClone();
|
|
1432
|
+
this.hidden.clear();
|
|
1413
1433
|
for (const page of this.pages) this.restore(page.element);
|
|
1414
1434
|
this.pages = [];
|
|
1415
1435
|
for (const el of Object.values(this.shadows)) el.remove();
|
|
@@ -1465,7 +1485,7 @@ function createBook(container, userOptions) {
|
|
|
1465
1485
|
let pendingRelayout = null;
|
|
1466
1486
|
const observer = new ResizeObserver(() => {
|
|
1467
1487
|
if (pendingRelayout !== null) return;
|
|
1468
|
-
pendingRelayout =
|
|
1488
|
+
pendingRelayout = clock.requestFrame(() => {
|
|
1469
1489
|
pendingRelayout = null;
|
|
1470
1490
|
const size = {
|
|
1471
1491
|
width: container.clientWidth,
|
|
@@ -1519,9 +1539,10 @@ function createBook(container, userOptions) {
|
|
|
1519
1539
|
});
|
|
1520
1540
|
},
|
|
1521
1541
|
update: relayout,
|
|
1542
|
+
redraw: () => controller.redraw(),
|
|
1522
1543
|
destroy() {
|
|
1523
1544
|
observer.disconnect();
|
|
1524
|
-
if (pendingRelayout !== null)
|
|
1545
|
+
if (pendingRelayout !== null) clock.cancelFrame(pendingRelayout);
|
|
1525
1546
|
detachInput();
|
|
1526
1547
|
controller.destroy();
|
|
1527
1548
|
renderer.destroy();
|
|
@@ -1530,6 +1551,6 @@ function createBook(container, userOptions) {
|
|
|
1530
1551
|
};
|
|
1531
1552
|
}
|
|
1532
1553
|
//#endregion
|
|
1533
|
-
export { ClickMode,
|
|
1554
|
+
export { ClickMode, FlipCorner, FlipDirection, FlipState, Layout, Orientation, PageDensity, SizeMode, computeFold, computeLayout, createBook };
|
|
1534
1555
|
|
|
1535
1556
|
//# sourceMappingURL=index.js.map
|