Skip to content

Commit c565ffe

Browse files
committed
Add new properties, update demo
1 parent 8f84086 commit c565ffe

File tree

6 files changed

+240
-41
lines changed

6 files changed

+240
-41
lines changed

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
[![npm version](https://img.shields.io/npm/v/pdfjs-viewer-element?logo=npm&logoColor=fff)](https://www.npmjs.com/package/pdfjs-viewer-element)
44
[![Package Quality](https://packagequality.com/shield/pdfjs-viewer-element.svg)](https://packagequality.com/#?package=pdfjs-viewer-element)
55

6-
A web component for viewing pdf files in the browser. The package is based on [PDF.js viewer](https://mozilla.github.io/pdf.js/web/viewer.html) build and works with any framework. See [demo](https://alekswebnet.github.io/pdfjs-viewer-element/index.html).
6+
A web component for viewing pdf files in the browser. The package is based on [PDF.js default viewer](https://mozilla.github.io/pdf.js/web/viewer.html) build and works with any framework. See [demo](https://alekswebnet.github.io/pdfjs-viewer-element/).
77

8-
⚠️ `pdfjs-viewer-element` uses PDF.js [prebuilt](http://mozilla.github.io/pdf.js/getting_started/), that includes the generic build of PDF.js and the viewer. To use the package you should download and **place the prebuilt** files to some directory of your project. You may specify the path to this directory with `viewer-path` property (`/pdfjs` by default).
8+
⚠️ `pdfjs-viewer-element` uses PDF.js [prebuilt](http://mozilla.github.io/pdf.js/getting_started/), that includes the generic build of PDF.js and the viewer. To use the package you should download and **place the prebuilt** files to some directory of your project. Then specify the path to this directory with `viewer-path` property (`/pdfjs` by default).
99

1010
## Install
1111
```
@@ -31,7 +31,7 @@ import 'pdfjs-viewer-element'
3131
```
3232

3333
```javascript
34-
<pdfjs-viewer-element src="/compressed.tracemonkey-pldi-09.pdf" viewer-path="/path-to-viewer"></pdfjs-viewer-element>
34+
<pdfjs-viewer-element src="/file.pdf" viewer-path="/path-to-viewer"></pdfjs-viewer-element>
3535
```
3636

3737
## Properties
@@ -40,7 +40,22 @@ import 'pdfjs-viewer-element'
4040

4141
`viewer-path` - path to prebuilt directory (`/pdfjs` by default).
4242

43-
`locale` - language localization (e.g. 'en', 'en-US', 'es', 'de'), see [pdf.js i10n files](https://github.com/mozilla/pdf.js/tree/master/l10n) (the language of your browser by default)
43+
`src` - Path to pdf file
44+
45+
`viewer-path` - Path to PDF.js prebuilt
46+
47+
`locale` - Language of the interface
48+
49+
`page` - Page number
50+
51+
`search` - Search text
52+
53+
`phrase` - Search by phrase
54+
55+
`zoom` - Zoom level
56+
57+
`pagemode` - Page mode
58+
4459
## License
4560
For this package - [MIT](http://opensource.org/licenses/MIT).
4661

index.html

Lines changed: 172 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,181 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.min.css" rel="stylesheet" />
6+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
67
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
78
<title>pdfjs-viewer-element</title>
8-
<link rel="stylesheet" href="./src/index.css" />
9+
<meta name="description" content="A web component for viewing pdf files in the browser, based on PDF.js">
10+
<script src="https://unpkg.com/petite-vue" defer init></script>
911
<script type="module" src="/src/pdfjs-viewer-element.ts"></script>
12+
<script>var locales=["ach","af","an","ar","ast","az","be","bg","bn","bo","br","brx","bs","ca","cak","ckb","cs","cy","da","de","dsb","el","en-CA","en-GB","en-US","eo","es-AR","es-CL","es-ES","es-MX","et","eu","fa","ff","fi","fr","fur","fy-NL","ga-IE","gd","gl","gn","gu-IN","he","hi-IN","hr","hsb","hu","hy-AM","hye","ia","id","is","it","ja","ka","kab","kk","km","kn","ko","lij","lo","lt","ltg","lv","meh","mk","mr","ms","my","nb-NO","ne-NP","nl","nn-NO","oc","pa-IN","pl","pt-BR","pt-PT","rm","ro","ru","sat","sc","scn","sco","si","sk","skr","sl","son","sq","sr","sv-SE","szl","ta","te","tg","th","tl","tr","trs","uk","ur","uz","vi","wo","xh","zh-CN","zh-TW"];</script>
13+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/prism.min.js"></script>
14+
<style>
15+
@media (min-width: 992px) {
16+
.content-col {
17+
max-height: 100vh;
18+
max-height: 100dvh;
19+
}
20+
}
21+
</style>
1022
</head>
11-
<body>
12-
<pdfjs-viewer-element
13-
src="/compressed.tracemonkey-pldi-09.pdf"
14-
viewer-path="/pdfjs"
15-
locale="en"
16-
style="height: 100dvh"
17-
></pdfjs-viewer-element>
23+
<body class="bg-light">
24+
<div class="d-flex" v-scope="{ tab: window.location.href.includes('/api') ? 'api' : 'start' , src: '/example.pdf', viewerPath: '/pdfjs', page: null, locale: null, locales: window.locales, zoom: 'auto', pagemode: 'none', search: null, phrase: true}">
25+
<div class="container-fluid flex-fill p-0 ps-lg-2 overflow-hidden">
26+
<div class="row h-100">
27+
<div class="col-lg-6 px-4 pt-4 pb-4 overflow-auto content-col">
28+
<div class="d-flex pt-0 pt-lg-4 justify-content-center align-items-center">
29+
<a class="d-block" href="https://github.com/alekswebnet/pdfjs-viewer-element" target="_blank">
30+
<svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle">
31+
<path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
32+
</svg>
33+
</a>
34+
</div>
35+
<div class="pt-1 d-flex align-items-center justify-content-center flex-wrap">
36+
<h1 class="me-2 me-sm-3 mb-0" style="white-space: nowrap">pdfjs-viewer-element</h1>
37+
<a href="https://www.npmjs.com/package/pdfjs-viewer-element" target="_blank"><img class="mt-1" src="https://img.shields.io/npm/v/pdfjs-viewer-element" alt="Npm version"></a>
38+
</div>
39+
<p class="lead text-center text-muted pb-sm-4">
40+
A web component for viewing pdf files in the browser, based on
41+
<a href="https://github.com/mozilla/pdf.js" target="_blank">PDF.js</a> viewer.
42+
</p>
43+
<ul class="nav nav-tabs" role="tablist" style="display: none" :class="{ 'd-flex': !!tab }">
44+
<li class="nav-item bg-light" role="presentation">
45+
<button
46+
class="nav-link bg-light"
47+
:class="{ 'active': tab === 'start' }"
48+
:style="{ 'border-bottom-color': tab === 'start' ? 'var(--bs-light)' : '#dee2e6' }"
49+
type="button"
50+
role="tab"
51+
@click="window.history.pushState({}, '', `${window.location.origin}`), tab = 'start'"
52+
aria-controls="start">📦 Getting Started</button>
53+
</li>
54+
<li class="nav-item bg-light" role="presentation">
55+
<button
56+
class="nav-link bg-light"
57+
:class="{ 'active': tab === 'api' }"
58+
:style="{ 'border-bottom-color': tab === 'api' ? 'var(--bs-light)' : '#dee2e6' }"
59+
type="button"
60+
role="tab"
61+
@click="window.history.pushState({}, '', `${window.location.origin}/api`), tab = 'api'"
62+
aria-controls="api">⚙️ API</button>
63+
</li>
64+
</ul>
65+
<div style="display: none" class="pt-4 small" :class="{ 'd-block': tab === 'start' }">
66+
<p class="fs-6">Download and place the
67+
<a href="http://mozilla.github.io/pdf.js/getting_started" target="_blank">PDF.js prebuilt</a>
68+
files to some directory of your project. Specify the path to this directory with <code style="color: #905">viewer-path</code> property (<code style="color: #07a">/pdfjs</code> by default). </p>
69+
<p class="fs-6 mb-0">Install using module bundlers: </p>
70+
<pre class="d-flex flex-column bg-white border rounded language-bash">
71+
<code style="color: #999"># With npm</code>
72+
<code>npm install pdfjs-viewer-element</code>
73+
<code style="color: #999"># With yarn</code>
74+
<code>yarn add pdfjs-viewer-element</code>
75+
</pre>
76+
<pre class="d-flex flex-column bg-white border rounded language-js">
77+
<code>import 'pdfjs-viewer-element'</code>
78+
</pre>
79+
<p class="fs-6 mb-0">Install using browser: </p>
80+
<pre class="d-flex bg-white border rounded language-markup">
81+
<code>&lt;script type="module" src="https://cdn.skypack.dev/pdfjs-viewer-element"&gt;&lt;/script&gt;</code>
82+
</pre>
83+
<p class="fs-6 mb-0">Usage: </p>
84+
<pre class="d-flex bg-white border rounded language-markup">
85+
<code>&lt;pdfjs-viewer-element scr="/file.pdf" viewer-path="/pdfjs"&gt;&lt;/pdfjs-viewer-element&gt;</code>
86+
</pre>
87+
</div>
88+
<form style="display: none" class="pt-4" :class="{ 'd-block': tab === 'api' }">
89+
<p class="fs-6 mb-2">Properties overview:</p>
90+
<div class="row">
91+
<div class="col-5 col-sm-3"><label for="src" class="col-form-label"><code style="color: #905">src</code></label></div>
92+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Path to pdf file</span></div>
93+
<div class="col-sm-4 d-flex align-items-center">
94+
<input id="src" v-model="src" class="form-control form-control-sm" type="text" aria-label="Pdf file path" disabled />
95+
</div>
96+
</div>
97+
<div class="row">
98+
<div class="col-5 col-sm-3"><label for="viewerPath" class="col-form-label"><code style="color: #905">viewer-path</code></label></div>
99+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Path to PDF.js prebuilt</span></div>
100+
<div class="col-sm-4 d-flex align-items-center">
101+
<input id="viewerPath" v-model="viewerPath" class="form-control form-control-sm" type="text" aria-label="Viewer path" disabled />
102+
</div>
103+
</div>
104+
<div class="row">
105+
<div class="col-5 col-sm-3"><label for="locale" class="col-form-label"><code style="color: #905">locale</code></label></div>
106+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Language of the interface</span></div>
107+
<div class="col-sm-4 d-flex align-items-center">
108+
<select id="locale" v-model="locale" class="form-select form-select-sm" aria-label="Language localization">
109+
<option selected :value="null">Default</option>
110+
<option v-for="loc in locales" :value="loc" v-html="loc"></option>
111+
</select>
112+
</div>
113+
</div>
114+
<div class="row">
115+
<div class="col-5 col-sm-3"><label for="page" class="col-form-label"><code style="color: #905">page</code></label></div>
116+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Page number</span></div>
117+
<div class="col-sm-4 d-flex align-items-center">
118+
<input id="page" v-model="page" class="form-control form-control-sm" type="number" aria-label="Page number" />
119+
</div>
120+
</div>
121+
<hr class="d-block d-sm-none mb-0">
122+
<div class="row">
123+
<div class="col-5 col-sm-3"><label for="search" class="col-form-label"><code style="color: #905">search</code></label></div>
124+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Search text</span></div>
125+
<div class="col-sm-4 d-flex align-items-center">
126+
<input id="search" v-model="search" class="form-control form-control-sm" type="text" aria-label="Search" />
127+
</div>
128+
</div>
129+
<div class="row">
130+
<div class="col-5 col-sm-3"><label for="phrase" class="col-form-label"><code style="color: #905">phrase</code></label></div>
131+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Search by phrase</span></div>
132+
<div class="col-sm-4 d-flex align-items-center">
133+
<input id="phrase" v-model="phrase" class="form-check-input" type="checkbox" aria-label="Search by phrase" />
134+
</div>
135+
</div>
136+
<div class="row">
137+
<div class="col-5 col-sm-3"><label for="zoom" class="col-form-label"><code style="color: #905">zoom</code></label></div>
138+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Zoom level</span></div>
139+
<div class="col-sm-4 d-flex align-items-center">
140+
<select id="zoom" v-model="zoom" class="form-select form-select-sm" aria-label="Zoom">
141+
<option selected value="auto">auto</option>
142+
<option value="page-width">page-width</option>
143+
<option value="page-height">page-height</option>
144+
<option value="page-fit">page-fit</option>
145+
<option value="200">200%</option>
146+
</select>
147+
</div>
148+
</div>
149+
<div class="row">
150+
<div class="col-5 col-sm-3"><label for="pagemode" class="col-form-label"><code style="color: #905">pagemode</code></label></div>
151+
<div class="col-7 col-sm-5 col-form-label text-muted d-flex"><span class="ms-auto ms-sm-0 small">Page mode</span></div>
152+
<div class="col-sm-4 d-flex align-items-center">
153+
<select id="pagemode" v-model="pagemode" class="form-select form-select-sm" aria-label="Page mode">
154+
<option selected value="none">none</option>
155+
<option value="thumbs">thumbs</option>
156+
<option value="bookmarks">bookmarks</option>
157+
<option value="attachments">attachments</option>
158+
</select>
159+
</div>
160+
</div>
161+
</form>
162+
</div>
163+
<hr class="d-block d-lg-none mb-0 mt-0 bg-secondary">
164+
<div class="col-lg-6 h-100 px-2 ps-lg-0 h-lg-auto" style="height: 100vh; height: 100dvh">
165+
<pdfjs-viewer-element
166+
:src="src"
167+
:viewer-path="viewerPath"
168+
:locale="locale"
169+
:page="page"
170+
:search="search"
171+
:phrase="phrase"
172+
:zoom="zoom"
173+
:pagemode="pagemode"
174+
style="height: 100vh; height: 100dvh"
175+
class="border-start">
176+
</pdfjs-viewer-element>
177+
</div>
178+
</div>
179+
</div>
180+
</div>
18181
</body>
19182
</html>
File renamed without changes.

src/debounce.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const debounce = (fn: Function, ms = 300) => {
2+
let timeoutId: ReturnType<typeof setTimeout>;
3+
return function (this: any, ...args: any[]) {
4+
clearTimeout(timeoutId);
5+
timeoutId = setTimeout(() => fn.apply(this, args), ms);
6+
};
7+
};

src/index.css

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/pdfjs-viewer-element.ts

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { debounce } from "./debounce";
2+
13
const template = document.createElement('template');
2-
template.innerHTML = `<iframe frameborder="0" width="100%"></iframe><style>:host{width:100%;display:block;overflow:hidden}:host iframe{height:inherit}</style>`
4+
template.innerHTML = `<iframe frameborder="0" width="100%"></iframe><style>:host{width:100%;display:block;overflow:hidden}:host iframe{height:100%}</style>`
35

46
export class PdfjsViewerElement extends HTMLElement {
57
constructor() {
@@ -8,42 +10,58 @@ export class PdfjsViewerElement extends HTMLElement {
810
shadowRoot.appendChild(template.content.cloneNode(true))
911
}
1012

11-
private iframe!: PdfjsViewerElementIframe;
13+
private iframe!: PdfjsViewerElementIframe
1214

1315
static get observedAttributes() {
14-
return ['src', 'viewer-path', 'locale']
16+
return ['src', 'viewer-path', 'locale', 'page', 'search', 'phrase', 'zoom', 'pagemode']
1517
}
1618

1719
connectedCallback() {
18-
this.iframe = this.shadowRoot?.querySelector('iframe') as PdfjsViewerElementIframe
19-
this.setAttributes()
20-
this.initEventListeners()
20+
this.iframe = this.shadowRoot!.querySelector('iframe') as PdfjsViewerElementIframe
21+
this.setEventListeners()
2122
}
2223

23-
attributeChangedCallback(name: string) {
24-
if (['src', 'viewer-path'].includes(name)) {
25-
this.setAttributes()
26-
this.initEventListeners()
27-
}
24+
attributeChangedCallback() {
25+
this.onAttrsChanged()
2826
}
2927

30-
setAttributes() {
31-
if (!this.hasAttribute('viewer-path')) this.setAttribute('viewer-path', '/pdfjs')
32-
this.iframe?.setAttribute(
33-
'src',
34-
`${this.getAttribute('viewer-path')}/web/viewer.html?file=${this.getAttribute('src') || ''}`
28+
private onAttrsChanged = debounce(async () => {
29+
const url = this.iframe?.getAttribute('src')
30+
31+
if (url && url.split('&locale=')[1] !== this.getAttribute('locale')) {
32+
await this.setProps()
33+
this.iframe.contentWindow.location.reload()
34+
}
35+
else {
36+
await this.setProps()
37+
}
38+
})
39+
40+
private async setProps() {
41+
const viewerPath = this.getAttribute('viewer-path') || '/pdfjs'
42+
const file = this.getFileSrc(this.getAttribute('src') || '')
43+
const page = this.getAttribute('page') || ''
44+
const search = this.getAttribute('search') || ''
45+
const phrase = this.getAttribute('phrase') || ''
46+
const zoom = this.getAttribute('zoom') || ''
47+
const pagemode = this.getAttribute('pagemode') || ''
48+
const locale = this.getAttribute('locale')
49+
50+
this.iframe.setAttribute(
51+
'src',
52+
`${viewerPath}/web/viewer.html#file=${file}&page=${page}&zoom=${zoom}&pagemode=${pagemode}&search=${search}&phrase=${phrase}${locale ? '&locale='+locale : ''}`
3553
)
3654
}
3755

38-
initEventListeners() {
39-
document.addEventListener('webviewerloaded', () => {
40-
this.setViewerOptions()
56+
private setEventListeners() {
57+
document.addEventListener('webviewerloaded', async () => {
58+
this.iframe.contentWindow?.PDFViewerApplicationOptions.set('disablePreferences', true)
59+
this.iframe.contentWindow?.PDFViewerApplicationOptions.set('pdfBugEnabled', true)
4160
})
4261
}
4362

44-
setViewerOptions() {
45-
const locale = this.getAttribute('locale')
46-
if (locale) this.iframe.contentWindow?.PDFViewerApplicationOptions.set('locale', locale)
63+
private getFileSrc(path: string) {
64+
return path.startsWith('/') ? `${window.location.origin}${path}` : path
4765
}
4866
}
4967

@@ -55,8 +73,8 @@ declare global {
5573

5674
export interface PdfjsViewerElementIframeWindow extends Window {
5775
PDFViewerApplicationOptions: {
58-
set: (name: string, value: string) => void
59-
};
76+
set: (name: string, value: string | boolean) => void
77+
}
6078
}
6179

6280
export interface PdfjsViewerElementIframe extends HTMLIFrameElement {

0 commit comments

Comments
 (0)