datastake-daf 0.6.302 → 0.6.304

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.
@@ -525,404 +525,8 @@ const PdfForm = ({
525
525
 
526
526
  const organizedForm = useMemo(() => organizeFormByHeaders(form), [form]);
527
527
 
528
- // Constants for height calculations (same as PdfView)
529
- const PAGE_HEIGHT = 1587;
530
- const FOOTER_HEIGHT = 70;
531
- const HEADER_HEIGHT = 100;
532
-
533
- // Detect if running in headless/Puppeteer environment (more conservative detection)
534
- const isPuppeteerEnvironment = () => {
535
- if (typeof window === 'undefined') return false;
536
-
537
- // Only trigger on explicit URL parameters (most reliable)
538
- return (
539
- window.location.search.includes('puppeteer=true') ||
540
- window.location.search.includes('headless=true') ||
541
- window.location.search.includes('pdf=true') ||
542
- // Only very specific headless browser indicators
543
- (window.navigator.webdriver === true && window.navigator.userAgent.includes('HeadlessChrome'))
544
- );
545
- };
546
-
547
- // Debug function to log detection results (can be removed later)
548
- const debugEnvironment = () => {
549
- if (typeof window !== 'undefined' && window.console) {
550
- console.log('Environment Detection:', {
551
- isPuppeteer: isPuppeteerEnvironment(),
552
- userAgent: window.navigator.userAgent,
553
- webdriver: window.navigator.webdriver,
554
- resizeObserver: !!window.ResizeObserver,
555
- urlParams: window.location.search
556
- });
557
- }
558
- };
559
-
560
- // Balanced height estimation that prevents over-splitting while ensuring content fits
561
- const estimateTreeNodeHeight = (key, config, value, level = 0) => {
562
- const isPuppeteer = isPuppeteerEnvironment();
563
- // Call debug only once to avoid spam
564
- if (level === 0 && key === Object.keys(form)[0]) {
565
- debugEnvironment();
566
- }
567
-
568
- // More accurate base heights that align better with PdfView's ResizeObserver measurements
569
- const baseHeight = isPuppeteer ? 48 : 38; // More accurate base heights to match actual rendering
570
- const indentHeight = level * (isPuppeteer ? 4 : 3.5); // More accurate indent space
571
- let totalHeight = baseHeight + indentHeight;
572
-
573
- // Reasonable type-based adjustments based on actual rendered heights
574
- if (config?.type === 'header') {
575
- totalHeight += isPuppeteer ? 25 : 18; // Headers with reasonable padding
576
- } else if (config?.type === 'textarea') {
577
- totalHeight += isPuppeteer ? 60 : 45; // Textareas with multiline content
578
- } else if (config?.type === 'dataLink' || config?.type === 'dataLinkGroup') {
579
- totalHeight += isPuppeteer ? 35 : 25; // Data links with formatted content
580
- } else if (config?.type === 'groupInputs') {
581
- totalHeight += isPuppeteer ? 45 : 35; // Group inputs with structured layout
582
- } else if (config?.type === 'ajaxSubGroup') {
583
- totalHeight += isPuppeteer ? 50 : 40; // Ajax content with loading states
584
- }
585
-
586
- // More accurate content height estimation that better matches actual DOM measurements
587
- if (value && typeof value === 'string') {
588
- const cleanValue = value.trim();
589
- if (cleanValue.length > 150) {
590
- // Long text: be more generous with line estimates
591
- const estimatedLines = Math.ceil(cleanValue.length / 45); // Shorter assumed line length
592
- const lineHeight = isPuppeteer ? 20 : 18; // Slightly taller line height
593
- totalHeight += estimatedLines * lineHeight;
594
- } else if (cleanValue.length > 60) {
595
- // Medium text: likely spans 2-3 lines
596
- totalHeight += isPuppeteer ? 35 : 28;
597
- } else if (cleanValue.length > 15) {
598
- // Short-medium text: single to double line
599
- totalHeight += isPuppeteer ? 18 : 14;
600
- } else if (cleanValue.length > 0) {
601
- // Very short text: single line with padding
602
- totalHeight += isPuppeteer ? 12 : 10;
603
- }
604
- }
605
-
606
- // Add height for children recursively with balanced multiplier
607
- if (config?.inputs) {
608
- const childKeys = Object.keys(config.inputs)
609
- .filter(childKey => {
610
- const childConfig = config.inputs[childKey];
611
- // Check showIf condition
612
- if (childConfig?.showIf && !evaluateShowIfCondition(childConfig.showIf, data)) {
613
- return false;
614
- }
615
- return true;
616
- })
617
- .sort((a, b) => {
618
- const positionA = config.inputs[a]?.position || 0;
619
- const positionB = config.inputs[b]?.position || 0;
620
- return positionA - positionB;
621
- });
622
-
623
- let childrenHeight = 0;
624
- childKeys.forEach(childKey => {
625
- const childConfig = config.inputs[childKey];
626
- const childValue = value?.[childKey] || data?.[childKey];
627
- childrenHeight += estimateTreeNodeHeight(childKey, childConfig, childValue, level + 1);
628
- });
629
-
630
- // More accurate multiplier that accounts for spacing and margins in actual DOM
631
- totalHeight += childrenHeight * (isPuppeteer ? 1.25 : 1.15);
632
- }
633
-
634
- // Handle array/repeated content with accurate estimates
635
- if (Array.isArray(value) && value.length > 0) {
636
- value.forEach(itemValue => {
637
- const itemHeight = estimateTreeNodeHeight(key, config, itemValue, level);
638
- totalHeight += itemHeight * (isPuppeteer ? 1.1 : 1.05); // Minimal buffer
639
- });
640
- }
641
-
642
- // Minimal buffer for deep nesting
643
- if (level > 3) {
644
- totalHeight *= (isPuppeteer ? 1.08 : 1.05);
645
- }
646
-
647
- return Math.ceil(totalHeight);
648
- };
649
-
650
- // Helper function to split section based on height constraints (aligned with PdfView calculations)
651
- const createHeightConstrainedSections = (sectionKey, section) => {
652
- const isPuppeteer = isPuppeteerEnvironment();
653
-
654
- // Align with PdfView but use a more conservative limit to prevent large margin calculations
655
- // PdfView uses PAGE_HEIGHT - 30 - FOOTER_HEIGHT - HEADER_HEIGHT = 1387px
656
- // We'll use 85% of that to ensure sections fit comfortably and don't trigger large margins
657
- const PDFVIEW_MAX_HEIGHT = PAGE_HEIGHT - 30 - FOOTER_HEIGHT - HEADER_HEIGHT; // 1387px
658
- const MAX_SECTION_HEIGHT = Math.floor(PDFVIEW_MAX_HEIGHT * 0.85); // ~1179px - more conservative
659
- const MIN_SECTION_HEIGHT = 300; // Slightly larger minimum to ensure meaningful content
660
- const subSections = [];
661
-
662
- // Get all top-level items in the section with detailed analysis
663
- const topLevelItems = Object.keys(section)
664
- .filter(key => !(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle'))
665
- .map(key => ({
666
- key,
667
- config: section[key],
668
- estimatedHeight: estimateTreeNodeHeight(key, section[key], data?.[key]),
669
- canSplit: section[key]?.inputs && Object.keys(section[key].inputs).length > 3 // Only split if sufficient children
670
- }))
671
- .sort((a, b) => (a.config?.position || 0) - (b.config?.position || 0));
672
-
673
- // If the entire section fits, don't split it
674
- const totalSectionHeight = topLevelItems.reduce((sum, item) => sum + item.estimatedHeight, 0);
675
-
676
- // Add detailed debugging for height calculations
677
- if (typeof window !== 'undefined' && window.console) {
678
- console.log(`Section ${sectionKey}:`, {
679
- totalEstimatedHeight: totalSectionHeight,
680
- maxAllowedHeight: MAX_SECTION_HEIGHT,
681
- pdfViewMaxHeight: PDFVIEW_MAX_HEIGHT,
682
- utilizationPercentage: Math.round((totalSectionHeight / MAX_SECTION_HEIGHT) * 100) + '%',
683
- shouldSplit: totalSectionHeight > MAX_SECTION_HEIGHT,
684
- itemCount: topLevelItems.length,
685
- itemSummary: topLevelItems.map(item => ({
686
- key: item.key,
687
- height: item.estimatedHeight,
688
- type: item.config?.type
689
- }))
690
- });
691
- }
692
-
693
- if (totalSectionHeight <= MAX_SECTION_HEIGHT) {
694
- return [{
695
- key: sectionKey,
696
- section: section,
697
- title: section.label
698
- }];
699
- }
700
-
701
- let currentSubSection = {
702
- ...section,
703
- };
704
-
705
- // Remove all items from the base section structure
706
- Object.keys(section).forEach(key => {
707
- if (!(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle')) {
708
- delete currentSubSection[key];
709
- }
710
- });
711
-
712
- let currentHeight = isPuppeteer ? 120 : 100; // More accurate base height including section headers and margins
713
- let subSectionIndex = 0;
714
-
715
- topLevelItems.forEach((item, index) => {
716
- const { key, config, estimatedHeight, canSplit } = item;
717
-
718
- // More conservative splitting thresholds to work better with PdfView
719
- const SPLIT_THRESHOLD = 0.65; // Split if item takes up more than 65% of available space
720
-
721
- // If a single item is large and can be split, consider splitting it
722
- if (estimatedHeight > MAX_SECTION_HEIGHT * SPLIT_THRESHOLD && canSplit && estimatedHeight > MIN_SECTION_HEIGHT * 1.5) {
723
- // Split this large item into smaller parts
724
- const childSplits = splitLargeItem(key, config, data?.[key], MAX_SECTION_HEIGHT * 0.6);
725
-
726
- childSplits.forEach((splitItem, splitIndex) => {
727
- // Check if current subsection has room - be more lenient about splitting
728
- if (currentHeight + splitItem.estimatedHeight > MAX_SECTION_HEIGHT &&
729
- currentHeight > MIN_SECTION_HEIGHT &&
730
- Object.keys(currentSubSection).length > 4) {
731
-
732
- // Save current sub-section if it has meaningful content
733
- subSections.push({
734
- key: `${sectionKey}_part_${subSectionIndex}`,
735
- section: { ...currentSubSection },
736
- title: section.label
737
- });
738
-
739
- // Start new sub-section
740
- currentSubSection = {
741
- id: section.id,
742
- label: section.label,
743
- position: section.position + subSectionIndex + 1,
744
- subTitle: section.subTitle
745
- };
746
- currentHeight = isPuppeteer ? 120 : 100;
747
- subSectionIndex++;
748
- }
749
-
750
- // Add split item to current sub-section
751
- currentSubSection[splitItem.key] = splitItem.config;
752
- currentHeight += splitItem.estimatedHeight;
753
- });
754
- } else {
755
- // Regular processing for items that fit or shouldn't be split
756
- // More lenient threshold - only split if really necessary and ensures meaningful sections
757
- const remainingItems = topLevelItems.length - index - 1;
758
- const shouldSplitSection = (currentHeight + estimatedHeight > MAX_SECTION_HEIGHT) &&
759
- (currentHeight > MIN_SECTION_HEIGHT * 0.8) && // More lenient minimum
760
- (Object.keys(currentSubSection).length > 3) && // Fewer items required
761
- (remainingItems > 0); // Ensure there are items left for next section
762
-
763
- if (shouldSplitSection) {
764
-
765
- // Save current sub-section
766
- subSections.push({
767
- key: `${sectionKey}_part_${subSectionIndex}`,
768
- section: { ...currentSubSection },
769
- title: section.label
770
- });
771
-
772
- // Start new sub-section
773
- currentSubSection = {
774
- id: section.id,
775
- label: section.label,
776
- position: section.position + subSectionIndex + 1,
777
- subTitle: section.subTitle
778
- };
779
- currentHeight = isPuppeteer ? 120 : 100;
780
- subSectionIndex++;
781
- }
782
-
783
- // Add item to current sub-section
784
- currentSubSection[key] = config;
785
- currentHeight += estimatedHeight;
786
- }
787
- });
788
-
789
- // Add the final sub-section if it has meaningful content
790
- const finalSectionKeys = Object.keys(currentSubSection).filter(key =>
791
- !(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle')
792
- );
793
-
794
- if (finalSectionKeys.length > 0) {
795
- const finalSubSection = {
796
- key: subSectionIndex === 0 ? sectionKey : `${sectionKey}_part_${subSectionIndex}`,
797
- section: currentSubSection,
798
- title: section.label
799
- };
800
-
801
- subSections.push(finalSubSection);
802
-
803
- // Debug final section creation
804
- if (typeof window !== 'undefined' && window.console) {
805
- console.log(`Final section ${finalSubSection.key}:`, {
806
- itemCount: finalSectionKeys.length,
807
- estimatedHeight: currentHeight,
808
- utilizationPercentage: Math.round((currentHeight / MAX_SECTION_HEIGHT) * 100) + '%',
809
- isWithinLimits: currentHeight <= MAX_SECTION_HEIGHT
810
- });
811
- }
812
- }
813
-
814
- // If we only have one section and it's not too large, return the original
815
- if (subSections.length === 1 && subSections[0].key === sectionKey) {
816
- return [{
817
- key: sectionKey,
818
- section: section,
819
- title: section.label
820
- }];
821
- }
822
-
823
- return subSections.length > 0 ? subSections : [{
824
- key: sectionKey,
825
- section: section,
826
- title: section.label
827
- }];
828
- };
829
-
830
- // Helper function to split large items at the child level (more intelligent splitting)
831
- const splitLargeItem = (parentKey, parentConfig, parentValue, maxHeight) => {
832
- if (!parentConfig?.inputs) {
833
- return [{ key: parentKey, config: parentConfig, estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue) }];
834
- }
835
-
836
- const childItems = Object.keys(parentConfig.inputs)
837
- .filter(childKey => {
838
- const childConfig = parentConfig.inputs[childKey];
839
- return !childConfig?.showIf || evaluateShowIfCondition(childConfig.showIf, data);
840
- })
841
- .map(childKey => ({
842
- key: childKey,
843
- config: parentConfig.inputs[childKey],
844
- estimatedHeight: estimateTreeNodeHeight(childKey, parentConfig.inputs[childKey], parentValue?.[childKey])
845
- }))
846
- .sort((a, b) => (a.config?.position || 0) - (b.config?.position || 0));
847
-
848
- // If total children height is not that large, don't split
849
- const totalChildrenHeight = childItems.reduce((sum, child) => sum + child.estimatedHeight, 0);
850
- if (totalChildrenHeight <= maxHeight * 1.1) { // More conservative threshold
851
- return [{ key: parentKey, config: parentConfig, estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue) }];
852
- }
853
-
854
- const splits = [];
855
- let currentSplit = {
856
- key: `${parentKey}_part_0`,
857
- config: {
858
- ...parentConfig,
859
- inputs: {}
860
- },
861
- estimatedHeight: 60 // More accurate base height for parent structure with styling
862
- };
863
- let splitIndex = 0;
864
- const minItemsPerSplit = 2; // Ensure at least 2 items per split to avoid tiny sections
865
-
866
- childItems.forEach((child, index) => {
867
- const isLastChild = index === childItems.length - 1;
868
- const currentSplitItemCount = Object.keys(currentSplit.config.inputs).length;
869
-
870
- // Only split if:
871
- // 1. Adding this child would exceed max height
872
- // 2. Current split has at least minimum items
873
- // 3. There are enough remaining items to make another meaningful split
874
- const remainingItems = childItems.length - index;
875
- const shouldSplit = (currentSplit.estimatedHeight + child.estimatedHeight > maxHeight) &&
876
- (currentSplitItemCount >= minItemsPerSplit) &&
877
- (remainingItems > minItemsPerSplit || isLastChild);
878
-
879
- if (shouldSplit) {
880
- // Save current split if it has content
881
- splits.push(currentSplit);
882
- splitIndex++;
883
-
884
- // Start new split
885
- currentSplit = {
886
- key: `${parentKey}_part_${splitIndex}`,
887
- config: {
888
- ...parentConfig,
889
- label: parentConfig.label, // Keep original label without part indicator
890
- inputs: {}
891
- },
892
- estimatedHeight: 60
893
- };
894
- }
895
-
896
- // Add child to current split
897
- currentSplit.config.inputs[child.key] = child.config;
898
- currentSplit.estimatedHeight += child.estimatedHeight;
899
- });
900
-
901
- // Add the final split if it has content
902
- if (Object.keys(currentSplit.config.inputs).length > 0) {
903
- splits.push(currentSplit);
904
- }
905
-
906
- // If we ended up with only one split, return original
907
- if (splits.length <= 1) {
908
- return [{ key: parentKey, config: parentConfig, estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue) }];
909
- }
910
-
911
- return splits;
912
- };
913
-
914
528
  const pdfConfig = useMemo(() => {
915
529
  const sections = [];
916
- const isPuppeteer = isPuppeteerEnvironment();
917
-
918
- // Apply Puppeteer-specific class to document body if needed
919
- if (typeof document !== 'undefined') {
920
- if (isPuppeteer) {
921
- document.body.setAttribute('data-puppeteer', 'true');
922
- } else {
923
- document.body.removeAttribute('data-puppeteer');
924
- }
925
- }
926
530
 
927
531
  Object.keys(organizedForm).forEach((sectionKey) => {
928
532
  const section = organizedForm[sectionKey];
@@ -931,32 +535,27 @@ const PdfForm = ({
931
535
  return;
932
536
  }
933
537
 
934
- // Create height-constrained sub-sections
935
- const subSections = createHeightConstrainedSections(sectionKey, section);
936
-
937
- subSections.forEach(({ key, section: subSection, title }) => {
938
- sections.push({
939
- render: () => (
940
- <div key={key} className={`pdf-form-section ${isPuppeteer ? 'puppeteer-mode' : ''}`}>
941
- <PdfFormContent
942
- form={{ [key]: { ...subSection, label: title } }}
943
- data={data}
944
- t={t}
945
- user={user}
946
- title={formName}
947
- source={source}
948
- version={version}
949
- getApiBaseUrl={getApiBaseUrl}
950
- getAppHeader={getAppHeader}
951
- app={app}
952
- />
953
- </div>
954
- ),
955
- style: {
956
- marginBottom: '20px',
957
- padding: '0 20px'
958
- }
959
- });
538
+ sections.push({
539
+ render: () => (
540
+ <div key={sectionKey} className="pdf-form-section">
541
+ <PdfFormContent
542
+ form={{ [sectionKey]: section }}
543
+ data={data}
544
+ t={t}
545
+ user={user}
546
+ title={formName}
547
+ source={source}
548
+ version={version}
549
+ getApiBaseUrl={getApiBaseUrl}
550
+ getAppHeader={getAppHeader}
551
+ app={app}
552
+ />
553
+ </div>
554
+ ),
555
+ style: {
556
+ marginBottom: '20px',
557
+ padding: '0 20px'
558
+ }
960
559
  });
961
560
  });
962
561
 
@@ -991,4 +590,4 @@ PdfForm.propTypes = {
991
590
  user: PropTypes.object,
992
591
  };
993
592
 
994
- export default PdfForm;
593
+ export default PdfForm;
@@ -59,24 +59,17 @@
59
59
  }
60
60
 
61
61
  .pdf-form-section {
62
- margin-bottom: 20px; // Balanced spacing between sections
63
- padding-bottom: 10px; // Balanced padding to avoid footers
62
+ margin-bottom: 40px;
64
63
  position: relative;
65
64
  width: 100%;
66
- min-height: 50px; // Ensure minimum content height
67
-
68
- // Prevent completely empty sections
69
- &:empty {
70
- display: none;
71
- }
72
65
 
73
66
  .section-header {
74
- margin-bottom: 12px;
75
- padding-bottom: 6px;
67
+ margin-bottom: 25px;
68
+ padding-bottom: 8px;
76
69
  border-bottom: 1px solid #ddd;
77
70
 
78
71
  h2 {
79
- margin: 0 0 4px 0;
72
+ margin: 0 0 5px 0;
80
73
  font-size: 16px;
81
74
  font-weight: 700;
82
75
  color: #333;
@@ -105,8 +98,7 @@
105
98
 
106
99
  .tree-node {
107
100
  position: relative;
108
- margin-bottom: 8px; // Reasonable spacing between tree nodes
109
- padding: 1px 0; // Minimal padding
101
+ margin-bottom: 8px;
110
102
 
111
103
  &.parent {
112
104
  &::after {
@@ -489,27 +481,13 @@
489
481
  }
490
482
 
491
483
  @media print {
492
- // Spacer to ensure content clears the floating header in print/PDF mode
493
- .pdf-form-section:first-of-type::before {
494
- content: "";
495
- display: block;
496
- height: 120px;
497
- margin-bottom: 12px;
498
- }
499
-
500
484
  .pdf-form-section {
501
- page-break-inside: avoid;
485
+ // page-break-inside: avoid;
502
486
  margin-bottom: 20px;
503
- padding-bottom: 10px; // Reasonable space before potential breaks
504
-
505
- // Prevent orphaned sections
506
- &:empty {
507
- display: none;
508
- }
509
487
  }
510
488
 
511
489
  .pdf-tree .tree-node {
512
- page-break-inside: avoid;
490
+ // page-break-inside: avoid;
513
491
 
514
492
  &::after,
515
493
  .tree-indent .indent-line::before,
@@ -519,52 +497,9 @@
519
497
  -webkit-print-color-adjust: exact;
520
498
  }
521
499
 
522
- // Force page breaks for deeply nested content in print
523
- &.level-0,
524
- &.level-1 {
525
- &.parent {
526
- page-break-after: avoid;
527
- margin-bottom: 15px;
528
- }
529
- }
530
-
531
- // More conservative spacing for print
532
- .tree-node-content {
533
- padding: 6px 0; // Slightly more padding
534
-
535
- .tree-node-title {
536
- min-height: 28px; // Ensure minimum height
537
- }
538
- }
539
- }
540
- }
541
-
542
- // Balanced spacing for headless/PDF mode
543
- &.puppeteer-mode,
544
- body[data-puppeteer="true"] & {
545
- .pdf-form-section {
546
- margin-bottom: 30px;
547
- padding: 8px 0;
548
-
549
- &:last-child {
550
- margin-bottom: 40px;
551
- }
552
- }
553
-
554
- .pdf-tree .tree-node {
555
- margin-bottom: 10px;
556
-
557
- .tree-node-content {
558
- padding: 6px 0;
559
- }
560
-
561
- &.level-0 {
562
- margin-bottom: 18px;
563
- }
564
-
565
- &.level-1 {
566
- margin-bottom: 12px;
567
- }
500
+ // &.parent {
501
+ // page-break-after: avoid;
502
+ // }
568
503
  }
569
504
  }
570
505
  }
@@ -685,4 +620,4 @@
685
620
  .tree-node-content {
686
621
  padding-left: 10px;
687
622
  }
688
- }
623
+ }