jupiter-dynamic-forms 1.18.5 → 1.18.7

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
@@ -1711,7 +1711,9 @@ const filter$1 = {
1711
1711
  showFactsOnlyDescription: "Hide all blank rows and sections. Only roles and rows with at least one filled value will be shown.",
1712
1712
  searchByConcept: "Search by concept",
1713
1713
  searchByConceptDescription: "Type concept name or label to filter roles and rows. Press Apply Filter to apply.",
1714
- searchByConceptPlaceholder: "Type concept name or label and press Apply Filter..."
1714
+ searchByConceptPlaceholder: "Type concept name or label and press Apply Filter...",
1715
+ clearFilters: "Clear Filters",
1716
+ resetFilter: "Reset"
1715
1717
  };
1716
1718
  const column$1 = {
1717
1719
  addColumn: "Add Column",
@@ -1884,7 +1886,9 @@ const filter = {
1884
1886
  showFactsOnlyDescription: "Verberg alle lege rijen en secties. Alleen rollen en rijen met minimaal één ingevulde waarde worden weergegeven.",
1885
1887
  searchByConcept: "Zoeken op concept",
1886
1888
  searchByConceptDescription: "Typ een conceptnaam of label om rollen en rijen te filteren. Druk op Filter toepassen om te activeren.",
1887
- searchByConceptPlaceholder: "Typ conceptnaam of label en druk op Filter toepassen..."
1889
+ searchByConceptPlaceholder: "Typ conceptnaam of label en druk op Filter toepassen...",
1890
+ clearFilters: "Filters wissen",
1891
+ resetFilter: "Resetten"
1888
1892
  };
1889
1893
  const column = {
1890
1894
  addColumn: "Kolom toevoegen",
@@ -3052,6 +3056,16 @@ function determineInputTypeFromBaseChain(baseTypeChain, datatypes) {
3052
3056
  placeholder: I18n.t("field.enterWholeNumber")
3053
3057
  };
3054
3058
  }
3059
+ if (chainLower.some((type) => type.includes("percent") || type.includes("pure"))) {
3060
+ return {
3061
+ fieldType: "percentage",
3062
+ htmlInputType: "text",
3063
+ inputMode: "decimal",
3064
+ allowDecimals: true,
3065
+ step: 0.01,
3066
+ placeholder: I18n.t("field.enterPercentage")
3067
+ };
3068
+ }
3055
3069
  if (chainLower.some((type) => type.includes("decimal") || type === "decimal")) {
3056
3070
  if (chainString.includes("monetary") || chainString.includes("currency")) {
3057
3071
  if (chainString.includes("nodecimals") || chainString.includes("no-decimals") || chainString.includes("nodecimals20")) {
@@ -3226,6 +3240,17 @@ let JupiterFormField = class extends LitElement {
3226
3240
  }
3227
3241
  this._numericDraftValue = null;
3228
3242
  }
3243
+ if (this._isPercentItemType() && this.value !== null && this.value !== void 0) {
3244
+ const numVal = Number(this.value);
3245
+ if (!isNaN(numVal) && Math.abs(numVal) >= 1) {
3246
+ this.value = numVal / 100;
3247
+ const oldValue = numVal;
3248
+ this.dispatchEvent(new CustomEvent("field-change", {
3249
+ detail: { fieldId: this.field.id, conceptId: this.conceptId, columnId: this.columnId, value: this.value, oldValue },
3250
+ bubbles: true
3251
+ }));
3252
+ }
3253
+ }
3229
3254
  this._touched = true;
3230
3255
  this._validateXBRLDatatype();
3231
3256
  this._validateUnitSelection();
@@ -3260,6 +3285,16 @@ let JupiterFormField = class extends LitElement {
3260
3285
  (type) => type.includes("monetary") || type.includes("Monetary")
3261
3286
  );
3262
3287
  }
3288
+ _isPercentItemType() {
3289
+ if (!this.conceptType)
3290
+ return false;
3291
+ if (this.conceptType.toLowerCase().includes("percent"))
3292
+ return true;
3293
+ if (!this.datatypes)
3294
+ return false;
3295
+ const baseTypeChain = resolveBaseTypeChain(this.conceptType, this.datatypes);
3296
+ return baseTypeChain.some((type) => type.toLowerCase().includes("percent"));
3297
+ }
3263
3298
  /**
3264
3299
  * Prevents non-numeric input in number fields for Firefox compatibility
3265
3300
  * Firefox allows typing any character in type="number" inputs
@@ -3775,7 +3810,14 @@ let JupiterFormField = class extends LitElement {
3775
3810
  const step = typeConfig.step !== void 0 ? typeConfig.step : isNumericType(effectiveFieldType) ? "any" : void 0;
3776
3811
  const min = typeConfig.min;
3777
3812
  const max = typeConfig.max;
3778
- const renderedInputValue = this._isNumericField() && this._numericDraftValue !== null ? this._numericDraftValue : effectiveValue ?? "";
3813
+ let renderedInputValue;
3814
+ if (this._isNumericField() && this._numericDraftValue !== null) {
3815
+ renderedInputValue = this._numericDraftValue;
3816
+ } else if (effectiveFieldType === "percentage" && effectiveValue !== null && effectiveValue !== void 0 && effectiveValue !== "") {
3817
+ renderedInputValue = Number(effectiveValue).toFixed(4);
3818
+ } else {
3819
+ renderedInputValue = effectiveValue ?? "";
3820
+ }
3779
3821
  return html`
3780
3822
  <input
3781
3823
  id="${fieldId}"
@@ -3969,7 +4011,7 @@ let JupiterFormField = class extends LitElement {
3969
4011
  </div>
3970
4012
  ` : ""}
3971
4013
 
3972
- ${this._isMonetaryType() ? html`
4014
+ ${this._isMonetaryType() || this._isPercentItemType() ? html`
3973
4015
  <div class="period-controls">
3974
4016
  <label>${I18n.t("field.scale")}:</label>
3975
4017
  <input
@@ -3977,7 +4019,7 @@ let JupiterFormField = class extends LitElement {
3977
4019
  min="0"
3978
4020
  class="typed-member-input"
3979
4021
  placeholder="Leave blank for INF"
3980
- .value="${this.decimals !== void 0 && this.decimals !== null && this.decimals !== "" ? this.decimals : this.globalDecimals !== "INF" ? this.globalDecimals : ""}"
4022
+ .value="${this.decimals !== void 0 && this.decimals !== null && this.decimals !== "" ? this.decimals : this._isPercentItemType() ? "4" : this.globalDecimals !== "INF" ? this.globalDecimals : ""}"
3981
4023
  @input="${this._handleDecimalsChange}"
3982
4024
  ?disabled="${this.disabled}"
3983
4025
  />
@@ -6886,8 +6928,11 @@ let JupiterAdvancedFilter = class extends LitElement {
6886
6928
  composed: true
6887
6929
  }));
6888
6930
  }
6931
+ _sanitizeSearchText(text) {
6932
+ return text.replace(/[''‚‛′‵]/g, "'").replace(/[""„‟″‶]/g, '"').replace(/[​‌‍­]/g, "").replace(/\s+/g, " ").trim();
6933
+ }
6889
6934
  _handleConceptSearchInput(e2) {
6890
- this._localConceptSearchText = e2.target.value;
6935
+ this._localConceptSearchText = this._sanitizeSearchText(e2.target.value);
6891
6936
  this.dispatchEvent(new CustomEvent("filter-change", {
6892
6937
  detail: { showFactsOnly: this.showFactsOnly, conceptSearchText: this._localConceptSearchText },
6893
6938
  bubbles: true,
@@ -7632,6 +7677,14 @@ let JupiterFilterRolesDialog = class extends LitElement {
7632
7677
  bubbles: true
7633
7678
  }));
7634
7679
  }
7680
+ _handleAdvancedFilterReset() {
7681
+ this._showFactsOnly = false;
7682
+ this._conceptSearchText = "";
7683
+ this.dispatchEvent(new CustomEvent("advanced-filter-apply", {
7684
+ detail: { showFactsOnly: false, conceptSearchText: "" },
7685
+ bubbles: true
7686
+ }));
7687
+ }
7635
7688
  _handleBackdropClick(event) {
7636
7689
  if (event.target === this) {
7637
7690
  this._handleCancel();
@@ -8113,6 +8166,11 @@ let JupiterFilterRolesDialog = class extends LitElement {
8113
8166
  <button class="btn-text" @click="${this._switchToFilterRoles}">
8114
8167
  ${I18n.t("filter.backToFilterRoles")}
8115
8168
  </button>
8169
+ ${this._showFactsOnly || this._conceptSearchText.trim().length > 0 ? html`
8170
+ <button class="btn-text" @click="${this._handleAdvancedFilterReset}">
8171
+ ${I18n.t("filter.resetFilter")}
8172
+ </button>
8173
+ ` : ""}
8116
8174
  ` : html`
8117
8175
  <button class="btn-text" @click="${this._switchToAdvancedFilter}">
8118
8176
  ${I18n.t("filter.advancedFilter")}
@@ -9516,6 +9574,13 @@ let JupiterDynamicForm = class extends LitElement {
9516
9574
  _shouldShowFilterButton() {
9517
9575
  return true;
9518
9576
  }
9577
+ get _hasActiveAdvancedFilters() {
9578
+ return this._showFactsOnly || this._conceptSearchText.trim().length > 0;
9579
+ }
9580
+ _handleClearAdvancedFilters() {
9581
+ this._showFactsOnly = false;
9582
+ this._conceptSearchText = "";
9583
+ }
9519
9584
  _handleFilterRolesClick() {
9520
9585
  this._showFilterDialog = true;
9521
9586
  }
@@ -11241,7 +11306,7 @@ let JupiterDynamicForm = class extends LitElement {
11241
11306
  return null;
11242
11307
  }
11243
11308
  _addConceptDataToSubmission(concept, columnId, value, submissionData, section2) {
11244
- var _a, _b, _c, _d, _e;
11309
+ var _a, _b, _c, _d, _e, _f, _g;
11245
11310
  const field2 = concept.fields.find((f2) => f2.columnId === columnId);
11246
11311
  if (!field2)
11247
11312
  return;
@@ -11253,6 +11318,12 @@ let JupiterDynamicForm = class extends LitElement {
11253
11318
  const periodStartDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
11254
11319
  const periodEndDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
11255
11320
  const periodInstantDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.instantDate) || field2.periodInstantDate || periodEndDate || periodStartDate;
11321
+ if ((_c = concept.type) == null ? void 0 : _c.toLowerCase().includes("percentitemtype")) {
11322
+ const numericValue = Number(value);
11323
+ if (!isNaN(numericValue) && Math.abs(numericValue) >= 1) {
11324
+ value = numericValue / 100;
11325
+ }
11326
+ }
11256
11327
  const entry = {
11257
11328
  conceptId: concept.originalConceptId || concept.id,
11258
11329
  columnId,
@@ -11269,14 +11340,16 @@ let JupiterDynamicForm = class extends LitElement {
11269
11340
  entry.unit = fieldUnit;
11270
11341
  console.log(`✅ [Submission] Adding unit to entry: ${fieldUnit} for ${concept.id}/${columnId}`);
11271
11342
  }
11272
- const isMonetary = (_c = concept.type) == null ? void 0 : _c.toLowerCase().includes("monetary");
11273
- const fieldDecimals = (_d = this._decimalsData[concept.id]) == null ? void 0 : _d[columnId];
11274
- const decimalsValue = fieldDecimals || (this.decimals !== "INF" ? this.decimals : void 0);
11275
- if (isMonetary && decimalsValue) {
11343
+ const isMonetary = (_d = concept.type) == null ? void 0 : _d.toLowerCase().includes("monetary");
11344
+ const isPercent = (_e = concept.type) == null ? void 0 : _e.toLowerCase().includes("percentitemtype");
11345
+ const fieldDecimals = (_f = this._decimalsData[concept.id]) == null ? void 0 : _f[columnId];
11346
+ const globalFallback = isPercent ? "4" : this.decimals !== "INF" ? this.decimals : void 0;
11347
+ const decimalsValue = fieldDecimals || globalFallback;
11348
+ if ((isMonetary || isPercent) && decimalsValue) {
11276
11349
  const parsed = parseFloat(decimalsValue);
11277
11350
  entry.decimals = isNaN(parsed) ? decimalsValue : String(-Math.abs(parsed));
11278
11351
  }
11279
- if ((_e = column2 == null ? void 0 : column2.dimensionData) == null ? void 0 : _e.memberLabel) {
11352
+ if ((_g = column2 == null ? void 0 : column2.dimensionData) == null ? void 0 : _g.memberLabel) {
11280
11353
  entry.dimension = column2.dimensionData.memberLabel;
11281
11354
  }
11282
11355
  console.log(`📤 [Submission Entry] Created entry:`, JSON.stringify(entry, null, 2));
@@ -11414,7 +11487,7 @@ let JupiterDynamicForm = class extends LitElement {
11414
11487
  }
11415
11488
  if (concept.fields && concept.fields.length > 0) {
11416
11489
  concept.fields.forEach((field2) => {
11417
- var _a, _b, _c, _d, _e, _f, _g, _h;
11490
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
11418
11491
  const conceptData = this._formData[concept.id];
11419
11492
  let fieldValue = conceptData == null ? void 0 : conceptData[field2.columnId];
11420
11493
  const baseConceptId = field2.conceptId || concept.id.split("__").slice(0, -1).join("__") || concept.id;
@@ -11432,8 +11505,14 @@ let JupiterDynamicForm = class extends LitElement {
11432
11505
  }
11433
11506
  }
11434
11507
  if (fieldValue !== void 0 && fieldValue !== null && fieldValue !== "") {
11508
+ if ((_b = concept.type) == null ? void 0 : _b.toLowerCase().includes("percentitemtype")) {
11509
+ const numericFieldValue = Number(fieldValue);
11510
+ if (!isNaN(numericFieldValue) && Math.abs(numericFieldValue) >= 1) {
11511
+ fieldValue = numericFieldValue / 100;
11512
+ }
11513
+ }
11435
11514
  const column2 = this._findColumnByIdInSection(field2.columnId, section2);
11436
- const fieldPeriodData = (_b = this._periodData[concept.id]) == null ? void 0 : _b[field2.columnId];
11515
+ const fieldPeriodData = (_c = this._periodData[concept.id]) == null ? void 0 : _c[field2.columnId];
11437
11516
  const submissionEntry = {
11438
11517
  conceptId: concept.id,
11439
11518
  draftInstanceId: concept.id,
@@ -11455,21 +11534,23 @@ let JupiterDynamicForm = class extends LitElement {
11455
11534
  submissionEntry.period.endDate = endDate;
11456
11535
  }
11457
11536
  console.log(`🔍 [Submission] Concept: ${concept.id}, Column: ${field2.columnId}, Field Period: ${fieldPeriodData ? JSON.stringify(fieldPeriodData) : "none"}, Column Period: ${(column2 == null ? void 0 : column2.periodStartDate) || "none"} - ${(column2 == null ? void 0 : column2.periodEndDate) || "none"}, Used Period: ${submissionEntry.period.type === "instant" ? submissionEntry.period.date : `${submissionEntry.period.startDate} - ${submissionEntry.period.endDate}`}`);
11458
- const fieldUnit = (_c = this._unitData[concept.id]) == null ? void 0 : _c[field2.columnId];
11537
+ const fieldUnit = (_d = this._unitData[concept.id]) == null ? void 0 : _d[field2.columnId];
11459
11538
  if (fieldUnit) {
11460
11539
  submissionEntry.unit = fieldUnit;
11461
11540
  console.log(`✅ [Submission] Adding unit to entry: ${fieldUnit} for ${concept.id}/${field2.columnId}`);
11462
11541
  } else {
11463
11542
  console.log(`⚠️ [Submission] No unit found in _unitData for ${concept.id}/${field2.columnId}. _unitData state:`, JSON.stringify(this._unitData, null, 2));
11464
11543
  }
11465
- const isMonetary = (_d = concept.type) == null ? void 0 : _d.toLowerCase().includes("monetary");
11466
- const fieldDecimals = (_e = this._decimalsData[concept.id]) == null ? void 0 : _e[field2.columnId];
11467
- const decimalsValue = fieldDecimals || (this.decimals !== "INF" ? this.decimals : void 0);
11468
- if (isMonetary && decimalsValue) {
11544
+ const isMonetary = (_e = concept.type) == null ? void 0 : _e.toLowerCase().includes("monetary");
11545
+ const isPercent = (_f = concept.type) == null ? void 0 : _f.toLowerCase().includes("percentitemtype");
11546
+ const fieldDecimals = (_g = this._decimalsData[concept.id]) == null ? void 0 : _g[field2.columnId];
11547
+ const globalFallback = isPercent ? "4" : this.decimals !== "INF" ? this.decimals : void 0;
11548
+ const decimalsValue = fieldDecimals || globalFallback;
11549
+ if ((isMonetary || isPercent) && decimalsValue) {
11469
11550
  const parsed = parseFloat(decimalsValue);
11470
11551
  submissionEntry.decimals = isNaN(parsed) ? decimalsValue : String(-Math.abs(parsed));
11471
11552
  }
11472
- if ((column2 == null ? void 0 : column2.type) === "dimension" && ((_f = column2.dimensionData) == null ? void 0 : _f.dimensionIdKey)) {
11553
+ if ((column2 == null ? void 0 : column2.type) === "dimension" && ((_h = column2.dimensionData) == null ? void 0 : _h.dimensionIdKey)) {
11473
11554
  submissionEntry.dimension = column2.dimensionData.dimensionIdKey;
11474
11555
  console.log(`🔍 [DynamicForm] Using dimension key from field's column (${field2.columnId}):`, column2.dimensionData.dimensionIdKey);
11475
11556
  } else {
@@ -11505,12 +11586,12 @@ let JupiterDynamicForm = class extends LitElement {
11505
11586
  console.log(`🔍 [DynamicForm] Column details:`, {
11506
11587
  id: column2.id,
11507
11588
  type: column2.type,
11508
- hasTypedMembers: (_g = column2.dimensionData) == null ? void 0 : _g.hasTypedMembers,
11589
+ hasTypedMembers: (_i = column2.dimensionData) == null ? void 0 : _i.hasTypedMembers,
11509
11590
  dimensionData: column2.dimensionData
11510
11591
  });
11511
11592
  }
11512
11593
  }
11513
- if (!submissionEntry.typedMembers && ((_h = field2.crossRoleTypedMembers) == null ? void 0 : _h.length)) {
11594
+ if (!submissionEntry.typedMembers && ((_j = field2.crossRoleTypedMembers) == null ? void 0 : _j.length)) {
11514
11595
  const crossRoleKey = `${concept.id}__${field2.columnId}`;
11515
11596
  const crossRoleValues = this._typedMemberData[crossRoleKey];
11516
11597
  if (crossRoleValues) {
@@ -11877,8 +11958,17 @@ let JupiterDynamicForm = class extends LitElement {
11877
11958
  return result;
11878
11959
  }
11879
11960
  const query = searchText.toLowerCase().trim();
11961
+ const addAllDescendants = (concepts) => {
11962
+ var _a2;
11963
+ for (const concept of concepts) {
11964
+ result.add(concept.id);
11965
+ if ((_a2 = concept.children) == null ? void 0 : _a2.length) {
11966
+ addAllDescendants(concept.children);
11967
+ }
11968
+ }
11969
+ };
11880
11970
  const walkConcepts = (concepts) => {
11881
- var _a2, _b2, _c2;
11971
+ var _a2, _b2, _c2, _d;
11882
11972
  for (const concept of concepts) {
11883
11973
  let matches = ((_a2 = concept.conceptName) == null ? void 0 : _a2.toLowerCase().includes(query)) || ((_b2 = concept.id) == null ? void 0 : _b2.toLowerCase().includes(query));
11884
11974
  if (!matches && Array.isArray(concept.labels)) {
@@ -11889,8 +11979,10 @@ let JupiterDynamicForm = class extends LitElement {
11889
11979
  }
11890
11980
  if (matches) {
11891
11981
  result.add(concept.id);
11892
- }
11893
- if ((_c2 = concept.children) == null ? void 0 : _c2.length) {
11982
+ if ((_c2 = concept.children) == null ? void 0 : _c2.length) {
11983
+ addAllDescendants(concept.children);
11984
+ }
11985
+ } else if ((_d = concept.children) == null ? void 0 : _d.length) {
11894
11986
  walkConcepts(concept.children);
11895
11987
  }
11896
11988
  }
@@ -12711,8 +12803,8 @@ let JupiterDynamicForm = class extends LitElement {
12711
12803
  <div class="form-actions">
12712
12804
  <!-- Filter Roles Button (shown when more than 10 roles) -->
12713
12805
  ${this._shouldShowFilterButton() ? html`
12714
- <button
12715
- class="filter-roles-button"
12806
+ <button
12807
+ class="filter-roles-button ${this._hasActiveAdvancedFilters ? "has-active-filters" : ""}"
12716
12808
  @click="${this._handleFilterRolesClick}"
12717
12809
  ?disabled="${this.disabled || this.readonly}"
12718
12810
  >
@@ -12722,6 +12814,15 @@ let JupiterDynamicForm = class extends LitElement {
12722
12814
  ${this._selectedRoleIds.length === 0 ? I18n.t("filter.selectRoles") : I18n.t("filter.filterRoles")}
12723
12815
  <span class="roles-count">${this._selectedRoleIds.length}/${this._allSections.length}</span>
12724
12816
  </button>
12817
+ ${this._hasActiveAdvancedFilters ? html`
12818
+ <button
12819
+ class="clear-filters-button"
12820
+ @click="${this._handleClearAdvancedFilters}"
12821
+ ?disabled="${this.disabled || this.readonly}"
12822
+ >
12823
+ × ${I18n.t("filter.clearFilters")}
12824
+ </button>
12825
+ ` : ""}
12725
12826
  ` : ""}
12726
12827
 
12727
12828
  ${this.mode !== "admin" ? html`
@@ -13052,12 +13153,37 @@ JupiterDynamicForm.styles = css`
13052
13153
  opacity: 0.9;
13053
13154
  }
13054
13155
 
13156
+ .filter-roles-button.has-active-filters {
13157
+ background: var(--buttonBgColor, var(--jupiter-primary-color, #1976d2));
13158
+ color: var(--buttonTextColor, white);
13159
+ }
13160
+
13055
13161
  .filter-roles-button .filter-icon {
13056
13162
  width: 16px;
13057
13163
  height: 16px;
13058
13164
  fill: currentColor;
13059
13165
  }
13060
13166
 
13167
+ .clear-filters-button {
13168
+ display: flex;
13169
+ align-items: center;
13170
+ gap: 6px;
13171
+ padding: 8px 14px;
13172
+ background: transparent;
13173
+ border: 1px solid var(--jupiter-border-color, #ddd);
13174
+ color: var(--primaryTextColor, var(--jupiter-text-secondary, #666));
13175
+ border-radius: 4px;
13176
+ font-size: 13px;
13177
+ font-weight: 500;
13178
+ cursor: pointer;
13179
+ transition: all 0.2s ease;
13180
+ }
13181
+
13182
+ .clear-filters-button:hover {
13183
+ border-color: var(--primaryTextColor, var(--jupiter-primary-color, #1976d2));
13184
+ color: var(--primaryTextColor, var(--jupiter-primary-color, #1976d2));
13185
+ }
13186
+
13061
13187
  .roles-count {
13062
13188
  display: inline-flex;
13063
13189
  align-items: center;