@keenmate/web-multiselect 2.0.0-rc10 → 2.0.0-rc12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -186
- package/custom-elements.json +214 -75
- package/dist/index.d.ts +48 -3
- package/dist/multiselect.js +1342 -1222
- package/dist/multiselect.umd.js +13 -13
- package/dist/style.css +1 -1
- package/docs/usage.md +6 -0
- package/package.json +104 -104
- package/src/css/controls.css +95 -25
- package/src/css/floating.css +15 -4
- package/src/css/states.css +6 -5
- package/src/css/variables.css +59 -22
- package/vscode.html-custom-data.json +11 -1
- package/web-types.json +27 -2
package/custom-elements.json
CHANGED
|
@@ -254,11 +254,11 @@
|
|
|
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
|
{
|
|
@@ -738,6 +738,14 @@
|
|
|
738
738
|
},
|
|
739
739
|
"privacy": "private"
|
|
740
740
|
},
|
|
741
|
+
{
|
|
742
|
+
"kind": "field",
|
|
743
|
+
"name": "inputWrapper",
|
|
744
|
+
"type": {
|
|
745
|
+
"text": "HTMLDivElement"
|
|
746
|
+
},
|
|
747
|
+
"privacy": "private"
|
|
748
|
+
},
|
|
741
749
|
{
|
|
742
750
|
"kind": "field",
|
|
743
751
|
"name": "dropdown",
|
|
@@ -770,6 +778,14 @@
|
|
|
770
778
|
},
|
|
771
779
|
"privacy": "private"
|
|
772
780
|
},
|
|
781
|
+
{
|
|
782
|
+
"kind": "field",
|
|
783
|
+
"name": "clearButton",
|
|
784
|
+
"type": {
|
|
785
|
+
"text": "HTMLButtonElement"
|
|
786
|
+
},
|
|
787
|
+
"privacy": "private"
|
|
788
|
+
},
|
|
773
789
|
{
|
|
774
790
|
"kind": "field",
|
|
775
791
|
"name": "hint",
|
|
@@ -804,6 +820,15 @@
|
|
|
804
820
|
"privacy": "private",
|
|
805
821
|
"default": "null"
|
|
806
822
|
},
|
|
823
|
+
{
|
|
824
|
+
"kind": "field",
|
|
825
|
+
"name": "overlayCoord",
|
|
826
|
+
"type": {
|
|
827
|
+
"text": "OverlayHandle | null"
|
|
828
|
+
},
|
|
829
|
+
"privacy": "private",
|
|
830
|
+
"default": "null"
|
|
831
|
+
},
|
|
807
832
|
{
|
|
808
833
|
"kind": "method",
|
|
809
834
|
"name": "extractField",
|
|
@@ -823,11 +848,11 @@
|
|
|
823
848
|
{
|
|
824
849
|
"name": "opts",
|
|
825
850
|
"type": {
|
|
826
|
-
"text": "{\
|
|
851
|
+
"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 }"
|
|
827
852
|
}
|
|
828
853
|
}
|
|
829
854
|
],
|
|
830
|
-
"description": "Generic field extractor with the precedence:\
|
|
855
|
+
"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."
|
|
831
856
|
},
|
|
832
857
|
{
|
|
833
858
|
"kind": "method",
|
|
@@ -882,7 +907,7 @@
|
|
|
882
907
|
}
|
|
883
908
|
}
|
|
884
909
|
],
|
|
885
|
-
"description": "Badge display falls back to the regular display value rather than '[N/A]', so consumers can override badge\
|
|
910
|
+
"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)."
|
|
886
911
|
},
|
|
887
912
|
{
|
|
888
913
|
"kind": "method",
|
|
@@ -901,7 +926,7 @@
|
|
|
901
926
|
}
|
|
902
927
|
}
|
|
903
928
|
],
|
|
904
|
-
"description": "Full title — a fully-qualified label supplied with the data (never computed here). Used by\
|
|
929
|
+
"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."
|
|
905
930
|
},
|
|
906
931
|
{
|
|
907
932
|
"kind": "method",
|
|
@@ -1010,7 +1035,7 @@
|
|
|
1010
1035
|
}
|
|
1011
1036
|
}
|
|
1012
1037
|
],
|
|
1013
|
-
"description": "Tree mode: whether the visible node at `index` may be selected. Non-selectable\
|
|
1038
|
+
"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`."
|
|
1014
1039
|
},
|
|
1015
1040
|
{
|
|
1016
1041
|
"kind": "method",
|
|
@@ -1082,7 +1107,7 @@
|
|
|
1082
1107
|
"text": "boolean"
|
|
1083
1108
|
}
|
|
1084
1109
|
},
|
|
1085
|
-
"description": "Whether cascade checkbox mode is active: a multi-select tree with\
|
|
1110
|
+
"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."
|
|
1086
1111
|
},
|
|
1087
1112
|
{
|
|
1088
1113
|
"kind": "method",
|
|
@@ -1122,7 +1147,7 @@
|
|
|
1122
1147
|
}
|
|
1123
1148
|
}
|
|
1124
1149
|
],
|
|
1125
|
-
"description": "Toggle a tree node in cascade mode: flip its whole subtree, re-project the\
|
|
1150
|
+
"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.)."
|
|
1126
1151
|
},
|
|
1127
1152
|
{
|
|
1128
1153
|
"kind": "method",
|
|
@@ -1141,7 +1166,7 @@
|
|
|
1141
1166
|
}
|
|
1142
1167
|
}
|
|
1143
1168
|
],
|
|
1144
|
-
"description": "Given a checked-atom set, project it to emitted values under the active\
|
|
1169
|
+
"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)."
|
|
1145
1170
|
},
|
|
1146
1171
|
{
|
|
1147
1172
|
"kind": "method",
|
|
@@ -1152,7 +1177,7 @@
|
|
|
1152
1177
|
"text": "T[]"
|
|
1153
1178
|
}
|
|
1154
1179
|
},
|
|
1155
|
-
"description": "The \"meaningful selection\" list used by the counter chip — the rolled-up\
|
|
1180
|
+
"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."
|
|
1156
1181
|
},
|
|
1157
1182
|
{
|
|
1158
1183
|
"kind": "method",
|
|
@@ -1182,7 +1207,7 @@
|
|
|
1182
1207
|
"text": "void"
|
|
1183
1208
|
}
|
|
1184
1209
|
},
|
|
1185
|
-
"description": "Derive `treeNodes` + `filteredOptions` from the full tree, applying the\
|
|
1210
|
+
"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)."
|
|
1186
1211
|
},
|
|
1187
1212
|
{
|
|
1188
1213
|
"kind": "method",
|
|
@@ -1193,7 +1218,7 @@
|
|
|
1193
1218
|
"text": "void"
|
|
1194
1219
|
}
|
|
1195
1220
|
},
|
|
1196
|
-
"description": "Reset the visible list to \"everything\". **Tree-aware**: in tree mode it\
|
|
1221
|
+
"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."
|
|
1197
1222
|
},
|
|
1198
1223
|
{
|
|
1199
1224
|
"kind": "method",
|
|
@@ -1212,7 +1237,7 @@
|
|
|
1212
1237
|
}
|
|
1213
1238
|
}
|
|
1214
1239
|
],
|
|
1215
|
-
"description": "Tree mode: derive the visible list from an **external** set of matched\
|
|
1240
|
+
"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."
|
|
1216
1241
|
},
|
|
1217
1242
|
{
|
|
1218
1243
|
"kind": "method",
|
|
@@ -1223,7 +1248,7 @@
|
|
|
1223
1248
|
"text": "number"
|
|
1224
1249
|
}
|
|
1225
1250
|
},
|
|
1226
|
-
"description": "Tree + `search-mode=\"navigate\"`: keep the ENTIRE tree visible (the tree is always\
|
|
1251
|
+
"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."
|
|
1227
1252
|
},
|
|
1228
1253
|
{
|
|
1229
1254
|
"kind": "method",
|
|
@@ -1234,7 +1259,7 @@
|
|
|
1234
1259
|
"text": "void"
|
|
1235
1260
|
}
|
|
1236
1261
|
},
|
|
1237
|
-
"description": "(Re)compute `isRTL` from the host's `dir` (or an RTL ancestor) and derive the\
|
|
1262
|
+
"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."
|
|
1238
1263
|
},
|
|
1239
1264
|
{
|
|
1240
1265
|
"kind": "method",
|
|
@@ -1245,7 +1270,7 @@
|
|
|
1245
1270
|
"text": "void"
|
|
1246
1271
|
}
|
|
1247
1272
|
},
|
|
1248
|
-
"description": "Re-read `dir` and re-apply RTL mirroring live. The web-component calls this when\
|
|
1273
|
+
"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)."
|
|
1249
1274
|
},
|
|
1250
1275
|
{
|
|
1251
1276
|
"kind": "method",
|
|
@@ -1307,7 +1332,7 @@
|
|
|
1307
1332
|
"text": "void"
|
|
1308
1333
|
}
|
|
1309
1334
|
},
|
|
1310
|
-
"description": "Round the OUTER corners of the row at the very top and the row at the very\
|
|
1335
|
+
"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)."
|
|
1311
1336
|
},
|
|
1312
1337
|
{
|
|
1313
1338
|
"kind": "method",
|
|
@@ -1337,7 +1362,7 @@
|
|
|
1337
1362
|
}
|
|
1338
1363
|
}
|
|
1339
1364
|
],
|
|
1340
|
-
"description": "Default enabled/disabled state for the built-in actions, applied only when the consumer hasn't\
|
|
1365
|
+
"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."
|
|
1341
1366
|
},
|
|
1342
1367
|
{
|
|
1343
1368
|
"kind": "method",
|
|
@@ -1382,7 +1407,7 @@
|
|
|
1382
1407
|
"text": "string"
|
|
1383
1408
|
}
|
|
1384
1409
|
},
|
|
1385
|
-
"description": "Trailing info affordance for an option row, emitted only for the fullscreen\
|
|
1410
|
+
"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."
|
|
1386
1411
|
},
|
|
1387
1412
|
{
|
|
1388
1413
|
"kind": "method",
|
|
@@ -1407,7 +1432,7 @@
|
|
|
1407
1432
|
}
|
|
1408
1433
|
}
|
|
1409
1434
|
],
|
|
1410
|
-
"description": "Render a single tree-mode row. Separate from `renderOption`: a tree row is\
|
|
1435
|
+
"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."
|
|
1411
1436
|
},
|
|
1412
1437
|
{
|
|
1413
1438
|
"kind": "method",
|
|
@@ -1470,7 +1495,7 @@
|
|
|
1470
1495
|
"text": "string"
|
|
1471
1496
|
}
|
|
1472
1497
|
},
|
|
1473
|
-
"description": "Resolve the closed-state input placeholder for the current data/search state.\
|
|
1498
|
+
"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."
|
|
1474
1499
|
},
|
|
1475
1500
|
{
|
|
1476
1501
|
"kind": "method",
|
|
@@ -1481,7 +1506,7 @@
|
|
|
1481
1506
|
"text": "string"
|
|
1482
1507
|
}
|
|
1483
1508
|
},
|
|
1484
|
-
"description": "The search field placeholder. An explicit `searchPlaceholder` always wins and stays\
|
|
1509
|
+
"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)."
|
|
1485
1510
|
},
|
|
1486
1511
|
{
|
|
1487
1512
|
"kind": "method",
|
|
@@ -1541,7 +1566,7 @@
|
|
|
1541
1566
|
"text": "void"
|
|
1542
1567
|
}
|
|
1543
1568
|
},
|
|
1544
|
-
"description": "Abort the search request currently in flight, if any. The aborted request's results\
|
|
1569
|
+
"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)."
|
|
1545
1570
|
},
|
|
1546
1571
|
{
|
|
1547
1572
|
"kind": "method",
|
|
@@ -1566,7 +1591,7 @@
|
|
|
1566
1591
|
}
|
|
1567
1592
|
}
|
|
1568
1593
|
],
|
|
1569
|
-
"description": "Invoke the async `searchCallback` and apply its results. Split out of `handleSearch`\
|
|
1594
|
+
"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."
|
|
1570
1595
|
},
|
|
1571
1596
|
{
|
|
1572
1597
|
"kind": "method",
|
|
@@ -1664,7 +1689,7 @@
|
|
|
1664
1689
|
}
|
|
1665
1690
|
}
|
|
1666
1691
|
],
|
|
1667
|
-
"description": "Move focus by computing a new index from (current, total).\
|
|
1692
|
+
"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)."
|
|
1668
1693
|
},
|
|
1669
1694
|
{
|
|
1670
1695
|
"kind": "method",
|
|
@@ -1695,7 +1720,7 @@
|
|
|
1695
1720
|
}
|
|
1696
1721
|
}
|
|
1697
1722
|
],
|
|
1698
|
-
"description": "Given a target index and a preferred direction, return the nearest index\
|
|
1723
|
+
"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."
|
|
1699
1724
|
},
|
|
1700
1725
|
{
|
|
1701
1726
|
"kind": "method",
|
|
@@ -1786,7 +1811,7 @@
|
|
|
1786
1811
|
"text": "MultiSelectKeyboardController<T>"
|
|
1787
1812
|
}
|
|
1788
1813
|
},
|
|
1789
|
-
"description": "Lazily build (and cache) the imperative facade passed to `keydownCallback`. Bound to the\
|
|
1814
|
+
"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."
|
|
1790
1815
|
},
|
|
1791
1816
|
{
|
|
1792
1817
|
"kind": "method",
|
|
@@ -1797,7 +1822,7 @@
|
|
|
1797
1822
|
"text": "void"
|
|
1798
1823
|
}
|
|
1799
1824
|
},
|
|
1800
|
-
"description": "Clear the search box (both the main input and the fullscreen search) and reset the visible\
|
|
1825
|
+
"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."
|
|
1801
1826
|
},
|
|
1802
1827
|
{
|
|
1803
1828
|
"kind": "method",
|
|
@@ -1844,7 +1869,7 @@
|
|
|
1844
1869
|
}
|
|
1845
1870
|
}
|
|
1846
1871
|
],
|
|
1847
|
-
"description": "The single funnel for an interactive (user-initiated) selection. Consults\
|
|
1872
|
+
"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."
|
|
1848
1873
|
},
|
|
1849
1874
|
{
|
|
1850
1875
|
"kind": "method",
|
|
@@ -1863,7 +1888,7 @@
|
|
|
1863
1888
|
}
|
|
1864
1889
|
}
|
|
1865
1890
|
],
|
|
1866
|
-
"description": "The single funnel for an interactive (user-initiated) deselection. Every\
|
|
1891
|
+
"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."
|
|
1867
1892
|
},
|
|
1868
1893
|
{
|
|
1869
1894
|
"kind": "method",
|
|
@@ -1939,6 +1964,28 @@
|
|
|
1939
1964
|
}
|
|
1940
1965
|
}
|
|
1941
1966
|
},
|
|
1967
|
+
{
|
|
1968
|
+
"kind": "method",
|
|
1969
|
+
"name": "clearClick",
|
|
1970
|
+
"privacy": "private",
|
|
1971
|
+
"return": {
|
|
1972
|
+
"type": {
|
|
1973
|
+
"text": "void"
|
|
1974
|
+
}
|
|
1975
|
+
},
|
|
1976
|
+
"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)."
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
"kind": "method",
|
|
1980
|
+
"name": "updateClearButton",
|
|
1981
|
+
"privacy": "private",
|
|
1982
|
+
"return": {
|
|
1983
|
+
"type": {
|
|
1984
|
+
"text": "void"
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1987
|
+
"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."
|
|
1988
|
+
},
|
|
1942
1989
|
{
|
|
1943
1990
|
"kind": "method",
|
|
1944
1991
|
"name": "commit",
|
|
@@ -1956,27 +2003,45 @@
|
|
|
1956
2003
|
}
|
|
1957
2004
|
}
|
|
1958
2005
|
],
|
|
1959
|
-
"description": "Re-render and fire callbacks after a selection state change.\
|
|
2006
|
+
"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."
|
|
1960
2007
|
},
|
|
1961
2008
|
{
|
|
1962
2009
|
"kind": "method",
|
|
1963
2010
|
"name": "open",
|
|
1964
|
-
"privacy": "private",
|
|
1965
2011
|
"return": {
|
|
1966
2012
|
"type": {
|
|
1967
2013
|
"text": "void"
|
|
1968
2014
|
}
|
|
1969
|
-
}
|
|
2015
|
+
},
|
|
2016
|
+
"description": "Open the dropdown (no-op if already open, or if there is nothing to show)."
|
|
1970
2017
|
},
|
|
1971
2018
|
{
|
|
1972
2019
|
"kind": "method",
|
|
1973
2020
|
"name": "close",
|
|
1974
|
-
"privacy": "private",
|
|
1975
2021
|
"return": {
|
|
1976
2022
|
"type": {
|
|
1977
2023
|
"text": "void"
|
|
1978
2024
|
}
|
|
1979
|
-
}
|
|
2025
|
+
},
|
|
2026
|
+
"description": "Close the dropdown (no-op if already closed)."
|
|
2027
|
+
},
|
|
2028
|
+
{
|
|
2029
|
+
"kind": "method",
|
|
2030
|
+
"name": "toggle",
|
|
2031
|
+
"return": {
|
|
2032
|
+
"type": {
|
|
2033
|
+
"text": "void"
|
|
2034
|
+
}
|
|
2035
|
+
},
|
|
2036
|
+
"description": "Toggle the dropdown open/closed."
|
|
2037
|
+
},
|
|
2038
|
+
{
|
|
2039
|
+
"kind": "field",
|
|
2040
|
+
"name": "isOpen",
|
|
2041
|
+
"type": {
|
|
2042
|
+
"text": "boolean"
|
|
2043
|
+
},
|
|
2044
|
+
"description": "Whether the dropdown is currently open. Assigning opens/closes it."
|
|
1980
2045
|
},
|
|
1981
2046
|
{
|
|
1982
2047
|
"kind": "method",
|
|
@@ -1997,11 +2062,11 @@
|
|
|
1997
2062
|
{
|
|
1998
2063
|
"name": "opts",
|
|
1999
2064
|
"type": {
|
|
2000
|
-
"text": "{\
|
|
2065
|
+
"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 }"
|
|
2001
2066
|
}
|
|
2002
2067
|
}
|
|
2003
2068
|
],
|
|
2004
|
-
"description": "Anchor a floating panel (dropdown or selected-items popover) below/above the input with\
|
|
2069
|
+
"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."
|
|
2005
2070
|
},
|
|
2006
2071
|
{
|
|
2007
2072
|
"kind": "method",
|
|
@@ -2020,7 +2085,7 @@
|
|
|
2020
2085
|
}
|
|
2021
2086
|
}
|
|
2022
2087
|
],
|
|
2023
|
-
"description": "Surface a multiselect-branded, once-per-instance warning when core's drift check\
|
|
2088
|
+
"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`)."
|
|
2024
2089
|
},
|
|
2025
2090
|
{
|
|
2026
2091
|
"kind": "method",
|
|
@@ -2031,7 +2096,7 @@
|
|
|
2031
2096
|
"text": "void"
|
|
2032
2097
|
}
|
|
2033
2098
|
},
|
|
2034
|
-
"description": "Fullscreen counterpart of warnDrift. The overlay is a `position: fixed`,\
|
|
2099
|
+
"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."
|
|
2035
2100
|
},
|
|
2036
2101
|
{
|
|
2037
2102
|
"kind": "method",
|
|
@@ -2060,7 +2125,7 @@
|
|
|
2060
2125
|
}
|
|
2061
2126
|
}
|
|
2062
2127
|
],
|
|
2063
|
-
"description": "Switch how the open panels are presented. 'floating' anchors them to the input\
|
|
2128
|
+
"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."
|
|
2064
2129
|
},
|
|
2065
2130
|
{
|
|
2066
2131
|
"kind": "method",
|
|
@@ -2071,7 +2136,7 @@
|
|
|
2071
2136
|
"text": "void"
|
|
2072
2137
|
}
|
|
2073
2138
|
},
|
|
2074
|
-
"description": "Lock page scroll behind a fullscreen overlay via the core ref-counted helper.\
|
|
2139
|
+
"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."
|
|
2075
2140
|
},
|
|
2076
2141
|
{
|
|
2077
2142
|
"kind": "method",
|
|
@@ -2093,7 +2158,7 @@
|
|
|
2093
2158
|
"text": "void"
|
|
2094
2159
|
}
|
|
2095
2160
|
},
|
|
2096
|
-
"description": "Clip the host document's horizontal overflow while a fullscreen sheet is open.\
|
|
2161
|
+
"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."
|
|
2097
2162
|
},
|
|
2098
2163
|
{
|
|
2099
2164
|
"kind": "method",
|
|
@@ -2115,7 +2180,7 @@
|
|
|
2115
2180
|
"text": "void"
|
|
2116
2181
|
}
|
|
2117
2182
|
},
|
|
2118
|
-
"description": "While the fullscreen dropdown is open, keep it sitting above the soft keyboard.\
|
|
2183
|
+
"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."
|
|
2119
2184
|
},
|
|
2120
2185
|
{
|
|
2121
2186
|
"kind": "method",
|
|
@@ -2137,7 +2202,7 @@
|
|
|
2137
2202
|
"text": "number"
|
|
2138
2203
|
}
|
|
2139
2204
|
},
|
|
2140
|
-
"description": "The fullscreen size multiplier = `--ms-fullscreen-rem ÷ --ms-rem` (both read off\
|
|
2205
|
+
"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)."
|
|
2141
2206
|
},
|
|
2142
2207
|
{
|
|
2143
2208
|
"kind": "method",
|
|
@@ -2167,7 +2232,7 @@
|
|
|
2167
2232
|
}
|
|
2168
2233
|
}
|
|
2169
2234
|
],
|
|
2170
|
-
"description": "Size the virtual options scroll container for the current presentation. Applied on\
|
|
2235
|
+
"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."
|
|
2171
2236
|
},
|
|
2172
2237
|
{
|
|
2173
2238
|
"kind": "method",
|
|
@@ -2178,7 +2243,7 @@
|
|
|
2178
2243
|
"text": "number"
|
|
2179
2244
|
}
|
|
2180
2245
|
},
|
|
2181
|
-
"description": "Virtual popover badge row height (px). In the fullscreen phone view the rows are\
|
|
2246
|
+
"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."
|
|
2182
2247
|
},
|
|
2183
2248
|
{
|
|
2184
2249
|
"kind": "method",
|
|
@@ -2197,7 +2262,7 @@
|
|
|
2197
2262
|
}
|
|
2198
2263
|
}
|
|
2199
2264
|
],
|
|
2200
|
-
"description": "Clear the inline geometry that floating-ui's `anchor` writes on a panel\
|
|
2265
|
+
"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."
|
|
2201
2266
|
},
|
|
2202
2267
|
{
|
|
2203
2268
|
"kind": "method",
|
|
@@ -2230,7 +2295,7 @@
|
|
|
2230
2295
|
"text": "void"
|
|
2231
2296
|
}
|
|
2232
2297
|
},
|
|
2233
|
-
"description": "Back-gesture handling for the fullscreen sheet. On open we push a history entry\
|
|
2298
|
+
"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."
|
|
2234
2299
|
},
|
|
2235
2300
|
{
|
|
2236
2301
|
"kind": "method",
|
|
@@ -2241,7 +2306,7 @@
|
|
|
2241
2306
|
"text": "void"
|
|
2242
2307
|
}
|
|
2243
2308
|
},
|
|
2244
|
-
"description": "Back gesture/button fired: our pushed entry is already gone, so just close the\
|
|
2309
|
+
"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)."
|
|
2245
2310
|
},
|
|
2246
2311
|
{
|
|
2247
2312
|
"kind": "method",
|
|
@@ -2252,7 +2317,7 @@
|
|
|
2252
2317
|
"text": "void"
|
|
2253
2318
|
}
|
|
2254
2319
|
},
|
|
2255
|
-
"description": "Programmatic close: remove the listener and pop the entry we pushed (so the\
|
|
2320
|
+
"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)."
|
|
2256
2321
|
},
|
|
2257
2322
|
{
|
|
2258
2323
|
"kind": "method",
|
|
@@ -2263,7 +2328,7 @@
|
|
|
2263
2328
|
"text": "void"
|
|
2264
2329
|
}
|
|
2265
2330
|
},
|
|
2266
|
-
"description": "Build the fullscreen overlay header: a search field (proxying to the same\
|
|
2331
|
+
"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."
|
|
2267
2332
|
},
|
|
2268
2333
|
{
|
|
2269
2334
|
"kind": "method",
|
|
@@ -2274,7 +2339,7 @@
|
|
|
2274
2339
|
"text": "void"
|
|
2275
2340
|
}
|
|
2276
2341
|
},
|
|
2277
|
-
"description": "Build the navigate-mode match navigator (count + prev/next) and append it to the\
|
|
2342
|
+
"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)."
|
|
2278
2343
|
},
|
|
2279
2344
|
{
|
|
2280
2345
|
"kind": "method",
|
|
@@ -2315,7 +2380,7 @@
|
|
|
2315
2380
|
}
|
|
2316
2381
|
}
|
|
2317
2382
|
],
|
|
2318
|
-
"description": "Switch searchMode in place — the overlay's toggle path. The `search-mode` attribute\
|
|
2383
|
+
"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."
|
|
2319
2384
|
},
|
|
2320
2385
|
{
|
|
2321
2386
|
"kind": "method",
|
|
@@ -2326,7 +2391,7 @@
|
|
|
2326
2391
|
"text": "void"
|
|
2327
2392
|
}
|
|
2328
2393
|
},
|
|
2329
|
-
"description": "Sync the mode toggle's icon (via data-mode) and labels with the current searchMode.\
|
|
2394
|
+
"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)."
|
|
2330
2395
|
},
|
|
2331
2396
|
{
|
|
2332
2397
|
"kind": "method",
|
|
@@ -2337,7 +2402,7 @@
|
|
|
2337
2402
|
"text": "void"
|
|
2338
2403
|
}
|
|
2339
2404
|
},
|
|
2340
|
-
"description": "Sync the fullscreen match navigator (navigate mode only) with the current search\
|
|
2405
|
+
"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)."
|
|
2341
2406
|
},
|
|
2342
2407
|
{
|
|
2343
2408
|
"kind": "method",
|
|
@@ -2348,7 +2413,7 @@
|
|
|
2348
2413
|
"text": "void"
|
|
2349
2414
|
}
|
|
2350
2415
|
},
|
|
2351
|
-
"description": "Show the fullscreen search's inline clear (✕) only while the field has text.\
|
|
2416
|
+
"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)."
|
|
2352
2417
|
},
|
|
2353
2418
|
{
|
|
2354
2419
|
"kind": "method",
|
|
@@ -2359,7 +2424,7 @@
|
|
|
2359
2424
|
"text": "void"
|
|
2360
2425
|
}
|
|
2361
2426
|
},
|
|
2362
|
-
"description": "Clear the fullscreen search term via the same path a keystroke takes, then\
|
|
2427
|
+
"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."
|
|
2363
2428
|
},
|
|
2364
2429
|
{
|
|
2365
2430
|
"kind": "method",
|
|
@@ -2390,7 +2455,7 @@
|
|
|
2390
2455
|
"text": "void"
|
|
2391
2456
|
}
|
|
2392
2457
|
},
|
|
2393
|
-
"description": "Resolve any `selectedValues` entries that don't yet have a matching\
|
|
2458
|
+
"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."
|
|
2394
2459
|
},
|
|
2395
2460
|
{
|
|
2396
2461
|
"kind": "method",
|
|
@@ -2473,7 +2538,7 @@
|
|
|
2473
2538
|
}
|
|
2474
2539
|
}
|
|
2475
2540
|
],
|
|
2476
|
-
"description": "Coerce a render-callback result to an HTML string. Callbacks may return a string\
|
|
2541
|
+
"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.)"
|
|
2477
2542
|
},
|
|
2478
2543
|
{
|
|
2479
2544
|
"kind": "method",
|
|
@@ -2492,7 +2557,7 @@
|
|
|
2492
2557
|
}
|
|
2493
2558
|
}
|
|
2494
2559
|
],
|
|
2495
|
-
"description": "Normalize a class callback result (`string | string[] | null`) to a single\
|
|
2560
|
+
"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."
|
|
2496
2561
|
},
|
|
2497
2562
|
{
|
|
2498
2563
|
"kind": "method",
|
|
@@ -2517,7 +2582,7 @@
|
|
|
2517
2582
|
}
|
|
2518
2583
|
}
|
|
2519
2584
|
],
|
|
2520
|
-
"description": "Render a removable badge for a selected option (used by the badges/partial display modes\
|
|
2585
|
+
"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."
|
|
2521
2586
|
},
|
|
2522
2587
|
{
|
|
2523
2588
|
"kind": "method",
|
|
@@ -2601,7 +2666,7 @@
|
|
|
2601
2666
|
}
|
|
2602
2667
|
}
|
|
2603
2668
|
],
|
|
2604
|
-
"description": "Set the selection programmatically. **Silent by default** — it does not fire\
|
|
2669
|
+
"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."
|
|
2605
2670
|
},
|
|
2606
2671
|
{
|
|
2607
2672
|
"kind": "method",
|
|
@@ -2620,7 +2685,7 @@
|
|
|
2620
2685
|
}
|
|
2621
2686
|
}
|
|
2622
2687
|
],
|
|
2623
|
-
"description": "Merge a partial config update into the live picker without tearing down the DOM.\
|
|
2688
|
+
"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."
|
|
2624
2689
|
},
|
|
2625
2690
|
{
|
|
2626
2691
|
"kind": "field",
|
|
@@ -2663,11 +2728,11 @@
|
|
|
2663
2728
|
{
|
|
2664
2729
|
"name": "spec",
|
|
2665
2730
|
"type": {
|
|
2666
|
-
"text": "{\
|
|
2731
|
+
"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 }"
|
|
2667
2732
|
}
|
|
2668
2733
|
}
|
|
2669
2734
|
],
|
|
2670
|
-
"description": "Create or replace a tracked tooltip with the given id. Replacing destroys the old one,\
|
|
2735
|
+
"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."
|
|
2671
2736
|
},
|
|
2672
2737
|
{
|
|
2673
2738
|
"kind": "method",
|
|
@@ -2771,7 +2836,7 @@
|
|
|
2771
2836
|
"text": "void"
|
|
2772
2837
|
}
|
|
2773
2838
|
},
|
|
2774
|
-
"description": "Attach hover tooltips to the currently rendered dropdown options. Prunes existing option\
|
|
2839
|
+
"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."
|
|
2775
2840
|
},
|
|
2776
2841
|
{
|
|
2777
2842
|
"kind": "method",
|
|
@@ -2782,7 +2847,7 @@
|
|
|
2782
2847
|
"text": "void"
|
|
2783
2848
|
}
|
|
2784
2849
|
},
|
|
2785
|
-
"description": "Tag each currently-rendered fullscreen option row whose title is horizontally\
|
|
2850
|
+
"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."
|
|
2786
2851
|
},
|
|
2787
2852
|
{
|
|
2788
2853
|
"kind": "method",
|
|
@@ -2801,7 +2866,7 @@
|
|
|
2801
2866
|
}
|
|
2802
2867
|
}
|
|
2803
2868
|
],
|
|
2804
|
-
"description": "Reveal (or dismiss) the full label of a clipped fullscreen row when its info\
|
|
2869
|
+
"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."
|
|
2805
2870
|
},
|
|
2806
2871
|
{
|
|
2807
2872
|
"kind": "method",
|
|
@@ -2838,7 +2903,7 @@
|
|
|
2838
2903
|
}
|
|
2839
2904
|
}
|
|
2840
2905
|
],
|
|
2841
|
-
"description": "Show a transient message (\"toast\") on top of the component. Its reason for existing:\
|
|
2906
|
+
"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."
|
|
2842
2907
|
},
|
|
2843
2908
|
{
|
|
2844
2909
|
"kind": "method",
|
|
@@ -2860,7 +2925,7 @@
|
|
|
2860
2925
|
"text": "void"
|
|
2861
2926
|
}
|
|
2862
2927
|
},
|
|
2863
|
-
"description": "Hide (don't destroy) every currently-shown option tooltip immediately,\
|
|
2928
|
+
"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."
|
|
2864
2929
|
},
|
|
2865
2930
|
{
|
|
2866
2931
|
"kind": "method",
|
|
@@ -2871,7 +2936,7 @@
|
|
|
2871
2936
|
"text": "void"
|
|
2872
2937
|
}
|
|
2873
2938
|
},
|
|
2874
|
-
"description": "Destroy only the option tooltips (prefixed `option-`). Called before re-rendering or\
|
|
2939
|
+
"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."
|
|
2875
2940
|
},
|
|
2876
2941
|
{
|
|
2877
2942
|
"kind": "method",
|
|
@@ -2892,7 +2957,7 @@
|
|
|
2892
2957
|
"text": "void"
|
|
2893
2958
|
}
|
|
2894
2959
|
},
|
|
2895
|
-
"description": "Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`\
|
|
2960
|
+
"description": "Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`\nbefore rebuilding the actions row, so per-button tooltip state doesn't leak."
|
|
2896
2961
|
},
|
|
2897
2962
|
{
|
|
2898
2963
|
"kind": "method",
|
|
@@ -2903,7 +2968,7 @@
|
|
|
2903
2968
|
"text": "void"
|
|
2904
2969
|
}
|
|
2905
2970
|
},
|
|
2906
|
-
"description": "Destroy main-badges-container tooltips. Called before re-rendering the badges container.\
|
|
2971
|
+
"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."
|
|
2907
2972
|
},
|
|
2908
2973
|
{
|
|
2909
2974
|
"kind": "method",
|
|
@@ -3393,7 +3458,7 @@
|
|
|
3393
3458
|
"name": "inputs",
|
|
3394
3459
|
"privacy": "protected",
|
|
3395
3460
|
"static": true,
|
|
3396
|
-
"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: '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.' }, ]",
|
|
3461
|
+
"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: 'overlayGroup', attribute: 'overlay-group', converter: toText({ isNullable: true }), on: 'reinit', description: 'Scope the \"one overlay open at a time\" coordination to a named group. Overlays (multiselects, datepickers, external popovers) sharing a group dismiss each other when one opens; different groups are independent. Unset = the default (ungrouped) group.' }, { 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.' }, ]",
|
|
3397
3462
|
"type": {
|
|
3398
3463
|
"text": "readonly InputDef[]"
|
|
3399
3464
|
}
|
|
@@ -3982,6 +4047,44 @@
|
|
|
3982
4047
|
},
|
|
3983
4048
|
"description": "Dismiss the transient message shown by showMessage, if any."
|
|
3984
4049
|
},
|
|
4050
|
+
{
|
|
4051
|
+
"kind": "method",
|
|
4052
|
+
"name": "open",
|
|
4053
|
+
"return": {
|
|
4054
|
+
"type": {
|
|
4055
|
+
"text": "void"
|
|
4056
|
+
}
|
|
4057
|
+
},
|
|
4058
|
+
"description": "Open the dropdown."
|
|
4059
|
+
},
|
|
4060
|
+
{
|
|
4061
|
+
"kind": "method",
|
|
4062
|
+
"name": "close",
|
|
4063
|
+
"return": {
|
|
4064
|
+
"type": {
|
|
4065
|
+
"text": "void"
|
|
4066
|
+
}
|
|
4067
|
+
},
|
|
4068
|
+
"description": "Close the dropdown."
|
|
4069
|
+
},
|
|
4070
|
+
{
|
|
4071
|
+
"kind": "method",
|
|
4072
|
+
"name": "toggle",
|
|
4073
|
+
"return": {
|
|
4074
|
+
"type": {
|
|
4075
|
+
"text": "void"
|
|
4076
|
+
}
|
|
4077
|
+
},
|
|
4078
|
+
"description": "Toggle the dropdown open/closed."
|
|
4079
|
+
},
|
|
4080
|
+
{
|
|
4081
|
+
"kind": "field",
|
|
4082
|
+
"name": "isOpen",
|
|
4083
|
+
"type": {
|
|
4084
|
+
"text": "boolean"
|
|
4085
|
+
},
|
|
4086
|
+
"description": "Whether the dropdown is currently open. Assigning opens/closes it."
|
|
4087
|
+
},
|
|
3985
4088
|
{
|
|
3986
4089
|
"kind": "method",
|
|
3987
4090
|
"name": "destroy",
|
|
@@ -4368,6 +4471,16 @@
|
|
|
4368
4471
|
"attribute": "search-mode",
|
|
4369
4472
|
"description": "Whether typing filters the list or navigates it."
|
|
4370
4473
|
},
|
|
4474
|
+
{
|
|
4475
|
+
"kind": "field",
|
|
4476
|
+
"name": "overlayGroup",
|
|
4477
|
+
"privacy": "public",
|
|
4478
|
+
"type": {
|
|
4479
|
+
"text": "string | null"
|
|
4480
|
+
},
|
|
4481
|
+
"attribute": "overlay-group",
|
|
4482
|
+
"description": "Scope the \"one overlay open at a time\" coordination to a named group. Overlays (multiselects, datepickers, external popovers) sharing a group dismiss each other when one opens; different groups are independent. Unset = the default (ungrouped) group."
|
|
4483
|
+
},
|
|
4371
4484
|
{
|
|
4372
4485
|
"kind": "field",
|
|
4373
4486
|
"name": "actionsLayout",
|
|
@@ -4725,6 +4838,16 @@
|
|
|
4725
4838
|
"attribute": "show-counter",
|
|
4726
4839
|
"description": "Show a selected-count indicator."
|
|
4727
4840
|
},
|
|
4841
|
+
{
|
|
4842
|
+
"kind": "field",
|
|
4843
|
+
"name": "isClearShown",
|
|
4844
|
+
"privacy": "public",
|
|
4845
|
+
"type": {
|
|
4846
|
+
"text": "boolean"
|
|
4847
|
+
},
|
|
4848
|
+
"attribute": "show-clear",
|
|
4849
|
+
"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."
|
|
4850
|
+
},
|
|
4728
4851
|
{
|
|
4729
4852
|
"kind": "field",
|
|
4730
4853
|
"name": "isBadgeFullTitleShown",
|
|
@@ -5437,6 +5560,14 @@
|
|
|
5437
5560
|
"default": "'filter'",
|
|
5438
5561
|
"description": "Whether typing filters the list or navigates it."
|
|
5439
5562
|
},
|
|
5563
|
+
{
|
|
5564
|
+
"name": "overlay-group",
|
|
5565
|
+
"fieldName": "overlayGroup",
|
|
5566
|
+
"type": {
|
|
5567
|
+
"text": "string | null"
|
|
5568
|
+
},
|
|
5569
|
+
"description": "Scope the \"one overlay open at a time\" coordination to a named group. Overlays (multiselects, datepickers, external popovers) sharing a group dismiss each other when one opens; different groups are independent. Unset = the default (ungrouped) group."
|
|
5570
|
+
},
|
|
5440
5571
|
{
|
|
5441
5572
|
"name": "actions-layout",
|
|
5442
5573
|
"fieldName": "actionsLayout",
|
|
@@ -5725,6 +5856,14 @@
|
|
|
5725
5856
|
},
|
|
5726
5857
|
"description": "Show a selected-count indicator."
|
|
5727
5858
|
},
|
|
5859
|
+
{
|
|
5860
|
+
"name": "show-clear",
|
|
5861
|
+
"fieldName": "isClearShown",
|
|
5862
|
+
"type": {
|
|
5863
|
+
"text": "boolean"
|
|
5864
|
+
},
|
|
5865
|
+
"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."
|
|
5866
|
+
},
|
|
5728
5867
|
{
|
|
5729
5868
|
"name": "show-badge-full-title",
|
|
5730
5869
|
"fieldName": "isBadgeFullTitleShown",
|