@obvi/blueprint 1.2.0 → 1.3.1
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/README.md +21 -0
- package/dist/blueprint-choices.js +329 -163
- package/dist/blueprint-contract.json +4 -4
- package/dist/blueprint.css +171 -30
- package/dist/blueprint.js +275 -148
- package/dist/vendor/blueprint/blueprint.css +171 -30
- package/dist/vendor/blueprint/blueprint.js +275 -148
- package/package.json +9 -2
package/dist/blueprint.js
CHANGED
|
@@ -9,7 +9,7 @@ function extractRationale(root) {
|
|
|
9
9
|
if (!node) return null;
|
|
10
10
|
const wrap = node.ownerDocument.createElement("div");
|
|
11
11
|
wrap.className = "bp-choice-rationale";
|
|
12
|
-
wrap.append(...node.childNodes);
|
|
12
|
+
wrap.append(...[...node.childNodes].map((child) => child.cloneNode(true)));
|
|
13
13
|
return wrap;
|
|
14
14
|
}
|
|
15
15
|
function bodyWithoutRationale(root) {
|
|
@@ -120,6 +120,187 @@ function buildResolvedChoice(doc, { options, verdictKicker, resolvedValue }) {
|
|
|
120
120
|
root.append(stack);
|
|
121
121
|
return root;
|
|
122
122
|
}
|
|
123
|
+
function buildChoiceManifest(host, layout, options, resolvedValue) {
|
|
124
|
+
return {
|
|
125
|
+
id: host.id || host.getAttribute("name") || "",
|
|
126
|
+
label: host.getAttribute("data-obvious-choice-label") || host.getAttribute("aria-label") || host.getAttribute("name") || host.id || "",
|
|
127
|
+
layout,
|
|
128
|
+
resolvedValue,
|
|
129
|
+
options: options.map((option) => ({
|
|
130
|
+
value: option.value,
|
|
131
|
+
label: option.title || option.tab || option.value,
|
|
132
|
+
title: option.title
|
|
133
|
+
}))
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function dispatchChoiceEvent(host, type, detail) {
|
|
137
|
+
host.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true, detail }));
|
|
138
|
+
}
|
|
139
|
+
function setChoiceControlsDisabled(root, disabled) {
|
|
140
|
+
for (const control of root.querySelectorAll("input, button")) {
|
|
141
|
+
if (control instanceof HTMLInputElement || control instanceof HTMLButtonElement) {
|
|
142
|
+
control.disabled = disabled;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function appendLockedHostActions(doc, root, state) {
|
|
147
|
+
const actions = el(doc, "div", "bp-choice__actions");
|
|
148
|
+
const status = el(doc, "output", "bp-choice__hint");
|
|
149
|
+
status.setAttribute("aria-live", "polite");
|
|
150
|
+
status.textContent = state.message;
|
|
151
|
+
const reset = el(doc, "button", "bp-choice__reset");
|
|
152
|
+
reset.type = "button";
|
|
153
|
+
reset.disabled = state.busy;
|
|
154
|
+
reset.textContent = state.busy ? "Saving…" : state.reconsider;
|
|
155
|
+
actions.append(status, reset);
|
|
156
|
+
root.append(actions);
|
|
157
|
+
return reset;
|
|
158
|
+
}
|
|
159
|
+
function buildInteractiveChoice(doc, source) {
|
|
160
|
+
const {
|
|
161
|
+
layout,
|
|
162
|
+
options,
|
|
163
|
+
verdictKicker,
|
|
164
|
+
adoptLabel,
|
|
165
|
+
hint,
|
|
166
|
+
reconsider,
|
|
167
|
+
compareSummary,
|
|
168
|
+
compareBody,
|
|
169
|
+
scopeId,
|
|
170
|
+
initialValue,
|
|
171
|
+
busy,
|
|
172
|
+
message
|
|
173
|
+
} = source;
|
|
174
|
+
const viewName = `${scopeId}-view`;
|
|
175
|
+
const pickName = `${scopeId}-pick`;
|
|
176
|
+
const form = el(doc, "form", `bp-choice bp-choice--${layout}`);
|
|
177
|
+
form.dataset.bpChoice = scopeId;
|
|
178
|
+
const titleOpts = options.map((option) => ({ value: option.value, title: option.title }));
|
|
179
|
+
form.append(buildVerdict(doc, verdictKicker, titleOpts));
|
|
180
|
+
if (layout === "tabs") {
|
|
181
|
+
const selectedView = options.some((option) => option.value === initialValue) ? initialValue : options[0]?.value;
|
|
182
|
+
const seg = el(doc, "div", "bp-choice__seg");
|
|
183
|
+
seg.setAttribute("role", "tablist");
|
|
184
|
+
for (const opt of options) {
|
|
185
|
+
const segOpt = el(doc, "label", "bp-choice__seg-opt");
|
|
186
|
+
const input = doc.createElement("input");
|
|
187
|
+
input.type = "radio";
|
|
188
|
+
input.name = viewName;
|
|
189
|
+
input.value = opt.value;
|
|
190
|
+
input.className = "bp-choice__view";
|
|
191
|
+
if (opt.value === selectedView) {
|
|
192
|
+
input.defaultChecked = true;
|
|
193
|
+
input.checked = true;
|
|
194
|
+
}
|
|
195
|
+
segOpt.append(input, doc.createTextNode(opt.tab));
|
|
196
|
+
seg.append(segOpt);
|
|
197
|
+
}
|
|
198
|
+
form.append(seg);
|
|
199
|
+
const panels = el(doc, "div", "bp-choice__panels");
|
|
200
|
+
for (const opt of options) {
|
|
201
|
+
const panel = el(doc, "div", "bp-choice__panel");
|
|
202
|
+
panel.dataset.value = opt.value;
|
|
203
|
+
const h3 = el(doc, "h3");
|
|
204
|
+
h3.textContent = opt.title;
|
|
205
|
+
panel.append(h3);
|
|
206
|
+
panel.append(bodyWithoutRationale(opt.el));
|
|
207
|
+
const rationale = extractRationale(opt.el);
|
|
208
|
+
if (rationale) panel.append(rationale);
|
|
209
|
+
const actions = el(doc, "div", "bp-choice__actions");
|
|
210
|
+
const adopt = el(doc, "label", "bp-choice__adopt");
|
|
211
|
+
const commit = doc.createElement("input");
|
|
212
|
+
commit.type = "radio";
|
|
213
|
+
commit.name = pickName;
|
|
214
|
+
commit.value = opt.value;
|
|
215
|
+
commit.className = "bp-choice__commit";
|
|
216
|
+
adopt.append(commit, doc.createTextNode(adoptLabel));
|
|
217
|
+
actions.append(adopt);
|
|
218
|
+
panel.append(actions);
|
|
219
|
+
panels.append(panel);
|
|
220
|
+
}
|
|
221
|
+
form.append(panels);
|
|
222
|
+
const style = el(doc, "style");
|
|
223
|
+
style.textContent = scopedTabRules(`[data-bp-choice="${scopeId}"]`, viewName, pickName, titleOpts);
|
|
224
|
+
form.prepend(style);
|
|
225
|
+
form.append(buildFooter(doc, hint || "Switch tabs to preview · adopt to commit", reconsider));
|
|
226
|
+
}
|
|
227
|
+
if (layout === "stack") {
|
|
228
|
+
const stack = el(doc, "div", "bp-choice__stack");
|
|
229
|
+
for (const opt of options) {
|
|
230
|
+
const card = el(doc, "label", "bp-choice__card");
|
|
231
|
+
const input = doc.createElement("input");
|
|
232
|
+
input.type = "radio";
|
|
233
|
+
input.name = pickName;
|
|
234
|
+
input.value = opt.value;
|
|
235
|
+
input.className = "bp-choice__pick";
|
|
236
|
+
card.append(input);
|
|
237
|
+
const head = el(doc, "div", "bp-choice__card-head");
|
|
238
|
+
if (opt.optionLabel) head.append(label(doc, opt.optionLabel));
|
|
239
|
+
const chosen = el(doc, "span", "bp-choice__tag bp-choice__tag--ink bp-choice__card-chosen");
|
|
240
|
+
chosen.textContent = "✓ Chosen";
|
|
241
|
+
const rejected = el(doc, "span", "bp-choice__tag bp-choice__tag--out bp-choice__card-rejected");
|
|
242
|
+
rejected.textContent = "Not chosen";
|
|
243
|
+
head.append(chosen, rejected);
|
|
244
|
+
card.append(head);
|
|
245
|
+
const h4 = el(doc, "h4");
|
|
246
|
+
h4.textContent = opt.title;
|
|
247
|
+
card.append(h4);
|
|
248
|
+
card.append(bodyWithoutRationale(opt.el));
|
|
249
|
+
const rationale = extractRationale(opt.el);
|
|
250
|
+
if (rationale) card.append(rationale);
|
|
251
|
+
stack.append(card);
|
|
252
|
+
}
|
|
253
|
+
form.append(stack);
|
|
254
|
+
form.append(buildFooter(doc, hint || "Select a card to commit · rejected drafts stay readable below", reconsider));
|
|
255
|
+
}
|
|
256
|
+
if (layout === "gallery") {
|
|
257
|
+
const gallery = el(doc, "div", "bp-choice__gallery");
|
|
258
|
+
for (const opt of options) {
|
|
259
|
+
const mock = el(doc, "label", "bp-choice__mock");
|
|
260
|
+
const input = doc.createElement("input");
|
|
261
|
+
input.type = "radio";
|
|
262
|
+
input.name = pickName;
|
|
263
|
+
input.value = opt.value;
|
|
264
|
+
input.className = "bp-choice__pick";
|
|
265
|
+
mock.append(input);
|
|
266
|
+
const frame = el(doc, "div", "bp-choice__mock-frame");
|
|
267
|
+
const frameInner = frameContent(opt.el);
|
|
268
|
+
if (frameInner) frame.append(frameInner);
|
|
269
|
+
mock.append(frame);
|
|
270
|
+
const cap = el(doc, "div", "bp-choice__mock-cap");
|
|
271
|
+
const h4 = el(doc, "h4");
|
|
272
|
+
h4.textContent = opt.caption;
|
|
273
|
+
cap.append(h4);
|
|
274
|
+
const chosen = el(doc, "span", "bp-choice__tag bp-choice__tag--ink bp-choice__mock-pick bp-choice__mock-chosen");
|
|
275
|
+
chosen.textContent = "✓ Chosen";
|
|
276
|
+
const rejected = el(doc, "span", "bp-choice__tag bp-choice__tag--out bp-choice__mock-pick bp-choice__mock-rejected");
|
|
277
|
+
rejected.textContent = "Not chosen";
|
|
278
|
+
cap.append(chosen, rejected);
|
|
279
|
+
mock.append(cap);
|
|
280
|
+
gallery.append(mock);
|
|
281
|
+
}
|
|
282
|
+
form.append(gallery);
|
|
283
|
+
if (compareSummary && compareBody) {
|
|
284
|
+
const details = el(doc, "details", "bp-choice__compare");
|
|
285
|
+
const summary = el(doc, "summary");
|
|
286
|
+
summary.textContent = compareSummary;
|
|
287
|
+
details.append(summary);
|
|
288
|
+
const body = el(doc, "div", "bp-choice__compare-body");
|
|
289
|
+
body.append(compareBody.cloneNode(true));
|
|
290
|
+
details.append(body);
|
|
291
|
+
form.append(details);
|
|
292
|
+
}
|
|
293
|
+
form.append(buildFooter(doc, hint || "Select a mockup to track the decision", reconsider));
|
|
294
|
+
}
|
|
295
|
+
if (message) {
|
|
296
|
+
const status = el(doc, "output", "bp-choice__hint");
|
|
297
|
+
status.setAttribute("aria-live", "polite");
|
|
298
|
+
status.textContent = message;
|
|
299
|
+
form.append(status);
|
|
300
|
+
}
|
|
301
|
+
setChoiceControlsDisabled(form, busy);
|
|
302
|
+
return form;
|
|
303
|
+
}
|
|
123
304
|
var BlueprintRationaleElement = class extends HTMLElement {
|
|
124
305
|
connectedCallback() {
|
|
125
306
|
if (this.dataset.bpRendered) return;
|
|
@@ -130,158 +311,104 @@ var BlueprintRationaleElement = class extends HTMLElement {
|
|
|
130
311
|
var BlueprintChoiceOptionElement = class extends HTMLElement {
|
|
131
312
|
};
|
|
132
313
|
var BlueprintChoiceElement = class extends HTMLElement {
|
|
314
|
+
constructor() {
|
|
315
|
+
super();
|
|
316
|
+
this.choiceSource = null;
|
|
317
|
+
this.choiceHostState = null;
|
|
318
|
+
this.choiceScopeId = null;
|
|
319
|
+
}
|
|
133
320
|
connectedCallback() {
|
|
134
|
-
if (this.
|
|
321
|
+
if (this.choiceSource) return;
|
|
135
322
|
this.dataset.bpRendered = "1";
|
|
136
|
-
const doc = this.ownerDocument;
|
|
137
323
|
const layout = (this.getAttribute("layout") || "stack").toLowerCase();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
style.textContent = scopedTabRules(`[data-bp-choice="${scopeId}"]`, viewName, pickName, titleOpts);
|
|
200
|
-
form.prepend(style);
|
|
201
|
-
form.append(
|
|
202
|
-
buildFooter(
|
|
203
|
-
doc,
|
|
204
|
-
hint || "Switch tabs to preview · adopt to commit",
|
|
205
|
-
reconsider
|
|
206
|
-
)
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
if (layout === "stack") {
|
|
210
|
-
const stack = el(doc, "div", "bp-choice__stack");
|
|
211
|
-
for (const opt of options) {
|
|
212
|
-
const card = el(doc, "label", "bp-choice__card");
|
|
213
|
-
const input = doc.createElement("input");
|
|
214
|
-
input.type = "radio";
|
|
215
|
-
input.name = pickName;
|
|
216
|
-
input.value = opt.value;
|
|
217
|
-
input.className = "bp-choice__pick";
|
|
218
|
-
card.append(input);
|
|
219
|
-
const head = el(doc, "div", "bp-choice__card-head");
|
|
220
|
-
if (opt.optionLabel) head.append(label(doc, opt.optionLabel));
|
|
221
|
-
const chosen = el(doc, "span", "bp-choice__tag bp-choice__tag--ink bp-choice__card-chosen");
|
|
222
|
-
chosen.textContent = "✓ Chosen";
|
|
223
|
-
const rejected = el(doc, "span", "bp-choice__tag bp-choice__tag--out bp-choice__card-rejected");
|
|
224
|
-
rejected.textContent = "Not chosen";
|
|
225
|
-
head.append(chosen, rejected);
|
|
226
|
-
card.append(head);
|
|
227
|
-
const h4 = el(doc, "h4");
|
|
228
|
-
h4.textContent = opt.title;
|
|
229
|
-
card.append(h4);
|
|
230
|
-
card.append(bodyWithoutRationale(opt.el));
|
|
231
|
-
const rationale = extractRationale(opt.el);
|
|
232
|
-
if (rationale) card.append(rationale);
|
|
233
|
-
stack.append(card);
|
|
234
|
-
}
|
|
235
|
-
form.append(stack);
|
|
236
|
-
form.append(
|
|
237
|
-
buildFooter(
|
|
238
|
-
doc,
|
|
239
|
-
hint || "Select a card to commit · rejected drafts stay readable below",
|
|
240
|
-
reconsider
|
|
241
|
-
)
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
if (layout === "gallery") {
|
|
245
|
-
const gallery = el(doc, "div", "bp-choice__gallery");
|
|
246
|
-
for (const opt of options) {
|
|
247
|
-
const mock = el(doc, "label", "bp-choice__mock");
|
|
248
|
-
const input = doc.createElement("input");
|
|
249
|
-
input.type = "radio";
|
|
250
|
-
input.name = pickName;
|
|
251
|
-
input.value = opt.value;
|
|
252
|
-
input.className = "bp-choice__pick";
|
|
253
|
-
mock.append(input);
|
|
254
|
-
const frame = el(doc, "div", "bp-choice__mock-frame");
|
|
255
|
-
const frameInner = frameContent(opt.el);
|
|
256
|
-
if (frameInner) frame.append(frameInner);
|
|
257
|
-
mock.append(frame);
|
|
258
|
-
const cap = el(doc, "div", "bp-choice__mock-cap");
|
|
259
|
-
const h4 = el(doc, "h4");
|
|
260
|
-
h4.textContent = opt.caption;
|
|
261
|
-
cap.append(h4);
|
|
262
|
-
const chosen = el(doc, "span", "bp-choice__tag bp-choice__tag--ink bp-choice__mock-pick bp-choice__mock-chosen");
|
|
263
|
-
chosen.textContent = "✓ Chosen";
|
|
264
|
-
const rejected = el(doc, "span", "bp-choice__tag bp-choice__tag--out bp-choice__mock-pick bp-choice__mock-rejected");
|
|
265
|
-
rejected.textContent = "Not chosen";
|
|
266
|
-
cap.append(chosen, rejected);
|
|
267
|
-
mock.append(cap);
|
|
268
|
-
gallery.append(mock);
|
|
269
|
-
}
|
|
270
|
-
form.append(gallery);
|
|
271
|
-
if (compareSummary && compareBody) {
|
|
272
|
-
const details = el(doc, "details", "bp-choice__compare");
|
|
273
|
-
const summary = el(doc, "summary");
|
|
274
|
-
summary.textContent = compareSummary;
|
|
275
|
-
details.append(summary);
|
|
276
|
-
const body = el(doc, "div", "bp-choice__compare-body");
|
|
277
|
-
body.append(compareBody.cloneNode(true));
|
|
278
|
-
details.append(body);
|
|
279
|
-
form.append(details);
|
|
324
|
+
this.choiceSource = {
|
|
325
|
+
layout,
|
|
326
|
+
options: readOptions(this),
|
|
327
|
+
verdictKicker: this.getAttribute("verdict") ?? "Chosen",
|
|
328
|
+
adoptLabel: this.getAttribute("adopt") ?? "Adopt this direction",
|
|
329
|
+
hint: this.getAttribute("hint") ?? "",
|
|
330
|
+
reconsider: this.getAttribute("reconsider") ?? "Reconsider",
|
|
331
|
+
compareSummary: this.getAttribute("compare"),
|
|
332
|
+
compareBody: this.querySelector(':scope > [slot="compare"]'),
|
|
333
|
+
resolvedValue: this.getAttribute("resolved")
|
|
334
|
+
};
|
|
335
|
+
this.choiceScopeId = nextId("bp-choice");
|
|
336
|
+
this.renderChoice();
|
|
337
|
+
dispatchChoiceEvent(this, "bp-choice-ready", this.getDecisionManifest());
|
|
338
|
+
}
|
|
339
|
+
getDecisionManifest() {
|
|
340
|
+
if (!this.choiceSource) return null;
|
|
341
|
+
return buildChoiceManifest(
|
|
342
|
+
this,
|
|
343
|
+
this.choiceSource.layout,
|
|
344
|
+
this.choiceSource.options,
|
|
345
|
+
this.choiceSource.resolvedValue
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Apply ephemeral host-owned state without mutating authored attributes.
|
|
350
|
+
* Passing null restores the authored open/resolved rendering.
|
|
351
|
+
*
|
|
352
|
+
* @param {{ status: 'open' | 'locked', value: string | null, busy?: boolean, message?: string } | null} state
|
|
353
|
+
*/
|
|
354
|
+
applyDecisionState(state) {
|
|
355
|
+
if (!this.choiceSource) return false;
|
|
356
|
+
if (state !== null) {
|
|
357
|
+
const valueExists = state.value === null || this.choiceSource.options.some((option) => option.value === state.value);
|
|
358
|
+
if (state.status !== "open" && state.status !== "locked" || !valueExists) return false;
|
|
359
|
+
if (state.status === "locked" && state.value === null) return false;
|
|
360
|
+
}
|
|
361
|
+
this.choiceHostState = state ? { status: state.status, value: state.value, busy: Boolean(state.busy), message: state.message ?? "" } : null;
|
|
362
|
+
this.renderChoice();
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
renderChoice() {
|
|
366
|
+
if (!this.choiceSource || !this.choiceScopeId) return;
|
|
367
|
+
const source = this.choiceSource;
|
|
368
|
+
const hostState = this.choiceHostState;
|
|
369
|
+
const lockedValue = hostState?.status === "locked" ? hostState.value : hostState ? null : source.resolvedValue;
|
|
370
|
+
if (lockedValue !== null) {
|
|
371
|
+
const root = buildResolvedChoice(this.ownerDocument, {
|
|
372
|
+
options: source.options,
|
|
373
|
+
verdictKicker: source.verdictKicker,
|
|
374
|
+
resolvedValue: lockedValue
|
|
375
|
+
});
|
|
376
|
+
if (hostState) {
|
|
377
|
+
const reset = appendLockedHostActions(this.ownerDocument, root, {
|
|
378
|
+
message: hostState.message,
|
|
379
|
+
reconsider: source.reconsider,
|
|
380
|
+
busy: hostState.busy
|
|
381
|
+
});
|
|
382
|
+
reset.addEventListener("click", () => {
|
|
383
|
+
dispatchChoiceEvent(this, "bp-choice-reconsider", this.getDecisionManifest());
|
|
384
|
+
});
|
|
280
385
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
386
|
+
this.replaceChildren(root);
|
|
387
|
+
return;
|
|
284
388
|
}
|
|
389
|
+
const form = buildInteractiveChoice(this.ownerDocument, {
|
|
390
|
+
...source,
|
|
391
|
+
scopeId: this.choiceScopeId,
|
|
392
|
+
initialValue: hostState?.value ?? null,
|
|
393
|
+
busy: hostState?.busy ?? false,
|
|
394
|
+
message: hostState?.message ?? ""
|
|
395
|
+
});
|
|
396
|
+
form.addEventListener("change", (event) => {
|
|
397
|
+
const input = event.target;
|
|
398
|
+
if (!(input instanceof HTMLInputElement) || !input.checked) return;
|
|
399
|
+
const isCommit = source.layout === "tabs" ? input.classList.contains("bp-choice__commit") : input.classList.contains("bp-choice__pick");
|
|
400
|
+
if (!isCommit) return;
|
|
401
|
+
const option = source.options.find((candidate) => candidate.value === input.value);
|
|
402
|
+
if (!option) return;
|
|
403
|
+
dispatchChoiceEvent(this, "bp-choice-commit", {
|
|
404
|
+
...this.getDecisionManifest(),
|
|
405
|
+
value: option.value,
|
|
406
|
+
option: { value: option.value, label: option.title || option.tab || option.value, title: option.title }
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
form.addEventListener("reset", () => {
|
|
410
|
+
dispatchChoiceEvent(this, "bp-choice-reconsider", this.getDecisionManifest());
|
|
411
|
+
});
|
|
285
412
|
this.replaceChildren(form);
|
|
286
413
|
}
|
|
287
414
|
};
|