From 342214aafd52c78bca9914a57119946ec9594cc8 Mon Sep 17 00:00:00 2001 From: u8array Date: Sat, 9 May 2026 09:02:58 +0200 Subject: [PATCH 01/11] i18n(app): add collapse, expand, undo, redo translations Prep for upcoming a11y pass: icon-only buttons currently use hardcoded English title strings (Expand/Collapse/Undo/Redo). Localizing them gives us a proper aria-label source in every locale. --- src/locales/ar.ts | 4 ++++ src/locales/bg.ts | 4 ++++ src/locales/cs.ts | 4 ++++ src/locales/da.ts | 4 ++++ src/locales/de.ts | 4 ++++ src/locales/el.ts | 4 ++++ src/locales/en.ts | 4 ++++ src/locales/es.ts | 4 ++++ src/locales/et.ts | 4 ++++ src/locales/fa.ts | 4 ++++ src/locales/fi.ts | 4 ++++ src/locales/fr.ts | 4 ++++ src/locales/he.ts | 4 ++++ src/locales/hr.ts | 4 ++++ src/locales/hu.ts | 4 ++++ src/locales/it.ts | 4 ++++ src/locales/ja.ts | 4 ++++ src/locales/ko.ts | 4 ++++ src/locales/lt.ts | 4 ++++ src/locales/lv.ts | 4 ++++ src/locales/nl.ts | 4 ++++ src/locales/no.ts | 4 ++++ src/locales/pl.ts | 4 ++++ src/locales/pt.ts | 4 ++++ src/locales/ro.ts | 4 ++++ src/locales/sk.ts | 4 ++++ src/locales/sl.ts | 4 ++++ src/locales/sr.ts | 4 ++++ src/locales/sv.ts | 4 ++++ src/locales/tr.ts | 4 ++++ src/locales/zh-hans.ts | 4 ++++ src/locales/zh-hant.ts | 4 ++++ 32 files changed, 128 insertions(+) diff --git a/src/locales/ar.ts b/src/locales/ar.ts index f153b91c..3dfe5444 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -98,6 +98,10 @@ const ar = { addPage: 'إضافة صفحة', cancel: 'إلغاء', close: 'إغلاق', + collapse: 'طي', + expand: 'توسيع', + undo: 'تراجع', + redo: 'إعادة', deletePage: 'حذف الصفحة', deletePageConfirm: 'حذف الصفحة الحالية؟', openDesign: 'فتح تصميم', diff --git a/src/locales/bg.ts b/src/locales/bg.ts index f6ce769b..b62e1b7f 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -98,6 +98,10 @@ const bg = { addPage: 'Добавяне на страница', cancel: 'Отказ', close: 'Затваряне', + collapse: 'Свиване', + expand: 'Разширяване', + undo: 'Отмяна', + redo: 'Възстановяване', deletePage: 'Изтриване на страница', deletePageConfirm: 'Изтриване на текущата страница?', openDesign: 'Отвори дизайн', diff --git a/src/locales/cs.ts b/src/locales/cs.ts index 6b35536d..092db1d2 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -98,6 +98,10 @@ const cs = { addPage: 'Přidat stránku', cancel: 'Zrušit', close: 'Zavřít', + collapse: 'Sbalit', + expand: 'Rozbalit', + undo: 'Zpět', + redo: 'Znovu', deletePage: 'Smazat stránku', deletePageConfirm: 'Smazat aktuální stránku?', openDesign: 'Otevřít návrh', diff --git a/src/locales/da.ts b/src/locales/da.ts index db476eac..464e23c8 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -98,6 +98,10 @@ const da = { addPage: 'Tilføj side', cancel: 'Annuller', close: 'Luk', + collapse: 'Skjul', + expand: 'Udvid', + undo: 'Fortryd', + redo: 'Annuller fortryd', deletePage: 'Slet side', deletePageConfirm: 'Slet den aktuelle side?', openDesign: 'Åbn design', diff --git a/src/locales/de.ts b/src/locales/de.ts index 384a40d7..898f688d 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -98,6 +98,10 @@ const de = { addPage: 'Seite hinzufügen', cancel: 'Abbrechen', close: 'Schließen', + collapse: 'Einklappen', + expand: 'Ausklappen', + undo: 'Rückgängig', + redo: 'Wiederholen', deletePage: 'Seite löschen', deletePageConfirm: 'Aktuelle Seite löschen?', openDesign: 'Design öffnen', diff --git a/src/locales/el.ts b/src/locales/el.ts index fa979608..c3309636 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -98,6 +98,10 @@ const el = { addPage: 'Προσθήκη σελίδας', cancel: 'Ακύρωση', close: 'Κλείσιμο', + collapse: 'Σύμπτυξη', + expand: 'Ανάπτυξη', + undo: 'Αναίρεση', + redo: 'Επανάληψη', deletePage: 'Διαγραφή σελίδας', deletePageConfirm: 'Διαγραφή της τρέχουσας σελίδας;', openDesign: 'Άνοιγμα σχεδίου', diff --git a/src/locales/en.ts b/src/locales/en.ts index e671b5aa..0df38fe6 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -98,6 +98,10 @@ const en = { addPage: 'Add page', cancel: 'Cancel', close: 'Close', + collapse: 'Collapse', + expand: 'Expand', + undo: 'Undo', + redo: 'Redo', deletePage: 'Delete page', deletePageConfirm: 'Delete the current page?', openDesign: 'Open design', diff --git a/src/locales/es.ts b/src/locales/es.ts index 76cfe60b..9c10d79e 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -98,6 +98,10 @@ const es = { addPage: 'Añadir página', cancel: 'Cancelar', close: 'Cerrar', + collapse: 'Contraer', + expand: 'Expandir', + undo: 'Deshacer', + redo: 'Rehacer', deletePage: 'Eliminar página', deletePageConfirm: '¿Eliminar la página actual?', openDesign: 'Abrir diseño', diff --git a/src/locales/et.ts b/src/locales/et.ts index c8eef9a0..149c8ce1 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -98,6 +98,10 @@ const et = { addPage: 'Lisa leht', cancel: 'Loobu', close: 'Sulge', + collapse: 'Ahenda', + expand: 'Laienda', + undo: 'Võta tagasi', + redo: 'Tee uuesti', deletePage: 'Kustuta leht', deletePageConfirm: 'Kustuta praegune leht?', openDesign: 'Ava kujundus', diff --git a/src/locales/fa.ts b/src/locales/fa.ts index c53db8f0..24da4e62 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -98,6 +98,10 @@ const fa = { addPage: 'افزودن صفحه', cancel: 'لغو', close: 'بستن', + collapse: 'جمع کردن', + expand: 'گسترش', + undo: 'واگرد', + redo: 'تکرار', deletePage: 'حذف صفحه', deletePageConfirm: 'صفحه فعلی حذف شود؟', openDesign: 'باز کردن طرح', diff --git a/src/locales/fi.ts b/src/locales/fi.ts index 6091e60c..22397a38 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -98,6 +98,10 @@ const fi = { addPage: 'Lisää sivu', cancel: 'Peruuta', close: 'Sulje', + collapse: 'Tiivistä', + expand: 'Laajenna', + undo: 'Kumoa', + redo: 'Tee uudelleen', deletePage: 'Poista sivu', deletePageConfirm: 'Poistetaanko nykyinen sivu?', openDesign: 'Avaa rakenne', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index ae2a9eea..963dc6a4 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -98,6 +98,10 @@ const fr = { addPage: 'Ajouter une page', cancel: 'Annuler', close: 'Fermer', + collapse: 'Réduire', + expand: 'Développer', + undo: 'Annuler', + redo: 'Rétablir', deletePage: 'Supprimer la page', deletePageConfirm: 'Supprimer la page actuelle ?', openDesign: 'Ouvrir le design', diff --git a/src/locales/he.ts b/src/locales/he.ts index f9813428..f3121558 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -98,6 +98,10 @@ const he = { addPage: 'הוסף דף', cancel: 'ביטול', close: 'סגור', + collapse: 'צמצם', + expand: 'הרחב', + undo: 'בטל', + redo: 'בצע שוב', deletePage: 'מחק דף', deletePageConfirm: 'למחוק את הדף הנוכחי?', openDesign: 'פתח עיצוב', diff --git a/src/locales/hr.ts b/src/locales/hr.ts index 58455f4e..f0da5012 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -98,6 +98,10 @@ const hr = { addPage: 'Dodaj stranicu', cancel: 'Odustani', close: 'Zatvori', + collapse: 'Sažmi', + expand: 'Proširi', + undo: 'Poništi', + redo: 'Ponovi', deletePage: 'Izbriši stranicu', deletePageConfirm: 'Izbrisati trenutnu stranicu?', openDesign: 'Otvori dizajn', diff --git a/src/locales/hu.ts b/src/locales/hu.ts index 5ee4a238..765d3ec3 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -98,6 +98,10 @@ const hu = { addPage: 'Oldal hozzáadása', cancel: 'Mégse', close: 'Bezárás', + collapse: 'Összecsukás', + expand: 'Kibontás', + undo: 'Visszavonás', + redo: 'Ismét', deletePage: 'Oldal törlése', deletePageConfirm: 'Törli az aktuális oldalt?', openDesign: 'Terv megnyitása', diff --git a/src/locales/it.ts b/src/locales/it.ts index 73d3f84f..56da9af5 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -98,6 +98,10 @@ const it = { addPage: 'Aggiungi pagina', cancel: 'Annulla', close: 'Chiudi', + collapse: 'Riduci', + expand: 'Espandi', + undo: 'Annulla', + redo: 'Ripeti', deletePage: 'Elimina pagina', deletePageConfirm: 'Eliminare la pagina corrente?', openDesign: 'Apri design', diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 8efbdd96..3975910f 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -98,6 +98,10 @@ const ja = { addPage: 'ページを追加', cancel: 'キャンセル', close: '閉じる', + collapse: '折りたたむ', + expand: '展開', + undo: '元に戻す', + redo: 'やり直す', deletePage: 'ページを削除', deletePageConfirm: '現在のページを削除しますか?', openDesign: 'デザインを開く', diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 7c41997c..2a7126a7 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -98,6 +98,10 @@ const ko = { addPage: '페이지 추가', cancel: '취소', close: '닫기', + collapse: '접기', + expand: '펼치기', + undo: '실행 취소', + redo: '다시 실행', deletePage: '페이지 삭제', deletePageConfirm: '현재 페이지를 삭제하시겠습니까?', openDesign: '디자인 열기', diff --git a/src/locales/lt.ts b/src/locales/lt.ts index 5abd6b35..5c1bcddc 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -98,6 +98,10 @@ const lt = { addPage: 'Pridėti puslapį', cancel: 'Atšaukti', close: 'Uždaryti', + collapse: 'Sutraukti', + expand: 'Išskleisti', + undo: 'Atšaukti', + redo: 'Pakartoti', deletePage: 'Ištrinti puslapį', deletePageConfirm: 'Ištrinti dabartinį puslapį?', openDesign: 'Atidaryti dizainą', diff --git a/src/locales/lv.ts b/src/locales/lv.ts index 3c44611b..bdab2c57 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -98,6 +98,10 @@ const lv = { addPage: 'Pievienot lapu', cancel: 'Atcelt', close: 'Aizvērt', + collapse: 'Sakļaut', + expand: 'Izvērst', + undo: 'Atsaukt', + redo: 'Atkārtot', deletePage: 'Dzēst lapu', deletePageConfirm: 'Dzēst pašreizējo lapu?', openDesign: 'Atvērt dizainu', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 1caf6593..cb17cf4e 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -98,6 +98,10 @@ const nl = { addPage: 'Pagina toevoegen', cancel: 'Annuleren', close: 'Sluiten', + collapse: 'Samenvouwen', + expand: 'Uitvouwen', + undo: 'Ongedaan maken', + redo: 'Opnieuw', deletePage: 'Pagina verwijderen', deletePageConfirm: 'Huidige pagina verwijderen?', openDesign: 'Ontwerp openen', diff --git a/src/locales/no.ts b/src/locales/no.ts index ffbd1d33..f7b5bfad 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -98,6 +98,10 @@ const no = { addPage: 'Legg til side', cancel: 'Avbryt', close: 'Lukk', + collapse: 'Skjul', + expand: 'Utvid', + undo: 'Angre', + redo: 'Gjør om', deletePage: 'Slett side', deletePageConfirm: 'Slette gjeldende side?', openDesign: 'Åpne design', diff --git a/src/locales/pl.ts b/src/locales/pl.ts index c6208d89..c85f0cb2 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -98,6 +98,10 @@ const pl = { addPage: 'Dodaj stronę', cancel: 'Anuluj', close: 'Zamknij', + collapse: 'Zwiń', + expand: 'Rozwiń', + undo: 'Cofnij', + redo: 'Ponów', deletePage: 'Usuń stronę', deletePageConfirm: 'Usunąć bieżącą stronę?', openDesign: 'Otwórz projekt', diff --git a/src/locales/pt.ts b/src/locales/pt.ts index e668ac9d..546003ef 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -98,6 +98,10 @@ const pt = { addPage: 'Adicionar página', cancel: 'Cancelar', close: 'Fechar', + collapse: 'Recolher', + expand: 'Expandir', + undo: 'Desfazer', + redo: 'Refazer', deletePage: 'Eliminar página', deletePageConfirm: 'Eliminar a página atual?', openDesign: 'Abrir design', diff --git a/src/locales/ro.ts b/src/locales/ro.ts index ccc8498d..53776172 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -98,6 +98,10 @@ const ro = { addPage: 'Adaugă pagină', cancel: 'Anulează', close: 'Închide', + collapse: 'Restrânge', + expand: 'Extinde', + undo: 'Anulează', + redo: 'Refă', deletePage: 'Șterge pagina', deletePageConfirm: 'Ștergeți pagina curentă?', openDesign: 'Deschide design', diff --git a/src/locales/sk.ts b/src/locales/sk.ts index 1f703605..694d1d0a 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -98,6 +98,10 @@ const sk = { addPage: 'Pridať stránku', cancel: 'Zrušiť', close: 'Zavrieť', + collapse: 'Zbaliť', + expand: 'Rozbaliť', + undo: 'Späť', + redo: 'Znova', deletePage: 'Odstrániť stránku', deletePageConfirm: 'Odstrániť aktuálnu stránku?', openDesign: 'Otvoriť návrh', diff --git a/src/locales/sl.ts b/src/locales/sl.ts index 84f9aa9f..0ee93efd 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -98,6 +98,10 @@ const sl = { addPage: 'Dodaj stran', cancel: 'Prekliči', close: 'Zapri', + collapse: 'Strni', + expand: 'Razširi', + undo: 'Razveljavi', + redo: 'Ponovi', deletePage: 'Izbriši stran', deletePageConfirm: 'Izbrišem trenutno stran?', openDesign: 'Odpri dizajn', diff --git a/src/locales/sr.ts b/src/locales/sr.ts index 7bcfa35e..d58b73c8 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -98,6 +98,10 @@ const sr = { addPage: 'Додај страницу', cancel: 'Откажи', close: 'Затвори', + collapse: 'Скупи', + expand: 'Прошири', + undo: 'Опозови', + redo: 'Понови', deletePage: 'Обриши страницу', deletePageConfirm: 'Обрисати тренутну страницу?', openDesign: 'Отвори дизајн', diff --git a/src/locales/sv.ts b/src/locales/sv.ts index 01b1fda6..c46b9168 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -98,6 +98,10 @@ const sv = { addPage: 'Lägg till sida', cancel: 'Avbryt', close: 'Stäng', + collapse: 'Komprimera', + expand: 'Expandera', + undo: 'Ångra', + redo: 'Gör om', deletePage: 'Ta bort sida', deletePageConfirm: 'Ta bort aktuell sida?', openDesign: 'Öppna design', diff --git a/src/locales/tr.ts b/src/locales/tr.ts index e83465f6..14745e3a 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -98,6 +98,10 @@ const tr = { addPage: 'Sayfa ekle', cancel: 'İptal', close: 'Kapat', + collapse: 'Daralt', + expand: 'Genişlet', + undo: 'Geri al', + redo: 'Yinele', deletePage: 'Sayfayı sil', deletePageConfirm: 'Geçerli sayfa silinsin mi?', openDesign: 'Tasarım Aç', diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index 20a6eb70..9a62fc9a 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -98,6 +98,10 @@ const zhHans = { addPage: '添加页面', cancel: '取消', close: '关闭', + collapse: '折叠', + expand: '展开', + undo: '撤销', + redo: '重做', deletePage: '删除页面', deletePageConfirm: '删除当前页面?', openDesign: '打开设计', diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index 0f504d17..7d15b9e9 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -98,6 +98,10 @@ const zhHant = { addPage: '新增頁面', cancel: '取消', close: '關閉', + collapse: '收合', + expand: '展開', + undo: '復原', + redo: '重做', deletePage: '刪除頁面', deletePageConfirm: '刪除目前頁面?', openDesign: '開啟設計', From d5d1ebe03abf535bca1e4967169264024a842f72 Mon Sep 17 00:00:00 2001 From: u8array Date: Sat, 9 May 2026 09:04:26 +0200 Subject: [PATCH 02/11] a11y(modals): add ARIA dialog roles to ZplImport, PrintToZebra, ImportReport Each modal container now exposes role=dialog, aria-modal and aria-labelledby pointing at its title. Close buttons get aria-label via t.app.close so screen readers announce the icon-only control. Hardcoded English titles (Import ZPL, Import Report) are kept as-is for now; they're a separate i18n concern. --- src/components/Output/ImportReportModal.tsx | 8 +++++++- src/components/Output/PrintToZebraDialog.tsx | 6 +++++- src/components/Output/ZplImportModal.tsx | 8 +++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Output/ImportReportModal.tsx b/src/components/Output/ImportReportModal.tsx index bbaa19d3..aad3fde6 100644 --- a/src/components/Output/ImportReportModal.tsx +++ b/src/components/Output/ImportReportModal.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { XMarkIcon, ClipboardDocumentIcon, CheckIcon } from '@heroicons/react/16/solid'; import { partialLoss, formatReportAsText } from '../../lib/importReport'; import type { ImportResult } from '../../lib/importReport'; +import { useT } from '../../lib/useT'; export type { ImportResult }; @@ -55,6 +56,7 @@ interface Props { } export function ImportReportModal({ result, onClose }: Props) { + const t = useT(); const [copied, setCopied] = useState(false); const handleCopy = () => { @@ -69,12 +71,16 @@ export function ImportReportModal({ result, onClose }: Props) { className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.6)' }} onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }} + role="dialog" + aria-modal="true" + aria-labelledby="import-report-title" >
- Import Report + Import Report diff --git a/src/components/Output/ZPLOutput.tsx b/src/components/Output/ZPLOutput.tsx index 96df205d..13770d2e 100644 --- a/src/components/Output/ZPLOutput.tsx +++ b/src/components/Output/ZPLOutput.tsx @@ -40,7 +40,8 @@ export function ZPLOutput({ collapsed, onCollapse, onExpand }: Props) { From ec5a0b4496e34fea2ed29a1f03af777062becd96 Mon Sep 17 00:00:00 2001 From: u8array Date: Sat, 9 May 2026 09:07:06 +0200 Subject: [PATCH 04/11] fix(ui): platform-aware shortcut hints in undo/redo titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously hardcoded as ⌘Z / ⌘⇧Z which only matches Mac. The new kbd() helper renders ⌘Z on Mac and Ctrl+Z elsewhere, mirroring what useGlobalShortcuts already accepts (metaKey OR ctrlKey). --- src/components/AppShell.tsx | 5 +++-- src/lib/kbd.test.ts | 15 +++++++++++++++ src/lib/kbd.ts | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/lib/kbd.test.ts create mode 100644 src/lib/kbd.ts diff --git a/src/components/AppShell.tsx b/src/components/AppShell.tsx index b1ab15c8..b53b0091 100644 --- a/src/components/AppShell.tsx +++ b/src/components/AppShell.tsx @@ -36,6 +36,7 @@ import { localeNames } from "../locales"; import type { LocaleCode } from "../locales"; import { mmToUnit } from "../lib/units"; import { useT } from "../lib/useT"; +import { kbd } from "../lib/kbd"; import { useGlobalShortcuts } from "../hooks/useGlobalShortcuts"; import { useDesignFileActions } from "../hooks/useDesignFileActions"; import { useZplImportExport } from "../hooks/useZplImportExport"; @@ -114,7 +115,7 @@ export function AppShell() { -
+
+ Import Report + +
- + -
- - -
+
+ +
-
+ ); } diff --git a/src/components/Output/LabelPreview.tsx b/src/components/Output/LabelPreview.tsx index 45de1d2b..1a78d2c2 100644 --- a/src/components/Output/LabelPreview.tsx +++ b/src/components/Output/LabelPreview.tsx @@ -5,7 +5,7 @@ import { generateZPL } from '../../lib/zplGenerator'; import { fetchPreview, labelaryErrorMessage } from '../../lib/labelary'; import { triggerDownload } from '../../lib/triggerDownload'; import { useT } from '../../lib/useT'; -import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { DialogShell } from '../ui/DialogShell'; interface Props { onClose: () => void; @@ -24,11 +24,8 @@ export function LabelPreviewModal({ onClose }: Props) { const [error, setError] = useState(null); const urlRef = useRef(null); const zplRef = useRef(generateZPL(label, objects)); - const containerRef = useRef(null); const loading = !previewUrl && !error; - useFocusTrap(containerRef, onClose); - useEffect(() => { let cancelled = false; fetchPreview(zplRef.current, label) @@ -55,73 +52,65 @@ export function LabelPreviewModal({ onClose }: Props) { }; return ( -
-
e.stopPropagation()} - > -
- - {t.output.previewHeading} - - -
+
+ + {t.output.previewHeading} + + +
- {/* Inset preview area: bg-bg gives a clear edge against the surrounding - surface (especially in light mode where the label image is white). - The outer div scrolls; the inner one stays at least as large as the - viewport so small previews are still centered. */} -
-
- {loading && ( - {t.output.loading} - )} - {!loading && error && ( -
- {error} - -
- )} - {!loading && !error && previewUrl && ( - Label preview - )} -
+ {/* Inset preview area: bg-bg gives a clear edge against the surrounding + surface (especially in light mode where the label image is white). + The outer div scrolls; the inner one stays at least as large as the + viewport so small previews are still centered. */} +
+
+ {loading && ( + {t.output.loading} + )} + {!loading && error && ( +
+ {error} + +
+ )} + {!loading && !error && previewUrl && ( + Label preview + )}
+
- + -
+ ); } diff --git a/src/components/Output/LabelaryNoticeModal.tsx b/src/components/Output/LabelaryNoticeModal.tsx index 9f0c2287..0c109030 100644 --- a/src/components/Output/LabelaryNoticeModal.tsx +++ b/src/components/Output/LabelaryNoticeModal.tsx @@ -1,8 +1,7 @@ -import { useRef } from 'react'; import { XMarkIcon } from '@heroicons/react/16/solid'; import { useLabelStore } from '../../store/labelStore'; import { useT } from '../../lib/useT'; -import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { DialogShell } from '../ui/DialogShell'; interface Props { /** Called after the modal flipped the store flag, so callers only need @@ -20,8 +19,6 @@ interface Props { export function LabelaryNoticeModal({ onContinue, onClose }: Props) { const t = useT(); const acknowledgeLabelaryNotice = useLabelStore((s) => s.acknowledgeLabelaryNotice); - const containerRef = useRef(null); - useFocusTrap(containerRef, onClose); const handleContinue = () => { acknowledgeLabelaryNotice(); @@ -29,49 +26,41 @@ export function LabelaryNoticeModal({ onContinue, onClose }: Props) { }; return ( -
-
e.stopPropagation()} - > -
- - {t.output.previewNoticeTitle} - - -
+
+ + {t.output.previewNoticeTitle} + + +
-
- {t.output.previewNoticeBody} - - {t.output.previewNoticePrivacyLink} - - -
+
+ {t.output.previewNoticeBody} + + {t.output.previewNoticePrivacyLink} + +
-
+ ); } diff --git a/src/components/Output/PrintToZebraDialog.tsx b/src/components/Output/PrintToZebraDialog.tsx index fa7541a2..f08489d1 100644 --- a/src/components/Output/PrintToZebraDialog.tsx +++ b/src/components/Output/PrintToZebraDialog.tsx @@ -1,7 +1,7 @@ -import { useRef, useState } from "react"; +import { useState } from "react"; import { XMarkIcon } from "@heroicons/react/16/solid"; import { useT } from "../../lib/useT"; -import { useFocusTrap } from "../../hooks/useFocusTrap"; +import { DialogShell } from "../ui/DialogShell"; import { discoverBrowserPrintDevices, isConnectionRefused, @@ -33,8 +33,6 @@ interface Props { export function PrintToZebraDialog({ zpl, onClose }: Props) { const t = useT(); - const containerRef = useRef(null); - useFocusTrap(containerRef, onClose); const [tab, setTab] = useState("network"); // Network tab state @@ -110,142 +108,134 @@ export function PrintToZebraDialog({ zpl, onClose }: Props) { }`; return ( -
-
e.stopPropagation()} - > - {/* Header */} -
- - {t.zebraPrint.heading} - - -
+ {/* Header */} +
+ + {t.zebraPrint.heading} + + +
+ + {/* Tabs */} +
+ + +
+ + {/* Network tab */} + {tab === "network" && ( +
+ {window.location.protocol === "https:" && ( +

+ {t.zebraPrint.httpsWarning} +

+ )} +
+
+ + setIp(e.target.value)} + onBlur={persistNetwork} + placeholder="192.168.1.100" + className="bg-bg border border-border rounded px-2 py-1 text-xs font-mono text-text focus:outline-none focus:border-accent" + /> +
+
+ + setPort(e.target.value)} + onBlur={persistNetwork} + className="bg-bg border border-border rounded px-2 py-1 text-xs font-mono text-text focus:outline-none focus:border-accent" + /> +
+
- {/* Tabs */} -
- -
- {/* Network tab */} - {tab === "network" && ( -
- {window.location.protocol === "https:" && ( -

- {t.zebraPrint.httpsWarning} -

- )} -
-
- - setIp(e.target.value)} - onBlur={persistNetwork} - placeholder="192.168.1.100" - className="bg-bg border border-border rounded px-2 py-1 text-xs font-mono text-text focus:outline-none focus:border-accent" - /> -
-
- - setPort(e.target.value)} - onBlur={persistNetwork} - className="bg-bg border border-border rounded px-2 py-1 text-xs font-mono text-text focus:outline-none focus:border-accent" - /> -
+ +
+ )} + + {/* Browser Print tab */} + {tab === "browserprint" && ( +
+
+
+ +
- - -
- )} - - {/* Browser Print tab */} - {tab === "browserprint" && ( -
-
-
- - -
- -
- + - -
- )} -
-
+ +
+ )} + ); } diff --git a/src/components/Output/ZplImportModal.tsx b/src/components/Output/ZplImportModal.tsx index 2e8c8f76..9d52f1d0 100644 --- a/src/components/Output/ZplImportModal.tsx +++ b/src/components/Output/ZplImportModal.tsx @@ -6,7 +6,7 @@ import { useLabelStore } from '../../store/labelStore'; import { formatReportAsText, type ImportResult } from '../../lib/importReport'; import { ImportSummaryBody } from './ImportReportModal'; import { useT } from '../../lib/useT'; -import { useFocusTrap } from '../../hooks/useFocusTrap'; +import { DialogShell } from '../ui/DialogShell'; interface Props { onClose: () => void; @@ -14,8 +14,6 @@ interface Props { export function ZplImportModal({ onClose }: Props) { const t = useT(); - const containerRef = useRef(null); - useFocusTrap(containerRef, onClose); const [zpl, setZpl] = useState(''); const [error, setError] = useState(null); const [result, setResult] = useState(null); @@ -77,104 +75,98 @@ export function ZplImportModal({ onClose }: Props) { }; return ( -
{ if (e.target === e.currentTarget) onClose(); }} - role="dialog" - aria-modal="true" - aria-labelledby="zpl-import-title" + -
-
- Import ZPL - -
+
+ Import ZPL + +
- {result ? ( - <> - -
- + {result ? ( + <> + +
+ + +
+ + ) : ( + <> +
+

+ Import produces an{' '} + editable reconstruction + , not an exact replica. Simple labels import cleanly; complex or + machine-generated ZPL may lose fidelity. Use{' '} + Save design (.json) as the + lossless source format. +

+