jupiter-dynamic-forms 1.2.0 → 1.5.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/index.mjs CHANGED
@@ -523,18 +523,32 @@ class FormValidator {
523
523
  }
524
524
  }
525
525
  class XBRLFormBuilder {
526
+ /**
527
+ * Create unique concept ID by combining original ID with preferred label suffix
528
+ * This handles cases where the same concept appears multiple times with different labels
529
+ */
530
+ static createUniqueConceptId(concept) {
531
+ if (concept.preferredLabel && concept.preferredLabel.trim() !== "") {
532
+ const roleMatch = concept.preferredLabel.match(/\/([^\/]+)$/);
533
+ const roleSuffix = roleMatch ? roleMatch[1] : concept.preferredLabel.replace(/[^a-zA-Z0-9]/g, "_");
534
+ return `${concept.id}_${roleSuffix}`;
535
+ }
536
+ return concept.id;
537
+ }
526
538
  /**
527
539
  * Build form schema from XBRL input data
528
540
  * Creates accordion sections for each presentation role
529
541
  */
530
542
  static buildFormSchema(xbrlInput, periodStartDate, periodEndDate) {
543
+ var _a;
531
544
  if (!xbrlInput.presentation || xbrlInput.presentation.length === 0) {
532
545
  throw new Error("XBRL presentation data is required");
533
546
  }
534
547
  const presentationData = xbrlInput.presentation[0];
548
+ const hypercubeData = (_a = xbrlInput.hypercubes) == null ? void 0 : _a[0];
535
549
  const sections = [];
536
550
  presentationData.roles.forEach((role) => {
537
- const section = this.buildSectionFromRole(role, periodStartDate, periodEndDate);
551
+ const section = this.buildSectionFromRole(role, periodStartDate, periodEndDate, hypercubeData);
538
552
  this.assignFieldColumnIds(section);
539
553
  sections.push(section);
540
554
  });
@@ -549,19 +563,32 @@ class XBRLFormBuilder {
549
563
  /**
550
564
  * Build a form section from a presentation role
551
565
  */
552
- static buildSectionFromRole(role, periodStartDate, periodEndDate) {
566
+ static buildSectionFromRole(role, periodStartDate, periodEndDate, hypercubeData) {
553
567
  var _a;
554
568
  const title = this.extractRoleTitle(role.role);
569
+ const nonAbstractConcepts = this.getAllNonAbstractConcepts(role);
570
+ const periodTypes = new Set(
571
+ nonAbstractConcepts.filter((concept) => concept.periodType).map((concept) => concept.periodType)
572
+ );
573
+ const hypercubeRole = hypercubeData == null ? void 0 : hypercubeData.roles.find((hr) => hr.roleId === role.id);
574
+ if (hypercubeRole) {
575
+ console.log(`🏷️ Found matching hypercube role "${role.id}"`);
576
+ } else {
577
+ console.log(`❌ No hypercube role found for "${role.id}"`);
578
+ }
579
+ const columns = this.generateDefaultColumnsForRole(role, periodStartDate || "2025-01-01", periodEndDate || "2025-12-31", hypercubeRole, nonAbstractConcepts, periodTypes);
580
+ const availableColumnIds = columns.map((col) => col.id);
581
+ console.log(`📋 Available column IDs for role "${role.id}":`, availableColumnIds);
582
+ const roleInfo = { periodTypes, availableColumnIds };
555
583
  const conceptTrees = [];
556
584
  if ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) {
557
585
  role.presentationLinkbase.concepts.forEach((concept) => {
558
- const conceptTree = this.buildConceptTree(concept, 0, periodStartDate, periodEndDate);
586
+ const conceptTree = this.buildConceptTree(concept, 0, periodStartDate, periodEndDate, roleInfo);
559
587
  if (conceptTree) {
560
588
  conceptTrees.push(conceptTree);
561
589
  }
562
590
  });
563
591
  }
564
- const columns = this.generateDefaultColumnsForRole(role, periodStartDate || "2025-01-01", periodEndDate || "2025-12-31");
565
592
  return {
566
593
  id: role.id,
567
594
  title,
@@ -574,52 +601,97 @@ class XBRLFormBuilder {
574
601
  /**
575
602
  * Build concept tree from XBRL presentation concept
576
603
  */
577
- static buildConceptTree(concept, level, periodStartDate, periodEndDate) {
604
+ static buildConceptTree(concept, level, periodStartDate, periodEndDate, roleInfo) {
578
605
  const label = this.getPreferredLabel(concept.labels);
579
606
  const fields = [];
580
607
  if (!concept.elementAbstract) {
581
- const field = this.createFieldFromConcept(concept, periodStartDate, periodEndDate);
582
- if (field) {
583
- fields.push(field);
608
+ let columnIds = [];
609
+ if ((roleInfo == null ? void 0 : roleInfo.availableColumnIds) && roleInfo.availableColumnIds.length > 0) {
610
+ columnIds = roleInfo.availableColumnIds.filter((colId) => {
611
+ if (concept.periodType === "instant") {
612
+ return colId.includes("instant") || colId === "instant";
613
+ } else if (concept.periodType === "duration") {
614
+ return colId.includes("duration") || colId === "duration";
615
+ } else {
616
+ return true;
617
+ }
618
+ });
619
+ if (columnIds.length === 0) {
620
+ columnIds = roleInfo.availableColumnIds;
621
+ }
622
+ } else {
623
+ if (!(roleInfo == null ? void 0 : roleInfo.periodTypes) || roleInfo.periodTypes.size === 0) {
624
+ columnIds = ["default"];
625
+ } else if (roleInfo.periodTypes.size === 1 && roleInfo.periodTypes.has("instant")) {
626
+ columnIds = ["instant"];
627
+ } else if (roleInfo.periodTypes.size === 1 && roleInfo.periodTypes.has("duration")) {
628
+ columnIds = ["duration"];
629
+ } else {
630
+ if (concept.periodType === "instant") {
631
+ columnIds = ["instant"];
632
+ } else if (concept.periodType === "duration") {
633
+ columnIds = ["duration"];
634
+ } else {
635
+ columnIds = ["duration", "instant"];
636
+ }
637
+ }
584
638
  }
639
+ columnIds.forEach((columnId) => {
640
+ const field = this.createFieldFromConcept(concept, periodStartDate, periodEndDate, columnId);
641
+ if (field)
642
+ fields.push(field);
643
+ });
585
644
  }
586
645
  const children = [];
587
646
  if (concept.children && concept.children.length > 0) {
588
647
  concept.children.forEach((child) => {
589
- const childTree = this.buildConceptTree(child, level + 1, periodStartDate, periodEndDate);
648
+ const childTree = this.buildConceptTree(child, level + 1, periodStartDate, periodEndDate, roleInfo);
590
649
  if (childTree) {
591
650
  children.push(childTree);
592
651
  }
593
652
  });
594
653
  }
654
+ const hasContent = fields.length > 0 || children.length > 0;
655
+ if (!hasContent) {
656
+ return null;
657
+ }
595
658
  return {
596
- id: concept.id,
659
+ id: this.createUniqueConceptId(concept),
660
+ // Use unique ID for form management
661
+ originalConceptId: concept.id,
662
+ // Store original for submission data
597
663
  name: concept.conceptName,
598
664
  label,
599
665
  description: `${concept.conceptName} (${concept.type})`,
600
666
  level,
601
667
  children,
602
668
  fields,
603
- collapsed: level > 0
669
+ collapsed: level > 0,
604
670
  // Collapse nested levels by default
671
+ abstract: concept.elementAbstract,
672
+ periodType: concept.periodType
605
673
  };
606
674
  }
607
675
  /**
608
676
  * Create form field from XBRL concept
609
677
  */
610
- static createFieldFromConcept(concept, periodStartDate, periodEndDate) {
678
+ static createFieldFromConcept(concept, periodStartDate, periodEndDate, forcedColumnId) {
611
679
  const fieldType = this.mapXBRLTypeToFieldType(concept.type);
612
680
  const label = this.getPreferredLabel(concept.labels);
613
- let columnId = "default";
614
- if (concept.periodType === "instant") {
615
- columnId = "instant";
616
- } else if (concept.periodType === "duration") {
617
- columnId = "duration_start";
681
+ let columnId;
682
+ if (forcedColumnId) {
683
+ columnId = forcedColumnId;
618
684
  } else {
619
- columnId = "default";
685
+ if (concept.periodType === "instant") {
686
+ columnId = "instant";
687
+ } else if (concept.periodType === "duration") {
688
+ columnId = "duration";
689
+ } else {
690
+ columnId = "default";
691
+ }
620
692
  }
621
693
  const field = {
622
- id: `${concept.id}_field`,
694
+ id: `${concept.id}_${columnId}_field`,
623
695
  conceptId: concept.id,
624
696
  columnId,
625
697
  type: fieldType,
@@ -686,16 +758,8 @@ class XBRLFormBuilder {
686
758
  * Recursively assign column IDs to fields in a concept tree
687
759
  */
688
760
  static assignColumnIdsToConceptFields(conceptTree, columnIds) {
689
- conceptTree.fields.forEach((field) => {
690
- if (columnIds.includes("instant") && field.columnId === "instant") {
691
- field.columnId = "instant";
692
- } else if (columnIds.includes("duration_start") && field.columnId === "duration_start") {
693
- field.columnId = "duration_start";
694
- } else if (columnIds.includes("period_start") && (field.columnId === "duration_start" || field.columnId === "instant")) {
695
- field.columnId = "period_start";
696
- } else if (columnIds.length > 0) {
697
- field.columnId = columnIds[0];
698
- }
761
+ conceptTree.fields = conceptTree.fields.filter((field) => {
762
+ return columnIds.includes(field.columnId);
699
763
  });
700
764
  if (conceptTree.children) {
701
765
  conceptTree.children.forEach((child) => {
@@ -703,6 +767,18 @@ class XBRLFormBuilder {
703
767
  });
704
768
  }
705
769
  }
770
+ /**
771
+ * Count total fields in a concept tree recursively
772
+ */
773
+ static countFieldsInTree(conceptTree) {
774
+ let count = conceptTree.fields.length;
775
+ if (conceptTree.children) {
776
+ conceptTree.children.forEach((child) => {
777
+ count += this.countFieldsInTree(child);
778
+ });
779
+ }
780
+ return count;
781
+ }
706
782
  /**
707
783
  * Get all non-abstract concepts from a role recursively
708
784
  */
@@ -732,10 +808,14 @@ class XBRLFormBuilder {
732
808
  /**
733
809
  * Generate default columns based on period types of non-abstract concepts in a role
734
810
  */
735
- static generateDefaultColumnsForRole(role, periodStartDate, periodEndDate) {
736
- const nonAbstractConcepts = this.getAllNonAbstractConcepts(role);
737
- console.log(`📊 Analyzing role "${role.role}" with ${nonAbstractConcepts.length} non-abstract concepts`);
738
- if (nonAbstractConcepts.length === 0) {
811
+ static generateDefaultColumnsForRole(role, periodStartDate, periodEndDate, hypercubeRole, nonAbstractConcepts, rolePeriodTypes) {
812
+ var _a, _b, _c;
813
+ const concepts = nonAbstractConcepts || this.getAllNonAbstractConcepts(role);
814
+ const periodTypes = rolePeriodTypes || new Set(
815
+ concepts.filter((concept) => concept.periodType).map((concept) => concept.periodType)
816
+ );
817
+ console.log(`📊 Analyzing role "${role.role}" with ${concepts.length} non-abstract concepts`);
818
+ if (concepts.length === 0) {
739
819
  console.log("📊 No non-abstract concepts found, using default column");
740
820
  return [{
741
821
  id: "default",
@@ -746,71 +826,359 @@ class XBRLFormBuilder {
746
826
  removable: false
747
827
  }];
748
828
  }
749
- const periodTypes = new Set(
750
- nonAbstractConcepts.filter((concept) => concept.periodType).map((concept) => concept.periodType)
751
- );
829
+ let dimensionColumns = [];
830
+ if (((_a = hypercubeRole == null ? void 0 : hypercubeRole.items) == null ? void 0 : _a.length) === 1) {
831
+ const item = hypercubeRole.items[0];
832
+ console.log(`🔍 Processing hypercube item for role "${role.role}":`, {
833
+ itemId: item.id,
834
+ conceptIds: ((_b = item.conceptIds) == null ? void 0 : _b.length) || 0,
835
+ dimensions: item.dimensions.length
836
+ });
837
+ if (item.dimensions.length === 1) {
838
+ dimensionColumns = this.generateSingleDimensionColumns(item.dimensions[0], periodStartDate, periodEndDate, periodTypes);
839
+ } else if (item.dimensions.length > 1) {
840
+ dimensionColumns = this.generateMultiDimensionColumns(item.dimensions, periodStartDate, periodEndDate, periodTypes);
841
+ }
842
+ } else if (((_c = hypercubeRole == null ? void 0 : hypercubeRole.items) == null ? void 0 : _c.length) && hypercubeRole.items.length > 1) {
843
+ console.log(`⚠️ Multiple items found (${hypercubeRole.items.length}), skipping for now`);
844
+ }
752
845
  console.log(`📅 Found period types: ${Array.from(periodTypes).join(", ")}`);
753
846
  const columns = [];
847
+ if (dimensionColumns.length > 0) {
848
+ columns.push(...dimensionColumns);
849
+ } else {
850
+ if (periodTypes.size === 0) {
851
+ console.log("📅 No period types found, using default column");
852
+ columns.push({
853
+ id: "default",
854
+ title: "Value",
855
+ description: "Default value column",
856
+ type: "base",
857
+ order: 0,
858
+ removable: false
859
+ });
860
+ } else if (periodTypes.size === 1 && periodTypes.has("instant")) {
861
+ console.log("📅 All concepts are instant type, creating single column");
862
+ columns.push({
863
+ id: "instant",
864
+ title: `As of ${this.formatDateForDisplay(periodStartDate)}`,
865
+ description: `Values as of ${periodStartDate}`,
866
+ type: "base",
867
+ order: 0,
868
+ removable: false
869
+ });
870
+ } else if (periodTypes.size === 1 && periodTypes.has("duration")) {
871
+ console.log("📅 All concepts are duration type, creating single column with period range");
872
+ columns.push({
873
+ id: "duration",
874
+ title: `${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)}`,
875
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
876
+ type: "base",
877
+ order: 0,
878
+ removable: false
879
+ });
880
+ } else {
881
+ console.log("📅 Mixed period types found, creating both duration and instant columns");
882
+ columns.push({
883
+ id: "duration",
884
+ title: `${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)}`,
885
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
886
+ type: "base",
887
+ order: 0,
888
+ removable: false
889
+ });
890
+ columns.push({
891
+ id: "instant",
892
+ title: `As of ${this.formatDateForDisplay(periodStartDate)}`,
893
+ description: `Values as of ${periodStartDate}`,
894
+ type: "base",
895
+ order: 1,
896
+ removable: false
897
+ });
898
+ }
899
+ }
900
+ console.log(`📊 Generated ${columns.length} columns for role "${role.role}":`, columns.map((c2) => c2.title));
901
+ return columns;
902
+ }
903
+ /**
904
+ * Generate columns for single dimension scenarios
905
+ */
906
+ static generateSingleDimensionColumns(dimension, periodStartDate, periodEndDate, periodTypes) {
907
+ var _a, _b;
908
+ console.log(`📊 Found single dimension:`, {
909
+ id: dimension.id,
910
+ conceptName: dimension.conceptName,
911
+ membersCount: dimension.members.length
912
+ });
913
+ if (dimension.members.length === 0) {
914
+ return [];
915
+ }
916
+ const axisLabel = ((_a = dimension.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _a.label) || dimension.conceptName;
917
+ const firstMember = dimension.members[0];
918
+ const memberLabel = ((_b = firstMember.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _b.label) || firstMember.conceptName;
919
+ const dimensionInfo = {
920
+ axisId: dimension.id,
921
+ axisLabel,
922
+ memberId: firstMember.id,
923
+ memberLabel,
924
+ dimensionKey: `${axisLabel} | ${memberLabel}`,
925
+ dimensionIdKey: `${dimension.id} | ${firstMember.id}`
926
+ };
927
+ console.log(`📊 Generated dimension info:`, dimensionInfo);
928
+ const columns = [];
754
929
  if (periodTypes.size === 0) {
755
- console.log("📅 No period types found, using default column");
756
930
  columns.push({
757
931
  id: "default",
758
- title: "Value",
932
+ title: dimensionInfo.memberLabel,
759
933
  description: "Default value column",
760
- type: "base",
934
+ type: "dimension",
935
+ dimensionData: {
936
+ dimensionId: "default",
937
+ axisId: dimensionInfo.axisId,
938
+ memberId: dimensionInfo.memberId,
939
+ memberValue: dimensionInfo.memberLabel,
940
+ memberLabel: dimensionInfo.memberLabel,
941
+ axis: dimensionInfo.axisLabel,
942
+ axisLabel: dimensionInfo.axisLabel,
943
+ memberKey: dimensionInfo.dimensionKey,
944
+ dimensionIdKey: dimensionInfo.dimensionIdKey
945
+ },
761
946
  order: 0,
762
947
  removable: false
763
948
  });
764
949
  } else if (periodTypes.size === 1 && periodTypes.has("instant")) {
765
- console.log("📅 All concepts are instant type, creating single column");
766
950
  columns.push({
767
951
  id: "instant",
768
- title: `As of ${this.formatDateForDisplay(periodStartDate)}`,
952
+ title: `${dimensionInfo.memberLabel} (${this.formatDateForDisplay(periodStartDate)})`,
769
953
  description: `Values as of ${periodStartDate}`,
770
- type: "base",
954
+ type: "dimension",
955
+ dimensionData: {
956
+ dimensionId: "instant",
957
+ axisId: dimensionInfo.axisId,
958
+ memberId: dimensionInfo.memberId,
959
+ memberValue: dimensionInfo.memberLabel,
960
+ memberLabel: dimensionInfo.memberLabel,
961
+ axis: dimensionInfo.axisLabel,
962
+ axisLabel: dimensionInfo.axisLabel,
963
+ memberKey: dimensionInfo.dimensionKey,
964
+ dimensionIdKey: dimensionInfo.dimensionIdKey
965
+ },
771
966
  order: 0,
772
967
  removable: false
773
968
  });
774
969
  } else if (periodTypes.size === 1 && periodTypes.has("duration")) {
775
- console.log("📅 All concepts are duration type, creating two columns");
776
970
  columns.push({
777
- id: "duration_start",
778
- title: `From ${this.formatDateForDisplay(periodStartDate)}`,
779
- description: `Period start: ${periodStartDate}`,
780
- type: "base",
971
+ id: "duration",
972
+ title: `${dimensionInfo.memberLabel} (${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)})`,
973
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
974
+ type: "dimension",
975
+ dimensionData: {
976
+ dimensionId: "duration",
977
+ axisId: dimensionInfo.axisId,
978
+ memberId: dimensionInfo.memberId,
979
+ memberValue: dimensionInfo.memberLabel,
980
+ memberLabel: dimensionInfo.memberLabel,
981
+ axis: dimensionInfo.axisLabel,
982
+ axisLabel: dimensionInfo.axisLabel,
983
+ memberKey: dimensionInfo.dimensionKey,
984
+ dimensionIdKey: dimensionInfo.dimensionIdKey
985
+ },
781
986
  order: 0,
782
987
  removable: false
783
988
  });
784
- columns.push({
785
- id: "duration_end",
786
- title: `To ${this.formatDateForDisplay(periodEndDate)}`,
787
- description: `Period end: ${periodEndDate}`,
788
- type: "base",
789
- order: 1,
790
- removable: false
791
- });
792
989
  } else {
793
- console.log("📅 Mixed period types found, creating two columns for full range");
794
990
  columns.push({
795
- id: "period_start",
796
- title: `From ${this.formatDateForDisplay(periodStartDate)}`,
797
- description: `Period start: ${periodStartDate}`,
798
- type: "base",
991
+ id: "duration",
992
+ title: `${dimensionInfo.memberLabel} (${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)})`,
993
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
994
+ type: "dimension",
995
+ dimensionData: {
996
+ dimensionId: "duration",
997
+ axisId: dimensionInfo.axisId,
998
+ memberId: dimensionInfo.memberId,
999
+ memberValue: dimensionInfo.memberLabel,
1000
+ memberLabel: dimensionInfo.memberLabel,
1001
+ axis: dimensionInfo.axisLabel,
1002
+ axisLabel: dimensionInfo.axisLabel,
1003
+ memberKey: dimensionInfo.dimensionKey,
1004
+ dimensionIdKey: dimensionInfo.dimensionIdKey
1005
+ },
799
1006
  order: 0,
800
1007
  removable: false
801
1008
  });
802
1009
  columns.push({
803
- id: "period_end",
804
- title: `To ${this.formatDateForDisplay(periodEndDate)}`,
805
- description: `Period end: ${periodEndDate}`,
806
- type: "base",
1010
+ id: "instant",
1011
+ title: `${dimensionInfo.memberLabel} (${this.formatDateForDisplay(periodStartDate)})`,
1012
+ description: `Values as of ${periodStartDate}`,
1013
+ type: "dimension",
1014
+ dimensionData: {
1015
+ dimensionId: "instant",
1016
+ axisId: dimensionInfo.axisId,
1017
+ memberId: dimensionInfo.memberId,
1018
+ memberValue: dimensionInfo.memberLabel,
1019
+ memberLabel: dimensionInfo.memberLabel,
1020
+ axis: dimensionInfo.axisLabel,
1021
+ axisLabel: dimensionInfo.axisLabel,
1022
+ memberKey: dimensionInfo.dimensionKey,
1023
+ dimensionIdKey: dimensionInfo.dimensionIdKey
1024
+ },
807
1025
  order: 1,
808
1026
  removable: false
809
1027
  });
810
1028
  }
811
- console.log(`📊 Generated ${columns.length} columns for role "${role.role}":`, columns.map((c2) => c2.title));
812
1029
  return columns;
813
1030
  }
1031
+ /**
1032
+ * Generate columns for multi-dimension scenarios - creates combinations of all dimension members
1033
+ */
1034
+ static generateMultiDimensionColumns(dimensions, periodStartDate, periodEndDate, periodTypes) {
1035
+ console.log(`📊 Processing ${dimensions.length} dimensions for multi-dimensional columns`);
1036
+ const dimensionInfos = dimensions.map((dimension) => {
1037
+ var _a;
1038
+ const axisLabel = ((_a = dimension.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _a.label) || dimension.conceptName;
1039
+ const allMembers = this.getAllDimensionMembers(dimension.members);
1040
+ return {
1041
+ id: dimension.id,
1042
+ axisLabel,
1043
+ members: allMembers.map((member) => {
1044
+ var _a2;
1045
+ return {
1046
+ id: member.id,
1047
+ label: ((_a2 = member.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _a2.label) || member.conceptName
1048
+ };
1049
+ })
1050
+ };
1051
+ });
1052
+ const combinations = this.generateDimensionCombinations(dimensionInfos);
1053
+ console.log(`📊 Generated ${combinations.length} dimension combinations`);
1054
+ const columns = [];
1055
+ combinations.forEach((combination, index) => {
1056
+ const columnTitle = combination.map((c2) => c2.memberLabel).join(" | ");
1057
+ const dimensionData = {
1058
+ dimensionId: `multi_${index}`,
1059
+ memberValue: columnTitle,
1060
+ // Required field
1061
+ memberLabel: columnTitle,
1062
+ // Required field
1063
+ combinations: combination.map((c2) => ({
1064
+ axisId: c2.axisId,
1065
+ axisLabel: c2.axisLabel,
1066
+ memberId: c2.memberId,
1067
+ memberLabel: c2.memberLabel
1068
+ })),
1069
+ memberKey: columnTitle,
1070
+ dimensionIdKey: combination.map((c2) => `${c2.axisId}|${c2.memberId}`).join("::")
1071
+ };
1072
+ if (periodTypes.size === 0 || periodTypes.size === 1 && periodTypes.has("duration")) {
1073
+ columns.push({
1074
+ id: `duration_${index}`,
1075
+ title: `${columnTitle} (${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)})`,
1076
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
1077
+ type: "dimension",
1078
+ dimensionData: {
1079
+ ...dimensionData,
1080
+ dimensionId: `duration_${index}`
1081
+ },
1082
+ order: index * 2,
1083
+ removable: false
1084
+ });
1085
+ } else if (periodTypes.size === 1 && periodTypes.has("instant")) {
1086
+ columns.push({
1087
+ id: `instant_${index}`,
1088
+ title: `${columnTitle} (${this.formatDateForDisplay(periodStartDate)})`,
1089
+ description: `Values as of ${periodStartDate}`,
1090
+ type: "dimension",
1091
+ dimensionData: {
1092
+ ...dimensionData,
1093
+ dimensionId: `instant_${index}`
1094
+ },
1095
+ order: index * 2,
1096
+ removable: false
1097
+ });
1098
+ } else {
1099
+ columns.push({
1100
+ id: `duration_${index}`,
1101
+ title: `${columnTitle} (${this.formatDateForDisplay(periodStartDate)} / ${this.formatDateForDisplay(periodEndDate)})`,
1102
+ description: `Period: ${periodStartDate} to ${periodEndDate}`,
1103
+ type: "dimension",
1104
+ dimensionData: {
1105
+ ...dimensionData,
1106
+ dimensionId: `duration_${index}`
1107
+ },
1108
+ order: index * 2,
1109
+ removable: false
1110
+ });
1111
+ columns.push({
1112
+ id: `instant_${index}`,
1113
+ title: `${columnTitle} (${this.formatDateForDisplay(periodStartDate)})`,
1114
+ description: `Values as of ${periodStartDate}`,
1115
+ type: "dimension",
1116
+ dimensionData: {
1117
+ ...dimensionData,
1118
+ dimensionId: `instant_${index}`
1119
+ },
1120
+ order: index * 2 + 1,
1121
+ removable: false
1122
+ });
1123
+ }
1124
+ });
1125
+ console.log(`📊 Generated ${columns.length} multi-dimensional columns`);
1126
+ return columns;
1127
+ }
1128
+ /**
1129
+ * Get all members including children recursively
1130
+ */
1131
+ static getAllDimensionMembers(members) {
1132
+ const allMembers = [];
1133
+ members.forEach((member) => {
1134
+ allMembers.push(member);
1135
+ if (member.children && member.children.length > 0) {
1136
+ allMembers.push(...this.getAllDimensionMembers(member.children));
1137
+ }
1138
+ });
1139
+ return allMembers;
1140
+ }
1141
+ /**
1142
+ * Generate all combinations of dimension members
1143
+ */
1144
+ static generateDimensionCombinations(dimensionInfos) {
1145
+ if (dimensionInfos.length === 0)
1146
+ return [];
1147
+ if (dimensionInfos.length === 1) {
1148
+ return dimensionInfos[0].members.map((member) => [{
1149
+ axisId: dimensionInfos[0].id,
1150
+ axisLabel: dimensionInfos[0].axisLabel,
1151
+ memberId: member.id,
1152
+ memberLabel: member.label
1153
+ }]);
1154
+ }
1155
+ const [firstDimension, ...restDimensions] = dimensionInfos;
1156
+ const restCombinations = this.generateDimensionCombinations(restDimensions);
1157
+ const combinations = [];
1158
+ firstDimension.members.forEach((member) => {
1159
+ if (restCombinations.length === 0) {
1160
+ combinations.push([{
1161
+ axisId: firstDimension.id,
1162
+ axisLabel: firstDimension.axisLabel,
1163
+ memberId: member.id,
1164
+ memberLabel: member.label
1165
+ }]);
1166
+ } else {
1167
+ restCombinations.forEach((restCombination) => {
1168
+ combinations.push([
1169
+ {
1170
+ axisId: firstDimension.id,
1171
+ axisLabel: firstDimension.axisLabel,
1172
+ memberId: member.id,
1173
+ memberLabel: member.label
1174
+ },
1175
+ ...restCombination
1176
+ ]);
1177
+ });
1178
+ }
1179
+ });
1180
+ return combinations;
1181
+ }
814
1182
  /**
815
1183
  * Format date for display in column headers
816
1184
  */
@@ -864,15 +1232,15 @@ class XBRLFormBuilder {
864
1232
  }];
865
1233
  }
866
1234
  }
867
- var __defProp$3 = Object.defineProperty;
868
- var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
869
- var __decorateClass$3 = (decorators, target, key, kind) => {
870
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
1235
+ var __defProp$4 = Object.defineProperty;
1236
+ var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
1237
+ var __decorateClass$4 = (decorators, target, key, kind) => {
1238
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
871
1239
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
872
1240
  if (decorator = decorators[i2])
873
1241
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
874
1242
  if (kind && result)
875
- __defProp$3(target, key, result);
1243
+ __defProp$4(target, key, result);
876
1244
  return result;
877
1245
  };
878
1246
  let JupiterFormField = class extends LitElement {
@@ -881,6 +1249,7 @@ let JupiterFormField = class extends LitElement {
881
1249
  this.value = null;
882
1250
  this.disabled = false;
883
1251
  this.locale = "en-US";
1252
+ this.hideLabel = false;
884
1253
  this._errors = [];
885
1254
  this._touched = false;
886
1255
  }
@@ -949,10 +1318,14 @@ let JupiterFormField = class extends LitElement {
949
1318
  const hasErrors = this._errors.some((e2) => e2.severity === "error");
950
1319
  const hasWarnings = this._errors.some((e2) => e2.severity === "warning");
951
1320
  const cssClass = `field-input ${hasErrors ? "error" : hasWarnings ? "warning" : ""}`;
1321
+ const fieldId = `${this.conceptId}__${this.columnId}`;
1322
+ const fieldName = `data[${this.conceptId}][${this.columnId}]`;
952
1323
  switch (this.field.type) {
953
1324
  case "textarea":
954
1325
  return html`
955
1326
  <textarea
1327
+ id="${fieldId}"
1328
+ name="${fieldName}"
956
1329
  class="${cssClass}"
957
1330
  .value="${this.value || ""}"
958
1331
  ?disabled="${this.disabled || this.field.disabled}"
@@ -965,6 +1338,8 @@ let JupiterFormField = class extends LitElement {
965
1338
  case "select":
966
1339
  return html`
967
1340
  <select
1341
+ id="${fieldId}"
1342
+ name="${fieldName}"
968
1343
  class="${cssClass}"
969
1344
  .value="${this.value || ""}"
970
1345
  ?disabled="${this.disabled || this.field.disabled}"
@@ -988,6 +1363,8 @@ let JupiterFormField = class extends LitElement {
988
1363
  return html`
989
1364
  <div class="checkbox-container">
990
1365
  <input
1366
+ id="${fieldId}"
1367
+ name="${fieldName}"
991
1368
  type="checkbox"
992
1369
  class="field-input"
993
1370
  .checked="${Boolean(this.value)}"
@@ -1002,6 +1379,8 @@ let JupiterFormField = class extends LitElement {
1002
1379
  default:
1003
1380
  return html`
1004
1381
  <input
1382
+ id="${fieldId}"
1383
+ name="${fieldName}"
1005
1384
  type="${this._getInputType()}"
1006
1385
  class="${cssClass}"
1007
1386
  .value="${this.value || ""}"
@@ -1036,7 +1415,7 @@ let JupiterFormField = class extends LitElement {
1036
1415
  }
1037
1416
  }
1038
1417
  render() {
1039
- const showLabel = this.field.type !== "boolean";
1418
+ const showLabel = !this.hideLabel && this.field.type !== "boolean";
1040
1419
  return html`
1041
1420
  <div class="field-container">
1042
1421
  ${showLabel ? html`
@@ -1063,7 +1442,11 @@ let JupiterFormField = class extends LitElement {
1063
1442
  JupiterFormField.styles = css`
1064
1443
  :host {
1065
1444
  display: block;
1066
- margin-bottom: 8px;
1445
+ margin-bottom: 0px; /* Remove bottom margin for table layout */
1446
+ }
1447
+
1448
+ :host([hideLabel]) {
1449
+ margin-bottom: 0px;
1067
1450
  }
1068
1451
 
1069
1452
  .field-container {
@@ -1085,10 +1468,10 @@ JupiterFormField.styles = css`
1085
1468
 
1086
1469
  .field-input {
1087
1470
  width: 100%;
1088
- padding: 8px 12px;
1471
+ padding: 6px 8px; /* Reduced padding for table cells */
1089
1472
  border: 1px solid var(--jupiter-border-color, #ddd);
1090
1473
  border-radius: 4px;
1091
- font-size: 14px;
1474
+ font-size: 13px; /* Slightly smaller font for table */
1092
1475
  font-family: inherit;
1093
1476
  background: var(--jupiter-input-background, #fff);
1094
1477
  color: var(--jupiter-text-primary, #333);
@@ -1162,42 +1545,45 @@ JupiterFormField.styles = css`
1162
1545
  width: auto;
1163
1546
  }
1164
1547
  `;
1165
- __decorateClass$3([
1548
+ __decorateClass$4([
1166
1549
  n2({ type: Object })
1167
1550
  ], JupiterFormField.prototype, "field", 2);
1168
- __decorateClass$3([
1551
+ __decorateClass$4([
1169
1552
  n2({ type: String })
1170
1553
  ], JupiterFormField.prototype, "conceptId", 2);
1171
- __decorateClass$3([
1554
+ __decorateClass$4([
1172
1555
  n2({ type: String })
1173
1556
  ], JupiterFormField.prototype, "columnId", 2);
1174
- __decorateClass$3([
1557
+ __decorateClass$4([
1175
1558
  n2()
1176
1559
  ], JupiterFormField.prototype, "value", 2);
1177
- __decorateClass$3([
1560
+ __decorateClass$4([
1178
1561
  n2({ type: Boolean })
1179
1562
  ], JupiterFormField.prototype, "disabled", 2);
1180
- __decorateClass$3([
1563
+ __decorateClass$4([
1181
1564
  n2({ type: String })
1182
1565
  ], JupiterFormField.prototype, "locale", 2);
1183
- __decorateClass$3([
1566
+ __decorateClass$4([
1567
+ n2({ type: Boolean })
1568
+ ], JupiterFormField.prototype, "hideLabel", 2);
1569
+ __decorateClass$4([
1184
1570
  r()
1185
1571
  ], JupiterFormField.prototype, "_errors", 2);
1186
- __decorateClass$3([
1572
+ __decorateClass$4([
1187
1573
  r()
1188
1574
  ], JupiterFormField.prototype, "_touched", 2);
1189
- JupiterFormField = __decorateClass$3([
1575
+ JupiterFormField = __decorateClass$4([
1190
1576
  t$1("jupiter-form-field")
1191
1577
  ], JupiterFormField);
1192
- var __defProp$2 = Object.defineProperty;
1193
- var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
1194
- var __decorateClass$2 = (decorators, target, key, kind) => {
1195
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
1578
+ var __defProp$3 = Object.defineProperty;
1579
+ var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
1580
+ var __decorateClass$3 = (decorators, target, key, kind) => {
1581
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
1196
1582
  for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
1197
1583
  if (decorator = decorators[i2])
1198
1584
  result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1199
1585
  if (kind && result)
1200
- __defProp$2(target, key, result);
1586
+ __defProp$3(target, key, result);
1201
1587
  return result;
1202
1588
  };
1203
1589
  let JupiterConceptTree = class extends LitElement {
@@ -1207,11 +1593,18 @@ let JupiterConceptTree = class extends LitElement {
1207
1593
  this.formData = {};
1208
1594
  this.disabled = false;
1209
1595
  this.locale = "en-US";
1596
+ this.expandedConcepts = /* @__PURE__ */ new Set();
1210
1597
  this._expanded = true;
1211
1598
  }
1212
1599
  connectedCallback() {
1213
1600
  super.connectedCallback();
1214
- this._expanded = !this.concept.collapsed;
1601
+ this._expanded = this.expandedConcepts.has(this.concept.id);
1602
+ }
1603
+ willUpdate(changedProperties) {
1604
+ super.willUpdate(changedProperties);
1605
+ if (changedProperties.has("expandedConcepts")) {
1606
+ this._expanded = this.expandedConcepts.has(this.concept.id);
1607
+ }
1215
1608
  }
1216
1609
  _toggleExpanded() {
1217
1610
  this._expanded = !this._expanded;
@@ -1224,7 +1617,8 @@ let JupiterConceptTree = class extends LitElement {
1224
1617
  }));
1225
1618
  }
1226
1619
  _getFieldForColumn(columnId) {
1227
- return this.concept.fields.find((field) => field.columnId === columnId);
1620
+ var _a;
1621
+ return (_a = this.concept.fields) == null ? void 0 : _a.find((field) => field.columnId === columnId);
1228
1622
  }
1229
1623
  _getFieldValue(field) {
1230
1624
  var _a;
@@ -1240,104 +1634,103 @@ let JupiterConceptTree = class extends LitElement {
1240
1634
  render() {
1241
1635
  const hasChildren = this.concept.children && this.concept.children.length > 0;
1242
1636
  const level = this.concept.level || 0;
1637
+ const isAbstract = this.concept.abstract || false;
1243
1638
  return html`
1244
- <div class="concept-row" style="--level: ${level}">
1245
- <!-- Concept Label Column -->
1246
- <div class="concept-header" @click="${this._toggleExpanded}">
1639
+ <!-- Concept Name Cell (Left Column) -->
1640
+ <td class="concept-name-cell ${isAbstract ? "abstract" : hasChildren ? "" : "leaf"}">
1641
+ <div class="concept-content" style="--level: ${level}">
1247
1642
  <div class="concept-indent"></div>
1248
- <div class="concept-toggle ${hasChildren ? this._expanded ? "expanded" : "" : "no-children"}">
1643
+ <div class="concept-toggle ${hasChildren ? this._expanded ? "expanded" : "" : "no-children"}"
1644
+ @click="${this._toggleExpanded}">
1249
1645
  ${hasChildren ? "▶" : ""}
1250
1646
  </div>
1251
- <div class="concept-label">
1647
+ <div class="concept-label"
1648
+ @click="${this._toggleExpanded}"
1649
+ title="${this.concept.id}${this.concept.description ? " - " + this.concept.description : ""}">
1252
1650
  ${this.concept.label}
1253
- ${this.concept.description ? html`
1254
- <span class="concept-description">${this.concept.description}</span>
1255
- ` : ""}
1256
1651
  </div>
1257
1652
  </div>
1653
+ </td>
1258
1654
 
1259
- <!-- Field Columns -->
1260
- ${this.columns.map((column) => {
1655
+ <!-- Input Field Cells (Period Columns) - Only for non-abstract concepts -->
1656
+ ${this.columns.map((column) => {
1261
1657
  const field = this._getFieldForColumn(column.id);
1658
+ const shouldShowField = !isAbstract && field;
1262
1659
  return html`
1263
- <div class="field-cell ${!field ? "empty" : ""}">
1264
- ${field ? html`
1265
- <jupiter-form-field
1266
- .field="${field}"
1267
- .conceptId="${this.concept.id}"
1268
- .columnId="${column.id}"
1269
- .value="${this._getFieldValue(field)}"
1270
- .disabled="${this.disabled}"
1271
- .locale="${this.locale}"
1272
- @field-change="${this._handleFieldChange}"
1273
- ></jupiter-form-field>
1274
- ` : ""}
1275
- </div>
1276
- `;
1660
+ <td class="field-cell ${!shouldShowField ? "empty" : ""} ${isAbstract ? "abstract-row" : ""}">
1661
+ ${shouldShowField ? html`
1662
+ <jupiter-form-field
1663
+ .field="${field}"
1664
+ .conceptId="${this.concept.id}"
1665
+ .columnId="${column.id}"
1666
+ .value="${this._getFieldValue(field)}"
1667
+ .disabled="${this.disabled}"
1668
+ .locale="${this.locale}"
1669
+ .hideLabel="${true}"
1670
+ @field-change="${this._handleFieldChange}"
1671
+ ></jupiter-form-field>
1672
+ ` : ""}
1673
+ </td>
1674
+ `;
1277
1675
  })}
1278
- </div>
1279
-
1280
- <!-- Children Concepts -->
1281
- ${hasChildren ? html`
1282
- <div class="children ${!this._expanded ? "collapsed" : ""}">
1283
- ${this.concept.children.map((child) => html`
1284
- <jupiter-concept-tree
1285
- .concept="${child}"
1286
- .columns="${this.columns}"
1287
- .formData="${this.formData}"
1288
- .disabled="${this.disabled}"
1289
- .locale="${this.locale}"
1290
- @field-change="${this._handleFieldChange}"
1291
- @concept-expand="${(e2) => {
1292
- e2.stopPropagation();
1293
- this.dispatchEvent(new CustomEvent("concept-expand", { detail: e2.detail, bubbles: true }));
1294
- }}"
1295
- ></jupiter-concept-tree>
1296
- `)}
1297
- </div>
1298
- ` : ""}
1299
1676
  `;
1300
1677
  }
1301
1678
  };
1302
1679
  JupiterConceptTree.styles = css`
1303
1680
  :host {
1304
- display: block;
1305
- }
1306
-
1307
- .concept-row {
1681
+ /* Component renders table cells directly */
1308
1682
  display: contents;
1309
1683
  }
1310
1684
 
1311
- .concept-header {
1312
- display: flex;
1313
- align-items: center;
1314
- padding: 8px 12px;
1315
- background: var(--jupiter-concept-background, #f8f9fa);
1685
+ .concept-name-cell {
1686
+ vertical-align: top;
1687
+ padding: 2px 4px;
1316
1688
  border: 1px solid var(--jupiter-border-color, #ddd);
1317
1689
  border-bottom: none;
1318
- font-weight: 500;
1319
- cursor: pointer;
1320
- user-select: none;
1690
+ background: var(--jupiter-concept-background, #f8f9fa);
1691
+ position: sticky;
1692
+ left: 0;
1693
+ z-index: 1;
1694
+ width: 300px;
1695
+ min-width: 300px;
1696
+ max-width: 300px;
1697
+ }
1698
+
1699
+ .concept-name-cell.abstract {
1700
+ background: var(--jupiter-abstract-background, #f0f2f5);
1701
+ font-weight: 600;
1702
+ }
1703
+
1704
+ .concept-name-cell.leaf {
1705
+ background: var(--jupiter-leaf-background, #fff);
1706
+ font-weight: 400;
1321
1707
  }
1322
1708
 
1323
- .concept-header:hover {
1324
- background: var(--jupiter-concept-hover-background, #e9ecef);
1709
+ .concept-content {
1710
+ display: flex;
1711
+ align-items: center;
1712
+ min-height: 28px;
1713
+ font-size: 13px;
1714
+ line-height: 1.3;
1715
+ width: 100%;
1716
+ overflow: hidden;
1325
1717
  }
1326
1718
 
1327
1719
  .concept-indent {
1328
- width: calc(var(--level, 0) * 20px);
1720
+ width: calc(var(--level, 0) * 16px);
1329
1721
  flex-shrink: 0;
1330
1722
  }
1331
1723
 
1332
1724
  .concept-toggle {
1333
- width: 16px;
1334
- height: 16px;
1335
- margin-right: 8px;
1725
+ width: 14px;
1726
+ height: 14px;
1727
+ margin-right: 6px;
1336
1728
  display: flex;
1337
1729
  align-items: center;
1338
1730
  justify-content: center;
1339
- font-size: 12px;
1731
+ font-size: 10px;
1340
1732
  transition: transform 0.2s ease;
1733
+ cursor: pointer;
1341
1734
  }
1342
1735
 
1343
1736
  .concept-toggle.expanded {
@@ -1351,71 +1744,390 @@ JupiterConceptTree.styles = css`
1351
1744
  .concept-label {
1352
1745
  flex: 1;
1353
1746
  color: var(--jupiter-text-primary, #333);
1354
- }
1355
-
1356
- .concept-description {
1357
- font-size: 12px;
1358
- color: var(--jupiter-text-secondary, #666);
1359
- font-weight: normal;
1360
- margin-left: 8px;
1361
- }
1362
-
1363
- .concept-fields {
1364
- display: contents;
1747
+ cursor: pointer;
1748
+ word-wrap: break-word;
1749
+ overflow-wrap: break-word;
1750
+ hyphens: auto;
1751
+ line-height: 1.3;
1752
+ min-width: 0; /* Allows flex item to shrink below content size */
1365
1753
  }
1366
1754
 
1367
1755
  .field-cell {
1756
+ vertical-align: middle;
1757
+ padding: 2px 6px;
1368
1758
  border: 1px solid var(--jupiter-border-color, #ddd);
1369
1759
  border-bottom: none;
1370
- padding: 8px;
1371
1760
  background: var(--jupiter-cell-background, #fff);
1372
- min-height: 40px;
1373
- display: flex;
1374
- align-items: center;
1375
- }
1376
-
1377
- .field-cell:last-child {
1378
- border-right: 1px solid var(--jupiter-border-color, #ddd);
1761
+ min-height: 28px;
1762
+ text-align: center;
1763
+ min-width: 180px;
1764
+ width: 180px;
1379
1765
  }
1380
1766
 
1381
1767
  .field-cell.empty {
1382
1768
  background: var(--jupiter-empty-cell-background, #f8f9fa);
1383
1769
  }
1384
1770
 
1385
- .children {
1386
- display: contents;
1771
+ .field-cell.abstract-row {
1772
+ background: var(--jupiter-abstract-cell-background, #f0f2f5);
1387
1773
  }
1388
1774
 
1389
- .children.collapsed {
1775
+ /* Children are rendered as separate table rows, not nested */
1776
+ .children-wrapper {
1390
1777
  display: none;
1391
1778
  }
1392
-
1393
- .concept-row:last-of-type .concept-header,
1394
- .concept-row:last-of-type .field-cell {
1395
- border-bottom: 1px solid var(--jupiter-border-color, #ddd);
1396
- }
1397
1779
  `;
1398
- __decorateClass$2([
1780
+ __decorateClass$3([
1399
1781
  n2({ type: Object })
1400
1782
  ], JupiterConceptTree.prototype, "concept", 2);
1401
- __decorateClass$2([
1783
+ __decorateClass$3([
1402
1784
  n2({ type: Array })
1403
1785
  ], JupiterConceptTree.prototype, "columns", 2);
1404
- __decorateClass$2([
1786
+ __decorateClass$3([
1405
1787
  n2({ type: Object })
1406
1788
  ], JupiterConceptTree.prototype, "formData", 2);
1407
- __decorateClass$2([
1789
+ __decorateClass$3([
1408
1790
  n2({ type: Boolean })
1409
1791
  ], JupiterConceptTree.prototype, "disabled", 2);
1410
- __decorateClass$2([
1792
+ __decorateClass$3([
1411
1793
  n2({ type: String })
1412
1794
  ], JupiterConceptTree.prototype, "locale", 2);
1413
- __decorateClass$2([
1795
+ __decorateClass$3([
1796
+ n2({ type: Set })
1797
+ ], JupiterConceptTree.prototype, "expandedConcepts", 2);
1798
+ __decorateClass$3([
1414
1799
  r()
1415
1800
  ], JupiterConceptTree.prototype, "_expanded", 2);
1416
- JupiterConceptTree = __decorateClass$2([
1801
+ JupiterConceptTree = __decorateClass$3([
1417
1802
  t$1("jupiter-concept-tree")
1418
1803
  ], JupiterConceptTree);
1804
+ var __defProp$2 = Object.defineProperty;
1805
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
1806
+ var __decorateClass$2 = (decorators, target, key, kind) => {
1807
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
1808
+ for (var i2 = decorators.length - 1, decorator; i2 >= 0; i2--)
1809
+ if (decorator = decorators[i2])
1810
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1811
+ if (kind && result)
1812
+ __defProp$2(target, key, result);
1813
+ return result;
1814
+ };
1815
+ let JupiterAddColumnDialog = class extends LitElement {
1816
+ constructor() {
1817
+ super(...arguments);
1818
+ this.periodType = "duration";
1819
+ this.open = false;
1820
+ this._startDate = "";
1821
+ this._endDate = "";
1822
+ this._instantDate = "";
1823
+ this._selectedType = "duration";
1824
+ }
1825
+ updated(changedProperties) {
1826
+ if (changedProperties.has("open") && this.open) {
1827
+ this._resetForm();
1828
+ }
1829
+ }
1830
+ connectedCallback() {
1831
+ super.connectedCallback();
1832
+ this._resetForm();
1833
+ }
1834
+ willUpdate(changedProperties) {
1835
+ super.willUpdate(changedProperties);
1836
+ if (changedProperties.has("open") && this.open) {
1837
+ this._resetForm();
1838
+ }
1839
+ }
1840
+ _resetForm() {
1841
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1842
+ this._startDate = today;
1843
+ this._endDate = today;
1844
+ this._instantDate = today;
1845
+ this._selectedType = "duration";
1846
+ }
1847
+ _handleCancel() {
1848
+ this.open = false;
1849
+ this.dispatchEvent(new CustomEvent("dialog-cancel", { bubbles: true }));
1850
+ }
1851
+ _handleConfirm() {
1852
+ if (!this._isFormValid())
1853
+ return;
1854
+ const request = {
1855
+ periodType: this.periodType === "mixed" ? this._selectedType : this.periodType
1856
+ };
1857
+ if (this.periodType === "instant" || this.periodType === "mixed" && this._selectedType === "instant") {
1858
+ request.instantDate = this._instantDate;
1859
+ } else {
1860
+ request.startDate = this._startDate;
1861
+ request.endDate = this._endDate;
1862
+ }
1863
+ this.dispatchEvent(new CustomEvent("column-add", {
1864
+ detail: request,
1865
+ bubbles: true
1866
+ }));
1867
+ }
1868
+ _isFormValid() {
1869
+ if (this.periodType === "mixed") {
1870
+ if (this._selectedType === "instant") {
1871
+ return !!this._instantDate;
1872
+ } else {
1873
+ return !!this._startDate && !!this._endDate && this._startDate <= this._endDate;
1874
+ }
1875
+ } else if (this.periodType === "duration") {
1876
+ return !!this._startDate && !!this._endDate && this._startDate <= this._endDate;
1877
+ } else if (this.periodType === "instant") {
1878
+ return !!this._instantDate;
1879
+ }
1880
+ return false;
1881
+ }
1882
+ _handleStartDateChange(e2) {
1883
+ this._startDate = e2.target.value;
1884
+ }
1885
+ _handleEndDateChange(e2) {
1886
+ this._endDate = e2.target.value;
1887
+ }
1888
+ _handleSelectedTypeChange(e2) {
1889
+ this._selectedType = e2.target.value;
1890
+ }
1891
+ _handleInstantDateChange(e2) {
1892
+ this._instantDate = e2.target.value;
1893
+ }
1894
+ render() {
1895
+ if (!this.open)
1896
+ return html``;
1897
+ const isValid = this._isFormValid();
1898
+ return html`
1899
+ <div class="dialog" @click="${(e2) => e2.stopPropagation()}">
1900
+ <div class="dialog-header">
1901
+ <h2 class="dialog-title">Add Column</h2>
1902
+ <p class="dialog-description">
1903
+ ${this.periodType === "instant" ? "Add a new instant period column by specifying the date." : this.periodType === "duration" ? "Add a new duration period column by specifying the start and end dates." : "This section contains both instant and duration concepts. Choose the type of column to add."}
1904
+ </p>
1905
+ </div>
1906
+
1907
+ <div class="dialog-content">
1908
+ ${this.periodType === "mixed" ? html`
1909
+ <div class="form-group">
1910
+ <label class="form-label required">Column Type</label>
1911
+ <select
1912
+ class="form-input"
1913
+ .value="${this._selectedType}"
1914
+ @change="${this._handleSelectedTypeChange}"
1915
+ required
1916
+ >
1917
+ <option value="instant">Instant (single date)</option>
1918
+ <option value="duration">Duration (start and end dates)</option>
1919
+ </select>
1920
+ </div>
1921
+ ` : ""}
1922
+
1923
+ ${this.periodType === "instant" || this.periodType === "mixed" && this._selectedType === "instant" ? html`
1924
+ <div class="form-group">
1925
+ <label class="form-label required">Instant Date</label>
1926
+ <input
1927
+ type="date"
1928
+ class="form-input"
1929
+ .value="${this._instantDate}"
1930
+ @change="${this._handleInstantDateChange}"
1931
+ required
1932
+ />
1933
+ </div>
1934
+ ` : html`
1935
+ <div class="form-group">
1936
+ <label class="form-label required">Start Period Date</label>
1937
+ <input
1938
+ type="date"
1939
+ class="form-input"
1940
+ .value="${this._startDate}"
1941
+ @change="${this._handleStartDateChange}"
1942
+ required
1943
+ />
1944
+ </div>
1945
+ <div class="form-group">
1946
+ <label class="form-label required">End Period Date</label>
1947
+ <input
1948
+ type="date"
1949
+ class="form-input"
1950
+ .value="${this._endDate}"
1951
+ @change="${this._handleEndDateChange}"
1952
+ required
1953
+ />
1954
+ </div>
1955
+ `}
1956
+ </div>
1957
+
1958
+ <div class="dialog-actions">
1959
+ <button class="btn btn-cancel" @click="${this._handleCancel}">
1960
+ Cancel
1961
+ </button>
1962
+ <button
1963
+ class="btn btn-primary"
1964
+ ?disabled="${!isValid}"
1965
+ @click="${this._handleConfirm}"
1966
+ >
1967
+ Add Column
1968
+ </button>
1969
+ </div>
1970
+ </div>
1971
+ `;
1972
+ }
1973
+ };
1974
+ JupiterAddColumnDialog.styles = css`
1975
+ :host {
1976
+ position: fixed;
1977
+ top: 0;
1978
+ left: 0;
1979
+ width: 100%;
1980
+ height: 100%;
1981
+ background: rgba(0, 0, 0, 0.5);
1982
+ z-index: 1000;
1983
+ display: flex;
1984
+ align-items: center;
1985
+ justify-content: center;
1986
+ opacity: 0;
1987
+ visibility: hidden;
1988
+ transition: opacity 0.3s ease, visibility 0.3s ease;
1989
+ }
1990
+
1991
+ :host([open]) {
1992
+ opacity: 1;
1993
+ visibility: visible;
1994
+ }
1995
+
1996
+ .dialog {
1997
+ background: white;
1998
+ border-radius: 8px;
1999
+ padding: 24px;
2000
+ min-width: 400px;
2001
+ max-width: 500px;
2002
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
2003
+ transform: scale(0.9);
2004
+ transition: transform 0.3s ease;
2005
+ }
2006
+
2007
+ :host([open]) .dialog {
2008
+ transform: scale(1);
2009
+ }
2010
+
2011
+ .dialog-header {
2012
+ margin-bottom: 20px;
2013
+ }
2014
+
2015
+ .dialog-title {
2016
+ font-size: 20px;
2017
+ font-weight: 600;
2018
+ color: var(--jupiter-text-primary, #333);
2019
+ margin: 0 0 8px 0;
2020
+ }
2021
+
2022
+ .dialog-description {
2023
+ font-size: 14px;
2024
+ color: var(--jupiter-text-secondary, #666);
2025
+ margin: 0;
2026
+ }
2027
+
2028
+ .dialog-content {
2029
+ margin-bottom: 24px;
2030
+ }
2031
+
2032
+ .form-group {
2033
+ margin-bottom: 16px;
2034
+ }
2035
+
2036
+ .form-label {
2037
+ display: block;
2038
+ font-weight: 500;
2039
+ margin-bottom: 6px;
2040
+ color: var(--jupiter-text-primary, #333);
2041
+ font-size: 14px;
2042
+ }
2043
+
2044
+ .form-label.required::after {
2045
+ content: ' *';
2046
+ color: var(--jupiter-error-color, #d32f2f);
2047
+ }
2048
+
2049
+ .form-input {
2050
+ width: 100%;
2051
+ box-sizing: border-box;
2052
+ padding: 10px 12px;
2053
+ border: 1px solid var(--jupiter-border-color, #ddd);
2054
+ border-radius: 4px;
2055
+ font-size: 14px;
2056
+ font-family: inherit;
2057
+ transition: border-color 0.2s ease;
2058
+ }
2059
+
2060
+ .form-input:focus {
2061
+ outline: none;
2062
+ border-color: var(--jupiter-primary-color, #007bff);
2063
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
2064
+ }
2065
+
2066
+ select.form-input {
2067
+ cursor: pointer;
2068
+ background-color: white;
2069
+ }
2070
+
2071
+ .dialog-actions {
2072
+ display: flex;
2073
+ gap: 12px;
2074
+ justify-content: flex-end;
2075
+ }
2076
+
2077
+ .btn {
2078
+ padding: 10px 20px;
2079
+ border: none;
2080
+ border-radius: 4px;
2081
+ font-size: 14px;
2082
+ font-weight: 500;
2083
+ cursor: pointer;
2084
+ transition: background-color 0.2s ease;
2085
+ }
2086
+
2087
+ .btn-cancel {
2088
+ background: var(--jupiter-neutral-background, #f5f5f5);
2089
+ color: var(--jupiter-text-primary, #333);
2090
+ }
2091
+
2092
+ .btn-cancel:hover {
2093
+ background: var(--jupiter-neutral-background-hover, #e0e0e0);
2094
+ }
2095
+
2096
+ .btn-primary {
2097
+ background: var(--jupiter-primary-color, #667eea);
2098
+ color: white;
2099
+ }
2100
+
2101
+ .btn-primary:hover {
2102
+ background: var(--jupiter-primary-color-dark, #5a6fd8);
2103
+ }
2104
+
2105
+ .btn-primary:disabled {
2106
+ background: var(--jupiter-disabled-background, #ccc);
2107
+ cursor: not-allowed;
2108
+ }
2109
+ `;
2110
+ __decorateClass$2([
2111
+ n2({ type: String })
2112
+ ], JupiterAddColumnDialog.prototype, "periodType", 2);
2113
+ __decorateClass$2([
2114
+ n2({ type: Boolean })
2115
+ ], JupiterAddColumnDialog.prototype, "open", 2);
2116
+ __decorateClass$2([
2117
+ r()
2118
+ ], JupiterAddColumnDialog.prototype, "_startDate", 2);
2119
+ __decorateClass$2([
2120
+ r()
2121
+ ], JupiterAddColumnDialog.prototype, "_endDate", 2);
2122
+ __decorateClass$2([
2123
+ r()
2124
+ ], JupiterAddColumnDialog.prototype, "_instantDate", 2);
2125
+ __decorateClass$2([
2126
+ r()
2127
+ ], JupiterAddColumnDialog.prototype, "_selectedType", 2);
2128
+ JupiterAddColumnDialog = __decorateClass$2([
2129
+ t$1("jupiter-add-column-dialog")
2130
+ ], JupiterAddColumnDialog);
1419
2131
  var __defProp$1 = Object.defineProperty;
1420
2132
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1421
2133
  var __decorateClass$1 = (decorators, target, key, kind) => {
@@ -1436,10 +2148,58 @@ let JupiterFormSection = class extends LitElement {
1436
2148
  this.collapsible = true;
1437
2149
  this.locale = "en-US";
1438
2150
  this._expanded = true;
2151
+ this._showAddColumnDialog = false;
2152
+ this._sectionPeriodType = "duration";
2153
+ this._expandedConcepts = /* @__PURE__ */ new Set();
1439
2154
  }
1440
2155
  connectedCallback() {
1441
2156
  super.connectedCallback();
1442
2157
  this._expanded = this.section.expanded !== false;
2158
+ this._determinePeriodType();
2159
+ }
2160
+ _determinePeriodType() {
2161
+ const nonAbstractConcepts = this._getAllNonAbstractConcepts(this.section.concepts);
2162
+ console.log(`🔍 Section ${this.section.id} (${this.section.title}): Found ${nonAbstractConcepts.length} non-abstract concepts`);
2163
+ console.log(`📝 All concepts in section:`, this.section.concepts.map((c2) => ({
2164
+ name: c2.name,
2165
+ abstract: c2.abstract,
2166
+ periodType: c2.periodType
2167
+ })));
2168
+ if (nonAbstractConcepts.length === 0) {
2169
+ this._sectionPeriodType = "duration";
2170
+ console.log(`📊 Section ${this.section.id}: No concepts, defaulting to duration`);
2171
+ return;
2172
+ }
2173
+ const periodTypes = new Set(nonAbstractConcepts.map((c2) => c2.periodType).filter(Boolean));
2174
+ console.log(`🏷️ Section ${this.section.id}: Found period types:`, Array.from(periodTypes));
2175
+ console.log(
2176
+ `📝 Section ${this.section.id}: Non-abstract concepts and their period types:`,
2177
+ nonAbstractConcepts.map((c2) => ({ name: c2.name, periodType: c2.periodType, abstract: c2.abstract }))
2178
+ );
2179
+ if (periodTypes.size === 0) {
2180
+ this._sectionPeriodType = "duration";
2181
+ console.log(`📊 Section ${this.section.id}: No period types defined, defaulting to duration`);
2182
+ } else if (periodTypes.size === 1) {
2183
+ const singleType = Array.from(periodTypes)[0];
2184
+ this._sectionPeriodType = singleType === "instant" ? "instant" : "duration";
2185
+ console.log(`📊 Section ${this.section.id}: Single period type '${singleType}', setting to '${this._sectionPeriodType}'`);
2186
+ } else {
2187
+ this._sectionPeriodType = "mixed";
2188
+ console.log(`📊 Section ${this.section.id}: Mixed period types, setting to 'mixed'`);
2189
+ }
2190
+ console.log(`✅ Section ${this.section.id}: Final period type: ${this._sectionPeriodType}`);
2191
+ }
2192
+ _getAllNonAbstractConcepts(concepts) {
2193
+ const result = [];
2194
+ for (const concept of concepts) {
2195
+ if (!concept.abstract) {
2196
+ result.push(concept);
2197
+ }
2198
+ if (concept.children) {
2199
+ result.push(...this._getAllNonAbstractConcepts(concept.children));
2200
+ }
2201
+ }
2202
+ return result;
1443
2203
  }
1444
2204
  _toggleExpanded() {
1445
2205
  if (!this.collapsible)
@@ -1455,7 +2215,58 @@ let JupiterFormSection = class extends LitElement {
1455
2215
  }
1456
2216
  _handleRemoveColumn(columnId) {
1457
2217
  this.dispatchEvent(new CustomEvent("column-remove", {
1458
- detail: { columnId },
2218
+ detail: {
2219
+ columnId,
2220
+ sectionId: this.section.id
2221
+ },
2222
+ bubbles: true
2223
+ }));
2224
+ }
2225
+ _handleAddColumn() {
2226
+ const buttonPeriodType = this._determineButtonPeriodType();
2227
+ this._sectionPeriodType = buttonPeriodType;
2228
+ this._showAddColumnDialog = true;
2229
+ }
2230
+ _determineButtonPeriodType() {
2231
+ const allNonAbstractConcepts = this._getAllNonAbstractConcepts(this.section.concepts);
2232
+ if (allNonAbstractConcepts.length === 0) {
2233
+ return "duration";
2234
+ }
2235
+ const periodTypes = new Set(
2236
+ allNonAbstractConcepts.map((c2) => c2.periodType).filter((periodType) => periodType !== void 0 && periodType !== null)
2237
+ );
2238
+ if (periodTypes.size === 0) {
2239
+ return "duration";
2240
+ } else if (periodTypes.size === 1) {
2241
+ const singleType = Array.from(periodTypes)[0];
2242
+ return singleType === "instant" ? "instant" : "duration";
2243
+ } else {
2244
+ return "mixed";
2245
+ }
2246
+ }
2247
+ _getConceptsWithFields(concepts) {
2248
+ const result = [];
2249
+ for (const concept of concepts) {
2250
+ if (!concept.abstract && concept.fields && concept.fields.length > 0) {
2251
+ result.push(concept);
2252
+ }
2253
+ if (concept.children) {
2254
+ result.push(...this._getConceptsWithFields(concept.children));
2255
+ }
2256
+ }
2257
+ return result;
2258
+ }
2259
+ _handleDialogCancel() {
2260
+ this._showAddColumnDialog = false;
2261
+ }
2262
+ _handleColumnAdd(event) {
2263
+ event.stopPropagation();
2264
+ this._showAddColumnDialog = false;
2265
+ this.dispatchEvent(new CustomEvent("column-add-request", {
2266
+ detail: {
2267
+ sectionId: this.section.id,
2268
+ columnRequest: event.detail
2269
+ },
1459
2270
  bubbles: true
1460
2271
  }));
1461
2272
  }
@@ -1466,8 +2277,26 @@ let JupiterFormSection = class extends LitElement {
1466
2277
  bubbles: true
1467
2278
  }));
1468
2279
  }
2280
+ _flattenConcepts(concepts, expanded = /* @__PURE__ */ new Set()) {
2281
+ const result = [];
2282
+ for (const concept of concepts) {
2283
+ result.push(concept);
2284
+ if (concept.children && concept.children.length > 0 && expanded.has(concept.id)) {
2285
+ const childrenFlattened = this._flattenConcepts(concept.children, expanded);
2286
+ result.push(...childrenFlattened);
2287
+ }
2288
+ }
2289
+ return result;
2290
+ }
1469
2291
  _handleConceptExpand(event) {
1470
2292
  event.stopPropagation();
2293
+ const { conceptId, expanded } = event.detail;
2294
+ if (expanded) {
2295
+ this._expandedConcepts.add(conceptId);
2296
+ } else {
2297
+ this._expandedConcepts.delete(conceptId);
2298
+ }
2299
+ this.requestUpdate();
1471
2300
  this.dispatchEvent(new CustomEvent("concept-expand", {
1472
2301
  detail: event.detail,
1473
2302
  bubbles: true
@@ -1514,7 +2343,7 @@ let JupiterFormSection = class extends LitElement {
1514
2343
  <div class="table-container">
1515
2344
  <table class="form-table">
1516
2345
  <thead class="table-header">
1517
- <tr>
2346
+ <tr class="header-row">
1518
2347
  <th class="header-cell concept-column">Concept</th>
1519
2348
  ${this.columns.map((column) => html`
1520
2349
  <th class="header-cell ${column.removable ? "removable" : ""}">
@@ -1532,24 +2361,43 @@ let JupiterFormSection = class extends LitElement {
1532
2361
  ` : ""}
1533
2362
  </th>
1534
2363
  `)}
2364
+ <th class="header-cell">
2365
+ <button class="add-column-btn" @click="${(e2) => {
2366
+ e2.stopPropagation();
2367
+ this._handleAddColumn();
2368
+ }}" title="Add Column">
2369
+ + Add Column
2370
+ </button>
2371
+ </th>
1535
2372
  </tr>
1536
2373
  </thead>
1537
2374
  <tbody class="table-body">
1538
- ${this.section.concepts.map((concept) => html`
1539
- <jupiter-concept-tree
1540
- .concept="${concept}"
1541
- .columns="${this.columns}"
1542
- .formData="${this.formData}"
1543
- .disabled="${this.disabled}"
1544
- .locale="${this.locale}"
1545
- @field-change="${this._handleFieldChange}"
1546
- @concept-expand="${this._handleConceptExpand}"
1547
- ></jupiter-concept-tree>
2375
+ ${this._flattenConcepts(this.section.concepts, this._expandedConcepts).map((concept) => html`
2376
+ <tr>
2377
+ <jupiter-concept-tree
2378
+ .concept="${concept}"
2379
+ .columns="${this.columns}"
2380
+ .formData="${this.formData}"
2381
+ .disabled="${this.disabled}"
2382
+ .locale="${this.locale}"
2383
+ .expandedConcepts="${this._expandedConcepts}"
2384
+ @field-change="${this._handleFieldChange}"
2385
+ @concept-expand="${this._handleConceptExpand}"
2386
+ ></jupiter-concept-tree>
2387
+ </tr>
1548
2388
  `)}
1549
2389
  </tbody>
1550
2390
  </table>
1551
2391
  </div>
1552
2392
  </div>
2393
+
2394
+ <!-- Add Column Dialog -->
2395
+ <jupiter-add-column-dialog
2396
+ .periodType="${this._sectionPeriodType}"
2397
+ ?open="${this._showAddColumnDialog}"
2398
+ @dialog-cancel="${this._handleDialogCancel}"
2399
+ @column-add="${this._handleColumnAdd}"
2400
+ ></jupiter-add-column-dialog>
1553
2401
  `;
1554
2402
  }
1555
2403
  };
@@ -1633,7 +2481,8 @@ JupiterFormSection.styles = css`
1633
2481
 
1634
2482
  .form-table {
1635
2483
  width: 100%;
1636
- border-collapse: collapse;
2484
+ border-collapse: separate;
2485
+ border-spacing: 0;
1637
2486
  background: var(--jupiter-table-background, #fff);
1638
2487
  }
1639
2488
 
@@ -1644,18 +2493,27 @@ JupiterFormSection.styles = css`
1644
2493
  z-index: 1;
1645
2494
  }
1646
2495
 
2496
+ .header-row {
2497
+ /* Table row - no special display needed */
2498
+ }
2499
+
1647
2500
  .header-cell {
1648
- padding: 12px;
2501
+ padding: 8px 12px;
1649
2502
  text-align: left;
1650
2503
  font-weight: 600;
1651
2504
  color: var(--jupiter-text-primary, #333);
1652
2505
  border: 1px solid var(--jupiter-border-color, #ddd);
1653
2506
  background: var(--jupiter-header-background, #f8f9fa);
1654
- min-width: 150px;
2507
+ min-width: 180px;
2508
+ width: 180px;
2509
+ font-size: 14px;
2510
+ vertical-align: middle;
1655
2511
  }
1656
2512
 
1657
2513
  .header-cell.concept-column {
2514
+ width: 300px;
1658
2515
  min-width: 300px;
2516
+ max-width: 300px;
1659
2517
  position: sticky;
1660
2518
  left: 0;
1661
2519
  z-index: 2;
@@ -1686,8 +2544,19 @@ JupiterFormSection.styles = css`
1686
2544
  background: var(--jupiter-error-color-dark, #b71c1c);
1687
2545
  }
1688
2546
 
1689
- .table-body {
1690
- display: table-row-group;
2547
+ .add-column-btn {
2548
+ background: var(--jupiter-primary-color, #667eea);
2549
+ color: white;
2550
+ border: none;
2551
+ padding: 8px 16px;
2552
+ border-radius: 4px;
2553
+ cursor: pointer;
2554
+ font-size: 12px;
2555
+ margin: 8px;
2556
+ }
2557
+
2558
+ .add-column-btn:hover {
2559
+ background: var(--jupiter-primary-color-dark, #5a6fd8);
1691
2560
  }
1692
2561
 
1693
2562
  .empty-section {
@@ -1718,6 +2587,15 @@ __decorateClass$1([
1718
2587
  __decorateClass$1([
1719
2588
  r()
1720
2589
  ], JupiterFormSection.prototype, "_expanded", 2);
2590
+ __decorateClass$1([
2591
+ r()
2592
+ ], JupiterFormSection.prototype, "_showAddColumnDialog", 2);
2593
+ __decorateClass$1([
2594
+ r()
2595
+ ], JupiterFormSection.prototype, "_sectionPeriodType", 2);
2596
+ __decorateClass$1([
2597
+ r()
2598
+ ], JupiterFormSection.prototype, "_expandedConcepts", 2);
1721
2599
  JupiterFormSection = __decorateClass$1([
1722
2600
  t$1("jupiter-form-section")
1723
2601
  ], JupiterFormSection);
@@ -1873,21 +2751,208 @@ let JupiterDynamicForm = class extends LitElement {
1873
2751
  }));
1874
2752
  }
1875
2753
  _handleColumnRemove(event) {
1876
- const { columnId } = event.detail;
1877
- this._columns = this._columns.filter((col) => col.id !== columnId);
1878
- for (const conceptId in this._formData) {
1879
- if (this._formData[conceptId][columnId] !== void 0) {
1880
- delete this._formData[conceptId][columnId];
2754
+ const { columnId, sectionId } = event.detail;
2755
+ if (this._currentSchema && sectionId) {
2756
+ const targetSection = this._currentSchema.sections.find((section) => section.id === sectionId);
2757
+ if (targetSection && targetSection.columns) {
2758
+ targetSection.columns = targetSection.columns.filter((col) => col.id !== columnId);
2759
+ }
2760
+ } else {
2761
+ this._columns = this._columns.filter((col) => col.id !== columnId);
2762
+ if (this._currentSchema) {
2763
+ this._currentSchema.sections.forEach((section) => {
2764
+ if (section.columns) {
2765
+ section.columns = section.columns.filter((col) => col.id !== columnId);
2766
+ }
2767
+ });
1881
2768
  }
1882
2769
  }
2770
+ this._removeColumnData(columnId, sectionId);
1883
2771
  this._dirty = true;
1884
2772
  this._validateForm();
1885
2773
  this.requestUpdate();
1886
2774
  this.dispatchEvent(new CustomEvent("column-remove", {
1887
- detail: { columnId },
2775
+ detail: { columnId, sectionId },
1888
2776
  bubbles: true
1889
2777
  }));
1890
2778
  }
2779
+ _handleColumnAddRequest(event) {
2780
+ const { sectionId, columnRequest } = event.detail;
2781
+ this._addColumnFromRequest(columnRequest, sectionId);
2782
+ }
2783
+ _addColumnFromRequest(request, sectionId) {
2784
+ var _a, _b, _c, _d, _e;
2785
+ const timestamp = Date.now();
2786
+ const newColumnId = `col-${timestamp}`;
2787
+ let title = "";
2788
+ let description = "";
2789
+ if (request.periodType === "instant") {
2790
+ title = request.instantDate || "Instant Date";
2791
+ description = `Instant period: ${request.instantDate}`;
2792
+ } else if (request.periodType === "duration" || request.periodType === "mixed") {
2793
+ title = `${request.startDate} / ${request.endDate}`;
2794
+ description = `Duration period: ${request.startDate} to ${request.endDate}`;
2795
+ }
2796
+ let dimensionData = {
2797
+ dimensionId: "period",
2798
+ memberValue: request.periodType === "instant" ? `instant-${request.instantDate}` : `duration-${request.startDate}-${request.endDate}`,
2799
+ memberLabel: title
2800
+ };
2801
+ if ((_b = (_a = this.xbrlInput) == null ? void 0 : _a.hypercubes) == null ? void 0 : _b[0]) {
2802
+ const hypercubeRole = this.xbrlInput.hypercubes[0].roles.find((hr) => hr.roleId === sectionId);
2803
+ if (((_c = hypercubeRole == null ? void 0 : hypercubeRole.items) == null ? void 0 : _c.length) === 1) {
2804
+ const item = hypercubeRole.items[0];
2805
+ if (item.dimensions.length === 1) {
2806
+ const dimension = item.dimensions[0];
2807
+ if (dimension.members.length > 0) {
2808
+ const axisLabel = ((_d = dimension.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _d.label) || dimension.conceptName;
2809
+ const firstMember = dimension.members[0];
2810
+ const memberLabel = ((_e = firstMember.labels.find((l2) => l2.role === "http://www.xbrl.org/2003/role/label")) == null ? void 0 : _e.label) || firstMember.conceptName;
2811
+ const periodPart = request.periodType === "instant" ? `(${request.instantDate})` : `(${request.startDate} / ${request.endDate})`;
2812
+ title = `${memberLabel} ${periodPart}`;
2813
+ dimensionData = {
2814
+ dimensionId: newColumnId,
2815
+ axisId: dimension.id,
2816
+ memberId: firstMember.id,
2817
+ memberValue: memberLabel,
2818
+ memberLabel,
2819
+ axis: axisLabel,
2820
+ axisLabel,
2821
+ memberKey: `${axisLabel} | ${memberLabel}`,
2822
+ dimensionIdKey: `${dimension.id} | ${firstMember.id}`
2823
+ };
2824
+ console.log(`📊 Applied hypercube dimension to new column: ${dimensionData.memberKey}`);
2825
+ }
2826
+ }
2827
+ }
2828
+ }
2829
+ const newColumn = {
2830
+ id: newColumnId,
2831
+ title,
2832
+ description,
2833
+ type: dimensionData.memberKey ? "dimension" : "dimension",
2834
+ order: this._columns.length,
2835
+ removable: true,
2836
+ dimensionData
2837
+ };
2838
+ if (this._currentSchema) {
2839
+ const targetSection = this._currentSchema.sections.find((section) => section.id === sectionId);
2840
+ if (targetSection) {
2841
+ if (!targetSection.columns) {
2842
+ targetSection.columns = [...this._columns];
2843
+ }
2844
+ targetSection.columns = [...targetSection.columns, newColumn];
2845
+ }
2846
+ }
2847
+ this._replicateFieldsForNewColumn(newColumnId, request, sectionId);
2848
+ this._dirty = true;
2849
+ this.requestUpdate();
2850
+ this.dispatchEvent(new CustomEvent("column-add", {
2851
+ detail: { column: newColumn },
2852
+ bubbles: true
2853
+ }));
2854
+ }
2855
+ _removeColumnData(columnId, sectionId) {
2856
+ if (!this._currentSchema)
2857
+ return;
2858
+ if (sectionId) {
2859
+ const targetSection = this._currentSchema.sections.find((section) => section.id === sectionId);
2860
+ if (targetSection) {
2861
+ this._removeColumnDataFromConcepts(targetSection.concepts, columnId);
2862
+ }
2863
+ } else {
2864
+ for (const conceptId in this._formData) {
2865
+ if (this._formData[conceptId][columnId] !== void 0) {
2866
+ delete this._formData[conceptId][columnId];
2867
+ }
2868
+ }
2869
+ }
2870
+ }
2871
+ _removeColumnDataFromConcepts(concepts, columnId) {
2872
+ concepts.forEach((concept) => {
2873
+ if (this._formData[concept.id] && this._formData[concept.id][columnId] !== void 0) {
2874
+ delete this._formData[concept.id][columnId];
2875
+ }
2876
+ if (concept.fields) {
2877
+ concept.fields = concept.fields.filter((field) => field.columnId !== columnId);
2878
+ }
2879
+ if (concept.children) {
2880
+ this._removeColumnDataFromConcepts(concept.children, columnId);
2881
+ }
2882
+ });
2883
+ }
2884
+ _shouldCreateFieldForConcept(concept, request) {
2885
+ if (concept.abstract) {
2886
+ return false;
2887
+ }
2888
+ if (concept.periodType) {
2889
+ if (request.periodType === "instant" && concept.periodType !== "instant") {
2890
+ return false;
2891
+ }
2892
+ if (request.periodType === "duration" && concept.periodType !== "duration") {
2893
+ return false;
2894
+ }
2895
+ }
2896
+ return true;
2897
+ }
2898
+ _replicateFieldsForNewColumn(columnId, request, sectionId) {
2899
+ if (!this._currentSchema)
2900
+ return;
2901
+ const formBuilder = new XBRLFormBuilder();
2902
+ const targetSection = this._currentSchema.sections.find((section) => section.id === sectionId);
2903
+ if (targetSection) {
2904
+ this._replicateFieldsForSection(targetSection.concepts, columnId, request, formBuilder);
2905
+ }
2906
+ }
2907
+ _replicateFieldsForSection(concepts, columnId, request, formBuilder) {
2908
+ concepts.forEach((concept) => {
2909
+ var _a;
2910
+ if (this._shouldCreateFieldForConcept(concept, request)) {
2911
+ const existingField = (_a = concept.fields) == null ? void 0 : _a[0];
2912
+ let field;
2913
+ if (existingField) {
2914
+ field = {
2915
+ id: `${concept.id}_${columnId}`,
2916
+ conceptId: concept.id,
2917
+ columnId,
2918
+ type: existingField.type,
2919
+ label: existingField.label,
2920
+ placeholder: existingField.placeholder,
2921
+ required: existingField.required,
2922
+ disabled: existingField.disabled,
2923
+ validation: existingField.validation,
2924
+ defaultValue: existingField.defaultValue,
2925
+ // Set period dates from the add column request
2926
+ periodStartDate: request.periodType === "instant" ? request.instantDate : request.startDate,
2927
+ periodEndDate: request.periodType === "instant" ? request.instantDate : request.endDate
2928
+ };
2929
+ } else {
2930
+ field = {
2931
+ id: `${concept.id}_${columnId}`,
2932
+ conceptId: concept.id,
2933
+ columnId,
2934
+ type: "text",
2935
+ label: concept.label || concept.name,
2936
+ placeholder: `Enter ${concept.name}`,
2937
+ required: false,
2938
+ disabled: false,
2939
+ validation: [],
2940
+ defaultValue: "",
2941
+ // Set period dates from the add column request
2942
+ periodStartDate: request.periodType === "instant" ? request.instantDate : request.startDate,
2943
+ periodEndDate: request.periodType === "instant" ? request.instantDate : request.endDate
2944
+ };
2945
+ }
2946
+ if (!concept.fields) {
2947
+ concept.fields = [];
2948
+ }
2949
+ concept.fields.push(field);
2950
+ }
2951
+ if (concept.children) {
2952
+ this._replicateFieldsForSection(concept.children, columnId, request, formBuilder);
2953
+ }
2954
+ });
2955
+ }
1891
2956
  _addColumn() {
1892
2957
  const newColumnId = `dim-${Date.now()}`;
1893
2958
  const newColumn = {
@@ -1913,15 +2978,95 @@ let JupiterDynamicForm = class extends LitElement {
1913
2978
  _handleSubmit() {
1914
2979
  this._submitted = true;
1915
2980
  this._validateForm();
2981
+ const submissionData = this._generateSubmissionData();
1916
2982
  this.dispatchEvent(new CustomEvent("form-submit", {
1917
2983
  detail: {
1918
2984
  data: this._formData,
2985
+ submissionData,
2986
+ valid: this._valid,
2987
+ errors: this._errors
2988
+ },
2989
+ bubbles: true
2990
+ }));
2991
+ }
2992
+ _handleSaveDraft() {
2993
+ const draftData = this._generateSubmissionData();
2994
+ this.dispatchEvent(new CustomEvent("form-save-draft", {
2995
+ detail: {
2996
+ data: this._formData,
2997
+ draftData,
1919
2998
  valid: this._valid,
1920
2999
  errors: this._errors
1921
3000
  },
1922
3001
  bubbles: true
1923
3002
  }));
1924
3003
  }
3004
+ _generateSubmissionData() {
3005
+ const submissionData = [];
3006
+ if (!this._currentSchema) {
3007
+ return submissionData;
3008
+ }
3009
+ this._currentSchema.sections.forEach((section) => {
3010
+ this._processConceptsForSubmission(section.concepts, submissionData, section);
3011
+ });
3012
+ return submissionData;
3013
+ }
3014
+ _findColumnById(columnId) {
3015
+ if (!this._currentSchema) {
3016
+ return void 0;
3017
+ }
3018
+ for (const section of this._currentSchema.sections) {
3019
+ if (section.columns) {
3020
+ const column = section.columns.find((col) => col.id === columnId);
3021
+ if (column) {
3022
+ return column;
3023
+ }
3024
+ }
3025
+ }
3026
+ return void 0;
3027
+ }
3028
+ _processConceptsForSubmission(concepts, submissionData, section) {
3029
+ concepts.forEach((concept) => {
3030
+ if (concept.fields && concept.fields.length > 0) {
3031
+ concept.fields.forEach((field) => {
3032
+ var _a, _b, _c;
3033
+ const conceptData = this._formData[concept.id];
3034
+ const fieldValue = conceptData == null ? void 0 : conceptData[field.columnId];
3035
+ if (fieldValue !== void 0 && fieldValue !== null && fieldValue !== "") {
3036
+ const column = this._findColumnById(field.columnId);
3037
+ const submissionEntry = {
3038
+ conceptId: concept.id,
3039
+ value: fieldValue,
3040
+ period: {
3041
+ type: concept.periodType || "duration"
3042
+ }
3043
+ };
3044
+ if (concept.periodType === "instant") {
3045
+ submissionEntry.period.date = field.periodStartDate || this.periodStartDate;
3046
+ } else {
3047
+ submissionEntry.period.startDate = field.periodStartDate || this.periodStartDate;
3048
+ submissionEntry.period.endDate = field.periodEndDate || this.periodEndDate;
3049
+ }
3050
+ if ((column == null ? void 0 : column.type) === "dimension" && ((_a = column.dimensionData) == null ? void 0 : _a.dimensionIdKey)) {
3051
+ submissionEntry.dimension = column.dimensionData.dimensionIdKey;
3052
+ } else if ((_b = section == null ? void 0 : section.columns) == null ? void 0 : _b.length) {
3053
+ const sectionDimensionColumn = section.columns.find((col) => {
3054
+ var _a2;
3055
+ return col.type === "dimension" && ((_a2 = col.dimensionData) == null ? void 0 : _a2.dimensionIdKey);
3056
+ });
3057
+ if ((_c = sectionDimensionColumn == null ? void 0 : sectionDimensionColumn.dimensionData) == null ? void 0 : _c.dimensionIdKey) {
3058
+ submissionEntry.dimension = sectionDimensionColumn.dimensionData.dimensionIdKey;
3059
+ }
3060
+ }
3061
+ submissionData.push(submissionEntry);
3062
+ }
3063
+ });
3064
+ }
3065
+ if (concept.children) {
3066
+ this._processConceptsForSubmission(concept.children, submissionData, section);
3067
+ }
3068
+ });
3069
+ }
1925
3070
  _handleReset() {
1926
3071
  this._formData = { ...this.initialData };
1927
3072
  this._touched.clear();
@@ -1982,19 +3127,6 @@ let JupiterDynamicForm = class extends LitElement {
1982
3127
  ` : ""}
1983
3128
  </div>
1984
3129
 
1985
- <!-- Add Column Section -->
1986
- ${config.enableColumnManagement !== false ? html`
1987
- <div class="add-column-container">
1988
- <button
1989
- class="add-column-btn"
1990
- @click="${this._addColumn}"
1991
- ?disabled="${this.disabled || this.readonly || config.maxColumns && this._columns.length >= config.maxColumns}"
1992
- >
1993
- Add Dimension Column
1994
- </button>
1995
- </div>
1996
- ` : ""}
1997
-
1998
3130
  <!-- Form Content -->
1999
3131
  <div class="form-sections">
2000
3132
  <!-- Validation Summary -->
@@ -2022,26 +3154,27 @@ let JupiterDynamicForm = class extends LitElement {
2022
3154
  @section-expand="${this._handleSectionExpand}"
2023
3155
  @concept-expand="${this._handleConceptExpand}"
2024
3156
  @column-remove="${this._handleColumnRemove}"
3157
+ @column-add-request="${this._handleColumnAddRequest}"
2025
3158
  ></jupiter-form-section>
2026
3159
  `)}
2027
3160
  </div>
2028
3161
 
2029
- <!-- Form Actions -->
3162
+ <!-- Form Actions - Fixed Footer -->
2030
3163
  <div class="form-actions">
2031
3164
  <button
2032
- class="btn-primary"
2033
- @click="${this._handleSubmit}"
3165
+ class="btn-secondary"
3166
+ @click="${this._handleSaveDraft}"
2034
3167
  ?disabled="${this.disabled || this.readonly}"
2035
3168
  >
2036
- Submit
3169
+ Save Draft
2037
3170
  </button>
2038
3171
 
2039
3172
  <button
2040
- class="btn-outline"
2041
- @click="${this._handleReset}"
2042
- ?disabled="${this.disabled || !this._dirty}"
3173
+ class="btn-primary"
3174
+ @click="${this._handleSubmit}"
3175
+ ?disabled="${this.disabled || this.readonly}"
2043
3176
  >
2044
- Reset
3177
+ Submit
2045
3178
  </button>
2046
3179
 
2047
3180
  <div class="form-meta">
@@ -2089,42 +3222,23 @@ JupiterDynamicForm.styles = css`
2089
3222
  }
2090
3223
 
2091
3224
  .form-actions {
3225
+ position: fixed;
3226
+ bottom: 0;
3227
+ left: 0;
3228
+ right: 0;
2092
3229
  padding: 16px 24px;
2093
3230
  border-top: 1px solid var(--jupiter-border-color, #ddd);
2094
3231
  background: var(--jupiter-form-actions-background, #f8f9fa);
2095
3232
  display: flex;
2096
3233
  gap: 12px;
2097
3234
  align-items: center;
2098
- }
2099
-
2100
- .add-column-container {
2101
- padding: 16px 24px;
2102
- border-bottom: 1px solid var(--jupiter-border-color, #ddd);
2103
- background: var(--jupiter-form-actions-background, #f8f9fa);
2104
- }
2105
-
2106
- .add-column-btn {
2107
- padding: 8px 16px;
2108
- background: var(--jupiter-primary-color, #1976d2);
2109
- color: white;
2110
- border: none;
2111
- border-radius: 4px;
2112
- cursor: pointer;
2113
- font-size: 14px;
2114
- font-weight: 500;
2115
- }
2116
-
2117
- .add-column-btn:hover {
2118
- background: var(--jupiter-primary-color-dark, #1565c0);
2119
- }
2120
-
2121
- .add-column-btn:disabled {
2122
- background: var(--jupiter-disabled-background, #ccc);
2123
- cursor: not-allowed;
3235
+ z-index: 1000;
3236
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
2124
3237
  }
2125
3238
 
2126
3239
  .form-sections {
2127
3240
  padding: 24px;
3241
+ padding-bottom: 100px; /* Add space for fixed footer */
2128
3242
  }
2129
3243
 
2130
3244
  .validation-summary {
@@ -2258,6 +3372,7 @@ JupiterDynamicForm = __decorateClass([
2258
3372
  const version = "1.0.0";
2259
3373
  export {
2260
3374
  FormValidator,
3375
+ JupiterAddColumnDialog,
2261
3376
  JupiterConceptTree,
2262
3377
  JupiterDynamicForm,
2263
3378
  JupiterFormField,