@@ -23,6 +23,11 @@ import { AlignButtons } from "./AlignButtons";
2323import { inputCls , labelCls } from "./styles" ;
2424import type { LabelConfig } from "../../types/ObjectType" ;
2525
26+ /** Built-in alphanumeric font IDs the Zebra firmware ships with. Used as
27+ * suggestions for ^CF — the input stays free-text so user-defined ^CW
28+ * aliases (single letters) can still be entered. */
29+ const ZPL_BUILTIN_FONT_IDS = [ '0' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' ] as const ;
30+
2631interface PropertiesPanelProps {
2732 /** Imperative handle on the canvas — used for actions that need live render
2833 * bboxes (alignment, future zoom-to-selection, etc.). Required so the
@@ -430,7 +435,9 @@ function LabelConfigPanel({
430435 < CollapsibleSection
431436 id = "label-output"
432437 title = { t . label . outputHeading }
438+ defaultOpen = { false }
433439 >
440+ < div className = "flex flex-col gap-3" >
434441 < div className = "flex flex-col gap-1" >
435442 < label className = { labelCls } > { t . label . mediaMode } </ label >
436443 < select
@@ -451,6 +458,61 @@ function LabelConfigPanel({
451458 </ select >
452459 </ div >
453460
461+ < div className = "flex flex-col gap-1" >
462+ < label className = { labelCls } >
463+ { t . label . offsetsHeading }
464+ < InformationCircleIcon
465+ className = "w-3.5 h-3.5 ml-1 inline-block align-text-bottom text-muted cursor-help"
466+ title = { t . label . offsetsHint }
467+ />
468+ </ label >
469+ < div className = "grid grid-cols-3 gap-2" >
470+ < div className = "flex flex-col gap-1" >
471+ < label className = "text-[10px] text-muted" >
472+ { t . label . labelHomeX }
473+ </ label >
474+ < input
475+ type = "number"
476+ className = { inputCls }
477+ value = { label . labelHomeX ?? "" }
478+ min = { 0 }
479+ onChange = { ( e ) =>
480+ onUpdate ( { labelHomeX : parseIntOrUndef ( e . target . value ) } )
481+ }
482+ />
483+ </ div >
484+ < div className = "flex flex-col gap-1" >
485+ < label className = "text-[10px] text-muted" >
486+ { t . label . labelHomeY }
487+ </ label >
488+ < input
489+ type = "number"
490+ className = { inputCls }
491+ value = { label . labelHomeY ?? "" }
492+ min = { 0 }
493+ onChange = { ( e ) =>
494+ onUpdate ( { labelHomeY : parseIntOrUndef ( e . target . value ) } )
495+ }
496+ />
497+ </ div >
498+ < div className = "flex flex-col gap-1" >
499+ < label className = "text-[10px] text-muted" >
500+ { t . label . labelTop }
501+ </ label >
502+ < input
503+ type = "number"
504+ className = { inputCls }
505+ value = { label . labelTop ?? "" }
506+ min = { - 120 }
507+ max = { 120 }
508+ onChange = { ( e ) =>
509+ onUpdate ( { labelTop : parseIntOrUndef ( e . target . value ) } )
510+ }
511+ />
512+ </ div >
513+ </ div >
514+ </ div >
515+
454516 < div className = "flex flex-col gap-1" >
455517 < label className = { labelCls } > { t . label . labelShift } </ label >
456518 < input
@@ -480,51 +542,165 @@ function LabelConfigPanel({
480542 }
481543 />
482544 </ div >
545+
546+ </ div >
547+ </ CollapsibleSection >
548+
549+ < CollapsibleSection
550+ id = "label-quantity-advanced"
551+ title = { t . label . quantityAdvancedHeading }
552+ defaultOpen = { false }
553+ >
554+ < div className = "flex flex-col gap-3" >
555+ < div className = "flex flex-col gap-1" >
556+ < label className = { labelCls } > { t . label . pauseCount } </ label >
557+ < input
558+ type = "number"
559+ className = { inputCls }
560+ value = { label . pauseCount ?? "" }
561+ min = { 0 }
562+ max = { 99999999 }
563+ onChange = { ( e ) =>
564+ onUpdate ( { pauseCount : parseIntOrUndef ( e . target . value ) } )
565+ }
566+ />
567+ </ div >
568+
569+ < div className = "flex flex-col gap-1" >
570+ < label className = { labelCls } > { t . label . replicates } </ label >
571+ < input
572+ type = "number"
573+ className = { inputCls }
574+ value = { label . replicates ?? "" }
575+ min = { 0 }
576+ max = { 99999999 }
577+ onChange = { ( e ) =>
578+ onUpdate ( { replicates : parseIntOrUndef ( e . target . value ) } )
579+ }
580+ />
581+ </ div >
582+
583+ < label
584+ className = "flex items-center gap-2 text-xs"
585+ title = { t . label . overridePauseCountHint }
586+ >
587+ < input
588+ type = "checkbox"
589+ checked = { label . overridePauseCount === "Y" }
590+ onChange = { ( e ) =>
591+ onUpdate ( {
592+ overridePauseCount : e . target . checked ? "Y" : undefined ,
593+ } )
594+ }
595+ />
596+ { t . label . overridePauseCount }
597+ </ label >
598+ </ div >
483599 </ CollapsibleSection >
484600
485601 < CollapsibleSection
486602 id = "label-printer-settings"
487603 title = { t . label . printerSettingsHeading }
488604 defaultOpen = { false }
489605 >
606+ < div className = "flex flex-col gap-3" >
490607 < div className = "flex flex-col gap-1" >
491608 < label className = { labelCls } >
492- { t . label . printSpeed }
609+ { t . label . speedHeading }
493610 < InformationCircleIcon
494611 className = "w-3.5 h-3.5 ml-1 inline-block align-text-bottom text-muted cursor-help"
495612 title = { t . label . printSpeedHint }
496613 />
497614 </ label >
498- < input
499- type = "number"
500- className = { inputCls }
501- value = { label . printSpeed ?? "" }
502- min = { 2 }
503- max = { 14 }
504- onChange = { ( e ) =>
505- onUpdate ( { printSpeed : parseIntOrUndef ( e . target . value ) } )
506- }
507- />
615+ < div className = "grid grid-cols-3 gap-2" >
616+ < div className = "flex flex-col gap-1" >
617+ < label className = "text-[10px] text-muted" >
618+ { t . label . speedPrint }
619+ </ label >
620+ < input
621+ type = "number"
622+ className = { inputCls }
623+ value = { label . printSpeed ?? "" }
624+ min = { 2 }
625+ max = { 14 }
626+ onChange = { ( e ) =>
627+ onUpdate ( { printSpeed : parseIntOrUndef ( e . target . value ) } )
628+ }
629+ />
630+ </ div >
631+ < div className = "flex flex-col gap-1" >
632+ < label className = "text-[10px] text-muted" >
633+ { t . label . speedSlew }
634+ </ label >
635+ < input
636+ type = "number"
637+ className = { inputCls }
638+ value = { label . slewSpeed ?? "" }
639+ min = { 2 }
640+ max = { 14 }
641+ onChange = { ( e ) =>
642+ onUpdate ( { slewSpeed : parseIntOrUndef ( e . target . value ) } )
643+ }
644+ />
645+ </ div >
646+ < div className = "flex flex-col gap-1" >
647+ < label className = "text-[10px] text-muted" >
648+ { t . label . speedBackfeed }
649+ </ label >
650+ < input
651+ type = "number"
652+ className = { inputCls }
653+ value = { label . backfeedSpeed ?? "" }
654+ min = { 2 }
655+ max = { 14 }
656+ onChange = { ( e ) =>
657+ onUpdate ( { backfeedSpeed : parseIntOrUndef ( e . target . value ) } )
658+ }
659+ />
660+ </ div >
661+ </ div >
508662 </ div >
509663
510664 < div className = "flex flex-col gap-1" >
511665 < label className = { labelCls } >
512- { t . label . darkness }
666+ { t . label . darknessHeading }
513667 < InformationCircleIcon
514668 className = "w-3.5 h-3.5 ml-1 inline-block align-text-bottom text-muted cursor-help"
515669 title = { t . label . darknessHint }
516670 />
517671 </ label >
518- < input
519- type = "number"
520- className = { inputCls }
521- value = { label . darkness ?? "" }
522- min = { - 30 }
523- max = { 30 }
524- onChange = { ( e ) =>
525- onUpdate ( { darkness : parseIntOrUndef ( e . target . value ) } )
526- }
527- />
672+ < div className = "grid grid-cols-2 gap-2" >
673+ < div className = "flex flex-col gap-1" >
674+ < label className = "text-[10px] text-muted" >
675+ { t . label . darknessPermanent }
676+ </ label >
677+ < input
678+ type = "number"
679+ className = { inputCls }
680+ value = { label . darkness ?? "" }
681+ min = { - 30 }
682+ max = { 30 }
683+ onChange = { ( e ) =>
684+ onUpdate ( { darkness : parseIntOrUndef ( e . target . value ) } )
685+ }
686+ />
687+ </ div >
688+ < div className = "flex flex-col gap-1" >
689+ < label className = "text-[10px] text-muted" >
690+ { t . label . darknessInstant }
691+ </ label >
692+ < input
693+ type = "number"
694+ className = { inputCls }
695+ value = { label . instantDarkness ?? "" }
696+ min = { 0 }
697+ max = { 30 }
698+ onChange = { ( e ) =>
699+ onUpdate ( { instantDarkness : parseIntOrUndef ( e . target . value ) } )
700+ }
701+ />
702+ </ div >
703+ </ div >
528704 </ div >
529705
530706 < div className = "flex flex-col gap-1" >
@@ -562,11 +738,21 @@ function LabelConfigPanel({
562738 < option value = "N" > { t . label . printOrientationN } </ option >
563739 < option value = "I" > { t . label . printOrientationI } </ option >
564740 </ select >
741+ < label className = "flex items-center gap-2 text-xs mt-1" >
742+ < input
743+ type = "checkbox"
744+ checked = { label . mirror === "Y" }
745+ onChange = { ( e ) =>
746+ onUpdate ( { mirror : e . target . checked ? "Y" : undefined } )
747+ }
748+ />
749+ { t . label . mirror }
750+ </ label >
565751 </ div >
566752
567753 < div className = "flex flex-col gap-1" >
568754 < label className = { labelCls } > { t . label . defaultFont } </ label >
569- < div className = "grid grid-cols-2 gap-2" >
755+ < div className = "grid grid-cols-3 gap-2" >
570756 < div className = "flex flex-col gap-1" >
571757 < label className = "text-[10px] text-muted" >
572758 { t . label . defaultFontId }
@@ -575,6 +761,7 @@ function LabelConfigPanel({
575761 type = "text"
576762 className = { inputCls }
577763 maxLength = { 2 }
764+ list = "zpl-default-font-ids"
578765 value = { label . defaultFontId ?? "" }
579766 onChange = { ( e ) =>
580767 onUpdate ( { defaultFontId : e . target . value || undefined } )
@@ -597,10 +784,32 @@ function LabelConfigPanel({
597784 }
598785 />
599786 </ div >
787+ < div className = "flex flex-col gap-1" >
788+ < label className = "text-[10px] text-muted" >
789+ { t . label . defaultFontWidth }
790+ </ label >
791+ < input
792+ type = "number"
793+ className = { inputCls }
794+ min = { 0 }
795+ value = { label . defaultFontWidth ?? "" }
796+ onChange = { ( e ) =>
797+ onUpdate ( {
798+ defaultFontWidth : parseIntOrUndef ( e . target . value ) ,
799+ } )
800+ }
801+ />
802+ </ div >
600803 </ div >
601804 </ div >
805+ </ div >
602806 </ CollapsibleSection >
603807 </ div >
808+ < datalist id = "zpl-default-font-ids" >
809+ { ZPL_BUILTIN_FONT_IDS . map ( ( id ) => (
810+ < option key = { id } value = { id } />
811+ ) ) }
812+ </ datalist >
604813 </ div >
605814 ) ;
606815}
0 commit comments