@pine-ds/core 4.9.1 → 4.11.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.
Files changed (98) hide show
  1. package/components/pds-checkbox2.js +1 -1
  2. package/components/pds-chip2.js +1 -1
  3. package/components/pds-combobox.js +2 -2
  4. package/components/pds-datepicker.js +1 -1
  5. package/components/pds-dropdown-menu.js +1 -1
  6. package/components/truncation-tooltip.js +1 -1
  7. package/dist/cjs/loader.cjs.js +1 -1
  8. package/dist/cjs/pds-checkbox.cjs.entry.js +1 -1
  9. package/dist/cjs/pds-chip.cjs.entry.js +5 -3
  10. package/dist/cjs/pds-combobox.cjs.entry.js +101 -15
  11. package/dist/cjs/pds-copytext.cjs.entry.js +1 -1
  12. package/dist/cjs/pds-datepicker.cjs.entry.js +253 -75
  13. package/dist/cjs/pds-dropdown-menu.cjs.entry.js +58 -46
  14. package/dist/cjs/pds-table-cell.cjs.entry.js +1 -1
  15. package/dist/cjs/pds-text.cjs.entry.js +1 -1
  16. package/dist/cjs/pine-core.cjs.js +1 -1
  17. package/dist/cjs/{truncation-tooltip-DdB78RIe.js → truncation-tooltip-Bikp7n9I.js} +1 -1
  18. package/dist/collection/components/pds-checkbox/pds-checkbox.css +20 -14
  19. package/dist/collection/components/pds-chip/pds-chip.js +4 -2
  20. package/dist/collection/components/pds-combobox/grouped-options-panel.js +39 -1
  21. package/dist/collection/components/pds-combobox/pds-combobox.css +37 -1
  22. package/dist/collection/components/pds-combobox/pds-combobox.js +62 -14
  23. package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +48 -0
  24. package/dist/collection/components/pds-datepicker/date-utils.js +42 -0
  25. package/dist/collection/components/pds-datepicker/pds-datepicker.js +163 -64
  26. package/dist/collection/components/pds-datepicker/stories/pds-datepicker.stories.js +11 -0
  27. package/dist/collection/components/pds-datepicker/value.js +76 -18
  28. package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu.js +64 -46
  29. package/dist/collection/utils/truncation-tooltip.js +1 -1
  30. package/dist/docs.json +42 -7
  31. package/dist/esm/loader.js +1 -1
  32. package/dist/esm/pds-checkbox.entry.js +1 -1
  33. package/dist/esm/pds-chip.entry.js +5 -3
  34. package/dist/esm/pds-combobox.entry.js +101 -15
  35. package/dist/esm/pds-copytext.entry.js +1 -1
  36. package/dist/esm/pds-datepicker.entry.js +253 -75
  37. package/dist/esm/pds-dropdown-menu.entry.js +58 -46
  38. package/dist/esm/pds-table-cell.entry.js +1 -1
  39. package/dist/esm/pds-text.entry.js +1 -1
  40. package/dist/esm/pine-core.js +1 -1
  41. package/dist/esm/{truncation-tooltip-CRSdYzpA.js → truncation-tooltip-DBcPipr9.js} +1 -1
  42. package/dist/esm-es5/loader.js +1 -1
  43. package/dist/esm-es5/pds-checkbox.entry.js +1 -1
  44. package/dist/esm-es5/pds-chip.entry.js +1 -1
  45. package/dist/esm-es5/pds-combobox.entry.js +1 -1
  46. package/dist/esm-es5/pds-copytext.entry.js +1 -1
  47. package/dist/esm-es5/pds-datepicker.entry.js +1 -1
  48. package/dist/esm-es5/pds-dropdown-menu.entry.js +1 -1
  49. package/dist/esm-es5/pds-table-cell.entry.js +1 -1
  50. package/dist/esm-es5/pds-text.entry.js +1 -1
  51. package/dist/esm-es5/pine-core.js +1 -1
  52. package/dist/esm-es5/truncation-tooltip-DBcPipr9.js +1 -0
  53. package/dist/pine-core/p-0b7b3fc2.system.entry.js +3 -0
  54. package/dist/pine-core/{p-9a776443.entry.js → p-124a0a24.entry.js} +1 -1
  55. package/dist/pine-core/p-128c04e7.entry.js +1 -0
  56. package/dist/pine-core/p-3359a935.entry.js +1 -0
  57. package/dist/pine-core/{p-3d34b967.system.entry.js → p-3a1dc752.system.entry.js} +1 -1
  58. package/dist/pine-core/{p-20079552.system.entry.js → p-639acef8.system.entry.js} +1 -1
  59. package/dist/pine-core/{p-beb7a430.entry.js → p-97356d4b.entry.js} +1 -1
  60. package/dist/pine-core/p-BZVUD2on.js +1 -0
  61. package/dist/pine-core/p-Dj-z_ugg.system.js +1 -0
  62. package/dist/pine-core/p-XK45dzWR.system.js +1 -1
  63. package/dist/pine-core/{p-4d1a8803.entry.js → p-afc2f165.entry.js} +1 -1
  64. package/dist/pine-core/p-c6a9e62c.system.entry.js +1 -0
  65. package/dist/pine-core/p-d4968f9d.system.entry.js +1 -0
  66. package/dist/pine-core/p-da8b864a.entry.js +2 -0
  67. package/dist/pine-core/p-dd7ded84.entry.js +1 -0
  68. package/dist/pine-core/p-de455073.system.entry.js +1 -0
  69. package/dist/pine-core/{p-1cc39908.system.entry.js → p-eb56af4a.system.entry.js} +1 -1
  70. package/dist/pine-core/p-f354dbac.entry.js +1 -0
  71. package/dist/pine-core/{p-93fd7dd8.system.entry.js → p-f834cb07.system.entry.js} +1 -1
  72. package/dist/pine-core/pine-core.esm.js +1 -1
  73. package/dist/types/components/pds-combobox/combobox-interface.d.ts +26 -0
  74. package/dist/types/components/pds-combobox/grouped-options-panel.d.ts +17 -0
  75. package/dist/types/components/pds-combobox/pds-combobox.d.ts +12 -2
  76. package/dist/types/components/pds-datepicker/date-utils.d.ts +19 -0
  77. package/dist/types/components/pds-datepicker/datepicker-interface.d.ts +2 -1
  78. package/dist/types/components/pds-datepicker/pds-datepicker.d.ts +30 -5
  79. package/dist/types/components/pds-datepicker/value.d.ts +14 -2
  80. package/dist/types/components/pds-dropdown-menu/pds-dropdown-menu.d.ts +4 -0
  81. package/dist/types/components.d.ts +25 -10
  82. package/dist/types/utils/truncation-tooltip.d.ts +1 -1
  83. package/dist/vscode.html-data.json +11 -4
  84. package/hydrate/index.js +422 -142
  85. package/hydrate/index.mjs +422 -142
  86. package/package.json +2 -2
  87. package/dist/esm-es5/truncation-tooltip-CRSdYzpA.js +0 -1
  88. package/dist/pine-core/p-37718637.entry.js +0 -1
  89. package/dist/pine-core/p-383d84f4.entry.js +0 -2
  90. package/dist/pine-core/p-3e30a761.entry.js +0 -1
  91. package/dist/pine-core/p-54f1e86f.system.entry.js +0 -1
  92. package/dist/pine-core/p-73efb1ed.system.entry.js +0 -1
  93. package/dist/pine-core/p-8436cbfb.entry.js +0 -1
  94. package/dist/pine-core/p-DQyF6v9Q.system.js +0 -1
  95. package/dist/pine-core/p-DwQ5Tjis.js +0 -1
  96. package/dist/pine-core/p-b3d4f97a.system.entry.js +0 -3
  97. package/dist/pine-core/p-d47b2a6a.system.entry.js +0 -1
  98. package/dist/pine-core/p-f018af99.entry.js +0 -1
@@ -4,7 +4,7 @@ import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/d
4
4
  import { cancelDebouncedEvent, debounceEvent, flushDebouncedEvent } from "../../utils/utils";
5
5
  import { closest } from "../../utils/closest";
6
6
  import DOMPurify from "dompurify";
7
- import { FlyoutController, buildPanelEntries, findGroup, groupIconOf, groupLabelOf, isGroupDisabled, isOptionDisabled, optionIconOf, panelColumn, parentOptgroupOf, } from "./grouped-options-panel";
7
+ import { FlyoutController, buildPanelEntries, findGroup, groupBadgeOf, groupIconOf, groupLabelOf, isGroupDisabled, isOptionDisabled, optionBadgeOf, optionIconOf, panelColumn, parentOptgroupOf, } from "./grouped-options-panel";
8
8
  /**
9
9
  * @slot option - Option elements for the combobox dropdown
10
10
  * @slot trigger-content - Custom content for the button trigger when customTriggerContent is true
@@ -1133,6 +1133,7 @@ export class PdsCombobox {
1133
1133
  // Only strings group an option or name an icon; anything else from JS is ignored, not coerced.
1134
1134
  if (typeof opt.icon === 'string' && opt.icon !== '')
1135
1135
  optionEl.setAttribute('data-icon', opt.icon);
1136
+ this.setBadgeAttributes(optionEl, opt.badge);
1136
1137
  if (typeof opt.group !== 'string' || opt.group === '') {
1137
1138
  ungrouped.push(optionEl);
1138
1139
  this.panelPositions.set(optionEl, position);
@@ -1149,6 +1150,8 @@ export class PdsCombobox {
1149
1150
  if (!optgroup.hasAttribute('data-icon') && typeof opt.groupIcon === 'string' && opt.groupIcon !== '') {
1150
1151
  optgroup.setAttribute('data-icon', opt.groupIcon);
1151
1152
  }
1153
+ if (!optgroup.hasAttribute('data-badge'))
1154
+ this.setBadgeAttributes(optgroup, opt.groupBadge);
1152
1155
  optgroup.appendChild(optionEl);
1153
1156
  });
1154
1157
  [...ungrouped, ...groupEntries].forEach(entry => {
@@ -1638,7 +1641,10 @@ export class PdsCombobox {
1638
1641
  };
1639
1642
  const reveal = () => {
1640
1643
  e.preventDefault();
1641
- this.revealedGroupLabel = current.label;
1644
+ // The row's name, badge and all, as the row itself is named.
1645
+ const optgroup = current;
1646
+ const badge = groupBadgeOf(optgroup);
1647
+ this.revealedGroupLabel = badge === null ? optgroup.label : `${optgroup.label}, ${badge.label}`;
1642
1648
  return true;
1643
1649
  };
1644
1650
  switch (e.key) {
@@ -2187,11 +2193,16 @@ export class PdsCombobox {
2187
2193
  return;
2188
2194
  const { optgroup, key, options, members } = group;
2189
2195
  const groupIcon = groupIconOf(optgroup);
2196
+ const badge = groupBadgeOf(optgroup);
2190
2197
  // As in the grouped displays: disabled when there's nothing left in it to choose.
2191
2198
  const disabled = members.length > 0 && members.every(member => isOptionDisabled(member));
2192
- rendered.push(h("li", { key: key, class: { 'pds-combobox__group': true, 'pds-combobox__group--disabled': disabled }, role: "group", "aria-label": optgroup.label, "aria-disabled": disabled ? 'true' : undefined }, h("div", { class: { 'pds-combobox__group-label': true, 'pds-combobox__group-label--with-icon': groupIcon !== null }, "aria-hidden": "true" }, groupIcon !== null && (
2199
+ rendered.push(h("li", { key: key, class: { 'pds-combobox__group': true, 'pds-combobox__group--disabled': disabled }, role: "group", "aria-label": badge === null ? optgroup.label : `${optgroup.label}, ${badge.label}`, "aria-disabled": disabled ? 'true' : undefined }, h("div", { class: {
2200
+ 'pds-combobox__group-label': true,
2201
+ 'pds-combobox__group-label--with-icon': groupIcon !== null,
2202
+ 'pds-combobox__group-label--with-badge': badge !== null,
2203
+ }, "aria-hidden": "true" }, groupIcon !== null && (
2193
2204
  // `name`, not `icon`: `icon` also loads URLs and data: SVGs, unsanitized.
2194
- h("pds-icon", { name: groupIcon, size: "small", "aria-hidden": "true", class: "pds-combobox__group-icon" })), optgroup.label), h("ul", { class: "pds-combobox__group-list", role: "presentation" }, options)));
2205
+ h("pds-icon", { name: groupIcon, size: "small", "aria-hidden": "true", class: "pds-combobox__group-icon" })), badge === null ? optgroup.label : h("span", { class: "pds-combobox__group-label-text" }, optgroup.label), this.renderBadge(badge, 'pds-combobox__group-badge')), h("ul", { class: "pds-combobox__group-list", role: "presentation" }, options)));
2195
2206
  group = null;
2196
2207
  };
2197
2208
  this.filteredItems.forEach((item, itemIdx) => {
@@ -2317,20 +2328,56 @@ export class PdsCombobox {
2317
2328
  const isChip = this.isOptionChip(option);
2318
2329
  const isDisabled = isOptionDisabled(option);
2319
2330
  const tree = level !== undefined;
2320
- return (h("li", { key: option.value, id: this.optionDomId(index), role: tree ? 'treeitem' : 'option', "aria-selected": isSelected ? 'true' : tree ? undefined : 'false', "aria-disabled": isDisabled ? 'true' : undefined, "aria-level": level, "aria-setsize": setsize, "aria-posinset": posinset, "aria-label": isLayout || isChip ? option.getAttribute('aria-label') || this.getOptionLabel(option) : undefined, tabindex: this.usesBodyPortal || this.usesPanelSearch || tree ? '-1' : isHighlighted ? '0' : '-1', class: {
2331
+ const badge = isLayout || isChip ? null : optionBadgeOf(option);
2332
+ return (h("li", { key: option.value, id: this.optionDomId(index), role: tree ? 'treeitem' : 'option', "aria-selected": isSelected ? 'true' : tree ? undefined : 'false', "aria-disabled": isDisabled ? 'true' : undefined, "aria-level": level, "aria-setsize": setsize, "aria-posinset": posinset, "aria-label": isLayout || isChip
2333
+ ? option.getAttribute('aria-label') || this.getOptionLabel(option)
2334
+ : badge && `${this.getOptionLabel(option)}, ${badge.label}`, tabindex: this.usesBodyPortal || this.usesPanelSearch || tree ? '-1' : isHighlighted ? '0' : '-1', class: {
2321
2335
  'pds-combobox__option': true,
2322
2336
  'pds-combobox__option--highlighted': isHighlighted,
2323
2337
  'pds-combobox__option--layout': isLayout,
2324
2338
  'pds-combobox__option--chip': isChip,
2325
2339
  'pds-combobox__option--disabled': isDisabled,
2326
- }, "data-option-index": index, onMouseDown: this.onOptionMouseDown, onClick: this.onOptionClick, onMouseEnter: this.onOptionMouseEnter, onKeyDown: this.onOptionKeyDown }, isLayout ? (h("pds-box", { class: "pds-combobox__option-layout-wrapper", innerHTML: this.getOptionLayoutContent(option) })) : isChip ? (this.renderOptionChip(option)) : (this.renderOptionLabel(option)), isSelected && h("pds-icon", { icon: "check", size: "regular", "aria-hidden": "true", class: "pds-combobox__option-check" })));
2340
+ 'pds-combobox__option--with-badge': badge !== null,
2341
+ }, "data-option-index": index, onMouseDown: this.onOptionMouseDown, onClick: this.onOptionClick, onMouseEnter: this.onOptionMouseEnter, onKeyDown: this.onOptionKeyDown }, isLayout ? (h("pds-box", { class: "pds-combobox__option-layout-wrapper", innerHTML: this.getOptionLayoutContent(option) })) : isChip ? (this.renderOptionChip(option)) : ([this.renderOptionLabel(option), this.renderOptionBadge(option)]), isSelected && h("pds-icon", { icon: "check", size: "regular", "aria-hidden": "true", class: "pds-combobox__option-check" })));
2327
2342
  }
2328
- /** An option's text, after its `data-icon` when it has one. */
2343
+ /** Copies an `options` badge to an option's or optgroup's `data-badge*` attributes; only strings count. */
2344
+ setBadgeAttributes(element, badge) {
2345
+ if (typeof badge !== 'object' || badge === null)
2346
+ return;
2347
+ const { label, sentiment, icon } = badge;
2348
+ if (typeof label !== 'string' || label.trim() === '')
2349
+ return;
2350
+ element.setAttribute('data-badge', label);
2351
+ if (typeof sentiment === 'string')
2352
+ element.setAttribute('data-badge-sentiment', sentiment);
2353
+ if (typeof icon === 'string')
2354
+ element.setAttribute('data-badge-icon', icon);
2355
+ }
2356
+ /** An option's badge, after its text. */
2357
+ renderOptionBadge(option) {
2358
+ return this.renderBadge(optionBadgeOf(option), 'pds-combobox__option-badge');
2359
+ }
2360
+ /** A badge as a `pds-chip`: after an option's text, or a group's label. */
2361
+ renderBadge(badge, className) {
2362
+ var _a;
2363
+ if (badge === null)
2364
+ return null;
2365
+ return (h("pds-chip", { size: "sm", sentiment: badge.sentiment,
2366
+ // A Pine icon name only, checked by optionBadgeOf; pds-chip's `icon` also loads URLs.
2367
+ icon: (_a = badge.icon) !== null && _a !== void 0 ? _a : undefined,
2368
+ // A long label, often a translated one, truncates rather than squeezing the option's text.
2369
+ maxWidth: "50%", class: className }, badge.label));
2370
+ }
2371
+ /**
2372
+ * An option's text, after its `data-icon` when it has one. Beside a badge the text is always
2373
+ * wrapped, so it can shrink and wrap rather than push the badge out of the row.
2374
+ */
2329
2375
  renderOptionLabel(option) {
2330
2376
  const icon = optionIconOf(option);
2331
2377
  const label = this.truncate ? (h("span", { key: this.getOptionLabel(option), class: "pds-combobox__option-text" }, this.getOptionLabel(option))) : this.getOptionLabel(option);
2332
- if (icon === null)
2333
- return label;
2378
+ if (icon === null) {
2379
+ return optionBadgeOf(option) === null ? label : h("span", { class: "pds-combobox__option-label" }, label);
2380
+ }
2334
2381
  return (h("span", { class: "pds-combobox__option-label" }, h("pds-icon", { name: icon, size: "regular", "aria-hidden": "true", class: "pds-combobox__option-icon" }), label));
2335
2382
  }
2336
2383
  /** The grouped panel's rows, in the order the panel draws them and the keyboard moves through. */
@@ -2369,7 +2416,8 @@ export class PdsCombobox {
2369
2416
  }
2370
2417
  /**
2371
2418
  * Drilled into a group: its treeitem, expanded. Its row reads as a way back ("Back: Tag") and
2372
- * leads its options.
2419
+ * leads its options. It leaves out the group's badge: the row is a way back, not a group to
2420
+ * choose, and the badge was on the row that opened it. An option's own badge still shows.
2373
2421
  */
2374
2422
  renderGroupBackRow(group, { posinset, setsize }) {
2375
2423
  const id = this.groupDomId(group.optgroup);
@@ -2398,9 +2446,9 @@ export class PdsCombobox {
2398
2446
  'pds-combobox__group-row--highlighted': isCurrent && (flyout || this.keyboardNavigating),
2399
2447
  'pds-combobox__group-row--keyboard': isCurrent && this.keyboardNavigating,
2400
2448
  'pds-combobox__group-row--disabled': disabled,
2401
- }, role: "treeitem", "aria-expanded": isOpen ? 'true' : 'false', "aria-disabled": disabled ? 'true' : undefined, "aria-level": 1, "aria-posinset": posinset, "aria-setsize": setsize, "aria-labelledby": `${id}-label`, "data-group-index": groupIndex, onMouseEnter: flyout ? this.onGroupRowMouseEnter : undefined, onMouseLeave: flyout ? this.onGroupRowMouseLeave : undefined }, h("div", { class: "pds-combobox__group-row-trigger", "data-group-index": groupIndex, onMouseDown: this.onOptionMouseDown, onMouseEnter: this.onGroupRowTriggerMouseEnter, onClick: this.onGroupRowClick }, h("span", { class: "pds-combobox__group-row-label", id: `${id}-label` }, group.icon !== null && (
2449
+ }, role: "treeitem", "aria-expanded": isOpen ? 'true' : 'false', "aria-disabled": disabled ? 'true' : undefined, "aria-level": 1, "aria-posinset": posinset, "aria-setsize": setsize, "aria-labelledby": group.badge === null ? `${id}-label` : undefined, "aria-label": group.badge === null ? undefined : `${group.label}, ${group.badge.label}`, "data-group-index": groupIndex, onMouseEnter: flyout ? this.onGroupRowMouseEnter : undefined, onMouseLeave: flyout ? this.onGroupRowMouseLeave : undefined }, h("div", { class: "pds-combobox__group-row-trigger", "data-group-index": groupIndex, onMouseDown: this.onOptionMouseDown, onMouseEnter: this.onGroupRowTriggerMouseEnter, onClick: this.onGroupRowClick }, h("span", { class: "pds-combobox__group-row-label", id: `${id}-label` }, group.icon !== null && (
2402
2450
  // `name`, not `icon`: `icon` also loads URLs and data: SVGs, unsanitized.
2403
- h("pds-icon", { name: group.icon, size: "regular", "aria-hidden": "true", class: "pds-combobox__group-icon" })), group.label), h("pds-icon", { name: "caret-right", size: "regular", "aria-hidden": "true", class: "pds-combobox__group-row-caret" })), isOpen && (h("ul", { class: "pds-combobox__flyout", role: "group", "aria-label": group.label, style: { minWidth: this.flyoutMinWidth, maxWidth: this.flyoutMaxWidth } }, this.renderGroupOptions(group)))));
2451
+ h("pds-icon", { name: group.icon, size: "regular", "aria-hidden": "true", class: "pds-combobox__group-icon" })), group.label), this.renderBadge(group.badge, 'pds-combobox__group-badge'), h("pds-icon", { name: "caret-right", size: "regular", "aria-hidden": "true", class: "pds-combobox__group-row-caret" })), isOpen && (h("ul", { class: "pds-combobox__flyout", role: "group", "aria-label": group.label, style: { minWidth: this.flyoutMinWidth, maxWidth: this.flyoutMaxWidth } }, this.renderGroupOptions(group)))));
2404
2452
  }
2405
2453
  // Helper method to render the caret icon
2406
2454
  renderCaretIcon() {
@@ -2557,13 +2605,13 @@ export class PdsCombobox {
2557
2605
  ]
2558
2606
  .filter(Boolean)
2559
2607
  .join(' ');
2560
- return (h(Host, { key: '2786062989bc490e87ea382b5c80f7e2758c1a8a', "aria-disabled": this.disabled ? 'true' : null }, h("div", { key: 'b3b272a99528d725a52d3092a3650efacaf54b34', class: "pds-combobox", tabIndex: -1, onFocusout: this.onComboboxFocusOut, part: "combobox" }, this.hasVisibleLabel && (h("label", { key: '8e2e0021e1f60cddfe41b918406c7d76659fbff1', htmlFor: this.trigger === 'input' ? this.componentId : undefined, id: this.labelId, class: "pds-combobox__label", onClick: this.onLabelClick }, this.label)), this.trigger === 'input' ? (h("div", { class: "pds-combobox__input-wrapper", style: { width: this.triggerWidth } }, h("input", { ref: el => {
2608
+ return (h(Host, { key: '104eb1c4cdca4494b10dea660e27a98b0c18bf64', "aria-disabled": this.disabled ? 'true' : null }, h("div", { key: '2548ff2b304bb6a5a249b5059a0b60b55cdf2152', class: "pds-combobox", tabIndex: -1, onFocusout: this.onComboboxFocusOut, part: "combobox" }, this.hasVisibleLabel && (h("label", { key: '7a261c72452ca7a278e1067e41c6eed52f29edc8', htmlFor: this.trigger === 'input' ? this.componentId : undefined, id: this.labelId, class: "pds-combobox__label", onClick: this.onLabelClick }, this.label)), this.trigger === 'input' ? (h("div", { class: "pds-combobox__input-wrapper", style: { width: this.triggerWidth } }, h("input", { ref: el => {
2561
2609
  this.inputEl = el;
2562
2610
  this.triggerEl = el;
2563
2611
  }, class: {
2564
2612
  'pds-combobox__input': true,
2565
2613
  'pds-combobox__input--invalid': this.isInvalid(),
2566
- }, type: "text", role: "combobox", "aria-autocomplete": "list", "aria-haspopup": this.inputPopupRole === 'tree' ? 'tree' : undefined, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, value: this.displayText, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onClick: this.handleInputClick, onKeyDown: this.handleKeyDown, autocomplete: "off", part: "input" }), h("pds-icon", { icon: "enlarge", "aria-hidden": "true", class: "pds-combobox__input-icon" }))) : this.trigger === 'chip' ? (h("div", { class: this.getChipTriggerClass(), style: { width: this.triggerWidth }, role: this.usesPanelSearch ? 'button' : 'combobox', "aria-haspopup": this.triggerPopupRole, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.triggerAriaLabel, "aria-labelledby": this.triggerLabelledBy, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "chip-trigger" }, this.renderChipTriggerContent())) : (h("div", { class: triggerClass, style: { width: this.triggerWidth }, role: this.usesPanelSearch ? 'button' : 'combobox', "aria-haspopup": this.triggerPopupRole, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.triggerAriaLabel, "aria-labelledby": this.triggerLabelledBy, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, onMouseEnter: this.truncate ? this.onButtonTriggerMouseEnter : undefined, ref: el => (this.triggerEl = el), part: "button-trigger" }, this.renderButtonTriggerContent())), this.renderMessages(), h("div", { key: '2216779564fa3f4efd1b4d0972ac045fa0851078', style: { display: 'none' } }, h("slot", { key: '77b344d7301193f435949cbe2be2b28e1d7f6602', onSlotchange: () => this.updateOptions() })), this.renderDropdown(), this.groupDisplay !== 'inline' && [
2614
+ }, type: "text", role: "combobox", "aria-autocomplete": "list", "aria-haspopup": this.inputPopupRole === 'tree' ? 'tree' : undefined, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, value: this.displayText, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onClick: this.handleInputClick, onKeyDown: this.handleKeyDown, autocomplete: "off", part: "input" }), h("pds-icon", { icon: "enlarge", "aria-hidden": "true", class: "pds-combobox__input-icon" }))) : this.trigger === 'chip' ? (h("div", { class: this.getChipTriggerClass(), style: { width: this.triggerWidth }, role: this.usesPanelSearch ? 'button' : 'combobox', "aria-haspopup": this.triggerPopupRole, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.triggerAriaLabel, "aria-labelledby": this.triggerLabelledBy, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "chip-trigger" }, this.renderChipTriggerContent())) : (h("div", { class: triggerClass, style: { width: this.triggerWidth }, role: this.usesPanelSearch ? 'button' : 'combobox', "aria-haspopup": this.triggerPopupRole, "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.usesPanelSearch ? undefined : this.activeDescendantId, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.triggerAriaLabel, "aria-labelledby": this.triggerLabelledBy, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, onMouseEnter: this.truncate ? this.onButtonTriggerMouseEnter : undefined, ref: el => (this.triggerEl = el), part: "button-trigger" }, this.renderButtonTriggerContent())), this.renderMessages(), h("div", { key: 'ffd3bc72b34a7278f3fa706a236017a26b86ea3f', style: { display: 'none' } }, h("slot", { key: 'f47a81191ad7fd8c6bf63cf8ef39934cbf7fd28c', onSlotchange: () => this.updateOptions() })), this.renderDropdown(), this.groupDisplay !== 'inline' && [
2567
2615
  h("span", { key: "drill-status", class: "visually-hidden", role: "status", "aria-live": "polite" }, this.isOpen && this.effectiveGroupDisplay === 'drill-in' && this.openGroup !== null ? this.openGroup.label : ''),
2568
2616
  // Its own region: sharing one, revealing a row then opening it would leave the text the
2569
2617
  // same, and opening the group would go unannounced.
@@ -598,6 +598,7 @@ OptionIconsWithGroups.args = {
598
598
  { id: 'email-activity', text: 'Email activity', group: 'Email' },
599
599
  { id: 'marketing-status', text: 'Email marketing status', group: 'Email' },
600
600
  { id: 'sequence', text: 'Sequence', group: 'Email' },
601
+ { id: 'responsive', text: 'Responsive marketing emails', group: 'Email', badge: { label: 'New', sentiment: 'accent', icon: 'star' } },
601
602
  { id: 'event', text: 'Event', icon: 'calendar-date' },
602
603
  { id: 'custom-fields', text: 'Custom fields', group: 'Fields', groupIcon: 'columns' },
603
604
  { id: 'default-fields', text: 'Default fields', group: 'Fields' },
@@ -748,6 +749,53 @@ DisabledOptions.argTypes = {
748
749
  groupDisplay: { control: 'inline-radio', options: ['inline', 'flyout', 'drill-in'] },
749
750
  };
750
751
 
752
+ // A plan-gated group: listed, disabled, with a Premium badge on its row, before the chevron.
753
+ // The badge comes from the first option in the group that sets groupBadge.
754
+ export const GroupBadges = (args) => html`
755
+ <div style="min-height: 320px;">
756
+ <pds-combobox
757
+ component-id="combobox-group-badges"
758
+ label=${args.label}
759
+ placeholder=${args.placeholder}
760
+ trigger="button"
761
+ mode="select-only"
762
+ group-display=${args.groupDisplay}
763
+ drill-in-back-label="Back"
764
+ trigger-width="300px"
765
+ dropdown-width="300px"
766
+ .options=${args.options}
767
+ ></pds-combobox>
768
+ </div>`;
769
+
770
+ GroupBadges.args = {
771
+ label: 'Action',
772
+ placeholder: 'Choose an action',
773
+ groupDisplay: 'drill-in',
774
+ options: [
775
+ { id: 'email_send', text: 'Send an email', group: 'Email', groupIcon: 'mail' },
776
+ { id: 'contact_tag_add', text: 'Add a tag', group: 'Contact', groupIcon: 'contact' },
777
+ {
778
+ id: 'facebook_message_send',
779
+ text: 'Send a Facebook message',
780
+ group: 'Facebook',
781
+ groupBadge: { label: 'Premium', sentiment: 'brand', icon: 'premium' },
782
+ disabled: true,
783
+ },
784
+ { id: 'facebook_comment_reply', text: 'Reply to a comment', group: 'Facebook', disabled: true },
785
+ {
786
+ id: 'instagram_message_send',
787
+ text: 'Send an Instagram message',
788
+ group: 'Instagram',
789
+ groupBadge: { label: 'Premium', sentiment: 'brand', icon: 'premium' },
790
+ disabled: true,
791
+ },
792
+ ],
793
+ };
794
+
795
+ GroupBadges.argTypes = {
796
+ groupDisplay: { control: 'inline-radio', options: ['inline', 'flyout', 'drill-in'] },
797
+ };
798
+
751
799
  export const OptionGroupsPdsText = (args) => html`
752
800
  <pds-combobox
753
801
  component-id="combobox-pds-text-groups"
@@ -130,3 +130,45 @@ export function dateFormatter(locale, options) {
130
130
  export function formatDate(date, locale, options) {
131
131
  return dateFormatter(locale, options).format(date);
132
132
  }
133
+ /**
134
+ * Month-day mode's year. A leap year, so February has its 29th; every date the mode holds is in it,
135
+ * and the value drops it (`--MM-DD`).
136
+ */
137
+ export const MONTH_DAY_YEAR = 2000;
138
+ const MONTH_DAY = /^--(\d{2})-(\d{2})$/;
139
+ /** The same month and day in month-day mode's year. */
140
+ export function inMonthDayYear(date) {
141
+ return new Date(Date.UTC(MONTH_DAY_YEAR, date.getUTCMonth(), date.getUTCDate()));
142
+ }
143
+ /**
144
+ * Parses a month and day, `--MM-DD` (ISO 8601 without a year), to a date in month-day mode's year.
145
+ * A full `YYYY-MM-DD` reads as its month and day. `null` when it isn't a real month and day.
146
+ */
147
+ export function parseMonthDay(value) {
148
+ const text = (value !== null && value !== void 0 ? value : '').trim();
149
+ const match = MONTH_DAY.exec(text);
150
+ if (match)
151
+ return parseIsoDate(`${MONTH_DAY_YEAR}-${match[1]}-${match[2]}`);
152
+ const date = parseIsoDate(text);
153
+ return date && inMonthDayYear(date);
154
+ }
155
+ /** A date's month and day as `--MM-DD`. */
156
+ export function toMonthDay(date) {
157
+ return `-${toIsoDate(date).slice(4)}`;
158
+ }
159
+ /**
160
+ * A month's days for month-day mode, which has no weekdays: rows of seven from the 1st, with the
161
+ * last row filled out with `null`.
162
+ */
163
+ export function monthDayRows(month) {
164
+ const cells = [];
165
+ for (let day = 1; day <= daysInMonth(MONTH_DAY_YEAR, month); day++) {
166
+ cells.push(new Date(Date.UTC(MONTH_DAY_YEAR, month, day)));
167
+ }
168
+ while (cells.length % 7 !== 0)
169
+ cells.push(null);
170
+ const rows = [];
171
+ for (let i = 0; i < cells.length; i += 7)
172
+ rows.push(cells.slice(i, i + 7));
173
+ return rows;
174
+ }