@outbuild-company/schedule-core 1.3.0 → 1.4.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/cdn/schedule-core.global.js +1 -1
- package/dist/cdn/schedule-core.global.js.map +1 -1
- package/dist/index.cjs +435 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -2
- package/dist/index.d.ts +91 -2
- package/dist/index.js +435 -110
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -243,6 +243,23 @@ function dispatchVisibilitySet(action, deps) {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
// src/internal/filter/field-registry.ts
|
|
246
|
+
var STATUS_ORDER = [
|
|
247
|
+
"Waiting",
|
|
248
|
+
"Done",
|
|
249
|
+
"Doing",
|
|
250
|
+
"Overdue",
|
|
251
|
+
"Advancement"
|
|
252
|
+
];
|
|
253
|
+
var CONSTRAINT_TYPE_ORDER = [
|
|
254
|
+
"mfo",
|
|
255
|
+
"mso",
|
|
256
|
+
"snlt",
|
|
257
|
+
"snet",
|
|
258
|
+
"alap",
|
|
259
|
+
"asap",
|
|
260
|
+
"fnet",
|
|
261
|
+
"fnlt"
|
|
262
|
+
];
|
|
246
263
|
var MILLISECONDS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
247
264
|
function toDays(hours, hoursPerDay) {
|
|
248
265
|
if (hours === null) return null;
|
|
@@ -351,20 +368,34 @@ var FIELD_REGISTRY = {
|
|
|
351
368
|
extract: (activity) => activity.responsableIds
|
|
352
369
|
},
|
|
353
370
|
tagIds: { valueKind: "id-array", extract: (activity) => activity.tagIds },
|
|
354
|
-
status: {
|
|
371
|
+
status: {
|
|
372
|
+
valueKind: "enum",
|
|
373
|
+
order: STATUS_ORDER,
|
|
374
|
+
extract: (activity) => activity.status
|
|
375
|
+
},
|
|
355
376
|
constraintType: {
|
|
356
377
|
valueKind: "enum",
|
|
378
|
+
order: CONSTRAINT_TYPE_ORDER,
|
|
357
379
|
extract: (activity) => activity.constraintType
|
|
358
380
|
},
|
|
359
381
|
calendarId: {
|
|
360
|
-
valueKind: "
|
|
382
|
+
valueKind: "reference",
|
|
361
383
|
extract: (activity) => activity.calendarId === null ? null : String(activity.calendarId)
|
|
362
384
|
},
|
|
385
|
+
// Normalized to string like calendarId. It is stored as a number, so it used
|
|
386
|
+
// to reach compareValues through the numeric branch while its sibling
|
|
387
|
+
// reference went through the string one — the same field kind comparing two
|
|
388
|
+
// different ways. localeCompare with numeric:true keeps the digit ordering.
|
|
363
389
|
subcontractId: {
|
|
364
|
-
valueKind: "
|
|
365
|
-
extract: (activity) => activity.subcontractId
|
|
390
|
+
valueKind: "reference",
|
|
391
|
+
extract: (activity) => activity.subcontractId === null ? null : String(activity.subcontractId)
|
|
366
392
|
},
|
|
367
|
-
|
|
393
|
+
// Declared boolean, not enum: it is one, and saying so is what lets the enum
|
|
394
|
+
// branch demand an order without inventing one for true/false.
|
|
395
|
+
isCritical: {
|
|
396
|
+
valueKind: "boolean",
|
|
397
|
+
extract: (activity) => activity.isCritical
|
|
398
|
+
}
|
|
368
399
|
};
|
|
369
400
|
function getFieldDescriptor(field) {
|
|
370
401
|
return FIELD_REGISTRY[field] ?? null;
|
|
@@ -491,6 +522,12 @@ function evaluateVisibleIds(input) {
|
|
|
491
522
|
return visibleIds;
|
|
492
523
|
}
|
|
493
524
|
|
|
525
|
+
// src/internal/filter/context.ts
|
|
526
|
+
var COLLATION_LOCALE = "en";
|
|
527
|
+
function buildFilterContext(hoursPerDay) {
|
|
528
|
+
return { hoursPerDay, locale: COLLATION_LOCALE };
|
|
529
|
+
}
|
|
530
|
+
|
|
494
531
|
// src/internal/filter/validate.ts
|
|
495
532
|
var OPERATORS_BY_KIND = {
|
|
496
533
|
string: /* @__PURE__ */ new Set(["includes", "notIncludes", "is", "isNot"]),
|
|
@@ -504,7 +541,11 @@ var OPERATORS_BY_KIND = {
|
|
|
504
541
|
]),
|
|
505
542
|
date: /* @__PURE__ */ new Set(["after", "before"]),
|
|
506
543
|
"id-array": /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
|
|
507
|
-
enum: /* @__PURE__ */ new Set(["someOf", "notSomeOf"])
|
|
544
|
+
enum: /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
|
|
545
|
+
// Same membership operators as enum: splitting the kinds was about ordering,
|
|
546
|
+
// not about filtering, and these two filter exactly like an enum.
|
|
547
|
+
boolean: /* @__PURE__ */ new Set(["someOf", "notSomeOf"]),
|
|
548
|
+
reference: /* @__PURE__ */ new Set(["someOf", "notSomeOf"])
|
|
508
549
|
};
|
|
509
550
|
function validateCriterion(criterion) {
|
|
510
551
|
const descriptor = getFieldDescriptor(criterion.field);
|
|
@@ -549,7 +590,7 @@ function resolveVisibleIds(adapter, filter, hoursPerDay) {
|
|
|
549
590
|
activities,
|
|
550
591
|
parentOf: (activityId) => adapter.getParentId(activityId),
|
|
551
592
|
filter,
|
|
552
|
-
context:
|
|
593
|
+
context: buildFilterContext(hoursPerDay)
|
|
553
594
|
});
|
|
554
595
|
}
|
|
555
596
|
function dispatchFilterSet(action, deps) {
|
|
@@ -577,6 +618,140 @@ function dispatchFilterSet(action, deps) {
|
|
|
577
618
|
return { ok: true, changes };
|
|
578
619
|
}
|
|
579
620
|
|
|
621
|
+
// src/internal/hierarchy/root-parent.ts
|
|
622
|
+
var ROOT_PARENT_ID = "0";
|
|
623
|
+
function isRootParent(parent) {
|
|
624
|
+
return parent == null || parent === 0 || parent === ROOT_PARENT_ID;
|
|
625
|
+
}
|
|
626
|
+
function normalizeParentKey(parent) {
|
|
627
|
+
return isRootParent(parent) ? ROOT_PARENT_ID : String(parent);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/internal/hierarchy/visual-order.ts
|
|
631
|
+
var NOT_SET_SORT_VALUE = Number.MAX_SAFE_INTEGER;
|
|
632
|
+
function getChildrenInVisualOrder(parentId, adapter) {
|
|
633
|
+
const children = collectChildrenSnapshots(parentId, adapter);
|
|
634
|
+
const userOrder = adapter.getOrderComparator?.() ?? null;
|
|
635
|
+
children.sort(
|
|
636
|
+
userOrder === null ? compareActivitiesInVisualOrder : (a, b) => userOrder(a, b) || compareActivitiesInVisualOrder(a, b)
|
|
637
|
+
);
|
|
638
|
+
return children.map((a) => String(a.id));
|
|
639
|
+
}
|
|
640
|
+
function iterateInVisualOrder(adapter) {
|
|
641
|
+
const result = [];
|
|
642
|
+
const visit = (activity) => {
|
|
643
|
+
result.push(activity);
|
|
644
|
+
const childIds = getChildrenInVisualOrder(String(activity.id), adapter);
|
|
645
|
+
for (const childId of childIds) {
|
|
646
|
+
const child = adapter.getActivity(childId);
|
|
647
|
+
if (child) visit(child);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter);
|
|
651
|
+
for (const rootId of rootIds) {
|
|
652
|
+
const root = adapter.getActivity(rootId);
|
|
653
|
+
if (root) visit(root);
|
|
654
|
+
}
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
function findPreviousNonSelectedSibling(taskId, selected, adapter) {
|
|
658
|
+
const activity = adapter.getActivity(taskId);
|
|
659
|
+
if (!activity) return null;
|
|
660
|
+
const parentKey = parentKeyOf(activity);
|
|
661
|
+
const siblings = getChildrenInVisualOrder(parentKey, adapter);
|
|
662
|
+
const idx = siblings.findIndex((id) => String(id) === String(taskId));
|
|
663
|
+
if (idx <= 0) return null;
|
|
664
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
665
|
+
const candidateId = siblings[i];
|
|
666
|
+
if (candidateId === void 0) continue;
|
|
667
|
+
if (!setHas(selected, candidateId)) return candidateId;
|
|
668
|
+
}
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
function visualIndexInParent(taskId, adapter) {
|
|
672
|
+
const activity = adapter.getActivity(taskId);
|
|
673
|
+
if (!activity) return -1;
|
|
674
|
+
const parentKey = parentKeyOf(activity);
|
|
675
|
+
const siblings = getChildrenInVisualOrder(parentKey, adapter);
|
|
676
|
+
return siblings.findIndex((id) => String(id) === String(taskId));
|
|
677
|
+
}
|
|
678
|
+
function collectChildrenSnapshots(parentId, adapter) {
|
|
679
|
+
const key = String(parentId);
|
|
680
|
+
if (key === "0") {
|
|
681
|
+
return adapter.getAllActivities().filter((a) => a.parentId === null);
|
|
682
|
+
}
|
|
683
|
+
const childIds = adapter.getChildren(parentId);
|
|
684
|
+
const out = [];
|
|
685
|
+
for (const id of childIds) {
|
|
686
|
+
const snap = adapter.getActivity(id);
|
|
687
|
+
if (snap) out.push(snap);
|
|
688
|
+
}
|
|
689
|
+
return out;
|
|
690
|
+
}
|
|
691
|
+
function compareActivitiesInVisualOrder(a, b) {
|
|
692
|
+
const ac = correlativeIdOf(a);
|
|
693
|
+
const bc = correlativeIdOf(b);
|
|
694
|
+
if (ac !== bc) return ac - bc;
|
|
695
|
+
return String(a.id).localeCompare(String(b.id));
|
|
696
|
+
}
|
|
697
|
+
function correlativeIdOf(activity) {
|
|
698
|
+
const raw = activity.correlativeId;
|
|
699
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
700
|
+
return Number.isFinite(n) ? n : NOT_SET_SORT_VALUE;
|
|
701
|
+
}
|
|
702
|
+
function parentKeyOf(activity) {
|
|
703
|
+
const parentId = activity.parentId;
|
|
704
|
+
if (parentId === null) return "0";
|
|
705
|
+
return parentId;
|
|
706
|
+
}
|
|
707
|
+
function setHas(set, id) {
|
|
708
|
+
if (set.has(id)) return true;
|
|
709
|
+
return set.has(String(id));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/dispatch/shared/collect-branch-order.ts
|
|
713
|
+
function collectBranchOrder(adapter) {
|
|
714
|
+
const branches = [];
|
|
715
|
+
const visit = (parentId) => {
|
|
716
|
+
const childIds = getChildrenInVisualOrder(parentId, adapter);
|
|
717
|
+
if (childIds.length > 1) {
|
|
718
|
+
branches.push({ parentId, childIds });
|
|
719
|
+
}
|
|
720
|
+
for (const childId of childIds) {
|
|
721
|
+
visit(childId);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
visit(ROOT_PARENT_ID);
|
|
725
|
+
return branches;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/dispatch/order.ts
|
|
729
|
+
function validateRule(rule) {
|
|
730
|
+
if (getFieldDescriptor(rule.field) === null) return "unknown_field";
|
|
731
|
+
if (rule.direction !== "asc" && rule.direction !== "desc") {
|
|
732
|
+
return "unknown_operator";
|
|
733
|
+
}
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
function dispatchSortSet(action, deps) {
|
|
737
|
+
const { adapter } = deps;
|
|
738
|
+
for (const rule of action.rules) {
|
|
739
|
+
const rejection = validateRule(rule);
|
|
740
|
+
if (rejection !== null) return { ok: false, reason: rejection };
|
|
741
|
+
}
|
|
742
|
+
const order = action.rules.length === 0 ? null : { rules: action.rules };
|
|
743
|
+
adapter.setActiveOrder(order);
|
|
744
|
+
const changes = {
|
|
745
|
+
source: action,
|
|
746
|
+
activities: [],
|
|
747
|
+
links: [],
|
|
748
|
+
calendars: [],
|
|
749
|
+
trackingEvents: [],
|
|
750
|
+
order: collectBranchOrder(adapter)
|
|
751
|
+
};
|
|
752
|
+
return { ok: true, changes };
|
|
753
|
+
}
|
|
754
|
+
|
|
580
755
|
// src/columns/text/constants.ts
|
|
581
756
|
var TEXT = "text";
|
|
582
757
|
|
|
@@ -1883,15 +2058,6 @@ function isSummaryActivity(activity, adapter) {
|
|
|
1883
2058
|
return isSummary(activity.type, adapter.getChildren(activity.id).length > 0);
|
|
1884
2059
|
}
|
|
1885
2060
|
|
|
1886
|
-
// src/internal/hierarchy/root-parent.ts
|
|
1887
|
-
var ROOT_PARENT_ID = "0";
|
|
1888
|
-
function isRootParent(parent) {
|
|
1889
|
-
return parent == null || parent === 0 || parent === ROOT_PARENT_ID;
|
|
1890
|
-
}
|
|
1891
|
-
function normalizeParentKey(parent) {
|
|
1892
|
-
return isRootParent(parent) ? ROOT_PARENT_ID : String(parent);
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
2061
|
// src/autoscheduler/engine/alap-pass.ts
|
|
1896
2062
|
async function alapPass(reversedIds, links, asapPlans, adapter, _options, isCurrent) {
|
|
1897
2063
|
const plans = new Map(asapPlans);
|
|
@@ -8941,6 +9107,7 @@ var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
|
|
|
8941
9107
|
"selection-replace",
|
|
8942
9108
|
"visibility-set",
|
|
8943
9109
|
"filter-set",
|
|
9110
|
+
"sort-set",
|
|
8944
9111
|
"sir-sync",
|
|
8945
9112
|
"activity-lookahead-sync",
|
|
8946
9113
|
"persistence-acknowledge",
|
|
@@ -8952,9 +9119,10 @@ var PURE_VIEW_STATE_KINDS = /* @__PURE__ */ new Set([
|
|
|
8952
9119
|
"selection-toggle",
|
|
8953
9120
|
"selection-replace",
|
|
8954
9121
|
"visibility-set",
|
|
8955
|
-
"filter-set"
|
|
9122
|
+
"filter-set",
|
|
9123
|
+
"sort-set"
|
|
8956
9124
|
]);
|
|
8957
|
-
function
|
|
9125
|
+
function reappliesViewState(action) {
|
|
8958
9126
|
return !PURE_VIEW_STATE_KINDS.has(action.kind);
|
|
8959
9127
|
}
|
|
8960
9128
|
var NO_STRUCTURAL_EXEMPTIONS = /* @__PURE__ */ new Set();
|
|
@@ -9826,85 +9994,6 @@ var DISPATCH_TRACK_EVENT = {
|
|
|
9826
9994
|
ACTIVITY_OUTDENT: "schedule_activity_outdent"
|
|
9827
9995
|
};
|
|
9828
9996
|
|
|
9829
|
-
// src/internal/hierarchy/visual-order.ts
|
|
9830
|
-
var NOT_SET_SORT_VALUE = Number.MAX_SAFE_INTEGER;
|
|
9831
|
-
function getChildrenInVisualOrder(parentId, adapter) {
|
|
9832
|
-
const children = collectChildrenSnapshots(parentId, adapter);
|
|
9833
|
-
children.sort(compareActivitiesInVisualOrder);
|
|
9834
|
-
return children.map((a) => String(a.id));
|
|
9835
|
-
}
|
|
9836
|
-
function iterateInVisualOrder(adapter) {
|
|
9837
|
-
const result = [];
|
|
9838
|
-
const visit = (activity) => {
|
|
9839
|
-
result.push(activity);
|
|
9840
|
-
const childIds = getChildrenInVisualOrder(String(activity.id), adapter);
|
|
9841
|
-
for (const childId of childIds) {
|
|
9842
|
-
const child = adapter.getActivity(childId);
|
|
9843
|
-
if (child) visit(child);
|
|
9844
|
-
}
|
|
9845
|
-
};
|
|
9846
|
-
const rootIds = getChildrenInVisualOrder(ROOT_PARENT_ID, adapter);
|
|
9847
|
-
for (const rootId of rootIds) {
|
|
9848
|
-
const root = adapter.getActivity(rootId);
|
|
9849
|
-
if (root) visit(root);
|
|
9850
|
-
}
|
|
9851
|
-
return result;
|
|
9852
|
-
}
|
|
9853
|
-
function findPreviousNonSelectedSibling(taskId, selected, adapter) {
|
|
9854
|
-
const activity = adapter.getActivity(taskId);
|
|
9855
|
-
if (!activity) return null;
|
|
9856
|
-
const parentKey = parentKeyOf(activity);
|
|
9857
|
-
const siblings = getChildrenInVisualOrder(parentKey, adapter);
|
|
9858
|
-
const idx = siblings.findIndex((id) => String(id) === String(taskId));
|
|
9859
|
-
if (idx <= 0) return null;
|
|
9860
|
-
for (let i = idx - 1; i >= 0; i--) {
|
|
9861
|
-
const candidateId = siblings[i];
|
|
9862
|
-
if (candidateId === void 0) continue;
|
|
9863
|
-
if (!setHas(selected, candidateId)) return candidateId;
|
|
9864
|
-
}
|
|
9865
|
-
return null;
|
|
9866
|
-
}
|
|
9867
|
-
function visualIndexInParent(taskId, adapter) {
|
|
9868
|
-
const activity = adapter.getActivity(taskId);
|
|
9869
|
-
if (!activity) return -1;
|
|
9870
|
-
const parentKey = parentKeyOf(activity);
|
|
9871
|
-
const siblings = getChildrenInVisualOrder(parentKey, adapter);
|
|
9872
|
-
return siblings.findIndex((id) => String(id) === String(taskId));
|
|
9873
|
-
}
|
|
9874
|
-
function collectChildrenSnapshots(parentId, adapter) {
|
|
9875
|
-
const key = String(parentId);
|
|
9876
|
-
if (key === "0") {
|
|
9877
|
-
return adapter.getAllActivities().filter((a) => a.parentId === null);
|
|
9878
|
-
}
|
|
9879
|
-
const childIds = adapter.getChildren(parentId);
|
|
9880
|
-
const out = [];
|
|
9881
|
-
for (const id of childIds) {
|
|
9882
|
-
const snap = adapter.getActivity(id);
|
|
9883
|
-
if (snap) out.push(snap);
|
|
9884
|
-
}
|
|
9885
|
-
return out;
|
|
9886
|
-
}
|
|
9887
|
-
function compareActivitiesInVisualOrder(a, b) {
|
|
9888
|
-
const ac = correlativeIdOf(a);
|
|
9889
|
-
const bc = correlativeIdOf(b);
|
|
9890
|
-
if (ac !== bc) return ac - bc;
|
|
9891
|
-
return String(a.id).localeCompare(String(b.id));
|
|
9892
|
-
}
|
|
9893
|
-
function correlativeIdOf(activity) {
|
|
9894
|
-
const raw = activity.correlativeId;
|
|
9895
|
-
const n = typeof raw === "number" ? raw : Number(raw);
|
|
9896
|
-
return Number.isFinite(n) ? n : NOT_SET_SORT_VALUE;
|
|
9897
|
-
}
|
|
9898
|
-
function parentKeyOf(activity) {
|
|
9899
|
-
const parentId = activity.parentId;
|
|
9900
|
-
if (parentId === null) return "0";
|
|
9901
|
-
return parentId;
|
|
9902
|
-
}
|
|
9903
|
-
function setHas(set, id) {
|
|
9904
|
-
if (set.has(id)) return true;
|
|
9905
|
-
return set.has(String(id));
|
|
9906
|
-
}
|
|
9907
|
-
|
|
9908
9997
|
// src/internal/hierarchy/recompute-correlative-ids.ts
|
|
9909
9998
|
function recomputeCorrelativeIds(adapter) {
|
|
9910
9999
|
adapter.invalidateVisualOrderIds?.();
|
|
@@ -12750,6 +12839,9 @@ async function dispatch(action, options, deps) {
|
|
|
12750
12839
|
hoursPerDay: deps.sector.hoursPerDay
|
|
12751
12840
|
});
|
|
12752
12841
|
}
|
|
12842
|
+
if (action.kind === "sort-set") {
|
|
12843
|
+
return dispatchSortSet(action, { adapter: deps.adapter });
|
|
12844
|
+
}
|
|
12753
12845
|
if (action.kind === "sir-sync") {
|
|
12754
12846
|
return dispatchSirSync(action, deps);
|
|
12755
12847
|
}
|
|
@@ -15837,6 +15929,7 @@ var ViewStateStore = class {
|
|
|
15837
15929
|
checkedSet = /* @__PURE__ */ new Set();
|
|
15838
15930
|
hiddenSet = /* @__PURE__ */ new Set();
|
|
15839
15931
|
activeFilter = null;
|
|
15932
|
+
activeOrder = null;
|
|
15840
15933
|
isChecked(activityId) {
|
|
15841
15934
|
return this.checkedSet.has(activityId);
|
|
15842
15935
|
}
|
|
@@ -15852,6 +15945,12 @@ var ViewStateStore = class {
|
|
|
15852
15945
|
setActiveFilter(nextFilter) {
|
|
15853
15946
|
this.activeFilter = nextFilter;
|
|
15854
15947
|
}
|
|
15948
|
+
getActiveOrder() {
|
|
15949
|
+
return this.activeOrder;
|
|
15950
|
+
}
|
|
15951
|
+
setActiveOrder(nextOrder) {
|
|
15952
|
+
this.activeOrder = nextOrder;
|
|
15953
|
+
}
|
|
15855
15954
|
checkedIds() {
|
|
15856
15955
|
return [...this.checkedSet];
|
|
15857
15956
|
}
|
|
@@ -15878,18 +15977,21 @@ var ViewStateStore = class {
|
|
|
15878
15977
|
this.checkedSet = new Set(seed2.checkedIds ?? []);
|
|
15879
15978
|
this.hiddenSet = new Set(seed2.hiddenIds ?? []);
|
|
15880
15979
|
this.activeFilter = null;
|
|
15980
|
+
this.activeOrder = null;
|
|
15881
15981
|
}
|
|
15882
15982
|
snapshot() {
|
|
15883
15983
|
return {
|
|
15884
15984
|
checked: new Set(this.checkedSet),
|
|
15885
15985
|
hidden: new Set(this.hiddenSet),
|
|
15886
|
-
activeFilter: this.activeFilter
|
|
15986
|
+
activeFilter: this.activeFilter,
|
|
15987
|
+
activeOrder: this.activeOrder
|
|
15887
15988
|
};
|
|
15888
15989
|
}
|
|
15889
15990
|
restore(snapshot) {
|
|
15890
15991
|
this.checkedSet = new Set(snapshot.checked);
|
|
15891
15992
|
this.hiddenSet = new Set(snapshot.hidden);
|
|
15892
15993
|
this.activeFilter = snapshot.activeFilter;
|
|
15994
|
+
this.activeOrder = snapshot.activeOrder;
|
|
15893
15995
|
}
|
|
15894
15996
|
};
|
|
15895
15997
|
|
|
@@ -15902,9 +16004,11 @@ var ScheduleState = class {
|
|
|
15902
16004
|
_outgoing = /* @__PURE__ */ new Map();
|
|
15903
16005
|
_incoming = /* @__PURE__ */ new Map();
|
|
15904
16006
|
_hierarchy;
|
|
16007
|
+
_buildOrderComparator;
|
|
15905
16008
|
_cache = {
|
|
15906
16009
|
ids: null,
|
|
15907
|
-
visualOrderIds: null
|
|
16010
|
+
visualOrderIds: null,
|
|
16011
|
+
orderComparator: null
|
|
15908
16012
|
};
|
|
15909
16013
|
_writeCapture = new WriteCapture();
|
|
15910
16014
|
_viewState = new ViewStateStore();
|
|
@@ -15913,7 +16017,8 @@ var ScheduleState = class {
|
|
|
15913
16017
|
_promotionSnapshotBefore = null;
|
|
15914
16018
|
_calendar;
|
|
15915
16019
|
_flags;
|
|
15916
|
-
constructor(snapshot) {
|
|
16020
|
+
constructor(snapshot, buildOrderComparator = () => null) {
|
|
16021
|
+
this._buildOrderComparator = buildOrderComparator;
|
|
15917
16022
|
for (const activity of snapshot.activities) {
|
|
15918
16023
|
this._activities.set(activity.id, activity);
|
|
15919
16024
|
}
|
|
@@ -16022,8 +16127,47 @@ var ScheduleState = class {
|
|
|
16022
16127
|
this._captureViewStateOnce();
|
|
16023
16128
|
this._viewState.setActiveFilter(nextFilter);
|
|
16024
16129
|
}
|
|
16130
|
+
getActiveOrder() {
|
|
16131
|
+
return this._viewState.getActiveOrder();
|
|
16132
|
+
}
|
|
16133
|
+
setActiveOrder(nextOrder) {
|
|
16134
|
+
this._captureViewStateOnce();
|
|
16135
|
+
this._viewState.setActiveOrder(nextOrder);
|
|
16136
|
+
this.invalidateDerivedOrder();
|
|
16137
|
+
}
|
|
16138
|
+
/**
|
|
16139
|
+
* Rebuild-on-demand, memoized: at most one build per invalidation, never one
|
|
16140
|
+
* per comparison. getChildrenInVisualOrder asks for this once and then sorts a
|
|
16141
|
+
* whole sibling group with it, and collectBranchOrder walks every branch, so a
|
|
16142
|
+
* build per read would be thousands per dispatch.
|
|
16143
|
+
*
|
|
16144
|
+
* The factory reads hoursPerDay and the collation locale at build time, so a
|
|
16145
|
+
* rebuilt comparator always reflects the current context — that is what keeps
|
|
16146
|
+
* ordering and filtering from seeing two different hoursPerDay.
|
|
16147
|
+
*/
|
|
16148
|
+
getOrderComparator() {
|
|
16149
|
+
const activeOrder = this._viewState.getActiveOrder();
|
|
16150
|
+
if (activeOrder === null) {
|
|
16151
|
+
this._cache.orderComparator = null;
|
|
16152
|
+
return null;
|
|
16153
|
+
}
|
|
16154
|
+
if (this._cache.orderComparator) return this._cache.orderComparator;
|
|
16155
|
+
this._cache.orderComparator = this._buildOrderComparator(activeOrder);
|
|
16156
|
+
return this._cache.orderComparator;
|
|
16157
|
+
}
|
|
16158
|
+
/**
|
|
16159
|
+
* Both order caches, always together. The comparator decides the sequence, so
|
|
16160
|
+
* a rebuilt comparator with a surviving sequence would paint the old order.
|
|
16161
|
+
* invalidateVisualOrderIds stays separate on purpose: a structural change
|
|
16162
|
+
* moves rows without touching the rules, so the comparator is still valid.
|
|
16163
|
+
*/
|
|
16164
|
+
invalidateDerivedOrder() {
|
|
16165
|
+
this._cache.orderComparator = null;
|
|
16166
|
+
this._cache.visualOrderIds = null;
|
|
16167
|
+
}
|
|
16025
16168
|
hydrateViewState(seed2) {
|
|
16026
16169
|
this._viewState.hydrate(seed2);
|
|
16170
|
+
this.invalidateDerivedOrder();
|
|
16027
16171
|
}
|
|
16028
16172
|
_captureViewStateOnce() {
|
|
16029
16173
|
if (this._writeCapture.peek() === null) return;
|
|
@@ -16223,6 +16367,7 @@ var ScheduleState = class {
|
|
|
16223
16367
|
this._restoreActivityFields(before);
|
|
16224
16368
|
if (this._viewStateBefore !== null) {
|
|
16225
16369
|
this._viewState.restore(this._viewStateBefore);
|
|
16370
|
+
this.invalidateDerivedOrder();
|
|
16226
16371
|
}
|
|
16227
16372
|
if (this._promotionSnapshotBefore !== null) {
|
|
16228
16373
|
this._promotionSnapshot = this._promotionSnapshotBefore;
|
|
@@ -16348,7 +16493,7 @@ function readChildrenIds(state, parentId) {
|
|
|
16348
16493
|
});
|
|
16349
16494
|
return roots;
|
|
16350
16495
|
}
|
|
16351
|
-
return
|
|
16496
|
+
return getChildrenInVisualOrder(key, state);
|
|
16352
16497
|
}
|
|
16353
16498
|
function readSelectedActivityIds(state) {
|
|
16354
16499
|
return state.checkedIds().map(String);
|
|
@@ -16492,7 +16637,7 @@ function mergeCoalesced(top, next) {
|
|
|
16492
16637
|
}
|
|
16493
16638
|
function getDispatchHistoryPolicy(action) {
|
|
16494
16639
|
if (action.kind === "persistence-acknowledge") return "clear-on-success";
|
|
16495
|
-
if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set") {
|
|
16640
|
+
if (action.kind === "sir-sync" || action.kind === "activity-lookahead-sync" || action.kind === "baseline-apply" || action.kind === "ponderator-criterion-set" || action.kind === "status-criteria-set" || action.kind === "selection-toggle" || action.kind === "selection-replace" || action.kind === "visibility-set" || action.kind === "filter-set" || action.kind === "sort-set") {
|
|
16496
16641
|
return "skip";
|
|
16497
16642
|
}
|
|
16498
16643
|
return "record";
|
|
@@ -16779,6 +16924,45 @@ var UndoRecorder = class {
|
|
|
16779
16924
|
}
|
|
16780
16925
|
};
|
|
16781
16926
|
|
|
16927
|
+
// src/dispatch/shared/resequenced-parents.ts
|
|
16928
|
+
var POSITIONAL_FIELDS = ["parentId", "correlativeId"];
|
|
16929
|
+
function collectResequencedParents(changes, parentOf) {
|
|
16930
|
+
const touched = /* @__PURE__ */ new Set();
|
|
16931
|
+
const addParentOf = (activityId) => {
|
|
16932
|
+
const parentId = parentOf(activityId);
|
|
16933
|
+
touched.add(parentId === null ? ROOT_PARENT_ID : String(parentId));
|
|
16934
|
+
};
|
|
16935
|
+
for (const change of changes.activities) {
|
|
16936
|
+
const activityId = String(change.id);
|
|
16937
|
+
if (change.kind === "created") {
|
|
16938
|
+
addParentOf(activityId);
|
|
16939
|
+
continue;
|
|
16940
|
+
}
|
|
16941
|
+
if (change.kind === "deleted") {
|
|
16942
|
+
const deletedFields = change.fields;
|
|
16943
|
+
if (deletedFields && "parentId" in deletedFields) {
|
|
16944
|
+
touched.add(parentKeyOf2(deletedFields.parentId?.before));
|
|
16945
|
+
}
|
|
16946
|
+
continue;
|
|
16947
|
+
}
|
|
16948
|
+
const fields = change.fields;
|
|
16949
|
+
if (!fields) continue;
|
|
16950
|
+
const movedPositionally = POSITIONAL_FIELDS.some(
|
|
16951
|
+
(field) => field in fields
|
|
16952
|
+
);
|
|
16953
|
+
if (!movedPositionally) continue;
|
|
16954
|
+
addParentOf(activityId);
|
|
16955
|
+
if ("parentId" in fields) {
|
|
16956
|
+
touched.add(parentKeyOf2(fields.parentId?.before));
|
|
16957
|
+
}
|
|
16958
|
+
}
|
|
16959
|
+
return touched;
|
|
16960
|
+
}
|
|
16961
|
+
function parentKeyOf2(parentId) {
|
|
16962
|
+
if (parentId === null || parentId === void 0) return ROOT_PARENT_ID;
|
|
16963
|
+
return String(parentId);
|
|
16964
|
+
}
|
|
16965
|
+
|
|
16782
16966
|
// src/init/build-state-snapshot.ts
|
|
16783
16967
|
function buildStoreLink(link) {
|
|
16784
16968
|
return {
|
|
@@ -18092,6 +18276,65 @@ var CustomIdTracker = class {
|
|
|
18092
18276
|
}
|
|
18093
18277
|
};
|
|
18094
18278
|
|
|
18279
|
+
// src/internal/order/build-comparator.ts
|
|
18280
|
+
var MISSING_LAST = 1;
|
|
18281
|
+
var MISSING_FIRST = -1;
|
|
18282
|
+
function compareMissing(aMissing, bMissing) {
|
|
18283
|
+
if (!aMissing && !bMissing) return null;
|
|
18284
|
+
if (aMissing && bMissing) return 0;
|
|
18285
|
+
return aMissing ? MISSING_LAST : MISSING_FIRST;
|
|
18286
|
+
}
|
|
18287
|
+
function isMissing(value) {
|
|
18288
|
+
return value === null || value === void 0;
|
|
18289
|
+
}
|
|
18290
|
+
function enumRank(value, order) {
|
|
18291
|
+
const index = order.indexOf(String(value));
|
|
18292
|
+
return index === -1 ? order.length : index;
|
|
18293
|
+
}
|
|
18294
|
+
function compareValues(a, b, locale) {
|
|
18295
|
+
if (a instanceof Date && b instanceof Date) {
|
|
18296
|
+
return a.getTime() - b.getTime();
|
|
18297
|
+
}
|
|
18298
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
18299
|
+
return a - b;
|
|
18300
|
+
}
|
|
18301
|
+
if (typeof a === "boolean" && typeof b === "boolean") {
|
|
18302
|
+
return Number(a) - Number(b);
|
|
18303
|
+
}
|
|
18304
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
18305
|
+
return a.length - b.length;
|
|
18306
|
+
}
|
|
18307
|
+
return String(a).localeCompare(String(b), locale, {
|
|
18308
|
+
sensitivity: "base",
|
|
18309
|
+
numeric: true
|
|
18310
|
+
});
|
|
18311
|
+
}
|
|
18312
|
+
function compareByRule(rule, context) {
|
|
18313
|
+
const descriptor = getFieldDescriptor(rule.field);
|
|
18314
|
+
if (descriptor === null) return null;
|
|
18315
|
+
return (a, b) => {
|
|
18316
|
+
const valueA = descriptor.extract(a, context);
|
|
18317
|
+
const valueB = descriptor.extract(b, context);
|
|
18318
|
+
const missing = compareMissing(isMissing(valueA), isMissing(valueB));
|
|
18319
|
+
if (missing !== null) return missing;
|
|
18320
|
+
const result = descriptor.valueKind === "enum" ? enumRank(valueA, descriptor.order) - enumRank(valueB, descriptor.order) : compareValues(valueA, valueB, context.locale);
|
|
18321
|
+
return rule.direction === "desc" ? -result : result;
|
|
18322
|
+
};
|
|
18323
|
+
}
|
|
18324
|
+
function buildComparator(order, context) {
|
|
18325
|
+
const comparators = order.rules.map((rule) => compareByRule(rule, context)).filter(
|
|
18326
|
+
(comparator) => comparator !== null
|
|
18327
|
+
);
|
|
18328
|
+
if (comparators.length === 0) return null;
|
|
18329
|
+
return (a, b) => {
|
|
18330
|
+
for (const comparator of comparators) {
|
|
18331
|
+
const result = comparator(a, b);
|
|
18332
|
+
if (result !== 0) return result;
|
|
18333
|
+
}
|
|
18334
|
+
return 0;
|
|
18335
|
+
};
|
|
18336
|
+
}
|
|
18337
|
+
|
|
18095
18338
|
// src/internal/post-processors/demote-childless-summaries.ts
|
|
18096
18339
|
function demoteChildlessSummaries(state) {
|
|
18097
18340
|
const idsToDemote = [];
|
|
@@ -18272,7 +18515,8 @@ function initializeCore(input) {
|
|
|
18272
18515
|
parsed2.calendars,
|
|
18273
18516
|
baseCalendars
|
|
18274
18517
|
);
|
|
18275
|
-
const
|
|
18518
|
+
const buildOrderComparator = (order) => buildComparator(order, buildFilterContext(sector.hoursPerDay));
|
|
18519
|
+
const state = new ScheduleState(snapshot, buildOrderComparator);
|
|
18276
18520
|
state.hydrateViewState({ hiddenIds: parsed2.viewStateSeed.hiddenIds });
|
|
18277
18521
|
const scheduler = new AutoScheduler(state, reporter);
|
|
18278
18522
|
demoteChildlessSummaries(state);
|
|
@@ -18424,6 +18668,14 @@ var ScheduleCore = class {
|
|
|
18424
18668
|
this.assertReady();
|
|
18425
18669
|
return this.coreRuntime.state.hiddenIds().map(String);
|
|
18426
18670
|
}
|
|
18671
|
+
getActiveFilter() {
|
|
18672
|
+
this.assertReady();
|
|
18673
|
+
return this.coreRuntime.state.getActiveFilter();
|
|
18674
|
+
}
|
|
18675
|
+
getActiveOrder() {
|
|
18676
|
+
this.assertReady();
|
|
18677
|
+
return this.coreRuntime.state.getActiveOrder();
|
|
18678
|
+
}
|
|
18427
18679
|
getVisualOrderIds() {
|
|
18428
18680
|
this.assertReady();
|
|
18429
18681
|
return [...this.coreRuntime.state.getVisualOrderIds()];
|
|
@@ -18540,7 +18792,7 @@ var ScheduleCore = class {
|
|
|
18540
18792
|
this._saveTracker.snapshotLinks(this.getAllLinksView());
|
|
18541
18793
|
this._undo.clear();
|
|
18542
18794
|
}
|
|
18543
|
-
result = this.
|
|
18795
|
+
result = this._withReappliedViewState(action, result);
|
|
18544
18796
|
if (!result.ok) return result;
|
|
18545
18797
|
if (dispatchChangesSchedulingState(action) && changeSetIsSubstantive(result.changes)) {
|
|
18546
18798
|
this._recordScheduleMutation(options.skipCriticalPath !== true);
|
|
@@ -18562,11 +18814,82 @@ var ScheduleCore = class {
|
|
|
18562
18814
|
await this.recomputeCriticalPath();
|
|
18563
18815
|
}
|
|
18564
18816
|
}
|
|
18565
|
-
|
|
18566
|
-
if (!result.ok || !
|
|
18567
|
-
const merged = this.
|
|
18817
|
+
_withReappliedViewState(action, result) {
|
|
18818
|
+
if (!result.ok || !reappliesViewState(action)) return result;
|
|
18819
|
+
const merged = this._reapplyViewState(result.changes);
|
|
18568
18820
|
return merged === null ? result : { ...result, changes: merged };
|
|
18569
18821
|
}
|
|
18822
|
+
/**
|
|
18823
|
+
* The single place the two view-state passes are chained. Dispatch, undo and
|
|
18824
|
+
* redo all route through here: when this existed only inside the dispatch
|
|
18825
|
+
* path, undo and redo reapplied the filter and forgot the order, so an edit
|
|
18826
|
+
* that moved a row and was then undone left the row in its new position.
|
|
18827
|
+
*
|
|
18828
|
+
* The sequence between the passes is indifferent. Filter and order are
|
|
18829
|
+
* orthogonal projections over the same tree — the filter decides which rows
|
|
18830
|
+
* exist on screen and never reads the order; the order sequences every
|
|
18831
|
+
* sibling group from the hierarchy index and never reads visibility. Either
|
|
18832
|
+
* sequence produces the same ChangeSet.
|
|
18833
|
+
*
|
|
18834
|
+
* Both passes answer null while their state is inactive, so an unfiltered,
|
|
18835
|
+
* unsorted schedule pays two property reads.
|
|
18836
|
+
*/
|
|
18837
|
+
_reapplyViewState(changes) {
|
|
18838
|
+
const withFilter = this._reapplyActiveFilter(changes);
|
|
18839
|
+
const withOrder = this._reapplyActiveOrder(withFilter ?? changes);
|
|
18840
|
+
const sequenced = this._emitTouchedBranchOrder(
|
|
18841
|
+
withOrder ?? withFilter ?? changes
|
|
18842
|
+
);
|
|
18843
|
+
return sequenced ?? withOrder ?? withFilter;
|
|
18844
|
+
}
|
|
18845
|
+
/**
|
|
18846
|
+
* Emits `order` for the branches this mutation resequenced, when no user order
|
|
18847
|
+
* is active.
|
|
18848
|
+
*
|
|
18849
|
+
* The contract is that `order` reports a CHANGED SEQUENCE, not the presence of
|
|
18850
|
+
* a sort. Tying emission to the cause instead of the effect is what produced
|
|
18851
|
+
* the undo bug and, later, the reparent ones: without an active order a move,
|
|
18852
|
+
* an indent, an outdent or the undo of any of them rearranged rows and said
|
|
18853
|
+
* nothing, so an incremental consumer kept the old sequence. The undo of a
|
|
18854
|
+
* reparent was the worst of them — it carried neither `order` nor a single
|
|
18855
|
+
* correlativeId, so the position was not recoverable by any consumer.
|
|
18856
|
+
*
|
|
18857
|
+
* The touched branches are derived from the ChangeSet rather than accumulated
|
|
18858
|
+
* in the state: an entity whose parentId or correlativeId moved, plus the
|
|
18859
|
+
* parents of created and deleted rows, is exactly the set of branches whose
|
|
18860
|
+
* sequence can differ. That keeps this linear in the blast radius, adds
|
|
18861
|
+
* nothing to the write path, and cannot leak across dispatches.
|
|
18862
|
+
*/
|
|
18863
|
+
_emitTouchedBranchOrder(changes) {
|
|
18864
|
+
const adapter = this.coreRuntime.state;
|
|
18865
|
+
if (adapter.getActiveOrder() !== null) return null;
|
|
18866
|
+
const touched = collectResequencedParents(
|
|
18867
|
+
changes,
|
|
18868
|
+
(activityId) => adapter.getParentId(activityId)
|
|
18869
|
+
);
|
|
18870
|
+
if (touched.size === 0) return null;
|
|
18871
|
+
const order = [...touched].map((parentId) => ({
|
|
18872
|
+
parentId,
|
|
18873
|
+
childIds: getChildrenInVisualOrder(parentId, adapter)
|
|
18874
|
+
})).filter((branch) => branch.childIds.length > 1);
|
|
18875
|
+
if (order.length === 0) return null;
|
|
18876
|
+
return { ...changes, order };
|
|
18877
|
+
}
|
|
18878
|
+
/**
|
|
18879
|
+
* Re-sequences the grid after any mutation that could have changed a value the
|
|
18880
|
+
* active order sorts by. Ordering is a view over the data, so an edit that
|
|
18881
|
+
* moves a row past its sibling must move the row, exactly as the filter makes
|
|
18882
|
+
* a no-longer-matching row disappear.
|
|
18883
|
+
*
|
|
18884
|
+
* Production only re-sorts after a bar drag; diverging from that is a
|
|
18885
|
+
* deliberate product decision, not an oversight.
|
|
18886
|
+
*/
|
|
18887
|
+
_reapplyActiveOrder(changes) {
|
|
18888
|
+
const adapter = this.coreRuntime.state;
|
|
18889
|
+
if (adapter.getActiveOrder() === null) return null;
|
|
18890
|
+
adapter.invalidateDerivedOrder();
|
|
18891
|
+
return { ...changes, order: collectBranchOrder(adapter) };
|
|
18892
|
+
}
|
|
18570
18893
|
_reapplyActiveFilter(changes) {
|
|
18571
18894
|
const filter = this.coreRuntime.state.getActiveFilter();
|
|
18572
18895
|
if (filter === null) return null;
|
|
@@ -18575,7 +18898,7 @@ var ScheduleCore = class {
|
|
|
18575
18898
|
activities: adapter.getAllActivities(),
|
|
18576
18899
|
parentOf: (activityId) => adapter.getParentId(activityId),
|
|
18577
18900
|
filter,
|
|
18578
|
-
context:
|
|
18901
|
+
context: buildFilterContext(this.coreRuntime.sector.hoursPerDay)
|
|
18579
18902
|
});
|
|
18580
18903
|
const viewState = applyVisibleSet(adapter, visibleIds);
|
|
18581
18904
|
if (viewState.length === 0) return null;
|
|
@@ -18734,7 +19057,7 @@ var ScheduleCore = class {
|
|
|
18734
19057
|
entry,
|
|
18735
19058
|
"before"
|
|
18736
19059
|
);
|
|
18737
|
-
return this.
|
|
19060
|
+
return this._reapplyViewState(changes) ?? changes;
|
|
18738
19061
|
});
|
|
18739
19062
|
void operation.then(
|
|
18740
19063
|
(changes) => {
|
|
@@ -18767,7 +19090,7 @@ var ScheduleCore = class {
|
|
|
18767
19090
|
entry,
|
|
18768
19091
|
"after"
|
|
18769
19092
|
);
|
|
18770
|
-
return this.
|
|
19093
|
+
return this._reapplyViewState(changes) ?? changes;
|
|
18771
19094
|
});
|
|
18772
19095
|
void operation.then(
|
|
18773
19096
|
(changes) => {
|
|
@@ -18901,6 +19224,7 @@ var DISPATCH_ACTION_KIND = {
|
|
|
18901
19224
|
SELECTION_REPLACE: "selection-replace",
|
|
18902
19225
|
VISIBILITY_SET: "visibility-set",
|
|
18903
19226
|
FILTER_SET: "filter-set",
|
|
19227
|
+
SORT_SET: "sort-set",
|
|
18904
19228
|
SIR_SYNC: "sir-sync",
|
|
18905
19229
|
ACTIVITY_LOOKAHEAD_SYNC: "activity-lookahead-sync"
|
|
18906
19230
|
};
|
|
@@ -18927,6 +19251,7 @@ var KIND_CATALOG_COVERS_UNION = {
|
|
|
18927
19251
|
[DISPATCH_ACTION_KIND.SELECTION_REPLACE]: true,
|
|
18928
19252
|
[DISPATCH_ACTION_KIND.VISIBILITY_SET]: true,
|
|
18929
19253
|
[DISPATCH_ACTION_KIND.FILTER_SET]: true,
|
|
19254
|
+
[DISPATCH_ACTION_KIND.SORT_SET]: true,
|
|
18930
19255
|
[DISPATCH_ACTION_KIND.SIR_SYNC]: true,
|
|
18931
19256
|
[DISPATCH_ACTION_KIND.ACTIVITY_LOOKAHEAD_SYNC]: true
|
|
18932
19257
|
};
|