@nice2dev/ui-printing 1.2.2 → 1.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nice2dev/ui-printing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "React components for print management: template editor, template browser, print preview, print queue and print button",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"printing",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"react": ">=17.0.0",
|
|
49
49
|
"react-dom": ">=17.0.0",
|
|
50
|
-
"@nice2dev/ui": "^1.
|
|
50
|
+
"@nice2dev/ui": "^1.3.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@nice2dev/ui": "^1.
|
|
53
|
+
"@nice2dev/ui": "^1.3.0",
|
|
54
54
|
"@types/react": "^19.2.18",
|
|
55
55
|
"@types/react-dom": "^19.2.7",
|
|
56
56
|
"@vitejs/plugin-react": "^4.7.0",
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
NiceIcon,
|
|
3
|
+
useInputHints,
|
|
4
|
+
useIntent,
|
|
5
|
+
useNiceTranslation,
|
|
6
|
+
useOverlayFocus,
|
|
7
|
+
} from '@nice2dev/ui';
|
|
8
|
+
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
|
3
9
|
import { createPortal } from 'react-dom';
|
|
4
10
|
|
|
5
11
|
import type {
|
|
@@ -259,18 +265,10 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
259
265
|
if (!asPopup || !isOpen) {
|
|
260
266
|
return;
|
|
261
267
|
}
|
|
262
|
-
const handler = (e: KeyboardEvent) => {
|
|
263
|
-
if (e.key === 'Escape') {
|
|
264
|
-
handleClose();
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
document.addEventListener('keydown', handler);
|
|
268
268
|
document.body.style.overflow = 'hidden';
|
|
269
269
|
return () => {
|
|
270
|
-
document.removeEventListener('keydown', handler);
|
|
271
270
|
document.body.style.overflow = '';
|
|
272
271
|
};
|
|
273
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
274
272
|
}, [asPopup, isOpen]);
|
|
275
273
|
|
|
276
274
|
const dataList = useMemo(() => {
|
|
@@ -285,12 +283,53 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
285
283
|
|
|
286
284
|
const currentData = dataList[entityIdx] ?? {};
|
|
287
285
|
|
|
286
|
+
// The popup holds focus; Escape / B close it — only the top overlay — and focus goes back to
|
|
287
|
+
// the opener (1.3.0, a document listener before). A pad: LB / RB between the records, LT / RT
|
|
288
|
+
// zoom.
|
|
289
|
+
const dialogRef = useRef<HTMLDivElement>(null);
|
|
290
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
291
|
+
useOverlayFocus(dialogRef, { open: asPopup && isOpen, onClose: handleClose });
|
|
292
|
+
useIntent(
|
|
293
|
+
['sectionPrev', 'sectionNext', 'pagePrev', 'pageNext'],
|
|
294
|
+
(d) => {
|
|
295
|
+
if (d.intent === 'sectionPrev' || d.intent === 'sectionNext') {
|
|
296
|
+
const by = d.intent === 'sectionNext' ? 1 : -1;
|
|
297
|
+
setEntityIdx((i) => Math.min(Math.max(i + by, 0), dataList.length - 1));
|
|
298
|
+
} else if (d.source === 'keyboard') {
|
|
299
|
+
return false;
|
|
300
|
+
} else {
|
|
301
|
+
const by = d.intent === 'pageNext' ? 0.1 : -0.1;
|
|
302
|
+
setZoom((z) => Math.min(2.5, Math.max(0.3, z + by)));
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
{ ref: rootRef, enabled: !asPopup || isOpen },
|
|
306
|
+
);
|
|
307
|
+
useInputHints(
|
|
308
|
+
rootRef,
|
|
309
|
+
!asPopup || isOpen
|
|
310
|
+
? [
|
|
311
|
+
...(dataList.length > 1
|
|
312
|
+
? [
|
|
313
|
+
{
|
|
314
|
+
intent: 'sectionPrev' as const,
|
|
315
|
+
label: t('printPreview.previousPage', 'Previous page'),
|
|
316
|
+
},
|
|
317
|
+
{ intent: 'sectionNext' as const, label: t('printPreview.nextPage', 'Next page') },
|
|
318
|
+
]
|
|
319
|
+
: []),
|
|
320
|
+
{ intent: 'pageNext', label: t('printPreview.zoomIn', 'Zoom in') },
|
|
321
|
+
{ intent: 'pagePrev', label: t('printPreview.zoomOut', 'Zoom out') },
|
|
322
|
+
]
|
|
323
|
+
: [],
|
|
324
|
+
);
|
|
325
|
+
|
|
288
326
|
if (asPopup && !isOpen) {
|
|
289
327
|
return null;
|
|
290
328
|
}
|
|
291
329
|
|
|
292
330
|
const body = (
|
|
293
331
|
<div
|
|
332
|
+
ref={rootRef}
|
|
294
333
|
className={`${styles.root} ${theme === 'dark' ? styles.dark : ''} ${className ?? ''}`}
|
|
295
334
|
style={style}
|
|
296
335
|
>
|
|
@@ -303,8 +342,9 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
303
342
|
className={styles.navBtn}
|
|
304
343
|
disabled={entityIdx === 0}
|
|
305
344
|
onClick={() => setEntityIdx((i) => i - 1)}
|
|
345
|
+
aria-label={t('printPreview.previousPage', 'Previous page')}
|
|
306
346
|
>
|
|
307
|
-
|
|
347
|
+
<NiceIcon name="chevron-left" size={14} />
|
|
308
348
|
</button>
|
|
309
349
|
<span className={styles.navLabel}>
|
|
310
350
|
{entityIdx + 1} / {dataList.length}
|
|
@@ -313,8 +353,9 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
313
353
|
className={styles.navBtn}
|
|
314
354
|
disabled={entityIdx >= dataList.length - 1}
|
|
315
355
|
onClick={() => setEntityIdx((i) => i + 1)}
|
|
356
|
+
aria-label={t('printPreview.nextPage', 'Next page')}
|
|
316
357
|
>
|
|
317
|
-
|
|
358
|
+
<NiceIcon name="chevron-right" size={14} />
|
|
318
359
|
</button>
|
|
319
360
|
</>
|
|
320
361
|
)}
|
|
@@ -324,19 +365,28 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
324
365
|
</div>
|
|
325
366
|
|
|
326
367
|
<div className={styles.zoomBar}>
|
|
327
|
-
<button
|
|
328
|
-
|
|
368
|
+
<button
|
|
369
|
+
className={styles.zoomBtn}
|
|
370
|
+
onClick={() => setZoom((z) => Math.max(0.3, z - 0.1))}
|
|
371
|
+
aria-label={t('printPreview.zoomOut', 'Zoom out')}
|
|
372
|
+
>
|
|
373
|
+
<NiceIcon name="minus" size={14} />
|
|
329
374
|
</button>
|
|
330
375
|
<span className={styles.zoomLabel}>{Math.round(zoom * 100)}%</span>
|
|
331
|
-
<button
|
|
332
|
-
|
|
376
|
+
<button
|
|
377
|
+
className={styles.zoomBtn}
|
|
378
|
+
onClick={() => setZoom((z) => Math.min(2.5, z + 0.1))}
|
|
379
|
+
aria-label={t('printPreview.zoomIn', 'Zoom in')}
|
|
380
|
+
>
|
|
381
|
+
<NiceIcon name="plus" size={14} />
|
|
333
382
|
</button>
|
|
334
383
|
<button
|
|
335
384
|
className={styles.zoomBtn}
|
|
336
385
|
onClick={() => setZoom(0.8)}
|
|
337
386
|
title={t('printing.fit', 'Fit')}
|
|
387
|
+
aria-label={t('printing.fit', 'Fit')}
|
|
338
388
|
>
|
|
339
|
-
|
|
389
|
+
<NiceIcon name="expand" size={14} />
|
|
340
390
|
</button>
|
|
341
391
|
</div>
|
|
342
392
|
|
|
@@ -405,8 +455,10 @@ export const NicePrintPreview: React.FC<NicePrintPreviewProps> = ({
|
|
|
405
455
|
role="presentation"
|
|
406
456
|
>
|
|
407
457
|
<div
|
|
458
|
+
ref={dialogRef}
|
|
408
459
|
role="dialog"
|
|
409
460
|
aria-modal="true"
|
|
461
|
+
aria-label={template.name}
|
|
410
462
|
style={{
|
|
411
463
|
background: 'var(--nice-bg-elevated, var(--nice-bg-primary, #ffffff))',
|
|
412
464
|
borderRadius: 12,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { NiceIcon, useNiceTranslation } from '@nice2dev/ui';
|
|
1
|
+
import { clickOnActivationKey, NiceIcon, useNiceTranslation } from '@nice2dev/ui';
|
|
3
2
|
import React, { useState } from 'react';
|
|
4
3
|
|
|
5
4
|
import type {
|
|
@@ -144,6 +143,9 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
|
|
|
144
143
|
<div className={styles.grid}>
|
|
145
144
|
{filtered.map((tpl) => (
|
|
146
145
|
<div
|
|
146
|
+
role={onSelect ? 'button' : undefined}
|
|
147
|
+
tabIndex={onSelect ? 0 : undefined}
|
|
148
|
+
onKeyDown={onSelect ? clickOnActivationKey : undefined}
|
|
147
149
|
key={tpl.id}
|
|
148
150
|
className={`${styles.card} ${selectedId === tpl.id ? styles.cardSelected : ''}`}
|
|
149
151
|
onClick={() => onSelect?.(tpl)}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { NiceAppShell, NiceIcon, useNiceTranslation } from '@nice2dev/ui';
|
|
1
|
+
import { clickOnActivationKey, NiceAppShell, NiceIcon, useNiceTranslation } from '@nice2dev/ui';
|
|
3
2
|
import type { NiceTranslateFn } from '@nice2dev/ui';
|
|
4
3
|
import React, {
|
|
5
4
|
useState,
|
|
@@ -331,7 +330,12 @@ const FieldElement: React.FC<FieldElementProps> = ({
|
|
|
331
330
|
};
|
|
332
331
|
|
|
333
332
|
return (
|
|
334
|
-
<div
|
|
333
|
+
<div
|
|
334
|
+
role="presentation"
|
|
335
|
+
style={baseStyle}
|
|
336
|
+
onClick={onClick}
|
|
337
|
+
onMouseDown={readOnly ? undefined : onDragStart}
|
|
338
|
+
>
|
|
335
339
|
{isLine ? (
|
|
336
340
|
<div
|
|
337
341
|
style={{
|
|
@@ -900,6 +904,9 @@ const LeftPanel: React.FC<LeftPanelProps> = ({
|
|
|
900
904
|
<div className={styles.fieldList}>
|
|
901
905
|
{(filteredFields ?? []).map((f) => (
|
|
902
906
|
<div
|
|
907
|
+
role="button"
|
|
908
|
+
tabIndex={0}
|
|
909
|
+
onKeyDown={clickOnActivationKey}
|
|
903
910
|
key={f.path}
|
|
904
911
|
className={styles.fieldItem}
|
|
905
912
|
draggable
|
|
@@ -954,6 +961,9 @@ const LeftPanel: React.FC<LeftPanelProps> = ({
|
|
|
954
961
|
.reverse()
|
|
955
962
|
.map((f) => (
|
|
956
963
|
<div
|
|
964
|
+
role="button"
|
|
965
|
+
tabIndex={0}
|
|
966
|
+
onKeyDown={clickOnActivationKey}
|
|
957
967
|
key={f.id}
|
|
958
968
|
className={`${styles.layerItem} ${selectedIds.has(f.id) ? styles.layerItemSelected : ''}`}
|
|
959
969
|
onClick={() => onSelectField(f.id)}
|
|
@@ -2459,7 +2469,11 @@ export const NiceTemplateEditor: React.FC<NiceTemplateEditorProps> = ({
|
|
|
2459
2469
|
}
|
|
2460
2470
|
>
|
|
2461
2471
|
{/* Canvas area */}
|
|
2462
|
-
<div
|
|
2472
|
+
<div
|
|
2473
|
+
role="presentation"
|
|
2474
|
+
className={styles.canvasArea}
|
|
2475
|
+
onClick={() => setSelectedIds(new Set())}
|
|
2476
|
+
>
|
|
2463
2477
|
{/* Test data bar */}
|
|
2464
2478
|
{Array.isArray(sampleData) && sampleData.length > 0 && (
|
|
2465
2479
|
<div className={styles.testDataBar}>
|
|
@@ -2490,7 +2504,11 @@ export const NiceTemplateEditor: React.FC<NiceTemplateEditorProps> = ({
|
|
|
2490
2504
|
)}
|
|
2491
2505
|
|
|
2492
2506
|
{/* Scrollable paper area */}
|
|
2493
|
-
<div
|
|
2507
|
+
<div
|
|
2508
|
+
role="presentation"
|
|
2509
|
+
className={styles.paperScroll}
|
|
2510
|
+
onClick={(e) => e.stopPropagation()}
|
|
2511
|
+
>
|
|
2494
2512
|
<div className={styles.paperShadow} style={{ width: paperWPx, minHeight: paperHPx }}>
|
|
2495
2513
|
{/* Margin guides */}
|
|
2496
2514
|
<div
|