jupiter-dynamic-forms 1.0.1 → 1.2.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/core/dynamic-form.d.ts +2 -0
- package/dist/core/dynamic-form.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -13
- package/dist/index.mjs.map +1 -1
- package/dist/schema/types.d.ts +3 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/utils/xbrl-form-builder.d.ts +27 -3
- package/dist/utils/xbrl-form-builder.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -527,14 +527,15 @@ class XBRLFormBuilder {
|
|
|
527
527
|
* Build form schema from XBRL input data
|
|
528
528
|
* Creates accordion sections for each presentation role
|
|
529
529
|
*/
|
|
530
|
-
static buildFormSchema(xbrlInput) {
|
|
530
|
+
static buildFormSchema(xbrlInput, periodStartDate, periodEndDate) {
|
|
531
531
|
if (!xbrlInput.presentation || xbrlInput.presentation.length === 0) {
|
|
532
532
|
throw new Error("XBRL presentation data is required");
|
|
533
533
|
}
|
|
534
534
|
const presentationData = xbrlInput.presentation[0];
|
|
535
535
|
const sections = [];
|
|
536
536
|
presentationData.roles.forEach((role) => {
|
|
537
|
-
const section = this.buildSectionFromRole(role);
|
|
537
|
+
const section = this.buildSectionFromRole(role, periodStartDate, periodEndDate);
|
|
538
|
+
this.assignFieldColumnIds(section);
|
|
538
539
|
sections.push(section);
|
|
539
540
|
});
|
|
540
541
|
return {
|
|
@@ -548,34 +549,36 @@ class XBRLFormBuilder {
|
|
|
548
549
|
/**
|
|
549
550
|
* Build a form section from a presentation role
|
|
550
551
|
*/
|
|
551
|
-
static buildSectionFromRole(role) {
|
|
552
|
+
static buildSectionFromRole(role, periodStartDate, periodEndDate) {
|
|
552
553
|
var _a;
|
|
553
554
|
const title = this.extractRoleTitle(role.role);
|
|
554
555
|
const conceptTrees = [];
|
|
555
556
|
if ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) {
|
|
556
557
|
role.presentationLinkbase.concepts.forEach((concept) => {
|
|
557
|
-
const conceptTree = this.buildConceptTree(concept, 0);
|
|
558
|
+
const conceptTree = this.buildConceptTree(concept, 0, periodStartDate, periodEndDate);
|
|
558
559
|
if (conceptTree) {
|
|
559
560
|
conceptTrees.push(conceptTree);
|
|
560
561
|
}
|
|
561
562
|
});
|
|
562
563
|
}
|
|
564
|
+
const columns = this.generateDefaultColumnsForRole(role, periodStartDate || "2025-01-01", periodEndDate || "2025-12-31");
|
|
563
565
|
return {
|
|
564
566
|
id: role.id,
|
|
565
567
|
title,
|
|
566
568
|
description: `Section for ${title}`,
|
|
567
569
|
concepts: conceptTrees,
|
|
570
|
+
columns,
|
|
568
571
|
expanded: false
|
|
569
572
|
};
|
|
570
573
|
}
|
|
571
574
|
/**
|
|
572
575
|
* Build concept tree from XBRL presentation concept
|
|
573
576
|
*/
|
|
574
|
-
static buildConceptTree(concept, level) {
|
|
577
|
+
static buildConceptTree(concept, level, periodStartDate, periodEndDate) {
|
|
575
578
|
const label = this.getPreferredLabel(concept.labels);
|
|
576
579
|
const fields = [];
|
|
577
580
|
if (!concept.elementAbstract) {
|
|
578
|
-
const field = this.createFieldFromConcept(concept);
|
|
581
|
+
const field = this.createFieldFromConcept(concept, periodStartDate, periodEndDate);
|
|
579
582
|
if (field) {
|
|
580
583
|
fields.push(field);
|
|
581
584
|
}
|
|
@@ -583,7 +586,7 @@ class XBRLFormBuilder {
|
|
|
583
586
|
const children = [];
|
|
584
587
|
if (concept.children && concept.children.length > 0) {
|
|
585
588
|
concept.children.forEach((child) => {
|
|
586
|
-
const childTree = this.buildConceptTree(child, level + 1);
|
|
589
|
+
const childTree = this.buildConceptTree(child, level + 1, periodStartDate, periodEndDate);
|
|
587
590
|
if (childTree) {
|
|
588
591
|
children.push(childTree);
|
|
589
592
|
}
|
|
@@ -604,14 +607,21 @@ class XBRLFormBuilder {
|
|
|
604
607
|
/**
|
|
605
608
|
* Create form field from XBRL concept
|
|
606
609
|
*/
|
|
607
|
-
static createFieldFromConcept(concept) {
|
|
610
|
+
static createFieldFromConcept(concept, periodStartDate, periodEndDate) {
|
|
608
611
|
const fieldType = this.mapXBRLTypeToFieldType(concept.type);
|
|
609
612
|
const label = this.getPreferredLabel(concept.labels);
|
|
610
|
-
|
|
613
|
+
let columnId = "default";
|
|
614
|
+
if (concept.periodType === "instant") {
|
|
615
|
+
columnId = "instant";
|
|
616
|
+
} else if (concept.periodType === "duration") {
|
|
617
|
+
columnId = "duration_start";
|
|
618
|
+
} else {
|
|
619
|
+
columnId = "default";
|
|
620
|
+
}
|
|
621
|
+
const field = {
|
|
611
622
|
id: `${concept.id}_field`,
|
|
612
623
|
conceptId: concept.id,
|
|
613
|
-
columnId
|
|
614
|
-
// Default column, will be expanded later
|
|
624
|
+
columnId,
|
|
615
625
|
type: fieldType,
|
|
616
626
|
label,
|
|
617
627
|
placeholder: `Enter ${label.toLowerCase()}`,
|
|
@@ -620,6 +630,11 @@ class XBRLFormBuilder {
|
|
|
620
630
|
disabled: concept.elementAbstract,
|
|
621
631
|
defaultValue: null
|
|
622
632
|
};
|
|
633
|
+
if (concept.periodType === "duration" || concept.periodType === "instant") {
|
|
634
|
+
field.periodStartDate = periodStartDate || "2025-01-01";
|
|
635
|
+
field.periodEndDate = periodEndDate || "2025-12-31";
|
|
636
|
+
}
|
|
637
|
+
return field;
|
|
623
638
|
}
|
|
624
639
|
/**
|
|
625
640
|
* Map XBRL data type to form field type
|
|
@@ -655,6 +670,162 @@ class XBRLFormBuilder {
|
|
|
655
670
|
return terse.label;
|
|
656
671
|
return labels[0].label;
|
|
657
672
|
}
|
|
673
|
+
/**
|
|
674
|
+
* Assign appropriate column IDs to fields based on section's generated columns
|
|
675
|
+
*/
|
|
676
|
+
static assignFieldColumnIds(section) {
|
|
677
|
+
if (!section.columns || section.columns.length === 0) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const columnIds = section.columns.map((col) => col.id);
|
|
681
|
+
section.concepts.forEach((conceptTree) => {
|
|
682
|
+
this.assignColumnIdsToConceptFields(conceptTree, columnIds);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Recursively assign column IDs to fields in a concept tree
|
|
687
|
+
*/
|
|
688
|
+
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
|
+
}
|
|
699
|
+
});
|
|
700
|
+
if (conceptTree.children) {
|
|
701
|
+
conceptTree.children.forEach((child) => {
|
|
702
|
+
this.assignColumnIdsToConceptFields(child, columnIds);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Get all non-abstract concepts from a role recursively
|
|
708
|
+
*/
|
|
709
|
+
static getAllNonAbstractConcepts(role) {
|
|
710
|
+
var _a;
|
|
711
|
+
const nonAbstractConcepts = [];
|
|
712
|
+
if ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) {
|
|
713
|
+
role.presentationLinkbase.concepts.forEach((concept) => {
|
|
714
|
+
this.collectNonAbstractConcepts(concept, nonAbstractConcepts);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
return nonAbstractConcepts;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Recursively collect non-abstract concepts from a concept tree
|
|
721
|
+
*/
|
|
722
|
+
static collectNonAbstractConcepts(concept, collection) {
|
|
723
|
+
if (!concept.elementAbstract) {
|
|
724
|
+
collection.push(concept);
|
|
725
|
+
}
|
|
726
|
+
if (concept.children && concept.children.length > 0) {
|
|
727
|
+
concept.children.forEach((child) => {
|
|
728
|
+
this.collectNonAbstractConcepts(child, collection);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Generate default columns based on period types of non-abstract concepts in a role
|
|
734
|
+
*/
|
|
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) {
|
|
739
|
+
console.log("📊 No non-abstract concepts found, using default column");
|
|
740
|
+
return [{
|
|
741
|
+
id: "default",
|
|
742
|
+
title: "Value",
|
|
743
|
+
description: "Default value column",
|
|
744
|
+
type: "base",
|
|
745
|
+
order: 0,
|
|
746
|
+
removable: false
|
|
747
|
+
}];
|
|
748
|
+
}
|
|
749
|
+
const periodTypes = new Set(
|
|
750
|
+
nonAbstractConcepts.filter((concept) => concept.periodType).map((concept) => concept.periodType)
|
|
751
|
+
);
|
|
752
|
+
console.log(`📅 Found period types: ${Array.from(periodTypes).join(", ")}`);
|
|
753
|
+
const columns = [];
|
|
754
|
+
if (periodTypes.size === 0) {
|
|
755
|
+
console.log("📅 No period types found, using default column");
|
|
756
|
+
columns.push({
|
|
757
|
+
id: "default",
|
|
758
|
+
title: "Value",
|
|
759
|
+
description: "Default value column",
|
|
760
|
+
type: "base",
|
|
761
|
+
order: 0,
|
|
762
|
+
removable: false
|
|
763
|
+
});
|
|
764
|
+
} else if (periodTypes.size === 1 && periodTypes.has("instant")) {
|
|
765
|
+
console.log("📅 All concepts are instant type, creating single column");
|
|
766
|
+
columns.push({
|
|
767
|
+
id: "instant",
|
|
768
|
+
title: `As of ${this.formatDateForDisplay(periodStartDate)}`,
|
|
769
|
+
description: `Values as of ${periodStartDate}`,
|
|
770
|
+
type: "base",
|
|
771
|
+
order: 0,
|
|
772
|
+
removable: false
|
|
773
|
+
});
|
|
774
|
+
} else if (periodTypes.size === 1 && periodTypes.has("duration")) {
|
|
775
|
+
console.log("📅 All concepts are duration type, creating two columns");
|
|
776
|
+
columns.push({
|
|
777
|
+
id: "duration_start",
|
|
778
|
+
title: `From ${this.formatDateForDisplay(periodStartDate)}`,
|
|
779
|
+
description: `Period start: ${periodStartDate}`,
|
|
780
|
+
type: "base",
|
|
781
|
+
order: 0,
|
|
782
|
+
removable: false
|
|
783
|
+
});
|
|
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
|
+
} else {
|
|
793
|
+
console.log("📅 Mixed period types found, creating two columns for full range");
|
|
794
|
+
columns.push({
|
|
795
|
+
id: "period_start",
|
|
796
|
+
title: `From ${this.formatDateForDisplay(periodStartDate)}`,
|
|
797
|
+
description: `Period start: ${periodStartDate}`,
|
|
798
|
+
type: "base",
|
|
799
|
+
order: 0,
|
|
800
|
+
removable: false
|
|
801
|
+
});
|
|
802
|
+
columns.push({
|
|
803
|
+
id: "period_end",
|
|
804
|
+
title: `To ${this.formatDateForDisplay(periodEndDate)}`,
|
|
805
|
+
description: `Period end: ${periodEndDate}`,
|
|
806
|
+
type: "base",
|
|
807
|
+
order: 1,
|
|
808
|
+
removable: false
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
console.log(`📊 Generated ${columns.length} columns for role "${role.role}":`, columns.map((c2) => c2.title));
|
|
812
|
+
return columns;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Format date for display in column headers
|
|
816
|
+
*/
|
|
817
|
+
static formatDateForDisplay(dateString) {
|
|
818
|
+
try {
|
|
819
|
+
const date = new Date(dateString);
|
|
820
|
+
return date.toLocaleDateString("en-US", {
|
|
821
|
+
year: "numeric",
|
|
822
|
+
month: "short",
|
|
823
|
+
day: "numeric"
|
|
824
|
+
});
|
|
825
|
+
} catch {
|
|
826
|
+
return dateString;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
658
829
|
/**
|
|
659
830
|
* Extract entity name from entrypoint URL
|
|
660
831
|
*/
|
|
@@ -1568,6 +1739,8 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
1568
1739
|
this.initialData = {};
|
|
1569
1740
|
this.disabled = false;
|
|
1570
1741
|
this.readonly = false;
|
|
1742
|
+
this.periodStartDate = "2025-01-01";
|
|
1743
|
+
this.periodEndDate = "2025-12-31";
|
|
1571
1744
|
this._formData = {};
|
|
1572
1745
|
this._columns = [];
|
|
1573
1746
|
this._errors = [];
|
|
@@ -1590,7 +1763,12 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
1590
1763
|
if (this.xbrlInput) {
|
|
1591
1764
|
try {
|
|
1592
1765
|
console.log("🔄 Initializing form from XBRL input:", this.xbrlInput);
|
|
1593
|
-
this.
|
|
1766
|
+
console.log("📅 Using period dates:", this.periodStartDate, "to", this.periodEndDate);
|
|
1767
|
+
this._currentSchema = XBRLFormBuilder.buildFormSchema(
|
|
1768
|
+
this.xbrlInput,
|
|
1769
|
+
this.periodStartDate,
|
|
1770
|
+
this.periodEndDate
|
|
1771
|
+
);
|
|
1594
1772
|
console.log("✅ Generated schema with sections:", this._currentSchema.sections.length);
|
|
1595
1773
|
this._columns = [
|
|
1596
1774
|
{
|
|
@@ -1835,7 +2013,7 @@ let JupiterDynamicForm = class extends LitElement {
|
|
|
1835
2013
|
${schema.sections.map((section) => html`
|
|
1836
2014
|
<jupiter-form-section
|
|
1837
2015
|
.section="${section}"
|
|
1838
|
-
.columns="${this._columns}"
|
|
2016
|
+
.columns="${section.columns || this._columns}"
|
|
1839
2017
|
.formData="${this._formData}"
|
|
1840
2018
|
.disabled="${this.disabled || this.readonly}"
|
|
1841
2019
|
.collapsible="${config.collapsibleSections !== false}"
|
|
@@ -2044,6 +2222,12 @@ __decorateClass([
|
|
|
2044
2222
|
__decorateClass([
|
|
2045
2223
|
n2({ type: Boolean })
|
|
2046
2224
|
], JupiterDynamicForm.prototype, "readonly", 2);
|
|
2225
|
+
__decorateClass([
|
|
2226
|
+
n2({ type: String })
|
|
2227
|
+
], JupiterDynamicForm.prototype, "periodStartDate", 2);
|
|
2228
|
+
__decorateClass([
|
|
2229
|
+
n2({ type: String })
|
|
2230
|
+
], JupiterDynamicForm.prototype, "periodEndDate", 2);
|
|
2047
2231
|
__decorateClass([
|
|
2048
2232
|
r()
|
|
2049
2233
|
], JupiterDynamicForm.prototype, "_formData", 2);
|