1+ import { StructurePreviewProps } from "@mendix/widget-plugin-platform/preview/structure-preview-api" ;
12import { hidePropertiesIn , hidePropertyIn , Properties } from "@mendix/pluggable-widgets-tools" ;
2- import { BarcodeGeneratorPreviewProps } from "../typings/BarcodeGeneratorProps" ;
3+ import { BarcodeGeneratorPreviewProps , CodeFormatEnum , CustomCodeFormatEnum } from "../typings/BarcodeGeneratorProps" ;
34import { validateAddonValue , validateBarcodeValue } from "./config/validation" ;
45
56export type Problem = {
@@ -15,49 +16,64 @@ export function getProperties(values: BarcodeGeneratorPreviewProps, defaultPrope
1516 if ( values . codeFormat === "QRCode" ) {
1617 hidePropertiesIn ( defaultProperties , values , [ "codeWidth" , "codeHeight" , "displayValue" , "codeMargin" ] ) ;
1718 } else {
18- hidePropertiesIn ( defaultProperties , values , [ "qrImage" , "qrSize" , "qrMargin" , "qrLevel" , "qrTitle" ] ) ;
19+ hidePropertiesIn ( defaultProperties , values , [
20+ "qrOverlay" ,
21+ "qrSize" ,
22+ "qrMargin" ,
23+ "qrLevel" ,
24+ "qrTitle" ,
25+ "showTitle"
26+ ] ) ;
1927 }
2028
21- if ( values . codeFormat !== "QRCode" || ! values . qrImage ) {
29+ if ( values . codeFormat !== "QRCode" || ! values . qrOverlay ) {
2230 hidePropertiesIn ( defaultProperties , values , [
23- "qrImageSrc " ,
24- "qrImageCenter " ,
25- "qrImageWidth " ,
26- "qrImageHeight " ,
27- "qrImageX " ,
28- "qrImageY " ,
29- "qrImageOpacity " ,
30- "qrImageExcavate "
31+ "qrOverlaySrc " ,
32+ "qrOverlayCenter " ,
33+ "qrOverlayWidth " ,
34+ "qrOverlayHeight " ,
35+ "qrOverlayX " ,
36+ "qrOverlayY " ,
37+ "qrOverlayOpacity " ,
38+ "qrOverlayExcavate "
3139 ] ) ;
3240 }
3341
34- if ( values . codeFormat !== "CODE128" && values . customCodeFormat !== "CODE128" ) {
42+ if ( values . codeFormat === "QRCode" || ( values . codeFormat !== "CODE128" && values . customCodeFormat !== "CODE128" ) ) {
3543 hidePropertyIn ( defaultProperties , values , "enableEan128" ) ;
3644 }
3745
46+ // enableFlat is only supported for EAN-13 and EAN-8, and NOT when addons are enabled
3847 if (
39- values . codeFormat === "QRCode" ||
40- values . codeFormat === "CODE128" ||
41- ( values . codeFormat === "Custom" &&
42- values . customCodeFormat !== "EAN13" &&
43- values . customCodeFormat !== "EAN8" &&
44- values . customCodeFormat !== "UPC" )
48+ ! (
49+ values . codeFormat === "Custom" &&
50+ ( values . customCodeFormat === "EAN13" || values . customCodeFormat === "EAN8" ) &&
51+ values . addonFormat === "None"
52+ )
4553 ) {
4654 hidePropertyIn ( defaultProperties , values , "enableFlat" ) ;
4755 }
4856
57+ // lastChar is only supported for EAN-13, and NOT when flat is enabled or addons are present
4958 if (
50- values . codeFormat === "QRCode" ||
51- values . codeFormat === "CODE128" ||
52- ( values . codeFormat === "Custom" && values . customCodeFormat !== "EAN13" )
59+ ! (
60+ values . codeFormat === "Custom" &&
61+ values . customCodeFormat === "EAN13" &&
62+ ! values . enableFlat &&
63+ values . addonFormat === "None"
64+ )
5365 ) {
5466 hidePropertyIn ( defaultProperties , values , "lastChar" ) ;
5567 }
5668
69+ // EAN addons are only supported for EAN-13, EAN-8, and UPC
5770 if (
5871 values . codeFormat === "QRCode" ||
5972 values . codeFormat === "CODE128" ||
60- ( values . codeFormat === "Custom" && values . customCodeFormat !== "EAN13" && values . customCodeFormat !== "EAN8" )
73+ ( values . codeFormat === "Custom" &&
74+ values . customCodeFormat !== "EAN13" &&
75+ values . customCodeFormat !== "EAN8" &&
76+ values . customCodeFormat !== "UPC" )
6177 ) {
6278 hidePropertiesIn ( defaultProperties , values , [ "addonFormat" , "addonValue" , "addonSpacing" ] ) ;
6379 }
@@ -77,17 +93,32 @@ export function getProperties(values: BarcodeGeneratorPreviewProps, defaultPrope
7793 hidePropertyIn ( defaultProperties , values , "enableMod43" ) ;
7894 }
7995
80- if ( values . qrImageCenter ) {
81- hidePropertiesIn ( defaultProperties , values , [ "qrImageX " , "qrImageY " ] ) ;
96+ if ( values . qrOverlayCenter ) {
97+ hidePropertiesIn ( defaultProperties , values , [ "qrOverlayX " , "qrOverlayY " ] ) ;
8298 }
8399
84100 if ( values . codeFormat !== "Custom" ) {
85101 hidePropertiesIn ( defaultProperties , values , [ "customCodeFormat" ] ) ;
86102 }
87103
104+ if ( ! values . allowDownload ) {
105+ hidePropertiesIn ( defaultProperties , values , [
106+ "downloadButtonCaption" ,
107+ "downloadButtonAriaLabel" ,
108+ "downloadFileName" ,
109+ "buttonPosition"
110+ ] ) ;
111+ }
112+
88113 return defaultProperties ;
89114}
90115
116+ export function getPreview ( _ : StructurePreviewProps , _isDarkMode : boolean ) : StructurePreviewProps | null {
117+ // Return null to use the widget icon (BarcodeGenerator.icon.png or BarcodeGenerator.icon.dark.png)
118+ // based on the user's theme settings
119+ return null ;
120+ }
121+
91122export function check ( _values : BarcodeGeneratorPreviewProps ) : Problem [ ] {
92123 const errors : Problem [ ] = [ ] ;
93124
@@ -120,36 +151,103 @@ export function check(_values: BarcodeGeneratorPreviewProps): Problem[] {
120151 return errors . concat ( valueProblems ) ;
121152}
122153
123- function getActiveFormat ( values : BarcodeGeneratorPreviewProps ) : string {
154+ function getActiveFormat ( values : BarcodeGeneratorPreviewProps ) : CodeFormatEnum | CustomCodeFormatEnum {
124155 if ( values . codeFormat === "Custom" ) {
125156 return values . customCodeFormat || "CODE128" ;
126157 }
127158
128159 return values . codeFormat ;
129160}
130161
162+ function stripQuotes ( value : string ) : string {
163+ // Remove leading/trailing quotes and whitespace from expression values
164+ let trimmed = value . trim ( ) ;
165+ // Match and remove surrounding quotes (single or double)
166+ if ( ( trimmed . startsWith ( "'" ) && trimmed . endsWith ( "'" ) ) || ( trimmed . startsWith ( '"' ) && trimmed . endsWith ( '"' ) ) ) {
167+ trimmed = trimmed . slice ( 1 , - 1 ) ;
168+ }
169+ return trimmed ;
170+ }
171+
172+ function isDynamicExpression ( value : string ) : boolean {
173+ // Check if the value is a dynamic expression (attribute binding, variable, etc.)
174+ // Dynamic expressions start with $ or contain / paths or are empty
175+ return ! value || value . startsWith ( "$" ) || value . includes ( "/" ) ;
176+ }
177+
178+ function getFormatHint ( format : string ) : string {
179+ const hints : Record < string , string > = {
180+ EAN13 : "EAN-13 requires 12 or 13 numeric digits" ,
181+ EAN8 : "EAN-8 requires 7 or 8 numeric digits" ,
182+ UPC : "UPC requires 11 or 12 numeric digits" ,
183+ ITF14 : "ITF-14 requires exactly 14 numeric digits" ,
184+ CODE39 : "CODE39: uppercase A-Z, digits, space and - . $ / + % (max 43 chars)" ,
185+ CODE128 : "CODE128: alphanumeric, no control characters (max 80 chars)" ,
186+ CODE93 : "CODE93: alphanumeric, no control characters (max 47 chars)" ,
187+ MSI : "MSI: numeric only (max 30 digits)" ,
188+ pharmacode : "Pharmacode: numeric only (max 7 digits)" ,
189+ codabar : "Codabar: digits, A-D start/stop, and - $ : / . + (max 20 chars)" ,
190+ QRCode : "QR Code: any text (max 1200 chars recommended)"
191+ } ;
192+ return hints [ format ] || "" ;
193+ }
194+
131195function validateCodeValues ( values : BarcodeGeneratorPreviewProps ) : Problem [ ] {
132196 const problems : Problem [ ] = [ ] ;
133- const val = values . codeValue ?? "" ;
134- const addon = values . addonValue ?? "" ;
197+ const rawVal = values . codeValue ?? "" ;
198+ const rawAddon = values . addonValue ?? "" ;
135199 const format = getActiveFormat ( values ) ;
136200
137- // Only validate static (design-time) values — if empty, skip (user may bind dynamically)
138- if ( ! val ) {
139- // still validate addon if present
140- } else {
141- const result = validateBarcodeValue ( format , val ) ;
142- if ( ! result . valid ) {
143- const msg = result . message || "Invalid barcode value for selected format." ;
144- problems . push ( { property : "codeValue" , severity : "warning" , message : msg } ) ;
201+ // Add informational hint for dynamic expressions
202+ if ( isDynamicExpression ( rawVal ) && rawVal ) {
203+ const hint = getFormatHint ( format ) ;
204+ if ( hint ) {
205+ problems . push ( {
206+ property : "codeValue" ,
207+ severity : "warning" ,
208+ message : `Dynamic value provided. Ensure runtime value matches format: ${ hint } `
209+ } ) ;
210+ }
211+ }
212+
213+ // Only validate static literal values, skip dynamic expressions (attribute bindings, variables, etc.)
214+ if ( ! isDynamicExpression ( rawVal ) ) {
215+ const val = stripQuotes ( rawVal ) ;
216+ if ( val ) {
217+ const result = validateBarcodeValue ( format , val ) ;
218+ if ( ! result . valid ) {
219+ const msg = result . message || "Invalid barcode value for selected format." ;
220+ problems . push ( { property : "codeValue" , severity : "error" , message : msg } ) ;
221+ }
145222 }
146223 }
147224
148- // Validate addon value if visible
149- const addonResult = validateAddonValue ( values . addonFormat , addon ) ;
150- if ( ! addonResult . valid ) {
151- const msg = addonResult . message || "Invalid addon value." ;
152- problems . push ( { property : "addonValue" , severity : "warning" , message : msg } ) ;
225+ // Validate addon value if visible and format is selected
226+ if ( values . addonFormat !== "None" ) {
227+ // Add informational hint for dynamic addon expressions
228+ if ( isDynamicExpression ( rawAddon ) && rawAddon ) {
229+ const addonHint =
230+ values . addonFormat === "EAN5"
231+ ? "EAN-5 addon requires exactly 5 numeric digits"
232+ : "EAN-2 addon requires exactly 2 numeric digits" ;
233+ problems . push ( {
234+ property : "addonValue" ,
235+ severity : "warning" ,
236+ message : `Dynamic addon value provided. Ensure runtime value matches format: ${ addonHint } `
237+ } ) ;
238+ }
239+
240+ // Validate static addon values
241+ if ( ! isDynamicExpression ( rawAddon ) ) {
242+ const addon = stripQuotes ( rawAddon ) ;
243+ if ( addon ) {
244+ const addonResult = validateAddonValue ( values . addonFormat , addon ) ;
245+ if ( ! addonResult . valid ) {
246+ const msg = addonResult . message || "Invalid addon value." ;
247+ problems . push ( { property : "addonValue" , severity : "error" , message : msg } ) ;
248+ }
249+ }
250+ }
153251 }
154252
155253 return problems ;
0 commit comments