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