@outbuild-company/schedule-core 1.20.1 → 1.22.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 +538 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -1
- package/dist/index.d.ts +72 -1
- package/dist/index.js +538 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -345,6 +345,10 @@ var DISPATCH_ACTION_KIND = {
|
|
|
345
345
|
ACTIVITY_COPY: "activity-copy",
|
|
346
346
|
CALENDAR_RECONCILE: "calendar-reconcile",
|
|
347
347
|
ACTIVITY_PASTE_COPIED: "activity-paste-copied",
|
|
348
|
+
ACTIVITY_DUPLICATE: "activity-duplicate",
|
|
349
|
+
CELL_COPY: "cell-copy",
|
|
350
|
+
CELL_CLIPBOARD_CLEAR: "cell-clipboard-clear",
|
|
351
|
+
CELL_PASTE_COPIED: "cell-paste-copied",
|
|
348
352
|
PERSISTENCE_ACKNOWLEDGE: "persistence-acknowledge",
|
|
349
353
|
BASELINE_APPLY: "baseline-apply",
|
|
350
354
|
PONDERATOR_CRITERION_SET: "ponderator-criterion-set",
|
|
@@ -593,6 +597,11 @@ function createActivitiesApi(engine) {
|
|
|
593
597
|
kind: DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED,
|
|
594
598
|
destination: { afterSiblingId }
|
|
595
599
|
}),
|
|
600
|
+
duplicate: (activityIds, context = {}) => engine.dispatch({
|
|
601
|
+
...context,
|
|
602
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_DUPLICATE,
|
|
603
|
+
activityIds
|
|
604
|
+
}),
|
|
596
605
|
clearClipboard: (context = {}) => engine.dispatch({
|
|
597
606
|
...context,
|
|
598
607
|
kind: DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR
|
|
@@ -611,6 +620,26 @@ function createActivitiesApi(engine) {
|
|
|
611
620
|
createDateGesturePreview: (gesture) => engine.createDateGesturePreview(gesture)
|
|
612
621
|
};
|
|
613
622
|
}
|
|
623
|
+
function createCellsApi(engine) {
|
|
624
|
+
return {
|
|
625
|
+
copy: (activityId, column, context = {}) => engine.dispatch({
|
|
626
|
+
...context,
|
|
627
|
+
kind: DISPATCH_ACTION_KIND.CELL_COPY,
|
|
628
|
+
activityId,
|
|
629
|
+
column
|
|
630
|
+
}),
|
|
631
|
+
paste: (activityIds, column, context = {}) => engine.dispatch({
|
|
632
|
+
...context,
|
|
633
|
+
kind: DISPATCH_ACTION_KIND.CELL_PASTE_COPIED,
|
|
634
|
+
activityIds,
|
|
635
|
+
column
|
|
636
|
+
}),
|
|
637
|
+
clearClipboard: (context = {}) => engine.dispatch({
|
|
638
|
+
...context,
|
|
639
|
+
kind: DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR
|
|
640
|
+
})
|
|
641
|
+
};
|
|
642
|
+
}
|
|
614
643
|
function createLinksApi(engine) {
|
|
615
644
|
return {
|
|
616
645
|
get: (id) => engine.getLinkView(id),
|
|
@@ -658,6 +687,7 @@ function createScheduleNamespaces(engine) {
|
|
|
658
687
|
);
|
|
659
688
|
return {
|
|
660
689
|
activities: createActivitiesApi(engine),
|
|
690
|
+
cells: createCellsApi(engine),
|
|
661
691
|
links: createLinksApi(engine),
|
|
662
692
|
calendars: {
|
|
663
693
|
get: (id) => engine.getCalendar(id),
|
|
@@ -919,6 +949,7 @@ var REJECTION_REASON = {
|
|
|
919
949
|
NO_ACTIVITY_SELECTION: "no_activity_selection",
|
|
920
950
|
CLIPBOARD_EMPTY: "clipboard_empty",
|
|
921
951
|
ACTIVITY_PASTE_INVARIANT_FAILED: "activity_paste_invariant_failed",
|
|
952
|
+
SOLE_SCHEDULE_ROOT: "sole_schedule_root",
|
|
922
953
|
ANCHOR_SIBLING_CONFLICT: "anchor_sibling_conflict",
|
|
923
954
|
ANCHOR_SIBLING_NOT_FOUND: "anchor_sibling_not_found",
|
|
924
955
|
ANCHOR_SIBLING_WRONG_PARENT: "anchor_sibling_wrong_parent",
|
|
@@ -3887,7 +3918,7 @@ function applyLinkOperation(op, deps) {
|
|
|
3887
3918
|
return applyDelete(op, deps);
|
|
3888
3919
|
}
|
|
3889
3920
|
}
|
|
3890
|
-
function
|
|
3921
|
+
function validateLinkCreateBatch(links, deps) {
|
|
3891
3922
|
if (links.length === 0) return { applied: true };
|
|
3892
3923
|
const existingLinks = deps.port.getAllLinks();
|
|
3893
3924
|
const duplicateKeys = /* @__PURE__ */ new Map();
|
|
@@ -3971,6 +4002,12 @@ function applyLinkCreates(links, deps) {
|
|
|
3971
4002
|
if (!effectiveVerdict.ok) {
|
|
3972
4003
|
return { applied: false, rejected: LINK_REJECTION_REASON.CYCLE };
|
|
3973
4004
|
}
|
|
4005
|
+
return { applied: true };
|
|
4006
|
+
}
|
|
4007
|
+
function applyLinkCreates(links, deps) {
|
|
4008
|
+
if (links.length === 0) return { applied: true };
|
|
4009
|
+
const validation = validateLinkCreateBatch(links, deps);
|
|
4010
|
+
if (!validation.applied) return validation;
|
|
3974
4011
|
for (const { operation, linkId } of links) {
|
|
3975
4012
|
const preserved = deps.preserved?.get(linkId);
|
|
3976
4013
|
deps.port.addLink({
|
|
@@ -11708,6 +11745,8 @@ var NON_SCHEDULING_INLINE_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
11708
11745
|
var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
|
|
11709
11746
|
DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR,
|
|
11710
11747
|
DISPATCH_ACTION_KIND.ACTIVITY_COPY,
|
|
11748
|
+
DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR,
|
|
11749
|
+
DISPATCH_ACTION_KIND.CELL_COPY,
|
|
11711
11750
|
DISPATCH_ACTION_KIND.SELECTION_TOGGLE,
|
|
11712
11751
|
DISPATCH_ACTION_KIND.SELECTION_REPLACE,
|
|
11713
11752
|
DISPATCH_ACTION_KIND.VISIBILITY_SET,
|
|
@@ -11726,6 +11765,8 @@ var NON_SCHEDULING_KINDS = /* @__PURE__ */ new Set([
|
|
|
11726
11765
|
var PURE_VIEW_STATE_KINDS = /* @__PURE__ */ new Set([
|
|
11727
11766
|
DISPATCH_ACTION_KIND.ACTIVITY_CLIPBOARD_CLEAR,
|
|
11728
11767
|
DISPATCH_ACTION_KIND.ACTIVITY_COPY,
|
|
11768
|
+
DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR,
|
|
11769
|
+
DISPATCH_ACTION_KIND.CELL_COPY,
|
|
11729
11770
|
DISPATCH_ACTION_KIND.SELECTION_TOGGLE,
|
|
11730
11771
|
DISPATCH_ACTION_KIND.SELECTION_REPLACE,
|
|
11731
11772
|
DISPATCH_ACTION_KIND.VISIBILITY_SET,
|
|
@@ -11743,7 +11784,7 @@ function reappliesViewState(action) {
|
|
|
11743
11784
|
}
|
|
11744
11785
|
var NO_STRUCTURAL_EXEMPTIONS = /* @__PURE__ */ new Set();
|
|
11745
11786
|
function editsOnlyNonSchedulingColumns(action) {
|
|
11746
|
-
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT) {
|
|
11787
|
+
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT || action.kind === DISPATCH_ACTION_KIND.CELL_PASTE_COPIED) {
|
|
11747
11788
|
return NON_SCHEDULING_INLINE_COLUMNS.has(action.column);
|
|
11748
11789
|
}
|
|
11749
11790
|
if (action.kind === DISPATCH_ACTION_KIND.BULK_EDIT) {
|
|
@@ -11754,7 +11795,7 @@ function editsOnlyNonSchedulingColumns(action) {
|
|
|
11754
11795
|
return false;
|
|
11755
11796
|
}
|
|
11756
11797
|
function editsOnlyDataDateColumns(action) {
|
|
11757
|
-
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT) {
|
|
11798
|
+
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT || action.kind === DISPATCH_ACTION_KIND.CELL_PASTE_COPIED) {
|
|
11758
11799
|
return DATA_DATE_COLUMNS.has(action.column);
|
|
11759
11800
|
}
|
|
11760
11801
|
return action.kind === DISPATCH_ACTION_KIND.BULK_EDIT && action.edits.every((edit) => DATA_DATE_COLUMNS.has(edit.column));
|
|
@@ -11796,6 +11837,9 @@ function dispatchChangesProgress(action) {
|
|
|
11796
11837
|
if (action.kind === DISPATCH_ACTION_KIND.BULK_EDIT) {
|
|
11797
11838
|
return action.edits.some((edit) => edit.column === COLUMN.PROGRESS);
|
|
11798
11839
|
}
|
|
11840
|
+
if (action.kind === DISPATCH_ACTION_KIND.CELL_PASTE_COPIED) {
|
|
11841
|
+
return action.column === COLUMN.PROGRESS;
|
|
11842
|
+
}
|
|
11799
11843
|
return false;
|
|
11800
11844
|
}
|
|
11801
11845
|
function resolveRunRealCost(action) {
|
|
@@ -11811,7 +11855,7 @@ var PROGRESS_AFFECTING_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
11811
11855
|
]);
|
|
11812
11856
|
function shouldRecomputeParentProgress(action) {
|
|
11813
11857
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_SET_PROGRESS) return true;
|
|
11814
|
-
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT) {
|
|
11858
|
+
if (action.kind === DISPATCH_ACTION_KIND.INLINE_EDIT || action.kind === DISPATCH_ACTION_KIND.CELL_PASTE_COPIED) {
|
|
11815
11859
|
return PROGRESS_AFFECTING_COLUMNS.has(action.column);
|
|
11816
11860
|
}
|
|
11817
11861
|
if (action.kind === DISPATCH_ACTION_KIND.BULK_EDIT) {
|
|
@@ -12783,6 +12827,15 @@ function mergeScheduledIds(firstSettleIds, secondSettleIds) {
|
|
|
12783
12827
|
}
|
|
12784
12828
|
|
|
12785
12829
|
// src/dispatch/shared/helpers.ts
|
|
12830
|
+
function getIncidentLinkSnapshots(adapter, activityId, isPredecessors) {
|
|
12831
|
+
const incidentLinkIds = isPredecessors ? adapter.getIncomingLinkIds(activityId) : adapter.getOutgoingLinkIds(activityId);
|
|
12832
|
+
const links = [];
|
|
12833
|
+
for (const linkId of incidentLinkIds) {
|
|
12834
|
+
const link = adapter.getLink(linkId);
|
|
12835
|
+
if (link) links.push(link);
|
|
12836
|
+
}
|
|
12837
|
+
return links;
|
|
12838
|
+
}
|
|
12786
12839
|
function buildLinkChangesForBatch(applied, beforeLinks, adapter) {
|
|
12787
12840
|
const out = [];
|
|
12788
12841
|
for (const { op, finalLinkId, rejected: rejected2 } of applied) {
|
|
@@ -14009,12 +14062,11 @@ async function dispatchInlineEditLinks(action, options, deps) {
|
|
|
14009
14062
|
byCorrelative.set(String(a.correlativeId), a.id);
|
|
14010
14063
|
});
|
|
14011
14064
|
const resolve = (corr) => byCorrelative.get(corr) ?? null;
|
|
14012
|
-
const
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
}
|
|
14065
|
+
const currentLinks = getIncidentLinkSnapshots(
|
|
14066
|
+
adapter,
|
|
14067
|
+
action.activityId,
|
|
14068
|
+
isPredecessors
|
|
14069
|
+
);
|
|
14018
14070
|
const diff = isPredecessors ? diffLinks({
|
|
14019
14071
|
targetActivityId: action.activityId,
|
|
14020
14072
|
currentLinks,
|
|
@@ -18482,6 +18534,457 @@ async function dispatchActivityPasteCopied(action, options, deps) {
|
|
|
18482
18534
|
};
|
|
18483
18535
|
}
|
|
18484
18536
|
|
|
18537
|
+
// src/activity-copy-paste/duplicate.ts
|
|
18538
|
+
var CLONE_YIELD_EVERY_N = 200;
|
|
18539
|
+
var CREATE_OPTIONS = {
|
|
18540
|
+
skipCorrelativeRecompute: true,
|
|
18541
|
+
skipBeforeSnap: true,
|
|
18542
|
+
skipCacheInvalidation: true,
|
|
18543
|
+
skipRollupCascades: true
|
|
18544
|
+
};
|
|
18545
|
+
async function dispatchActivityDuplicate(action, options, deps) {
|
|
18546
|
+
const resolved = resolveDuplicateRuns(action.activityIds, deps.adapter);
|
|
18547
|
+
if (!resolved.ok) return resolved;
|
|
18548
|
+
const frozenParent = firstFrozenRunParent(resolved.runs, deps.adapter);
|
|
18549
|
+
if (frozenParent) {
|
|
18550
|
+
return { ok: false, reason: REJECTION_REASON.PARENT_FROZEN_BY_SIR };
|
|
18551
|
+
}
|
|
18552
|
+
deps.scheduler.invalidateAllCaches();
|
|
18553
|
+
const beforeSnapshot = await snapshotActivities(
|
|
18554
|
+
deps.adapter,
|
|
18555
|
+
/* @__PURE__ */ new Set()
|
|
18556
|
+
);
|
|
18557
|
+
const state = {
|
|
18558
|
+
originalToNew: /* @__PURE__ */ new Map(),
|
|
18559
|
+
createdIds: [],
|
|
18560
|
+
createdParentIds: []
|
|
18561
|
+
};
|
|
18562
|
+
for (const run of resolved.runs) {
|
|
18563
|
+
const lastOriginal = run.originals.at(-1);
|
|
18564
|
+
if (!lastOriginal) continue;
|
|
18565
|
+
let anchorId = lastOriginal.id;
|
|
18566
|
+
for (const original of run.originals) {
|
|
18567
|
+
anchorId = await cloneSubtree(
|
|
18568
|
+
original,
|
|
18569
|
+
run.parentId,
|
|
18570
|
+
anchorId,
|
|
18571
|
+
action,
|
|
18572
|
+
deps,
|
|
18573
|
+
state
|
|
18574
|
+
);
|
|
18575
|
+
}
|
|
18576
|
+
}
|
|
18577
|
+
recomputeCreatedParentRollups(state, deps);
|
|
18578
|
+
const correlativeShifts = recomputeCorrelativeIds(deps.adapter);
|
|
18579
|
+
const createdIdSet = new Set(state.createdIds.map(String));
|
|
18580
|
+
const touched = /* @__PURE__ */ new Set();
|
|
18581
|
+
seedJournalBeforeImages(
|
|
18582
|
+
deps.adapter,
|
|
18583
|
+
correlativeShifts,
|
|
18584
|
+
createdIdSet,
|
|
18585
|
+
beforeSnapshot
|
|
18586
|
+
);
|
|
18587
|
+
foldCorrelativeShifts(
|
|
18588
|
+
deps.adapter,
|
|
18589
|
+
correlativeShifts,
|
|
18590
|
+
createdIdSet,
|
|
18591
|
+
beforeSnapshot,
|
|
18592
|
+
touched
|
|
18593
|
+
);
|
|
18594
|
+
for (const createdId of state.createdIds) touched.add(String(createdId));
|
|
18595
|
+
const applied = replicateInternalLinks(state, deps);
|
|
18596
|
+
const { scheduledIds } = await runPostMutation(
|
|
18597
|
+
{
|
|
18598
|
+
adapter: deps.adapter,
|
|
18599
|
+
scheduler: deps.scheduler,
|
|
18600
|
+
sector: deps.sector,
|
|
18601
|
+
defaultBaseCalendarId: deps.defaultBaseCalendarId
|
|
18602
|
+
},
|
|
18603
|
+
{
|
|
18604
|
+
action,
|
|
18605
|
+
autoscheduleFrom: state.createdIds.length > 0 ? AUTOSCHEDULE_SCOPE.ROOTS : null,
|
|
18606
|
+
recomputeParentsFrom: state.createdIds,
|
|
18607
|
+
preventSummaryTargetPullback: true,
|
|
18608
|
+
now: deps.now,
|
|
18609
|
+
options
|
|
18610
|
+
}
|
|
18611
|
+
);
|
|
18612
|
+
return {
|
|
18613
|
+
ok: true,
|
|
18614
|
+
changes: await assembleChangeSet(deps.adapter, {
|
|
18615
|
+
source: action,
|
|
18616
|
+
beforeSnap: beforeSnapshot,
|
|
18617
|
+
touchedIds: touched,
|
|
18618
|
+
scheduledIds,
|
|
18619
|
+
links: buildLinkChangesForBatch(
|
|
18620
|
+
applied,
|
|
18621
|
+
/* @__PURE__ */ new Map(),
|
|
18622
|
+
deps.adapter
|
|
18623
|
+
),
|
|
18624
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
18625
|
+
})
|
|
18626
|
+
};
|
|
18627
|
+
}
|
|
18628
|
+
function resolveDuplicateRuns(activityIds, adapter) {
|
|
18629
|
+
if (activityIds.length === 0) {
|
|
18630
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_IDS_EMPTY };
|
|
18631
|
+
}
|
|
18632
|
+
const requested = /* @__PURE__ */ new Set();
|
|
18633
|
+
const originals = [];
|
|
18634
|
+
for (const rawId of activityIds) {
|
|
18635
|
+
const activityId = String(rawId);
|
|
18636
|
+
if (requested.has(activityId)) continue;
|
|
18637
|
+
const activity = adapter.getActivity(activityId);
|
|
18638
|
+
if (!activity) {
|
|
18639
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
18640
|
+
}
|
|
18641
|
+
requested.add(activityId);
|
|
18642
|
+
originals.push(activity);
|
|
18643
|
+
}
|
|
18644
|
+
const roots = originals.filter(
|
|
18645
|
+
(activity) => !hasAncestorInSet(activity.id, requested, adapter)
|
|
18646
|
+
);
|
|
18647
|
+
const duplicatesSoleScheduleRoot = adapter.getRootIds().length === 1 && roots.some((activity) => activity.parentId == null);
|
|
18648
|
+
if (duplicatesSoleScheduleRoot) {
|
|
18649
|
+
return { ok: false, reason: REJECTION_REASON.SOLE_SCHEDULE_ROOT };
|
|
18650
|
+
}
|
|
18651
|
+
return { ok: true, runs: groupIntoContiguousRuns(roots, adapter) };
|
|
18652
|
+
}
|
|
18653
|
+
function firstFrozenRunParent(runs, adapter) {
|
|
18654
|
+
const checked = /* @__PURE__ */ new Set();
|
|
18655
|
+
for (const run of runs) {
|
|
18656
|
+
if (run.parentId === ROOT_PARENT_ID || checked.has(String(run.parentId))) {
|
|
18657
|
+
continue;
|
|
18658
|
+
}
|
|
18659
|
+
checked.add(String(run.parentId));
|
|
18660
|
+
const parent = adapter.getActivity(run.parentId);
|
|
18661
|
+
if (parent && isFrozen(parent)) return parent;
|
|
18662
|
+
}
|
|
18663
|
+
return null;
|
|
18664
|
+
}
|
|
18665
|
+
function groupIntoContiguousRuns(roots, adapter) {
|
|
18666
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
18667
|
+
for (const activity of roots) {
|
|
18668
|
+
const parentKey = normalizeParentKey(activity.parentId);
|
|
18669
|
+
const group = byParent.get(parentKey) ?? [];
|
|
18670
|
+
group.push(activity);
|
|
18671
|
+
byParent.set(parentKey, group);
|
|
18672
|
+
}
|
|
18673
|
+
const runs = [];
|
|
18674
|
+
for (const [parentKey, group] of byParent) {
|
|
18675
|
+
const parentId = parentKey === ROOT_PARENT_ID ? ROOT_PARENT_ID : parentKey;
|
|
18676
|
+
const positions = new Map(
|
|
18677
|
+
childrenInCorrelativeOrder(adapter, parentKey).map(
|
|
18678
|
+
(sibling, index) => [String(sibling.id), index]
|
|
18679
|
+
)
|
|
18680
|
+
);
|
|
18681
|
+
const positionOf = (activity) => positions.get(String(activity.id)) ?? -1;
|
|
18682
|
+
const ordered = [...group].sort(
|
|
18683
|
+
(left, right) => positionOf(left) - positionOf(right)
|
|
18684
|
+
);
|
|
18685
|
+
for (const originals of splitContiguous(ordered, positionOf)) {
|
|
18686
|
+
runs.push({ parentId, originals });
|
|
18687
|
+
}
|
|
18688
|
+
}
|
|
18689
|
+
return runs;
|
|
18690
|
+
}
|
|
18691
|
+
function splitContiguous(ordered, positionOf) {
|
|
18692
|
+
const runs = [];
|
|
18693
|
+
let previousPosition = null;
|
|
18694
|
+
for (const activity of ordered) {
|
|
18695
|
+
const position = positionOf(activity);
|
|
18696
|
+
const lastRun = runs.at(-1);
|
|
18697
|
+
if (lastRun && previousPosition !== null && position === previousPosition + 1) {
|
|
18698
|
+
lastRun.push(activity);
|
|
18699
|
+
} else {
|
|
18700
|
+
runs.push([activity]);
|
|
18701
|
+
}
|
|
18702
|
+
previousPosition = position;
|
|
18703
|
+
}
|
|
18704
|
+
return runs;
|
|
18705
|
+
}
|
|
18706
|
+
function seedJournalBeforeImages(adapter, shifts, excludeIds, beforeSnap) {
|
|
18707
|
+
const journal = adapter.peekWriteCapture();
|
|
18708
|
+
if (!journal) return;
|
|
18709
|
+
for (const shift of shifts) {
|
|
18710
|
+
if (excludeIds.has(shift.activityId) || beforeSnap.has(shift.activityId)) {
|
|
18711
|
+
continue;
|
|
18712
|
+
}
|
|
18713
|
+
const beforeImage = journal.before.get(shift.activityId);
|
|
18714
|
+
if (beforeImage) beforeSnap.set(shift.activityId, beforeImage);
|
|
18715
|
+
}
|
|
18716
|
+
}
|
|
18717
|
+
function childrenInCorrelativeOrder(adapter, parentKey) {
|
|
18718
|
+
return collectSiblings(adapter, parentKey).sort((left, right) => {
|
|
18719
|
+
const diff = readCorrelativeId(left) - readCorrelativeId(right);
|
|
18720
|
+
return diff || String(left.id).localeCompare(String(right.id));
|
|
18721
|
+
});
|
|
18722
|
+
}
|
|
18723
|
+
async function cloneSubtree(original, parentId, anchorId, action, deps, state) {
|
|
18724
|
+
const root = await createActivityCore(
|
|
18725
|
+
{
|
|
18726
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_CREATE,
|
|
18727
|
+
parentId,
|
|
18728
|
+
afterSiblingId: anchorId,
|
|
18729
|
+
overrides: copyOverrides(original),
|
|
18730
|
+
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
18731
|
+
},
|
|
18732
|
+
deps,
|
|
18733
|
+
{ ...CREATE_OPTIONS, customIdReferenceId: original.id }
|
|
18734
|
+
);
|
|
18735
|
+
if (!root.ok) throw invariantViolation("create", root.reason);
|
|
18736
|
+
registerCreated(state, original, root.newId, parentId);
|
|
18737
|
+
await cloneDescendantsIteratively(original, root.newId, action, deps, state);
|
|
18738
|
+
return root.newId;
|
|
18739
|
+
}
|
|
18740
|
+
async function cloneDescendantsIteratively(rootOriginal, rootNewParentId, action, deps, state) {
|
|
18741
|
+
const pending = new Queue();
|
|
18742
|
+
pending.enqueue({ original: rootOriginal, newParentId: rootNewParentId });
|
|
18743
|
+
let processed = 0;
|
|
18744
|
+
while (!pending.isEmpty()) {
|
|
18745
|
+
const { original, newParentId } = pending.dequeue();
|
|
18746
|
+
for (const child of childrenInCorrelativeOrder(
|
|
18747
|
+
deps.adapter,
|
|
18748
|
+
String(original.id)
|
|
18749
|
+
)) {
|
|
18750
|
+
const created = await createActivityCore(
|
|
18751
|
+
{
|
|
18752
|
+
kind: DISPATCH_ACTION_KIND.ACTIVITY_CREATE,
|
|
18753
|
+
parentId: newParentId,
|
|
18754
|
+
overrides: copyOverrides(child),
|
|
18755
|
+
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
18756
|
+
},
|
|
18757
|
+
deps,
|
|
18758
|
+
CREATE_OPTIONS
|
|
18759
|
+
);
|
|
18760
|
+
if (!created.ok) throw invariantViolation("create", created.reason);
|
|
18761
|
+
registerCreated(state, child, created.newId, newParentId);
|
|
18762
|
+
pending.enqueue({ original: child, newParentId: created.newId });
|
|
18763
|
+
processed += 1;
|
|
18764
|
+
if (processed % CLONE_YIELD_EVERY_N === 0) await yieldToBrowser();
|
|
18765
|
+
}
|
|
18766
|
+
}
|
|
18767
|
+
}
|
|
18768
|
+
function invariantViolation(step, reason) {
|
|
18769
|
+
return new Error(
|
|
18770
|
+
`[ScheduleCore] activity-duplicate: unexpected ${step} rejection after preflight passed (${reason})`
|
|
18771
|
+
);
|
|
18772
|
+
}
|
|
18773
|
+
function registerCreated(state, original, newId, parentId) {
|
|
18774
|
+
state.originalToNew.set(String(original.id), newId);
|
|
18775
|
+
state.createdIds.push(newId);
|
|
18776
|
+
state.createdParentIds.push(parentId);
|
|
18777
|
+
}
|
|
18778
|
+
function recomputeCreatedParentRollups(state, deps) {
|
|
18779
|
+
const recomputed = /* @__PURE__ */ new Set();
|
|
18780
|
+
for (const parentId of [...state.createdParentIds].reverse()) {
|
|
18781
|
+
if (parentId === ROOT_PARENT_ID || recomputed.has(String(parentId))) {
|
|
18782
|
+
continue;
|
|
18783
|
+
}
|
|
18784
|
+
recomputed.add(String(parentId));
|
|
18785
|
+
recomputeRollupCascadesForParent(parentId, deps.adapter);
|
|
18786
|
+
}
|
|
18787
|
+
}
|
|
18788
|
+
function replicateInternalLinks(state, deps) {
|
|
18789
|
+
const applied = [];
|
|
18790
|
+
for (const originalId of state.originalToNew.keys()) {
|
|
18791
|
+
for (const linkId of deps.adapter.getOutgoingLinkIds(originalId)) {
|
|
18792
|
+
const link = deps.adapter.getLink(linkId);
|
|
18793
|
+
if (!link) continue;
|
|
18794
|
+
const source = state.originalToNew.get(String(link.source));
|
|
18795
|
+
const target = state.originalToNew.get(String(link.target));
|
|
18796
|
+
if (source === void 0 || target === void 0) continue;
|
|
18797
|
+
const operation = {
|
|
18798
|
+
kind: LINK_OPERATION_KIND.CREATE,
|
|
18799
|
+
source,
|
|
18800
|
+
target,
|
|
18801
|
+
type: link.type,
|
|
18802
|
+
lag: link.lag
|
|
18803
|
+
};
|
|
18804
|
+
const result = applyLinkOperation(operation, {
|
|
18805
|
+
port: deps.adapter,
|
|
18806
|
+
newLinkId: deps.linkIdGen.next
|
|
18807
|
+
});
|
|
18808
|
+
if (!result.applied) {
|
|
18809
|
+
throw invariantViolation(
|
|
18810
|
+
"link",
|
|
18811
|
+
result.rejected ?? REJECTION_REASON.INVALID
|
|
18812
|
+
);
|
|
18813
|
+
}
|
|
18814
|
+
applied.push({
|
|
18815
|
+
op: operation,
|
|
18816
|
+
finalLinkId: result.linkId,
|
|
18817
|
+
rejected: null
|
|
18818
|
+
});
|
|
18819
|
+
}
|
|
18820
|
+
}
|
|
18821
|
+
return applied;
|
|
18822
|
+
}
|
|
18823
|
+
|
|
18824
|
+
// src/cell-copy-paste/copy.ts
|
|
18825
|
+
var CELL_CLIPBOARD_KIND = {
|
|
18826
|
+
FIELD: "field",
|
|
18827
|
+
LINKS: "links"
|
|
18828
|
+
};
|
|
18829
|
+
function copyLinkSpecs(adapter, activityId, isPredecessors) {
|
|
18830
|
+
return getIncidentLinkSnapshots(adapter, activityId, isPredecessors).map(
|
|
18831
|
+
(link) => ({
|
|
18832
|
+
otherActivityId: isPredecessors ? link.source : link.target,
|
|
18833
|
+
type: link.type,
|
|
18834
|
+
lag: link.lag
|
|
18835
|
+
})
|
|
18836
|
+
);
|
|
18837
|
+
}
|
|
18838
|
+
function emptyChangeSet(action) {
|
|
18839
|
+
return {
|
|
18840
|
+
ok: true,
|
|
18841
|
+
changes: {
|
|
18842
|
+
source: action,
|
|
18843
|
+
activities: [],
|
|
18844
|
+
links: [],
|
|
18845
|
+
calendars: [],
|
|
18846
|
+
trackingEvents: []
|
|
18847
|
+
}
|
|
18848
|
+
};
|
|
18849
|
+
}
|
|
18850
|
+
function dispatchCellCopy(action, deps) {
|
|
18851
|
+
const activity = deps.adapter.getActivity(action.activityId);
|
|
18852
|
+
if (!activity) {
|
|
18853
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
18854
|
+
}
|
|
18855
|
+
if (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS) {
|
|
18856
|
+
deps.clipboard.value = {
|
|
18857
|
+
kind: CELL_CLIPBOARD_KIND.LINKS,
|
|
18858
|
+
specs: copyLinkSpecs(
|
|
18859
|
+
deps.adapter,
|
|
18860
|
+
action.activityId,
|
|
18861
|
+
action.column === COLUMN.CUSTOM_PREDECESSORS
|
|
18862
|
+
)
|
|
18863
|
+
};
|
|
18864
|
+
return emptyChangeSet(action);
|
|
18865
|
+
}
|
|
18866
|
+
const pipeline = findPipelineForColumn(action.column);
|
|
18867
|
+
if (!pipeline) {
|
|
18868
|
+
return { ok: false, reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN };
|
|
18869
|
+
}
|
|
18870
|
+
const canonicalValue = readPipelineTargetValue(activity, pipeline.targetField);
|
|
18871
|
+
if (action.column === COLUMN.DURATION && !Number.isFinite(canonicalValue)) {
|
|
18872
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
18873
|
+
}
|
|
18874
|
+
const raw = action.column === COLUMN.DURATION ? canonicalValue / deps.hoursPerDay : cloneDomainValue(canonicalValue);
|
|
18875
|
+
deps.clipboard.value = { kind: CELL_CLIPBOARD_KIND.FIELD, raw };
|
|
18876
|
+
return emptyChangeSet(action);
|
|
18877
|
+
}
|
|
18878
|
+
function dispatchCellClipboardClear(action, clipboard) {
|
|
18879
|
+
clipboard.value = null;
|
|
18880
|
+
return emptyChangeSet(action);
|
|
18881
|
+
}
|
|
18882
|
+
|
|
18883
|
+
// src/cell-copy-paste/paste.ts
|
|
18884
|
+
function toDiffPlaceholder(activityId) {
|
|
18885
|
+
return String(activityId);
|
|
18886
|
+
}
|
|
18887
|
+
function fromDiffPlaceholder(adapter, placeholder) {
|
|
18888
|
+
const activityId = placeholder;
|
|
18889
|
+
return adapter.activityExists(activityId) ? activityId : null;
|
|
18890
|
+
}
|
|
18891
|
+
function linkOperationsForTarget(targetActivityId, isPredecessors, clipboard, deps) {
|
|
18892
|
+
const desiredSpecs = clipboard.specs.map((spec) => ({
|
|
18893
|
+
correlativeId: toDiffPlaceholder(spec.otherActivityId),
|
|
18894
|
+
type: spec.type,
|
|
18895
|
+
lag: spec.lag
|
|
18896
|
+
}));
|
|
18897
|
+
const currentLinks = getIncidentLinkSnapshots(
|
|
18898
|
+
deps.adapter,
|
|
18899
|
+
targetActivityId,
|
|
18900
|
+
isPredecessors
|
|
18901
|
+
);
|
|
18902
|
+
const resolve = (placeholder) => fromDiffPlaceholder(deps.adapter, placeholder);
|
|
18903
|
+
const diff = isPredecessors ? diffLinks({
|
|
18904
|
+
targetActivityId,
|
|
18905
|
+
currentLinks,
|
|
18906
|
+
desiredSpecs,
|
|
18907
|
+
resolveActivityByCorrelative: resolve
|
|
18908
|
+
}) : diffOutgoingLinks({
|
|
18909
|
+
sourceActivityId: targetActivityId,
|
|
18910
|
+
currentLinks,
|
|
18911
|
+
desiredSpecs,
|
|
18912
|
+
resolveActivityByCorrelative: resolve
|
|
18913
|
+
});
|
|
18914
|
+
return diff.operations;
|
|
18915
|
+
}
|
|
18916
|
+
async function dispatchCellPasteCopied(action, options, deps) {
|
|
18917
|
+
const clipboard = deps.cellClipboard.value;
|
|
18918
|
+
if (clipboard === null) {
|
|
18919
|
+
return { ok: false, reason: REJECTION_REASON.CLIPBOARD_EMPTY };
|
|
18920
|
+
}
|
|
18921
|
+
if (action.activityIds.length === 0) {
|
|
18922
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_IDS_EMPTY };
|
|
18923
|
+
}
|
|
18924
|
+
const hasMissingActivity = action.activityIds.some(
|
|
18925
|
+
(activityId) => !deps.adapter.activityExists(activityId)
|
|
18926
|
+
);
|
|
18927
|
+
if (hasMissingActivity) {
|
|
18928
|
+
return { ok: false, reason: REJECTION_REASON.ACTIVITY_NOT_FOUND };
|
|
18929
|
+
}
|
|
18930
|
+
if (action.column === COLUMN.CUSTOM_PREDECESSORS || action.column === COLUMN.CUSTOM_SUCESSORS) {
|
|
18931
|
+
if (clipboard.kind !== CELL_CLIPBOARD_KIND.LINKS) {
|
|
18932
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
18933
|
+
}
|
|
18934
|
+
const hasSelfReference = action.activityIds.some(
|
|
18935
|
+
(activityId) => clipboard.specs.some(
|
|
18936
|
+
(spec) => String(spec.otherActivityId) === String(activityId)
|
|
18937
|
+
)
|
|
18938
|
+
);
|
|
18939
|
+
if (hasSelfReference) {
|
|
18940
|
+
return { ok: false, reason: LINK_REJECTION_REASON.SELF_LINK };
|
|
18941
|
+
}
|
|
18942
|
+
const isPredecessors = action.column === COLUMN.CUSTOM_PREDECESSORS;
|
|
18943
|
+
const operations = action.activityIds.flatMap(
|
|
18944
|
+
(activityId) => linkOperationsForTarget(activityId, isPredecessors, clipboard, deps)
|
|
18945
|
+
);
|
|
18946
|
+
const createEntries = operations.filter(
|
|
18947
|
+
(operation) => operation.kind === LINK_OPERATION_KIND.CREATE
|
|
18948
|
+
).map((operation) => ({
|
|
18949
|
+
operation,
|
|
18950
|
+
linkId: deps.linkIdGen.next()
|
|
18951
|
+
}));
|
|
18952
|
+
const creation = validateLinkCreateBatch(createEntries, {
|
|
18953
|
+
port: deps.adapter,
|
|
18954
|
+
rejectParentLinkMutations: options.rejectParentLinkMutations ?? false
|
|
18955
|
+
});
|
|
18956
|
+
if (!creation.applied) {
|
|
18957
|
+
return {
|
|
18958
|
+
ok: false,
|
|
18959
|
+
reason: creation.rejected ?? REJECTION_REASON.INVALID
|
|
18960
|
+
};
|
|
18961
|
+
}
|
|
18962
|
+
return dispatchLinkBatch(operations, action, options, deps);
|
|
18963
|
+
}
|
|
18964
|
+
if (clipboard.kind !== CELL_CLIPBOARD_KIND.FIELD) {
|
|
18965
|
+
return { ok: false, reason: REJECTION_REASON.INVALID_VALUE };
|
|
18966
|
+
}
|
|
18967
|
+
if (!findPipelineForColumn(action.column)) {
|
|
18968
|
+
return { ok: false, reason: REJECTION_REASON.NO_PIPELINE_FOR_COLUMN };
|
|
18969
|
+
}
|
|
18970
|
+
const column = action.column;
|
|
18971
|
+
const result = await dispatchBulkEdit(
|
|
18972
|
+
{
|
|
18973
|
+
kind: DISPATCH_ACTION_KIND.BULK_EDIT,
|
|
18974
|
+
edits: action.activityIds.map((activityId) => ({
|
|
18975
|
+
activityId,
|
|
18976
|
+
column,
|
|
18977
|
+
newValue: clipboard.raw
|
|
18978
|
+
})),
|
|
18979
|
+
...action.eventSource === void 0 ? {} : { eventSource: action.eventSource }
|
|
18980
|
+
},
|
|
18981
|
+
options,
|
|
18982
|
+
deps
|
|
18983
|
+
);
|
|
18984
|
+
if (!result.ok) return result;
|
|
18985
|
+
return { ...result, changes: { ...result.changes, source: action } };
|
|
18986
|
+
}
|
|
18987
|
+
|
|
18485
18988
|
// src/dispatch/dispatch.ts
|
|
18486
18989
|
async function performDispatch(action, options, deps) {
|
|
18487
18990
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_COPY) {
|
|
@@ -18499,6 +19002,22 @@ async function performDispatch(action, options, deps) {
|
|
|
18499
19002
|
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_PASTE_COPIED) {
|
|
18500
19003
|
return dispatchActivityPasteCopied(action, options, deps);
|
|
18501
19004
|
}
|
|
19005
|
+
if (action.kind === DISPATCH_ACTION_KIND.ACTIVITY_DUPLICATE) {
|
|
19006
|
+
return dispatchActivityDuplicate(action, options, deps);
|
|
19007
|
+
}
|
|
19008
|
+
if (action.kind === DISPATCH_ACTION_KIND.CELL_COPY) {
|
|
19009
|
+
return dispatchCellCopy(action, {
|
|
19010
|
+
adapter: deps.adapter,
|
|
19011
|
+
clipboard: deps.cellClipboard,
|
|
19012
|
+
hoursPerDay: deps.sector.hoursPerDay
|
|
19013
|
+
});
|
|
19014
|
+
}
|
|
19015
|
+
if (action.kind === DISPATCH_ACTION_KIND.CELL_CLIPBOARD_CLEAR) {
|
|
19016
|
+
return dispatchCellClipboardClear(action, deps.cellClipboard);
|
|
19017
|
+
}
|
|
19018
|
+
if (action.kind === DISPATCH_ACTION_KIND.CELL_PASTE_COPIED) {
|
|
19019
|
+
return dispatchCellPasteCopied(action, options, deps);
|
|
19020
|
+
}
|
|
18502
19021
|
if (action.kind === ACTIVITY_BATCH_ACTION_KIND) {
|
|
18503
19022
|
return dispatchActivityBatch(action, options, deps);
|
|
18504
19023
|
}
|
|
@@ -30171,7 +30690,7 @@ function validateP6EndingActivitySearch(input) {
|
|
|
30171
30690
|
);
|
|
30172
30691
|
}
|
|
30173
30692
|
}
|
|
30174
|
-
function
|
|
30693
|
+
function emptyChangeSet2(source) {
|
|
30175
30694
|
return {
|
|
30176
30695
|
source,
|
|
30177
30696
|
activities: [],
|
|
@@ -30209,6 +30728,8 @@ function isCurrentFilterProjection(projection, filter, context) {
|
|
|
30209
30728
|
var ScheduleCore = class _ScheduleCore {
|
|
30210
30729
|
/** Activity reads and mutations. */
|
|
30211
30730
|
activities;
|
|
30731
|
+
/** Single-cell clipboard reads and mutations. */
|
|
30732
|
+
cells;
|
|
30212
30733
|
/** Dependency-link reads and mutations. */
|
|
30213
30734
|
links;
|
|
30214
30735
|
/** Calendar reads and reconciliation. */
|
|
@@ -30246,6 +30767,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
30246
30767
|
schedule: (patch) => this._scheduleP6(patch)
|
|
30247
30768
|
});
|
|
30248
30769
|
_activityClipboard = { value: null };
|
|
30770
|
+
_cellClipboard = { value: null };
|
|
30249
30771
|
_status = SCHEDULE_CORE_STATUS.READY;
|
|
30250
30772
|
coreRuntime;
|
|
30251
30773
|
_isFork;
|
|
@@ -30290,6 +30812,9 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
30290
30812
|
this._activityClipboard.value = cloneDomainValue(
|
|
30291
30813
|
source._activityClipboard.value
|
|
30292
30814
|
);
|
|
30815
|
+
this._cellClipboard.value = cloneDomainValue(
|
|
30816
|
+
source._cellClipboard.value
|
|
30817
|
+
);
|
|
30293
30818
|
this._undo = source._undo.fork();
|
|
30294
30819
|
this._saveTracker = source._saveTracker.fork();
|
|
30295
30820
|
this._scheduleRevision = source._scheduleRevision;
|
|
@@ -31518,7 +32043,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31518
32043
|
});
|
|
31519
32044
|
continue;
|
|
31520
32045
|
}
|
|
31521
|
-
const changes =
|
|
32046
|
+
const changes = emptyChangeSet2(action);
|
|
31522
32047
|
results.push({ ok: true, changes });
|
|
31523
32048
|
acceptedChangeSets.push(changes);
|
|
31524
32049
|
acceptedActions.push(action);
|
|
@@ -31647,6 +32172,7 @@ var ScheduleCore = class _ScheduleCore {
|
|
|
31647
32172
|
customIdTracker: this.coreRuntime.customIdTracker,
|
|
31648
32173
|
reporter: this.coreRuntime.reporter,
|
|
31649
32174
|
activityClipboard: this._activityClipboard,
|
|
32175
|
+
cellClipboard: this._cellClipboard,
|
|
31650
32176
|
stateRevision: this._stateRevision,
|
|
31651
32177
|
defaultBaseCalendarId: this.coreRuntime.baseCalendars.find(
|
|
31652
32178
|
(calendar) => calendar.baseDefault
|