jupiter-dynamic-forms 1.20.9 → 1.21.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
@@ -16185,9 +16185,10 @@ let JupiterDynamicForm = class extends LitElement {
16185
16185
  }
16186
16186
  return false;
16187
16187
  }
16188
- _findColumnByDimensions(columns, dims) {
16189
- var _a;
16190
- return (_a = columns.find((col) => {
16188
+ // Every column carrying all of `dims`. More than one is normal: a previous-year column copies
16189
+ // its current-year column's dimensions and differs only in period, which callers don't pass.
16190
+ _findColumnsByDimensions(columns, dims) {
16191
+ return columns.filter((col) => {
16191
16192
  if (col.type !== "dimension" || !col.dimensionData)
16192
16193
  return false;
16193
16194
  const combos = col.dimensionData.combinations;
@@ -16199,12 +16200,60 @@ let JupiterDynamicForm = class extends LitElement {
16199
16200
  );
16200
16201
  }
16201
16202
  return dims.length === 1 && this._normalizeAxisId(col.dimensionData.axisId ?? "") === this._normalizeAxisId(dims[0].axis) && this._normalizeMemberId(col.dimensionData.memberId ?? "") === this._normalizeMemberId(dims[0].member);
16202
- })) == null ? void 0 : _a.id;
16203
+ });
16204
+ }
16205
+ // Which of `candidates` a scrollToConcept call points at in one row. Narrowed first by
16206
+ // `period` (the columns whose fact has that period), then by `targetValue` (the column whose
16207
+ // stored fact equals it — exactly before up-to-scale, visible before hidden); a step that
16208
+ // singles nothing out is skipped. Else the first candidate.
16209
+ _pickColumn(section2, concept, candidates, targetValue, period, hiddenColumnIds) {
16210
+ var _a;
16211
+ let pool = candidates;
16212
+ if (period && pool.length > 1) {
16213
+ const inPeriod = pool.filter((c2) => this._periodMatches(this._cellPeriod(section2, concept, c2.id), period));
16214
+ if (inPeriod.length)
16215
+ pool = inPeriod;
16216
+ }
16217
+ const rowData = this._formData[concept.id];
16218
+ if (pool.length > 1 && targetValue !== null && rowData) {
16219
+ const filled = pool.filter((c2) => rowData[c2.id] !== void 0 && rowData[c2.id] !== null && rowData[c2.id] !== "");
16220
+ const exact = filled.filter((c2) => String(rowData[c2.id]) === targetValue || Number(rowData[c2.id]) === Number(targetValue));
16221
+ const holders = exact.length ? exact : filled.filter((c2) => this._valueLooselyMatches(rowData[c2.id], targetValue));
16222
+ const holder = holders.find((c2) => !(hiddenColumnIds == null ? void 0 : hiddenColumnIds.has(c2.id))) ?? holders[0];
16223
+ if (holder)
16224
+ return holder.id;
16225
+ }
16226
+ return (_a = pool[0]) == null ? void 0 : _a.id;
16227
+ }
16228
+ // The period Save Draft/Submit write for this cell's fact (same fallback chain as
16229
+ // _processConceptsForSubmission), so a period the host read off a saved fact compares equal —
16230
+ // including user-edited periods and periodStartLabel rows' "day before start" instants.
16231
+ _cellPeriod(section2, concept, columnId) {
16232
+ var _a, _b;
16233
+ const column2 = this._findColumnByIdInSection(columnId, section2);
16234
+ const field2 = ((_a = concept.fields) == null ? void 0 : _a.find((f2) => f2.columnId === columnId)) ?? { columnId };
16235
+ const fieldPeriodData = (_b = this._periodData[concept.id]) == null ? void 0 : _b[columnId];
16236
+ if (concept.periodType === "instant") {
16237
+ return { instant: this._resolveSubmissionInstantDate(concept, field2, fieldPeriodData, column2) };
16238
+ }
16239
+ return {
16240
+ startDate: (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate,
16241
+ endDate: (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate
16242
+ };
16243
+ }
16244
+ // Every date the host gave equals the cell's; an instant only matches an instant cell, a
16245
+ // start/end only a duration cell. Compared as YYYY-MM-DD so a time part doesn't matter.
16246
+ _periodMatches(cell, period) {
16247
+ const day = (d2) => d2 == null ? void 0 : d2.slice(0, 10);
16248
+ const instant = period.instant ?? period.date;
16249
+ if (instant)
16250
+ return day(cell.instant) === day(instant);
16251
+ return !cell.instant && (!period.startDate || day(cell.startDate) === day(period.startDate)) && (!period.endDate || day(cell.endDate) === day(period.endDate));
16203
16252
  }
16204
16253
  // Step 1 of scrollToConcept: pick the section + concept row to navigate to. Precedence:
16205
16254
  // exactConceptId (caller resolved the row) → role (JDF-087: caller named the role) → the
16206
16255
  // original whole-form search (visible sections first, then the rest).
16207
- _resolveScrollTarget(conceptName, dimensions, columnId, exactConceptId, role, targetValue) {
16256
+ _resolveScrollTarget(conceptName, dimensions, columnId, exactConceptId, role, targetValue, period) {
16208
16257
  var _a;
16209
16258
  const sectionsToSearch = [
16210
16259
  ...((_a = this._currentSchema) == null ? void 0 : _a.sections) ?? [],
@@ -16227,7 +16276,7 @@ let JupiterDynamicForm = class extends LitElement {
16227
16276
  const matches = rankMatchingSections(role, sectionsToSearch);
16228
16277
  console.log(`[scrollToConcept] role="${role}" matched ${matches.length} section(s): ${matches.slice(0, 5).map((m) => `${m.section.id} (tier ${m.tier})`).join(", ")}`);
16229
16278
  for (const { section: section2, tier } of matches) {
16230
- const hit = this._resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue);
16279
+ const hit = this._resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue, period);
16231
16280
  if (!hit)
16232
16281
  continue;
16233
16282
  if (tier >= ROLE_MATCH_TIER.SUBSTRING) {
@@ -16237,67 +16286,75 @@ let JupiterDynamicForm = class extends LitElement {
16237
16286
  }
16238
16287
  console.warn(`[scrollToConcept] "${conceptName}" not found in any section matching role="${role}" — falling back to whole-form search`);
16239
16288
  }
16289
+ let columnOnly = null;
16240
16290
  let tentative = null;
16241
16291
  for (const section2 of sectionsToSearch) {
16242
- const hit = this._resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue);
16292
+ const hit = this._resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue, period);
16243
16293
  if (!hit)
16244
16294
  continue;
16245
- if (hit.strong)
16295
+ if (hit.strong && hit.inPeriod)
16246
16296
  return { section: section2, concept: hit.concept, viaRole: false };
16247
- tentative ?? (tentative = { section: section2, concept: hit.concept });
16297
+ if (hit.strong)
16298
+ columnOnly ?? (columnOnly = { section: section2, concept: hit.concept });
16299
+ else
16300
+ tentative ?? (tentative = { section: section2, concept: hit.concept });
16248
16301
  }
16249
- return tentative ? { ...tentative, viaRole: false } : null;
16302
+ const fallback = columnOnly ?? tentative;
16303
+ return fallback ? { ...fallback, viaRole: false } : null;
16250
16304
  }
16251
16305
  // Finds conceptName's row in one section. `strong` is false when the concept is there but the
16252
16306
  // requested column (columnId, or one matching `dimensions`) isn't — the whole-form search keeps
16253
16307
  // such a hit only as a fallback while it looks for a section where the column is present.
16254
- _resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue) {
16308
+ // `inPeriod` is false when `period` was given but none of those columns has it (same idea).
16309
+ _resolveConceptInSection(section2, conceptName, dimensions, columnId, targetValue, period) {
16255
16310
  const candidates = this._findAllConceptsByName(section2.concepts, conceptName);
16256
16311
  if (!candidates.length)
16257
16312
  return null;
16258
16313
  const hasValueMatch = targetValue !== null;
16259
16314
  const cols = section2.columns ?? this._columns;
16260
- const resolvedColumnId = columnId ?? ((dimensions == null ? void 0 : dimensions.length) ? this._findColumnByDimensions(cols, dimensions) : void 0);
16315
+ const resolvedColumnIds = columnId ? [columnId] : (dimensions == null ? void 0 : dimensions.length) ? this._findColumnsByDimensions(cols, dimensions).map((c2) => c2.id) : [];
16316
+ const cellMatches = (row, id, usePeriod) => {
16317
+ var _a;
16318
+ const cellValue = (_a = this._formData[row.id]) == null ? void 0 : _a[id];
16319
+ if (cellValue === void 0 || cellValue === null || cellValue === "")
16320
+ return false;
16321
+ if (hasValueMatch && !this._valueLooselyMatches(cellValue, targetValue))
16322
+ return false;
16323
+ return !usePeriod || this._periodMatches(this._cellPeriod(section2, row, id), period);
16324
+ };
16325
+ const rowMatches = (row, usePeriod) => {
16326
+ if (resolvedColumnIds.length)
16327
+ return resolvedColumnIds.some((id) => cellMatches(row, id, usePeriod));
16328
+ if (!hasValueMatch && !usePeriod)
16329
+ return false;
16330
+ return Object.keys(this._formData[row.id] ?? {}).some((id) => cellMatches(row, id, usePeriod));
16331
+ };
16261
16332
  let picked;
16262
16333
  if (candidates.length > 1) {
16263
- picked = candidates.find((c2) => {
16264
- const rowData = this._formData[c2.id];
16265
- if (!rowData)
16266
- return false;
16267
- if (resolvedColumnId) {
16268
- const cellValue = rowData[resolvedColumnId];
16269
- if (cellValue === void 0 || cellValue === null || cellValue === "")
16270
- return false;
16271
- return !hasValueMatch || this._valueLooselyMatches(cellValue, targetValue);
16272
- }
16273
- if (hasValueMatch) {
16274
- return Object.values(rowData).some(
16275
- (v) => v !== void 0 && v !== null && v !== "" && this._valueLooselyMatches(v, targetValue)
16276
- );
16277
- }
16278
- return false;
16279
- });
16334
+ picked = (period ? candidates.find((c2) => rowMatches(c2, true)) : void 0) ?? candidates.find((c2) => rowMatches(c2, false));
16280
16335
  console.log(`[scrollToConcept] Section "${section2.id}" has ${candidates.length} rows named "${conceptName}" | disambiguated=${(picked == null ? void 0 : picked.id) ?? "none (using first)"}`);
16281
16336
  }
16282
16337
  const found = picked ?? candidates[0];
16338
+ const inPeriod = (ids) => !period || ids.some((id) => this._periodMatches(this._cellPeriod(section2, found, id), period));
16283
16339
  if (columnId) {
16284
16340
  const hasColumn = cols.some((c2) => c2.id === columnId);
16285
16341
  console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" | columnId="${columnId}" present=${hasColumn}`);
16286
- return { concept: found, strong: hasColumn };
16342
+ return { concept: found, strong: hasColumn, inPeriod: inPeriod(hasColumn ? [columnId] : []) };
16287
16343
  }
16288
16344
  if (dimensions == null ? void 0 : dimensions.length) {
16289
- console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" | colMatch=${resolvedColumnId ?? "null"} | cols=${cols.map((c2) => c2.id).join(",")}`);
16290
- return { concept: found, strong: !!resolvedColumnId };
16345
+ console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" | colMatch=${resolvedColumnIds.join(",") || "null"} | cols=${cols.map((c2) => c2.id).join(",")}`);
16346
+ return { concept: found, strong: resolvedColumnIds.length > 0, inPeriod: inPeriod(resolvedColumnIds) };
16291
16347
  }
16292
16348
  console.log(`[scrollToConcept] Section "${section2.id}" has concept "${found.id}" (no dims, taking first)`);
16293
- return { concept: found, strong: true };
16349
+ return { concept: found, strong: true, inPeriod: inPeriod(cols.map((c2) => c2.id)) };
16294
16350
  }
16295
16351
  async scrollToConcept(conceptName, dimensions, match, columnId, exactConceptId, role) {
16296
16352
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
16297
- console.log(`[scrollToConcept] ▶ START conceptName=${conceptName} value=${match == null ? void 0 : match.value} dims=${JSON.stringify(dimensions)} columnId=${columnId} exactConceptId=${exactConceptId} role=${role}`);
16353
+ console.log(`[scrollToConcept] ▶ START conceptName=${conceptName} value=${match == null ? void 0 : match.value} period=${JSON.stringify(match == null ? void 0 : match.period)} dims=${JSON.stringify(dimensions)} columnId=${columnId} exactConceptId=${exactConceptId} role=${role}`);
16298
16354
  const hasValueMatch = (match == null ? void 0 : match.value) !== void 0 && (match == null ? void 0 : match.value) !== null;
16299
16355
  const targetValue = hasValueMatch ? String(match.value) : null;
16300
- const resolved = this._resolveScrollTarget(conceptName, dimensions, columnId, exactConceptId, role, targetValue);
16356
+ const period = (match == null ? void 0 : match.period) && (match.period.instant || match.period.date || match.period.startDate || match.period.endDate) ? match.period : void 0;
16357
+ const resolved = this._resolveScrollTarget(conceptName, dimensions, columnId, exactConceptId, role, targetValue, period);
16301
16358
  if (!resolved) {
16302
16359
  console.warn(`[scrollToConcept] Concept not found: ${conceptName}`);
16303
16360
  return;
@@ -16331,7 +16388,16 @@ let JupiterDynamicForm = class extends LitElement {
16331
16388
  if (columnId) {
16332
16389
  targetColumnId = columnId;
16333
16390
  } else if (dimensions == null ? void 0 : dimensions.length) {
16334
- targetColumnId = this._findColumnByDimensions(columns, dimensions) ?? null;
16391
+ targetColumnId = this._pickColumn(
16392
+ targetSection,
16393
+ targetConcept,
16394
+ this._findColumnsByDimensions(columns, dimensions),
16395
+ targetValue,
16396
+ period,
16397
+ hiddenColumnIds
16398
+ ) ?? null;
16399
+ } else if (period && columns.some((c2) => this._periodMatches(this._cellPeriod(targetSection, targetConcept, c2.id), period))) {
16400
+ targetColumnId = this._pickColumn(targetSection, targetConcept, columns, targetValue, period, hiddenColumnIds) ?? null;
16335
16401
  } else if (!hasValueMatch) {
16336
16402
  targetColumnId = ((_c = columns.find((c2) => !(hiddenColumnIds == null ? void 0 : hiddenColumnIds.has(c2.id))) ?? columns[0]) == null ? void 0 : _c.id) ?? null;
16337
16403
  }
@@ -16356,7 +16422,8 @@ let JupiterDynamicForm = class extends LitElement {
16356
16422
  this._showColumn(targetSection.id, columnToShow);
16357
16423
  }
16358
16424
  }
16359
- console.log(`[scrollToConcept] targetColumnId=${targetColumnId} hasValueMatch=${hasValueMatch} targetValue=${targetValue}`);
16425
+ const periodPinned = !!period && !!targetColumnId && this._periodMatches(this._cellPeriod(targetSection, targetConcept, targetColumnId), period);
16426
+ console.log(`[scrollToConcept] targetColumnId=${targetColumnId} hasValueMatch=${hasValueMatch} targetValue=${targetValue} periodPinned=${periodPinned}`);
16360
16427
  this._conceptSearchText = conceptName.includes(":") ? conceptName.split(":").pop() : conceptName;
16361
16428
  this.requestUpdate();
16362
16429
  await this.updateComplete;
@@ -16417,7 +16484,7 @@ let JupiterDynamicForm = class extends LitElement {
16417
16484
  if (targetFieldEl)
16418
16485
  return;
16419
16486
  const columnMatch = targetColumnId ? fieldEl.conceptId === conceptId && fieldEl.columnId === targetColumnId : fieldEl.conceptId === conceptId;
16420
- const valueMatch = !hasValueMatch || valueMatchesTarget(fieldEl, targetValue);
16487
+ const valueMatch = !hasValueMatch || periodPinned || valueMatchesTarget(fieldEl, targetValue);
16421
16488
  if (columnMatch) {
16422
16489
  phase1FieldCount++;
16423
16490
  console.log(`[scrollToConcept] Phase1 candidate: conceptId=${fieldEl.conceptId} columnId=${fieldEl.columnId} value=${fieldEl.value} scale=${getFieldScaleFactor(fieldEl)} valueMatch=${valueMatch}`);