@simmalugnt-se/payload-content-health 0.4.1 → 0.4.2
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
- Work-list links open collapsed Payload `collapsible` fields around the target field, found by the
|
|
6
|
+
id Payload gives them (the same fix as visual editing's).
|
|
7
|
+
|
|
3
8
|
## 0.4.1
|
|
4
9
|
|
|
5
10
|
- Signing in from the work list leads back to it. The login link's `redirect` lacked the Admin route
|
|
@@ -120,8 +120,8 @@ function formRoot(anchor) {
|
|
|
120
120
|
return view?.querySelector(".collection-edit__form") ?? document;
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
|
-
* Open the rows and
|
|
124
|
-
* Returns the innermost scope
|
|
123
|
+
* Open the rows, tabs and collapsibles the plan needs, one level at a time.
|
|
124
|
+
* Returns the innermost scope, where the target renders.
|
|
125
125
|
*/
|
|
126
126
|
async function openSteps(steps, root, current) {
|
|
127
127
|
let scope = root;
|
|
@@ -137,6 +137,26 @@ async function openSteps(steps, root, current) {
|
|
|
137
137
|
scope = row;
|
|
138
138
|
continue;
|
|
139
139
|
}
|
|
140
|
+
if (step.kind === "collapsible") {
|
|
141
|
+
const collapsibleScope = scope;
|
|
142
|
+
const field = await waitForRendered(() => collapsibleScope.querySelector(`[id="${collapsibleDomId(step.path)}"]`), collapsibleScope);
|
|
143
|
+
const element = field?.querySelector(":scope > .collapsible");
|
|
144
|
+
const button = element?.querySelector(":scope > .collapsible__toggle-wrap > .collapsible__toggle");
|
|
145
|
+
if (!element || !button) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
if (element.classList.contains("collapsible--collapsed")) {
|
|
149
|
+
button.click();
|
|
150
|
+
}
|
|
151
|
+
const content = await waitFor(() => element.classList.contains("collapsible--collapsed")
|
|
152
|
+
? null
|
|
153
|
+
: element.querySelector(".collapsible__content"));
|
|
154
|
+
if (!content) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
scope = content;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
140
160
|
const tabsScope = scope;
|
|
141
161
|
const tabs = await waitForRendered(() => tabsFieldsIn(tabsScope)[step.ordinal] ?? null, tabsScope);
|
|
142
162
|
const button = tabs?.querySelectorAll(":scope > .tabs-field__tabs-wrap > .tabs-field__tabs > .tabs-field__tab-button")[step.index];
|
|
@@ -171,13 +191,17 @@ async function waitForRendered(find, scope, timeoutMs = WAIT_MS) {
|
|
|
171
191
|
}
|
|
172
192
|
return waitFor(find, timeoutMs);
|
|
173
193
|
}
|
|
174
|
-
/** Tabs fields at this level of the scope, in DOM order; nested
|
|
194
|
+
/** Tabs fields at this level of the scope, in DOM order; nested containers are other scopes. */
|
|
175
195
|
function tabsFieldsIn(scope) {
|
|
176
196
|
return Array.from(scope.querySelectorAll(".tabs-field")).filter((element) => {
|
|
177
|
-
const outer = element.parentElement?.closest(".tabs-field, .blocks-field, .array-field");
|
|
197
|
+
const outer = element.parentElement?.closest(".tabs-field, .collapsible-field, .blocks-field, .array-field");
|
|
178
198
|
return !outer || !scope.contains(outer);
|
|
179
199
|
});
|
|
180
200
|
}
|
|
201
|
+
/** Payload's id for a collapsible field: `field-collapsible-` and its path with `__` for dots. */
|
|
202
|
+
function collapsibleDomId(path) {
|
|
203
|
+
return `field-collapsible-${path.replace(/\./g, "__")}`;
|
|
204
|
+
}
|
|
181
205
|
function waitFor(find, timeoutMs = WAIT_MS) {
|
|
182
206
|
const deadline = performance.now() + timeoutMs;
|
|
183
207
|
return new Promise((resolve) => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Work out which tabs must be opened to show a form path.
|
|
3
|
-
* so a field in another tab has no DOM element until its tab button is clicked.
|
|
2
|
+
* Work out which tabs and collapsibles must be opened to show a form path.
|
|
4
3
|
* Pure: takes client field config, returns steps for the Admin bridge to perform.
|
|
5
4
|
*/
|
|
6
5
|
export type FieldLike = {
|
|
@@ -33,6 +32,15 @@ export type RevealStep =
|
|
|
33
32
|
ordinal: number;
|
|
34
33
|
index: number;
|
|
35
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Open the collapsible field Payload renders at this path, then enter its content. The path is
|
|
37
|
+
* Payload's own for an unnamed field (`meta._index-0-1`) and gives its DOM id, so a collapsible
|
|
38
|
+
* hidden by a condition does not shift which one is opened.
|
|
39
|
+
*/
|
|
40
|
+
| {
|
|
41
|
+
kind: "collapsible";
|
|
42
|
+
path: string;
|
|
43
|
+
}
|
|
36
44
|
/** Enter a block or array row. The row element becomes the scope. */
|
|
37
45
|
| {
|
|
38
46
|
kind: "row";
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* clicked in the preview. The packages do not import each other; keep fixes in step with that copy.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Work out which tabs must be opened to show a form path.
|
|
7
|
-
* so a field in another tab has no DOM element until its tab button is clicked.
|
|
6
|
+
* Work out which tabs and collapsibles must be opened to show a form path.
|
|
8
7
|
* Pure: takes client field config, returns steps for the Admin bridge to perform.
|
|
9
8
|
*/
|
|
10
9
|
export function planReveal(rootFields, path, lookup) {
|
|
@@ -18,22 +17,22 @@ function walkPath(rootFields, path, lookup) {
|
|
|
18
17
|
const segments = path.split(".");
|
|
19
18
|
const steps = [];
|
|
20
19
|
let fields = rootFields;
|
|
21
|
-
let counter = {
|
|
20
|
+
let counter = { tabs: 0 };
|
|
22
21
|
let walked = [];
|
|
23
22
|
let last = null;
|
|
24
23
|
for (let i = 0; i < segments.length;) {
|
|
25
24
|
const segment = segments[i] ?? "";
|
|
26
|
-
const found = findIn(fields, segment, counter);
|
|
25
|
+
const found = findIn(fields, segment, counter, { dataPath: walked.join("."), indexPath: "" });
|
|
27
26
|
if (!found) {
|
|
28
27
|
return null;
|
|
29
28
|
}
|
|
30
|
-
steps.push(...found.
|
|
29
|
+
steps.push(...found.steps);
|
|
31
30
|
counter = found.counter;
|
|
32
31
|
walked = [...walked, segment];
|
|
33
32
|
i += 1;
|
|
34
33
|
if (found.kind === "namedTab") {
|
|
35
34
|
fields = found.fields;
|
|
36
|
-
counter = {
|
|
35
|
+
counter = { tabs: 0 };
|
|
37
36
|
continue;
|
|
38
37
|
}
|
|
39
38
|
const field = found.field;
|
|
@@ -46,7 +45,7 @@ function walkPath(rootFields, path, lookup) {
|
|
|
46
45
|
steps.push({ kind: "row", parentPath, index: Number(next) });
|
|
47
46
|
walked = [...walked, next];
|
|
48
47
|
i += 1;
|
|
49
|
-
counter = {
|
|
48
|
+
counter = { tabs: 0 };
|
|
50
49
|
if (field.type === "array") {
|
|
51
50
|
fields = field.fields ?? [];
|
|
52
51
|
continue;
|
|
@@ -59,7 +58,7 @@ function walkPath(rootFields, path, lookup) {
|
|
|
59
58
|
continue;
|
|
60
59
|
}
|
|
61
60
|
if (field.fields) {
|
|
62
|
-
// Named group: same DOM scope, keep counting
|
|
61
|
+
// Named group: same DOM scope, keep counting revealable fields.
|
|
63
62
|
fields = field.fields;
|
|
64
63
|
continue;
|
|
65
64
|
}
|
|
@@ -68,51 +67,72 @@ function walkPath(rootFields, path, lookup) {
|
|
|
68
67
|
}
|
|
69
68
|
return { steps, field: last };
|
|
70
69
|
}
|
|
71
|
-
function findIn(fields, name, counter) {
|
|
72
|
-
for (const field of fields) {
|
|
70
|
+
function findIn(fields, name, counter, place) {
|
|
71
|
+
for (const [position, field] of fields.entries()) {
|
|
73
72
|
if (field.type === "tabs") {
|
|
74
|
-
const ordinal = counter.
|
|
75
|
-
counter.
|
|
73
|
+
const ordinal = counter.tabs;
|
|
74
|
+
counter.tabs += 1;
|
|
75
|
+
const tabsPlace = unnamedChild(place, position);
|
|
76
76
|
for (const [index, tab] of (field.tabs ?? []).entries()) {
|
|
77
77
|
const step = { kind: "tab", ordinal, index };
|
|
78
78
|
if (tab.name === name) {
|
|
79
79
|
return {
|
|
80
80
|
kind: "namedTab",
|
|
81
81
|
fields: tab.fields ?? [],
|
|
82
|
-
|
|
83
|
-
counter: {
|
|
82
|
+
steps: [step],
|
|
83
|
+
counter: { tabs: 0 },
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
if (!tab.name) {
|
|
87
|
-
const inner = findIn(tab.fields ?? [], name, {
|
|
87
|
+
const inner = findIn(tab.fields ?? [], name, { tabs: 0 }, unnamedChild(tabsPlace, index));
|
|
88
88
|
if (inner) {
|
|
89
|
-
return { ...inner,
|
|
89
|
+
return { ...inner, steps: [step, ...inner.steps] };
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
continue;
|
|
94
94
|
}
|
|
95
95
|
if (field.name === name) {
|
|
96
|
-
return { kind: "field", field,
|
|
96
|
+
return { kind: "field", field, steps: [], counter };
|
|
97
97
|
}
|
|
98
98
|
if (!field.name && field.fields) {
|
|
99
|
-
// Row, collapsible or unnamed group:
|
|
100
|
-
|
|
99
|
+
// Row, collapsible or unnamed group: no segment in the form path. A collapsible is its own
|
|
100
|
+
// DOM scope, found by the path Payload gives it.
|
|
101
|
+
const own = unnamedChild(place, position);
|
|
102
|
+
if (field.type === "collapsible") {
|
|
103
|
+
const inner = findIn(field.fields, name, { tabs: 0 }, own);
|
|
104
|
+
if (inner) {
|
|
105
|
+
const path = [place.dataPath, `_index-${own.indexPath}`].filter(Boolean).join(".");
|
|
106
|
+
return { ...inner, steps: [{ kind: "collapsible", path }, ...inner.steps] };
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const inner = findIn(field.fields, name, counter, own);
|
|
101
111
|
if (inner) {
|
|
102
112
|
return inner;
|
|
103
113
|
}
|
|
104
114
|
continue;
|
|
105
115
|
}
|
|
106
|
-
counter.
|
|
116
|
+
counter.tabs += countTabs(field);
|
|
107
117
|
}
|
|
108
118
|
return null;
|
|
109
119
|
}
|
|
120
|
+
/** The place of the unnamed field at `position`, whose own fields render there too. */
|
|
121
|
+
function unnamedChild(place, position) {
|
|
122
|
+
return {
|
|
123
|
+
dataPath: place.dataPath,
|
|
124
|
+
indexPath: place.indexPath ? `${place.indexPath}-${position}` : String(position),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
110
127
|
/** Tabs fields rendered inside a field that is skipped, so later ordinals stay aligned with the DOM. */
|
|
111
128
|
function countTabs(field) {
|
|
112
129
|
if (field.type === "tabs") {
|
|
113
130
|
return 1;
|
|
114
131
|
}
|
|
115
|
-
if (field.type === "blocks" ||
|
|
132
|
+
if (field.type === "blocks" ||
|
|
133
|
+
field.type === "array" ||
|
|
134
|
+
field.type === "collapsible" ||
|
|
135
|
+
!field.fields) {
|
|
116
136
|
return 0;
|
|
117
137
|
}
|
|
118
138
|
return field.fields.reduce((sum, child) => sum + countTabs(child), 0);
|
package/package.json
CHANGED