@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 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.0/LICENSE) and [NOTICE](https://cdn.jsdelivr.net/npm/@oods/components-vue@0.4.0/NOTICE). Generated output belongs to its user; installed package dependencies retain their own licenses.
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) dialog.showModal();
215
- else if (!props.open && dialog.open) dialog.close();
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
- // The browser closed it (a close request it would not let be cancelled) while open is still true. A close event
243
- // from moving a mounted dialog into the top layer finds it open again, and is not reported.
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 (props.open && !element.value?.open) emit("close");
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
- return vue.h("div", { class: "oods-ownership-meta", "data-oods-component": "OwnershipMeta", "data-meta-type": "ownership" }, content.length ? content : [
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
- overflowItems.value.length > 0 ? vue.h("div", { class: "oods-tabs-overflow" }, [
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 ? "page" : void 0,
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
- return vue.h("article", { id: props.id, class: "oods-trait-recipe", "data-oods-component": id, "aria-label": props.title }, [vue.h("h2", props.title), rows.length ? 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}`)]))) : vue.h("p", "No events recorded.")]);
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;