@netless/app-slide 0.2.39 → 0.2.41

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.
@@ -1,381 +1,381 @@
1
- import { sidebarSVG } from "./icons/sidebar";
2
- import { arrowLeftSVG } from "./icons/arrow-left";
3
- import { arrowRightSVG } from "./icons/arrow-right";
4
- import { playSVG } from "./icons/play";
5
- import { pauseSVG } from "./icons/pause";
6
-
7
- import type { ILazyLoadInstance } from "vanilla-lazyload";
8
- import LazyLoad from "vanilla-lazyload";
9
- import { SideEffectManager } from "side-effect-manager";
10
-
11
- export interface DocsViewerPage {
12
- src: string;
13
- height: number;
14
- width: number;
15
- thumbnail?: string;
16
- }
17
-
18
- export interface DocsViewerConfig {
19
- readonly: boolean;
20
- onNewPageIndex: (index: number) => void;
21
- onPlay?: () => void;
22
- urlInterrupter?: (url: string) => Promise<string>;
23
- }
24
-
25
- export class DocsViewer {
26
- public constructor({ readonly, onNewPageIndex, onPlay, urlInterrupter }: DocsViewerConfig) {
27
- this.readonly = readonly;
28
- this.onNewPageIndex = onNewPageIndex;
29
- this.onPlay = onPlay;
30
- this.urlInterrupter = urlInterrupter || (url => url);
31
-
32
- this.render();
33
- }
34
-
35
- protected readonly: boolean;
36
- protected onNewPageIndex: (index: number) => void;
37
- protected onPlay?: () => void;
38
- protected urlInterrupter: (url: string) => Promise<string> | string;
39
-
40
- private _pages: DocsViewerPage[] = [];
41
-
42
- public set pages(value: DocsViewerPage[]) {
43
- this._pages = value;
44
- this.refreshPreview().then(this.refreshBtnSidebar.bind(this));
45
- this.refreshTotalPage();
46
- }
47
-
48
- public get pages() {
49
- return this._pages;
50
- }
51
-
52
- public $content!: HTMLElement;
53
- public $preview!: HTMLElement;
54
- public $previewMask!: HTMLElement;
55
- public $footer!: HTMLElement;
56
- public $pageNumberInput!: HTMLInputElement;
57
- public $totalPage!: HTMLSpanElement;
58
- public $btnPlay!: HTMLButtonElement;
59
- public $btnSidebar!: HTMLButtonElement;
60
-
61
- public pageIndex = 0;
62
-
63
- public unmount(): void {
64
- this.$content.remove();
65
- this.$footer.remove();
66
- }
67
-
68
- public setReadonly(readonly: boolean): void {
69
- this.readonly = readonly;
70
- this.$content.classList.toggle(this.wrapClassName("readonly"), readonly);
71
- this.$footer.classList.toggle(this.wrapClassName("readonly"), readonly);
72
- this.$pageNumberInput.disabled = readonly;
73
- }
74
-
75
- public destroy(): void {
76
- this.previewLazyLoad?.destroy();
77
- this.sideEffect.flushAll();
78
- this.unmount();
79
- }
80
-
81
- public setPageIndex(pageIndex: number): void {
82
- if (!Number.isNaN(pageIndex)) {
83
- this.pageIndex = pageIndex;
84
- this.$pageNumberInput.value = String(pageIndex + 1);
85
- }
86
- }
87
-
88
- public refreshTotalPage(): void {
89
- if (this.pages.length) {
90
- this.$totalPage.textContent = " / " + this.pages.length;
91
- } else {
92
- this.$totalPage.textContent = "";
93
- }
94
- }
95
-
96
- public setSmallBox(isSmallBox: boolean): void {
97
- if (this.isSmallBox !== isSmallBox) {
98
- this.isSmallBox = isSmallBox;
99
- this.$footer.classList.toggle(this.wrapClassName("float-footer"), isSmallBox);
100
- }
101
- }
102
-
103
- public render(): HTMLElement {
104
- this.renderContent();
105
- this.renderFooter();
106
- return this.$content;
107
- }
108
-
109
- protected renderContent(): HTMLElement {
110
- if (!this.$content) {
111
- const $content = document.createElement("div");
112
- $content.className = this.wrapClassName("content");
113
- this.$content = $content;
114
-
115
- if (this.readonly) {
116
- $content.classList.add(this.wrapClassName("readonly"));
117
- }
118
-
119
- $content.appendChild(this.renderPreviewMask());
120
- $content.appendChild(this.renderPreview());
121
- }
122
- return this.$content;
123
- }
124
-
125
- private previewLazyLoad?: ILazyLoadInstance;
126
-
127
- protected renderPreview(): HTMLElement {
128
- if (!this.$preview) {
129
- const $preview = document.createElement("div");
130
- $preview.className = this.wrapClassName("preview") + " tele-fancy-scrollbar";
131
- this.$preview = $preview;
132
-
133
- this.refreshPreview();
134
-
135
- this.sideEffect.addEventListener($preview, "click", ev => {
136
- if (this.readonly) {
137
- return;
138
- }
139
- const pageIndex = (ev.target as HTMLElement).dataset?.pageIndex;
140
- if (pageIndex) {
141
- ev.preventDefault();
142
- ev.stopPropagation();
143
- ev.stopImmediatePropagation();
144
- this.onNewPageIndex(Number(pageIndex));
145
- this.togglePreview(false);
146
- }
147
- });
148
- }
149
-
150
- return this.$preview;
151
- }
152
-
153
- private async refreshPreview() {
154
- const { $preview } = this;
155
- const pageClassName = this.wrapClassName("preview-page");
156
- const pageNameClassName = this.wrapClassName("preview-page-name");
157
- while ($preview.firstChild) {
158
- $preview.firstChild.remove();
159
- }
160
-
161
- const previewSRCs: Record<number, Promise<string> | string> = [];
162
- for (let i = 0, len = this.pages.length; i < len; i++) {
163
- const page = this.pages[i];
164
- const src = page.thumbnail ?? (page.src.startsWith("ppt") ? void 0 : page.src);
165
- if (src) {
166
- previewSRCs[i] = this.urlInterrupter(src);
167
- }
168
- }
169
- // Promise.all(), but no closure
170
- for (let i = 0, len = this.pages.length; i < len; i++) {
171
- previewSRCs[i] = await previewSRCs[i]
172
- }
173
-
174
- this.pages.forEach((page, i) => {
175
- const previewSRC = previewSRCs[i] as string | undefined;
176
- if (!previewSRC) {
177
- return;
178
- }
179
-
180
- const pageIndex = String(i);
181
-
182
- const $page = document.createElement("a");
183
- $page.className = pageClassName + " " + this.wrapClassName(`preview-page-${i}`);
184
- $page.setAttribute("href", "#");
185
- $page.dataset.pageIndex = pageIndex;
186
-
187
- const $name = document.createElement("span");
188
- $name.className = pageNameClassName;
189
- $name.textContent = String(i + 1);
190
- $name.dataset.pageIndex = pageIndex;
191
-
192
- const $img = document.createElement("img");
193
- $img.width = page.width;
194
- $img.height = page.height;
195
- $img.dataset.src = previewSRC;
196
- $img.dataset.pageIndex = pageIndex;
197
-
198
- $page.appendChild($img);
199
- $page.appendChild($name);
200
- $preview.appendChild($page);
201
- });
202
-
203
- this.previewLazyLoad?.update();
204
- }
205
-
206
- protected renderPreviewMask(): HTMLElement {
207
- if (!this.$previewMask) {
208
- this.$previewMask = document.createElement("div");
209
- this.$previewMask.className = this.wrapClassName("preview-mask");
210
- this.sideEffect.addEventListener(this.$previewMask, "click", ev => {
211
- if (this.readonly) {
212
- return;
213
- }
214
- if (ev.target === this.$previewMask) {
215
- this.togglePreview(false);
216
- }
217
- });
218
- }
219
- return this.$previewMask;
220
- }
221
-
222
- public setPaused = () => {
223
- this.$btnPlay.classList.toggle(this.wrapClassName("footer-btn-playing"), false);
224
- };
225
-
226
- public setPlaying = () => {
227
- this.$btnPlay.classList.toggle(this.wrapClassName("footer-btn-playing"), true);
228
- };
229
-
230
- public refreshBtnSidebar() {
231
- this.$btnSidebar.style.display = this.pages.length > 0 ? "" : "none";
232
- }
233
-
234
- protected renderFooter(): HTMLElement {
235
- if (!this.$footer) {
236
- const $footer = document.createElement("div");
237
- $footer.className = this.wrapClassName("footer");
238
- this.$footer = $footer;
239
-
240
- if (this.readonly) {
241
- $footer.classList.add(this.wrapClassName("readonly"));
242
- }
243
-
244
- if (this.isSmallBox) {
245
- $footer.classList.add(this.wrapClassName("float-footer"));
246
- }
247
-
248
- const $btnSidebar = this.renderFooterBtn("btn-sidebar", sidebarSVG(this.namespace));
249
- this.sideEffect.addEventListener($btnSidebar, "click", () => {
250
- if (this.readonly) {
251
- return;
252
- }
253
- this.togglePreview();
254
- });
255
- this.$btnSidebar = $btnSidebar;
256
- this.$btnSidebar.style.display = "none";
257
- this.$footer.appendChild($btnSidebar);
258
-
259
- const $pageJumps = document.createElement("div");
260
- $pageJumps.className = this.wrapClassName("page-jumps");
261
-
262
- const $btnPageBack = this.renderFooterBtn("btn-page-back", arrowLeftSVG(this.namespace));
263
- this.sideEffect.addEventListener($btnPageBack, "click", () => {
264
- if (this.readonly) {
265
- return;
266
- }
267
- this.onNewPageIndex(this.pageIndex - 1);
268
- });
269
- $pageJumps.appendChild($btnPageBack);
270
-
271
- if (this.onPlay) {
272
- const $btnPlay = this.renderFooterBtn(
273
- "btn-page-play",
274
- playSVG(this.namespace),
275
- pauseSVG(this.namespace)
276
- );
277
- this.$btnPlay = $btnPlay;
278
- this.sideEffect.addEventListener($btnPlay, "click", () => {
279
- if (this.readonly) {
280
- return;
281
- }
282
- this.setPlaying();
283
- if (this.onPlay) {
284
- this.onPlay();
285
- }
286
- });
287
-
288
- $pageJumps.appendChild($btnPlay);
289
- }
290
-
291
- const $btnPageNext = this.renderFooterBtn("btn-page-next", arrowRightSVG(this.namespace));
292
- this.sideEffect.addEventListener($btnPageNext, "click", () => {
293
- if (this.readonly) {
294
- return;
295
- }
296
- this.onNewPageIndex(this.pageIndex + 1);
297
- });
298
- $pageJumps.appendChild($btnPageNext);
299
-
300
- const $pageNumber = document.createElement("div");
301
- $pageNumber.className = this.wrapClassName("page-number");
302
-
303
- const $pageNumberInput = document.createElement("input");
304
- $pageNumberInput.className = this.wrapClassName("page-number-input");
305
- $pageNumberInput.value = String(this.pageIndex + 1);
306
- if (this.readonly) {
307
- $pageNumberInput.disabled = true;
308
- }
309
- this.$pageNumberInput = $pageNumberInput;
310
- this.sideEffect.addEventListener($pageNumberInput, "focus", () => {
311
- $pageNumberInput.select();
312
- });
313
- this.sideEffect.addEventListener($pageNumberInput, "change", () => {
314
- if (this.readonly) {
315
- return;
316
- }
317
- if ($pageNumberInput.value) {
318
- this.onNewPageIndex(Number($pageNumberInput.value) - 1);
319
- }
320
- });
321
-
322
- const $totalPage = document.createElement("span");
323
- this.$totalPage = $totalPage;
324
-
325
- $pageNumber.appendChild($pageNumberInput);
326
- $pageNumber.appendChild($totalPage);
327
-
328
- this.$footer.appendChild($pageJumps);
329
- this.$footer.appendChild($pageNumber);
330
- }
331
- return this.$footer;
332
- }
333
-
334
- protected renderFooterBtn(
335
- className: string,
336
- $icon: SVGElement,
337
- $iconActive?: SVGElement
338
- ): HTMLButtonElement {
339
- const $btn = document.createElement("button");
340
- $btn.className = this.wrapClassName("footer-btn") + " " + this.wrapClassName(className);
341
-
342
- $btn.appendChild($icon);
343
-
344
- if ($iconActive) {
345
- $btn.appendChild($iconActive);
346
- }
347
-
348
- return $btn;
349
- }
350
-
351
- protected togglePreview(isShowPreview?: boolean): void {
352
- this.isShowPreview = isShowPreview ?? !this.isShowPreview;
353
- this.$content.classList.toggle(this.wrapClassName("preview-active"), this.isShowPreview);
354
- if (this.isShowPreview) {
355
- const $previewPage = this.$preview.querySelector<HTMLElement>(
356
- "." + this.wrapClassName(`preview-page-${this.pageIndex}`)
357
- );
358
- if ($previewPage) {
359
- this.previewLazyLoad ||= new LazyLoad({
360
- container: this.$preview,
361
- elements_selector: `.${this.wrapClassName("preview-page>img")}`,
362
- });
363
- this.$preview.scrollTo({
364
- top: $previewPage.offsetTop - 16,
365
- });
366
- }
367
- }
368
- }
369
-
370
- protected wrapClassName(className: string): string {
371
- return `${this.namespace}-${className}`;
372
- }
373
-
374
- protected namespace = "netless-app-slide";
375
-
376
- protected isShowPreview = false;
377
-
378
- protected isSmallBox = false;
379
-
380
- protected sideEffect = new SideEffectManager();
381
- }
1
+ import { sidebarSVG } from "./icons/sidebar";
2
+ import { arrowLeftSVG } from "./icons/arrow-left";
3
+ import { arrowRightSVG } from "./icons/arrow-right";
4
+ import { playSVG } from "./icons/play";
5
+ import { pauseSVG } from "./icons/pause";
6
+
7
+ import type { ILazyLoadInstance } from "vanilla-lazyload";
8
+ import LazyLoad from "vanilla-lazyload";
9
+ import { SideEffectManager } from "side-effect-manager";
10
+
11
+ export interface DocsViewerPage {
12
+ src: string;
13
+ height: number;
14
+ width: number;
15
+ thumbnail?: string;
16
+ }
17
+
18
+ export interface DocsViewerConfig {
19
+ readonly: boolean;
20
+ onNewPageIndex: (index: number) => void;
21
+ onPlay?: () => void;
22
+ urlInterrupter?: (url: string) => Promise<string>;
23
+ }
24
+
25
+ export class DocsViewer {
26
+ public constructor({ readonly, onNewPageIndex, onPlay, urlInterrupter }: DocsViewerConfig) {
27
+ this.readonly = readonly;
28
+ this.onNewPageIndex = onNewPageIndex;
29
+ this.onPlay = onPlay;
30
+ this.urlInterrupter = urlInterrupter || (url => url);
31
+
32
+ this.render();
33
+ }
34
+
35
+ protected readonly: boolean;
36
+ protected onNewPageIndex: (index: number) => void;
37
+ protected onPlay?: () => void;
38
+ protected urlInterrupter: (url: string) => Promise<string> | string;
39
+
40
+ private _pages: DocsViewerPage[] = [];
41
+
42
+ public set pages(value: DocsViewerPage[]) {
43
+ this._pages = value;
44
+ this.refreshPreview().then(this.refreshBtnSidebar.bind(this));
45
+ this.refreshTotalPage();
46
+ }
47
+
48
+ public get pages() {
49
+ return this._pages;
50
+ }
51
+
52
+ public $content!: HTMLElement;
53
+ public $preview!: HTMLElement;
54
+ public $previewMask!: HTMLElement;
55
+ public $footer!: HTMLElement;
56
+ public $pageNumberInput!: HTMLInputElement;
57
+ public $totalPage!: HTMLSpanElement;
58
+ public $btnPlay!: HTMLButtonElement;
59
+ public $btnSidebar!: HTMLButtonElement;
60
+
61
+ public pageIndex = 0;
62
+
63
+ public unmount(): void {
64
+ this.$content.remove();
65
+ this.$footer.remove();
66
+ }
67
+
68
+ public setReadonly(readonly: boolean): void {
69
+ this.readonly = readonly;
70
+ this.$content.classList.toggle(this.wrapClassName("readonly"), readonly);
71
+ this.$footer.classList.toggle(this.wrapClassName("readonly"), readonly);
72
+ this.$pageNumberInput.disabled = readonly;
73
+ }
74
+
75
+ public destroy(): void {
76
+ this.previewLazyLoad?.destroy();
77
+ this.sideEffect.flushAll();
78
+ this.unmount();
79
+ }
80
+
81
+ public setPageIndex(pageIndex: number): void {
82
+ if (!Number.isNaN(pageIndex)) {
83
+ this.pageIndex = pageIndex;
84
+ this.$pageNumberInput.value = String(pageIndex + 1);
85
+ }
86
+ }
87
+
88
+ public refreshTotalPage(): void {
89
+ if (this.pages.length) {
90
+ this.$totalPage.textContent = " / " + this.pages.length;
91
+ } else {
92
+ this.$totalPage.textContent = "";
93
+ }
94
+ }
95
+
96
+ public setSmallBox(isSmallBox: boolean): void {
97
+ if (this.isSmallBox !== isSmallBox) {
98
+ this.isSmallBox = isSmallBox;
99
+ this.$footer.classList.toggle(this.wrapClassName("float-footer"), isSmallBox);
100
+ }
101
+ }
102
+
103
+ public render(): HTMLElement {
104
+ this.renderContent();
105
+ this.renderFooter();
106
+ return this.$content;
107
+ }
108
+
109
+ protected renderContent(): HTMLElement {
110
+ if (!this.$content) {
111
+ const $content = document.createElement("div");
112
+ $content.className = this.wrapClassName("content");
113
+ this.$content = $content;
114
+
115
+ if (this.readonly) {
116
+ $content.classList.add(this.wrapClassName("readonly"));
117
+ }
118
+
119
+ $content.appendChild(this.renderPreviewMask());
120
+ $content.appendChild(this.renderPreview());
121
+ }
122
+ return this.$content;
123
+ }
124
+
125
+ private previewLazyLoad?: ILazyLoadInstance;
126
+
127
+ protected renderPreview(): HTMLElement {
128
+ if (!this.$preview) {
129
+ const $preview = document.createElement("div");
130
+ $preview.className = this.wrapClassName("preview") + " tele-fancy-scrollbar";
131
+ this.$preview = $preview;
132
+
133
+ this.refreshPreview();
134
+
135
+ this.sideEffect.addEventListener($preview, "click", ev => {
136
+ if (this.readonly) {
137
+ return;
138
+ }
139
+ const pageIndex = (ev.target as HTMLElement).dataset?.pageIndex;
140
+ if (pageIndex) {
141
+ ev.preventDefault();
142
+ ev.stopPropagation();
143
+ ev.stopImmediatePropagation();
144
+ this.onNewPageIndex(Number(pageIndex));
145
+ this.togglePreview(false);
146
+ }
147
+ });
148
+ }
149
+
150
+ return this.$preview;
151
+ }
152
+
153
+ private async refreshPreview() {
154
+ const { $preview } = this;
155
+ const pageClassName = this.wrapClassName("preview-page");
156
+ const pageNameClassName = this.wrapClassName("preview-page-name");
157
+ while ($preview.firstChild) {
158
+ $preview.firstChild.remove();
159
+ }
160
+
161
+ const previewSRCs: Record<number, Promise<string> | string> = [];
162
+ for (let i = 0, len = this.pages.length; i < len; i++) {
163
+ const page = this.pages[i];
164
+ const src = page.thumbnail ?? (page.src.startsWith("ppt") ? void 0 : page.src);
165
+ if (src) {
166
+ previewSRCs[i] = this.urlInterrupter(src);
167
+ }
168
+ }
169
+ // Promise.all(), but no closure
170
+ for (let i = 0, len = this.pages.length; i < len; i++) {
171
+ previewSRCs[i] = await previewSRCs[i]
172
+ }
173
+
174
+ this.pages.forEach((page, i) => {
175
+ const previewSRC = previewSRCs[i] as string | undefined;
176
+ if (!previewSRC) {
177
+ return;
178
+ }
179
+
180
+ const pageIndex = String(i);
181
+
182
+ const $page = document.createElement("a");
183
+ $page.className = pageClassName + " " + this.wrapClassName(`preview-page-${i}`);
184
+ $page.setAttribute("href", "#");
185
+ $page.dataset.pageIndex = pageIndex;
186
+
187
+ const $name = document.createElement("span");
188
+ $name.className = pageNameClassName;
189
+ $name.textContent = String(i + 1);
190
+ $name.dataset.pageIndex = pageIndex;
191
+
192
+ const $img = document.createElement("img");
193
+ $img.width = page.width;
194
+ $img.height = page.height;
195
+ $img.dataset.src = previewSRC;
196
+ $img.dataset.pageIndex = pageIndex;
197
+
198
+ $page.appendChild($img);
199
+ $page.appendChild($name);
200
+ $preview.appendChild($page);
201
+ });
202
+
203
+ this.previewLazyLoad?.update();
204
+ }
205
+
206
+ protected renderPreviewMask(): HTMLElement {
207
+ if (!this.$previewMask) {
208
+ this.$previewMask = document.createElement("div");
209
+ this.$previewMask.className = this.wrapClassName("preview-mask");
210
+ this.sideEffect.addEventListener(this.$previewMask, "click", ev => {
211
+ if (this.readonly) {
212
+ return;
213
+ }
214
+ if (ev.target === this.$previewMask) {
215
+ this.togglePreview(false);
216
+ }
217
+ });
218
+ }
219
+ return this.$previewMask;
220
+ }
221
+
222
+ public setPaused = () => {
223
+ this.$btnPlay.classList.toggle(this.wrapClassName("footer-btn-playing"), false);
224
+ };
225
+
226
+ public setPlaying = () => {
227
+ this.$btnPlay.classList.toggle(this.wrapClassName("footer-btn-playing"), true);
228
+ };
229
+
230
+ public refreshBtnSidebar() {
231
+ this.$btnSidebar.style.display = this.pages.length > 0 ? "" : "none";
232
+ }
233
+
234
+ protected renderFooter(): HTMLElement {
235
+ if (!this.$footer) {
236
+ const $footer = document.createElement("div");
237
+ $footer.className = this.wrapClassName("footer");
238
+ this.$footer = $footer;
239
+
240
+ if (this.readonly) {
241
+ $footer.classList.add(this.wrapClassName("readonly"));
242
+ }
243
+
244
+ if (this.isSmallBox) {
245
+ $footer.classList.add(this.wrapClassName("float-footer"));
246
+ }
247
+
248
+ const $btnSidebar = this.renderFooterBtn("btn-sidebar", sidebarSVG(this.namespace));
249
+ this.sideEffect.addEventListener($btnSidebar, "click", () => {
250
+ if (this.readonly) {
251
+ return;
252
+ }
253
+ this.togglePreview();
254
+ });
255
+ this.$btnSidebar = $btnSidebar;
256
+ this.$btnSidebar.style.display = "none";
257
+ this.$footer.appendChild($btnSidebar);
258
+
259
+ const $pageJumps = document.createElement("div");
260
+ $pageJumps.className = this.wrapClassName("page-jumps");
261
+
262
+ const $btnPageBack = this.renderFooterBtn("btn-page-back", arrowLeftSVG(this.namespace));
263
+ this.sideEffect.addEventListener($btnPageBack, "click", () => {
264
+ if (this.readonly) {
265
+ return;
266
+ }
267
+ this.onNewPageIndex(this.pageIndex - 1);
268
+ });
269
+ $pageJumps.appendChild($btnPageBack);
270
+
271
+ if (this.onPlay) {
272
+ const $btnPlay = this.renderFooterBtn(
273
+ "btn-page-play",
274
+ playSVG(this.namespace),
275
+ pauseSVG(this.namespace)
276
+ );
277
+ this.$btnPlay = $btnPlay;
278
+ this.sideEffect.addEventListener($btnPlay, "click", () => {
279
+ if (this.readonly) {
280
+ return;
281
+ }
282
+ this.setPlaying();
283
+ if (this.onPlay) {
284
+ this.onPlay();
285
+ }
286
+ });
287
+
288
+ $pageJumps.appendChild($btnPlay);
289
+ }
290
+
291
+ const $btnPageNext = this.renderFooterBtn("btn-page-next", arrowRightSVG(this.namespace));
292
+ this.sideEffect.addEventListener($btnPageNext, "click", () => {
293
+ if (this.readonly) {
294
+ return;
295
+ }
296
+ this.onNewPageIndex(this.pageIndex + 1);
297
+ });
298
+ $pageJumps.appendChild($btnPageNext);
299
+
300
+ const $pageNumber = document.createElement("div");
301
+ $pageNumber.className = this.wrapClassName("page-number");
302
+
303
+ const $pageNumberInput = document.createElement("input");
304
+ $pageNumberInput.className = this.wrapClassName("page-number-input");
305
+ $pageNumberInput.value = String(this.pageIndex + 1);
306
+ if (this.readonly) {
307
+ $pageNumberInput.disabled = true;
308
+ }
309
+ this.$pageNumberInput = $pageNumberInput;
310
+ this.sideEffect.addEventListener($pageNumberInput, "focus", () => {
311
+ $pageNumberInput.select();
312
+ });
313
+ this.sideEffect.addEventListener($pageNumberInput, "change", () => {
314
+ if (this.readonly) {
315
+ return;
316
+ }
317
+ if ($pageNumberInput.value) {
318
+ this.onNewPageIndex(Number($pageNumberInput.value) - 1);
319
+ }
320
+ });
321
+
322
+ const $totalPage = document.createElement("span");
323
+ this.$totalPage = $totalPage;
324
+
325
+ $pageNumber.appendChild($pageNumberInput);
326
+ $pageNumber.appendChild($totalPage);
327
+
328
+ this.$footer.appendChild($pageJumps);
329
+ this.$footer.appendChild($pageNumber);
330
+ }
331
+ return this.$footer;
332
+ }
333
+
334
+ protected renderFooterBtn(
335
+ className: string,
336
+ $icon: SVGElement,
337
+ $iconActive?: SVGElement
338
+ ): HTMLButtonElement {
339
+ const $btn = document.createElement("button");
340
+ $btn.className = this.wrapClassName("footer-btn") + " " + this.wrapClassName(className);
341
+
342
+ $btn.appendChild($icon);
343
+
344
+ if ($iconActive) {
345
+ $btn.appendChild($iconActive);
346
+ }
347
+
348
+ return $btn;
349
+ }
350
+
351
+ protected togglePreview(isShowPreview?: boolean): void {
352
+ this.isShowPreview = isShowPreview ?? !this.isShowPreview;
353
+ this.$content.classList.toggle(this.wrapClassName("preview-active"), this.isShowPreview);
354
+ if (this.isShowPreview) {
355
+ const $previewPage = this.$preview.querySelector<HTMLElement>(
356
+ "." + this.wrapClassName(`preview-page-${this.pageIndex}`)
357
+ );
358
+ if ($previewPage) {
359
+ this.previewLazyLoad ||= new LazyLoad({
360
+ container: this.$preview,
361
+ elements_selector: `.${this.wrapClassName("preview-page>img")}`,
362
+ });
363
+ this.$preview.scrollTo({
364
+ top: $previewPage.offsetTop - 16,
365
+ });
366
+ }
367
+ }
368
+ }
369
+
370
+ protected wrapClassName(className: string): string {
371
+ return `${this.namespace}-${className}`;
372
+ }
373
+
374
+ protected namespace = "netless-app-slide";
375
+
376
+ protected isShowPreview = false;
377
+
378
+ protected isSmallBox = false;
379
+
380
+ protected sideEffect = new SideEffectManager();
381
+ }