@oods/components-vue 0.4.0 → 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/README.md +1 -1
- package/dist/index.cjs +337 -12
- package/dist/index.d.cts +168 -13
- package/dist/index.d.ts +168 -13
- package/dist/index.js +338 -15
- package/dist/ported.cjs +56 -6
- package/dist/ported.d.cts +10 -1
- package/dist/ported.d.ts +10 -1
- package/dist/ported.js +57 -7
- package/evidence/vue-readiness.v1.json +146 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -34,4 +34,4 @@ Use matching versions of the OODS packages. These packages are separate from the
|
|
|
34
34
|
|
|
35
35
|
## License
|
|
36
36
|
|
|
37
|
-
Apache License 2.0. See [LICENSE](https://cdn.jsdelivr.net/npm/@oods/components-vue@0.4.
|
|
37
|
+
Apache License 2.0. See [LICENSE](https://cdn.jsdelivr.net/npm/@oods/components-vue@0.4.2/LICENSE) and [NOTICE](https://cdn.jsdelivr.net/npm/@oods/components-vue@0.4.2/NOTICE). Generated output belongs to its user; installed package dependencies retain their own licenses.
|
package/dist/index.cjs
CHANGED
|
@@ -187,6 +187,31 @@ var Button = vue.defineComponent({
|
|
|
187
187
|
}
|
|
188
188
|
});
|
|
189
189
|
var canShowModal = () => typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
|
|
190
|
+
var pageLocks = 0;
|
|
191
|
+
var pageStyle;
|
|
192
|
+
function lockPageScroll() {
|
|
193
|
+
const page = document.documentElement;
|
|
194
|
+
if (pageLocks++ === 0) {
|
|
195
|
+
const scrollbar = window.innerWidth - page.clientWidth;
|
|
196
|
+
pageStyle = { overflow: page.style.overflow, paddingRight: page.style.paddingRight };
|
|
197
|
+
if (scrollbar > 0) page.style.paddingRight = `${parseFloat(getComputedStyle(page).paddingRight) + scrollbar}px`;
|
|
198
|
+
page.style.overflow = "hidden";
|
|
199
|
+
}
|
|
200
|
+
let held = true;
|
|
201
|
+
return () => {
|
|
202
|
+
if (!held) return;
|
|
203
|
+
held = false;
|
|
204
|
+
if (--pageLocks === 0 && pageStyle) {
|
|
205
|
+
page.style.overflow = pageStyle.overflow;
|
|
206
|
+
page.style.paddingRight = pageStyle.paddingRight;
|
|
207
|
+
pageStyle = void 0;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
var outsideBox = (event, dialog) => {
|
|
212
|
+
const box = dialog.getBoundingClientRect();
|
|
213
|
+
return event.clientX < box.left || event.clientX > box.right || event.clientY < box.top || event.clientY > box.bottom;
|
|
214
|
+
};
|
|
190
215
|
var Dialog = vue.defineComponent({
|
|
191
216
|
name: "OodsDialog",
|
|
192
217
|
props: {
|
|
@@ -208,15 +233,24 @@ var Dialog = vue.defineComponent({
|
|
|
208
233
|
const element = vue.ref(null);
|
|
209
234
|
const mounted = vue.ref(false);
|
|
210
235
|
const managed = vue.computed(() => props.modal && mounted.value && canShowModal());
|
|
236
|
+
let unlockScroll;
|
|
237
|
+
let pressedBackdrop = false;
|
|
238
|
+
const releaseScroll = () => {
|
|
239
|
+
unlockScroll?.();
|
|
240
|
+
unlockScroll = void 0;
|
|
241
|
+
};
|
|
211
242
|
const sync = () => {
|
|
212
243
|
const dialog = element.value;
|
|
213
244
|
if (!dialog || !managed.value) return;
|
|
214
|
-
if (props.open && !dialog.open)
|
|
215
|
-
|
|
245
|
+
if (props.open && !dialog.open) {
|
|
246
|
+
dialog.showModal();
|
|
247
|
+
unlockScroll ??= lockPageScroll();
|
|
248
|
+
} else if (!props.open && dialog.open) dialog.close();
|
|
216
249
|
};
|
|
217
250
|
vue.onMounted(() => {
|
|
218
251
|
mounted.value = true;
|
|
219
252
|
});
|
|
253
|
+
vue.onBeforeUnmount(releaseScroll);
|
|
220
254
|
vue.watch([() => props.open, managed], sync, { flush: "post" });
|
|
221
255
|
const requestClose = (event) => {
|
|
222
256
|
event.preventDefault();
|
|
@@ -239,10 +273,23 @@ var Dialog = vue.defineComponent({
|
|
|
239
273
|
if (event.key === "Escape" && !event.defaultPrevented) requestClose(event);
|
|
240
274
|
},
|
|
241
275
|
onCancel: requestClose,
|
|
242
|
-
//
|
|
243
|
-
//
|
|
276
|
+
// A close event from moving a mounted dialog into the top layer finds it open again, and is not reported. Any other
|
|
277
|
+
// close releases the page; when the browser closed it (a close request it would not let be cancelled) while open is
|
|
278
|
+
// still true, it is reported.
|
|
244
279
|
onClose: () => {
|
|
245
|
-
if (
|
|
280
|
+
if (element.value?.open) return;
|
|
281
|
+
releaseScroll();
|
|
282
|
+
if (props.open) emit("close");
|
|
283
|
+
},
|
|
284
|
+
onMousedown: (event) => {
|
|
285
|
+
pressedBackdrop = managed.value && event.target === event.currentTarget && outsideBox(event, event.currentTarget);
|
|
286
|
+
},
|
|
287
|
+
// s223-m02 (#2527 ruling 13b): a click on the backdrop emits close, as the close control does. A press that began
|
|
288
|
+
// inside the dialog (text selected and dragged out) is not a backdrop click.
|
|
289
|
+
onClick: (event) => {
|
|
290
|
+
const backdrop = pressedBackdrop && event.target === event.currentTarget && outsideBox(event, event.currentTarget);
|
|
291
|
+
pressedBackdrop = false;
|
|
292
|
+
if (backdrop) emit("close");
|
|
246
293
|
}
|
|
247
294
|
}, [
|
|
248
295
|
vue.h("div", { class: "oods-dialog__header" }, [
|
|
@@ -805,12 +852,15 @@ function createBadgeFamily(options) {
|
|
|
805
852
|
};
|
|
806
853
|
for (const key of options.extraProps) props[key] = String;
|
|
807
854
|
for (const key of options.booleanProps ?? []) props[key] = { type: [String, Boolean], default: void 0 };
|
|
855
|
+
if (options.booleanProps) props.hideWhenFalse = { type: [Boolean, String], default: void 0 };
|
|
808
856
|
return vue.defineComponent({
|
|
809
857
|
name: `Oods${options.component}`,
|
|
810
858
|
props,
|
|
811
859
|
setup(componentProps, { slots }) {
|
|
812
860
|
return () => {
|
|
813
861
|
const record = componentProps;
|
|
862
|
+
const flag = options.booleanProps?.map((key) => record[key]).find((value) => typeof value === "boolean");
|
|
863
|
+
if (record.hideWhenFalse === true && flag === false) return null;
|
|
814
864
|
const content = authoredContent(slots.default?.());
|
|
815
865
|
const scalar = scalarContent(content);
|
|
816
866
|
const read = (key) => typeof record[key] === "boolean" && options.booleanProps?.includes(key) ? String(record[key]) : record[key];
|
|
@@ -1437,12 +1487,13 @@ var TagSummary = vue.defineComponent({
|
|
|
1437
1487
|
});
|
|
1438
1488
|
var OwnershipMeta = vue.defineComponent({
|
|
1439
1489
|
name: "OodsOwnershipMeta",
|
|
1440
|
-
props: { title: String, label: String, heading: String, name: String, ownerType: String, owner_type: String, role: String, ownershipRole: String },
|
|
1490
|
+
props: { title: String, label: String, heading: String, name: String, ownerType: String, owner_type: String, role: String, ownershipRole: String, ownerLabel: String },
|
|
1441
1491
|
setup(props, { slots }) {
|
|
1442
1492
|
return () => {
|
|
1443
1493
|
const content = authoredContent(slots.default?.());
|
|
1444
1494
|
const terms = [["Owner Type", firstScalar(props.ownerType, props.owner_type)], ["Role", firstScalar(props.role, props.ownershipRole)]].filter((entry) => entry[1] !== void 0);
|
|
1445
|
-
|
|
1495
|
+
const phrase = props.ownerLabel !== void 0 ? componentContracts.ownershipPhrase(props.ownerLabel, firstScalar(props.ownerType, props.owner_type), firstScalar(props.role, props.ownershipRole)) : void 0;
|
|
1496
|
+
return vue.h("div", { class: "oods-ownership-meta", "data-oods-component": "OwnershipMeta", "data-meta-type": "ownership" }, content.length ? content : phrase ? [vue.h("span", { "data-meta-item": "true" }, phrase)] : [
|
|
1446
1497
|
vue.h("span", { "data-meta-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Ownership"),
|
|
1447
1498
|
...terms.map(([term, value]) => vue.h("span", { key: term, "data-meta-item": "true" }, [vue.h("strong", `${term}:`), ` ${value}`]))
|
|
1448
1499
|
]);
|
|
@@ -1472,11 +1523,14 @@ var CancellationBadge = createBadgeFamily({
|
|
|
1472
1523
|
booleanLabels: ["Cancellation scheduled", "No cancellation scheduled"]
|
|
1473
1524
|
});
|
|
1474
1525
|
var ArchiveSummary = vue.defineComponent({
|
|
1526
|
+
// s224-m01 (#2542 ruling 6): hideWhenDefault is Boolean first, as the badges' hideWhenFalse is: a generated template writes
|
|
1527
|
+
// the bare attribute, which Vue casts to true only then.
|
|
1475
1528
|
name: "OodsArchiveSummary",
|
|
1476
|
-
props: { title: String, label: String, heading: String, name: String, isArchived: { type: [String, Boolean], default: void 0 }, archived: { type: [String, Boolean], default: void 0 }, status: { type: [String, Boolean], default: void 0 }, archivedAt: String, reason: String, archiveReason: String, summary: String, text: String, description: String },
|
|
1529
|
+
props: { title: String, label: String, heading: String, name: String, isArchived: { type: [String, Boolean], default: void 0 }, archived: { type: [String, Boolean], default: void 0 }, status: { type: [String, Boolean], default: void 0 }, archivedAt: String, reason: String, archiveReason: String, summary: String, text: String, description: String, hideWhenDefault: { type: [Boolean, String], default: void 0 } },
|
|
1477
1530
|
setup(props, { slots }) {
|
|
1478
1531
|
return () => {
|
|
1479
1532
|
const content = authoredContent(slots.default?.());
|
|
1533
|
+
if (props.hideWhenDefault === true && (props.isArchived ?? props.archived ?? props.status) === false && !props.archivedAt && !firstText(props.reason, props.archiveReason) && !content.length) return null;
|
|
1480
1534
|
const terms = [["Archived", componentContracts.summaryValue(props.isArchived ?? props.archived ?? props.status)], ["Archived At", props.archivedAt ? componentContracts.formatDateTime(props.archivedAt) : void 0], ["Reason", firstText(props.reason, props.archiveReason)]].filter((entry) => entry[1] !== void 0);
|
|
1481
1535
|
const fallback = firstText(props.summary, props.text, props.description);
|
|
1482
1536
|
return vue.h("section", { class: "oods-archive-summary", "data-oods-component": "ArchiveSummary", "data-summary-type": "archive" }, [
|
|
@@ -2132,13 +2186,16 @@ var Tabs = vue.defineComponent({
|
|
|
2132
2186
|
onClick: () => selectTab(item.id),
|
|
2133
2187
|
onKeydown: (event) => onTabKeydown(event, item.id)
|
|
2134
2188
|
}, slots.itemLabel?.({ item, selected: item.id === activeId.value }) ?? [item.label])),
|
|
2135
|
-
|
|
2189
|
+
// s224-m01 (#2542 ruling 2): the overflow markup is React's too: both class families, the trigger's data
|
|
2190
|
+
// attribute and aria-current="true" on an item.
|
|
2191
|
+
overflowItems.value.length > 0 ? vue.h("div", { class: "oods-tabs-overflow oods-tabs__overflow" }, [
|
|
2136
2192
|
vue.h("button", {
|
|
2137
2193
|
type: "button",
|
|
2138
2194
|
class: "oods-tabs-overflow-trigger",
|
|
2139
2195
|
"aria-label": `${props.overflowLabel} tabs`,
|
|
2140
2196
|
"aria-haspopup": "menu",
|
|
2141
2197
|
"aria-expanded": overflowOpen.value,
|
|
2198
|
+
"data-tabs-overflow-trigger": "true",
|
|
2142
2199
|
onClick: () => {
|
|
2143
2200
|
overflowOpen.value = !overflowOpen.value;
|
|
2144
2201
|
}
|
|
@@ -2148,7 +2205,7 @@ var Tabs = vue.defineComponent({
|
|
|
2148
2205
|
type: "button",
|
|
2149
2206
|
role: "menuitem",
|
|
2150
2207
|
disabled: itemDisabled(item),
|
|
2151
|
-
"aria-current": item.id === activeId.value ? "
|
|
2208
|
+
"aria-current": item.id === activeId.value ? "true" : void 0,
|
|
2152
2209
|
onClick: () => {
|
|
2153
2210
|
selectTab(item.id);
|
|
2154
2211
|
overflowOpen.value = false;
|
|
@@ -2168,6 +2225,63 @@ var Tabs = vue.defineComponent({
|
|
|
2168
2225
|
]);
|
|
2169
2226
|
}
|
|
2170
2227
|
});
|
|
2228
|
+
var SegmentedControl = vue.defineComponent({
|
|
2229
|
+
name: "OodsSegmentedControl",
|
|
2230
|
+
props: {
|
|
2231
|
+
id: String,
|
|
2232
|
+
label: String,
|
|
2233
|
+
modelValue: String,
|
|
2234
|
+
value: String,
|
|
2235
|
+
defaultValue: { type: String, default: "" },
|
|
2236
|
+
name: String,
|
|
2237
|
+
size: { type: String, default: "md" },
|
|
2238
|
+
disabled: Boolean,
|
|
2239
|
+
options: { type: Array, default: () => [] }
|
|
2240
|
+
},
|
|
2241
|
+
emits: {
|
|
2242
|
+
"update:modelValue": (_value) => true,
|
|
2243
|
+
change: (_value) => true
|
|
2244
|
+
},
|
|
2245
|
+
setup(props, { emit, slots }) {
|
|
2246
|
+
const generatedId = vue.useId();
|
|
2247
|
+
const internalValue = vue.ref(props.defaultValue);
|
|
2248
|
+
const currentValue = vue.computed(() => props.modelValue ?? props.value ?? internalValue.value);
|
|
2249
|
+
const select = (event) => {
|
|
2250
|
+
const next = event.target.value;
|
|
2251
|
+
if (props.modelValue === void 0 && props.value === void 0) internalValue.value = next;
|
|
2252
|
+
emit("update:modelValue", next);
|
|
2253
|
+
emit("change", next);
|
|
2254
|
+
};
|
|
2255
|
+
return () => {
|
|
2256
|
+
const id = props.id || `oods-segmentedcontrol-${generatedId}`;
|
|
2257
|
+
const label = slots.label?.() ?? props.label;
|
|
2258
|
+
return vue.h("div", {
|
|
2259
|
+
class: "oods-field oods-segmented-control",
|
|
2260
|
+
"data-oods-component": "SegmentedControl",
|
|
2261
|
+
"data-size": props.size
|
|
2262
|
+
}, [
|
|
2263
|
+
vue.h("span", { id: `${id}-label`, class: "oods-field-label" }, [label]),
|
|
2264
|
+
vue.h("div", {
|
|
2265
|
+
id,
|
|
2266
|
+
role: "radiogroup",
|
|
2267
|
+
"aria-labelledby": `${id}-label`,
|
|
2268
|
+
class: "oods-segmented-control__track"
|
|
2269
|
+
}, props.options.map((option) => vue.h("label", { key: option.value, class: "oods-segmented-control__option" }, [
|
|
2270
|
+
vue.h("input", {
|
|
2271
|
+
type: "radio",
|
|
2272
|
+
class: "oods-segmented-control__input",
|
|
2273
|
+
name: props.name || id,
|
|
2274
|
+
value: option.value,
|
|
2275
|
+
checked: option.value === currentValue.value,
|
|
2276
|
+
disabled: props.disabled || option.disabled,
|
|
2277
|
+
onChange: select
|
|
2278
|
+
}),
|
|
2279
|
+
vue.h("span", { class: "oods-segmented-control__label" }, option.label)
|
|
2280
|
+
])))
|
|
2281
|
+
]);
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
});
|
|
2171
2285
|
function isRecord2(value) {
|
|
2172
2286
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2173
2287
|
}
|
|
@@ -2505,12 +2619,15 @@ var CancellationSummary = vue.defineComponent({
|
|
|
2505
2619
|
cancelAtPeriodEndField: String,
|
|
2506
2620
|
requestedAtField: String,
|
|
2507
2621
|
reasonField: String,
|
|
2508
|
-
codeField: String
|
|
2622
|
+
codeField: String,
|
|
2623
|
+
// s224-m01 (#2542 ruling 6): Boolean first, as the badges' hideWhenFalse is (a bare attribute casts to true).
|
|
2624
|
+
hideWhenDefault: { type: [Boolean, String], default: void 0 }
|
|
2509
2625
|
},
|
|
2510
2626
|
setup(props, { attrs, slots }) {
|
|
2511
2627
|
return () => {
|
|
2512
2628
|
const slotValue = slotSummaryValue(slots);
|
|
2513
2629
|
const cancellationValue = props.cancelAtPeriodEnd ?? slotValue;
|
|
2630
|
+
if (props.hideWhenDefault === true && props.cancelAtPeriodEnd === false && !props.requestedAt && !props.reason && !props.code && slotValue === void 0) return null;
|
|
2514
2631
|
const rows = [];
|
|
2515
2632
|
if (cancellationValue !== void 0) {
|
|
2516
2633
|
rows.push(summaryEntry(
|
|
@@ -2985,7 +3102,8 @@ var array = Array;
|
|
|
2985
3102
|
function eventRecipe(id, kind, title) {
|
|
2986
3103
|
return vue.defineComponent({ name: id, props: { id: String, title: { type: String, default: title }, archivedAt: String, restoredAt: String, archivedBy: String, reason: String, timestamp: String, code: String, history: array, status: String, showActor: { type: Boolean, default: true }, showReason: { type: Boolean, default: true } }, setup: (props) => () => {
|
|
2987
3104
|
const rows = componentContracts.traitEventRows(kind, props);
|
|
2988
|
-
|
|
3105
|
+
if (!rows.length) return null;
|
|
3106
|
+
return vue.h("article", { id: props.id, class: "oods-trait-recipe", "data-oods-component": id, "aria-label": props.title }, [vue.h("h2", props.title), vue.h("ol", rows.map((row, index) => vue.h("li", { key: index }, [vue.h("strong", row.title), vue.h("p", row.at ? vue.h("time", { datetime: row.at }, row.time) : row.time), row.actor && vue.h("p", `Actor: ${row.actor}`), row.reason && vue.h("p", `Reason: ${row.reason}`), row.code && vue.h("p", `Code: ${row.code}`)])))]);
|
|
2989
3107
|
} });
|
|
2990
3108
|
}
|
|
2991
3109
|
var ArchiveEvent = eventRecipe("ArchiveEvent", "archive", "Archive events");
|
|
@@ -3086,6 +3204,211 @@ var VizLinePreview = preview("VizLinePreview", "line");
|
|
|
3086
3204
|
var VizMarkPreview = preview("VizMarkPreview", "bar");
|
|
3087
3205
|
var VizPointPreview = preview("VizPointPreview", "scatter");
|
|
3088
3206
|
var VizScatterPreview = preview("VizScatterPreview", "scatter");
|
|
3207
|
+
var Combobox = vue.defineComponent({
|
|
3208
|
+
name: "OodsCombobox",
|
|
3209
|
+
props: {
|
|
3210
|
+
id: String,
|
|
3211
|
+
label: String,
|
|
3212
|
+
options: { type: Array, default: () => [] },
|
|
3213
|
+
modelValue: String,
|
|
3214
|
+
value: String,
|
|
3215
|
+
defaultValue: { type: String, default: "" },
|
|
3216
|
+
placeholder: String,
|
|
3217
|
+
name: String,
|
|
3218
|
+
required: Boolean,
|
|
3219
|
+
disabled: Boolean,
|
|
3220
|
+
help: String,
|
|
3221
|
+
validation: Object,
|
|
3222
|
+
/** The control height, as Button, Input and Select: xs 24, sm 28, md 32 (default) or lg 40. */
|
|
3223
|
+
size: { type: String, default: "md" }
|
|
3224
|
+
},
|
|
3225
|
+
emits: {
|
|
3226
|
+
"update:modelValue": (_value) => true,
|
|
3227
|
+
change: (_value) => true
|
|
3228
|
+
},
|
|
3229
|
+
setup(props, { emit, slots }) {
|
|
3230
|
+
const generatedId = vue.useId();
|
|
3231
|
+
const controlId = vue.computed(() => props.id || `oods-combobox-${generatedId}`);
|
|
3232
|
+
const internalValue = vue.ref(props.defaultValue);
|
|
3233
|
+
const query = vue.ref(null);
|
|
3234
|
+
const open = vue.ref(false);
|
|
3235
|
+
const active = vue.ref(-1);
|
|
3236
|
+
const root = vue.ref(null);
|
|
3237
|
+
const listbox = vue.ref(null);
|
|
3238
|
+
const chosenValue = vue.computed(() => props.modelValue ?? props.value ?? internalValue.value);
|
|
3239
|
+
const chosen = vue.computed(() => props.options.find((option) => option.value === chosenValue.value));
|
|
3240
|
+
const text = vue.computed(() => query.value ?? chosen.value?.label ?? "");
|
|
3241
|
+
const matches = vue.computed(() => {
|
|
3242
|
+
const needle = (query.value ?? "").trim().toLowerCase();
|
|
3243
|
+
return props.options.map((option) => needle === "" || option.label.toLowerCase().includes(needle));
|
|
3244
|
+
});
|
|
3245
|
+
const reachable = vue.computed(() => props.options.flatMap((option, index) => matches.value[index] && !option.disabled ? [index] : []));
|
|
3246
|
+
const optionId = (index) => `${controlId.value}-option-${index}`;
|
|
3247
|
+
const close = () => {
|
|
3248
|
+
open.value = false;
|
|
3249
|
+
active.value = -1;
|
|
3250
|
+
};
|
|
3251
|
+
const commit = (next) => {
|
|
3252
|
+
if (props.modelValue === void 0 && props.value === void 0) internalValue.value = next;
|
|
3253
|
+
emit("update:modelValue", next);
|
|
3254
|
+
emit("change", next);
|
|
3255
|
+
};
|
|
3256
|
+
const pick = (index) => {
|
|
3257
|
+
const option = props.options[index];
|
|
3258
|
+
if (!option || option.disabled) return;
|
|
3259
|
+
query.value = null;
|
|
3260
|
+
close();
|
|
3261
|
+
if (option.value !== chosenValue.value) commit(option.value);
|
|
3262
|
+
};
|
|
3263
|
+
const outside = (event) => {
|
|
3264
|
+
if (!root.value?.contains(event.target)) close();
|
|
3265
|
+
};
|
|
3266
|
+
vue.watch(open, (isOpen) => {
|
|
3267
|
+
if (isOpen) document.addEventListener("pointerdown", outside);
|
|
3268
|
+
else document.removeEventListener("pointerdown", outside);
|
|
3269
|
+
});
|
|
3270
|
+
vue.onBeforeUnmount(() => {
|
|
3271
|
+
if (typeof document !== "undefined") document.removeEventListener("pointerdown", outside);
|
|
3272
|
+
});
|
|
3273
|
+
vue.watch([open, active], async () => {
|
|
3274
|
+
if (!open.value || active.value < 0) return;
|
|
3275
|
+
await vue.nextTick();
|
|
3276
|
+
const element = listbox.value?.children[active.value];
|
|
3277
|
+
element?.scrollIntoView?.({ block: "nearest" });
|
|
3278
|
+
});
|
|
3279
|
+
const keydown = (event) => {
|
|
3280
|
+
if (event.defaultPrevented || props.disabled || event.isComposing) return;
|
|
3281
|
+
const order = reachable.value;
|
|
3282
|
+
const first = order[0] ?? -1;
|
|
3283
|
+
const last = order[order.length - 1] ?? -1;
|
|
3284
|
+
const at = order.indexOf(active.value);
|
|
3285
|
+
switch (event.key) {
|
|
3286
|
+
case "ArrowDown":
|
|
3287
|
+
event.preventDefault();
|
|
3288
|
+
active.value = !open.value || at === -1 ? first : order[(at + 1) % order.length];
|
|
3289
|
+
open.value = true;
|
|
3290
|
+
return;
|
|
3291
|
+
case "ArrowUp":
|
|
3292
|
+
event.preventDefault();
|
|
3293
|
+
active.value = !open.value || at <= 0 ? last : order[at - 1];
|
|
3294
|
+
open.value = true;
|
|
3295
|
+
return;
|
|
3296
|
+
case "Home":
|
|
3297
|
+
case "End":
|
|
3298
|
+
if (!open.value) return;
|
|
3299
|
+
event.preventDefault();
|
|
3300
|
+
active.value = event.key === "Home" ? first : last;
|
|
3301
|
+
return;
|
|
3302
|
+
case "Enter":
|
|
3303
|
+
if (!open.value) return;
|
|
3304
|
+
event.preventDefault();
|
|
3305
|
+
if (active.value >= 0) pick(active.value);
|
|
3306
|
+
return;
|
|
3307
|
+
case "Escape":
|
|
3308
|
+
if (open.value) {
|
|
3309
|
+
event.preventDefault();
|
|
3310
|
+
close();
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
if (text.value === "" && chosenValue.value === "") return;
|
|
3314
|
+
event.preventDefault();
|
|
3315
|
+
query.value = null;
|
|
3316
|
+
if (chosenValue.value !== "") commit("");
|
|
3317
|
+
return;
|
|
3318
|
+
case "Tab":
|
|
3319
|
+
if (open.value) close();
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
};
|
|
3323
|
+
return () => {
|
|
3324
|
+
const id = controlId.value;
|
|
3325
|
+
const label = slots.label?.() ?? props.label;
|
|
3326
|
+
const help = slots.help?.() ?? props.help;
|
|
3327
|
+
const validation = slots.validation?.() ?? props.validation?.message;
|
|
3328
|
+
const tone = props.validation ? props.validation.state === "error" ? "critical" : props.validation.state : void 0;
|
|
3329
|
+
const describedBy = [help ? `${id}-help` : void 0, validation ? `${id}-validation` : void 0].filter(Boolean).join(" ") || void 0;
|
|
3330
|
+
const noMatches = open.value && !matches.value.some(Boolean);
|
|
3331
|
+
return vue.h("div", {
|
|
3332
|
+
ref: root,
|
|
3333
|
+
class: "oods-field oods-combobox",
|
|
3334
|
+
...tone ? { style: { "--cmp-input-message-border": `var(--sys-status-${tone}-border)`, "--cmp-input-message-text": `var(--sys-status-${tone}-text)` } } : {},
|
|
3335
|
+
"data-oods-component": "Combobox",
|
|
3336
|
+
"data-size": props.size,
|
|
3337
|
+
"data-validation-state": props.validation?.state
|
|
3338
|
+
}, [
|
|
3339
|
+
vue.h("label", { id: `${id}-label`, class: "oods-field-label", for: id }, [
|
|
3340
|
+
label,
|
|
3341
|
+
props.required ? vue.h("span", { class: "oods-field-required", "aria-hidden": "true" }, "*") : null
|
|
3342
|
+
]),
|
|
3343
|
+
vue.h("div", { class: "oods-combobox__control" }, [
|
|
3344
|
+
vue.h("input", {
|
|
3345
|
+
id,
|
|
3346
|
+
type: "text",
|
|
3347
|
+
role: "combobox",
|
|
3348
|
+
class: "oods-field-control oods-combobox__input",
|
|
3349
|
+
value: text.value,
|
|
3350
|
+
placeholder: props.placeholder,
|
|
3351
|
+
autocomplete: "off",
|
|
3352
|
+
"aria-autocomplete": "list",
|
|
3353
|
+
"aria-expanded": open.value ? "true" : "false",
|
|
3354
|
+
"aria-controls": `${id}-listbox`,
|
|
3355
|
+
"aria-activedescendant": open.value && active.value >= 0 ? optionId(active.value) : void 0,
|
|
3356
|
+
"aria-describedby": describedBy,
|
|
3357
|
+
"aria-invalid": props.validation?.state === "error" ? "true" : void 0,
|
|
3358
|
+
required: props.required,
|
|
3359
|
+
disabled: props.disabled,
|
|
3360
|
+
onInput: (event) => {
|
|
3361
|
+
query.value = event.target.value;
|
|
3362
|
+
open.value = true;
|
|
3363
|
+
active.value = -1;
|
|
3364
|
+
},
|
|
3365
|
+
onKeydown: keydown,
|
|
3366
|
+
onClick: () => {
|
|
3367
|
+
if (!props.disabled) open.value = true;
|
|
3368
|
+
},
|
|
3369
|
+
onBlur: () => {
|
|
3370
|
+
query.value = null;
|
|
3371
|
+
close();
|
|
3372
|
+
}
|
|
3373
|
+
}),
|
|
3374
|
+
vue.h("span", { class: "oods-combobox__chevron", "aria-hidden": "true" }, [statusIcon("chevron-down")]),
|
|
3375
|
+
vue.h("div", {
|
|
3376
|
+
class: "oods-combobox__popup",
|
|
3377
|
+
hidden: !open.value,
|
|
3378
|
+
onMousedown: (event) => event.preventDefault()
|
|
3379
|
+
}, [
|
|
3380
|
+
vue.h(
|
|
3381
|
+
"ul",
|
|
3382
|
+
{ ref: listbox, id: `${id}-listbox`, class: "oods-combobox__listbox", role: "listbox", "aria-labelledby": `${id}-label` },
|
|
3383
|
+
props.options.map((option, index) => vue.h("li", {
|
|
3384
|
+
key: option.value,
|
|
3385
|
+
id: optionId(index),
|
|
3386
|
+
class: "oods-combobox__option",
|
|
3387
|
+
role: "option",
|
|
3388
|
+
"aria-selected": option.value === chosenValue.value ? "true" : "false",
|
|
3389
|
+
"aria-disabled": option.disabled ? "true" : void 0,
|
|
3390
|
+
"data-value": option.value,
|
|
3391
|
+
"data-active": index === active.value ? "true" : void 0,
|
|
3392
|
+
hidden: !matches.value[index],
|
|
3393
|
+
onClick: () => pick(index),
|
|
3394
|
+
onPointermove: () => {
|
|
3395
|
+
if (!option.disabled && index !== active.value) active.value = index;
|
|
3396
|
+
}
|
|
3397
|
+
}, [
|
|
3398
|
+
vue.h("span", { class: "oods-combobox__option-label" }, option.label),
|
|
3399
|
+
vue.h("span", { class: "oods-combobox__check", "aria-hidden": "true" }, [statusIcon("check")])
|
|
3400
|
+
]))
|
|
3401
|
+
),
|
|
3402
|
+
vue.h("p", { class: "oods-combobox__empty", role: "status" }, noMatches ? "No matches" : "")
|
|
3403
|
+
])
|
|
3404
|
+
]),
|
|
3405
|
+
props.name ? vue.h("input", { type: "hidden", name: props.name, value: chosenValue.value }) : null,
|
|
3406
|
+
help ? vue.h("p", { id: `${id}-help`, class: "oods-field-help" }, help) : null,
|
|
3407
|
+
validation ? vue.h("p", { id: `${id}-validation`, class: "oods-field-error", role: props.validation?.state === "error" ? "alert" : void 0 }, validation) : null
|
|
3408
|
+
]);
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
});
|
|
3089
3412
|
|
|
3090
3413
|
exports.AddressCollectionPanel = AddressCollectionPanel;
|
|
3091
3414
|
exports.AddressEditor = AddressEditor;
|
|
@@ -3118,6 +3441,7 @@ exports.ClassificationPanel = ClassificationPanel;
|
|
|
3118
3441
|
exports.ColorStatePicker = ColorStatePicker;
|
|
3119
3442
|
exports.ColorSwatch = ColorSwatch;
|
|
3120
3443
|
exports.ColorizedBadge = ColorizedBadge;
|
|
3444
|
+
exports.Combobox = Combobox;
|
|
3121
3445
|
exports.CommunicationDetailPanel = CommunicationDetailPanel;
|
|
3122
3446
|
exports.CycleProgressCard = CycleProgressCard;
|
|
3123
3447
|
exports.DatePicker = DatePicker;
|
|
@@ -3153,6 +3477,7 @@ exports.RelativeTimestamp = RelativeTimestamp;
|
|
|
3153
3477
|
exports.RoleAssignmentForm = RoleAssignmentForm;
|
|
3154
3478
|
exports.RoleBadgeList = RoleBadgeList;
|
|
3155
3479
|
exports.SearchInput = SearchInput;
|
|
3480
|
+
exports.SegmentedControl = SegmentedControl;
|
|
3156
3481
|
exports.Select = Select;
|
|
3157
3482
|
exports.SortIndicator = SortIndicator;
|
|
3158
3483
|
exports.Stack = Stack;
|