@lichens-innovation/ts-common 1.22.0 → 1.24.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/dist/pdf.cjs CHANGED
@@ -4,6 +4,8 @@ var jspdf = require('jspdf');
4
4
  var jspdfAutotable = require('jspdf-autotable');
5
5
  var dateFns = require('date-fns');
6
6
 
7
+ // src/pdf/pdf-generator.ts
8
+
7
9
  // src/utils/types.utils.ts
8
10
  var isNullish = (value) => value === null || value === void 0;
9
11
  var DEFAULT_REPORT_FILENAME = "report.pdf";
@@ -117,6 +119,12 @@ var PdfGenerator = class {
117
119
  };
118
120
  console.warn("[checkTableOverflow] table overflow", infos);
119
121
  }
122
+ computeTableMargins({ tableWidth, tableAlign }) {
123
+ const leftover = this.availableWidth - tableWidth;
124
+ const alignOffsets = { left: 0, center: leftover / 2, right: leftover };
125
+ const offset = alignOffsets[tableAlign];
126
+ return { left: this.margin + offset, right: this.margin + (leftover - offset) };
127
+ }
120
128
  addTable(addTableArgs) {
121
129
  const {
122
130
  lineWidth = 5e-3,
@@ -127,7 +135,8 @@ var PdfGenerator = class {
127
135
  body,
128
136
  startY = this._currentY,
129
137
  columnWidths,
130
- cellPadding = 0.05
138
+ cellPadding = 0.05,
139
+ tableAlign = "left"
131
140
  } = addTableArgs;
132
141
  if (body.length === 0) {
133
142
  this._currentY = startY;
@@ -142,6 +151,8 @@ var PdfGenerator = class {
142
151
  });
143
152
  const tableLineWidth = drawLineForTable ? lineWidth : 0;
144
153
  const cellLineWidth = drawLineForCells ? lineWidth : 0;
154
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
155
+ const margin = this.computeTableMargins({ tableWidth, tableAlign });
145
156
  jspdfAutotable.autoTable(this.doc, {
146
157
  startY,
147
158
  head: !isNullish(head) ? [head] : void 0,
@@ -149,7 +160,7 @@ var PdfGenerator = class {
149
160
  theme: "grid",
150
161
  tableLineWidth,
151
162
  tableLineColor: lineColor,
152
- margin: { left: this.margin, right: this.margin },
163
+ margin,
153
164
  bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },
154
165
  headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },
155
166
  columnStyles
package/dist/pdf.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":["format","jsPDF","autoTable"],"mappings":"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","file":"pdf.cjs","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from '../utils';\nimport { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable } from 'jspdf-autotable';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":["format","jsPDF","autoTable"],"mappings":"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","file":"pdf.cjs","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport interface ComputeTableMarginsArgs {\n tableWidth: number;\n tableAlign: TableCellHalign;\n}\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n /** Horizontal placement of the table within the available width (default 'left'). */\n tableAlign?: TableCellHalign;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable, type MarginPaddingInput } from 'jspdf-autotable';\nimport { isNullish, type Dimensions } from '../utils';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type ComputeTableMarginsArgs,\n type PageSize,\n type PdfOptions,\n type TableCellHalign,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n private computeTableMargins({ tableWidth, tableAlign }: ComputeTableMarginsArgs): MarginPaddingInput {\n const leftover = this.availableWidth - tableWidth;\n const alignOffsets: Record<TableCellHalign, number> = { left: 0, center: leftover / 2, right: leftover };\n const offset = alignOffsets[tableAlign];\n return { left: this.margin + offset, right: this.margin + (leftover - offset) };\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n tableAlign = 'left',\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n const tableWidth = widths.reduce((sum, width) => sum + width, 0);\n const margin: MarginPaddingInput = this.computeTableMargins({ tableWidth, tableAlign });\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin,\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
package/dist/pdf.d.cts CHANGED
@@ -59,6 +59,10 @@ type CellStyle = {
59
59
  fontStyle?: FontStyle;
60
60
  fillColor?: FillColorRgbTuple;
61
61
  };
62
+ interface ComputeTableMarginsArgs {
63
+ tableWidth: number;
64
+ tableAlign: TableCellHalign;
65
+ }
62
66
  type CellValue = string | number;
63
67
  /** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */
64
68
  interface TableCellDef {
@@ -89,6 +93,8 @@ interface AddTableArgs {
89
93
  lineColor?: number;
90
94
  /** Cell padding in doc units (inches) (default 0.05). */
91
95
  cellPadding?: number;
96
+ /** Horizontal placement of the table within the available width (default 'left'). */
97
+ tableAlign?: TableCellHalign;
92
98
  }
93
99
  interface AddImageArgs {
94
100
  dataUri: string;
@@ -121,6 +127,7 @@ declare class PdfGenerator {
121
127
  setCurrentY(y: number): void;
122
128
  addVerticalGap(delta: number): void;
123
129
  checkTableOverflow(columnWidths: number[]): void;
130
+ private computeTableMargins;
124
131
  addTable(addTableArgs: AddTableArgs): number | undefined;
125
132
  /**
126
133
  * Add an image at the given position and size. Does not update currentY.
@@ -139,4 +146,4 @@ declare class PdfGenerator {
139
146
  private drawAvailableAreaRectangle;
140
147
  }
141
148
 
142
- export { type AddImageArgs, type AddTableArgs, type CellStyle, type CellValue, DEFAULT_FOOTER_CELL_BUILDER, DEFAULT_OPTIONS, DEFAULT_REPORT_FILENAME, DEFAULT_THEME, type FillColorRgbTuple, type FontStyle, type FooterCellArgs, type FooterCellBuilder, Gaps, IMAGE_GAP, type PageOrientation, type PageSize, type PaperFormat, PdfColors, PdfGenerator, type PdfOptions, type TableCellDef, type TableCellHalign, type TableCellInput, type TableCellValign, type ThemeConfig };
149
+ export { type AddImageArgs, type AddTableArgs, type CellStyle, type CellValue, type ComputeTableMarginsArgs, DEFAULT_FOOTER_CELL_BUILDER, DEFAULT_OPTIONS, DEFAULT_REPORT_FILENAME, DEFAULT_THEME, type FillColorRgbTuple, type FontStyle, type FooterCellArgs, type FooterCellBuilder, Gaps, IMAGE_GAP, type PageOrientation, type PageSize, type PaperFormat, PdfColors, PdfGenerator, type PdfOptions, type TableCellDef, type TableCellHalign, type TableCellInput, type TableCellValign, type ThemeConfig };
package/dist/pdf.d.ts CHANGED
@@ -59,6 +59,10 @@ type CellStyle = {
59
59
  fontStyle?: FontStyle;
60
60
  fillColor?: FillColorRgbTuple;
61
61
  };
62
+ interface ComputeTableMarginsArgs {
63
+ tableWidth: number;
64
+ tableAlign: TableCellHalign;
65
+ }
62
66
  type CellValue = string | number;
63
67
  /** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */
64
68
  interface TableCellDef {
@@ -89,6 +93,8 @@ interface AddTableArgs {
89
93
  lineColor?: number;
90
94
  /** Cell padding in doc units (inches) (default 0.05). */
91
95
  cellPadding?: number;
96
+ /** Horizontal placement of the table within the available width (default 'left'). */
97
+ tableAlign?: TableCellHalign;
92
98
  }
93
99
  interface AddImageArgs {
94
100
  dataUri: string;
@@ -121,6 +127,7 @@ declare class PdfGenerator {
121
127
  setCurrentY(y: number): void;
122
128
  addVerticalGap(delta: number): void;
123
129
  checkTableOverflow(columnWidths: number[]): void;
130
+ private computeTableMargins;
124
131
  addTable(addTableArgs: AddTableArgs): number | undefined;
125
132
  /**
126
133
  * Add an image at the given position and size. Does not update currentY.
@@ -139,4 +146,4 @@ declare class PdfGenerator {
139
146
  private drawAvailableAreaRectangle;
140
147
  }
141
148
 
142
- export { type AddImageArgs, type AddTableArgs, type CellStyle, type CellValue, DEFAULT_FOOTER_CELL_BUILDER, DEFAULT_OPTIONS, DEFAULT_REPORT_FILENAME, DEFAULT_THEME, type FillColorRgbTuple, type FontStyle, type FooterCellArgs, type FooterCellBuilder, Gaps, IMAGE_GAP, type PageOrientation, type PageSize, type PaperFormat, PdfColors, PdfGenerator, type PdfOptions, type TableCellDef, type TableCellHalign, type TableCellInput, type TableCellValign, type ThemeConfig };
149
+ export { type AddImageArgs, type AddTableArgs, type CellStyle, type CellValue, type ComputeTableMarginsArgs, DEFAULT_FOOTER_CELL_BUILDER, DEFAULT_OPTIONS, DEFAULT_REPORT_FILENAME, DEFAULT_THEME, type FillColorRgbTuple, type FontStyle, type FooterCellArgs, type FooterCellBuilder, Gaps, IMAGE_GAP, type PageOrientation, type PageSize, type PaperFormat, PdfColors, PdfGenerator, type PdfOptions, type TableCellDef, type TableCellHalign, type TableCellInput, type TableCellValign, type ThemeConfig };
package/dist/pdf.js CHANGED
@@ -2,6 +2,8 @@ import { jsPDF } from 'jspdf';
2
2
  import { autoTable } from 'jspdf-autotable';
3
3
  import { format } from 'date-fns';
4
4
 
5
+ // src/pdf/pdf-generator.ts
6
+
5
7
  // src/utils/types.utils.ts
6
8
  var isNullish = (value) => value === null || value === void 0;
7
9
  var DEFAULT_REPORT_FILENAME = "report.pdf";
@@ -115,6 +117,12 @@ var PdfGenerator = class {
115
117
  };
116
118
  console.warn("[checkTableOverflow] table overflow", infos);
117
119
  }
120
+ computeTableMargins({ tableWidth, tableAlign }) {
121
+ const leftover = this.availableWidth - tableWidth;
122
+ const alignOffsets = { left: 0, center: leftover / 2, right: leftover };
123
+ const offset = alignOffsets[tableAlign];
124
+ return { left: this.margin + offset, right: this.margin + (leftover - offset) };
125
+ }
118
126
  addTable(addTableArgs) {
119
127
  const {
120
128
  lineWidth = 5e-3,
@@ -125,7 +133,8 @@ var PdfGenerator = class {
125
133
  body,
126
134
  startY = this._currentY,
127
135
  columnWidths,
128
- cellPadding = 0.05
136
+ cellPadding = 0.05,
137
+ tableAlign = "left"
129
138
  } = addTableArgs;
130
139
  if (body.length === 0) {
131
140
  this._currentY = startY;
@@ -140,6 +149,8 @@ var PdfGenerator = class {
140
149
  });
141
150
  const tableLineWidth = drawLineForTable ? lineWidth : 0;
142
151
  const cellLineWidth = drawLineForCells ? lineWidth : 0;
152
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
153
+ const margin = this.computeTableMargins({ tableWidth, tableAlign });
143
154
  autoTable(this.doc, {
144
155
  startY,
145
156
  head: !isNullish(head) ? [head] : void 0,
@@ -147,7 +158,7 @@ var PdfGenerator = class {
147
158
  theme: "grid",
148
159
  tableLineWidth,
149
160
  tableLineColor: lineColor,
150
- margin: { left: this.margin, right: this.margin },
161
+ margin,
151
162
  bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },
152
163
  headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },
153
164
  columnStyles
package/dist/pdf.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":[],"mappings":"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","file":"pdf.js","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from '../utils';\nimport { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable } from 'jspdf-autotable';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":[],"mappings":"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","file":"pdf.js","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport interface ComputeTableMarginsArgs {\n tableWidth: number;\n tableAlign: TableCellHalign;\n}\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n /** Horizontal placement of the table within the available width (default 'left'). */\n tableAlign?: TableCellHalign;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable, type MarginPaddingInput } from 'jspdf-autotable';\nimport { isNullish, type Dimensions } from '../utils';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type ComputeTableMarginsArgs,\n type PageSize,\n type PdfOptions,\n type TableCellHalign,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n private computeTableMargins({ tableWidth, tableAlign }: ComputeTableMarginsArgs): MarginPaddingInput {\n const leftover = this.availableWidth - tableWidth;\n const alignOffsets: Record<TableCellHalign, number> = { left: 0, center: leftover / 2, right: leftover };\n const offset = alignOffsets[tableAlign];\n return { left: this.margin + offset, right: this.margin + (leftover - offset) };\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n tableAlign = 'left',\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n const tableWidth = widths.reduce((sum, width) => sum + width, 0);\n const margin: MarginPaddingInput = this.computeTableMargins({ tableWidth, tableAlign });\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin,\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
package/dist/rjsf.cjs CHANGED
@@ -191,14 +191,16 @@ var useLocalizedForm = (metaFormSchema) => {
191
191
  var customFormats = {
192
192
  "phone-america": /^\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/
193
193
  };
194
- var useRjsfValidator = (language) => {
194
+ var createRjsfValidator = (language) => {
195
195
  const appLanguage = language;
196
196
  const localizerFn = localizer__default.default[appLanguage] ?? localizer__default.default.en;
197
197
  return validatorAjv8.customizeValidator({ customFormats }, localizerFn);
198
198
  };
199
+ var useRjsfValidator = (language) => createRjsfValidator(language);
199
200
 
200
201
  exports.RJSF_STRING_TO_I18N_KEY = RJSF_STRING_TO_I18N_KEY;
201
202
  exports.addResourceBundleForRjsf = addResourceBundleForRjsf;
203
+ exports.createRjsfValidator = createRjsfValidator;
202
204
  exports.dateToDateOnlyString = dateToDateOnlyString;
203
205
  exports.formatDateOnlyForDisplay = formatDateOnlyForDisplay;
204
206
  exports.formatDateTimeForDisplay = formatDateTimeForDisplay;
package/dist/rjsf.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/rjsf/i18n/en/rjsf.json","../src/rjsf/i18n/fr/rjsf.json","../src/rjsf/i18n/i18n.ts","../src/utils/types.utils.ts","../src/utils/string.utils.ts","../src/rjsf/utils/rjsf-i18n.utils.ts","../src/rjsf/utils/rjsf-widgets.utils.ts","../src/rjsf/utils/use-localized-form.ts","../src/rjsf/utils/use-rjsf-validator.ts"],"names":["rjsf_default","replaceStringParameters","useTranslation","localizer","customizeValidator"],"mappings":"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","file":"rjsf.cjs","sourcesContent":["{\n \"rjsf\": {\n \"add\": \"Add\",\n \"debugFormDataTitle\": \"JSON data\",\n \"addItem\": \"Add Item\",\n \"clear\": \"Clear\",\n \"errors\": \"Errors detected in the form\",\n \"copy\": \"Copy\",\n \"keyLabel\": \"%1 Key\",\n \"moveDown\": \"Move down\",\n \"moveUp\": \"Move up\",\n \"no\": \"No\",\n \"remove\": \"Remove\",\n \"submit\": \"Submit\",\n \"yes\": \"Yes\"\n }\n}\n","{\n \"rjsf\": {\n \"add\": \"Ajouter\",\n \"debugFormDataTitle\": \"Données JSON\",\n \"addItem\": \"Ajouter un élément\",\n \"clear\": \"Effacer\",\n \"errors\": \"Erreurs détectées dans le formulaire\",\n \"copy\": \"Copier\",\n \"keyLabel\": \"Clé %1\",\n \"moveDown\": \"Déplacer vers le bas\",\n \"moveUp\": \"Déplacer vers le haut\",\n \"no\": \"Non\",\n \"remove\": \"Supprimer\",\n \"submit\": \"Envoyer\",\n \"yes\": \"Oui\"\n }\n}\n","import type { i18n } from \"i18next\";\n\nimport en from \"./en/rjsf.json\";\nimport fr from \"./fr/rjsf.json\";\n\nconst RJSF_NAMESPACE = \"rjsf\";\n\nexport const addResourceBundleForRjsf = (instance: i18n): void => {\n if (!instance?.isInitialized) {\n throw new Error(\"[addResourceBundleForRjsf] i18next must be initialized prior to calling this method. Call initI18N() or i18next.init() first.\");\n }\n\n if (instance.hasResourceBundle(\"en\", RJSF_NAMESPACE)) return;\n\n const isDeep = true;\n const isOverwrite = true;\n\n instance.addResourceBundle(\"en\", RJSF_NAMESPACE, en.rjsf, isDeep, isOverwrite);\n instance.addResourceBundle(\"fr\", RJSF_NAMESPACE, fr.rjsf, isDeep, isOverwrite);\n\n // Note: if ever we get duplicated instances of react-i18next, we will need to register the\n // instance provided by this ts-common package like so:\n // import { initReactI18next } from 'react-i18next'\n // instance.use(initReactI18next);\n\n // eslint-disable-next-line no-console\n console.info('[addResourceBundleForRjsf] RJSF i18n initialized'); \n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { REGEX_ALPHANUMERIC } from './regex';\nimport { isNullish } from './types.utils';\n\nexport const isBlank = (str?: string | null): str is null | undefined | '' => {\n return isNullish(str) || str?.trim() === '';\n};\n\nexport const isNotBlank = (str?: string | null): str is string => {\n return !isBlank(str);\n};\n\nexport const isAlphanumeric = (value: string): boolean => {\n return REGEX_ALPHANUMERIC.test(value);\n};\n\n/**\n * Removes diacritical marks (e.g., accents, umlauts) from a string.\n * This method normalizes the input string to its canonical decomposition\n * form (NFD) and removes any combining diacritical marks.\n *\n * @param {string} value - The input string to normalize.\n * @returns {string} - The normalized string with diacritical marks removed.\n *\n * @example\n * const result = removeDiacriticalMarks(\"Ça va très bien, n'est-ce pas?\");\n * console.log(result); // \"Ca va tres bien, n'est-ce pas?\"\n */\nexport const removeDiacriticalMarks = (value: string): string => {\n if (!value) {\n return '';\n }\n\n return value.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n};\n\n/**\n * Capitalize the first letter of a string.\n * @param str - The input string\n * @returns The string with the first character uppercased\n */\nexport const capitalizeFirst = (str: string): string => {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\n/**\n * Count the number of words in a text string.\n * Words are separated by whitespace.\n * @param text - The text to count words in\n * @returns The number of words found\n */\nexport const countWords = (text: string): number => {\n if (!text || text.trim().length === 0) return 0;\n return text.split(/\\s+/).filter((word) => word.length > 0).length;\n};\n\n/**\n * Truncate a string to a maximum length, adding an ellipsis if truncated.\n * @param str - The string to truncate\n * @param maxLength - Maximum length before truncation\n * @param ellipsis - The ellipsis string to append (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (str: string, maxLength: number, ellipsis = '...'): string => {\n if (!str || str.length <= maxLength) return str;\n return str.slice(0, maxLength - ellipsis.length) + ellipsis;\n};\n\n/**\n * Converts a string to kebab-case.\n * Handles camelCase, PascalCase, snake_case, and space-separated strings.\n *\n * @example\n * toKebabCase(\"myComponentName\") // → \"my-component-name\"\n * toKebabCase(\"XMLParser\") // → \"xml-parser\"\n */\nexport const toKebabCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase();\n};\n\n/**\n * Converts a string to camelCase.\n * Handles kebab-case, snake_case, PascalCase, and space-separated strings.\n *\n * @example\n * toCamelCase(\"my-component-name\") // → \"myComponentName\"\n * toCamelCase(\"my_snake_case\") // → \"mySnakeCase\"\n */\nexport const toCamelCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .trim()\n .replace(/[-_\\s]+(.)/g, (_, char: string) => char.toUpperCase())\n .replace(/^[A-Z]/, (char) => char.toLowerCase());\n};\n\n/**\n * Converts a string to a URL-safe slug.\n * Removes diacritical marks, lowercases, replaces non-alphanumeric characters with hyphens,\n * and strips leading/trailing hyphens.\n *\n * @param value - The input string to slugify\n * @returns The slugified string\n *\n * @example\n * slugify(\"Héllo Wörld!\") // → \"hello-world\"\n * slugify(\" My Blog Post \") // → \"my-blog-post\"\n */\nexport const slugify = (value: string): string => {\n if (!value) return '';\n return removeDiacriticalMarks(value)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n};\n\n/**\n * Parses an optional comma-separated string into a trimmed, non-blank string array.\n */\nexport const parseCommaSeparatedList = (raw?: string | null): string[] => {\n if (isBlank(raw)) {\n return [];\n }\n\n return raw\n .split(',')\n .map((s: string) => s.trim())\n .filter(isNotBlank);\n};\n","import { replaceStringParameters } from \"@rjsf/utils\";\nimport { i18n } from \"i18next\";\nimport { isBlank } from \"../../utils/string.utils\";\n\n/** Maps RJSF TranslatableString values to our i18n keys (rjsf namespace). */\nexport const RJSF_STRING_TO_I18N_KEY: Record<string, string> = {\n \"Add Item\": \"rjsf:addItem\",\n Add: \"rjsf:add\",\n Copy: \"rjsf:copy\",\n \"Move down\": \"rjsf:moveDown\",\n \"Move up\": \"rjsf:moveUp\",\n Remove: \"rjsf:remove\",\n \"clear input\": \"rjsf:clear\",\n \"%1 Key\": \"rjsf:keyLabel\", // TODO: RJSF-001 find a way to dynamicaly resolve the rjsf-i18n.utils \"%1 Key\" pattern\n Yes: \"rjsf:yes\",\n No: \"rjsf:no\",\n Errors: \"rjsf:errors\",\n};\n\ninterface TranslateStringArgs {\n i18n: i18n;\n stringToTranslate: string;\n params?: string[];\n}\n\nexport const translateRjsfString = ({ stringToTranslate, params, i18n }: TranslateStringArgs): string => {\n const i18nKey = RJSF_STRING_TO_I18N_KEY[stringToTranslate];\n if (isBlank(i18nKey)) {\n // eslint-disable-next-line no-console\n console.warn(`[translateRjsfString] RJSF i18n: missing key: \"${stringToTranslate}\"`);\n }\n\n const translated = i18nKey ? i18n.t(i18nKey) : stringToTranslate;\n return replaceStringParameters(translated, params);\n};\n","import type { WidgetProps } from \"@rjsf/utils\";\nimport { isBlank } from \"../../utils/string.utils\";\nimport { isNullish } from \"../../utils/types.utils\";\n\n/**\n * Returns true when the widget has at least one validation error from RJSF.\n *\n * @param rawErrors - The rawErrors from WidgetProps (often `unknown`).\n * @returns true if rawErrors is a non-empty array.\n */\nexport const hasRjsfErrors = (rawErrors: unknown): boolean => Array.isArray(rawErrors) && rawErrors.length > 0;\n\nexport interface GetRjsfDisplayLabelArgs {\n label?: string;\n required?: boolean;\n hideLabel?: boolean;\n}\n\n/**\n * Returns the label to display for an RJSF widget, with optional required asterisk and\n * hideLabel support.\n *\n * @param label - The label to display for the widget.\n * @param required - Whether the field is required.\n * @param hideLabel - Whether to hide the label.\n *\n * @returns The label to display for the widget.\n */\nexport const getRjsfDisplayLabel = ({ label, required, hideLabel }: GetRjsfDisplayLabelArgs): string | undefined => {\n if (hideLabel) return undefined;\n if (isBlank(label)) return undefined;\n\n const requiredSuffix = required ? \" *\" : \"\";\n return `${label}${requiredSuffix}`;\n};\n\nconst HEX_COLOR_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n\n/**\n * Reads the `labelColor` from a widget/field `ui:options` object.\n *\n * @param options - The `ui:options` object (e.g. WidgetProps.options or uiSchema[\"ui:options\"]).\n * @returns A validated hex color string (#RGB or #RRGGBB), or undefined when missing/invalid (default color).\n */\nexport const getRjsfLabelColor = (options?: Record<string, unknown> | null): string | undefined => {\n const color = options?.labelColor;\n if (typeof color !== \"string\") return undefined;\n const trimmed = color.trim();\n return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : undefined;\n};\n\ninterface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumOptionDisplay = { label: string; value: string };\n\nconst toEnumOptionDisplay = (o: EnumOption): EnumOptionDisplay => ({\n label: o?.label ?? String(o?.value ?? \"\"),\n value: String(o?.value ?? \"\"),\n});\n\nexport const mapEnumOptions = (options: WidgetProps[\"options\"]): EnumOptionDisplay[] => {\n const enumOptions = options?.enumOptions as EnumOption[] | undefined;\n if (!Array.isArray(enumOptions)) return [];\n return enumOptions.map(toEnumOptionDisplay);\n};\n\n/**\n * Converts a value to display string, or empty string when null/undefined.\n */\nexport const toStringOrEmpty = (value: unknown): string => (isNullish(value) ? \"\" : String(value));\n\n/**\n * Converts a value to display string, or undefined when null/undefined (e.g. for select placeholder).\n */\nexport const toStringOrUndefined = (value: unknown): string | undefined =>\n isNullish(value) ? undefined : String(value);\n\nexport interface GetRjsfTextChangeValueArgs {\n text: string;\n emptyValue?: unknown;\n}\n\n/**\n * Returns the value to pass to onChange for a text input.\n *\n * @param args.text - The current text input value.\n * @param args.emptyValue - The value to use when text is empty (e.g. options.emptyValue from WidgetProps).\n * @returns emptyValue when text is empty, otherwise text.\n */\nexport const getRjsfTextChangeValue = ({ text, emptyValue }: GetRjsfTextChangeValueArgs): unknown => {\n if (text === \"\") return emptyValue;\n return text;\n};\n\nexport const parseDateOrNull = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const date = new Date(value);\n const isValid = !Number.isNaN(date.getTime());\n return isValid ? date : null;\n};\n\n/** Returns YYYY-MM-DD for JSON Schema \"date\" format (form storage). */\nexport const dateToDateOnlyString = (date: Date): string => {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n};\n\nconst DATE_ONLY_REGEX = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parses a date-only or full ISO string into a Date at local midnight (for date picker display). */\nexport const parseDateOnlyToLocalDate = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n if (!DATE_ONLY_REGEX.test(dateOnly)) return null;\n\n const [y, m, d] = dateOnly.split(\"-\").map(Number);\n return new Date(y, m - 1, d);\n};\n\n/** Extracts date-only YYYY-MM-DD for display from a full ISO or date-only string. */\nexport const formatDateOnlyForDisplay = (value?: string): string => {\n if (isBlank(value)) return \"\";\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n return DATE_ONLY_REGEX.test(dateOnly) ? dateOnly : \"\";\n};\n\n/** Formats a full ISO or date string for date-time display (locale string). */\nexport const formatDateTimeForDisplay = (value?: string): string => {\n const date = parseDateOrNull(value);\n return date ? date.toLocaleString() : \"\";\n};\n\ntype FormLayoutContext = {\n labelCol?: { span: number };\n wrapperCol?: { span: number };\n};\n\nconst DEFAULT_SPAN = 24;\n\nexport const getFormLayoutCols = (formContext?: unknown): FormLayoutContext => {\n if (isNullish(formContext)) {\n return {\n labelCol: { span: DEFAULT_SPAN },\n wrapperCol: { span: DEFAULT_SPAN },\n };\n }\n\n const { labelCol, wrapperCol } = formContext as FormLayoutContext;\n return {\n labelCol: labelCol ?? { span: DEFAULT_SPAN },\n wrapperCol: wrapperCol ?? { span: DEFAULT_SPAN },\n };\n};\n","import { isBlank } from \"../../utils/string.utils\";\nimport { useTranslation } from \"react-i18next\";\nimport type { LocalizedFormSchema, MetaFormSchema } from \"./rjsf-i18n-types\";\n\nconst DEFAULT_LANGUAGE = \"fr\";\n\n// i18n.language can be 'fr-CA', so we take the first part\nconst parseSimplifiedLangCode = (language?: string): string => {\n if (isBlank(language)) return DEFAULT_LANGUAGE;\n return language.split(\"-\")[0];\n};\n\nconst getFallbackLanguage = (metaFormSchema: MetaFormSchema): string => {\n return Object.keys(metaFormSchema.i18n)[0] ?? DEFAULT_LANGUAGE;\n};\n\nexport const useLocalizedForm = (metaFormSchema: MetaFormSchema): LocalizedFormSchema => {\n const { i18n } = useTranslation();\n const language = parseSimplifiedLangCode(i18n.language);\n\n if (metaFormSchema.i18n[language]) {\n return metaFormSchema.i18n[language];\n }\n\n const fallbackLanguage = getFallbackLanguage(metaFormSchema);\n // eslint-disable-next-line no-console\n console.warn(\n `Language ${language} not found in metaFormSchema ${metaFormSchema.id}. Falling back to ${fallbackLanguage}.`\n );\n\n return metaFormSchema.i18n[fallbackLanguage];\n};\n","import { customizeValidator, type Localizer } from \"@rjsf/validator-ajv8\";\nimport localizer from \"ajv-i18n\";\n\nconst customFormats = {\n \"phone-america\": /^\\(?\\d{3}\\)?[\\s-]?\\d{3}[\\s-]?\\d{4}$/,\n};\n\ntype ValidationLanguages = keyof typeof localizer;\n\nexport const useRjsfValidator = (language: string) => {\n const appLanguage = language as ValidationLanguages;\n const localizerFn: Localizer = localizer[appLanguage] ?? localizer.en;\n\n return customizeValidator({ customFormats }, localizerFn);\n};\n"]}
1
+ {"version":3,"sources":["../src/rjsf/i18n/en/rjsf.json","../src/rjsf/i18n/fr/rjsf.json","../src/rjsf/i18n/i18n.ts","../src/utils/types.utils.ts","../src/utils/string.utils.ts","../src/rjsf/utils/rjsf-i18n.utils.ts","../src/rjsf/utils/rjsf-widgets.utils.ts","../src/rjsf/utils/use-localized-form.ts","../src/rjsf/utils/use-rjsf-validator.ts"],"names":["rjsf_default","replaceStringParameters","useTranslation","localizer","customizeValidator"],"mappings":"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","file":"rjsf.cjs","sourcesContent":["{\n \"rjsf\": {\n \"add\": \"Add\",\n \"debugFormDataTitle\": \"JSON data\",\n \"addItem\": \"Add Item\",\n \"clear\": \"Clear\",\n \"errors\": \"Errors detected in the form\",\n \"copy\": \"Copy\",\n \"keyLabel\": \"%1 Key\",\n \"moveDown\": \"Move down\",\n \"moveUp\": \"Move up\",\n \"no\": \"No\",\n \"remove\": \"Remove\",\n \"submit\": \"Submit\",\n \"yes\": \"Yes\"\n }\n}\n","{\n \"rjsf\": {\n \"add\": \"Ajouter\",\n \"debugFormDataTitle\": \"Données JSON\",\n \"addItem\": \"Ajouter un élément\",\n \"clear\": \"Effacer\",\n \"errors\": \"Erreurs détectées dans le formulaire\",\n \"copy\": \"Copier\",\n \"keyLabel\": \"Clé %1\",\n \"moveDown\": \"Déplacer vers le bas\",\n \"moveUp\": \"Déplacer vers le haut\",\n \"no\": \"Non\",\n \"remove\": \"Supprimer\",\n \"submit\": \"Envoyer\",\n \"yes\": \"Oui\"\n }\n}\n","import type { i18n } from \"i18next\";\n\nimport en from \"./en/rjsf.json\";\nimport fr from \"./fr/rjsf.json\";\n\nconst RJSF_NAMESPACE = \"rjsf\";\n\nexport const addResourceBundleForRjsf = (instance: i18n): void => {\n if (!instance?.isInitialized) {\n throw new Error(\"[addResourceBundleForRjsf] i18next must be initialized prior to calling this method. Call initI18N() or i18next.init() first.\");\n }\n\n if (instance.hasResourceBundle(\"en\", RJSF_NAMESPACE)) return;\n\n const isDeep = true;\n const isOverwrite = true;\n\n instance.addResourceBundle(\"en\", RJSF_NAMESPACE, en.rjsf, isDeep, isOverwrite);\n instance.addResourceBundle(\"fr\", RJSF_NAMESPACE, fr.rjsf, isDeep, isOverwrite);\n\n // Note: if ever we get duplicated instances of react-i18next, we will need to register the\n // instance provided by this ts-common package like so:\n // import { initReactI18next } from 'react-i18next'\n // instance.use(initReactI18next);\n\n // eslint-disable-next-line no-console\n console.info('[addResourceBundleForRjsf] RJSF i18n initialized'); \n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { REGEX_ALPHANUMERIC } from './regex';\nimport { isNullish } from './types.utils';\n\nexport const isBlank = (str?: string | null): str is null | undefined | '' => {\n return isNullish(str) || str?.trim() === '';\n};\n\nexport const isNotBlank = (str?: string | null): str is string => {\n return !isBlank(str);\n};\n\nexport const isAlphanumeric = (value: string): boolean => {\n return REGEX_ALPHANUMERIC.test(value);\n};\n\n/**\n * Removes diacritical marks (e.g., accents, umlauts) from a string.\n * This method normalizes the input string to its canonical decomposition\n * form (NFD) and removes any combining diacritical marks.\n *\n * @param {string} value - The input string to normalize.\n * @returns {string} - The normalized string with diacritical marks removed.\n *\n * @example\n * const result = removeDiacriticalMarks(\"Ça va très bien, n'est-ce pas?\");\n * console.log(result); // \"Ca va tres bien, n'est-ce pas?\"\n */\nexport const removeDiacriticalMarks = (value: string): string => {\n if (!value) {\n return '';\n }\n\n return value.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n};\n\n/**\n * Capitalize the first letter of a string.\n * @param str - The input string\n * @returns The string with the first character uppercased\n */\nexport const capitalizeFirst = (str: string): string => {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\n/**\n * Count the number of words in a text string.\n * Words are separated by whitespace.\n * @param text - The text to count words in\n * @returns The number of words found\n */\nexport const countWords = (text: string): number => {\n if (!text || text.trim().length === 0) return 0;\n return text.split(/\\s+/).filter((word) => word.length > 0).length;\n};\n\n/**\n * Truncate a string to a maximum length, adding an ellipsis if truncated.\n * @param str - The string to truncate\n * @param maxLength - Maximum length before truncation\n * @param ellipsis - The ellipsis string to append (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (str: string, maxLength: number, ellipsis = '...'): string => {\n if (!str || str.length <= maxLength) return str;\n return str.slice(0, maxLength - ellipsis.length) + ellipsis;\n};\n\n/**\n * Converts a string to kebab-case.\n * Handles camelCase, PascalCase, snake_case, and space-separated strings.\n *\n * @example\n * toKebabCase(\"myComponentName\") // → \"my-component-name\"\n * toKebabCase(\"XMLParser\") // → \"xml-parser\"\n */\nexport const toKebabCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase();\n};\n\n/**\n * Converts a string to camelCase.\n * Handles kebab-case, snake_case, PascalCase, and space-separated strings.\n *\n * @example\n * toCamelCase(\"my-component-name\") // → \"myComponentName\"\n * toCamelCase(\"my_snake_case\") // → \"mySnakeCase\"\n */\nexport const toCamelCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .trim()\n .replace(/[-_\\s]+(.)/g, (_, char: string) => char.toUpperCase())\n .replace(/^[A-Z]/, (char) => char.toLowerCase());\n};\n\n/**\n * Converts a string to a URL-safe slug.\n * Removes diacritical marks, lowercases, replaces non-alphanumeric characters with hyphens,\n * and strips leading/trailing hyphens.\n *\n * @param value - The input string to slugify\n * @returns The slugified string\n *\n * @example\n * slugify(\"Héllo Wörld!\") // → \"hello-world\"\n * slugify(\" My Blog Post \") // → \"my-blog-post\"\n */\nexport const slugify = (value: string): string => {\n if (!value) return '';\n return removeDiacriticalMarks(value)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n};\n\n/**\n * Parses an optional comma-separated string into a trimmed, non-blank string array.\n */\nexport const parseCommaSeparatedList = (raw?: string | null): string[] => {\n if (isBlank(raw)) {\n return [];\n }\n\n return raw\n .split(',')\n .map((s: string) => s.trim())\n .filter(isNotBlank);\n};\n","import { replaceStringParameters } from \"@rjsf/utils\";\nimport { i18n } from \"i18next\";\nimport { isBlank } from \"../../utils/string.utils\";\n\n/** Maps RJSF TranslatableString values to our i18n keys (rjsf namespace). */\nexport const RJSF_STRING_TO_I18N_KEY: Record<string, string> = {\n \"Add Item\": \"rjsf:addItem\",\n Add: \"rjsf:add\",\n Copy: \"rjsf:copy\",\n \"Move down\": \"rjsf:moveDown\",\n \"Move up\": \"rjsf:moveUp\",\n Remove: \"rjsf:remove\",\n \"clear input\": \"rjsf:clear\",\n \"%1 Key\": \"rjsf:keyLabel\", // TODO: RJSF-001 find a way to dynamicaly resolve the rjsf-i18n.utils \"%1 Key\" pattern\n Yes: \"rjsf:yes\",\n No: \"rjsf:no\",\n Errors: \"rjsf:errors\",\n};\n\ninterface TranslateStringArgs {\n i18n: i18n;\n stringToTranslate: string;\n params?: string[];\n}\n\nexport const translateRjsfString = ({ stringToTranslate, params, i18n }: TranslateStringArgs): string => {\n const i18nKey = RJSF_STRING_TO_I18N_KEY[stringToTranslate];\n if (isBlank(i18nKey)) {\n // eslint-disable-next-line no-console\n console.warn(`[translateRjsfString] RJSF i18n: missing key: \"${stringToTranslate}\"`);\n }\n\n const translated = i18nKey ? i18n.t(i18nKey) : stringToTranslate;\n return replaceStringParameters(translated, params);\n};\n","import type { WidgetProps } from \"@rjsf/utils\";\nimport { isBlank } from \"../../utils/string.utils\";\nimport { isNullish } from \"../../utils/types.utils\";\n\n/**\n * Returns true when the widget has at least one validation error from RJSF.\n *\n * @param rawErrors - The rawErrors from WidgetProps (often `unknown`).\n * @returns true if rawErrors is a non-empty array.\n */\nexport const hasRjsfErrors = (rawErrors: unknown): boolean => Array.isArray(rawErrors) && rawErrors.length > 0;\n\nexport interface GetRjsfDisplayLabelArgs {\n label?: string;\n required?: boolean;\n hideLabel?: boolean;\n}\n\n/**\n * Returns the label to display for an RJSF widget, with optional required asterisk and\n * hideLabel support.\n *\n * @param label - The label to display for the widget.\n * @param required - Whether the field is required.\n * @param hideLabel - Whether to hide the label.\n *\n * @returns The label to display for the widget.\n */\nexport const getRjsfDisplayLabel = ({ label, required, hideLabel }: GetRjsfDisplayLabelArgs): string | undefined => {\n if (hideLabel) return undefined;\n if (isBlank(label)) return undefined;\n\n const requiredSuffix = required ? \" *\" : \"\";\n return `${label}${requiredSuffix}`;\n};\n\nconst HEX_COLOR_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n\n/**\n * Reads the `labelColor` from a widget/field `ui:options` object.\n *\n * @param options - The `ui:options` object (e.g. WidgetProps.options or uiSchema[\"ui:options\"]).\n * @returns A validated hex color string (#RGB or #RRGGBB), or undefined when missing/invalid (default color).\n */\nexport const getRjsfLabelColor = (options?: Record<string, unknown> | null): string | undefined => {\n const color = options?.labelColor;\n if (typeof color !== \"string\") return undefined;\n const trimmed = color.trim();\n return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : undefined;\n};\n\ninterface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumOptionDisplay = { label: string; value: string };\n\nconst toEnumOptionDisplay = (o: EnumOption): EnumOptionDisplay => ({\n label: o?.label ?? String(o?.value ?? \"\"),\n value: String(o?.value ?? \"\"),\n});\n\nexport const mapEnumOptions = (options: WidgetProps[\"options\"]): EnumOptionDisplay[] => {\n const enumOptions = options?.enumOptions as EnumOption[] | undefined;\n if (!Array.isArray(enumOptions)) return [];\n return enumOptions.map(toEnumOptionDisplay);\n};\n\n/**\n * Converts a value to display string, or empty string when null/undefined.\n */\nexport const toStringOrEmpty = (value: unknown): string => (isNullish(value) ? \"\" : String(value));\n\n/**\n * Converts a value to display string, or undefined when null/undefined (e.g. for select placeholder).\n */\nexport const toStringOrUndefined = (value: unknown): string | undefined =>\n isNullish(value) ? undefined : String(value);\n\nexport interface GetRjsfTextChangeValueArgs {\n text: string;\n emptyValue?: unknown;\n}\n\n/**\n * Returns the value to pass to onChange for a text input.\n *\n * @param args.text - The current text input value.\n * @param args.emptyValue - The value to use when text is empty (e.g. options.emptyValue from WidgetProps).\n * @returns emptyValue when text is empty, otherwise text.\n */\nexport const getRjsfTextChangeValue = ({ text, emptyValue }: GetRjsfTextChangeValueArgs): unknown => {\n if (text === \"\") return emptyValue;\n return text;\n};\n\nexport const parseDateOrNull = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const date = new Date(value);\n const isValid = !Number.isNaN(date.getTime());\n return isValid ? date : null;\n};\n\n/** Returns YYYY-MM-DD for JSON Schema \"date\" format (form storage). */\nexport const dateToDateOnlyString = (date: Date): string => {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n};\n\nconst DATE_ONLY_REGEX = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parses a date-only or full ISO string into a Date at local midnight (for date picker display). */\nexport const parseDateOnlyToLocalDate = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n if (!DATE_ONLY_REGEX.test(dateOnly)) return null;\n\n const [y, m, d] = dateOnly.split(\"-\").map(Number);\n return new Date(y, m - 1, d);\n};\n\n/** Extracts date-only YYYY-MM-DD for display from a full ISO or date-only string. */\nexport const formatDateOnlyForDisplay = (value?: string): string => {\n if (isBlank(value)) return \"\";\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n return DATE_ONLY_REGEX.test(dateOnly) ? dateOnly : \"\";\n};\n\n/** Formats a full ISO or date string for date-time display (locale string). */\nexport const formatDateTimeForDisplay = (value?: string): string => {\n const date = parseDateOrNull(value);\n return date ? date.toLocaleString() : \"\";\n};\n\ntype FormLayoutContext = {\n labelCol?: { span: number };\n wrapperCol?: { span: number };\n};\n\nconst DEFAULT_SPAN = 24;\n\nexport const getFormLayoutCols = (formContext?: unknown): FormLayoutContext => {\n if (isNullish(formContext)) {\n return {\n labelCol: { span: DEFAULT_SPAN },\n wrapperCol: { span: DEFAULT_SPAN },\n };\n }\n\n const { labelCol, wrapperCol } = formContext as FormLayoutContext;\n return {\n labelCol: labelCol ?? { span: DEFAULT_SPAN },\n wrapperCol: wrapperCol ?? { span: DEFAULT_SPAN },\n };\n};\n","import { isBlank } from \"../../utils/string.utils\";\nimport { useTranslation } from \"react-i18next\";\nimport type { LocalizedFormSchema, MetaFormSchema } from \"./rjsf-i18n-types\";\n\nconst DEFAULT_LANGUAGE = \"fr\";\n\n// i18n.language can be 'fr-CA', so we take the first part\nconst parseSimplifiedLangCode = (language?: string): string => {\n if (isBlank(language)) return DEFAULT_LANGUAGE;\n return language.split(\"-\")[0];\n};\n\nconst getFallbackLanguage = (metaFormSchema: MetaFormSchema): string => {\n return Object.keys(metaFormSchema.i18n)[0] ?? DEFAULT_LANGUAGE;\n};\n\nexport const useLocalizedForm = (metaFormSchema: MetaFormSchema): LocalizedFormSchema => {\n const { i18n } = useTranslation();\n const language = parseSimplifiedLangCode(i18n.language);\n\n if (metaFormSchema.i18n[language]) {\n return metaFormSchema.i18n[language];\n }\n\n const fallbackLanguage = getFallbackLanguage(metaFormSchema);\n // eslint-disable-next-line no-console\n console.warn(\n `Language ${language} not found in metaFormSchema ${metaFormSchema.id}. Falling back to ${fallbackLanguage}.`\n );\n\n return metaFormSchema.i18n[fallbackLanguage];\n};\n","import { customizeValidator, type Localizer } from \"@rjsf/validator-ajv8\";\nimport localizer from \"ajv-i18n\";\n\nconst customFormats = {\n \"phone-america\": /^\\(?\\d{3}\\)?[\\s-]?\\d{3}[\\s-]?\\d{4}$/,\n};\n\ntype ValidationLanguages = keyof typeof localizer;\n\n/**\n * Builds an AJV validator for the given language. Pure (no React) so the caller\n * can memoize it. Prefer this + `useMemo` in the React layer: RJSF rebuilds its\n * `schemaUtils`/`retrieveSchema` cache from the validator identity, so a fresh\n * validator on every render recompiles every conditional (`allOf`/`if`/`then`)\n * subschema on each keystroke. See `RjsfPaperRenderer` for the memoized usage.\n */\nexport const createRjsfValidator = (language: string) => {\n const appLanguage = language as ValidationLanguages;\n const localizerFn: Localizer = localizer[appLanguage] ?? localizer.en;\n\n return customizeValidator({ customFormats }, localizerFn);\n};\n\n/**\n * Backward-compatible wrapper around {@link createRjsfValidator}. Not memoized —\n * returns a new validator on every call. Prefer `createRjsfValidator` memoized in\n * the React layer.\n */\nexport const useRjsfValidator = (language: string) => createRjsfValidator(language);\n"]}
package/dist/rjsf.d.cts CHANGED
@@ -105,6 +105,19 @@ declare const getFormLayoutCols: (formContext?: unknown) => FormLayoutContext;
105
105
 
106
106
  declare const useLocalizedForm: (metaFormSchema: MetaFormSchema) => LocalizedFormSchema;
107
107
 
108
+ /**
109
+ * Builds an AJV validator for the given language. Pure (no React) so the caller
110
+ * can memoize it. Prefer this + `useMemo` in the React layer: RJSF rebuilds its
111
+ * `schemaUtils`/`retrieveSchema` cache from the validator identity, so a fresh
112
+ * validator on every render recompiles every conditional (`allOf`/`if`/`then`)
113
+ * subschema on each keystroke. See `RjsfPaperRenderer` for the memoized usage.
114
+ */
115
+ declare const createRjsfValidator: (language: string) => _rjsf_validator_ajv8_lib_validator_js.default<any, _rjsf_utils.RJSFSchema, any>;
116
+ /**
117
+ * Backward-compatible wrapper around {@link createRjsfValidator}. Not memoized —
118
+ * returns a new validator on every call. Prefer `createRjsfValidator` memoized in
119
+ * the React layer.
120
+ */
108
121
  declare const useRjsfValidator: (language: string) => _rjsf_validator_ajv8_lib_validator_js.default<any, _rjsf_utils.RJSFSchema, any>;
109
122
 
110
- export { type EnumOptionDisplay, type GetRjsfDisplayLabelArgs, type GetRjsfTextChangeValueArgs, type Language, type LocalizedFormSchema, type MetaFormSchema, RJSF_STRING_TO_I18N_KEY, type RjsfRegistryWithTranslate, addResourceBundleForRjsf, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
123
+ export { type EnumOptionDisplay, type GetRjsfDisplayLabelArgs, type GetRjsfTextChangeValueArgs, type Language, type LocalizedFormSchema, type MetaFormSchema, RJSF_STRING_TO_I18N_KEY, type RjsfRegistryWithTranslate, addResourceBundleForRjsf, createRjsfValidator, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
package/dist/rjsf.d.ts CHANGED
@@ -105,6 +105,19 @@ declare const getFormLayoutCols: (formContext?: unknown) => FormLayoutContext;
105
105
 
106
106
  declare const useLocalizedForm: (metaFormSchema: MetaFormSchema) => LocalizedFormSchema;
107
107
 
108
+ /**
109
+ * Builds an AJV validator for the given language. Pure (no React) so the caller
110
+ * can memoize it. Prefer this + `useMemo` in the React layer: RJSF rebuilds its
111
+ * `schemaUtils`/`retrieveSchema` cache from the validator identity, so a fresh
112
+ * validator on every render recompiles every conditional (`allOf`/`if`/`then`)
113
+ * subschema on each keystroke. See `RjsfPaperRenderer` for the memoized usage.
114
+ */
115
+ declare const createRjsfValidator: (language: string) => _rjsf_validator_ajv8_lib_validator_js.default<any, _rjsf_utils.RJSFSchema, any>;
116
+ /**
117
+ * Backward-compatible wrapper around {@link createRjsfValidator}. Not memoized —
118
+ * returns a new validator on every call. Prefer `createRjsfValidator` memoized in
119
+ * the React layer.
120
+ */
108
121
  declare const useRjsfValidator: (language: string) => _rjsf_validator_ajv8_lib_validator_js.default<any, _rjsf_utils.RJSFSchema, any>;
109
122
 
110
- export { type EnumOptionDisplay, type GetRjsfDisplayLabelArgs, type GetRjsfTextChangeValueArgs, type Language, type LocalizedFormSchema, type MetaFormSchema, RJSF_STRING_TO_I18N_KEY, type RjsfRegistryWithTranslate, addResourceBundleForRjsf, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
123
+ export { type EnumOptionDisplay, type GetRjsfDisplayLabelArgs, type GetRjsfTextChangeValueArgs, type Language, type LocalizedFormSchema, type MetaFormSchema, RJSF_STRING_TO_I18N_KEY, type RjsfRegistryWithTranslate, addResourceBundleForRjsf, createRjsfValidator, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
package/dist/rjsf.js CHANGED
@@ -185,12 +185,13 @@ var useLocalizedForm = (metaFormSchema) => {
185
185
  var customFormats = {
186
186
  "phone-america": /^\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/
187
187
  };
188
- var useRjsfValidator = (language) => {
188
+ var createRjsfValidator = (language) => {
189
189
  const appLanguage = language;
190
190
  const localizerFn = localizer[appLanguage] ?? localizer.en;
191
191
  return customizeValidator({ customFormats }, localizerFn);
192
192
  };
193
+ var useRjsfValidator = (language) => createRjsfValidator(language);
193
194
 
194
- export { RJSF_STRING_TO_I18N_KEY, addResourceBundleForRjsf, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
195
+ export { RJSF_STRING_TO_I18N_KEY, addResourceBundleForRjsf, createRjsfValidator, dateToDateOnlyString, formatDateOnlyForDisplay, formatDateTimeForDisplay, getFormLayoutCols, getRjsfDisplayLabel, getRjsfLabelColor, getRjsfTextChangeValue, hasRjsfErrors, mapEnumOptions, parseDateOnlyToLocalDate, parseDateOrNull, toStringOrEmpty, toStringOrUndefined, translateRjsfString, useLocalizedForm, useRjsfValidator };
195
196
  //# sourceMappingURL=rjsf.js.map
196
197
  //# sourceMappingURL=rjsf.js.map
package/dist/rjsf.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/rjsf/i18n/en/rjsf.json","../src/rjsf/i18n/fr/rjsf.json","../src/rjsf/i18n/i18n.ts","../src/utils/types.utils.ts","../src/utils/string.utils.ts","../src/rjsf/utils/rjsf-i18n.utils.ts","../src/rjsf/utils/rjsf-widgets.utils.ts","../src/rjsf/utils/use-localized-form.ts","../src/rjsf/utils/use-rjsf-validator.ts"],"names":["rjsf_default"],"mappings":"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","file":"rjsf.js","sourcesContent":["{\n \"rjsf\": {\n \"add\": \"Add\",\n \"debugFormDataTitle\": \"JSON data\",\n \"addItem\": \"Add Item\",\n \"clear\": \"Clear\",\n \"errors\": \"Errors detected in the form\",\n \"copy\": \"Copy\",\n \"keyLabel\": \"%1 Key\",\n \"moveDown\": \"Move down\",\n \"moveUp\": \"Move up\",\n \"no\": \"No\",\n \"remove\": \"Remove\",\n \"submit\": \"Submit\",\n \"yes\": \"Yes\"\n }\n}\n","{\n \"rjsf\": {\n \"add\": \"Ajouter\",\n \"debugFormDataTitle\": \"Données JSON\",\n \"addItem\": \"Ajouter un élément\",\n \"clear\": \"Effacer\",\n \"errors\": \"Erreurs détectées dans le formulaire\",\n \"copy\": \"Copier\",\n \"keyLabel\": \"Clé %1\",\n \"moveDown\": \"Déplacer vers le bas\",\n \"moveUp\": \"Déplacer vers le haut\",\n \"no\": \"Non\",\n \"remove\": \"Supprimer\",\n \"submit\": \"Envoyer\",\n \"yes\": \"Oui\"\n }\n}\n","import type { i18n } from \"i18next\";\n\nimport en from \"./en/rjsf.json\";\nimport fr from \"./fr/rjsf.json\";\n\nconst RJSF_NAMESPACE = \"rjsf\";\n\nexport const addResourceBundleForRjsf = (instance: i18n): void => {\n if (!instance?.isInitialized) {\n throw new Error(\"[addResourceBundleForRjsf] i18next must be initialized prior to calling this method. Call initI18N() or i18next.init() first.\");\n }\n\n if (instance.hasResourceBundle(\"en\", RJSF_NAMESPACE)) return;\n\n const isDeep = true;\n const isOverwrite = true;\n\n instance.addResourceBundle(\"en\", RJSF_NAMESPACE, en.rjsf, isDeep, isOverwrite);\n instance.addResourceBundle(\"fr\", RJSF_NAMESPACE, fr.rjsf, isDeep, isOverwrite);\n\n // Note: if ever we get duplicated instances of react-i18next, we will need to register the\n // instance provided by this ts-common package like so:\n // import { initReactI18next } from 'react-i18next'\n // instance.use(initReactI18next);\n\n // eslint-disable-next-line no-console\n console.info('[addResourceBundleForRjsf] RJSF i18n initialized'); \n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { REGEX_ALPHANUMERIC } from './regex';\nimport { isNullish } from './types.utils';\n\nexport const isBlank = (str?: string | null): str is null | undefined | '' => {\n return isNullish(str) || str?.trim() === '';\n};\n\nexport const isNotBlank = (str?: string | null): str is string => {\n return !isBlank(str);\n};\n\nexport const isAlphanumeric = (value: string): boolean => {\n return REGEX_ALPHANUMERIC.test(value);\n};\n\n/**\n * Removes diacritical marks (e.g., accents, umlauts) from a string.\n * This method normalizes the input string to its canonical decomposition\n * form (NFD) and removes any combining diacritical marks.\n *\n * @param {string} value - The input string to normalize.\n * @returns {string} - The normalized string with diacritical marks removed.\n *\n * @example\n * const result = removeDiacriticalMarks(\"Ça va très bien, n'est-ce pas?\");\n * console.log(result); // \"Ca va tres bien, n'est-ce pas?\"\n */\nexport const removeDiacriticalMarks = (value: string): string => {\n if (!value) {\n return '';\n }\n\n return value.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n};\n\n/**\n * Capitalize the first letter of a string.\n * @param str - The input string\n * @returns The string with the first character uppercased\n */\nexport const capitalizeFirst = (str: string): string => {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\n/**\n * Count the number of words in a text string.\n * Words are separated by whitespace.\n * @param text - The text to count words in\n * @returns The number of words found\n */\nexport const countWords = (text: string): number => {\n if (!text || text.trim().length === 0) return 0;\n return text.split(/\\s+/).filter((word) => word.length > 0).length;\n};\n\n/**\n * Truncate a string to a maximum length, adding an ellipsis if truncated.\n * @param str - The string to truncate\n * @param maxLength - Maximum length before truncation\n * @param ellipsis - The ellipsis string to append (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (str: string, maxLength: number, ellipsis = '...'): string => {\n if (!str || str.length <= maxLength) return str;\n return str.slice(0, maxLength - ellipsis.length) + ellipsis;\n};\n\n/**\n * Converts a string to kebab-case.\n * Handles camelCase, PascalCase, snake_case, and space-separated strings.\n *\n * @example\n * toKebabCase(\"myComponentName\") // → \"my-component-name\"\n * toKebabCase(\"XMLParser\") // → \"xml-parser\"\n */\nexport const toKebabCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase();\n};\n\n/**\n * Converts a string to camelCase.\n * Handles kebab-case, snake_case, PascalCase, and space-separated strings.\n *\n * @example\n * toCamelCase(\"my-component-name\") // → \"myComponentName\"\n * toCamelCase(\"my_snake_case\") // → \"mySnakeCase\"\n */\nexport const toCamelCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .trim()\n .replace(/[-_\\s]+(.)/g, (_, char: string) => char.toUpperCase())\n .replace(/^[A-Z]/, (char) => char.toLowerCase());\n};\n\n/**\n * Converts a string to a URL-safe slug.\n * Removes diacritical marks, lowercases, replaces non-alphanumeric characters with hyphens,\n * and strips leading/trailing hyphens.\n *\n * @param value - The input string to slugify\n * @returns The slugified string\n *\n * @example\n * slugify(\"Héllo Wörld!\") // → \"hello-world\"\n * slugify(\" My Blog Post \") // → \"my-blog-post\"\n */\nexport const slugify = (value: string): string => {\n if (!value) return '';\n return removeDiacriticalMarks(value)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n};\n\n/**\n * Parses an optional comma-separated string into a trimmed, non-blank string array.\n */\nexport const parseCommaSeparatedList = (raw?: string | null): string[] => {\n if (isBlank(raw)) {\n return [];\n }\n\n return raw\n .split(',')\n .map((s: string) => s.trim())\n .filter(isNotBlank);\n};\n","import { replaceStringParameters } from \"@rjsf/utils\";\nimport { i18n } from \"i18next\";\nimport { isBlank } from \"../../utils/string.utils\";\n\n/** Maps RJSF TranslatableString values to our i18n keys (rjsf namespace). */\nexport const RJSF_STRING_TO_I18N_KEY: Record<string, string> = {\n \"Add Item\": \"rjsf:addItem\",\n Add: \"rjsf:add\",\n Copy: \"rjsf:copy\",\n \"Move down\": \"rjsf:moveDown\",\n \"Move up\": \"rjsf:moveUp\",\n Remove: \"rjsf:remove\",\n \"clear input\": \"rjsf:clear\",\n \"%1 Key\": \"rjsf:keyLabel\", // TODO: RJSF-001 find a way to dynamicaly resolve the rjsf-i18n.utils \"%1 Key\" pattern\n Yes: \"rjsf:yes\",\n No: \"rjsf:no\",\n Errors: \"rjsf:errors\",\n};\n\ninterface TranslateStringArgs {\n i18n: i18n;\n stringToTranslate: string;\n params?: string[];\n}\n\nexport const translateRjsfString = ({ stringToTranslate, params, i18n }: TranslateStringArgs): string => {\n const i18nKey = RJSF_STRING_TO_I18N_KEY[stringToTranslate];\n if (isBlank(i18nKey)) {\n // eslint-disable-next-line no-console\n console.warn(`[translateRjsfString] RJSF i18n: missing key: \"${stringToTranslate}\"`);\n }\n\n const translated = i18nKey ? i18n.t(i18nKey) : stringToTranslate;\n return replaceStringParameters(translated, params);\n};\n","import type { WidgetProps } from \"@rjsf/utils\";\nimport { isBlank } from \"../../utils/string.utils\";\nimport { isNullish } from \"../../utils/types.utils\";\n\n/**\n * Returns true when the widget has at least one validation error from RJSF.\n *\n * @param rawErrors - The rawErrors from WidgetProps (often `unknown`).\n * @returns true if rawErrors is a non-empty array.\n */\nexport const hasRjsfErrors = (rawErrors: unknown): boolean => Array.isArray(rawErrors) && rawErrors.length > 0;\n\nexport interface GetRjsfDisplayLabelArgs {\n label?: string;\n required?: boolean;\n hideLabel?: boolean;\n}\n\n/**\n * Returns the label to display for an RJSF widget, with optional required asterisk and\n * hideLabel support.\n *\n * @param label - The label to display for the widget.\n * @param required - Whether the field is required.\n * @param hideLabel - Whether to hide the label.\n *\n * @returns The label to display for the widget.\n */\nexport const getRjsfDisplayLabel = ({ label, required, hideLabel }: GetRjsfDisplayLabelArgs): string | undefined => {\n if (hideLabel) return undefined;\n if (isBlank(label)) return undefined;\n\n const requiredSuffix = required ? \" *\" : \"\";\n return `${label}${requiredSuffix}`;\n};\n\nconst HEX_COLOR_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n\n/**\n * Reads the `labelColor` from a widget/field `ui:options` object.\n *\n * @param options - The `ui:options` object (e.g. WidgetProps.options or uiSchema[\"ui:options\"]).\n * @returns A validated hex color string (#RGB or #RRGGBB), or undefined when missing/invalid (default color).\n */\nexport const getRjsfLabelColor = (options?: Record<string, unknown> | null): string | undefined => {\n const color = options?.labelColor;\n if (typeof color !== \"string\") return undefined;\n const trimmed = color.trim();\n return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : undefined;\n};\n\ninterface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumOptionDisplay = { label: string; value: string };\n\nconst toEnumOptionDisplay = (o: EnumOption): EnumOptionDisplay => ({\n label: o?.label ?? String(o?.value ?? \"\"),\n value: String(o?.value ?? \"\"),\n});\n\nexport const mapEnumOptions = (options: WidgetProps[\"options\"]): EnumOptionDisplay[] => {\n const enumOptions = options?.enumOptions as EnumOption[] | undefined;\n if (!Array.isArray(enumOptions)) return [];\n return enumOptions.map(toEnumOptionDisplay);\n};\n\n/**\n * Converts a value to display string, or empty string when null/undefined.\n */\nexport const toStringOrEmpty = (value: unknown): string => (isNullish(value) ? \"\" : String(value));\n\n/**\n * Converts a value to display string, or undefined when null/undefined (e.g. for select placeholder).\n */\nexport const toStringOrUndefined = (value: unknown): string | undefined =>\n isNullish(value) ? undefined : String(value);\n\nexport interface GetRjsfTextChangeValueArgs {\n text: string;\n emptyValue?: unknown;\n}\n\n/**\n * Returns the value to pass to onChange for a text input.\n *\n * @param args.text - The current text input value.\n * @param args.emptyValue - The value to use when text is empty (e.g. options.emptyValue from WidgetProps).\n * @returns emptyValue when text is empty, otherwise text.\n */\nexport const getRjsfTextChangeValue = ({ text, emptyValue }: GetRjsfTextChangeValueArgs): unknown => {\n if (text === \"\") return emptyValue;\n return text;\n};\n\nexport const parseDateOrNull = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const date = new Date(value);\n const isValid = !Number.isNaN(date.getTime());\n return isValid ? date : null;\n};\n\n/** Returns YYYY-MM-DD for JSON Schema \"date\" format (form storage). */\nexport const dateToDateOnlyString = (date: Date): string => {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n};\n\nconst DATE_ONLY_REGEX = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parses a date-only or full ISO string into a Date at local midnight (for date picker display). */\nexport const parseDateOnlyToLocalDate = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n if (!DATE_ONLY_REGEX.test(dateOnly)) return null;\n\n const [y, m, d] = dateOnly.split(\"-\").map(Number);\n return new Date(y, m - 1, d);\n};\n\n/** Extracts date-only YYYY-MM-DD for display from a full ISO or date-only string. */\nexport const formatDateOnlyForDisplay = (value?: string): string => {\n if (isBlank(value)) return \"\";\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n return DATE_ONLY_REGEX.test(dateOnly) ? dateOnly : \"\";\n};\n\n/** Formats a full ISO or date string for date-time display (locale string). */\nexport const formatDateTimeForDisplay = (value?: string): string => {\n const date = parseDateOrNull(value);\n return date ? date.toLocaleString() : \"\";\n};\n\ntype FormLayoutContext = {\n labelCol?: { span: number };\n wrapperCol?: { span: number };\n};\n\nconst DEFAULT_SPAN = 24;\n\nexport const getFormLayoutCols = (formContext?: unknown): FormLayoutContext => {\n if (isNullish(formContext)) {\n return {\n labelCol: { span: DEFAULT_SPAN },\n wrapperCol: { span: DEFAULT_SPAN },\n };\n }\n\n const { labelCol, wrapperCol } = formContext as FormLayoutContext;\n return {\n labelCol: labelCol ?? { span: DEFAULT_SPAN },\n wrapperCol: wrapperCol ?? { span: DEFAULT_SPAN },\n };\n};\n","import { isBlank } from \"../../utils/string.utils\";\nimport { useTranslation } from \"react-i18next\";\nimport type { LocalizedFormSchema, MetaFormSchema } from \"./rjsf-i18n-types\";\n\nconst DEFAULT_LANGUAGE = \"fr\";\n\n// i18n.language can be 'fr-CA', so we take the first part\nconst parseSimplifiedLangCode = (language?: string): string => {\n if (isBlank(language)) return DEFAULT_LANGUAGE;\n return language.split(\"-\")[0];\n};\n\nconst getFallbackLanguage = (metaFormSchema: MetaFormSchema): string => {\n return Object.keys(metaFormSchema.i18n)[0] ?? DEFAULT_LANGUAGE;\n};\n\nexport const useLocalizedForm = (metaFormSchema: MetaFormSchema): LocalizedFormSchema => {\n const { i18n } = useTranslation();\n const language = parseSimplifiedLangCode(i18n.language);\n\n if (metaFormSchema.i18n[language]) {\n return metaFormSchema.i18n[language];\n }\n\n const fallbackLanguage = getFallbackLanguage(metaFormSchema);\n // eslint-disable-next-line no-console\n console.warn(\n `Language ${language} not found in metaFormSchema ${metaFormSchema.id}. Falling back to ${fallbackLanguage}.`\n );\n\n return metaFormSchema.i18n[fallbackLanguage];\n};\n","import { customizeValidator, type Localizer } from \"@rjsf/validator-ajv8\";\nimport localizer from \"ajv-i18n\";\n\nconst customFormats = {\n \"phone-america\": /^\\(?\\d{3}\\)?[\\s-]?\\d{3}[\\s-]?\\d{4}$/,\n};\n\ntype ValidationLanguages = keyof typeof localizer;\n\nexport const useRjsfValidator = (language: string) => {\n const appLanguage = language as ValidationLanguages;\n const localizerFn: Localizer = localizer[appLanguage] ?? localizer.en;\n\n return customizeValidator({ customFormats }, localizerFn);\n};\n"]}
1
+ {"version":3,"sources":["../src/rjsf/i18n/en/rjsf.json","../src/rjsf/i18n/fr/rjsf.json","../src/rjsf/i18n/i18n.ts","../src/utils/types.utils.ts","../src/utils/string.utils.ts","../src/rjsf/utils/rjsf-i18n.utils.ts","../src/rjsf/utils/rjsf-widgets.utils.ts","../src/rjsf/utils/use-localized-form.ts","../src/rjsf/utils/use-rjsf-validator.ts"],"names":["rjsf_default"],"mappings":"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","file":"rjsf.js","sourcesContent":["{\n \"rjsf\": {\n \"add\": \"Add\",\n \"debugFormDataTitle\": \"JSON data\",\n \"addItem\": \"Add Item\",\n \"clear\": \"Clear\",\n \"errors\": \"Errors detected in the form\",\n \"copy\": \"Copy\",\n \"keyLabel\": \"%1 Key\",\n \"moveDown\": \"Move down\",\n \"moveUp\": \"Move up\",\n \"no\": \"No\",\n \"remove\": \"Remove\",\n \"submit\": \"Submit\",\n \"yes\": \"Yes\"\n }\n}\n","{\n \"rjsf\": {\n \"add\": \"Ajouter\",\n \"debugFormDataTitle\": \"Données JSON\",\n \"addItem\": \"Ajouter un élément\",\n \"clear\": \"Effacer\",\n \"errors\": \"Erreurs détectées dans le formulaire\",\n \"copy\": \"Copier\",\n \"keyLabel\": \"Clé %1\",\n \"moveDown\": \"Déplacer vers le bas\",\n \"moveUp\": \"Déplacer vers le haut\",\n \"no\": \"Non\",\n \"remove\": \"Supprimer\",\n \"submit\": \"Envoyer\",\n \"yes\": \"Oui\"\n }\n}\n","import type { i18n } from \"i18next\";\n\nimport en from \"./en/rjsf.json\";\nimport fr from \"./fr/rjsf.json\";\n\nconst RJSF_NAMESPACE = \"rjsf\";\n\nexport const addResourceBundleForRjsf = (instance: i18n): void => {\n if (!instance?.isInitialized) {\n throw new Error(\"[addResourceBundleForRjsf] i18next must be initialized prior to calling this method. Call initI18N() or i18next.init() first.\");\n }\n\n if (instance.hasResourceBundle(\"en\", RJSF_NAMESPACE)) return;\n\n const isDeep = true;\n const isOverwrite = true;\n\n instance.addResourceBundle(\"en\", RJSF_NAMESPACE, en.rjsf, isDeep, isOverwrite);\n instance.addResourceBundle(\"fr\", RJSF_NAMESPACE, fr.rjsf, isDeep, isOverwrite);\n\n // Note: if ever we get duplicated instances of react-i18next, we will need to register the\n // instance provided by this ts-common package like so:\n // import { initReactI18next } from 'react-i18next'\n // instance.use(initReactI18next);\n\n // eslint-disable-next-line no-console\n console.info('[addResourceBundleForRjsf] RJSF i18n initialized'); \n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { REGEX_ALPHANUMERIC } from './regex';\nimport { isNullish } from './types.utils';\n\nexport const isBlank = (str?: string | null): str is null | undefined | '' => {\n return isNullish(str) || str?.trim() === '';\n};\n\nexport const isNotBlank = (str?: string | null): str is string => {\n return !isBlank(str);\n};\n\nexport const isAlphanumeric = (value: string): boolean => {\n return REGEX_ALPHANUMERIC.test(value);\n};\n\n/**\n * Removes diacritical marks (e.g., accents, umlauts) from a string.\n * This method normalizes the input string to its canonical decomposition\n * form (NFD) and removes any combining diacritical marks.\n *\n * @param {string} value - The input string to normalize.\n * @returns {string} - The normalized string with diacritical marks removed.\n *\n * @example\n * const result = removeDiacriticalMarks(\"Ça va très bien, n'est-ce pas?\");\n * console.log(result); // \"Ca va tres bien, n'est-ce pas?\"\n */\nexport const removeDiacriticalMarks = (value: string): string => {\n if (!value) {\n return '';\n }\n\n return value.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n};\n\n/**\n * Capitalize the first letter of a string.\n * @param str - The input string\n * @returns The string with the first character uppercased\n */\nexport const capitalizeFirst = (str: string): string => {\n if (!str) return str;\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\n/**\n * Count the number of words in a text string.\n * Words are separated by whitespace.\n * @param text - The text to count words in\n * @returns The number of words found\n */\nexport const countWords = (text: string): number => {\n if (!text || text.trim().length === 0) return 0;\n return text.split(/\\s+/).filter((word) => word.length > 0).length;\n};\n\n/**\n * Truncate a string to a maximum length, adding an ellipsis if truncated.\n * @param str - The string to truncate\n * @param maxLength - Maximum length before truncation\n * @param ellipsis - The ellipsis string to append (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (str: string, maxLength: number, ellipsis = '...'): string => {\n if (!str || str.length <= maxLength) return str;\n return str.slice(0, maxLength - ellipsis.length) + ellipsis;\n};\n\n/**\n * Converts a string to kebab-case.\n * Handles camelCase, PascalCase, snake_case, and space-separated strings.\n *\n * @example\n * toKebabCase(\"myComponentName\") // → \"my-component-name\"\n * toKebabCase(\"XMLParser\") // → \"xml-parser\"\n */\nexport const toKebabCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase();\n};\n\n/**\n * Converts a string to camelCase.\n * Handles kebab-case, snake_case, PascalCase, and space-separated strings.\n *\n * @example\n * toCamelCase(\"my-component-name\") // → \"myComponentName\"\n * toCamelCase(\"my_snake_case\") // → \"mySnakeCase\"\n */\nexport const toCamelCase = (value?: string | null): string => {\n if (isBlank(value)) return '';\n return value\n .trim()\n .replace(/[-_\\s]+(.)/g, (_, char: string) => char.toUpperCase())\n .replace(/^[A-Z]/, (char) => char.toLowerCase());\n};\n\n/**\n * Converts a string to a URL-safe slug.\n * Removes diacritical marks, lowercases, replaces non-alphanumeric characters with hyphens,\n * and strips leading/trailing hyphens.\n *\n * @param value - The input string to slugify\n * @returns The slugified string\n *\n * @example\n * slugify(\"Héllo Wörld!\") // → \"hello-world\"\n * slugify(\" My Blog Post \") // → \"my-blog-post\"\n */\nexport const slugify = (value: string): string => {\n if (!value) return '';\n return removeDiacriticalMarks(value)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n};\n\n/**\n * Parses an optional comma-separated string into a trimmed, non-blank string array.\n */\nexport const parseCommaSeparatedList = (raw?: string | null): string[] => {\n if (isBlank(raw)) {\n return [];\n }\n\n return raw\n .split(',')\n .map((s: string) => s.trim())\n .filter(isNotBlank);\n};\n","import { replaceStringParameters } from \"@rjsf/utils\";\nimport { i18n } from \"i18next\";\nimport { isBlank } from \"../../utils/string.utils\";\n\n/** Maps RJSF TranslatableString values to our i18n keys (rjsf namespace). */\nexport const RJSF_STRING_TO_I18N_KEY: Record<string, string> = {\n \"Add Item\": \"rjsf:addItem\",\n Add: \"rjsf:add\",\n Copy: \"rjsf:copy\",\n \"Move down\": \"rjsf:moveDown\",\n \"Move up\": \"rjsf:moveUp\",\n Remove: \"rjsf:remove\",\n \"clear input\": \"rjsf:clear\",\n \"%1 Key\": \"rjsf:keyLabel\", // TODO: RJSF-001 find a way to dynamicaly resolve the rjsf-i18n.utils \"%1 Key\" pattern\n Yes: \"rjsf:yes\",\n No: \"rjsf:no\",\n Errors: \"rjsf:errors\",\n};\n\ninterface TranslateStringArgs {\n i18n: i18n;\n stringToTranslate: string;\n params?: string[];\n}\n\nexport const translateRjsfString = ({ stringToTranslate, params, i18n }: TranslateStringArgs): string => {\n const i18nKey = RJSF_STRING_TO_I18N_KEY[stringToTranslate];\n if (isBlank(i18nKey)) {\n // eslint-disable-next-line no-console\n console.warn(`[translateRjsfString] RJSF i18n: missing key: \"${stringToTranslate}\"`);\n }\n\n const translated = i18nKey ? i18n.t(i18nKey) : stringToTranslate;\n return replaceStringParameters(translated, params);\n};\n","import type { WidgetProps } from \"@rjsf/utils\";\nimport { isBlank } from \"../../utils/string.utils\";\nimport { isNullish } from \"../../utils/types.utils\";\n\n/**\n * Returns true when the widget has at least one validation error from RJSF.\n *\n * @param rawErrors - The rawErrors from WidgetProps (often `unknown`).\n * @returns true if rawErrors is a non-empty array.\n */\nexport const hasRjsfErrors = (rawErrors: unknown): boolean => Array.isArray(rawErrors) && rawErrors.length > 0;\n\nexport interface GetRjsfDisplayLabelArgs {\n label?: string;\n required?: boolean;\n hideLabel?: boolean;\n}\n\n/**\n * Returns the label to display for an RJSF widget, with optional required asterisk and\n * hideLabel support.\n *\n * @param label - The label to display for the widget.\n * @param required - Whether the field is required.\n * @param hideLabel - Whether to hide the label.\n *\n * @returns The label to display for the widget.\n */\nexport const getRjsfDisplayLabel = ({ label, required, hideLabel }: GetRjsfDisplayLabelArgs): string | undefined => {\n if (hideLabel) return undefined;\n if (isBlank(label)) return undefined;\n\n const requiredSuffix = required ? \" *\" : \"\";\n return `${label}${requiredSuffix}`;\n};\n\nconst HEX_COLOR_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;\n\n/**\n * Reads the `labelColor` from a widget/field `ui:options` object.\n *\n * @param options - The `ui:options` object (e.g. WidgetProps.options or uiSchema[\"ui:options\"]).\n * @returns A validated hex color string (#RGB or #RRGGBB), or undefined when missing/invalid (default color).\n */\nexport const getRjsfLabelColor = (options?: Record<string, unknown> | null): string | undefined => {\n const color = options?.labelColor;\n if (typeof color !== \"string\") return undefined;\n const trimmed = color.trim();\n return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : undefined;\n};\n\ninterface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumOptionDisplay = { label: string; value: string };\n\nconst toEnumOptionDisplay = (o: EnumOption): EnumOptionDisplay => ({\n label: o?.label ?? String(o?.value ?? \"\"),\n value: String(o?.value ?? \"\"),\n});\n\nexport const mapEnumOptions = (options: WidgetProps[\"options\"]): EnumOptionDisplay[] => {\n const enumOptions = options?.enumOptions as EnumOption[] | undefined;\n if (!Array.isArray(enumOptions)) return [];\n return enumOptions.map(toEnumOptionDisplay);\n};\n\n/**\n * Converts a value to display string, or empty string when null/undefined.\n */\nexport const toStringOrEmpty = (value: unknown): string => (isNullish(value) ? \"\" : String(value));\n\n/**\n * Converts a value to display string, or undefined when null/undefined (e.g. for select placeholder).\n */\nexport const toStringOrUndefined = (value: unknown): string | undefined =>\n isNullish(value) ? undefined : String(value);\n\nexport interface GetRjsfTextChangeValueArgs {\n text: string;\n emptyValue?: unknown;\n}\n\n/**\n * Returns the value to pass to onChange for a text input.\n *\n * @param args.text - The current text input value.\n * @param args.emptyValue - The value to use when text is empty (e.g. options.emptyValue from WidgetProps).\n * @returns emptyValue when text is empty, otherwise text.\n */\nexport const getRjsfTextChangeValue = ({ text, emptyValue }: GetRjsfTextChangeValueArgs): unknown => {\n if (text === \"\") return emptyValue;\n return text;\n};\n\nexport const parseDateOrNull = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const date = new Date(value);\n const isValid = !Number.isNaN(date.getTime());\n return isValid ? date : null;\n};\n\n/** Returns YYYY-MM-DD for JSON Schema \"date\" format (form storage). */\nexport const dateToDateOnlyString = (date: Date): string => {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n};\n\nconst DATE_ONLY_REGEX = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parses a date-only or full ISO string into a Date at local midnight (for date picker display). */\nexport const parseDateOnlyToLocalDate = (value?: string): Date | null => {\n if (isBlank(value)) return null;\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n if (!DATE_ONLY_REGEX.test(dateOnly)) return null;\n\n const [y, m, d] = dateOnly.split(\"-\").map(Number);\n return new Date(y, m - 1, d);\n};\n\n/** Extracts date-only YYYY-MM-DD for display from a full ISO or date-only string. */\nexport const formatDateOnlyForDisplay = (value?: string): string => {\n if (isBlank(value)) return \"\";\n\n const trimmed = String(value).trim();\n const dateOnly = trimmed.slice(0, 10);\n return DATE_ONLY_REGEX.test(dateOnly) ? dateOnly : \"\";\n};\n\n/** Formats a full ISO or date string for date-time display (locale string). */\nexport const formatDateTimeForDisplay = (value?: string): string => {\n const date = parseDateOrNull(value);\n return date ? date.toLocaleString() : \"\";\n};\n\ntype FormLayoutContext = {\n labelCol?: { span: number };\n wrapperCol?: { span: number };\n};\n\nconst DEFAULT_SPAN = 24;\n\nexport const getFormLayoutCols = (formContext?: unknown): FormLayoutContext => {\n if (isNullish(formContext)) {\n return {\n labelCol: { span: DEFAULT_SPAN },\n wrapperCol: { span: DEFAULT_SPAN },\n };\n }\n\n const { labelCol, wrapperCol } = formContext as FormLayoutContext;\n return {\n labelCol: labelCol ?? { span: DEFAULT_SPAN },\n wrapperCol: wrapperCol ?? { span: DEFAULT_SPAN },\n };\n};\n","import { isBlank } from \"../../utils/string.utils\";\nimport { useTranslation } from \"react-i18next\";\nimport type { LocalizedFormSchema, MetaFormSchema } from \"./rjsf-i18n-types\";\n\nconst DEFAULT_LANGUAGE = \"fr\";\n\n// i18n.language can be 'fr-CA', so we take the first part\nconst parseSimplifiedLangCode = (language?: string): string => {\n if (isBlank(language)) return DEFAULT_LANGUAGE;\n return language.split(\"-\")[0];\n};\n\nconst getFallbackLanguage = (metaFormSchema: MetaFormSchema): string => {\n return Object.keys(metaFormSchema.i18n)[0] ?? DEFAULT_LANGUAGE;\n};\n\nexport const useLocalizedForm = (metaFormSchema: MetaFormSchema): LocalizedFormSchema => {\n const { i18n } = useTranslation();\n const language = parseSimplifiedLangCode(i18n.language);\n\n if (metaFormSchema.i18n[language]) {\n return metaFormSchema.i18n[language];\n }\n\n const fallbackLanguage = getFallbackLanguage(metaFormSchema);\n // eslint-disable-next-line no-console\n console.warn(\n `Language ${language} not found in metaFormSchema ${metaFormSchema.id}. Falling back to ${fallbackLanguage}.`\n );\n\n return metaFormSchema.i18n[fallbackLanguage];\n};\n","import { customizeValidator, type Localizer } from \"@rjsf/validator-ajv8\";\nimport localizer from \"ajv-i18n\";\n\nconst customFormats = {\n \"phone-america\": /^\\(?\\d{3}\\)?[\\s-]?\\d{3}[\\s-]?\\d{4}$/,\n};\n\ntype ValidationLanguages = keyof typeof localizer;\n\n/**\n * Builds an AJV validator for the given language. Pure (no React) so the caller\n * can memoize it. Prefer this + `useMemo` in the React layer: RJSF rebuilds its\n * `schemaUtils`/`retrieveSchema` cache from the validator identity, so a fresh\n * validator on every render recompiles every conditional (`allOf`/`if`/`then`)\n * subschema on each keystroke. See `RjsfPaperRenderer` for the memoized usage.\n */\nexport const createRjsfValidator = (language: string) => {\n const appLanguage = language as ValidationLanguages;\n const localizerFn: Localizer = localizer[appLanguage] ?? localizer.en;\n\n return customizeValidator({ customFormats }, localizerFn);\n};\n\n/**\n * Backward-compatible wrapper around {@link createRjsfValidator}. Not memoized —\n * returns a new validator on every call. Prefer `createRjsfValidator` memoized in\n * the React layer.\n */\nexport const useRjsfValidator = (language: string) => createRjsfValidator(language);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lichens-innovation/ts-common",
3
- "version": "1.22.0",
3
+ "version": "1.24.0",
4
4
  "description": "Reusable generic typescript utilities, types, constants, helpers",
5
5
  "keywords": [
6
6
  "typescript",