@keenmate/web-multiselect 2.0.0-rc09 → 2.0.0-rc11

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.
@@ -250,15 +250,15 @@
250
250
  "text": "MultiSelectConfig<T>"
251
251
  },
252
252
  "privacy": "private",
253
- "default": "{ // String options searchHint: element.dataset.searchHint || '', searchPlaceholder: element.dataset.searchPlaceholder || undefined, selectPlaceholder: element.dataset.selectPlaceholder || 'Pick an option...', noDataPlaceholder: element.dataset.noDataPlaceholder || undefined, dropdownMinWidth: element.dataset.dropdownMinWidth || undefined, dropdownMaxWidth: element.dataset.dropdownMaxWidth || undefined, badgesDisplayMode: (element.dataset.badgesDisplayMode as any) || 'badges', badgesPosition: (element.dataset.badgesPosition as BadgesPosition) || 'bottom', badgesThresholdMode: (element.dataset.badgesThresholdMode as any) || 'count', maxHeight: element.dataset.maxHeight || '20rem', emptyMessage: element.dataset.emptyMessage || 'No results found', loadingMessage: element.dataset.loadingMessage || 'Loading...', searchInputMode: (element.dataset.searchInputMode as SearchInputMode) || 'normal', searchMode: (element.dataset.searchMode as SearchMode) || 'filter', // Number options badgesThreshold: element.dataset.badgesThreshold ? parseInt(element.dataset.badgesThreshold) : undefined, minSearchLength: parseInt(element.dataset.minSearchLength || '0') || 0, searchDebounce: parseInt(element.dataset.searchDebounce || '0') || 0, // Boolean options (internal names with 'is' prefix) isMultipleEnabled: element.dataset.multiple !== 'false', isGroupsAllowed: element.dataset.allowGroups !== 'false', isCheckboxesShown: element.dataset.showCheckboxes !== 'false', isActionsSticky: element.dataset.stickyActions !== 'false', isCloseOnSelect: element.dataset.closeOnSelect === 'true', isPlacementLocked: element.dataset.lockPlacement !== 'false', isSearchEnabled: element.dataset.enableSearch !== 'false', isAddNewAllowed: element.dataset.allowAddNew === 'true', isCounterShown: element.dataset.showCounter === 'true', isSearchModeToggleShown: element.dataset.showSearchModeToggle === 'true', isKeepOptionsOnSearch: element.dataset.keepOptionsOnSearch !== 'false', shouldKeepSearchOnClose: element.dataset.keepSearchOnClose !== 'false', // Data and callbacks options: [], container: undefined, // Override with provided options ...options }"
253
+ "default": "{ // String options searchHint: element.dataset.searchHint || '', searchPlaceholder: element.dataset.searchPlaceholder || undefined, selectPlaceholder: element.dataset.selectPlaceholder || 'Pick an option...', noDataPlaceholder: element.dataset.noDataPlaceholder || undefined, dropdownMinWidth: element.dataset.dropdownMinWidth || undefined, dropdownMaxWidth: element.dataset.dropdownMaxWidth || undefined, badgesDisplayMode: (element.dataset.badgesDisplayMode as any) || 'badges', badgesPosition: (element.dataset.badgesPosition as BadgesPosition) || 'bottom', badgesThresholdMode: (element.dataset.badgesThresholdMode as any) || 'count', maxHeight: element.dataset.maxHeight || '20rem', emptyMessage: element.dataset.emptyMessage || 'No results found', loadingMessage: element.dataset.loadingMessage || 'Loading...', searchInputMode: (element.dataset.searchInputMode as SearchInputMode) || 'normal', searchMode: (element.dataset.searchMode as SearchMode) || 'filter', // Number options badgesThreshold: element.dataset.badgesThreshold ? parseInt(element.dataset.badgesThreshold) : undefined, minSearchLength: parseInt(element.dataset.minSearchLength || '0') || 0, searchDebounce: parseInt(element.dataset.searchDebounce || '0') || 0, // Boolean options (internal names with 'is' prefix) isMultipleEnabled: element.dataset.multiple !== 'false', isGroupsAllowed: element.dataset.allowGroups !== 'false', isCheckboxesShown: element.dataset.showCheckboxes !== 'false', isActionsSticky: element.dataset.stickyActions !== 'false', isCloseOnSelect: element.dataset.closeOnSelect === 'true', isPlacementLocked: element.dataset.lockPlacement !== 'false', isSearchEnabled: element.dataset.enableSearch !== 'false', isAddNewAllowed: element.dataset.allowAddNew === 'true', isCounterShown: element.dataset.showCounter === 'true', isSelectedPopoverEnabled: element.dataset.enableSelectedPopover !== 'false', isSearchModeToggleShown: element.dataset.showSearchModeToggle === 'true', isKeepOptionsOnSearch: element.dataset.keepOptionsOnSearch !== 'false', shouldKeepSearchOnClose: element.dataset.keepSearchOnClose !== 'false', // Data and callbacks options: [], container: undefined, // Override with provided options ...options }"
254
254
  },
255
255
  {
256
256
  "kind": "field",
257
- "name": "isOpen",
257
+ "name": "#isOpen",
258
+ "privacy": "private",
258
259
  "type": {
259
260
  "text": "boolean"
260
261
  },
261
- "privacy": "private",
262
262
  "default": "false"
263
263
  },
264
264
  {
@@ -469,6 +469,15 @@
469
469
  "privacy": "private",
470
470
  "default": "false"
471
471
  },
472
+ {
473
+ "kind": "field",
474
+ "name": "fullscreenContainingBlockWarned",
475
+ "type": {
476
+ "text": "boolean"
477
+ },
478
+ "privacy": "private",
479
+ "default": "false"
480
+ },
472
481
  {
473
482
  "kind": "field",
474
483
  "name": "presentationMode",
@@ -729,6 +738,14 @@
729
738
  },
730
739
  "privacy": "private"
731
740
  },
741
+ {
742
+ "kind": "field",
743
+ "name": "inputWrapper",
744
+ "type": {
745
+ "text": "HTMLDivElement"
746
+ },
747
+ "privacy": "private"
748
+ },
732
749
  {
733
750
  "kind": "field",
734
751
  "name": "dropdown",
@@ -761,6 +778,14 @@
761
778
  },
762
779
  "privacy": "private"
763
780
  },
781
+ {
782
+ "kind": "field",
783
+ "name": "clearButton",
784
+ "type": {
785
+ "text": "HTMLButtonElement"
786
+ },
787
+ "privacy": "private"
788
+ },
764
789
  {
765
790
  "kind": "field",
766
791
  "name": "hint",
@@ -814,11 +839,11 @@
814
839
  {
815
840
  "name": "opts",
816
841
  "type": {
817
- "text": "{\r\n member?: string;\r\n callback?: (item: T) => R;\r\n tupleIndex?: 0 | 1;\r\n tupleSkip?: boolean;\r\n transform?: (raw: any) => R;\r\n fallback: R | (() => R);\r\n }"
842
+ "text": "{\n member?: string;\n callback?: (item: T) => R;\n tupleIndex?: 0 | 1;\n tupleSkip?: boolean;\n transform?: (raw: any) => R;\n fallback: R | (() => R);\n }"
818
843
  }
819
844
  }
820
845
  ],
821
- "description": "Generic field extractor with the precedence:\r\n tuple short-circuit -> member property -> callback -> fallback\r\n\r\nTuple handling:\r\n - `tupleIndex` (0 | 1): for `[key, value]` items, return that slot.\r\n - `tupleSkip: true`: for any tuple, skip directly to fallback (used for icon/subtitle/group/disabled —\r\n fields that don't make sense on a 2-element array).\r\n - neither: tuples flow through the member/callback/fallback chain as if they were objects.\r\n\r\n`transform` is applied to tuple-slot and member-property reads (not to callback returns or the fallback),\r\nso e.g. you can pass `String` to coerce numeric members to strings while letting a typed callback return its\r\nown type unchanged."
846
+ "description": "Generic field extractor with the precedence:\n tuple short-circuit -> member property -> callback -> fallback\n\nTuple handling:\n - `tupleIndex` (0 | 1): for `[key, value]` items, return that slot.\n - `tupleSkip: true`: for any tuple, skip directly to fallback (used for icon/subtitle/group/disabled —\n fields that don't make sense on a 2-element array).\n - neither: tuples flow through the member/callback/fallback chain as if they were objects.\n\n`transform` is applied to tuple-slot and member-property reads (not to callback returns or the fallback),\nso e.g. you can pass `String` to coerce numeric members to strings while letting a typed callback return its\nown type unchanged."
822
847
  },
823
848
  {
824
849
  "kind": "method",
@@ -873,7 +898,7 @@
873
898
  }
874
899
  }
875
900
  ],
876
- "description": "Badge display falls back to the regular display value rather than '[N/A]', so consumers can override badge\r\ntext independently. Doesn't fit the extractField shape (no tuple/member layer of its own)."
901
+ "description": "Badge display falls back to the regular display value rather than '[N/A]', so consumers can override badge\ntext independently. Doesn't fit the extractField shape (no tuple/member layer of its own)."
877
902
  },
878
903
  {
879
904
  "kind": "method",
@@ -892,7 +917,7 @@
892
917
  }
893
918
  }
894
919
  ],
895
- "description": "Full title — a fully-qualified label supplied with the data (never computed here). Used by\r\nbadges when `isBadgeFullTitleShown` is on. Returns undefined when the option has none."
920
+ "description": "Full title — a fully-qualified label supplied with the data (never computed here). Used by\nbadges when `isBadgeFullTitleShown` is on. Returns undefined when the option has none."
896
921
  },
897
922
  {
898
923
  "kind": "method",
@@ -1001,7 +1026,7 @@
1001
1026
  }
1002
1027
  }
1003
1028
  ],
1004
- "description": "Tree mode: whether the visible node at `index` may be selected. Non-selectable\r\nnodes (see `isSelectableMember`/`getIsSelectableCallback`) still render — just\r\nwithout a checkbox — but are skipped by focus and cannot be toggled. Always\r\ntrue outside tree mode. `treeNodes` is index-aligned with `filteredOptions`."
1029
+ "description": "Tree mode: whether the visible node at `index` may be selected. Non-selectable\nnodes (see `isSelectableMember`/`getIsSelectableCallback`) still render — just\nwithout a checkbox — but are skipped by focus and cannot be toggled. Always\ntrue outside tree mode. `treeNodes` is index-aligned with `filteredOptions`."
1005
1030
  },
1006
1031
  {
1007
1032
  "kind": "method",
@@ -1073,7 +1098,7 @@
1073
1098
  "text": "boolean"
1074
1099
  }
1075
1100
  },
1076
- "description": "Whether cascade checkbox mode is active: a multi-select tree with\r\n`checkbox-mode=\"cascade\"`. Checking a node then toggles its whole subtree\r\nand branches show a tristate box."
1101
+ "description": "Whether cascade checkbox mode is active: a multi-select tree with\n`checkbox-mode=\"cascade\"`. Checking a node then toggles its whole subtree\nand branches show a tristate box."
1077
1102
  },
1078
1103
  {
1079
1104
  "kind": "method",
@@ -1113,7 +1138,7 @@
1113
1138
  }
1114
1139
  }
1115
1140
  ],
1116
- "description": "Toggle a tree node in cascade mode: flip its whole subtree, re-project the\r\nchecked atoms to emitted values under the active policy, and commit the diff\r\nso badges / form / change events reflect the policy (rolled-up branches, etc.)."
1141
+ "description": "Toggle a tree node in cascade mode: flip its whole subtree, re-project the\nchecked atoms to emitted values under the active policy, and commit the diff\nso badges / form / change events reflect the policy (rolled-up branches, etc.)."
1117
1142
  },
1118
1143
  {
1119
1144
  "kind": "method",
@@ -1132,7 +1157,7 @@
1132
1157
  }
1133
1158
  }
1134
1159
  ],
1135
- "description": "Given a checked-atom set, project it to emitted values under the active\r\npolicy, diff it against the current selection, and commit. Shared by every\r\ncascade entry point (node toggle, Select All) so they all emit the same\r\npolicy-projected shape (e.g. a full subtree rolls up to one value)."
1160
+ "description": "Given a checked-atom set, project it to emitted values under the active\npolicy, diff it against the current selection, and commit. Shared by every\ncascade entry point (node toggle, Select All) so they all emit the same\npolicy-projected shape (e.g. a full subtree rolls up to one value)."
1136
1161
  },
1137
1162
  {
1138
1163
  "kind": "method",
@@ -1143,7 +1168,7 @@
1143
1168
  "text": "T[]"
1144
1169
  }
1145
1170
  },
1146
- "description": "The \"meaningful selection\" list used by the counter chip — the rolled-up\r\nminimal cover, regardless of the active emit policy. In cascade mode\r\n`leaves`/`all` emit many values for a single branch pick, which made the\r\ncounter read e.g. `[5]` for what a person experiences as two selections.\r\nThe counter should count the branches actually chosen, and stay stable when\r\nthe policy knob flips. Outside cascade this is just the selected options."
1171
+ "description": "The \"meaningful selection\" list used by the counter chip — the rolled-up\nminimal cover, regardless of the active emit policy. In cascade mode\n`leaves`/`all` emit many values for a single branch pick, which made the\ncounter read e.g. `[5]` for what a person experiences as two selections.\nThe counter should count the branches actually chosen, and stay stable when\nthe policy knob flips. Outside cascade this is just the selected options."
1147
1172
  },
1148
1173
  {
1149
1174
  "kind": "method",
@@ -1173,7 +1198,7 @@
1173
1198
  "text": "void"
1174
1199
  }
1175
1200
  },
1176
- "description": "Derive `treeNodes` + `filteredOptions` from the full tree, applying the\r\ncurrent search term. Matching nodes keep all their ancestors visible so\r\nindentation stays coherent (the tree is always fully expanded)."
1201
+ "description": "Derive `treeNodes` + `filteredOptions` from the full tree, applying the\ncurrent search term. Matching nodes keep all their ancestors visible so\nindentation stays coherent (the tree is always fully expanded)."
1177
1202
  },
1178
1203
  {
1179
1204
  "kind": "method",
@@ -1184,7 +1209,7 @@
1184
1209
  "text": "void"
1185
1210
  }
1186
1211
  },
1187
- "description": "Reset the visible list to \"everything\". **Tree-aware**: in tree mode it\r\nrebuilds `treeNodes` (kept index-aligned with `filteredOptions`) from the\r\nfull tree, so the two never drift. A raw `filteredOptions = [...allOptions]`\r\nwould leave `treeNodes` stale after clearing a search — the virtual list\r\nthen reserves height for every option but renders blank rows because\r\n`treeNodes[index]` is undefined. Always use this to clear the visible list."
1212
+ "description": "Reset the visible list to \"everything\". **Tree-aware**: in tree mode it\nrebuilds `treeNodes` (kept index-aligned with `filteredOptions`) from the\nfull tree, so the two never drift. A raw `filteredOptions = [...allOptions]`\nwould leave `treeNodes` stale after clearing a search — the virtual list\nthen reserves height for every option but renders blank rows because\n`treeNodes[index]` is undefined. Always use this to clear the visible list."
1188
1213
  },
1189
1214
  {
1190
1215
  "kind": "method",
@@ -1203,7 +1228,7 @@
1203
1228
  }
1204
1229
  }
1205
1230
  ],
1206
- "description": "Tree mode: derive the visible list from an **external** set of matched\r\noptions — e.g. the results returned by `searchCallback` — keeping each\r\nmatch's ancestors so indentation stays coherent. This is the async-search\r\nanalogue of `rebuildTreeVisible`: the matching is done by the caller (their\r\nown index/engine) instead of a local substring test, but ancestor\r\npreservation and `treeNodes`/`filteredOptions` index-alignment still happen\r\nhere. Pass all options to show the whole tree."
1231
+ "description": "Tree mode: derive the visible list from an **external** set of matched\noptions — e.g. the results returned by `searchCallback` — keeping each\nmatch's ancestors so indentation stays coherent. This is the async-search\nanalogue of `rebuildTreeVisible`: the matching is done by the caller (their\nown index/engine) instead of a local substring test, but ancestor\npreservation and `treeNodes`/`filteredOptions` index-alignment still happen\nhere. Pass all options to show the whole tree."
1207
1232
  },
1208
1233
  {
1209
1234
  "kind": "method",
@@ -1214,7 +1239,7 @@
1214
1239
  "text": "number"
1215
1240
  }
1216
1241
  },
1217
- "description": "Tree + `search-mode=\"navigate\"`: keep the ENTIRE tree visible (the tree is always\r\nfully expanded, so `flatNodes` is the whole thing) and record which visible rows\r\nmatch the term in `matchingIndices` — the flat-list navigate behavior, but over\r\n`treeNodes`. Filter mode collapses the hierarchy to matches + ancestors; navigate\r\nmode instead leaves the structure intact so the user can jump between matches\r\n(Ctrl+Arrow on desktop, the fullscreen navigator on touch). Returns the index of\r\nthe first match, or -1 (no term / no matches), so the caller can set focus."
1242
+ "description": "Tree + `search-mode=\"navigate\"`: keep the ENTIRE tree visible (the tree is always\nfully expanded, so `flatNodes` is the whole thing) and record which visible rows\nmatch the term in `matchingIndices` — the flat-list navigate behavior, but over\n`treeNodes`. Filter mode collapses the hierarchy to matches + ancestors; navigate\nmode instead leaves the structure intact so the user can jump between matches\n(Ctrl+Arrow on desktop, the fullscreen navigator on touch). Returns the index of\nthe first match, or -1 (no term / no matches), so the caller can set focus."
1218
1243
  },
1219
1244
  {
1220
1245
  "kind": "method",
@@ -1225,7 +1250,7 @@
1225
1250
  "text": "void"
1226
1251
  }
1227
1252
  },
1228
- "description": "(Re)compute `isRTL` from the host's `dir` (or an RTL ancestor) and derive the\r\ndirection-mirrored badges position. Pure state — callers apply the DOM effects\r\n(class toggle, panel `dir`, badge re-render). In Shadow DOM the `dir` lives on\r\nthe host element, not the shadow content, so we resolve the host first."
1253
+ "description": "(Re)compute `isRTL` from the host's `dir` (or an RTL ancestor) and derive the\ndirection-mirrored badges position. Pure state — callers apply the DOM effects\n(class toggle, panel `dir`, badge re-render). In Shadow DOM the `dir` lives on\nthe host element, not the shadow content, so we resolve the host first."
1229
1254
  },
1230
1255
  {
1231
1256
  "kind": "method",
@@ -1236,7 +1261,7 @@
1236
1261
  "text": "void"
1237
1262
  }
1238
1263
  },
1239
- "description": "Re-read `dir` and re-apply RTL mirroring live. The web-component calls this when\r\nits `dir` attribute changes at runtime (e.g. an app-wide language/direction\r\nswitch). Most layout follows the inherited CSS `direction` automatically (the\r\ncomponent is authored with logical properties); this fixes the parts pinned at\r\nbuild time — the `.ms--rtl` class (badges/count-display placement) and the\r\nexplicit `dir` on the shadow-root-appended panels (which don't sit under the\r\n`.ms--rtl` element, so they'd otherwise keep a stale build-time direction)."
1264
+ "description": "Re-read `dir` and re-apply RTL mirroring live. The web-component calls this when\nits `dir` attribute changes at runtime (e.g. an app-wide language/direction\nswitch). Most layout follows the inherited CSS `direction` automatically (the\ncomponent is authored with logical properties); this fixes the parts pinned at\nbuild time — the `.ms--rtl` class (badges/count-display placement) and the\nexplicit `dir` on the shadow-root-appended panels (which don't sit under the\n`.ms--rtl` element, so they'd otherwise keep a stale build-time direction)."
1240
1265
  },
1241
1266
  {
1242
1267
  "kind": "method",
@@ -1289,6 +1314,17 @@
1289
1314
  }
1290
1315
  ]
1291
1316
  },
1317
+ {
1318
+ "kind": "method",
1319
+ "name": "applyEdgeOptionRadii",
1320
+ "privacy": "private",
1321
+ "return": {
1322
+ "type": {
1323
+ "text": "void"
1324
+ }
1325
+ },
1326
+ "description": "Round the OUTER corners of the row at the very top and the row at the very\nbottom of the list so a focused/selected row's background — and crucially its\nfocus `outline`, which traces the row's OWN box and follows its border-radius\nbut NOT an ancestor's overflow clip — curves with the panel instead of poking a\nsquare corner past it.\n\nKeyed off DOM order, not option index, so grouping works: when grouped the top\nrow is a `.ms__group-label` (not the first option, which sits below it), so we\nround whichever element is physically first/last. VirtualScroll renders rows in\nindex order into one innerHTML, so DOM order == visual order there too.\n\nLogical corners (`border-start-*` / `border-end-*`) so it mirrors in RTL. A\nspace-taking vertical scrollbar occupies the inline-END gutter, so the END-side\ncorners stay square then (the panel's rounded end corner is the scrollbar\ntrack's). The radius is 0 in the fullscreen sheet (that scope zeroes the var)."
1327
+ },
1292
1328
  {
1293
1329
  "kind": "method",
1294
1330
  "name": "renderDropdownVirtual",
@@ -1317,7 +1353,7 @@
1317
1353
  }
1318
1354
  }
1319
1355
  ],
1320
- "description": "Default enabled/disabled state for the built-in actions, applied only when the consumer hasn't\r\nset an explicit `isDisabled` / `getIsDisabledCallback`:\r\n- `select-all` is disabled when it would add nothing (every selectable, non-disabled filtered\r\n option is already selected — this also covers an empty list).\r\n- `clear-all` is disabled when nothing is selected."
1356
+ "description": "Default enabled/disabled state for the built-in actions, applied only when the consumer hasn't\nset an explicit `isDisabled` / `getIsDisabledCallback`:\n- `select-all` is disabled when it would add nothing (every selectable, non-disabled filtered\n option is already selected — this also covers an empty list).\n- `clear-all` is disabled when nothing is selected."
1321
1357
  },
1322
1358
  {
1323
1359
  "kind": "method",
@@ -1362,7 +1398,7 @@
1362
1398
  "text": "string"
1363
1399
  }
1364
1400
  },
1365
- "description": "Trailing info affordance for an option row, emitted only for the fullscreen\r\noverlay. CSS keeps it hidden until `markTruncatedOptions()` tags the row\r\n`.ms__option--truncated`, so it appears only when the label is actually clipped.\r\nTapping it reveals the full label — the touch substitute for the hover option\r\ntooltip, which never fires on touch (the very devices that get fullscreen).\r\n`tabindex=\"-1\"` keeps it out of the tab order; the search input owns keyboarding."
1401
+ "description": "Trailing info affordance for an option row, emitted only for the fullscreen\noverlay. CSS keeps it hidden until `markTruncatedOptions()` tags the row\n`.ms__option--truncated`, so it appears only when the label is actually clipped.\nTapping it reveals the full label — the touch substitute for the hover option\ntooltip, which never fires on touch (the very devices that get fullscreen).\n`tabindex=\"-1\"` keeps it out of the tab order; the search input owns keyboarding."
1366
1402
  },
1367
1403
  {
1368
1404
  "kind": "method",
@@ -1387,7 +1423,7 @@
1387
1423
  }
1388
1424
  }
1389
1425
  ],
1390
- "description": "Render a single tree-mode row. Separate from `renderOption`: a tree row is\r\nindented by its depth (via the `--ms-tree-depth` custom property) and\r\ntagged branch/leaf, but otherwise carries the same selection/checkbox/\r\nicon/subtitle content. The tree is always fully expanded, so there is no\r\nchevron/toggle — every node is just a normal, selectable option."
1426
+ "description": "Render a single tree-mode row. Separate from `renderOption`: a tree row is\nindented by its depth (via the `--ms-tree-depth` custom property) and\ntagged branch/leaf, but otherwise carries the same selection/checkbox/\nicon/subtitle content. The tree is always fully expanded, so there is no\nchevron/toggle — every node is just a normal, selectable option."
1391
1427
  },
1392
1428
  {
1393
1429
  "kind": "method",
@@ -1450,7 +1486,7 @@
1450
1486
  "text": "string"
1451
1487
  }
1452
1488
  },
1453
- "description": "Resolve the closed-state input placeholder for the current data/search state.\r\nPriority: explicit no-data placeholder (when the list is empty) → \"pick\" prompt when\r\nsearch is unusable → the search placeholder."
1489
+ "description": "Resolve the closed-state input placeholder for the current data/search state.\nPriority: explicit no-data placeholder (when the list is empty) → \"pick\" prompt when\nsearch is unusable → the search placeholder."
1454
1490
  },
1455
1491
  {
1456
1492
  "kind": "method",
@@ -1461,7 +1497,7 @@
1461
1497
  "text": "string"
1462
1498
  }
1463
1499
  },
1464
- "description": "The search field placeholder. An explicit `searchPlaceholder` always wins and stays\r\nfixed. Otherwise the default is \"Search...\" — except when the in-overlay mode toggle\r\nis enabled (`isSearchModeToggleShown`), where it becomes mode-aware so the field labels\r\nthe current behavior: \"Search…\" in navigate mode, \"Filter…\" in filter mode. Refreshed\r\non a live mode switch (see setSearchModeLive → refreshSearchPlaceholder)."
1500
+ "description": "The search field placeholder. An explicit `searchPlaceholder` always wins and stays\nfixed. Otherwise the default is \"Search...\" — except when the in-overlay mode toggle\nis enabled (`isSearchModeToggleShown`), where it becomes mode-aware so the field labels\nthe current behavior: \"Search…\" in navigate mode, \"Filter…\" in filter mode. Refreshed\non a live mode switch (see setSearchModeLive → refreshSearchPlaceholder)."
1465
1501
  },
1466
1502
  {
1467
1503
  "kind": "method",
@@ -1521,7 +1557,7 @@
1521
1557
  "text": "void"
1522
1558
  }
1523
1559
  },
1524
- "description": "Abort the search request currently in flight, if any. The aborted request's results\r\nare then ignored (and the consumer's `searchCallback` can short-circuit its fetch via\r\nthe `AbortSignal` it was handed)."
1560
+ "description": "Abort the search request currently in flight, if any. The aborted request's results\nare then ignored (and the consumer's `searchCallback` can short-circuit its fetch via\nthe `AbortSignal` it was handed)."
1525
1561
  },
1526
1562
  {
1527
1563
  "kind": "method",
@@ -1546,7 +1582,7 @@
1546
1582
  }
1547
1583
  }
1548
1584
  ],
1549
- "description": "Invoke the async `searchCallback` and apply its results. Split out of `handleSearch`\r\nso it can be called immediately or after the debounce timer.\r\n\r\nAny request still in flight is aborted before a new one starts, so a slow earlier\r\nrequest can't overwrite a newer one — and consumers that wire the passed `AbortSignal`\r\ninto their fetch get the request actually cancelled, not just ignored. The\r\n`aborted` / `searchTerm === value` guards drop superseded or out-of-order responses."
1585
+ "description": "Invoke the async `searchCallback` and apply its results. Split out of `handleSearch`\nso it can be called immediately or after the debounce timer.\n\nAny request still in flight is aborted before a new one starts, so a slow earlier\nrequest can't overwrite a newer one — and consumers that wire the passed `AbortSignal`\ninto their fetch get the request actually cancelled, not just ignored. The\n`aborted` / `searchTerm === value` guards drop superseded or out-of-order responses."
1550
1586
  },
1551
1587
  {
1552
1588
  "kind": "method",
@@ -1644,7 +1680,7 @@
1644
1680
  }
1645
1681
  }
1646
1682
  ],
1647
- "description": "Move focus by computing a new index from (current, total).\r\nReturning -1 from `compute` is a no-op (used for empty list / no match)."
1683
+ "description": "Move focus by computing a new index from (current, total).\nReturning -1 from `compute` is a no-op (used for empty list / no match)."
1648
1684
  },
1649
1685
  {
1650
1686
  "kind": "method",
@@ -1675,7 +1711,7 @@
1675
1711
  }
1676
1712
  }
1677
1713
  ],
1678
- "description": "Given a target index and a preferred direction, return the nearest index\r\nwhose node is selectable (skipping non-selectable tree nodes). Falls back to\r\nthe opposite direction, then to -1 if nothing is selectable. No-op outside\r\ntree mode."
1714
+ "description": "Given a target index and a preferred direction, return the nearest index\nwhose node is selectable (skipping non-selectable tree nodes). Falls back to\nthe opposite direction, then to -1 if nothing is selectable. No-op outside\ntree mode."
1679
1715
  },
1680
1716
  {
1681
1717
  "kind": "method",
@@ -1766,7 +1802,7 @@
1766
1802
  "text": "MultiSelectKeyboardController<T>"
1767
1803
  }
1768
1804
  },
1769
- "description": "Lazily build (and cache) the imperative facade passed to `keydownCallback`. Bound to the\r\nsame private actions the built-in key handling uses, so consumer shortcuts behave identically."
1805
+ "description": "Lazily build (and cache) the imperative facade passed to `keydownCallback`. Bound to the\nsame private actions the built-in key handling uses, so consumer shortcuts behave identically."
1770
1806
  },
1771
1807
  {
1772
1808
  "kind": "method",
@@ -1777,7 +1813,7 @@
1777
1813
  "text": "void"
1778
1814
  }
1779
1815
  },
1780
- "description": "Clear the search box (both the main input and the fullscreen search) and reset the visible\r\nlist. Shared by Escape and the keyboard controller."
1816
+ "description": "Clear the search box (both the main input and the fullscreen search) and reset the visible\nlist. Shared by Escape and the keyboard controller."
1781
1817
  },
1782
1818
  {
1783
1819
  "kind": "method",
@@ -1824,7 +1860,7 @@
1824
1860
  }
1825
1861
  }
1826
1862
  ],
1827
- "description": "The single funnel for an interactive (user-initiated) selection. Consults\r\n`beforeSelectCallback` and only mutates state if allowed, so the veto can\r\nnever be bypassed by a new UI entry point. Programmatic `setSelected` and\r\nthe Select-All button deliberately do not route through here.\r\nReturns true if the option was selected, false if the veto blocked it."
1863
+ "description": "The single funnel for an interactive (user-initiated) selection. Consults\n`beforeSelectCallback` and only mutates state if allowed, so the veto can\nnever be bypassed by a new UI entry point. Programmatic `setSelected` and\nthe Select-All button deliberately do not route through here.\nReturns true if the option was selected, false if the veto blocked it."
1828
1864
  },
1829
1865
  {
1830
1866
  "kind": "method",
@@ -1843,7 +1879,7 @@
1843
1879
  }
1844
1880
  }
1845
1881
  ],
1846
- "description": "The single funnel for an interactive (user-initiated) deselection. Every\r\nremoval affordance — dropdown toggle, badge × button, selected-items\r\npopover × button, and the \"remove hidden\" badge — routes through here so\r\nthe `beforeDeselectCallback` veto applies uniformly. Programmatic\r\n`setSelected` and the Clear-All button deliberately bypass it.\r\nReturns true if the option was deselected, false if the veto blocked it."
1882
+ "description": "The single funnel for an interactive (user-initiated) deselection. Every\nremoval affordance — dropdown toggle, badge × button, selected-items\npopover × button, and the \"remove hidden\" badge — routes through here so\nthe `beforeDeselectCallback` veto applies uniformly. Programmatic\n`setSelected` and the Clear-All button deliberately bypass it.\nReturns true if the option was deselected, false if the veto blocked it."
1847
1883
  },
1848
1884
  {
1849
1885
  "kind": "method",
@@ -1919,6 +1955,28 @@
1919
1955
  }
1920
1956
  }
1921
1957
  },
1958
+ {
1959
+ "kind": "method",
1960
+ "name": "clearClick",
1961
+ "privacy": "private",
1962
+ "return": {
1963
+ "type": {
1964
+ "text": "void"
1965
+ }
1966
+ },
1967
+ "description": "Inline clear (✕) handler: wipe the whole selection and any search text, then\nrestore focus to the input. clearAll() → commit() → renderBadges() already\nrefreshes this button's visibility (it hides once nothing is selected)."
1968
+ },
1969
+ {
1970
+ "kind": "method",
1971
+ "name": "updateClearButton",
1972
+ "privacy": "private",
1973
+ "return": {
1974
+ "type": {
1975
+ "text": "void"
1976
+ }
1977
+ },
1978
+ "description": "Show the inline clear (✕) only when it is opted in (isClearShown), something is\nselected, and the control is enabled. Called from renderBadges() so it tracks\nevery selection change. Uses inline display like the counter / fullscreen clear."
1979
+ },
1922
1980
  {
1923
1981
  "kind": "method",
1924
1982
  "name": "commit",
@@ -1936,27 +1994,45 @@
1936
1994
  }
1937
1995
  }
1938
1996
  ],
1939
- "description": "Re-render and fire callbacks after a selection state change.\r\n`added` / `removed` drive per-item select/deselect callbacks.\r\n`onChange` fires once if anything actually changed."
1997
+ "description": "Re-render and fire callbacks after a selection state change.\n`added` / `removed` drive per-item select/deselect callbacks.\n`onChange` fires once if anything actually changed."
1940
1998
  },
1941
1999
  {
1942
2000
  "kind": "method",
1943
2001
  "name": "open",
1944
- "privacy": "private",
1945
2002
  "return": {
1946
2003
  "type": {
1947
2004
  "text": "void"
1948
2005
  }
1949
- }
2006
+ },
2007
+ "description": "Open the dropdown (no-op if already open, or if there is nothing to show)."
1950
2008
  },
1951
2009
  {
1952
2010
  "kind": "method",
1953
2011
  "name": "close",
1954
- "privacy": "private",
1955
2012
  "return": {
1956
2013
  "type": {
1957
2014
  "text": "void"
1958
2015
  }
1959
- }
2016
+ },
2017
+ "description": "Close the dropdown (no-op if already closed)."
2018
+ },
2019
+ {
2020
+ "kind": "method",
2021
+ "name": "toggle",
2022
+ "return": {
2023
+ "type": {
2024
+ "text": "void"
2025
+ }
2026
+ },
2027
+ "description": "Toggle the dropdown open/closed."
2028
+ },
2029
+ {
2030
+ "kind": "field",
2031
+ "name": "isOpen",
2032
+ "type": {
2033
+ "text": "boolean"
2034
+ },
2035
+ "description": "Whether the dropdown is currently open. Assigning opens/closes it."
1960
2036
  },
1961
2037
  {
1962
2038
  "kind": "method",
@@ -1977,11 +2053,11 @@
1977
2053
  {
1978
2054
  "name": "opts",
1979
2055
  "type": {
1980
- "text": "{\r\n getPlacement: () => Placement | null;\r\n setPlacement: (p: Placement) => void;\r\n /** When false, never locks (re-flips on every update). Defaults to true. */\r\n isLocked?: () => boolean;\r\n applyMaxWidth?: boolean;\r\n afterPosition?: () => void;\r\n }"
2056
+ "text": "{\n getPlacement: () => Placement | null;\n setPlacement: (p: Placement) => void;\n /** When false, never locks (re-flips on every update). Defaults to true. */\n isLocked?: () => boolean;\n applyMaxWidth?: boolean;\n afterPosition?: () => void;\n }"
1981
2057
  }
1982
2058
  }
1983
2059
  ],
1984
- "description": "Anchor a floating panel (dropdown or selected-items popover) below/above the input with\r\nplacement-locking and width-syncing. Returns the `autoUpdate` cleanup.\r\n\r\nBoth panels share: anchor on input, sync width, default to 'bottom-start', flip on first\r\ncompute then lock the resulting placement, optionally clamp by dropdownMin/MaxWidth."
2060
+ "description": "Anchor a floating panel (dropdown or selected-items popover) below/above the input with\nplacement-locking and width-syncing. Returns the `autoUpdate` cleanup.\n\nBoth panels share: anchor on input, sync width, default to 'bottom-start', flip on first\ncompute then lock the resulting placement, optionally clamp by dropdownMin/MaxWidth."
1985
2061
  },
1986
2062
  {
1987
2063
  "kind": "method",
@@ -2000,7 +2076,18 @@
2000
2076
  }
2001
2077
  }
2002
2078
  ],
2003
- "description": "Surface a multiselect-branded, once-per-instance warning when core's drift check\r\n(`anchor`'s `onDrift`) reports the panel didn't land where it was positioned. The\r\nconsumer has an ancestor that establishes a fixed containing block but isn't on the\r\nreliable-anchors list (likely `contain: paint|layout|strict` or `container-type`).\r\nWe can't fix it from inside the library, but we point at the likely culprit. Core\r\nowns the measurement + culprit-finding + CB-CSS diagnostic (`detectFixedDrift`)."
2079
+ "description": "Surface a multiselect-branded, once-per-instance warning when core's drift check\n(`anchor`'s `onDrift`) reports the panel didn't land where it was positioned. The\nconsumer has an ancestor that establishes a fixed containing block but isn't on the\nreliable-anchors list (likely `contain: paint|layout|strict` or `container-type`).\nWe can't fix it from inside the library, but we point at the likely culprit. Core\nowns the measurement + culprit-finding + CB-CSS diagnostic (`detectFixedDrift`)."
2080
+ },
2081
+ {
2082
+ "kind": "method",
2083
+ "name": "warnFullscreenContainingBlock",
2084
+ "privacy": "private",
2085
+ "return": {
2086
+ "type": {
2087
+ "text": "void"
2088
+ }
2089
+ },
2090
+ "description": "Fullscreen counterpart of warnDrift. The overlay is a `position: fixed`,\nfull-viewport sheet — but if an ancestor of the host establishes a fixed-positioning\ncontaining block (`transform` / `perspective` / `filter` / `backdrop-filter` / a\nqualifying `will-change`), the browser anchors the sheet to THAT ancestor's box instead\nof the viewport, so it no longer covers the screen (offset, clipped, or mis-sized).\n\nUnlike the floating path — where core measures real drift after positioning — nothing\nanchors the sheet, so there's no drift to observe. Instead we ask core's shared\nheuristic (`getFixedPositionOffsetParent`, the same one that feeds the floating platform)\nwhether the sheet's true offset parent is the viewport (`window`) or an element. An\nelement means it WILL be mis-anchored; warn once, pointing at the culprit. We only check\nthe reliably-honoured properties core lists (transform family) — `contain` /\n`container-type` are omitted because browsers don't honour them for fixed positioning,\nso they don't actually break the sheet."
2004
2091
  },
2005
2092
  {
2006
2093
  "kind": "method",
@@ -2029,7 +2116,7 @@
2029
2116
  }
2030
2117
  }
2031
2118
  ],
2032
- "description": "Switch how the open panels are presented. 'floating' anchors them to the input\r\n(the default); 'fullscreen' renders them as full-viewport overlays (the phone\r\npattern) — the dropdown with its own search header + close, the selected-items\r\npopover with its existing header + close. Driven by the element's\r\n`environmentChanged` hook (auto → fullscreen on phones). A no-op when unchanged;\r\nwhen a panel is already open it re-applies live so an orientation flip / viewport\r\nresize can swap presentation without a reopen."
2119
+ "description": "Switch how the open panels are presented. 'floating' anchors them to the input\n(the default); 'fullscreen' renders them as full-viewport overlays (the phone\npattern) — the dropdown with its own search header + close, the selected-items\npopover with its existing header + close. Driven by the element's\n`environmentChanged` hook (auto → fullscreen on phones). A no-op when unchanged;\nwhen a panel is already open it re-applies live so an orientation flip / viewport\nresize can swap presentation without a reopen."
2033
2120
  },
2034
2121
  {
2035
2122
  "kind": "method",
@@ -2040,7 +2127,7 @@
2040
2127
  "text": "void"
2041
2128
  }
2042
2129
  },
2043
- "description": "Lock page scroll behind a fullscreen overlay via the core ref-counted helper.\r\nIdempotent per instance: the dropdown and the selected-items popover are mutually\r\nexclusive (opening one closes the other), so we hold at most one lock at a time,\r\nand a redundant call is a no-op rather than acquiring a second."
2130
+ "description": "Lock page scroll behind a fullscreen overlay via the core ref-counted helper.\nIdempotent per instance: the dropdown and the selected-items popover are mutually\nexclusive (opening one closes the other), so we hold at most one lock at a time,\nand a redundant call is a no-op rather than acquiring a second."
2044
2131
  },
2045
2132
  {
2046
2133
  "kind": "method",
@@ -2062,7 +2149,7 @@
2062
2149
  "text": "void"
2063
2150
  }
2064
2151
  },
2065
- "description": "Clip the host document's horizontal overflow while a fullscreen sheet is open.\r\n\r\nA page that overflows horizontally (e.g. an unbreakable-wide token in a heading)\r\nmakes the mobile browser SHRINK-TO-FIT: it zooms the page out so the overflow fits,\r\nwhich desyncs the visual viewport from the layout viewport. Our fullscreen sheet is\r\n`position: fixed` — anchored to the LAYOUT viewport — so under that zoom it no longer\r\nlands flush against the physical screen edges, and the top slips under the system bar\r\n(looks like \"the bar covers the sheet\"). This is NOT a safe-area problem; safe-area\r\ninsets are 0 in that state. Clamping `overflow-x: hidden` on <html>/<body> removes the\r\noverflow, so the browser drops the zoom and the sheet sits flush. Complements\r\nlockBodyScroll() (vertical axis); the saved inline value is restored on close.\r\n\r\nOnly <html> is touched (not <body>): clipping the root's horizontal overflow is\r\nenough to collapse the scrollWidth and cancel the shrink-to-fit, and it avoids\r\nconflicting with core's lockBodyScroll(), which owns <body>'s `overflow`. Idempotent."
2152
+ "description": "Clip the host document's horizontal overflow while a fullscreen sheet is open.\n\nA page that overflows horizontally (e.g. an unbreakable-wide token in a heading)\nmakes the mobile browser SHRINK-TO-FIT: it zooms the page out so the overflow fits,\nwhich desyncs the visual viewport from the layout viewport. Our fullscreen sheet is\n`position: fixed` — anchored to the LAYOUT viewport — so under that zoom it no longer\nlands flush against the physical screen edges, and the top slips under the system bar\n(looks like \"the bar covers the sheet\"). This is NOT a safe-area problem; safe-area\ninsets are 0 in that state. Clamping `overflow-x: hidden` on <html>/<body> removes the\noverflow, so the browser drops the zoom and the sheet sits flush. Complements\nlockBodyScroll() (vertical axis); the saved inline value is restored on close.\n\nOnly <html> is touched (not <body>): clipping the root's horizontal overflow is\nenough to collapse the scrollWidth and cancel the shrink-to-fit, and it avoids\nconflicting with core's lockBodyScroll(), which owns <body>'s `overflow`. Idempotent."
2066
2153
  },
2067
2154
  {
2068
2155
  "kind": "method",
@@ -2084,7 +2171,7 @@
2084
2171
  "text": "void"
2085
2172
  }
2086
2173
  },
2087
- "description": "While the fullscreen dropdown is open, keep it sitting above the soft keyboard.\r\nDelegates to core's `observeKeyboardInset` (which tracks `window.visualViewport`\r\nand pins the panel's height/top so its flex column reflows above the keyboard);\r\nwe just hold the returned cleanup. No-op where `visualViewport` is unavailable."
2174
+ "description": "While the fullscreen dropdown is open, keep it sitting above the soft keyboard.\nDelegates to core's `observeKeyboardInset` (which tracks `window.visualViewport`\nand pins the panel's height/top so its flex column reflows above the keyboard);\nwe just hold the returned cleanup. No-op where `visualViewport` is unavailable."
2088
2175
  },
2089
2176
  {
2090
2177
  "kind": "method",
@@ -2106,7 +2193,7 @@
2106
2193
  "text": "number"
2107
2194
  }
2108
2195
  },
2109
- "description": "The fullscreen size multiplier = `--ms-fullscreen-rem ÷ --ms-rem` (both read off\r\nthe host). CSS scales itself — every size is `calc(N × --ms-rem)` and the panel\r\noverrides `--ms-rem` — so this exists only for the JS-driven pixel heights that\r\nCSS can't reach: the virtual/fixed option rows and the popover's virtual badges.\r\nReturns 1 when floating (or when computed styles aren't readable, e.g. jsdom)."
2196
+ "description": "The fullscreen size multiplier = `--ms-fullscreen-rem ÷ --ms-rem` (both read off\nthe host). CSS scales itself — every size is `calc(N × --ms-rem)` and the panel\noverrides `--ms-rem` — so this exists only for the JS-driven pixel heights that\nCSS can't reach: the virtual/fixed option rows and the popover's virtual badges.\nReturns 1 when floating (or when computed styles aren't readable, e.g. jsdom)."
2110
2197
  },
2111
2198
  {
2112
2199
  "kind": "method",
@@ -2136,7 +2223,7 @@
2136
2223
  }
2137
2224
  }
2138
2225
  ],
2139
- "description": "Size the virtual options scroll container for the current presentation. Applied on\r\nevery render (the container itself is built once), so a floating⇄fullscreen switch\r\nre-sizes it: floating = a fixed maxHeight scroll box; fullscreen = flex-fill the\r\npanel's flex column (no fixed height). Also refreshes --ms-option-height to the\r\nscaled row height so the CSS row height matches the virtual scroller's itemHeight."
2226
+ "description": "Size the virtual options scroll container for the current presentation. Applied on\nevery render (the container itself is built once), so a floating⇄fullscreen switch\nre-sizes it: floating = a fixed maxHeight scroll box; fullscreen = flex-fill the\npanel's flex column (no fixed height). Also refreshes --ms-option-height to the\nscaled row height so the CSS row height matches the virtual scroller's itemHeight."
2140
2227
  },
2141
2228
  {
2142
2229
  "kind": "method",
@@ -2147,7 +2234,7 @@
2147
2234
  "text": "number"
2148
2235
  }
2149
2236
  },
2150
- "description": "Virtual popover badge row height (px). In the fullscreen phone view the rows are\r\nscaled up AND given extra height so a selected item is a comfortable, dropdown-like\r\ntouch target (the default 36px pill is short for touch). Mirrors the CSS\r\n`--ms-badge-height` override for the fullscreen popover (floating.css) so the\r\nvirtual list's fixed height agrees with the non-virtual pills."
2237
+ "description": "Virtual popover badge row height (px). In the fullscreen phone view the rows are\nscaled up AND given extra height so a selected item is a comfortable, dropdown-like\ntouch target (the default 36px pill is short for touch). Mirrors the CSS\n`--ms-badge-height` override for the fullscreen popover (floating.css) so the\nvirtual list's fixed height agrees with the non-virtual pills."
2151
2238
  },
2152
2239
  {
2153
2240
  "kind": "method",
@@ -2166,7 +2253,7 @@
2166
2253
  }
2167
2254
  }
2168
2255
  ],
2169
- "description": "Clear the inline geometry that floating-ui's `anchor` writes on a panel\r\n(position/left/top plus our composed max-width/min-width). Inline styles beat\r\nthe stylesheet, so a panel left over from a floating cycle would otherwise pin\r\nitself where it last anchored and ignore the fullscreen CSS (position: fixed;\r\ninset: 0; width: 100vw). Must run when switching a panel floating → fullscreen."
2256
+ "description": "Clear the inline geometry that floating-ui's `anchor` writes on a panel\n(position/left/top plus our composed max-width/min-width). Inline styles beat\nthe stylesheet, so a panel left over from a floating cycle would otherwise pin\nitself where it last anchored and ignore the fullscreen CSS (position: fixed;\ninset: 0; width: 100vw). Must run when switching a panel floating → fullscreen."
2170
2257
  },
2171
2258
  {
2172
2259
  "kind": "method",
@@ -2199,7 +2286,7 @@
2199
2286
  "text": "void"
2200
2287
  }
2201
2288
  },
2202
- "description": "Back-gesture handling for the fullscreen sheet. On open we push a history entry\r\n(same URL) and listen for `popstate`; the phone Back gesture/button then pops that\r\nentry — which we treat as \"close the sheet\" — instead of navigating away from the\r\npage. A programmatic close (✕, selection, Escape) consumes the entry via\r\n`history.back()` so the stack is left as it was found."
2289
+ "description": "Back-gesture handling for the fullscreen sheet. On open we push a history entry\n(same URL) and listen for `popstate`; the phone Back gesture/button then pops that\nentry — which we treat as \"close the sheet\" — instead of navigating away from the\npage. A programmatic close (✕, selection, Escape) consumes the entry via\n`history.back()` so the stack is left as it was found."
2203
2290
  },
2204
2291
  {
2205
2292
  "kind": "method",
@@ -2210,7 +2297,7 @@
2210
2297
  "text": "void"
2211
2298
  }
2212
2299
  },
2213
- "description": "Back gesture/button fired: our pushed entry is already gone, so just close the\r\nsheet — WITHOUT popping history again (popOverlayHistory becomes a no-op)."
2300
+ "description": "Back gesture/button fired: our pushed entry is already gone, so just close the\nsheet — WITHOUT popping history again (popOverlayHistory becomes a no-op)."
2214
2301
  },
2215
2302
  {
2216
2303
  "kind": "method",
@@ -2221,7 +2308,7 @@
2221
2308
  "text": "void"
2222
2309
  }
2223
2310
  },
2224
- "description": "Programmatic close: remove the listener and pop the entry we pushed (so the\r\nhistory stack returns to its pre-open state). No-op if a Back gesture already\r\nconsumed it (overlayHistoryActive is false by then)."
2311
+ "description": "Programmatic close: remove the listener and pop the entry we pushed (so the\nhistory stack returns to its pre-open state). No-op if a Back gesture already\nconsumed it (overlayHistoryActive is false by then)."
2225
2312
  },
2226
2313
  {
2227
2314
  "kind": "method",
@@ -2232,7 +2319,7 @@
2232
2319
  "text": "void"
2233
2320
  }
2234
2321
  },
2235
- "description": "Build the fullscreen overlay header: a search field (proxying to the same\r\n`handleSearch`/`handleKeydown` path as the main input, since the overlay covers\r\nit) plus a close button. Inserted before the scrolling list so it pins to the\r\ntop of the fixed panel. `renderDropdown()` only rewrites `dropdownInner`, so the\r\nheader survives re-renders."
2322
+ "description": "Build the fullscreen overlay header: a search field (proxying to the same\n`handleSearch`/`handleKeydown` path as the main input, since the overlay covers\nit) plus a close button. Inserted before the scrolling list so it pins to the\ntop of the fixed panel. `renderDropdown()` only rewrites `dropdownInner`, so the\nheader survives re-renders."
2236
2323
  },
2237
2324
  {
2238
2325
  "kind": "method",
@@ -2243,7 +2330,7 @@
2243
2330
  "text": "void"
2244
2331
  }
2245
2332
  },
2246
- "description": "Build the navigate-mode match navigator (count + prev/next) and append it to the\r\nfullscreen header, once. No-op if already built or the header isn't present. The\r\nnav wraps onto its own full-width row under the search box (header is flex-wrap;\r\nthe nav takes 100% basis)."
2333
+ "description": "Build the navigate-mode match navigator (count + prev/next) and append it to the\nfullscreen header, once. No-op if already built or the header isn't present. The\nnav wraps onto its own full-width row under the search box (header is flex-wrap;\nthe nav takes 100% basis)."
2247
2334
  },
2248
2335
  {
2249
2336
  "kind": "method",
@@ -2284,7 +2371,7 @@
2284
2371
  }
2285
2372
  }
2286
2373
  ],
2287
- "description": "Switch searchMode in place — the overlay's toggle path. The `search-mode` attribute\r\nis reinit-on-change (it rebuilds and closes the overlay); this instead mutates the\r\nlive config, adds/removes the match navigator to match, and re-projects the current\r\nterm under the new mode (filter narrows the list / navigate keeps all + highlights),\r\nall without tearing the open sheet down. Focus stays on the search field."
2374
+ "description": "Switch searchMode in place — the overlay's toggle path. The `search-mode` attribute\nis reinit-on-change (it rebuilds and closes the overlay); this instead mutates the\nlive config, adds/removes the match navigator to match, and re-projects the current\nterm under the new mode (filter narrows the list / navigate keeps all + highlights),\nall without tearing the open sheet down. Focus stays on the search field."
2288
2375
  },
2289
2376
  {
2290
2377
  "kind": "method",
@@ -2295,7 +2382,7 @@
2295
2382
  "text": "void"
2296
2383
  }
2297
2384
  },
2298
- "description": "Sync the mode toggle's icon (via data-mode) and labels with the current searchMode.\r\nNo-op when the toggle isn't built (opt-out, floating panel, or search hidden)."
2385
+ "description": "Sync the mode toggle's icon (via data-mode) and labels with the current searchMode.\nNo-op when the toggle isn't built (opt-out, floating panel, or search hidden)."
2299
2386
  },
2300
2387
  {
2301
2388
  "kind": "method",
@@ -2306,7 +2393,7 @@
2306
2393
  "text": "void"
2307
2394
  }
2308
2395
  },
2309
- "description": "Sync the fullscreen match navigator (navigate mode only) with the current search\r\nstate: hide it until there's a term, then show \"N of M\" while a match is focused\r\n(or \"M matches\" / \"No matches\"), and disable the prev/next buttons when there's\r\nnothing to step through. No-op when the navigator isn't built (floating panel,\r\nfilter mode, or search disabled)."
2396
+ "description": "Sync the fullscreen match navigator (navigate mode only) with the current search\nstate: hide it until there's a term, then show \"N of M\" while a match is focused\n(or \"M matches\" / \"No matches\"), and disable the prev/next buttons when there's\nnothing to step through. No-op when the navigator isn't built (floating panel,\nfilter mode, or search disabled)."
2310
2397
  },
2311
2398
  {
2312
2399
  "kind": "method",
@@ -2317,7 +2404,7 @@
2317
2404
  "text": "void"
2318
2405
  }
2319
2406
  },
2320
- "description": "Show the fullscreen search's inline clear (✕) only while the field has text.\r\nNo-op when the button isn't built (floating panel, readonly/hidden search)."
2407
+ "description": "Show the fullscreen search's inline clear (✕) only while the field has text.\nNo-op when the button isn't built (floating panel, readonly/hidden search)."
2321
2408
  },
2322
2409
  {
2323
2410
  "kind": "method",
@@ -2328,7 +2415,7 @@
2328
2415
  "text": "void"
2329
2416
  }
2330
2417
  },
2331
- "description": "Clear the fullscreen search term via the same path a keystroke takes, then\r\nrefocus the field so the user can keep typing. Touch has no keyboard Escape,\r\nso this button is the on-screen way to reset a search."
2418
+ "description": "Clear the fullscreen search term via the same path a keystroke takes, then\nrefocus the field so the user can keep typing. Touch has no keyboard Escape,\nso this button is the on-screen way to reset a search."
2332
2419
  },
2333
2420
  {
2334
2421
  "kind": "method",
@@ -2359,7 +2446,7 @@
2359
2446
  "text": "void"
2360
2447
  }
2361
2448
  },
2362
- "description": "Resolve any `selectedValues` entries that don't yet have a matching\r\n`selectedOptions` object by looking them up in the current `allOptions`.\r\nIdempotent; safe to call after init *and* after `options` is replaced\r\n(e.g., async fetch, `searchCallback` result, or late `element.options =`\r\nassignment). Without this, `initial-values` declared before options\r\narrive ends up with phantom values that `getValue()` can never report."
2449
+ "description": "Resolve any `selectedValues` entries that don't yet have a matching\n`selectedOptions` object by looking them up in the current `allOptions`.\nIdempotent; safe to call after init *and* after `options` is replaced\n(e.g., async fetch, `searchCallback` result, or late `element.options =`\nassignment). Without this, `initial-values` declared before options\narrive ends up with phantom values that `getValue()` can never report."
2363
2450
  },
2364
2451
  {
2365
2452
  "kind": "method",
@@ -2425,6 +2512,44 @@
2425
2512
  }
2426
2513
  ]
2427
2514
  },
2515
+ {
2516
+ "kind": "method",
2517
+ "name": "toHtml",
2518
+ "privacy": "private",
2519
+ "return": {
2520
+ "type": {
2521
+ "text": "string"
2522
+ }
2523
+ },
2524
+ "parameters": [
2525
+ {
2526
+ "name": "v",
2527
+ "type": {
2528
+ "text": "string | HTMLElement | null | undefined"
2529
+ }
2530
+ }
2531
+ ],
2532
+ "description": "Coerce a render-callback result to an HTML string. Callbacks may return a string\n(HTML) or an HTMLElement (serialized via `outerHTML`); null/undefined → ''. Used by\nevery \"return string | HTMLElement\" content callback that builds into an innerHTML\nstring. (DOM sinks that hold a live node instead — the reveal/message panels — use\ntextContent/appendChild directly and intentionally don't go through here.)"
2533
+ },
2534
+ {
2535
+ "kind": "method",
2536
+ "name": "classSuffix",
2537
+ "privacy": "private",
2538
+ "return": {
2539
+ "type": {
2540
+ "text": "string"
2541
+ }
2542
+ },
2543
+ "parameters": [
2544
+ {
2545
+ "name": "v",
2546
+ "type": {
2547
+ "text": "string | string[] | null | undefined"
2548
+ }
2549
+ }
2550
+ ],
2551
+ "description": "Normalize a class callback result (`string | string[] | null`) to a single\nspace-joined string with falsy entries dropped — e.g. `['a', '', 'b'] → \"a b\"`,\n`null → \"\"`. Callers add their own leading space / base class as needed."
2552
+ },
2428
2553
  {
2429
2554
  "kind": "method",
2430
2555
  "name": "renderBadgeHTML",
@@ -2448,7 +2573,7 @@
2448
2573
  }
2449
2574
  }
2450
2575
  ],
2451
- "description": "Render a removable badge for a selected option (used by the badges/partial display modes\r\nand by the selected-items popover).\r\n\r\n- In the popover, `renderSelectedItemContentCallback` and `getSelectedItemClassCallback` win\r\n over the regular badge callbacks; that's how consumers customize popover items independently.\r\n- The `data-value` and aria-label both go through `getItemBadgeDisplayValue` so badge text and\r\n accessible name stay in sync."
2576
+ "description": "Render a removable badge for a selected option (used by the badges/partial display modes\nand by the selected-items popover).\n\n- In the popover, `renderSelectedItemContentCallback` and `getSelectedItemClassCallback` win\n over the regular badge callbacks; that's how consumers customize popover items independently.\n- The `data-value` and aria-label both go through `getItemBadgeDisplayValue` so badge text and\n accessible name stay in sync."
2452
2577
  },
2453
2578
  {
2454
2579
  "kind": "method",
@@ -2532,7 +2657,7 @@
2532
2657
  }
2533
2658
  }
2534
2659
  ],
2535
- "description": "Set the selection programmatically. **Silent by default** — it does not fire\r\n`select`/`deselect`/`change` (so restoring saved state, cascade resets, or a\r\nserver-authoritative correction can't loop back or trip \"user changed it\"\r\nhandlers). Pass `{ notify: true }` to announce the result as a **single\r\naggregate `change`** — for a deliberate user gesture (e.g. an action button)\r\nthat should reach the same listeners a manual pick does, without the per-item\r\n`select`/`deselect` flood a bulk change would otherwise cause."
2660
+ "description": "Set the selection programmatically. **Silent by default** — it does not fire\n`select`/`deselect`/`change` (so restoring saved state, cascade resets, or a\nserver-authoritative correction can't loop back or trip \"user changed it\"\nhandlers). Pass `{ notify: true }` to announce the result as a **single\naggregate `change`** — for a deliberate user gesture (e.g. an action button)\nthat should reach the same listeners a manual pick does, without the per-item\n`select`/`deselect` flood a bulk change would otherwise cause."
2536
2661
  },
2537
2662
  {
2538
2663
  "kind": "method",
@@ -2551,7 +2676,7 @@
2551
2676
  }
2552
2677
  }
2553
2678
  ],
2554
- "description": "Merge a partial config update into the live picker without tearing down the DOM.\r\n\r\nHandles the cheap structural toggles inline (no-checkboxes class, badges-position class,\r\ninput placeholder, search-input mode) and re-renders dropdown + badges + hidden inputs.\r\n\r\nReturns `true` if the change could be applied in place. Returns `false` for changes that\r\ntruly require rebuilding the DOM scaffolding (currently: adding/removing the `searchHint`\r\nelement, since it's only created in `buildHTML` if a hint string was provided). The caller\r\nshould fall back to destroy + re-init in that case."
2679
+ "description": "Merge a partial config update into the live picker without tearing down the DOM.\n\nHandles the cheap structural toggles inline (no-checkboxes class, badges-position class,\ninput placeholder, search-input mode) and re-renders dropdown + badges + hidden inputs.\n\nReturns `true` if the change could be applied in place. Returns `false` for changes that\ntruly require rebuilding the DOM scaffolding (currently: adding/removing the `searchHint`\nelement, since it's only created in `buildHTML` if a hint string was provided). The caller\nshould fall back to destroy + re-init in that case."
2555
2680
  },
2556
2681
  {
2557
2682
  "kind": "field",
@@ -2594,11 +2719,11 @@
2594
2719
  {
2595
2720
  "name": "spec",
2596
2721
  "type": {
2597
- "text": "{\r\n id: string;\r\n trigger: HTMLElement;\r\n content: string | HTMLElement;\r\n onBeforeShow?: () => void;\r\n /** Override placement (default: `badgeTooltipPlacement`). */\r\n placement?: Placement;\r\n /** Override offset (default: `badgeTooltipOffset`). */\r\n offsetDistance?: number;\r\n /** Override show delay (default: `badgeTooltipDelay`). */\r\n showDelay?: number;\r\n /** Tooltip element CSS class (default: badge tooltip styling). */\r\n cssClass?: string;\r\n /** Visibility-toggle CSS class (default: badge tooltip visible class). */\r\n visibleClass?: string;\r\n /** Anchor to and follow the mouse pointer. */\r\n followCursor?: boolean;\r\n }"
2722
+ "text": "{\n id: string;\n trigger: HTMLElement;\n content: string | HTMLElement;\n onBeforeShow?: () => void;\n /** Override placement (default: `badgeTooltipPlacement`). */\n placement?: Placement;\n /** Override offset (default: `badgeTooltipOffset`). */\n offsetDistance?: number;\n /** Override show delay (default: `badgeTooltipDelay`). */\n showDelay?: number;\n /** Tooltip element CSS class (default: badge tooltip styling). */\n cssClass?: string;\n /** Visibility-toggle CSS class (default: badge tooltip visible class). */\n visibleClass?: string;\n /** Anchor to and follow the mouse pointer. */\n followCursor?: boolean;\n }"
2598
2723
  }
2599
2724
  }
2600
2725
  ],
2601
- "description": "Create or replace a tracked tooltip with the given id. Replacing destroys the old one,\r\nwhich is the normal flow when re-rendering badges/actions."
2726
+ "description": "Create or replace a tracked tooltip with the given id. Replacing destroys the old one,\nwhich is the normal flow when re-rendering badges/actions."
2602
2727
  },
2603
2728
  {
2604
2729
  "kind": "method",
@@ -2702,7 +2827,7 @@
2702
2827
  "text": "void"
2703
2828
  }
2704
2829
  },
2705
- "description": "Attach hover tooltips to the currently rendered dropdown options. Prunes existing option\r\ntooltips first, so it's safe to call on every render and on every virtual-scroll range change\r\n(where option DOM is recycled). Each option resolves its source object via `data-index` into\r\n`filteredOptions`, the same global index `renderOption` was given."
2830
+ "description": "Attach hover tooltips to the currently rendered dropdown options. Prunes existing option\ntooltips first, so it's safe to call on every render and on every virtual-scroll range change\n(where option DOM is recycled). Each option resolves its source object via `data-index` into\n`filteredOptions`, the same global index `renderOption` was given."
2706
2831
  },
2707
2832
  {
2708
2833
  "kind": "method",
@@ -2713,7 +2838,7 @@
2713
2838
  "text": "void"
2714
2839
  }
2715
2840
  },
2716
- "description": "Tag each currently-rendered fullscreen option row whose title is horizontally\r\nclipped with `.ms__option--truncated`, so CSS reveals its info affordance.\r\nRuns per virtual-scroll render (rows recycle) and on the non-virtual render.\r\nHorizontal (ellipsis) overflow only — the truncation mode this pairs with;\r\na wrapping title isn't \"cut\", it grows vertically. No-op unless fullscreen."
2841
+ "description": "Tag each currently-rendered fullscreen option row whose title is horizontally\nclipped with `.ms__option--truncated`, so CSS reveals its info affordance.\nRuns per virtual-scroll render (rows recycle) and on the non-virtual render.\nHorizontal (ellipsis) overflow only — the truncation mode this pairs with;\na wrapping title isn't \"cut\", it grows vertically. No-op unless fullscreen."
2717
2842
  },
2718
2843
  {
2719
2844
  "kind": "method",
@@ -2732,7 +2857,7 @@
2732
2857
  }
2733
2858
  }
2734
2859
  ],
2735
- "description": "Reveal (or dismiss) the full label of a clipped fullscreen row when its info\r\naffordance is tapped — hover tooltips don't fire on touch, and a hover tooltip's\r\nsynthetic mouseleave (from the tap itself, under devtools touch emulation) would\r\nflash it away. So this is a manually-controlled `createPopover` panel, mounted in\r\nthe shadow root for component styling, that stays until explicitly dismissed:\r\na second tap on the same button, a list scroll, a re-render, an outside tap, or\r\nclosing the panel (see hideLabelReveal + its call sites). Tapping the same button\r\nwhile it's shown toggles it off."
2860
+ "description": "Reveal (or dismiss) the full label of a clipped fullscreen row when its info\naffordance is tapped — hover tooltips don't fire on touch, and a hover tooltip's\nsynthetic mouseleave (from the tap itself, under devtools touch emulation) would\nflash it away. So this is a manually-controlled `createPopover` panel, mounted in\nthe shadow root for component styling, that stays until explicitly dismissed:\na second tap on the same button, a list scroll, a re-render, an outside tap, or\nclosing the panel (see hideLabelReveal + its call sites). Tapping the same button\nwhile it's shown toggles it off."
2736
2861
  },
2737
2862
  {
2738
2863
  "kind": "method",
@@ -2769,7 +2894,7 @@
2769
2894
  }
2770
2895
  }
2771
2896
  ],
2772
- "description": "Show a transient message (\"toast\") on top of the component. Its reason for existing:\r\nin the fullscreen overlay the sheet covers the whole page, so a consumer can't surface\r\nfeedback (a blocked veto, a hint) where the user can see it. This renders above the\r\npanel in BOTH presentations — anchored under the control when floating, pinned to the\r\nbottom of the viewport (over the overlay) when fullscreen.\r\n\r\nContent is a string (plain text) or an HTMLElement (rich markup). `opts.variant`\r\n(info | warning | error | success) picks the tone; `opts.duration` sets auto-dismiss\r\n(0 = sticky). Tapping the message dismisses it. Only one shows at a time — a new call\r\nreplaces the previous. Also reached automatically when a veto callback returns a string."
2897
+ "description": "Show a transient message (\"toast\") on top of the component. Its reason for existing:\nin the fullscreen overlay the sheet covers the whole page, so a consumer can't surface\nfeedback (a blocked veto, a hint) where the user can see it. This renders above the\npanel in BOTH presentations — anchored under the control when floating, pinned to the\nbottom of the viewport (over the overlay) when fullscreen.\n\nContent is a string (plain text) or an HTMLElement (rich markup). `opts.variant`\n(info | warning | error | success) picks the tone; `opts.duration` sets auto-dismiss\n(0 = sticky). Tapping the message dismisses it. Only one shows at a time — a new call\nreplaces the previous. Also reached automatically when a veto callback returns a string."
2773
2898
  },
2774
2899
  {
2775
2900
  "kind": "method",
@@ -2791,7 +2916,7 @@
2791
2916
  "text": "void"
2792
2917
  }
2793
2918
  },
2794
- "description": "Hide (don't destroy) every currently-shown option tooltip immediately,\r\nignoring the hide delay. Wired to dropdown scroll so a tooltip can't trail\r\nits recycling/scrolling anchor row. Handles stay in the map; a fresh hover\r\nre-shows them."
2919
+ "description": "Hide (don't destroy) every currently-shown option tooltip immediately,\nignoring the hide delay. Wired to dropdown scroll so a tooltip can't trail\nits recycling/scrolling anchor row. Handles stay in the map; a fresh hover\nre-shows them."
2795
2920
  },
2796
2921
  {
2797
2922
  "kind": "method",
@@ -2802,7 +2927,7 @@
2802
2927
  "text": "void"
2803
2928
  }
2804
2929
  },
2805
- "description": "Destroy only the option tooltips (prefixed `option-`). Called before re-rendering or\r\nrecycling the options list so per-option tooltip state doesn't leak."
2930
+ "description": "Destroy only the option tooltips (prefixed `option-`). Called before re-rendering or\nrecycling the options list so per-option tooltip state doesn't leak."
2806
2931
  },
2807
2932
  {
2808
2933
  "kind": "method",
@@ -2823,7 +2948,7 @@
2823
2948
  "text": "void"
2824
2949
  }
2825
2950
  },
2826
- "description": "Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`\r\nbefore rebuilding the actions row, so per-button tooltip state doesn't leak."
2951
+ "description": "Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`\nbefore rebuilding the actions row, so per-button tooltip state doesn't leak."
2827
2952
  },
2828
2953
  {
2829
2954
  "kind": "method",
@@ -2834,7 +2959,7 @@
2834
2959
  "text": "void"
2835
2960
  }
2836
2961
  },
2837
- "description": "Destroy main-badges-container tooltips. Called before re-rendering the badges container.\r\nPopover tooltips (prefixed `popover-`) survive — they're owned by the popover lifecycle and\r\ncleaned up in `hideSelectedPopover`. Action-button tooltips (prefixed `action-`) survive too."
2962
+ "description": "Destroy main-badges-container tooltips. Called before re-rendering the badges container.\nPopover tooltips (prefixed `popover-`) survive — they're owned by the popover lifecycle and\ncleaned up in `hideSelectedPopover`. Action-button tooltips (prefixed `action-`) survive too."
2838
2963
  },
2839
2964
  {
2840
2965
  "kind": "method",
@@ -3324,7 +3449,7 @@
3324
3449
  "name": "inputs",
3325
3450
  "privacy": "protected",
3326
3451
  "static": true,
3327
- "default": "[ // ── Strings (cosmetic → update). Optional ones are nullable: absent → null ─ { configKey: 'searchHint', attribute: 'search-hint', converter: toText({ isNullable: true }), on: 'update', description: 'Small hint text shown beneath the search input.' }, { configKey: 'searchPlaceholder', attribute: 'search-placeholder', converter: toText({ isNullable: true }), on: 'update', description: 'Placeholder text for the search input. When unset it defaults to \"Search...\"; if `show-search-mode-toggle` is on, the default instead becomes mode-aware (\"Search…\" in navigate, \"Filter…\" in filter). An explicit value always wins and stays fixed.' }, { configKey: 'selectPlaceholder', attribute: 'select-placeholder', converter: toText({ default: 'Pick an option...' }), on: 'update', description: 'Placeholder shown on the control when nothing is selected.' }, { configKey: 'noDataPlaceholder', attribute: 'no-data-placeholder', converter: toText({ isNullable: true }), on: 'update', description: 'Text shown when there are no options at all.' }, { configKey: 'dropdownMinWidth', attribute: 'dropdown-min-width', converter: toText({ isNullable: true }), on: 'update', description: 'Minimum width of the dropdown panel (any CSS length).' }, { configKey: 'dropdownMaxWidth', attribute: 'dropdown-max-width', converter: toText({ isNullable: true }), on: 'update', description: 'Maximum width of the dropdown panel (any CSS length).' }, { configKey: 'maxHeight', attribute: 'max-height', converter: toText({ default: '20rem' }), on: 'update', description: 'Maximum height of the dropdown list before it scrolls.' }, { configKey: 'emptyMessage', attribute: 'empty-message', converter: toText({ default: 'No results found' }), on: 'update', description: 'Message shown when a search yields no matches.' }, { configKey: 'loadingMessage', attribute: 'loading-message', converter: toText({ default: 'Loading...' }), on: 'update', description: 'Message shown while options are loading.' }, { configKey: 'removeButtonTooltipText', attribute: 'remove-button-tooltip-text', converter: toText({ isNullable: true }), on: 'update', description: 'Tooltip text for a badge remove (×) button.' }, { configKey: 'formFieldId', attribute: 'name', converter: toText({ isNullable: true }), on: 'reinit', description: 'HTML form field name/id used for the hidden input(s).' }, // ── CSS-var sugar (mirrored to a host style prop in reinit()/update()) ──── { configKey: 'dropdownWidth', attribute: 'dropdown-width', converter: toText({ isNullable: true }), on: 'update', description: 'Fixed dropdown width; mirrored to the `--ms-dropdown-width` CSS variable.' }, { configKey: 'selectedPopoverWidth', attribute: 'selected-popover-width', converter: toText({ isNullable: true }), on: 'update', description: 'Selected-items popover width; mirrored to `--ms-selected-popover-width`.' }, // ── Member properties (structural → reinit; optional → nullable) ───────── { configKey: 'valueMember', attribute: 'value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name on an option object that holds its value.' }, { configKey: 'displayValueMember', attribute: 'display-value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option display label.' }, { configKey: 'searchValueMember', attribute: 'search-value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name searched against (falls back to the display value).' }, { configKey: 'iconMember', attribute: 'icon-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option icon.' }, { configKey: 'subtitleMember', attribute: 'subtitle-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option subtitle.' }, { configKey: 'fullTitleMember', attribute: 'full-title-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option full/long title.' }, { configKey: 'groupMember', attribute: 'group-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name used to group options under headers.' }, { configKey: 'disabledMember', attribute: 'disabled-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that marks an option disabled.' }, // ── Tree of options (structural → reinit; optional → nullable) ─────────── { configKey: 'pathMember', attribute: 'path-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node materialized tree path.' }, { configKey: 'parentPathMember', attribute: 'parent-path-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node parent path.' }, { configKey: 'levelMember', attribute: 'level-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node depth level.' }, { configKey: 'hasChildrenMember', attribute: 'has-children-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name flagging that a node has children.' }, { configKey: 'isSelectableMember', attribute: 'is-selectable-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name marking whether a node can be selected.' }, { configKey: 'treePathSeparator', attribute: 'tree-path-separator', converter: toText({ default: '.' }), reflect: true, on: 'reinit', description: 'Separator between segments in a materialized tree path.' }, { configKey: 'isTreeEnabled', converter: toBool('tristate'), on: 'reinit', type: 'boolean', description: 'Force tree mode on/off. Property-only; when unset (null) tree mode auto-enables if a path source (path-member / getPathCallback) is present.' }, { configKey: 'checkboxMode', attribute: 'checkbox-mode', converter: toEnum(['independent', 'cascade'] as const, { default: 'independent' }), reflect: true, on: 'update', description: `Tree checkbox interaction. - \\`independent\\` (default) — toggles only the clicked node. - \\`cascade\\` — checks a node whole subtree and shows a tristate (checked / indeterminate / unchecked) box on branches. Tree + multiple only.` }, { configKey: 'cascadeSelectPolicy', attribute: 'cascade-select-policy', converter: toEnum(['rolled-up', 'leaves', 'all'] as const, { default: 'rolled-up' }), reflect: true, on: 'update', description: `In \\`cascade\\` mode, which values a selection emits (badges / form / change): - \\`rolled-up\\` (default) — minimal cover: a fully-selected subtree collapses to its root; partially-selected branches emit their individually-checked descendants. - \\`leaves\\` — only the checked leaf-level nodes. - \\`all\\` — every fully-checked node (branches and leaves).` }, // ── Enums ──────────────────────────────────────────────────────────────── { configKey: 'badgesDisplayMode', attribute: 'badges-display-mode', converter: toEnum(['badges', 'count', 'compact', 'partial', 'none'] as const, { default: 'badges' }), on: 'reinit', description: 'How the current selection is shown in the control.' }, { configKey: 'badgesPosition', attribute: 'badges-position', converter: toEnum(['top', 'bottom', 'left', 'right'] as const, { default: 'bottom' }), on: 'reinit', description: 'Where the badges/selection appear relative to the input.' }, { configKey: 'badgesThresholdMode', attribute: 'badges-threshold-mode', converter: toEnum(['count', 'partial'] as const, { default: 'count' }), on: 'update', description: 'How `badgesThreshold` is interpreted: collapse to a count badge, or keep partial badges + a \"more\" badge.' }, { configKey: 'searchInputMode', attribute: 'search-input-mode', converter: toEnum(['normal', 'readonly', 'hidden'] as const, { default: 'normal' }), on: 'reinit', description: 'Search field mode: editable, read-only, or hidden.' }, { configKey: 'searchMode', attribute: 'search-mode', converter: toEnum(['filter', 'navigate'] as const, { default: 'filter' }), on: 'reinit', description: 'Whether typing filters the list or navigates it.' }, { configKey: 'actionsLayout', attribute: 'actions-layout', converter: toEnum(['nowrap', 'wrap'] as const, { default: 'nowrap' }), on: 'reinit', description: 'Whether the action bar wraps or stays on one line.' }, { configKey: 'actionsPosition', attribute: 'actions-position', converter: toEnum(['top', 'bottom'] as const, { default: 'top' }), on: 'reinit', description: 'Whether the action bar sits above or below the list.' }, { configKey: 'actionsAlign', attribute: 'actions-align', converter: toEnum(['stretch', 'left', 'right', 'center', 'space-between'] as const, { default: 'stretch' }), on: 'update', description: 'Horizontal alignment of the action buttons.' }, { configKey: 'checkboxAlign', attribute: 'checkbox-align', converter: toEnum(['top', 'center', 'bottom'] as const, { default: 'center' }), on: 'update', description: 'Vertical alignment of an option checkbox.' }, { configKey: 'valueFormat', attribute: 'value-format', converter: toEnum(['json', 'csv', 'array'] as const, { default: 'json' }), on: 'reinit', description: 'Serialization format the control emits its value in.' }, { configKey: 'badgeTooltipPlacement', attribute: 'badge-tooltip-placement', converter: toEnum(PLACEMENTS, { default: 'top' }), on: 'update', description: 'Preferred placement of a badge tooltip relative to its badge (floating-ui placement).' }, { configKey: 'optionTooltipPlacement', attribute: 'option-tooltip-placement', converter: toEnum(PLACEMENTS, { default: 'top-start' }), on: 'update', description: 'Preferred placement of an option tooltip (floating-ui placement).' }, { configKey: 'mobilePresentation', attribute: 'mobile-presentation', converter: toEnum(['auto', 'floating', 'fullscreen'] as const, { default: 'auto' }), reflect: true, on: 'update', description: 'How the open dropdown is presented on phones. `auto` (default) keeps the floating panel on desktop/tablet and switches to a full-screen overlay on phone-sized touch devices (touch primary + shorter viewport side < 600px, orientation-robust); `floating` forces the anchored panel everywhere; `fullscreen` forces the full-screen overlay on any device (handy for previews/testing). Resolved reactively from the device/viewport environment.' }, { configKey: 'fullscreenAutofocus', attribute: 'fullscreen-autofocus', converter: toBool('default-false'), on: 'update', description: 'In the phone fullscreen overlay, auto-focus the search field on open (pops the soft keyboard immediately). Default `false`: the sheet opens with the list visible and the keyboard closed, appearing only when the user taps the search. Set `true` to type-to-filter right away. No effect in the floating presentation.' }, // ── Numbers ────────────────────────────────────────────────────────────── { configKey: 'badgesThreshold', attribute: 'badges-threshold', converter: toInt(), on: 'update', description: 'Threshold at which badges collapse to a count/compact view.' }, { configKey: 'badgesMaxVisible', attribute: 'badges-max-visible', converter: toInt(), on: 'update', description: 'Maximum number of badges rendered before overflow.' }, { configKey: 'minSearchLength', attribute: 'min-search-length', converter: toInt({ default: 0 }), on: 'update', description: 'Minimum characters before searching/filtering starts.' }, { configKey: 'searchDebounce', attribute: 'search-debounce', converter: toInt({ default: 0 }), on: 'update', description: 'Debounce delay in ms applied to the search input.' }, { configKey: 'virtualScrollThreshold', attribute: 'virtual-scroll-threshold', converter: toInt({ default: 100 }), on: 'reinit', description: 'Option count above which virtual scrolling turns on.' }, { configKey: 'optionHeight', attribute: 'option-height', converter: toInt({ default: 50 }), on: 'update', description: 'Fixed row height in px used by virtual scrolling.' }, { configKey: 'badgeHeight', attribute: 'badge-height', converter: toInt({ default: 36 }), on: 'update', description: 'Fixed badge height in px used for layout/virtualization.' }, { configKey: 'virtualScrollBuffer', attribute: 'virtual-scroll-buffer', converter: toInt({ default: 10 }), on: 'update', description: 'Extra rows rendered above/below the viewport when virtualizing.' }, { configKey: 'badgeTooltipDelay', attribute: 'badge-tooltip-delay', converter: toInt({ default: 100 }), on: 'update', description: 'Delay in ms before a badge tooltip appears.' }, { configKey: 'badgeTooltipOffset', attribute: 'badge-tooltip-offset', converter: toInt({ default: 8 }), on: 'update', description: 'Gap in px between a badge and its tooltip.' }, { configKey: 'optionTooltipDelay', attribute: 'option-tooltip-delay', converter: toInt(), on: 'update', description: 'Delay in ms before an option tooltip appears (falls back to badgeTooltipDelay).' }, { configKey: 'optionTooltipOffset', attribute: 'option-tooltip-offset', converter: toInt(), on: 'update', description: 'Gap in px between an option and its tooltip.' }, // ── Booleans (default true) ────────────────────────────────────────────── { configKey: 'isMultipleEnabled', attribute: 'multiple', converter: toBool('default-true'), on: 'reinit', description: 'Allow selecting multiple options. When off, selecting one replaces the previous.' }, { configKey: 'isGroupsAllowed', attribute: 'allow-groups', converter: toBool('default-true'), on: 'reinit', description: 'Allow grouping options under group headers.' }, { configKey: 'isCheckboxesShown', attribute: 'show-checkboxes', converter: toBool('default-true'), on: 'reinit', description: 'Show a checkbox on each option.' }, { configKey: 'isActionsSticky', attribute: 'sticky-actions', converter: toBool('default-true'), on: 'update', description: 'Keep the action bar pinned while the list scrolls.' }, { configKey: 'isPlacementLocked', attribute: 'lock-placement', converter: toBool('default-true'), on: 'update', description: 'Keep the dropdown initial placement instead of flipping when it fits.' }, { configKey: 'isSearchEnabled', attribute: 'enable-search', converter: toBool('default-true'), on: 'reinit', description: 'Show the search input.' }, { configKey: 'isKeepOptionsOnSearch', attribute: 'keep-options-on-search', converter: toBool('default-true'), on: 'update', description: 'Keep already-selected options visible while filtering.' }, { configKey: 'shouldKeepSearchOnClose', attribute: 'should-keep-search-on-close', converter: toBool('default-true'), on: 'update', description: 'Preserve the search text after the dropdown closes.' }, // ── Booleans (default false) ───────────────────────────────────────────── { configKey: 'isCloseOnSelect', attribute: 'close-on-select', converter: toBool('default-false'), on: 'update', description: 'Close the dropdown immediately after a selection.' }, { configKey: 'isAddNewAllowed', attribute: 'allow-add-new', converter: toBool('default-false'), on: 'reinit', description: 'Allow adding a new option from the search text.' }, { configKey: 'isCounterShown', attribute: 'show-counter', converter: toBool('default-false'), on: 'update', description: 'Show a selected-count indicator.' }, { configKey: 'isBadgeFullTitleShown', attribute: 'show-badge-full-title', converter: toBool('default-false'), on: 'update', description: 'Show the full title on badges instead of the short label.' }, { configKey: 'isVirtualScrollEnabled', attribute: 'enable-virtual-scroll', converter: toBool('default-false'), on: 'reinit', description: 'Force virtual scrolling on regardless of the threshold.' }, { configKey: 'isBadgeTooltipsEnabled', attribute: 'enable-badge-tooltips', converter: toBool('default-false'), on: 'update', description: 'Enable tooltips on badges.' }, { configKey: 'isOptionTooltipsEnabled', attribute: 'enable-option-tooltips', converter: toBool('default-false'), on: 'update', description: 'Enable tooltips on options.' }, { configKey: 'isOptionTooltipFollowCursor', attribute: 'option-tooltip-follow-cursor', converter: toBool('default-false'), on: 'update', description: 'Make option tooltips follow the pointer.' }, { configKey: 'isSearchModeToggleShown', attribute: 'show-search-mode-toggle', converter: toBool('default-false'), on: 'update', description: 'Show a clickable toggle in the phone fullscreen overlay search header that flips `search-mode` between `filter` and `navigate` live. Fullscreen-only; no effect in the floating presentation or when search is disabled.' }, // ── Special attributes ─────────────────────────────────────────────────── { configKey: 'initialValues', attribute: 'initial-values', converter: toInitialValues(), default: [], on: 'reinit', type: 'Array<string | number>', description: 'Values selected on first render. Accepts a JSON array (`[\"a\",\"b\"]`) or a bare CSV (`a,b,c`).' }, { configKey: 'showDebugInfo', attribute: 'show-debug-info', converter: toBool('default-false'), on: 'update', description: 'Render an in-component debug panel.', deprecated: 'Use per-instance logging (el.enableLogging()) instead.' }, // ── Complex property (data) ────────────────────────────────────────────── { configKey: 'options', converter: toObjectArray(), on: 'reinit', type: 'ReadonlyArray<Record<string, unknown>>', description: 'The array of option objects to render. The JS API — assign `el.options` directly. For HTML authoring use the `data-options` attribute (parsed per `data-options-format`) or declarative <option> children; both feed the same list and take precedence over this property in the order: <option> children > property > data-options.' }, { configKey: 'optionsSource', attribute: 'data-options', converter: toText({ isNullable: true }), on: 'reinit', type: 'string', description: 'HTML-authoring source for the option list, parsed per `data-options-format`. Reactive: changing either attribute re-renders. Prefer the `options` property in JS; a set `options` property and declarative <option> children both win over this.' }, { configKey: 'optionsFormat', attribute: 'data-options-format', converter: toEnum(OPTIONS_FORMATS, { default: 'json' }), on: 'reinit', type: \"'json' | 'csv' | 'plain'\", description: 'How to parse the `data-options` attribute: `json` (a JSON array of objects or [value, label] tuples), `csv` (rows split on `data-options-row-splitter`, cells on `data-options-splitter`; the first row is a header — map columns via *-member), or `plain` (bare values split on both splitters -> [value, label] tuples, value === label). Default `json`.' }, { configKey: 'optionsSplitter', attribute: 'data-options-splitter', converter: toText({ default: ',' }), on: 'reinit', type: 'string', description: 'Field/cell delimiter for the `csv` and `plain` `data-options` formats. Default `,`. Escapes `\\\\t` `\\\\n` `\\\\r` are honoured (e.g. `data-options-splitter=\"\\\\t\"` for TSV). Ignored for `json`.' }, { configKey: 'optionsRowSplitter', attribute: 'data-options-row-splitter', converter: toText({ default: '\\n' }), on: 'reinit', type: 'string', description: 'Row/record delimiter for the `csv` and `plain` `data-options` formats. Default newline. Escapes honoured (e.g. `data-options-row-splitter=\";\"` for single-line data). Ignored for `json`.' }, { configKey: 'actionButtons', converter: toValue({ validate: (v): v is unknown[] => Array.isArray(v) }), on: 'reinit', type: 'Array<Record<string, unknown>>', description: 'Custom action buttons for the dropdown footer/header. Property-only; when unset the default Select-All / Clear buttons apply.' }, // ── Callbacks: data shape (structural → reinit) ────────────────────────── { configKey: 'getValueCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string | number', description: 'Extract an option value (overrides valueMember).' }, { configKey: 'getPathCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Extract a node tree path (enables tree mode; overrides pathMember).' }, { configKey: 'getGroupCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Extract the group name from an option (overrides groupMember).' }, { configKey: 'getDisabledCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => boolean', description: 'Whether an option is disabled (overrides disabledMember).' }, { configKey: 'getIsSelectableCallback', converter: cb(), on: 'reinit', type: '(node: unknown) => boolean', description: 'Whether a tree node can be selected (overrides is-selectable-member).' }, { configKey: 'getSearchValueCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Text an option is searched against (overrides searchValueMember).' }, { configKey: 'searchCallback', converter: cb(), on: 'reinit', type: '(searchTerm: string, signal?: AbortSignal) => Promise<unknown[]>', description: 'Custom / async search; return the filtered options.' }, // ── Callbacks: display / render (cosmetic → update) ────────────────────── { configKey: 'getDisplayValueCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Compute the display label for an option (overrides displayValueMember).' }, { configKey: 'getBadgeDisplayCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Compute the text shown on an option badge.' }, { configKey: 'getBadgeClassCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | string[]', description: 'Extra CSS class(es) for an option badge.' }, { configKey: 'getIconCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Icon for an option (overrides iconMember).' }, { configKey: 'getSubtitleCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Subtitle for an option (overrides subtitleMember).' }, { configKey: 'getFullTitleCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Full title for an option (used by badges when show-badge-full-title is on).' }, { configKey: 'getCounterCallback', converter: cb(), on: 'update', type: '(count: number, moreCount?: number) => string', description: 'Render the selected-count label.' }, { configKey: 'getValueFormatCallback', converter: cb(), on: 'update', type: '(selectedValues: (string | number)[]) => string', description: 'Serialize the selected values for form submission.' }, { configKey: 'getBadgeTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Tooltip content for an option badge.' }, { configKey: 'getOptionTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Tooltip content for an option row.' }, { configKey: 'getRemoveButtonTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Tooltip text for a badge remove button.' }, { configKey: 'getSelectedItemClassCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | string[]', description: 'Extra CSS class(es) for a selected item.' }, { configKey: 'renderOptionContentCallback', converter: cb(), on: 'update', type: '(item: unknown, context: OptionContentRenderContext) => string | HTMLElement', description: 'Custom render for an option row; may return HTML or an element.' }, { configKey: 'renderBadgeContentCallback', converter: cb(), on: 'update', type: '(item: unknown, context: BadgeContentRenderContext) => string | HTMLElement', description: 'Custom render for a badge; may return HTML or an element.' }, { configKey: 'renderGroupLabelContentCallback', converter: cb(), on: 'update', type: '(groupName: string) => string | HTMLElement', description: 'Customize a group label; may return an HTML string or element.' }, { configKey: 'renderSelectedContentCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Custom render for the whole selected area.' }, { configKey: 'renderSelectedItemContentCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Custom render for one selected item.' }, { configKey: 'customStylesCallback', converter: cb(), on: 'update', type: '() => string', description: 'Returns a CSS string injected into the component via a replaceable style slot (§12.8).' }, // ── Callbacks: before-hooks (behavior-shaping) ─────────────────────────── { configKey: 'beforeSearchCallback', converter: cb(), on: 'update', type: '(searchTerm: string) => string | null', description: 'Runs before a search; return a rewritten term or null to veto.' }, { configKey: 'beforeSelectCallback', converter: cb(), on: 'update', type: '(option: unknown, selectedOptions: unknown[]) => boolean | string | void', description: 'Runs before selecting; return false to veto, or a string to veto and show it as a message.' }, { configKey: 'beforeDeselectCallback', converter: cb(), on: 'update', type: '(option: unknown, selectedOptions: unknown[]) => boolean | string | void', description: 'Runs before deselecting; return false to veto, or a string to veto and show it as a message.' }, { configKey: 'addNewCallback', converter: cb(), on: 'update', type: '(value: string) => unknown | Promise<unknown>', description: 'Create a new option from the typed text.' }, { configKey: 'keydownCallback', converter: cb(), on: 'update', type: '(context: MultiSelectKeydownContext) => boolean | void', description: 'Intercept keydown before built-in handling; return true to suppress the default. Gets the event, current state, and an imperative controller.' }, ]",
3452
+ "default": "[ // ── Strings (cosmetic → update). Optional ones are nullable: absent → null ─ { configKey: 'searchHint', attribute: 'search-hint', converter: toText({ isNullable: true }), on: 'update', description: 'Small hint text shown beneath the search input.' }, { configKey: 'searchPlaceholder', attribute: 'search-placeholder', converter: toText({ isNullable: true }), on: 'update', description: 'Placeholder text for the search input. When unset it defaults to \"Search...\"; if `show-search-mode-toggle` is on, the default instead becomes mode-aware (\"Search…\" in navigate, \"Filter…\" in filter). An explicit value always wins and stays fixed.' }, { configKey: 'selectPlaceholder', attribute: 'select-placeholder', converter: toText({ default: 'Pick an option...' }), on: 'update', description: 'Placeholder shown on the control when nothing is selected.' }, { configKey: 'noDataPlaceholder', attribute: 'no-data-placeholder', converter: toText({ isNullable: true }), on: 'update', description: 'Text shown when there are no options at all.' }, { configKey: 'dropdownMinWidth', attribute: 'dropdown-min-width', converter: toText({ isNullable: true }), on: 'update', description: 'Minimum width of the dropdown panel (any CSS length).' }, { configKey: 'dropdownMaxWidth', attribute: 'dropdown-max-width', converter: toText({ isNullable: true }), on: 'update', description: 'Maximum width of the dropdown panel (any CSS length).' }, { configKey: 'maxHeight', attribute: 'max-height', converter: toText({ default: '20rem' }), on: 'update', description: 'Maximum height of the dropdown list before it scrolls.' }, { configKey: 'emptyMessage', attribute: 'empty-message', converter: toText({ default: 'No results found' }), on: 'update', description: 'Message shown when a search yields no matches.' }, { configKey: 'loadingMessage', attribute: 'loading-message', converter: toText({ default: 'Loading...' }), on: 'update', description: 'Message shown while options are loading.' }, { configKey: 'removeButtonTooltipText', attribute: 'remove-button-tooltip-text', converter: toText({ isNullable: true }), on: 'update', description: 'Tooltip text for a badge remove (×) button.' }, { configKey: 'formFieldId', attribute: 'name', converter: toText({ isNullable: true }), on: 'reinit', description: 'HTML form field name/id used for the hidden input(s).' }, // ── CSS-var sugar (mirrored to a host style prop in reinit()/update()) ──── { configKey: 'dropdownWidth', attribute: 'dropdown-width', converter: toText({ isNullable: true }), on: 'update', description: 'Fixed dropdown width; mirrored to the `--ms-dropdown-width` CSS variable.' }, { configKey: 'selectedPopoverWidth', attribute: 'selected-popover-width', converter: toText({ isNullable: true }), on: 'update', description: 'Selected-items popover width; mirrored to `--ms-selected-popover-width`.' }, // ── Member properties (structural → reinit; optional → nullable) ───────── { configKey: 'valueMember', attribute: 'value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name on an option object that holds its value.' }, { configKey: 'displayValueMember', attribute: 'display-value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option display label.' }, { configKey: 'searchValueMember', attribute: 'search-value-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name searched against (falls back to the display value).' }, { configKey: 'iconMember', attribute: 'icon-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option icon.' }, { configKey: 'subtitleMember', attribute: 'subtitle-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option subtitle.' }, { configKey: 'fullTitleMember', attribute: 'full-title-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that holds an option full/long title.' }, { configKey: 'groupMember', attribute: 'group-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name used to group options under headers.' }, { configKey: 'disabledMember', attribute: 'disabled-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name that marks an option disabled.' }, // ── Tree of options (structural → reinit; optional → nullable) ─────────── { configKey: 'pathMember', attribute: 'path-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node materialized tree path.' }, { configKey: 'parentPathMember', attribute: 'parent-path-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node parent path.' }, { configKey: 'levelMember', attribute: 'level-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name holding a node depth level.' }, { configKey: 'hasChildrenMember', attribute: 'has-children-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name flagging that a node has children.' }, { configKey: 'isSelectableMember', attribute: 'is-selectable-member', converter: toText({ isNullable: true }), reflect: true, on: 'reinit', description: 'Property name marking whether a node can be selected.' }, { configKey: 'treePathSeparator', attribute: 'tree-path-separator', converter: toText({ default: '.' }), reflect: true, on: 'reinit', description: 'Separator between segments in a materialized tree path.' }, { configKey: 'isTreeEnabled', converter: toBool('tristate'), on: 'reinit', type: 'boolean', description: 'Force tree mode on/off. Property-only; when unset (null) tree mode auto-enables if a path source (path-member / getPathCallback) is present.' }, { configKey: 'checkboxMode', attribute: 'checkbox-mode', converter: toEnum(['independent', 'cascade'] as const, { default: 'independent' }), reflect: true, on: 'update', description: `Tree checkbox interaction. - \\`independent\\` (default) — toggles only the clicked node. - \\`cascade\\` — checks a node whole subtree and shows a tristate (checked / indeterminate / unchecked) box on branches. Tree + multiple only.` }, { configKey: 'cascadeSelectPolicy', attribute: 'cascade-select-policy', converter: toEnum(['rolled-up', 'leaves', 'all'] as const, { default: 'rolled-up' }), reflect: true, on: 'update', description: `In \\`cascade\\` mode, which values a selection emits (badges / form / change): - \\`rolled-up\\` (default) — minimal cover: a fully-selected subtree collapses to its root; partially-selected branches emit their individually-checked descendants. - \\`leaves\\` — only the checked leaf-level nodes. - \\`all\\` — every fully-checked node (branches and leaves).` }, // ── Enums ──────────────────────────────────────────────────────────────── { configKey: 'badgesDisplayMode', attribute: 'badges-display-mode', converter: toEnum(['badges', 'count', 'compact', 'partial', 'none'] as const, { default: 'badges' }), on: 'reinit', description: 'How the current selection is shown in the control.' }, { configKey: 'badgesPosition', attribute: 'badges-position', converter: toEnum(['top', 'bottom', 'left', 'right'] as const, { default: 'bottom' }), on: 'reinit', description: 'Where the badges/selection appear relative to the input.' }, { configKey: 'badgesThresholdMode', attribute: 'badges-threshold-mode', converter: toEnum(['count', 'partial'] as const, { default: 'count' }), on: 'update', description: 'How `badgesThreshold` is interpreted: collapse to a count badge, or keep partial badges + a \"more\" badge.' }, { configKey: 'searchInputMode', attribute: 'search-input-mode', converter: toEnum(['normal', 'readonly', 'hidden'] as const, { default: 'normal' }), on: 'reinit', description: 'Search field mode: editable, read-only, or hidden.' }, { configKey: 'searchMode', attribute: 'search-mode', converter: toEnum(['filter', 'navigate'] as const, { default: 'filter' }), on: 'reinit', description: 'Whether typing filters the list or navigates it.' }, { configKey: 'actionsLayout', attribute: 'actions-layout', converter: toEnum(['nowrap', 'wrap'] as const, { default: 'nowrap' }), on: 'reinit', description: 'Whether the action bar wraps or stays on one line.' }, { configKey: 'actionsPosition', attribute: 'actions-position', converter: toEnum(['top', 'bottom'] as const, { default: 'top' }), on: 'reinit', description: 'Whether the action bar sits above or below the list.' }, { configKey: 'actionsAlign', attribute: 'actions-align', converter: toEnum(['stretch', 'left', 'right', 'center', 'space-between'] as const, { default: 'stretch' }), on: 'update', description: 'Horizontal alignment of the action buttons.' }, { configKey: 'checkboxAlign', attribute: 'checkbox-align', converter: toEnum(['top', 'center', 'bottom'] as const, { default: 'center' }), on: 'update', description: 'Vertical alignment of an option checkbox.' }, { configKey: 'valueFormat', attribute: 'value-format', converter: toEnum(['json', 'csv', 'array'] as const, { default: 'json' }), on: 'reinit', description: 'Serialization format the control emits its value in.' }, { configKey: 'badgeTooltipPlacement', attribute: 'badge-tooltip-placement', converter: toEnum(PLACEMENTS, { default: 'top' }), on: 'update', description: 'Preferred placement of a badge tooltip relative to its badge (floating-ui placement).' }, { configKey: 'optionTooltipPlacement', attribute: 'option-tooltip-placement', converter: toEnum(PLACEMENTS, { default: 'top-start' }), on: 'update', description: 'Preferred placement of an option tooltip (floating-ui placement).' }, { configKey: 'mobilePresentation', attribute: 'mobile-presentation', converter: toEnum(['auto', 'floating', 'fullscreen'] as const, { default: 'auto' }), reflect: true, on: 'update', description: 'How the open dropdown is presented on phones. `auto` (default) keeps the floating panel on desktop/tablet and switches to a full-screen overlay on phone-sized touch devices (touch primary + shorter viewport side < 600px, orientation-robust); `floating` forces the anchored panel everywhere; `fullscreen` forces the full-screen overlay on any device (handy for previews/testing). Resolved reactively from the device/viewport environment.' }, { configKey: 'fullscreenAutofocus', attribute: 'fullscreen-autofocus', converter: toBool('default-false'), on: 'update', description: 'In the phone fullscreen overlay, auto-focus the search field on open (pops the soft keyboard immediately). Default `false`: the sheet opens with the list visible and the keyboard closed, appearing only when the user taps the search. Set `true` to type-to-filter right away. No effect in the floating presentation.' }, // ── Numbers ────────────────────────────────────────────────────────────── { configKey: 'badgesThreshold', attribute: 'badges-threshold', converter: toInt(), on: 'update', description: 'Threshold at which badges collapse to a count/compact view.' }, { configKey: 'badgesMaxVisible', attribute: 'badges-max-visible', converter: toInt(), on: 'update', description: 'Maximum number of badges rendered before overflow.' }, { configKey: 'collapseBadgesBelow', attribute: 'collapse-badges-below', converter: toInt(), on: 'update', description: 'Container-responsive opt-in (off by default). When set to a px width, the control watches its OWN border box (not the window, via the core `resized` hook / a shared ResizeObserver) and collapses `badges-display-mode` to `count` (\"N selected\") while the box is narrower than this — so a picker in a narrow column/sidebar never overflows with pills, even on a wide monitor. Widening past the threshold restores the configured badges mode. Element-only: the override is applied to the live picker, never to your `badges-display-mode` config.' }, { configKey: 'minSearchLength', attribute: 'min-search-length', converter: toInt({ default: 0 }), on: 'update', description: 'Minimum characters before searching/filtering starts.' }, { configKey: 'searchDebounce', attribute: 'search-debounce', converter: toInt({ default: 0 }), on: 'update', description: 'Debounce delay in ms applied to the search input.' }, { configKey: 'virtualScrollThreshold', attribute: 'virtual-scroll-threshold', converter: toInt({ default: 100 }), on: 'reinit', description: 'Option count above which virtual scrolling turns on.' }, { configKey: 'optionHeight', attribute: 'option-height', converter: toInt({ default: 50 }), on: 'update', description: 'Fixed row height in px used by virtual scrolling.' }, { configKey: 'badgeHeight', attribute: 'badge-height', converter: toInt({ default: 36 }), on: 'update', description: 'Fixed badge height in px used for layout/virtualization.' }, { configKey: 'virtualScrollBuffer', attribute: 'virtual-scroll-buffer', converter: toInt({ default: 10 }), on: 'update', description: 'Extra rows rendered above/below the viewport when virtualizing.' }, { configKey: 'badgeTooltipDelay', attribute: 'badge-tooltip-delay', converter: toInt({ default: 100 }), on: 'update', description: 'Delay in ms before a badge tooltip appears.' }, { configKey: 'badgeTooltipOffset', attribute: 'badge-tooltip-offset', converter: toInt({ default: 8 }), on: 'update', description: 'Gap in px between a badge and its tooltip.' }, { configKey: 'optionTooltipDelay', attribute: 'option-tooltip-delay', converter: toInt(), on: 'update', description: 'Delay in ms before an option tooltip appears (falls back to badgeTooltipDelay).' }, { configKey: 'optionTooltipOffset', attribute: 'option-tooltip-offset', converter: toInt(), on: 'update', description: 'Gap in px between an option and its tooltip.' }, // ── Booleans (default true) ────────────────────────────────────────────── { configKey: 'isMultipleEnabled', attribute: 'multiple', converter: toBool('default-true'), on: 'reinit', description: 'Allow selecting multiple options. When off, selecting one replaces the previous.' }, { configKey: 'isGroupsAllowed', attribute: 'allow-groups', converter: toBool('default-true'), on: 'reinit', description: 'Allow grouping options under group headers.' }, { configKey: 'isCheckboxesShown', attribute: 'show-checkboxes', converter: toBool('default-true'), on: 'reinit', description: 'Show a checkbox on each option.' }, { configKey: 'isActionsSticky', attribute: 'sticky-actions', converter: toBool('default-true'), on: 'update', description: 'Keep the action bar pinned while the list scrolls.' }, { configKey: 'isPlacementLocked', attribute: 'lock-placement', converter: toBool('default-true'), on: 'update', description: 'Keep the dropdown initial placement instead of flipping when it fits.' }, { configKey: 'isSearchEnabled', attribute: 'enable-search', converter: toBool('default-true'), on: 'reinit', description: 'Show the search input.' }, { configKey: 'isKeepOptionsOnSearch', attribute: 'keep-options-on-search', converter: toBool('default-true'), on: 'update', description: 'Keep already-selected options visible while filtering.' }, { configKey: 'shouldKeepSearchOnClose', attribute: 'should-keep-search-on-close', converter: toBool('default-true'), on: 'update', description: 'Preserve the search text after the dropdown closes.' }, { configKey: 'isSelectedPopoverEnabled', attribute: 'enable-selected-popover', converter: toBool('default-true'), on: 'update', description: 'Allow the selected-items popover to open (from the count/compact/\"+X more\" badge or the in-input counter). Turn off when you render your own selection UI, so those affordances become inert.' }, // ── Booleans (default false) ───────────────────────────────────────────── { configKey: 'isCloseOnSelect', attribute: 'close-on-select', converter: toBool('default-false'), on: 'update', description: 'Close the dropdown immediately after a selection.' }, { configKey: 'isAddNewAllowed', attribute: 'allow-add-new', converter: toBool('default-false'), on: 'reinit', description: 'Allow adding a new option from the search text.' }, { configKey: 'isCounterShown', attribute: 'show-counter', converter: toBool('default-false'), on: 'update', description: 'Show a selected-count indicator.' }, { configKey: 'isClearShown', attribute: 'show-clear', converter: toBool('default-false'), on: 'update', description: 'Show an inline clear (✕) button inside the input that wipes the whole selection. Appears only while something is selected and the control is enabled; clicking it clears the selection and any search text, fires `change`, and refocuses.' }, { configKey: 'isBadgeFullTitleShown', attribute: 'show-badge-full-title', converter: toBool('default-false'), on: 'update', description: 'Show the full title on badges instead of the short label.' }, { configKey: 'isVirtualScrollEnabled', attribute: 'enable-virtual-scroll', converter: toBool('default-false'), on: 'reinit', description: 'Force virtual scrolling on regardless of the threshold.' }, { configKey: 'isBadgeTooltipsEnabled', attribute: 'enable-badge-tooltips', converter: toBool('default-false'), on: 'update', description: 'Enable tooltips on badges.' }, { configKey: 'isOptionTooltipsEnabled', attribute: 'enable-option-tooltips', converter: toBool('default-false'), on: 'update', description: 'Enable tooltips on options.' }, { configKey: 'isOptionTooltipFollowCursor', attribute: 'option-tooltip-follow-cursor', converter: toBool('default-false'), on: 'update', description: 'Make option tooltips follow the pointer.' }, { configKey: 'isSearchModeToggleShown', attribute: 'show-search-mode-toggle', converter: toBool('default-false'), on: 'update', description: 'Show a clickable toggle in the phone fullscreen overlay search header that flips `search-mode` between `filter` and `navigate` live. Fullscreen-only; no effect in the floating presentation or when search is disabled.' }, // ── Special attributes ─────────────────────────────────────────────────── { configKey: 'initialValues', attribute: 'initial-values', converter: toInitialValues(), default: [], on: 'reinit', type: 'Array<string | number>', description: 'Values selected on first render. Accepts a JSON array (`[\"a\",\"b\"]`) or a bare CSV (`a,b,c`).' }, { configKey: 'showDebugInfo', attribute: 'show-debug-info', converter: toBool('default-false'), on: 'update', description: 'Render an in-component debug panel.', deprecated: 'Use per-instance logging (el.enableLogging()) instead.' }, // ── Complex property (data) ────────────────────────────────────────────── { configKey: 'options', converter: toObjectArray(), on: 'reinit', type: 'ReadonlyArray<Record<string, unknown>>', description: 'The array of option objects to render. The JS API — assign `el.options` directly. For HTML authoring use the `data-options` attribute (parsed per `data-options-format`) or declarative <option> children; both feed the same list and take precedence over this property in the order: <option> children > property > data-options.' }, { configKey: 'optionsSource', attribute: 'data-options', converter: toText({ isNullable: true }), on: 'reinit', type: 'string', description: 'HTML-authoring source for the option list, parsed per `data-options-format`. Reactive: changing either attribute re-renders. Prefer the `options` property in JS; a set `options` property and declarative <option> children both win over this.' }, { configKey: 'optionsFormat', attribute: 'data-options-format', converter: toEnum(OPTIONS_FORMATS, { default: 'json' }), on: 'reinit', type: \"'json' | 'csv' | 'plain'\", description: 'How to parse the `data-options` attribute: `json` (a JSON array of objects or [value, label] tuples), `csv` (rows split on `data-options-row-splitter`, cells on `data-options-splitter`; the first row is a header — map columns via *-member), or `plain` (bare values split on both splitters -> [value, label] tuples, value === label). Default `json`.' }, { configKey: 'optionsSplitter', attribute: 'data-options-splitter', converter: toText({ default: ',' }), on: 'reinit', type: 'string', description: 'Field/cell delimiter for the `csv` and `plain` `data-options` formats. Default `,`. Escapes `\\\\t` `\\\\n` `\\\\r` are honoured (e.g. `data-options-splitter=\"\\\\t\"` for TSV). Ignored for `json`.' }, { configKey: 'optionsRowSplitter', attribute: 'data-options-row-splitter', converter: toText({ default: '\\n' }), on: 'reinit', type: 'string', description: 'Row/record delimiter for the `csv` and `plain` `data-options` formats. Default newline. Escapes honoured (e.g. `data-options-row-splitter=\";\"` for single-line data). Ignored for `json`.' }, { configKey: 'actionButtons', converter: toValue({ validate: (v): v is unknown[] => Array.isArray(v) }), on: 'reinit', type: 'Array<Record<string, unknown>>', description: 'Custom action buttons for the dropdown footer/header. Property-only; when unset the default Select-All / Clear buttons apply.' }, // ── Callbacks: data shape (structural → reinit) ────────────────────────── { configKey: 'getValueCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string | number', description: 'Extract an option value (overrides valueMember).' }, { configKey: 'getPathCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Extract a node tree path (enables tree mode; overrides pathMember).' }, { configKey: 'getGroupCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Extract the group name from an option (overrides groupMember).' }, { configKey: 'getDisabledCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => boolean', description: 'Whether an option is disabled (overrides disabledMember).' }, { configKey: 'getIsSelectableCallback', converter: cb(), on: 'reinit', type: '(node: unknown) => boolean', description: 'Whether a tree node can be selected (overrides is-selectable-member).' }, { configKey: 'getSearchValueCallback', converter: cb(), on: 'reinit', type: '(item: unknown) => string', description: 'Text an option is searched against (overrides searchValueMember).' }, { configKey: 'searchCallback', converter: cb(), on: 'reinit', type: '(searchTerm: string, signal?: AbortSignal) => Promise<unknown[]>', description: 'Custom / async search; return the filtered options.' }, // ── Callbacks: display / render (cosmetic → update) ────────────────────── { configKey: 'getDisplayValueCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Compute the display label for an option (overrides displayValueMember).' }, { configKey: 'getBadgeDisplayCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Compute the text shown on an option badge.' }, { configKey: 'getBadgeClassCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | string[]', description: 'Extra CSS class(es) for an option badge.' }, { configKey: 'getIconCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Icon for an option (overrides iconMember).' }, { configKey: 'getSubtitleCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Subtitle for an option (overrides subtitleMember).' }, { configKey: 'getFullTitleCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Full title for an option (used by badges when show-badge-full-title is on).' }, { configKey: 'getCounterCallback', converter: cb(), on: 'update', type: '(count: number, moreCount?: number) => string', description: 'Render the selected-count label.' }, { configKey: 'getValueFormatCallback', converter: cb(), on: 'update', type: '(selectedValues: (string | number)[]) => string', description: 'Serialize the selected values for form submission.' }, { configKey: 'getBadgeTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Tooltip content for an option badge.' }, { configKey: 'getOptionTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Tooltip content for an option row.' }, { configKey: 'getRemoveButtonTooltipCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Tooltip text for a badge remove button.' }, { configKey: 'getSelectedItemClassCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | string[]', description: 'Extra CSS class(es) for a selected item.' }, { configKey: 'renderOptionContentCallback', converter: cb(), on: 'update', type: '(item: unknown, context: OptionContentRenderContext) => string | HTMLElement', description: 'Custom render for an option row; may return HTML or an element.' }, { configKey: 'renderBadgeContentCallback', converter: cb(), on: 'update', type: '(item: unknown, context: BadgeContentRenderContext) => string | HTMLElement', description: 'Custom render for a badge content (fills the built-in pill); may return HTML or an element.' }, { configKey: 'renderBadgeCallback', converter: cb(), on: 'update', type: '(item: unknown, context: BadgeContentRenderContext) => string | HTMLElement | null', description: 'Custom render for the WHOLE badge (main area), not just its content — return the entire pill/card. The component wraps it in `.ms__badge.ms__badge--custom` with `data-value` and delegates removal to any inner element with `data-action=\"remove\"` (or `.ms__badge-remove`). Return null/empty to fall back to the default pill for that item.' }, { configKey: 'renderGroupLabelContentCallback', converter: cb(), on: 'update', type: '(groupName: string) => string | HTMLElement', description: 'Customize a group label; may return an HTML string or element.' }, { configKey: 'renderSelectedContentCallback', converter: cb(), on: 'update', type: '(item: unknown) => string', description: 'Custom render for the whole selected area.' }, { configKey: 'renderSelectedItemContentCallback', converter: cb(), on: 'update', type: '(item: unknown) => string | HTMLElement', description: 'Custom render for one selected item.' }, { configKey: 'customStylesCallback', converter: cb(), on: 'update', type: '() => string', description: 'Returns a CSS string injected into the component via a replaceable style slot (§12.8).' }, // ── Callbacks: before-hooks (behavior-shaping) ─────────────────────────── { configKey: 'beforeSearchCallback', converter: cb(), on: 'update', type: '(searchTerm: string) => string | null', description: 'Runs before a search; return a rewritten term or null to veto.' }, { configKey: 'beforeSelectCallback', converter: cb(), on: 'update', type: '(option: unknown, selectedOptions: unknown[]) => boolean | string | void', description: 'Runs before selecting; return false to veto, or a string to veto and show it as a message.' }, { configKey: 'beforeDeselectCallback', converter: cb(), on: 'update', type: '(option: unknown, selectedOptions: unknown[]) => boolean | string | void', description: 'Runs before deselecting; return false to veto, or a string to veto and show it as a message.' }, { configKey: 'addNewCallback', converter: cb(), on: 'update', type: '(value: string) => unknown | Promise<unknown>', description: 'Create a new option from the typed text.' }, { configKey: 'keydownCallback', converter: cb(), on: 'update', type: '(context: MultiSelectKeydownContext) => boolean | void', description: 'Intercept keydown before built-in handling; return true to suppress the default. Gets the event, current state, and an imperative controller.' }, ]",
3328
3453
  "type": {
3329
3454
  "text": "readonly InputDef[]"
3330
3455
  }
@@ -3552,6 +3677,84 @@
3552
3677
  ],
3553
3678
  "description": "Resolve `mobile-presentation` against `env` and relay it to the live picker."
3554
3679
  },
3680
+ {
3681
+ "kind": "field",
3682
+ "name": "#badgesCollapsed",
3683
+ "privacy": "private",
3684
+ "type": {
3685
+ "text": "boolean"
3686
+ },
3687
+ "default": "false",
3688
+ "description": "Whether the live picker is currently forced to the collapsed count view."
3689
+ },
3690
+ {
3691
+ "kind": "method",
3692
+ "name": "resized",
3693
+ "privacy": "protected",
3694
+ "return": {
3695
+ "type": {
3696
+ "text": "void"
3697
+ }
3698
+ },
3699
+ "parameters": [
3700
+ {
3701
+ "name": "{ width }",
3702
+ "type": {
3703
+ "text": "ElementSize"
3704
+ }
3705
+ }
3706
+ ],
3707
+ "description": "This element's own border box changed (core §12.9 `resized`). Overriding the\r\nhook opts us into a shared page-wide ResizeObserver, subscribed on connect and\r\ndropped on disconnect. Unlike `environmentChanged`/`viewportChanged` (the\r\nWINDOW), this is our OWN box — a picker in a 400px sidebar on a 2560px monitor\r\nreflows on its width, not the viewport's. We only act when `collapse-badges-\r\nbelow` is set; otherwise it's a cheap no-op."
3708
+ },
3709
+ {
3710
+ "kind": "method",
3711
+ "name": "#applyBadgeCollapse",
3712
+ "privacy": "private",
3713
+ "return": {
3714
+ "type": {
3715
+ "text": "void"
3716
+ }
3717
+ },
3718
+ "parameters": [
3719
+ {
3720
+ "name": "width",
3721
+ "type": {
3722
+ "text": "number"
3723
+ }
3724
+ }
3725
+ ],
3726
+ "description": "Resolve `collapse-badges-below` against `width` and relay the decision to the\r\nlive picker. The override is pushed via `updateOptions` (never written back to\r\n`this.config`), so `this.config.badgesDisplayMode` stays the consumer's truth\r\nand widening past the threshold restores it exactly. A structural JS decision,\r\nso it lives here rather than in a CSS container query."
3727
+ },
3728
+ {
3729
+ "kind": "method",
3730
+ "name": "#restoreBadgesMode",
3731
+ "privacy": "private",
3732
+ "return": {
3733
+ "type": {
3734
+ "text": "void"
3735
+ }
3736
+ },
3737
+ "description": "Re-assert the consumer's configured badges mode from the pristine base config."
3738
+ },
3739
+ {
3740
+ "kind": "method",
3741
+ "name": "#pushBadgesMode",
3742
+ "privacy": "private",
3743
+ "return": {
3744
+ "type": {
3745
+ "text": "void"
3746
+ }
3747
+ },
3748
+ "parameters": [
3749
+ {
3750
+ "name": "mode",
3751
+ "type": {
3752
+ "text": "MultiSelectConfig<T>['badgesDisplayMode']"
3753
+ }
3754
+ }
3755
+ ],
3756
+ "description": "Relay a badges-display-mode override to the live picker (never written to `this.config`)."
3757
+ },
3555
3758
  {
3556
3759
  "kind": "method",
3557
3760
  "name": "#rebuildPicker",
@@ -3835,6 +4038,44 @@
3835
4038
  },
3836
4039
  "description": "Dismiss the transient message shown by showMessage, if any."
3837
4040
  },
4041
+ {
4042
+ "kind": "method",
4043
+ "name": "open",
4044
+ "return": {
4045
+ "type": {
4046
+ "text": "void"
4047
+ }
4048
+ },
4049
+ "description": "Open the dropdown."
4050
+ },
4051
+ {
4052
+ "kind": "method",
4053
+ "name": "close",
4054
+ "return": {
4055
+ "type": {
4056
+ "text": "void"
4057
+ }
4058
+ },
4059
+ "description": "Close the dropdown."
4060
+ },
4061
+ {
4062
+ "kind": "method",
4063
+ "name": "toggle",
4064
+ "return": {
4065
+ "type": {
4066
+ "text": "void"
4067
+ }
4068
+ },
4069
+ "description": "Toggle the dropdown open/closed."
4070
+ },
4071
+ {
4072
+ "kind": "field",
4073
+ "name": "isOpen",
4074
+ "type": {
4075
+ "text": "boolean"
4076
+ },
4077
+ "description": "Whether the dropdown is currently open. Assigning opens/closes it."
4078
+ },
3838
4079
  {
3839
4080
  "kind": "method",
3840
4081
  "name": "destroy",
@@ -4340,6 +4581,16 @@
4340
4581
  "attribute": "badges-max-visible",
4341
4582
  "description": "Maximum number of badges rendered before overflow."
4342
4583
  },
4584
+ {
4585
+ "kind": "field",
4586
+ "name": "collapseBadgesBelow",
4587
+ "privacy": "public",
4588
+ "type": {
4589
+ "text": "number"
4590
+ },
4591
+ "attribute": "collapse-badges-below",
4592
+ "description": "Container-responsive opt-in (off by default). When set to a px width, the control watches its OWN border box (not the window, via the core `resized` hook / a shared ResizeObserver) and collapses `badges-display-mode` to `count` (\"N selected\") while the box is narrower than this — so a picker in a narrow column/sidebar never overflows with pills, even on a wide monitor. Widening past the threshold restores the configured badges mode. Element-only: the override is applied to the live picker, never to your `badges-display-mode` config."
4593
+ },
4343
4594
  {
4344
4595
  "kind": "field",
4345
4596
  "name": "minSearchLength",
@@ -4528,6 +4779,16 @@
4528
4779
  "attribute": "should-keep-search-on-close",
4529
4780
  "description": "Preserve the search text after the dropdown closes."
4530
4781
  },
4782
+ {
4783
+ "kind": "field",
4784
+ "name": "isSelectedPopoverEnabled",
4785
+ "privacy": "public",
4786
+ "type": {
4787
+ "text": "boolean"
4788
+ },
4789
+ "attribute": "enable-selected-popover",
4790
+ "description": "Allow the selected-items popover to open (from the count/compact/\"+X more\" badge or the in-input counter). Turn off when you render your own selection UI, so those affordances become inert."
4791
+ },
4531
4792
  {
4532
4793
  "kind": "field",
4533
4794
  "name": "isCloseOnSelect",
@@ -4558,6 +4819,16 @@
4558
4819
  "attribute": "show-counter",
4559
4820
  "description": "Show a selected-count indicator."
4560
4821
  },
4822
+ {
4823
+ "kind": "field",
4824
+ "name": "isClearShown",
4825
+ "privacy": "public",
4826
+ "type": {
4827
+ "text": "boolean"
4828
+ },
4829
+ "attribute": "show-clear",
4830
+ "description": "Show an inline clear (✕) button inside the input that wipes the whole selection. Appears only while something is selected and the control is enabled; clicking it clears the selection and any search text, fires `change`, and refocuses."
4831
+ },
4561
4832
  {
4562
4833
  "kind": "field",
4563
4834
  "name": "isBadgeFullTitleShown",
@@ -4888,7 +5159,16 @@
4888
5159
  "type": {
4889
5160
  "text": "(item: unknown, context: BadgeContentRenderContext) => string | HTMLElement"
4890
5161
  },
4891
- "description": "Custom render for a badge; may return HTML or an element."
5162
+ "description": "Custom render for a badge content (fills the built-in pill); may return HTML or an element."
5163
+ },
5164
+ {
5165
+ "kind": "field",
5166
+ "name": "renderBadgeCallback",
5167
+ "privacy": "public",
5168
+ "type": {
5169
+ "text": "(item: unknown, context: BadgeContentRenderContext) => string | HTMLElement | null"
5170
+ },
5171
+ "description": "Custom render for the WHOLE badge (main area), not just its content — return the entire pill/card. The component wraps it in `.ms__badge.ms__badge--custom` with `data-value` and delegates removal to any inner element with `data-action=\"remove\"` (or `.ms__badge-remove`). Return null/empty to fall back to the default pill for that item."
4892
5172
  },
4893
5173
  {
4894
5174
  "kind": "field",
@@ -5357,6 +5637,14 @@
5357
5637
  },
5358
5638
  "description": "Maximum number of badges rendered before overflow."
5359
5639
  },
5640
+ {
5641
+ "name": "collapse-badges-below",
5642
+ "fieldName": "collapseBadgesBelow",
5643
+ "type": {
5644
+ "text": "number"
5645
+ },
5646
+ "description": "Container-responsive opt-in (off by default). When set to a px width, the control watches its OWN border box (not the window, via the core `resized` hook / a shared ResizeObserver) and collapses `badges-display-mode` to `count` (\"N selected\") while the box is narrower than this — so a picker in a narrow column/sidebar never overflows with pills, even on a wide monitor. Widening past the threshold restores the configured badges mode. Element-only: the override is applied to the live picker, never to your `badges-display-mode` config."
5647
+ },
5360
5648
  {
5361
5649
  "name": "min-search-length",
5362
5650
  "fieldName": "minSearchLength",
@@ -5509,6 +5797,14 @@
5509
5797
  },
5510
5798
  "description": "Preserve the search text after the dropdown closes."
5511
5799
  },
5800
+ {
5801
+ "name": "enable-selected-popover",
5802
+ "fieldName": "isSelectedPopoverEnabled",
5803
+ "type": {
5804
+ "text": "boolean"
5805
+ },
5806
+ "description": "Allow the selected-items popover to open (from the count/compact/\"+X more\" badge or the in-input counter). Turn off when you render your own selection UI, so those affordances become inert."
5807
+ },
5512
5808
  {
5513
5809
  "name": "close-on-select",
5514
5810
  "fieldName": "isCloseOnSelect",
@@ -5533,6 +5829,14 @@
5533
5829
  },
5534
5830
  "description": "Show a selected-count indicator."
5535
5831
  },
5832
+ {
5833
+ "name": "show-clear",
5834
+ "fieldName": "isClearShown",
5835
+ "type": {
5836
+ "text": "boolean"
5837
+ },
5838
+ "description": "Show an inline clear (✕) button inside the input that wipes the whole selection. Appears only while something is selected and the control is enabled; clicking it clears the selection and any search text, fires `change`, and refocuses."
5839
+ },
5536
5840
  {
5537
5841
  "name": "show-badge-full-title",
5538
5842
  "fieldName": "isBadgeFullTitleShown",