@vectoriox/iox-ui 5.22.0 → 5.23.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/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { deriveBrandTokens } from './brand';
|
|
|
2
2
|
import { SPEC_LEGACY_ALIASES } from './flavor-spec.generated';
|
|
3
3
|
import { SWISS_FLAVOR } from './flavor';
|
|
4
4
|
import { buildFlavorTokens } from './flavor-tokens';
|
|
5
|
-
import { FlavorSeedPlan, planFlavorSeed, SeedBaseline, SeedExpectation } from './flavor-seed';
|
|
5
|
+
import { FlavorRevertPlan, FlavorSeedPlan, planFlavorRevert, planFlavorSeed, SeedBaseline, SeedExpectation } from './flavor-seed';
|
|
6
6
|
import { DesignChoices, DesignTokenRow, flavorSourceStamp, StoredDesignTokenRow } from './design-token.model';
|
|
7
7
|
import { fontFamilyKey, FontFamilyToken, fontRoleReferences, parseFontFamilies } from './fonts';
|
|
8
8
|
import { flavorPackById, isRetiredFlavorId, packDesignChoices, StoredDesign } from './design-settings';
|
|
@@ -65,6 +65,19 @@ export function planDesignApply(
|
|
|
65
65
|
return planFlavorSeed(designTokenRows(next), existing, flavorSourceStamp(pack.id), designBaseline(previous));
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* "Revert to previous look" over the org's rows — the ONE plan the Revert executes and its hint counts
|
|
70
|
+
* (`kept`: the author's edits since the apply, left as they are). The apply's record of what it wrote
|
|
71
|
+
* decides what an edit since is; a snapshot stored before that record existed (DS8.1) is judged against
|
|
72
|
+
* the rows the applied design writes — the look's own author rows (a chosen font) still go back.
|
|
73
|
+
* `null` when the design holds no snapshot.
|
|
74
|
+
*/
|
|
75
|
+
export function planDesignRevert(current: ReadonlyArray<StoredDesignTokenRow>, design: StoredDesign): FlavorRevertPlan | null {
|
|
76
|
+
const snapshot = design?.revert;
|
|
77
|
+
if (!snapshot) return null;
|
|
78
|
+
return planFlavorRevert(current, snapshot.tokens, snapshot.wrote ?? designTokenRows(design.choices));
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
/** The rows a design is missing by name — the builder-load top-up (never touches an existing row). */
|
|
69
82
|
export function missingDesignRows(existing: ReadonlyArray<{ name: string }>, choices: DesignChoices): DesignTokenRow[] {
|
|
70
83
|
const have = new Set((existing ?? []).map(r => r.name));
|
|
@@ -25,13 +25,21 @@ export interface FlavorRevertSnapshot {
|
|
|
25
25
|
/** The design row's identity BEFORE the apply, restored with the tokens. */
|
|
26
26
|
from: { flavorId: string | null; flavorVersion: string | null; brandSeed: string | null };
|
|
27
27
|
tokens: Array<Pick<DesignTokenRow, 'name' | 'type' | 'value' | 'values' | 'source' | 'group' | 'tier'>>;
|
|
28
|
+
/** What the apply left in every row it wrote (`flavorSeedWrites`). A row holding something else
|
|
29
|
+
* since, as the author's (`source: user`), was edited AFTER the apply — the revert keeps it. */
|
|
30
|
+
wrote?: FlavorRevertWrite[];
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
/** One row as an apply left it — the value and dark half the revert compares a live row against. */
|
|
34
|
+
export type FlavorRevertWrite = Pick<DesignTokenRow, 'name' | 'value' | 'values'>;
|
|
35
|
+
|
|
30
36
|
export interface FlavorRevertPlan {
|
|
31
37
|
create: DesignTokenRow[];
|
|
32
38
|
update: Array<{ id: string; data: Partial<DesignTokenRow> }>;
|
|
33
39
|
/** Rows the apply created (flavor-stamped, absent from the snapshot) — removed on revert. */
|
|
34
40
|
remove: string[];
|
|
41
|
+
/** The author's edits made after the apply, left as they are (names) — what the Revert hint counts. */
|
|
42
|
+
kept: string[];
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
export const REVERT_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -49,19 +57,30 @@ export function revertAvailable(appliedAt: number | null | undefined, now: numbe
|
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
/**
|
|
52
|
-
* Restore the snapshot
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
60
|
+
* Restore the snapshot: snapshot rows come back to their stored value/dark/source (update when
|
|
61
|
+
* present, recreate when deleted); rows the apply CREATED (flavor-stamped and absent from the
|
|
62
|
+
* snapshot) are removed. A revert undoes the FLAVOR, not the author's week: hand-made rows born after
|
|
63
|
+
* the apply are not touched, and a row the author EDITED after the apply is kept (DS8: an edit makes
|
|
64
|
+
* the row the author's, and a pack switch keeps it — so does going back). Edited-after = `source: user`,
|
|
65
|
+
* differing from the snapshot, and holding something other than what the apply wrote (`wrote` — the
|
|
66
|
+
* apply's own author rows, a chosen font or an Element-styles edit, still go back with it). With no
|
|
67
|
+
* `wrote` at all, every author row that differs from the snapshot is judged edited after.
|
|
56
68
|
*/
|
|
57
69
|
export function planFlavorRevert(
|
|
58
70
|
current: ReadonlyArray<StoredDesignTokenRow>,
|
|
59
71
|
snapshot: FlavorRevertSnapshot['tokens'],
|
|
72
|
+
wrote?: ReadonlyArray<FlavorRevertWrite> | null,
|
|
60
73
|
): FlavorRevertPlan {
|
|
61
74
|
const byName = new Map<string, StoredDesignTokenRow>();
|
|
62
75
|
for (const row of current ?? []) byName.set(row.name, row);
|
|
76
|
+
const written = new Map((wrote ?? []).map(w => [w.name, w]));
|
|
77
|
+
const editedAfter = (live: StoredDesignTokenRow) => {
|
|
78
|
+
if (live.source !== USER_TOKEN_SOURCE) return false;
|
|
79
|
+
const w = written.get(live.name);
|
|
80
|
+
return !w || !sameValue(live, { value: w.value, dark: w.values?.dark });
|
|
81
|
+
};
|
|
63
82
|
const snapNames = new Set<string>();
|
|
64
|
-
const plan: FlavorRevertPlan = { create: [], update: [], remove: [] };
|
|
83
|
+
const plan: FlavorRevertPlan = { create: [], update: [], remove: [], kept: [] };
|
|
65
84
|
for (const snap of snapshot ?? []) {
|
|
66
85
|
snapNames.add(snap.name);
|
|
67
86
|
const live = byName.get(snap.name);
|
|
@@ -72,18 +91,33 @@ export function planFlavorRevert(
|
|
|
72
91
|
const differs = live.value !== snap.value
|
|
73
92
|
|| (live.values?.dark ?? null) !== (snap.values?.dark ?? null)
|
|
74
93
|
|| (live.source ?? null) !== (snap.source ?? null);
|
|
94
|
+
if (!differs || !live._id) continue;
|
|
95
|
+
if (editedAfter(live)) { plan.kept.push(live.name); continue; }
|
|
75
96
|
// `values` is ALWAYS sent: an absent key is dropped by JSON and a `$set` then keeps the
|
|
76
97
|
// apply's dark override — a snapshot row with no dark must clear it, so it sends `{}`.
|
|
77
|
-
|
|
78
|
-
plan.update.push({ id: live._id, data: { value: snap.value, values: snap.values ?? {}, source: snap.source } });
|
|
79
|
-
}
|
|
98
|
+
plan.update.push({ id: live._id, data: { value: snap.value, values: snap.values ?? {}, source: snap.source } });
|
|
80
99
|
}
|
|
81
100
|
for (const row of current ?? []) {
|
|
82
|
-
if (
|
|
101
|
+
if (snapNames.has(row.name) || !row._id) continue;
|
|
102
|
+
if (isSeeded(row)) plan.remove.push(row._id);
|
|
103
|
+
else if (written.has(row.name) && editedAfter(row)) plan.kept.push(row.name);
|
|
83
104
|
}
|
|
84
105
|
return plan;
|
|
85
106
|
}
|
|
86
107
|
|
|
108
|
+
/** What a seed plan leaves in each row it writes, by name — the snapshot's `wrote`. */
|
|
109
|
+
export function flavorSeedWrites(plan: FlavorSeedPlan, existing: ReadonlyArray<StoredDesignTokenRow>): FlavorRevertWrite[] {
|
|
110
|
+
const byId = new Map((existing ?? []).filter(r => r._id).map(r => [r._id as string, r]));
|
|
111
|
+
const out: FlavorRevertWrite[] = (plan?.create ?? []).map(r => ({ name: r.name, value: r.value, ...(r.values ? { values: r.values } : {}) }));
|
|
112
|
+
for (const u of plan?.update ?? []) {
|
|
113
|
+
const row = byId.get(u.id);
|
|
114
|
+
if (!row) continue;
|
|
115
|
+
const values = u.data.values ?? row.values;
|
|
116
|
+
out.push({ name: row.name, value: u.data.value ?? row.value, ...(values ? { values } : {}) });
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
87
121
|
/** What a previous seed wrote for one row — the "untouched" comparison base. */
|
|
88
122
|
export interface SeedExpectation {
|
|
89
123
|
value: string;
|