ui-soxo-bootstrap-core 2.6.40-dev.16 → 2.6.40-dev.18

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.
@@ -122,17 +122,20 @@ export const ExportReactCSV = ({
122
122
  const pdfDoc = await PDFDocument.create();
123
123
  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
124
124
  const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
125
- const pageSize = [842, 595];
126
- const margin = 30;
127
- const rowHeight = 20;
128
- const titleHeight = 38;
129
- const fontSize = 8;
130
- const headerFontSize = 8;
131
125
  const headersToUse = headers?.length
132
126
  ? headers
133
127
  : Object.keys(csvData?.[0] || {}).map((key) => ({ label: key, key }));
128
+ const pageSize = headersToUse.length > 12 ? [1190, 842] : [842, 595];
129
+ const margin = 30;
130
+ const minRowHeight = 20;
131
+ const titleHeight = 38;
132
+ const fontSize = headersToUse.length > 12 ? 7 : 8;
133
+ const headerFontSize = headersToUse.length > 12 ? 7 : 8;
134
+ const lineHeight = fontSize + 2;
135
+ const headerLineHeight = headerFontSize + 2;
136
+ const cellPaddingX = 4;
137
+ const cellPaddingY = 5;
134
138
  const availableWidth = pageSize[0] - margin * 2;
135
- const columnWidth = headersToUse.length ? availableWidth / headersToUse.length : availableWidth;
136
139
  let page;
137
140
  let y;
138
141
 
@@ -142,15 +145,173 @@ export const ExportReactCSV = ({
142
145
  return String(value);
143
146
  };
144
147
 
145
- const fitText = (text, width, textFont, size) => {
146
- const sanitizedText = sanitizeValue(text);
147
- if (textFont.widthOfTextAtSize(sanitizedText, size) <= width) return sanitizedText;
148
+ const getTextWidth = (text, textFont, size) => textFont.widthOfTextAtSize(sanitizeValue(text), size);
149
+
150
+ const splitLongWord = (word, maxWidth, textFont, size) => {
151
+ const characters = Array.from(word);
152
+ const chunks = [];
153
+ let chunk = '';
154
+
155
+ characters.forEach((character) => {
156
+ const nextChunk = `${chunk}${character}`;
157
+
158
+ if (chunk && getTextWidth(nextChunk, textFont, size) > maxWidth) {
159
+ chunks.push(chunk);
160
+ chunk = character;
161
+ } else {
162
+ chunk = nextChunk;
163
+ }
164
+ });
165
+
166
+ if (chunk) chunks.push(chunk);
167
+ return chunks;
168
+ };
169
+
170
+ const wrapText = (value, maxWidth, textFont, size) => {
171
+ const text = sanitizeValue(value);
172
+ const usableWidth = Math.max(maxWidth, 1);
173
+ const paragraphs = text.split(/\r?\n/);
174
+
175
+ return paragraphs.flatMap((paragraph) => {
176
+ const words = paragraph.split(/\s+/).filter(Boolean);
177
+ const lines = [];
178
+ let line = '';
179
+
180
+ if (!words.length) return [''];
181
+
182
+ words.forEach((word) => {
183
+ if (getTextWidth(word, textFont, size) > usableWidth) {
184
+ if (line) {
185
+ lines.push(line);
186
+ line = '';
187
+ }
188
+
189
+ const chunks = splitLongWord(word, usableWidth, textFont, size);
190
+ chunks.forEach((chunk, chunkIndex) => {
191
+ if (chunkIndex === chunks.length - 1) {
192
+ line = chunk;
193
+ } else {
194
+ lines.push(chunk);
195
+ }
196
+ });
197
+
198
+ return;
199
+ }
200
+
201
+ const nextLine = line ? `${line} ${word}` : word;
202
+ if (getTextWidth(nextLine, textFont, size) <= usableWidth) {
203
+ line = nextLine;
204
+ } else {
205
+ if (line) lines.push(line);
206
+ line = word;
207
+ }
208
+ });
209
+
210
+ if (line) lines.push(line);
211
+ return lines;
212
+ });
213
+ };
214
+
215
+ const getColumnWidths = () => {
216
+ if (!headersToUse.length) return [availableWidth];
217
+
218
+ const sampleRows = (csvData || []).slice(0, 100);
219
+ const minWidths = [];
220
+ const preferredWidths = headersToUse.map((header, index) => {
221
+ const label = sanitizeValue(header.label);
222
+ const compactColumn = label === '#' || ['slno', 'sl no'].includes(label.toLowerCase());
223
+ const minWidth = compactColumn ? 28 : 36;
224
+ const values = [label, ...sampleRows.map((row) => sanitizeValue(row[header.key]))];
225
+ const longestValueWidth = values.reduce((maxWidth, value) => Math.max(maxWidth, getTextWidth(value, font, fontSize)), 0);
226
+ const longestWordWidth = values.reduce((maxWidth, value) => {
227
+ const words = value.split(/\s+/).filter(Boolean);
228
+ return words.reduce((wordMaxWidth, word) => Math.max(wordMaxWidth, getTextWidth(word, font, fontSize)), maxWidth);
229
+ }, 0);
230
+ const headerWidth = getTextWidth(label, boldFont, headerFontSize);
231
+ const preferredWidth = Math.max(
232
+ minWidth,
233
+ Math.min(longestValueWidth + cellPaddingX * 2, compactColumn ? 48 : 130),
234
+ Math.min(longestWordWidth + cellPaddingX * 2, compactColumn ? 48 : 95),
235
+ headerWidth + cellPaddingX * 2
236
+ );
237
+
238
+ minWidths[index] = minWidth;
239
+ return preferredWidth;
240
+ });
241
+
242
+ const minTotal = minWidths.reduce((total, width) => total + width, 0);
243
+ const preferredTotal = preferredWidths.reduce((total, width) => total + width, 0);
244
+
245
+ if (preferredTotal <= availableWidth) {
246
+ const extraWidth = availableWidth - preferredTotal;
247
+ return preferredWidths.map((width) => width + (extraWidth * width) / preferredTotal);
248
+ }
148
249
 
149
- let fittedText = sanitizedText;
150
- while (fittedText.length && textFont.widthOfTextAtSize(`${fittedText}...`, size) > width) {
151
- fittedText = fittedText.slice(0, -1);
250
+ if (minTotal >= availableWidth) {
251
+ return minWidths.map((width) => (width * availableWidth) / minTotal);
152
252
  }
153
- return fittedText ? `${fittedText}...` : '';
253
+
254
+ const flexibleTotal = preferredWidths.reduce((total, width, index) => total + (width - minWidths[index]), 0);
255
+ const extraWidth = availableWidth - minTotal;
256
+
257
+ return preferredWidths.map((width, index) => {
258
+ const flexibleWidth = width - minWidths[index];
259
+ return minWidths[index] + (extraWidth * flexibleWidth) / flexibleTotal;
260
+ });
261
+ };
262
+
263
+ const columnWidths = getColumnWidths();
264
+
265
+ const getMaxLineCount = (cellLines) => Math.max(1, ...cellLines.map((lines) => lines.length));
266
+
267
+ const getRowHeight = (cellLines, rowLineHeight) =>
268
+ Math.max(minRowHeight, getMaxLineCount(cellLines) * rowLineHeight + cellPaddingY * 2);
269
+
270
+ const drawTableRow = (cellLines, rowHeight, options) => {
271
+ let x = margin;
272
+
273
+ cellLines.forEach((lines, index) => {
274
+ const width = columnWidths[index] || availableWidth;
275
+ page.drawRectangle({
276
+ x,
277
+ y: y - rowHeight,
278
+ width,
279
+ height: rowHeight,
280
+ color: options.backgroundColor,
281
+ borderColor: options.borderColor,
282
+ borderWidth: 0.5,
283
+ });
284
+
285
+ lines.forEach((line, lineIndex) => {
286
+ page.drawText(line, {
287
+ x: x + cellPaddingX,
288
+ y: y - cellPaddingY - options.fontSize - lineIndex * options.lineHeight,
289
+ size: options.fontSize,
290
+ font: options.font,
291
+ color: options.textColor,
292
+ });
293
+ });
294
+
295
+ x += width;
296
+ });
297
+
298
+ y -= rowHeight;
299
+ };
300
+
301
+ const headerCellLines = headersToUse.map((header, index) =>
302
+ wrapText(header.label, columnWidths[index] - cellPaddingX * 2, boldFont, headerFontSize)
303
+ );
304
+ const headerRowHeight = getRowHeight(headerCellLines, headerLineHeight);
305
+
306
+ const drawHeader = () => {
307
+ drawTableRow(headerCellLines, headerRowHeight, {
308
+ backgroundColor: rgb(0.92, 0.94, 0.96),
309
+ borderColor: rgb(0.72, 0.75, 0.78),
310
+ font: boldFont,
311
+ fontSize: headerFontSize,
312
+ lineHeight: headerLineHeight,
313
+ textColor: rgb(0.1, 0.1, 0.1),
314
+ });
154
315
  };
155
316
 
156
317
  const addPage = () => {
@@ -166,57 +327,67 @@ export const ExportReactCSV = ({
166
327
  });
167
328
 
168
329
  y -= titleHeight;
330
+ drawHeader();
331
+ };
169
332
 
170
- headersToUse.forEach((header, index) => {
171
- const x = margin + index * columnWidth;
172
- page.drawRectangle({
173
- x,
174
- y: y - 5,
175
- width: columnWidth,
176
- height: rowHeight,
177
- color: rgb(0.92, 0.94, 0.96),
178
- borderColor: rgb(0.72, 0.75, 0.78),
179
- borderWidth: 0.5,
180
- });
181
- page.drawText(fitText(header.label, columnWidth - 8, boldFont, headerFontSize), {
182
- x: x + 4,
183
- y: y + 1,
184
- size: headerFontSize,
185
- font: boldFont,
186
- color: rgb(0.1, 0.1, 0.1),
333
+ const drawDataRow = (row) => {
334
+ const rowFont = row.isSummaryRow ? boldFont : font;
335
+ const cellLines = headersToUse.map((header, index) =>
336
+ wrapText(row[header.key], columnWidths[index] - cellPaddingX * 2, rowFont, fontSize)
337
+ );
338
+ let remainingCellLines = cellLines.map((lines) => (lines.length ? lines : ['']));
339
+ const fullPageDataHeight = pageSize[1] - margin * 2 - titleHeight - headerRowHeight;
340
+
341
+ while (remainingCellLines.some((lines) => lines.length)) {
342
+ const rowHeight = getRowHeight(remainingCellLines, lineHeight);
343
+ const availableHeight = y - margin;
344
+
345
+ if (rowHeight > availableHeight && rowHeight <= fullPageDataHeight) {
346
+ addPage();
347
+ continue;
348
+ }
349
+
350
+ const maxLinesOnPage =
351
+ rowHeight > availableHeight
352
+ ? Math.max(1, Math.floor((availableHeight - cellPaddingY * 2) / lineHeight))
353
+ : getMaxLineCount(remainingCellLines);
354
+ const segmentCellLines = remainingCellLines.map((lines) => lines.slice(0, maxLinesOnPage));
355
+ const segmentHeight = getRowHeight(segmentCellLines, lineHeight);
356
+
357
+ if (segmentHeight > availableHeight && availableHeight < fullPageDataHeight) {
358
+ addPage();
359
+ continue;
360
+ }
361
+
362
+ drawTableRow(segmentCellLines, segmentHeight, {
363
+ backgroundColor: row.isSummaryRow ? rgb(0.97, 0.97, 0.97) : rgb(1, 1, 1),
364
+ borderColor: rgb(0.82, 0.84, 0.86),
365
+ font: rowFont,
366
+ fontSize,
367
+ lineHeight,
368
+ textColor: rgb(0.12, 0.12, 0.12),
187
369
  });
188
- });
189
370
 
190
- y -= rowHeight;
371
+ remainingCellLines = remainingCellLines.map((lines) => lines.slice(maxLinesOnPage));
372
+ if (remainingCellLines.some((lines) => lines.length)) addPage();
373
+ }
191
374
  };
192
375
 
193
376
  addPage();
194
377
 
195
378
  (csvData || []).forEach((row) => {
196
- if (y < margin + rowHeight) addPage();
379
+ drawDataRow(row);
380
+ });
197
381
 
198
- headersToUse.forEach((header, index) => {
199
- const x = margin + index * columnWidth;
200
- page.drawRectangle({
201
- x,
202
- y: y - 5,
203
- width: columnWidth,
204
- height: rowHeight,
205
- color: row.isSummaryRow ? rgb(0.97, 0.97, 0.97) : rgb(1, 1, 1),
206
- borderColor: rgb(0.82, 0.84, 0.86),
207
- borderWidth: 0.5,
208
- });
209
- page.drawText(fitText(row[header.key], columnWidth - 8, row.isSummaryRow ? boldFont : font, fontSize), {
210
- x: x + 4,
211
- y: y + 1,
212
- size: fontSize,
213
- font: row.isSummaryRow ? boldFont : font,
214
- color: rgb(0.12, 0.12, 0.12),
215
- });
382
+ if (!csvData?.length) {
383
+ page.drawText('No records found', {
384
+ x: margin,
385
+ y: y + 1,
386
+ size: fontSize,
387
+ font,
388
+ color: rgb(0.12, 0.12, 0.12),
216
389
  });
217
-
218
- y -= rowHeight;
219
- });
390
+ }
220
391
 
221
392
  const pdfBytes = await pdfDoc.save();
222
393
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });
@@ -354,9 +354,9 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
354
354
  <Search placeholder="Search menus & sub-menus…" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={onSearch} />
355
355
 
356
356
  <Space size="small">
357
- <Button onClick={getRecords} size={'small'} type="default">
357
+ {/* <Button onClick={getRecords} size={'small'} type="default">
358
358
  <ReloadOutlined />
359
- </Button>
359
+ </Button> */}
360
360
 
361
361
  <Switch checked={dragMode} onChange={toggleDragMode} checkedChildren="Order On" unCheckedChildren="Order Off" />
362
362
 
@@ -489,7 +489,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
489
489
  );
490
490
  };
491
491
  /* -----------------------------------------------------------------------
492
- PANEL ACTIONS
492
+ PANEL ACTIONS
493
493
  ------------------------------------------------------------------------ */
494
494
  function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord) {
495
495
  return (
@@ -597,11 +597,7 @@ function NestedMenu({
597
597
  }
598
598
 
599
599
  return (
600
- <Collapse
601
- accordion={!searchActive}
602
- activeKey={openKeys}
603
- onChange={(keys) => setOpenKeys(Array.isArray(keys) ? keys : keys ? [keys] : [])}
604
- >
600
+ <Collapse accordion={!searchActive} activeKey={openKeys} onChange={(keys) => setOpenKeys(Array.isArray(keys) ? keys : keys ? [keys] : [])}>
605
601
  {localItems.map((child, index) => (
606
602
  <Panel
607
603
  key={child.id}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.6.40-dev.16",
3
+ "version": "2.6.40-dev.18",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"