jupiter-dynamic-forms 1.15.0 → 1.15.2

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
@@ -3073,18 +3073,29 @@ let JupiterFormField = class extends LitElement {
3073
3073
  if (availableUnits.length === 0) {
3074
3074
  return;
3075
3075
  }
3076
- if (!this.unit) {
3077
- this._xbrlErrors = [
3078
- ...this._xbrlErrors,
3079
- {
3080
- type: "unit",
3081
- message: I18n.t("validation.unitRequired"),
3082
- expectedValue: availableUnits.map((u2) => u2.label).join(", "),
3083
- actualValue: ""
3084
- }
3085
- ];
3086
- console.log(`❌ [FormField] Unit validation failed for ${this.conceptId}: unit is required but not selected`);
3076
+ if (this.unit)
3077
+ return;
3078
+ const defaultUnit = availableUnits.find((u2) => u2.isDefault === true) ?? (availableUnits.length === 1 ? availableUnits[0] : null);
3079
+ if (defaultUnit) {
3080
+ this.unit = defaultUnit.id;
3081
+ this.dispatchEvent(new CustomEvent("unit-change", {
3082
+ detail: { fieldId: this.field.id, conceptId: this.conceptId, columnId: this.columnId, unit: this.unit },
3083
+ bubbles: true,
3084
+ composed: true
3085
+ }));
3086
+ console.log(`🎯 [FormField] Auto-selected default unit on blur: ${this.unit} for ${this.conceptId}`);
3087
+ return;
3087
3088
  }
3089
+ this._xbrlErrors = [
3090
+ ...this._xbrlErrors,
3091
+ {
3092
+ type: "unit",
3093
+ message: I18n.t("validation.unitRequired"),
3094
+ expectedValue: availableUnits.map((u2) => u2.label).join(", "),
3095
+ actualValue: ""
3096
+ }
3097
+ ];
3098
+ console.log(`❌ [FormField] Unit validation failed for ${this.conceptId}: unit is required but not selected`);
3088
3099
  }
3089
3100
  _handlePeriodIconClick() {
3090
3101
  this._availableUnits = this._collectUnitsForConceptType();
@@ -3512,7 +3523,7 @@ let JupiterFormField = class extends LitElement {
3512
3523
  }
3513
3524
  const hasUserValue = this.value !== null && this.value !== void 0;
3514
3525
  const effectiveValue = isPredefinedValue ? this.masterData[baseConceptId] : hasUserValue ? this.value : factValue;
3515
- const effectiveDisabled = isPredefinedValue || this.disabled;
3526
+ const effectiveDisabled = isPredefinedValue || this.disabled || this.mode === "readonly";
3516
3527
  return html`
3517
3528
  <div class="field-container">
3518
3529
  ${showLabel ? html`
@@ -3524,9 +3535,9 @@ let JupiterFormField = class extends LitElement {
3524
3535
  <div class="field-wrapper">
3525
3536
  ${this._renderInput(effectiveValue, effectiveDisabled)}
3526
3537
 
3527
- ${hasPeriodControl ? html`
3528
- <button
3529
- class="period-icon-btn"
3538
+ ${hasPeriodControl && this.mode !== "readonly" ? html`
3539
+ <button
3540
+ class="period-icon-btn"
3530
3541
  type="button"
3531
3542
  @click="${this._handlePeriodIconClick}"
3532
3543
  title="Edit period"
@@ -4978,6 +4989,20 @@ let JupiterFormSection = class extends LitElement {
4978
4989
  }
4979
4990
  return result;
4980
4991
  }
4992
+ /**
4993
+ * Returns true when every column cell for a concept has no value.
4994
+ * Used in readonly mode to hide fully-blank rows.
4995
+ */
4996
+ _isConceptRowBlank(concept) {
4997
+ var _a;
4998
+ for (const column2 of this.columns) {
4999
+ const value = (_a = this.formData[concept.id]) == null ? void 0 : _a[column2.id];
5000
+ if (value !== null && value !== void 0 && value !== "") {
5001
+ return false;
5002
+ }
5003
+ }
5004
+ return true;
5005
+ }
4981
5006
  _getAllConceptIds(concepts) {
4982
5007
  const result = [];
4983
5008
  for (const concept of concepts) {
@@ -5109,24 +5134,25 @@ let JupiterFormSection = class extends LitElement {
5109
5134
  ` : ""}
5110
5135
  </div>
5111
5136
 
5112
- <!-- Column menu icon (3 vertical dots) -->
5113
- <div
5137
+ <!-- Column menu icon (hidden in readonly mode) -->
5138
+ ${this.mode !== "readonly" ? html`
5139
+ <div
5114
5140
  class="column-menu-icon"
5115
5141
  @click="${(e2) => this._toggleColumnMenu(column2.id, e2)}"
5116
5142
  title="${I18n.t("column.columnOptions")}"
5117
5143
  >⋮</div>
5118
-
5144
+
5119
5145
  <!-- Context menu -->
5120
5146
  ${this._openMenuColumnId === column2.id ? html`
5121
5147
  <div class="column-context-menu">
5122
- <button
5148
+ <button
5123
5149
  class="column-context-menu-item"
5124
5150
  @click="${(e2) => this._handleAddColumnFromMenu(column2.id, e2)}"
5125
5151
  >
5126
5152
  ${I18n.t("column.addColumn")}
5127
5153
  </button>
5128
5154
  ${column2.removable ? html`
5129
- <button
5155
+ <button
5130
5156
  class="column-context-menu-item remove"
5131
5157
  @click="${(e2) => {
5132
5158
  e2.stopPropagation();
@@ -5138,13 +5164,14 @@ let JupiterFormSection = class extends LitElement {
5138
5164
  ` : ""}
5139
5165
  </div>
5140
5166
  ` : ""}
5167
+ ` : ""}
5141
5168
  </th>
5142
5169
  `;
5143
5170
  })}
5144
5171
  </tr>
5145
5172
  </thead>
5146
5173
  <tbody class="table-body">
5147
- ${this._flattenConcepts(this.section.concepts, this._expandedConcepts).map((concept) => html`
5174
+ ${this._flattenConcepts(this.section.concepts, this._expandedConcepts).filter((concept) => this.mode !== "readonly" || !this._isConceptRowBlank(concept)).map((concept) => html`
5148
5175
  <tr>
5149
5176
  <jupiter-concept-tree
5150
5177
  .concept="${concept}"
@@ -5765,13 +5792,9 @@ let JupiterFilterRolesDialog = class extends LitElement {
5765
5792
  }));
5766
5793
  }
5767
5794
  _handleApply() {
5768
- const currentlySelected = Array.from(this._tempSelectedRoles);
5769
- const orderedByOriginal = currentlySelected.sort((a2, b2) => {
5770
- const indexA = this.availableRoles.findIndex((r2) => r2.id === a2);
5771
- const indexB = this.availableRoles.findIndex((r2) => r2.id === b2);
5772
- return indexA - indexB;
5773
- });
5774
- this._chosenRoleOrder = orderedByOriginal;
5795
+ const orderedSelected = this._chosenRoleOrder.filter((id) => this._tempSelectedRoles.has(id));
5796
+ const notInOrder = Array.from(this._tempSelectedRoles).filter((id) => !this._chosenRoleOrder.includes(id));
5797
+ this._chosenRoleOrder = [...orderedSelected, ...notInOrder];
5775
5798
  const orderedRolesWithMetadata = this._chosenRoleOrder.map((roleId, index) => {
5776
5799
  var _a;
5777
5800
  const role = this.availableRoles.find((r2) => r2.id === roleId);
@@ -7197,6 +7220,7 @@ let JupiterDynamicForm = class extends LitElement {
7197
7220
  console.log("🌐 Using language:", this.language);
7198
7221
  this._initializePeriodPreferencesFromData();
7199
7222
  this._applyAxisFilterToPeriodPreferences();
7223
+ this._applyDimensionShowPreviousYear();
7200
7224
  console.log("⚙️ Using period preferences:", this._periodPreferences);
7201
7225
  this._currentSchema = XBRLFormBuilder.buildFormSchema(
7202
7226
  this.xbrlInput,
@@ -7537,6 +7561,41 @@ let JupiterDynamicForm = class extends LitElement {
7537
7561
  }
7538
7562
  this._periodPreferences = preferences;
7539
7563
  }
7564
+ /**
7565
+ * In admin mode, auto-enables showPreviousYear for roles that have at least one dimension
7566
+ * member selected (from roleFilterAxes defaults). This ensures such roles render with a
7567
+ * minimum of two columns (current + previous year) on fresh initialization.
7568
+ *
7569
+ * Skipped when form state is being restored from external props or localStorage so that
7570
+ * the user's previously saved preferences are not overridden.
7571
+ */
7572
+ _applyDimensionShowPreviousYear() {
7573
+ var _a, _b;
7574
+ if (this.mode !== "admin")
7575
+ return;
7576
+ const hasExternalState = !!(this.dynaformsFacts && this.dynaformsMetadata);
7577
+ const hasLocalDraft = ((_a = this._draftStorageService) == null ? void 0 : _a.hasDraft()) ?? false;
7578
+ if (hasExternalState || hasLocalDraft)
7579
+ return;
7580
+ const updated = { ...this._periodPreferences };
7581
+ let changed = false;
7582
+ for (const roleId of Object.keys(updated)) {
7583
+ const prefs = updated[roleId];
7584
+ if (prefs.showPreviousYear)
7585
+ continue;
7586
+ const hasDimensionMembers = ((_b = prefs.dimensionSelections) == null ? void 0 : _b.some(
7587
+ (ds) => ds.selectedMemberIds && ds.selectedMemberIds.length > 0
7588
+ )) ?? false;
7589
+ if (hasDimensionMembers) {
7590
+ updated[roleId] = { ...prefs, showPreviousYear: true };
7591
+ changed = true;
7592
+ console.log(`📅 [JDF-010] Auto-enabled showPreviousYear for role "${roleId}" (has dimension members)`);
7593
+ }
7594
+ }
7595
+ if (changed) {
7596
+ this._periodPreferences = updated;
7597
+ }
7598
+ }
7540
7599
  /**
7541
7600
  * Helper method to extract roleIds from _selectedRoleIds
7542
7601
  * Handles both legacy string[] and enhanced object[] structure
@@ -9684,6 +9743,32 @@ let JupiterDynamicForm = class extends LitElement {
9684
9743
  _toggleSidePanelCollapse() {
9685
9744
  this._sidePanelCollapsed = !this._sidePanelCollapsed;
9686
9745
  }
9746
+ /**
9747
+ * Returns true when every non-abstract concept row in the section has no value
9748
+ * in any column. Used in readonly mode to hide fully-blank sections.
9749
+ */
9750
+ _isSectionBlank(section2) {
9751
+ const columns = section2.columns || this._columns;
9752
+ const hasValue = (concepts) => {
9753
+ var _a;
9754
+ for (const concept of concepts) {
9755
+ if (!concept.abstract) {
9756
+ for (const column2 of columns) {
9757
+ const value = (_a = this._formData[concept.id]) == null ? void 0 : _a[column2.id];
9758
+ if (value !== null && value !== void 0 && value !== "") {
9759
+ return true;
9760
+ }
9761
+ }
9762
+ }
9763
+ if (concept.children && concept.children.length > 0) {
9764
+ if (hasValue(concept.children))
9765
+ return true;
9766
+ }
9767
+ }
9768
+ return false;
9769
+ };
9770
+ return !hasValue(section2.concepts);
9771
+ }
9687
9772
  _filterSidePanelSections(sections) {
9688
9773
  if (!this._sidePanelSearchQuery.trim()) {
9689
9774
  return sections;
@@ -10076,7 +10161,7 @@ let JupiterDynamicForm = class extends LitElement {
10076
10161
  <p>${I18n.t("filter.selectRoles")}</p>
10077
10162
  <p>${I18n.t("filter.roles")}: ${this._allSections.length}</p>
10078
10163
  </div>
10079
- ` : schema.sections.map((section2, index) => {
10164
+ ` : (this.mode === "readonly" ? schema.sections.filter((s2) => !this._isSectionBlank(s2)) : schema.sections).map((section2, index) => {
10080
10165
  var _a;
10081
10166
  return html`
10082
10167
  <jupiter-form-section
@@ -10113,7 +10198,7 @@ let JupiterDynamicForm = class extends LitElement {
10113
10198
  }
10114
10199
  _renderSidePanelLayout(schema, config, showValidationSummary, errorCount) {
10115
10200
  var _a;
10116
- const visibleSections = schema.sections;
10201
+ const visibleSections = this.mode === "readonly" ? schema.sections.filter((s2) => !this._isSectionBlank(s2)) : schema.sections;
10117
10202
  const filteredSections = this._filterSidePanelSections(visibleSections);
10118
10203
  if (!this._activeSidePanelRoleId || !filteredSections.find((s2) => s2.id === this._activeSidePanelRoleId)) {
10119
10204
  this._activeSidePanelRoleId = filteredSections.length > 0 ? filteredSections[0].id : null;
@@ -10290,7 +10375,8 @@ let JupiterDynamicForm = class extends LitElement {
10290
10375
  <!-- Form Content -->
10291
10376
  ${this.display === "sidePanel" ? this._renderSidePanelLayout(schema, config, showValidationSummary, errorCount) : this._renderAccordionLayout(schema, config, showValidationSummary, errorCount)}
10292
10377
 
10293
- <!-- Form Actions - Fixed Footer -->
10378
+ <!-- Form Actions - Fixed Footer (hidden in readonly mode) -->
10379
+ ${this.mode !== "readonly" ? html`
10294
10380
  <div class="form-actions">
10295
10381
  <!-- Filter Roles Button (shown when more than 10 roles) -->
10296
10382
  ${this._shouldShowFilterButton() ? html`
@@ -10325,8 +10411,8 @@ let JupiterDynamicForm = class extends LitElement {
10325
10411
  ${this.submitButtonLabel || I18n.t("form.submit")}
10326
10412
  </button>
10327
10413
 
10328
-
10329
10414
  </div>
10415
+ ` : ""}
10330
10416
 
10331
10417
  <!-- Filter Roles Dialog -->
10332
10418
  ${this._showFilterDialog ? html`