@lit-material/dual-list 0.0.0-init → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bohdaq
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @lit-material/dual-list
2
+
3
+ Picks a subset of a list by moving items between "Available" and "Chosen": project members, permissions, scopes, regions. Built with [Lit](https://lit.dev/). Part of [lit-material](https://github.com/bohdaq/lit-material).
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @lit-material/dual-list @lit-material/tokens
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```html
14
+ <lit-material-dual-list-selector id="scopes" filterable chosen-label="Granted"></lit-material-dual-list-selector>
15
+ <script type="module">
16
+ import "@lit-material/dual-list";
17
+ const scopes = document.querySelector("#scopes");
18
+ scopes.items = [
19
+ { value: "vm:read", label: "Read VMs" },
20
+ { value: "vm:write", label: "Manage VMs" },
21
+ { value: "billing", label: "Billing", disabled: true },
22
+ ];
23
+ scopes.selected = ["vm:read"];
24
+ scopes.addEventListener("change", (e) => console.log(e.detail.selected));
25
+ </script>
26
+ ```
27
+
28
+ ## API
29
+
30
+ | Property | Attribute | Type | Default |
31
+ | ---------------- | ----------------- | ---------------------------------------- | ------------- |
32
+ | `items` | — | `{ value, label, disabled? }[]` | `[]` |
33
+ | `selected` | — | `string[]` (chosen values) | `[]` |
34
+ | `availableLabel` | `available-label` | `string` | `"Available"` |
35
+ | `chosenLabel` | `chosen-label` | `string` | `"Chosen"` |
36
+ | `filterable` | `filterable` | `boolean` | `false` |
37
+ | `disabled` | `disabled` (reflected) | `boolean` | `false` |
38
+
39
+ Method: `move(side, values)`. Event: `change` (`detail: { selected }`, in `items` order). Parts:
40
+ `pane`, `list`, `option`. `--lit-material-dual-list-height` sets each list's height (default `16rem`).
41
+
42
+ ## Behavior
43
+
44
+ - Click marks an item, Ctrl/⌘-click adds to the marks, Shift-click marks a range; the arrow buttons
45
+ move the marked items (or everything the filter shows), a double-click moves one item.
46
+ - Each list is a multi-select `listbox`: ↑/↓ move, Space marks, Shift+↑/↓ extends the marks,
47
+ Ctrl+A marks everything shown, Enter moves the marked (or the current) item, Home/End jump. The
48
+ header counts the marked items for screen readers.
49
+ - Disabled items show but never move. Below 600px the lists stack and the buttons sit between them.
50
+
51
+ ## License
52
+
53
+ MIT
@@ -0,0 +1,441 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "readme": "",
4
+ "modules": [
5
+ {
6
+ "kind": "javascript-module",
7
+ "path": "src/dual-list-selector.ts",
8
+ "declarations": [
9
+ {
10
+ "kind": "class",
11
+ "description": "Picks a subset of a list by moving items between \"Available\" and\n\"Chosen\": project members, permissions, scopes, regions. `items` lists\neverything; `selected` holds the chosen values.\n\nMark items with a click (Ctrl/⌘-click adds to the marks, Shift-click\nmarks a range) and move them with the arrow buttons, or double-click an\nitem to move just it. Each list is a multi-select listbox: ↑/↓ move,\nSpace marks, Shift+↑/↓ extends the marks, Ctrl+A marks everything shown,\nEnter moves the marked items, Home/End jump. `filterable` adds a filter\nfield above each list; \"move all\" moves only what the filter shows.",
12
+ "name": "LitMaterialDualListSelector",
13
+ "cssProperties": [
14
+ {
15
+ "description": "Height of each list (default `16rem`).",
16
+ "name": "--lit-material-dual-list-height"
17
+ }
18
+ ],
19
+ "cssParts": [
20
+ {
21
+ "description": "Each side (header, filter, list).",
22
+ "name": "pane"
23
+ },
24
+ {
25
+ "description": "Each listbox.",
26
+ "name": "list"
27
+ },
28
+ {
29
+ "description": "Each item.",
30
+ "name": "option"
31
+ }
32
+ ],
33
+ "members": [
34
+ {
35
+ "kind": "field",
36
+ "name": "items",
37
+ "type": {
38
+ "text": "DualListItem[]"
39
+ },
40
+ "default": "[]",
41
+ "description": "Every item, in display order."
42
+ },
43
+ {
44
+ "kind": "field",
45
+ "name": "selected",
46
+ "type": {
47
+ "text": "string[]"
48
+ },
49
+ "default": "[]",
50
+ "description": "The chosen values."
51
+ },
52
+ {
53
+ "kind": "field",
54
+ "name": "availableLabel",
55
+ "type": {
56
+ "text": "string"
57
+ },
58
+ "default": "\"Available\"",
59
+ "attribute": "available-label"
60
+ },
61
+ {
62
+ "kind": "field",
63
+ "name": "chosenLabel",
64
+ "type": {
65
+ "text": "string"
66
+ },
67
+ "default": "\"Chosen\"",
68
+ "attribute": "chosen-label"
69
+ },
70
+ {
71
+ "kind": "field",
72
+ "name": "filterable",
73
+ "type": {
74
+ "text": "boolean"
75
+ },
76
+ "default": "false",
77
+ "description": "Adds a filter field above each list.",
78
+ "attribute": "filterable"
79
+ },
80
+ {
81
+ "kind": "field",
82
+ "name": "disabled",
83
+ "type": {
84
+ "text": "boolean"
85
+ },
86
+ "default": "false",
87
+ "attribute": "disabled",
88
+ "reflects": true
89
+ },
90
+ {
91
+ "kind": "field",
92
+ "name": "marks",
93
+ "type": {
94
+ "text": "Record<Side, Set<string>>"
95
+ },
96
+ "privacy": "private",
97
+ "default": "{ available: new Set(), chosen: new Set() }"
98
+ },
99
+ {
100
+ "kind": "field",
101
+ "name": "active",
102
+ "type": {
103
+ "text": "Record<Side, string | null>"
104
+ },
105
+ "privacy": "private",
106
+ "default": "{ available: null, chosen: null }"
107
+ },
108
+ {
109
+ "kind": "field",
110
+ "name": "filters",
111
+ "type": {
112
+ "text": "Record<Side, string>"
113
+ },
114
+ "privacy": "private",
115
+ "default": "{ available: \"\", chosen: \"\" }"
116
+ },
117
+ {
118
+ "kind": "field",
119
+ "name": "anchor",
120
+ "type": {
121
+ "text": "Record<Side, string | null>"
122
+ },
123
+ "privacy": "private",
124
+ "default": "{ available: null, chosen: null }",
125
+ "description": "Where Shift-selection extends from, per side."
126
+ },
127
+ {
128
+ "kind": "field",
129
+ "name": "uid",
130
+ "privacy": "private",
131
+ "readonly": true,
132
+ "default": "`dual-list-${++nextId}`"
133
+ },
134
+ {
135
+ "kind": "method",
136
+ "name": "visible",
137
+ "privacy": "private",
138
+ "return": {
139
+ "type": {
140
+ "text": "DualListItem[]"
141
+ }
142
+ },
143
+ "parameters": [
144
+ {
145
+ "name": "side",
146
+ "type": {
147
+ "text": "Side"
148
+ }
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "kind": "method",
154
+ "name": "movable",
155
+ "privacy": "private",
156
+ "return": {
157
+ "type": {
158
+ "text": "string[]"
159
+ }
160
+ },
161
+ "parameters": [
162
+ {
163
+ "name": "side",
164
+ "type": {
165
+ "text": "Side"
166
+ }
167
+ },
168
+ {
169
+ "name": "values",
170
+ "type": {
171
+ "text": "Iterable<string>"
172
+ }
173
+ }
174
+ ]
175
+ },
176
+ {
177
+ "kind": "method",
178
+ "name": "move",
179
+ "return": {
180
+ "type": {
181
+ "text": "void"
182
+ }
183
+ },
184
+ "parameters": [
185
+ {
186
+ "name": "side",
187
+ "type": {
188
+ "text": "Side"
189
+ }
190
+ },
191
+ {
192
+ "name": "values",
193
+ "type": {
194
+ "text": "Iterable<string>"
195
+ }
196
+ }
197
+ ],
198
+ "description": "Moves `values` from `side` to the other list."
199
+ },
200
+ {
201
+ "kind": "method",
202
+ "name": "setMarks",
203
+ "privacy": "private",
204
+ "return": {
205
+ "type": {
206
+ "text": "void"
207
+ }
208
+ },
209
+ "parameters": [
210
+ {
211
+ "name": "side",
212
+ "type": {
213
+ "text": "Side"
214
+ }
215
+ },
216
+ {
217
+ "name": "marks",
218
+ "type": {
219
+ "text": "Set<string>"
220
+ }
221
+ },
222
+ {
223
+ "name": "active",
224
+ "type": {
225
+ "text": "string | null"
226
+ }
227
+ }
228
+ ]
229
+ },
230
+ {
231
+ "kind": "method",
232
+ "name": "range",
233
+ "privacy": "private",
234
+ "return": {
235
+ "type": {
236
+ "text": "Set<string>"
237
+ }
238
+ },
239
+ "parameters": [
240
+ {
241
+ "name": "side",
242
+ "type": {
243
+ "text": "Side"
244
+ }
245
+ },
246
+ {
247
+ "name": "from",
248
+ "type": {
249
+ "text": "string | null"
250
+ }
251
+ },
252
+ {
253
+ "name": "to",
254
+ "type": {
255
+ "text": "string"
256
+ }
257
+ }
258
+ ]
259
+ },
260
+ {
261
+ "kind": "method",
262
+ "name": "onOptionClick",
263
+ "privacy": "private",
264
+ "return": {
265
+ "type": {
266
+ "text": "void"
267
+ }
268
+ },
269
+ "parameters": [
270
+ {
271
+ "name": "side",
272
+ "type": {
273
+ "text": "Side"
274
+ }
275
+ },
276
+ {
277
+ "name": "item",
278
+ "type": {
279
+ "text": "DualListItem"
280
+ }
281
+ },
282
+ {
283
+ "name": "event",
284
+ "type": {
285
+ "text": "MouseEvent"
286
+ }
287
+ }
288
+ ]
289
+ },
290
+ {
291
+ "kind": "method",
292
+ "name": "onKeyDown",
293
+ "privacy": "private",
294
+ "return": {
295
+ "type": {
296
+ "text": "void"
297
+ }
298
+ },
299
+ "parameters": [
300
+ {
301
+ "name": "side",
302
+ "type": {
303
+ "text": "Side"
304
+ }
305
+ },
306
+ {
307
+ "name": "event",
308
+ "type": {
309
+ "text": "KeyboardEvent"
310
+ }
311
+ }
312
+ ]
313
+ },
314
+ {
315
+ "kind": "method",
316
+ "name": "renderPane",
317
+ "privacy": "private",
318
+ "parameters": [
319
+ {
320
+ "name": "side",
321
+ "type": {
322
+ "text": "Side"
323
+ }
324
+ }
325
+ ]
326
+ },
327
+ {
328
+ "kind": "method",
329
+ "name": "optionIndex",
330
+ "privacy": "private",
331
+ "return": {
332
+ "type": {
333
+ "text": "number"
334
+ }
335
+ },
336
+ "parameters": [
337
+ {
338
+ "name": "value",
339
+ "type": {
340
+ "text": "string"
341
+ }
342
+ }
343
+ ]
344
+ }
345
+ ],
346
+ "events": [
347
+ {
348
+ "name": "change",
349
+ "type": {
350
+ "text": "CustomEvent"
351
+ },
352
+ "description": "After a move; `detail: { selected }`."
353
+ }
354
+ ],
355
+ "attributes": [
356
+ {
357
+ "name": "available-label",
358
+ "type": {
359
+ "text": "string"
360
+ },
361
+ "default": "\"Available\"",
362
+ "fieldName": "availableLabel"
363
+ },
364
+ {
365
+ "name": "chosen-label",
366
+ "type": {
367
+ "text": "string"
368
+ },
369
+ "default": "\"Chosen\"",
370
+ "fieldName": "chosenLabel"
371
+ },
372
+ {
373
+ "name": "filterable",
374
+ "type": {
375
+ "text": "boolean"
376
+ },
377
+ "default": "false",
378
+ "description": "Adds a filter field above each list.",
379
+ "fieldName": "filterable"
380
+ },
381
+ {
382
+ "name": "disabled",
383
+ "type": {
384
+ "text": "boolean"
385
+ },
386
+ "default": "false",
387
+ "fieldName": "disabled"
388
+ }
389
+ ],
390
+ "superclass": {
391
+ "name": "LitElement",
392
+ "package": "lit"
393
+ },
394
+ "tagName": "lit-material-dual-list-selector",
395
+ "customElement": true
396
+ }
397
+ ],
398
+ "exports": [
399
+ {
400
+ "kind": "js",
401
+ "name": "LitMaterialDualListSelector",
402
+ "declaration": {
403
+ "name": "LitMaterialDualListSelector",
404
+ "module": "src/dual-list-selector.ts"
405
+ }
406
+ },
407
+ {
408
+ "kind": "custom-element-definition",
409
+ "name": "lit-material-dual-list-selector",
410
+ "declaration": {
411
+ "name": "LitMaterialDualListSelector",
412
+ "module": "src/dual-list-selector.ts"
413
+ }
414
+ }
415
+ ]
416
+ },
417
+ {
418
+ "kind": "javascript-module",
419
+ "path": "src/index.ts",
420
+ "declarations": [],
421
+ "exports": [
422
+ {
423
+ "kind": "js",
424
+ "name": "LitMaterialDualListSelector",
425
+ "declaration": {
426
+ "name": "LitMaterialDualListSelector",
427
+ "module": "./dual-list-selector.js"
428
+ }
429
+ },
430
+ {
431
+ "kind": "js",
432
+ "name": "DualListItem",
433
+ "declaration": {
434
+ "name": "DualListItem",
435
+ "module": "./dual-list-selector.js"
436
+ }
437
+ }
438
+ ]
439
+ }
440
+ ]
441
+ }
@@ -0,0 +1,68 @@
1
+ import { LitElement } from "lit";
2
+ import "@lit-material/icon-button";
3
+ import "@lit-material/text-field";
4
+ export interface DualListItem {
5
+ value: string;
6
+ label: string;
7
+ /** Can't be moved either way. */
8
+ disabled?: boolean;
9
+ }
10
+ type Side = "available" | "chosen";
11
+ /**
12
+ * Picks a subset of a list by moving items between "Available" and
13
+ * "Chosen": project members, permissions, scopes, regions. `items` lists
14
+ * everything; `selected` holds the chosen values.
15
+ *
16
+ * Mark items with a click (Ctrl/⌘-click adds to the marks, Shift-click
17
+ * marks a range) and move them with the arrow buttons, or double-click an
18
+ * item to move just it. Each list is a multi-select listbox: ↑/↓ move,
19
+ * Space marks, Shift+↑/↓ extends the marks, Ctrl+A marks everything shown,
20
+ * Enter moves the marked items, Home/End jump. `filterable` adds a filter
21
+ * field above each list; "move all" moves only what the filter shows.
22
+ *
23
+ * @element lit-material-dual-list-selector
24
+ *
25
+ * @fires change - After a move; `detail: { selected }`.
26
+ *
27
+ * @csspart pane - Each side (header, filter, list).
28
+ * @csspart list - Each listbox.
29
+ * @csspart option - Each item.
30
+ *
31
+ * @cssprop --lit-material-dual-list-height - Height of each list (default `16rem`).
32
+ */
33
+ export declare class LitMaterialDualListSelector extends LitElement {
34
+ static styles: import("lit").CSSResult;
35
+ /** Every item, in display order. */
36
+ items: DualListItem[];
37
+ /** The chosen values. */
38
+ selected: string[];
39
+ availableLabel: string;
40
+ chosenLabel: string;
41
+ /** Adds a filter field above each list. */
42
+ filterable: boolean;
43
+ disabled: boolean;
44
+ private marks;
45
+ private active;
46
+ private filters;
47
+ /** Where Shift-selection extends from, per side. */
48
+ private anchor;
49
+ private readonly uid;
50
+ private visible;
51
+ private movable;
52
+ /** Moves `values` from `side` to the other list. */
53
+ move(side: Side, values: Iterable<string>): void;
54
+ private setMarks;
55
+ private range;
56
+ private onOptionClick;
57
+ private onKeyDown;
58
+ private renderPane;
59
+ private optionIndex;
60
+ render(): import("lit").TemplateResult<1>;
61
+ }
62
+ declare global {
63
+ interface HTMLElementTagNameMap {
64
+ "lit-material-dual-list-selector": LitMaterialDualListSelector;
65
+ }
66
+ }
67
+ export {};
68
+ //# sourceMappingURL=dual-list-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dual-list-selector.d.ts","sourceRoot":"","sources":["../src/dual-list-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAGrD,OAAO,2BAA2B,CAAC;AACnC,OAAO,0BAA0B,CAAC;AAElC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,IAAI,GAAG,WAAW,GAAG,QAAQ,CAAC;AAWnC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,2BAA4B,SAAQ,UAAU;IACzD,OAAgB,MAAM,0BA8GpB;IAEF,oCAAoC;IACJ,KAAK,EAAE,YAAY,EAAE,CAAM;IAE3D,yBAAyB;IACO,QAAQ,EAAE,MAAM,EAAE,CAAM;IAEZ,cAAc,SAAe;IAEhC,WAAW,SAAY;IAEhE,2CAA2C;IACd,UAAU,UAAS;IAEJ,QAAQ,UAAS;IAEpD,OAAO,CAAC,KAAK,CAA0E;IAEvF,OAAO,CAAC,MAAM,CAAkE;IAEhF,OAAO,CAAC,OAAO,CAAuD;IAE/E,oDAAoD;IACpD,OAAO,CAAC,MAAM,CAAkE;IAEhF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA2B;IAE/C,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,OAAO;IAKf,oDAAoD;IACpD,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI;IAgBhD,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,SAAS;IAqDjB,OAAO,CAAC,UAAU;IA8DlB,OAAO,CAAC,WAAW;IAIV,MAAM;CAmChB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iCAAiC,EAAE,2BAA2B,CAAC;KAChE;CACF"}
@@ -0,0 +1,414 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { css, html, LitElement, nothing } from "lit";
8
+ import { customElement, property, state } from "lit/decorators.js";
9
+ import { repeat } from "lit/directives/repeat.js";
10
+ import "@lit-material/icon-button";
11
+ import "@lit-material/text-field";
12
+ // Material icons chevron_right, chevron_left, keyboard_double_arrow_right/left (Apache 2.0).
13
+ const svg = (d) => html `<svg viewBox="0 0 24 24" aria-hidden="true"><path d=${d}></path></svg>`;
14
+ const addIcon = svg("M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z");
15
+ const removeIcon = svg("M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z");
16
+ const addAllIcon = svg("M6.41 6 5 7.41 9.58 12 5 16.59 6.41 18l6-6zm6.59 0-1.41 1.41L16.17 12l-4.58 4.59L13 18l6-6z");
17
+ const removeAllIcon = svg("M17.59 18 19 16.59 14.42 12 19 7.41 17.59 6l-6 6zm-6.59 0L12.41 16.59 7.83 12l4.58-4.59L11 6l-6 6z");
18
+ let nextId = 0;
19
+ /**
20
+ * Picks a subset of a list by moving items between "Available" and
21
+ * "Chosen": project members, permissions, scopes, regions. `items` lists
22
+ * everything; `selected` holds the chosen values.
23
+ *
24
+ * Mark items with a click (Ctrl/⌘-click adds to the marks, Shift-click
25
+ * marks a range) and move them with the arrow buttons, or double-click an
26
+ * item to move just it. Each list is a multi-select listbox: ↑/↓ move,
27
+ * Space marks, Shift+↑/↓ extends the marks, Ctrl+A marks everything shown,
28
+ * Enter moves the marked items, Home/End jump. `filterable` adds a filter
29
+ * field above each list; "move all" moves only what the filter shows.
30
+ *
31
+ * @element lit-material-dual-list-selector
32
+ *
33
+ * @fires change - After a move; `detail: { selected }`.
34
+ *
35
+ * @csspart pane - Each side (header, filter, list).
36
+ * @csspart list - Each listbox.
37
+ * @csspart option - Each item.
38
+ *
39
+ * @cssprop --lit-material-dual-list-height - Height of each list (default `16rem`).
40
+ */
41
+ let LitMaterialDualListSelector = class LitMaterialDualListSelector extends LitElement {
42
+ constructor() {
43
+ super(...arguments);
44
+ /** Every item, in display order. */
45
+ this.items = [];
46
+ /** The chosen values. */
47
+ this.selected = [];
48
+ this.availableLabel = "Available";
49
+ this.chosenLabel = "Chosen";
50
+ /** Adds a filter field above each list. */
51
+ this.filterable = false;
52
+ this.disabled = false;
53
+ this.marks = { available: new Set(), chosen: new Set() };
54
+ this.active = { available: null, chosen: null };
55
+ this.filters = { available: "", chosen: "" };
56
+ /** Where Shift-selection extends from, per side. */
57
+ this.anchor = { available: null, chosen: null };
58
+ this.uid = `dual-list-${++nextId}`;
59
+ }
60
+ static { this.styles = css `
61
+ :host {
62
+ display: flex;
63
+ align-items: stretch;
64
+ gap: 8px;
65
+ color: var(--md-sys-color-on-surface, light-dark(#1d1b20, #e6e0e9));
66
+ font-family: var(--md-sys-typescale-body-large-font, Roboto, system-ui, sans-serif);
67
+ font-size: var(--md-sys-typescale-body-medium-size, 0.875rem);
68
+ }
69
+ :host([hidden]) {
70
+ display: none;
71
+ }
72
+ .pane {
73
+ flex: 1;
74
+ min-width: 0;
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 8px;
78
+ }
79
+ .header {
80
+ display: flex;
81
+ justify-content: space-between;
82
+ gap: 8px;
83
+ font-weight: 500;
84
+ }
85
+ .count {
86
+ color: var(--md-sys-color-on-surface-variant, light-dark(#49454f, #cac4d0));
87
+ font-weight: 400;
88
+ font-variant-numeric: tabular-nums;
89
+ }
90
+ lit-material-text-field {
91
+ --lit-material-text-field-min-width: 0;
92
+ }
93
+ .list {
94
+ box-sizing: border-box;
95
+ height: var(--lit-material-dual-list-height, 16rem);
96
+ margin: 0;
97
+ padding: 4px 0;
98
+ overflow-y: auto;
99
+ list-style: none;
100
+ border: 1px solid var(--md-sys-color-outline-variant, light-dark(#cac4d0, #49454f));
101
+ border-radius: var(--md-sys-shape-corner-small, 8px);
102
+ outline: none;
103
+ }
104
+ .list:focus-visible {
105
+ outline: 2px solid var(--md-sys-color-primary, light-dark(#6750a4, #d0bcff));
106
+ outline-offset: 2px;
107
+ }
108
+ .option {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 8px;
112
+ padding: 6px 12px;
113
+ cursor: pointer;
114
+ user-select: none;
115
+ }
116
+ .option:hover {
117
+ background: color-mix(in srgb, currentColor 8%, transparent);
118
+ }
119
+ .option[aria-selected="true"] {
120
+ background: var(--md-sys-color-secondary-container, light-dark(#e8def8, #4a4458));
121
+ color: var(--md-sys-color-on-secondary-container, light-dark(#1d192b, #e8def8));
122
+ }
123
+ .option.active {
124
+ box-shadow: inset 2px 0 0 var(--md-sys-color-primary, light-dark(#6750a4, #d0bcff));
125
+ }
126
+ .option[aria-disabled="true"] {
127
+ opacity: 0.38;
128
+ cursor: default;
129
+ }
130
+ .mark {
131
+ flex: none;
132
+ width: 16px;
133
+ height: 16px;
134
+ box-sizing: border-box;
135
+ border: 2px solid currentColor;
136
+ border-radius: 2px;
137
+ opacity: 0.6;
138
+ }
139
+ .option[aria-selected="true"] .mark {
140
+ opacity: 1;
141
+ background: currentColor;
142
+ }
143
+ .empty {
144
+ padding: 6px 12px;
145
+ color: var(--md-sys-color-on-surface-variant, light-dark(#49454f, #cac4d0));
146
+ }
147
+ .controls {
148
+ display: flex;
149
+ flex-direction: column;
150
+ justify-content: center;
151
+ gap: 4px;
152
+ }
153
+ svg {
154
+ width: 100%;
155
+ height: 100%;
156
+ fill: currentColor;
157
+ }
158
+ @media (max-width: 600px) {
159
+ :host {
160
+ flex-direction: column;
161
+ }
162
+ .controls {
163
+ flex-direction: row;
164
+ justify-content: center;
165
+ }
166
+ .controls svg {
167
+ transform: rotate(90deg);
168
+ }
169
+ }
170
+ `; }
171
+ visible(side) {
172
+ const chosen = new Set(this.selected);
173
+ const filter = this.filters[side].trim().toLowerCase();
174
+ return this.items.filter((item) => chosen.has(item.value) === (side === "chosen") && (!filter || item.label.toLowerCase().includes(filter)));
175
+ }
176
+ movable(side, values) {
177
+ const shown = new Map(this.visible(side).map((item) => [item.value, item]));
178
+ return [...values].filter((v) => shown.has(v) && !shown.get(v).disabled);
179
+ }
180
+ /** Moves `values` from `side` to the other list. */
181
+ move(side, values) {
182
+ const moving = this.movable(side, values);
183
+ if (moving.length === 0 || this.disabled)
184
+ return;
185
+ const set = new Set(this.selected);
186
+ for (const v of moving) {
187
+ if (side === "available")
188
+ set.add(v);
189
+ else
190
+ set.delete(v);
191
+ }
192
+ // Keep `items` order.
193
+ this.selected = this.items.filter((item) => set.has(item.value)).map((item) => item.value);
194
+ this.marks = { ...this.marks, [side]: new Set() };
195
+ const rest = this.visible(side);
196
+ this.active = { ...this.active, [side]: rest[0]?.value ?? null };
197
+ this.dispatchEvent(new CustomEvent("change", { bubbles: true, composed: true, detail: { selected: this.selected } }));
198
+ }
199
+ setMarks(side, marks, active) {
200
+ this.marks = { ...this.marks, [side]: marks };
201
+ this.active = { ...this.active, [side]: active };
202
+ }
203
+ range(side, from, to) {
204
+ const values = this.visible(side).filter((i) => !i.disabled).map((i) => i.value);
205
+ const a = from === null ? -1 : values.indexOf(from);
206
+ const b = values.indexOf(to);
207
+ if (a < 0 || b < 0)
208
+ return new Set([to]);
209
+ return new Set(values.slice(Math.min(a, b), Math.max(a, b) + 1));
210
+ }
211
+ onOptionClick(side, item, event) {
212
+ if (item.disabled || this.disabled)
213
+ return;
214
+ let marks;
215
+ if (event.shiftKey) {
216
+ marks = this.range(side, this.anchor[side], item.value);
217
+ }
218
+ else {
219
+ marks = new Set(event.ctrlKey || event.metaKey ? this.marks[side] : []);
220
+ if (marks.has(item.value) && (event.ctrlKey || event.metaKey))
221
+ marks.delete(item.value);
222
+ else
223
+ marks.add(item.value);
224
+ this.anchor[side] = item.value;
225
+ }
226
+ this.setMarks(side, marks, item.value);
227
+ this.renderRoot.querySelector(`#${this.uid}-${side}`)?.focus();
228
+ }
229
+ onKeyDown(side, event) {
230
+ if (this.disabled)
231
+ return;
232
+ const items = this.visible(side);
233
+ if (items.length === 0)
234
+ return;
235
+ const index = Math.max(0, items.findIndex((i) => i.value === this.active[side]));
236
+ const go = (to) => {
237
+ const target = items[Math.min(items.length - 1, Math.max(0, to))];
238
+ if (event.shiftKey) {
239
+ this.setMarks(side, this.range(side, this.anchor[side] ?? this.active[side], target.value), target.value);
240
+ }
241
+ else {
242
+ this.active = { ...this.active, [side]: target.value };
243
+ }
244
+ this.updateComplete.then(() => this.renderRoot.querySelector(`[data-value="${CSS.escape(target.value)}"][data-side="${side}"]`)?.scrollIntoView({ block: "nearest" }));
245
+ };
246
+ switch (event.key) {
247
+ case "ArrowDown":
248
+ go(this.active[side] === null ? 0 : index + 1);
249
+ break;
250
+ case "ArrowUp":
251
+ go(index - 1);
252
+ break;
253
+ case "Home":
254
+ go(0);
255
+ break;
256
+ case "End":
257
+ go(items.length - 1);
258
+ break;
259
+ case " ": {
260
+ const value = this.active[side] ?? items[0].value;
261
+ const item = items.find((i) => i.value === value);
262
+ if (!item || item.disabled)
263
+ break;
264
+ const marks = new Set(this.marks[side]);
265
+ if (marks.has(value))
266
+ marks.delete(value);
267
+ else
268
+ marks.add(value);
269
+ this.anchor[side] = value;
270
+ this.setMarks(side, marks, value);
271
+ break;
272
+ }
273
+ case "Enter":
274
+ this.move(side, this.marks[side].size ? this.marks[side] : this.active[side] ? [this.active[side]] : []);
275
+ break;
276
+ case "a":
277
+ if (!(event.ctrlKey || event.metaKey))
278
+ return;
279
+ this.setMarks(side, new Set(items.filter((i) => !i.disabled).map((i) => i.value)), this.active[side]);
280
+ break;
281
+ default:
282
+ return;
283
+ }
284
+ event.preventDefault();
285
+ }
286
+ renderPane(side) {
287
+ const label = side === "available" ? this.availableLabel : this.chosenLabel;
288
+ const items = this.visible(side);
289
+ const total = side === "chosen" ? this.selected.length : this.items.length - this.selected.length;
290
+ const marked = this.marks[side].size;
291
+ const listId = `${this.uid}-${side}`;
292
+ const active = items.some((i) => i.value === this.active[side]) ? this.active[side] : null;
293
+ return html `<div part="pane" class="pane">
294
+ <div class="header">
295
+ <span id="${listId}-label">${label}</span>
296
+ <span class="count" aria-live="polite">${marked ? `${marked} of ${total} marked` : `${total}`}</span>
297
+ </div>
298
+ ${this.filterable
299
+ ? html `<lit-material-text-field
300
+ variant="outlined"
301
+ label=${`Filter ${label.toLowerCase()}`}
302
+ type="search"
303
+ autocomplete="off"
304
+ .value=${this.filters[side]}
305
+ ?disabled=${this.disabled}
306
+ @input=${(e) => (this.filters = { ...this.filters, [side]: e.target.value })}
307
+ ></lit-material-text-field>`
308
+ : nothing}
309
+ <ul
310
+ part="list"
311
+ class="list"
312
+ id=${listId}
313
+ role="listbox"
314
+ aria-multiselectable="true"
315
+ aria-labelledby="${listId}-label"
316
+ aria-activedescendant=${active ? `${listId}-${this.optionIndex(active)}` : nothing}
317
+ aria-disabled=${this.disabled ? "true" : nothing}
318
+ tabindex=${this.disabled ? "-1" : "0"}
319
+ @keydown=${(e) => this.onKeyDown(side, e)}
320
+ @focus=${() => {
321
+ if (!this.active[side] && items[0])
322
+ this.active = { ...this.active, [side]: items[0].value };
323
+ }}
324
+ >
325
+ ${items.length === 0
326
+ ? html `<li class="empty" role="presentation">${this.filters[side] ? "No matches" : "Nothing here"}</li>`
327
+ : repeat(items, (item) => item.value, (item) => html `<li
328
+ part="option"
329
+ class=${item.value === active ? "option active" : "option"}
330
+ id="${listId}-${this.optionIndex(item.value)}"
331
+ role="option"
332
+ data-side=${side}
333
+ data-value=${item.value}
334
+ aria-selected=${this.marks[side].has(item.value) ? "true" : "false"}
335
+ aria-disabled=${item.disabled ? "true" : nothing}
336
+ @click=${(e) => this.onOptionClick(side, item, e)}
337
+ @dblclick=${() => !item.disabled && this.move(side, [item.value])}
338
+ >
339
+ <span class="mark" aria-hidden="true"></span>${item.label}
340
+ </li>`)}
341
+ </ul>
342
+ </div>`;
343
+ }
344
+ optionIndex(value) {
345
+ return this.items.findIndex((i) => i.value === value);
346
+ }
347
+ render() {
348
+ const movableMarked = (side) => this.movable(side, this.marks[side]).length > 0;
349
+ const movableAll = (side) => this.visible(side).some((i) => !i.disabled);
350
+ const off = this.disabled;
351
+ return html `
352
+ ${this.renderPane("available")}
353
+ <div class="controls" role="group" aria-label="Move items">
354
+ <lit-material-icon-button
355
+ aria-label=${`Move marked to ${this.chosenLabel.toLowerCase()}`}
356
+ ?disabled=${off || !movableMarked("available")}
357
+ @click=${() => this.move("available", this.marks.available)}
358
+ ><span slot="icon">${addIcon}</span></lit-material-icon-button
359
+ >
360
+ <lit-material-icon-button
361
+ aria-label=${`Move all shown to ${this.chosenLabel.toLowerCase()}`}
362
+ ?disabled=${off || !movableAll("available")}
363
+ @click=${() => this.move("available", this.visible("available").map((i) => i.value))}
364
+ ><span slot="icon">${addAllIcon}</span></lit-material-icon-button
365
+ >
366
+ <lit-material-icon-button
367
+ aria-label=${`Move all shown to ${this.availableLabel.toLowerCase()}`}
368
+ ?disabled=${off || !movableAll("chosen")}
369
+ @click=${() => this.move("chosen", this.visible("chosen").map((i) => i.value))}
370
+ ><span slot="icon">${removeAllIcon}</span></lit-material-icon-button
371
+ >
372
+ <lit-material-icon-button
373
+ aria-label=${`Move marked to ${this.availableLabel.toLowerCase()}`}
374
+ ?disabled=${off || !movableMarked("chosen")}
375
+ @click=${() => this.move("chosen", this.marks.chosen)}
376
+ ><span slot="icon">${removeIcon}</span></lit-material-icon-button
377
+ >
378
+ </div>
379
+ ${this.renderPane("chosen")}
380
+ `;
381
+ }
382
+ };
383
+ __decorate([
384
+ property({ attribute: false })
385
+ ], LitMaterialDualListSelector.prototype, "items", void 0);
386
+ __decorate([
387
+ property({ attribute: false })
388
+ ], LitMaterialDualListSelector.prototype, "selected", void 0);
389
+ __decorate([
390
+ property({ attribute: "available-label" })
391
+ ], LitMaterialDualListSelector.prototype, "availableLabel", void 0);
392
+ __decorate([
393
+ property({ attribute: "chosen-label" })
394
+ ], LitMaterialDualListSelector.prototype, "chosenLabel", void 0);
395
+ __decorate([
396
+ property({ type: Boolean })
397
+ ], LitMaterialDualListSelector.prototype, "filterable", void 0);
398
+ __decorate([
399
+ property({ type: Boolean, reflect: true })
400
+ ], LitMaterialDualListSelector.prototype, "disabled", void 0);
401
+ __decorate([
402
+ state()
403
+ ], LitMaterialDualListSelector.prototype, "marks", void 0);
404
+ __decorate([
405
+ state()
406
+ ], LitMaterialDualListSelector.prototype, "active", void 0);
407
+ __decorate([
408
+ state()
409
+ ], LitMaterialDualListSelector.prototype, "filters", void 0);
410
+ LitMaterialDualListSelector = __decorate([
411
+ customElement("lit-material-dual-list-selector")
412
+ ], LitMaterialDualListSelector);
413
+ export { LitMaterialDualListSelector };
414
+ //# sourceMappingURL=dual-list-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dual-list-selector.js","sourceRoot":"","sources":["../src/dual-list-selector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { LitMaterialDualListSelector, type DualListItem } from "./dual-list-selector.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { LitMaterialDualListSelector } from "./dual-list-selector.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAqB,MAAM,yBAAyB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@lit-material/dual-list",
3
- "version": "0.0.0-init",
3
+ "version": "0.1.0",
4
+ "description": "Dual list selector for lit-material: move items between available and chosen lists, with filters and full keyboard support.",
4
5
  "license": "MIT",
5
- "description": "Placeholder: real versions are published from CI."
6
+ "author": {
7
+ "name": "bohdaq",
8
+ "email": "bohdaq@gmail.com",
9
+ "url": "https://github.com/bohdaq"
10
+ },
11
+ "homepage": "https://github.com/bohdaq/lit-material#readme",
12
+ "bugs": "https://github.com/bohdaq/lit-material/issues",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+ssh://git@github.com/bohdaq/lit-material.git",
16
+ "directory": "packages/dual-list"
17
+ },
18
+ "keywords": [
19
+ "lit",
20
+ "web-components",
21
+ "lit-material",
22
+ "material-design",
23
+ "material-design-3",
24
+ "dual-list",
25
+ "transfer-list",
26
+ "listbox",
27
+ "selector"
28
+ ],
29
+ "type": "module",
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "custom-elements.json"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "dependencies": {
46
+ "lit": "^3.2.0",
47
+ "@lit-material/button": "0.1.3",
48
+ "@lit-material/icon-button": "0.0.4",
49
+ "@lit-material/text-field": "0.3.0",
50
+ "@lit-material/tokens": "0.0.2"
51
+ },
52
+ "devDependencies": {
53
+ "@lit-labs/ssr": "^3.3.1",
54
+ "@open-wc/testing": "^4.0.0",
55
+ "@web/dev-server-esbuild": "^1.0.0",
56
+ "@web/test-runner": "^0.19.0",
57
+ "@web/test-runner-commands": "^0.9.0",
58
+ "@web/test-runner-playwright": "^0.11.0",
59
+ "tsx": "^4.19.0",
60
+ "typescript": "^5.7.0"
61
+ },
62
+ "customElements": "custom-elements.json",
63
+ "scripts": {
64
+ "build": "tsc -p tsconfig.json",
65
+ "typecheck": "tsc -p tsconfig.json --noEmit",
66
+ "test": "web-test-runner && node --import tsx --test src/*.ssr.test.ts",
67
+ "analyze": "custom-elements-manifest analyze --litelement --globs \"src/**/*.ts\" --exclude \"src/**/*.test.ts\" --exclude \"src/**/*.ssr.test.ts\""
68
+ }
6
69
  }