@rivet-health/design-system 40.7.0 → 40.8.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.
@@ -2090,12 +2090,19 @@ var RivSelect;
2090
2090
  return this.byId.has(option.id);
2091
2091
  }
2092
2092
  add(option) {
2093
- if (this.byId.has(option.id)) {
2094
- // Preserve insertion order while swapping to the newer object
2095
- // reference. Rebuilding the full Set matches the previous
2096
- // implementation's semantics; the cost of that rebuild only hits
2097
- // the rare "re-add existing" path — the common O(N²) case (fresh
2098
- // adds during construction) no longer pays it.
2093
+ const existing = this.byId.get(option.id);
2094
+ if (existing === option) {
2095
+ // Same object reference — nothing to update. Ancestor re-adds
2096
+ // during a selection snapshot hit this path repeatedly, so keeping
2097
+ // it O(1) is what prevents the tree-select N² blow-up seen in
2098
+ // RVT-25470.
2099
+ return this;
2100
+ }
2101
+ if (existing !== undefined) {
2102
+ // Different reference, same id — swap the stored ref while
2103
+ // preserving Set insertion order. The rebuild is O(N), but this
2104
+ // path is rare in practice (the hot ancestor-snapshot loop takes
2105
+ // the identity fast-path above).
2099
2106
  const items = [...this].map(o => (o.id === option.id ? option : o));
2100
2107
  this.byId.set(option.id, option);
2101
2108
  super.clear();
@@ -2394,17 +2401,22 @@ var RivSelect;
2394
2401
  const snapshot = new OptionSet(state.selection.selected);
2395
2402
  const topLevelOptions = getTopLevelOptions(state.sourceOptionGroups);
2396
2403
  const [parents] = getRelationshipMaps(topLevelOptions);
2397
- // For each selected option, add all its ancestors to the snapshot
2404
+ // Sibling leaves share ancestors, so once we've walked up through a
2405
+ // given parent id we can stop — anything above it has already been
2406
+ // added by a previous iteration. Without this short-circuit the loop
2407
+ // re-runs findOption (which is a full tree walk) once per shared
2408
+ // ancestor edge, which was the #2 hotspot behind OptionSet.add in
2409
+ // RVT-25470's profile.
2410
+ const visited = new Set();
2398
2411
  for (const selectedOption of state.selection.selected) {
2399
- let currentId = selectedOption.id;
2400
- let parentId = parents.get(currentId);
2401
- while (parentId !== undefined) {
2412
+ let parentId = parents.get(selectedOption.id);
2413
+ while (parentId !== undefined && !visited.has(parentId)) {
2414
+ visited.add(parentId);
2402
2415
  const parentOption = findOption(topLevelOptions, parentId);
2403
2416
  if (parentOption) {
2404
2417
  snapshot.add(parentOption);
2405
2418
  }
2406
- currentId = parentId;
2407
- parentId = parents.get(currentId);
2419
+ parentId = parents.get(parentId);
2408
2420
  }
2409
2421
  }
2410
2422
  return snapshot;
@@ -4209,18 +4221,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4209
4221
  'large',
4210
4222
  'xlarge',
4211
4223
  ];
4212
- // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators.
4213
- // A 4-digit leading segment is treated as year.
4224
+ // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators, as
4225
+ // well as separator-less digit runs (MMDDYYYY or MMDDYY). A 4-digit leading
4226
+ // segment in the separated form is treated as an ISO year; 2-digit years
4227
+ // pivot at 50 (<50 → 2000s, >=50 → 1900s).
4214
4228
  function parseDate(input) {
4215
- const parts = input.trim().split(/[/.-]/);
4216
- if (parts.length !== 3)
4229
+ const trimmed = input.trim();
4230
+ if (!trimmed)
4217
4231
  return undefined;
4218
- const nums = parts.map(p => parseInt(p, 10));
4219
- if (nums.some(isNaN))
4232
+ const ymd = /^\d+$/.test(trimmed)
4233
+ ? parseDigits(trimmed)
4234
+ : parseSeparated(trimmed);
4235
+ if (!ymd)
4220
4236
  return undefined;
4221
- let [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4222
- if (parts[0].length !== 4 && year < 100)
4223
- year += year < 50 ? 2000 : 1900;
4237
+ const [year, month, day] = ymd;
4224
4238
  const date = new Date(year, month - 1, day);
4225
4239
  return date.getFullYear() === year &&
4226
4240
  date.getMonth() === month - 1 &&
@@ -4229,6 +4243,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4229
4243
  : undefined;
4230
4244
  }
4231
4245
  DateComponent.parseDate = parseDate;
4246
+ function expandTwoDigitYear(year) {
4247
+ return year < 100 ? year + (year < 50 ? 2000 : 1900) : year;
4248
+ }
4249
+ // A run of digits with no separators: 8 → MMDDYYYY (literal year), 6 → MMDDYY
4250
+ // (2-digit year pivoted at 50).
4251
+ function parseDigits(digits) {
4252
+ if (digits.length !== 8 && digits.length !== 6)
4253
+ return undefined;
4254
+ const year = parseInt(digits.slice(4), 10);
4255
+ return [
4256
+ digits.length === 6 ? expandTwoDigitYear(year) : year,
4257
+ parseInt(digits.slice(0, 2), 10),
4258
+ parseInt(digits.slice(2, 4), 10),
4259
+ ];
4260
+ }
4261
+ function parseSeparated(input) {
4262
+ const parts = input.split(/[/.-]/);
4263
+ if (parts.length !== 3)
4264
+ return undefined;
4265
+ const nums = parts.map(p => parseInt(p, 10));
4266
+ if (nums.some(isNaN))
4267
+ return undefined;
4268
+ const [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4269
+ return [
4270
+ parts[0].length === 4 ? year : expandTwoDigitYear(year),
4271
+ month,
4272
+ day,
4273
+ ];
4274
+ }
4232
4275
  function isValidDate(date, min, max) {
4233
4276
  const time = date.getTime();
4234
4277
  return time >= min.getTime() && time <= max.getTime();