@harvard-lts/mirador-pdf-plugin 0.0.3 → 0.0.5

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 CHANGED
@@ -57,6 +57,7 @@ Mirador.viewer(
57
57
  ],
58
58
  miradorPdfPlugin: {
59
59
  pdfAPI: 'https://your-pdf-service.example.com/pdf/download/',
60
+ maxPages: 500,
60
61
  },
61
62
  },
62
63
  [...Plugin]
@@ -65,9 +66,10 @@ Mirador.viewer(
65
66
 
66
67
  ### Config options
67
68
 
68
- | Option | Type | Description |
69
- | -------- | ------ | ------------------------------------------------ |
70
- | `pdfAPI` | String | Base URL of the PDF download service (required). |
69
+ | Option | Type | Description |
70
+ | ---------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
71
+ | `pdfAPI` | String | Base URL of the PDF download service (required). |
72
+ | `maxPages` | Number | Maximum pages allowed in a single request (default: 500). Keep this in sync with the service's `MAX_PAGES_PER_REQUEST` env var so client/server agree. |
71
73
 
72
74
  ### URL construction
73
75
 
package/dist/es/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react';
1
+ import { useState, useEffect, useMemo } from 'react';
2
2
  import MenuItem from '@material-ui/core/MenuItem';
3
3
  import ListItemIcon from '@material-ui/core/ListItemIcon';
4
4
  import ListItemText from '@material-ui/core/ListItemText';
@@ -183,7 +183,7 @@ var miradorPdfMenuItem = {
183
183
  };
184
184
 
185
185
  var mapStateToProps = function mapStateToProps(state, _ref) {
186
- var _manifest$getSequence, _state$config;
186
+ var _manifest$getSequence, _state$config, _state$config2;
187
187
  var windowId = _ref.windowId;
188
188
  var manifest = getManifestoInstance(state, {
189
189
  windowId: windowId
@@ -193,6 +193,7 @@ var mapStateToProps = function mapStateToProps(state, _ref) {
193
193
  manifestId: (manifest || {}).id,
194
194
  totalPages: manifest ? ((_manifest$getSequence = manifest.getSequences()[0]) === null || _manifest$getSequence === void 0 ? void 0 : _manifest$getSequence.getTotalCanvases()) || 0 : 0,
195
195
  pdfAPI: (_state$config = state.config) === null || _state$config === void 0 || (_state$config = _state$config.miradorPdfPlugin) === null || _state$config === void 0 ? void 0 : _state$config.pdfAPI,
196
+ maxPages: ((_state$config2 = state.config) === null || _state$config2 === void 0 || (_state$config2 = _state$config2.miradorPdfPlugin) === null || _state$config2 === void 0 ? void 0 : _state$config2.maxPages) || 500,
196
197
  containerId: state.config.id
197
198
  };
198
199
  };
@@ -212,67 +213,102 @@ var extractUrn = function extractUrn(manifestId) {
212
213
  var withoutLeadingSlash = pathname.replace(/^\//, '');
213
214
  return withoutLeadingSlash.split(':MANIFEST')[0];
214
215
  };
216
+
217
+ // Pure validation of the pages input. Returns { valid, message } so it can drive
218
+ // both the inline error text and the disabled state of the Download button
219
+ // without setting React state during render.
220
+ var validatePages = function validatePages(pages, totalPages, maxPages) {
221
+ if (pages === '') {
222
+ // Empty input means "all pages" — only valid if the whole document fits.
223
+ if (totalPages > maxPages) {
224
+ return {
225
+ valid: false,
226
+ message: "This document has ".concat(totalPages, " pages, which exceeds the ").concat(maxPages, "-page limit. Please enter a range of up to ").concat(maxPages, " pages.")
227
+ };
228
+ }
229
+ return {
230
+ valid: true,
231
+ message: ''
232
+ };
233
+ }
234
+ var singlePattern = /^\d+$/;
235
+ var rangePattern = /^(\d+)-(\d+)$/;
236
+ if (singlePattern.test(pages)) {
237
+ var num = parseInt(pages, 10);
238
+ if (num < 1 || num > totalPages) {
239
+ return {
240
+ valid: false,
241
+ message: "Page must be between 1 and ".concat(totalPages, ".")
242
+ };
243
+ }
244
+ return {
245
+ valid: true,
246
+ message: ''
247
+ };
248
+ }
249
+ var rangeMatch = pages.match(rangePattern);
250
+ if (rangeMatch) {
251
+ var start = parseInt(rangeMatch[1], 10);
252
+ var end = parseInt(rangeMatch[2], 10);
253
+ if (start < 1 || end > totalPages) {
254
+ return {
255
+ valid: false,
256
+ message: "Page range must be between 1 and ".concat(totalPages, ".")
257
+ };
258
+ }
259
+ if (start >= end) {
260
+ return {
261
+ valid: false,
262
+ message: 'Start page must be less than end page.'
263
+ };
264
+ }
265
+ if (end - start + 1 > maxPages) {
266
+ return {
267
+ valid: false,
268
+ message: "You can request at most ".concat(maxPages, " pages at once. Please split this into smaller requests.")
269
+ };
270
+ }
271
+ return {
272
+ valid: true,
273
+ message: ''
274
+ };
275
+ }
276
+ return {
277
+ valid: false,
278
+ message: 'Please enter a page number or range (e.g. 5 or 5-10).'
279
+ };
280
+ };
215
281
  var MiradorPdfDialog = function MiradorPdfDialog(_ref3) {
216
282
  var open = _ref3.open,
217
283
  closeDialog = _ref3.closeDialog,
218
284
  manifestId = _ref3.manifestId,
219
285
  totalPages = _ref3.totalPages,
220
286
  pdfAPI = _ref3.pdfAPI,
287
+ maxPages = _ref3.maxPages,
221
288
  containerId = _ref3.containerId,
222
289
  classes = _ref3.classes;
223
290
  var _useState = useState(''),
224
291
  _useState2 = _slicedToArray(_useState, 2),
225
292
  pages = _useState2[0],
226
293
  setPages = _useState2[1];
227
- var _useState3 = useState(''),
228
- _useState4 = _slicedToArray(_useState3, 2),
229
- error = _useState4[0],
230
- setError = _useState4[1];
231
294
 
232
295
  // Reset form state whenever the dialog is closed
233
296
  useEffect(function () {
234
297
  if (!open) {
235
298
  setPages('');
236
- setError('');
237
299
  }
238
300
  }, [open]);
239
301
  var handleClose = function handleClose() {
240
302
  closeDialog();
241
303
  setPages('');
242
- setError('');
243
- };
244
- var validate = function validate() {
245
- if (pages === '') return true;
246
- var singlePattern = /^\d+$/;
247
- var rangePattern = /^(\d+)-(\d+)$/;
248
- if (singlePattern.test(pages)) {
249
- var num = parseInt(pages, 10);
250
- if (num < 1 || num > totalPages) {
251
- setError("Page must be between 1 and ".concat(totalPages, "."));
252
- return false;
253
- }
254
- return true;
255
- }
256
- var rangeMatch = pages.match(rangePattern);
257
- if (rangeMatch) {
258
- var start = parseInt(rangeMatch[1], 10);
259
- var end = parseInt(rangeMatch[2], 10);
260
- if (start < 1 || end > totalPages) {
261
- setError("Page range must be between 1 and ".concat(totalPages, "."));
262
- return false;
263
- }
264
- if (start >= end) {
265
- setError('Start page must be less than end page.');
266
- return false;
267
- }
268
- return true;
269
- }
270
- setError('Please enter a page number or range (e.g. 5 or 5-10).');
271
- return false;
272
304
  };
305
+
306
+ // Live validity drives both the disabled Download button and the helper text.
307
+ var validity = useMemo(function () {
308
+ return validatePages(pages, totalPages, maxPages);
309
+ }, [pages, totalPages, maxPages]);
273
310
  var handleDownload = function handleDownload() {
274
- setError('');
275
- if (!validate()) return;
311
+ if (!validity.valid) return;
276
312
  var urn = extractUrn(manifestId);
277
313
  var baseUrl = "".concat(pdfAPI.replace(/\/$/, ''), "/").concat(urn);
278
314
  var url;
@@ -309,7 +345,11 @@ var MiradorPdfDialog = function MiradorPdfDialog(_ref3) {
309
345
  children: [/*#__PURE__*/jsx(Typography, {
310
346
  variant: "body1",
311
347
  gutterBottom: true,
312
- children: "The document contains ".concat(totalPages, " pages and has an estimated file size of ").concat((totalPages * 0.7862).toFixed(2), " MB. All pages will be included by default. If you wish to download certain portions of it, you may provide a comma separated list of pages and/or ranges.")
348
+ children: "The document contains ".concat(totalPages, " pages and has an estimated file size of ").concat((totalPages * 0.7862).toFixed(2), " MB. All pages will be included by default. If you wish to download certain portions of it, you may provide a single page (e.g. 5) or a range (e.g. 5-10).")
349
+ }), /*#__PURE__*/jsx(Typography, {
350
+ variant: "body2",
351
+ gutterBottom: true,
352
+ children: "You can download up to ".concat(maxPages, " pages per request. For larger documents, please request several smaller ranges (e.g. 1-").concat(maxPages, ", then ").concat(maxPages + 1, "-").concat(maxPages * 2, ").")
313
353
  }), /*#__PURE__*/jsx(TextField, {
314
354
  id: "pdf-pages-input",
315
355
  label: "Pages",
@@ -320,8 +360,8 @@ var MiradorPdfDialog = function MiradorPdfDialog(_ref3) {
320
360
  onKeyDown: function onKeyDown(e) {
321
361
  if (e.key === 'Enter') handleDownload();
322
362
  },
323
- error: Boolean(error),
324
- helperText: error || "Enter a page number or range (e.g. 5 or 5-10). Max: ".concat(totalPages),
363
+ error: !validity.valid,
364
+ helperText: validity.valid ? "Enter a page number or range (e.g. 5 or 5-10). Max ".concat(maxPages, " pages per request.") : validity.message,
325
365
  fullWidth: true,
326
366
  margin: "normal",
327
367
  variant: "outlined"
@@ -335,6 +375,7 @@ var MiradorPdfDialog = function MiradorPdfDialog(_ref3) {
335
375
  onClick: handleDownload,
336
376
  color: "primary",
337
377
  variant: "contained",
378
+ disabled: !validity.valid,
338
379
  children: "Download"
339
380
  })]
340
381
  })]
@@ -342,6 +383,7 @@ var MiradorPdfDialog = function MiradorPdfDialog(_ref3) {
342
383
  };
343
384
  MiradorPdfDialog.defaultProps = {
344
385
  open: false,
386
+ maxPages: 500,
345
387
  containerId: null
346
388
  };
347
389
  var styles = function styles() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harvard-lts/mirador-pdf-plugin",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A Mirador 3 plugin that interacts with MPS PDF Download Service ",
5
5
  "module": "dist/es/index.js",
6
6
  "files": [
@@ -41,17 +41,17 @@
41
41
  "@rollup/plugin-babel": "^6.0.3",
42
42
  "@testing-library/jest-dom": "^6.1.5",
43
43
  "@testing-library/react": "^12.0.0",
44
- "@vitejs/plugin-react": "^4.2.1",
45
- "@vitest/coverage-v8": "^1.0.0",
46
- "@vitest/ui": "^1.0.0",
44
+ "@vitejs/plugin-react": "^4.7.0",
45
+ "@vitest/coverage-v8": "^4.1.8",
46
+ "@vitest/ui": "^4.1.8",
47
47
  "jsdom": "^23.0.0",
48
48
  "mirador": "^3.4.3",
49
49
  "npm-run-all": "^4.1.5",
50
50
  "parcel": "^2.8.3",
51
51
  "rollup": "^3.29.5",
52
52
  "url": "^0.11.4",
53
- "vite": "^5.0.0",
54
- "vitest": "^1.0.0",
53
+ "vite": "^7.3.5",
54
+ "vitest": "^4.1.8",
55
55
  "whatwg-fetch": "^3.6.20"
56
56
  }
57
57
  }