@rcarls/rc-transfer-list 0.2.0 → 0.3.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/README.md CHANGED
@@ -1,6 +1,99 @@
1
1
  # @rcarls/rc-transfer-list
2
2
 
3
- Side-by-side transfer list web component built from rc-listbox.
3
+ Transfer list that enhances a native `<select multiple>` into available and selected panes.
4
+
5
+ Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-transfer-list](https://richardcarls.github.io/rc-webcomponents/components/rc-transfer-list).
6
+
7
+ The component renders two listboxes and transfer/reorder actions in between.
8
+ Available options are on the left, and selected options appear on the right. It
9
+ is an older enterprise/admin UI pattern, but solves the specific problem of
10
+ multiple selection within a large amount of options.
11
+
12
+ Modern use cases include user and permissions management tasks, multi-select for
13
+ columns / fields / filters and list re-ordering.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @rcarls/rc-transfer-list
19
+ ```
20
+
21
+ ```bash
22
+ yarn add @rcarls/rc-transfer-list
23
+ ```
24
+
25
+ ## Import
26
+
27
+ ```js
28
+ import '@rcarls/rc-transfer-list/define';
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```html
34
+ <rc-transfer-list available-label="Available planets" selected-label="Mission route">
35
+ <select name="planets" multiple>
36
+ <option value="mercury">Mercury</option>
37
+ <option value="venus" selected>Venus</option>
38
+ <option value="earth">Earth</option>
39
+ <option value="mars">Mars</option>
40
+ </select>
41
+ </rc-transfer-list>
42
+ ```
43
+
44
+ The selected/right-hand list is reflected back to the native select, so form
45
+ submission serializes selected option values in the order shown in the selected
46
+ panel.
47
+
48
+ Use the `compact` attribute when the surrounding layout should present the
49
+ available list, actions, and selected list in one column. The component does not
50
+ switch layout automatically at viewport breakpoints.
51
+
52
+ ## API
53
+
54
+ ### Properties
55
+
56
+ | Property | Attribute | Type | Default | Description |
57
+ |---|---|---|---|---|
58
+ | `multiple` | `multiple` | `boolean` | `false` | Allows multiple highlighted rows in each listbox. |
59
+ | `compact` | `compact` | `boolean` | `false` | Stacks the panels and action toolbar into a compact one-column layout. |
60
+ | `availableLabel` | `available-label` | `string` | `'Available'` | Visible label for the available list. |
61
+ | `selectedLabel` | `selected-label` | `string` | `'Selected'` | Visible label for the selected list. |
62
+ | `available` | - | `ListboxOption[]` | native select state | Gets or replaces unselected options. |
63
+ | `selected` | - | `ListboxOption[]` | native select state | Gets or replaces selected options. |
64
+ | `defaultSelected` | - | `ListboxOption[] \| undefined` | `undefined` | Initial uncontrolled selected options, applied before host `selected` writes. |
65
+
66
+ ### Methods
67
+
68
+ | Method | Description |
69
+ |---|---|
70
+ | `addSelected()` | Moves highlighted available options to the selected list. |
71
+ | `addAll()` | Moves every available option to the selected list. |
72
+ | `removeSelected()` | Moves highlighted selected options back to the available list. |
73
+ | `clearSelected()` | Moves every selected option back to the available list. |
74
+ | `moveSelected(delta)` | Reorders highlighted selected options by `-1` or `1` rows. |
75
+
76
+ ### Events
77
+
78
+ | Event | Detail | Description |
79
+ |---|---|---|
80
+ | `rc-transfer-list-change` | `{ selected: ListboxOption[] }` | Fires after a user action changes the selected/right-hand list. |
81
+
82
+ Host property writes to `available`, `selected`, or `defaultSelected` are silent.
83
+
84
+ ## Keyboard
85
+
86
+ The embedded `rc-listbox` controls provide list navigation and selection. The
87
+ transfer list also supports these shortcuts when focus is inside the component:
88
+
89
+ | Shortcut | Action |
90
+ |---|---|
91
+ | `Alt+ArrowRight` | Add highlighted available options. |
92
+ | `Alt+ArrowLeft` | Remove highlighted selected options. |
93
+ | `Alt+ArrowUp` | Move highlighted selected options up. |
94
+ | `Alt+ArrowDown` | Move highlighted selected options down. |
95
+ | `Enter` | Add the highlighted available option in single-select mode. |
96
+ | `Delete` | Remove highlighted selected options. |
4
97
 
5
98
  ## Styling Hooks
6
99
 
@@ -8,3 +101,24 @@ Panels expose `data-empty` when their backing list has no options and
8
101
  `data-has-selection` when that panel currently has highlighted rows. The root
9
102
  exposes `data-can-move-up` and `data-can-move-down` when the selected panel has
10
103
  highlighted rows that can be reordered in that direction.
104
+
105
+ | CSS custom property | Default | Description |
106
+ |---|---|---|
107
+ | `--rc-transfer-list-gap` | `var(--rc-control-gap, 0.75rem)` | Gap between panels and the action toolbar. |
108
+ | `--rc-transfer-list-panel-gap` | `var(--rc-control-gap, 0.35rem)` | Gap between a panel label and its listbox. |
109
+ | `--rc-transfer-list-listbox-min-block-size` | `10rem` | Minimum block size for each listbox. |
110
+ | `--rc-transfer-list-listbox-border` | `var(--rc-border, 1px solid ButtonBorder)` | Border around each listbox. |
111
+ | `--rc-transfer-list-option-gap` | `var(--rc-item-gap, 0.4em)` | Gap between option adornments and labels. |
112
+ | `--rc-transfer-list-option-padding-block` | `var(--rc-item-padding-block, 0.3em)` | Block padding for option rows. |
113
+ | `--rc-transfer-list-option-padding-inline` | `var(--rc-item-padding-inline, 0.75em)` | Inline padding for option rows. |
114
+ | `--rc-transfer-list-option-selected-bg` | `var(--rc-highlight, Highlight)` | Selected option background. |
115
+ | `--rc-transfer-list-option-selected-color` | `var(--rc-highlight-text, HighlightText)` | Selected option foreground. |
116
+
117
+ | Part | Description |
118
+ |---|---|
119
+ | `root` | Root layout wrapper. |
120
+ | `panel` | Shared list panel surface. |
121
+ | `available-panel` | Available/left panel. |
122
+ | `selected-panel` | Selected/right panel. |
123
+ | `actions` | Transfer action toolbar. |
124
+ | `button` | Shared action button surface. |
@@ -46,7 +46,7 @@
46
46
  "declarations": [
47
47
  {
48
48
  "kind": "class",
49
- "description": "Side-by-side transfer list built from two `rc-listbox` instances.\n\nRequires a direct child `<select multiple>` element as the single source of\ntruth for option data and form participation. Unselected `<option>` elements\npopulate the available (left) panel; selected ones populate the selected\n(right) panel. Transfers are reflected back to the `<select>` so native form\nsubmission serialises the right-panel values in user-determined order.\n\nThe `available` and `selected` JS array properties write through to the\nbacking `<select>`, so the component can also be driven imperatively from a\nframework without giving up form participation.",
49
+ "description": "Transfer list that enhances a native <select multiple> into available and selected\npanes.\n\nThe component renders two listboxes and transfer/reorder actions in between.\nAvailable options are on the left, and selected options appear on the right. It\nis an older enterprise/admin UI pattern, but solves the specific problem of\nmultiple selection within a large amount of options.\n\nThe `available` and `selected` JS array properties write through to the\nbacking `<select>`, so the component can also be driven imperatively from a\nframework without giving up form participation.",
50
50
  "name": "RCTransferList",
51
51
  "cssProperties": [
52
52
  {
@@ -68,6 +68,31 @@
68
68
  "description": "Border around each listbox",
69
69
  "name": "--rc-transfer-list-listbox-border",
70
70
  "default": "var(--rc-border)"
71
+ },
72
+ {
73
+ "description": "Gap between option adornments and labels",
74
+ "name": "--rc-transfer-list-option-gap",
75
+ "default": "var(--rc-item-gap)"
76
+ },
77
+ {
78
+ "description": "Block padding for option rows",
79
+ "name": "--rc-transfer-list-option-padding-block",
80
+ "default": "var(--rc-item-padding-block)"
81
+ },
82
+ {
83
+ "description": "Inline padding for option rows",
84
+ "name": "--rc-transfer-list-option-padding-inline",
85
+ "default": "var(--rc-item-padding-inline)"
86
+ },
87
+ {
88
+ "description": "Selected option background",
89
+ "name": "--rc-transfer-list-option-selected-bg",
90
+ "default": "var(--rc-highlight)"
91
+ },
92
+ {
93
+ "description": "Selected option foreground",
94
+ "name": "--rc-transfer-list-option-selected-color",
95
+ "default": "var(--rc-highlight-text)"
71
96
  }
72
97
  ],
73
98
  "cssParts": [
@@ -108,6 +133,17 @@
108
133
  "attribute": "multiple",
109
134
  "reflects": true
110
135
  },
136
+ {
137
+ "kind": "field",
138
+ "name": "compact",
139
+ "type": {
140
+ "text": "boolean"
141
+ },
142
+ "default": "false",
143
+ "description": "Stack the panels and action toolbar into a compact one-column layout.",
144
+ "attribute": "compact",
145
+ "reflects": true
146
+ },
111
147
  {
112
148
  "kind": "field",
113
149
  "name": "availableLabel",
@@ -166,7 +202,7 @@
166
202
  },
167
203
  {
168
204
  "kind": "field",
169
- "name": "_select",
205
+ "name": "_$select",
170
206
  "type": {
171
207
  "text": "HTMLSelectElement | null"
172
208
  },
@@ -200,17 +236,12 @@
200
236
  "privacy": "private",
201
237
  "default": "false"
202
238
  },
203
- {
204
- "kind": "field",
205
- "name": "_hostObserver",
206
- "privacy": "private",
207
- "default": "new MutationObserver(() => this._setupSelect())"
208
- },
209
239
  {
210
240
  "kind": "field",
211
241
  "name": "_selectObserver",
212
242
  "privacy": "private",
213
- "default": "new MutationObserver(() => { if (!this._syncing) this.requestUpdate(); })"
243
+ "readonly": true,
244
+ "default": "new MutationObserverController(this, { target: null, disabled: true, callback: () => { if (!this._syncing) { this.requestUpdate(); } }, })"
214
245
  },
215
246
  {
216
247
  "kind": "field",
@@ -362,6 +393,24 @@
362
393
  "name": "_onListboxChange",
363
394
  "privacy": "private"
364
395
  },
396
+ {
397
+ "kind": "method",
398
+ "name": "_getListboxEventTargetId",
399
+ "privacy": "private",
400
+ "return": {
401
+ "type": {
402
+ "text": "string | undefined"
403
+ }
404
+ },
405
+ "parameters": [
406
+ {
407
+ "name": "e",
408
+ "type": {
409
+ "text": "Event"
410
+ }
411
+ }
412
+ ]
413
+ },
365
414
  {
366
415
  "kind": "method",
367
416
  "name": "_setupSelect",
@@ -370,7 +419,15 @@
370
419
  "type": {
371
420
  "text": "void"
372
421
  }
373
- }
422
+ },
423
+ "parameters": [
424
+ {
425
+ "name": "$select",
426
+ "type": {
427
+ "text": "HTMLSelectElement | null"
428
+ }
429
+ }
430
+ ]
374
431
  },
375
432
  {
376
433
  "kind": "method",
@@ -465,6 +522,15 @@
465
522
  "description": "Enable multi-selection in both lists.",
466
523
  "fieldName": "multiple"
467
524
  },
525
+ {
526
+ "name": "compact",
527
+ "type": {
528
+ "text": "boolean"
529
+ },
530
+ "default": "false",
531
+ "description": "Stack the panels and action toolbar into a compact one-column layout.",
532
+ "fieldName": "compact"
533
+ },
468
534
  {
469
535
  "name": "available-label",
470
536
  "type": {
@@ -1,16 +1,129 @@
1
+ import { css as _, LitElement as m, nothing as c, html as y } from "lit";
2
+ import { property as p, query as f, state as v } from "lit/decorators.js";
3
+ import { MutationObserverController as $, NativeChildController as x } from "@rcarls/rc-common";
1
4
  import "@rcarls/rc-listbox/define";
2
5
  import "@rcarls/rc-toolbar/define";
3
- import { LitElement as _, nothing as c, html as m } from "lit";
4
- import { property as p, query as b, state as f } from "lit/decorators.js";
5
- var y = Object.defineProperty, n = (u, e, t, s) => {
6
+ const S = _`
7
+ :host {
8
+ display: block;
9
+ }
10
+
11
+ :host([hidden]) {
12
+ display: none;
13
+ }
14
+
15
+ #root {
16
+ display: grid;
17
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
18
+ align-items: stretch;
19
+ gap: var(--rc-transfer-list-gap, var(--rc-control-gap, 0.75rem));
20
+ }
21
+
22
+ :host([compact]) #root {
23
+ grid-template-columns: minmax(0, 1fr);
24
+ }
25
+
26
+ .rc-transfer-list-panel {
27
+ display: flex;
28
+ min-inline-size: 0;
29
+ flex-direction: column;
30
+ gap: var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem));
31
+ }
32
+
33
+ .rc-transfer-list-panel > rc-listbox {
34
+ flex: 1 1 auto;
35
+ min-block-size: var(--rc-transfer-list-listbox-min-block-size, 10rem);
36
+ overflow: auto;
37
+ border: var(--rc-transfer-list-listbox-border, var(--rc-border, 1px solid ButtonBorder));
38
+ background: var(--rc-transfer-list-listbox-bg, var(--rc-surface, Canvas));
39
+ color: var(--rc-transfer-list-listbox-color, var(--rc-field-text, FieldText));
40
+ --rc-listbox-option-gap: var(--rc-transfer-list-option-gap, var(--rc-item-gap, 0.4em));
41
+ --rc-listbox-option-padding-block: var(
42
+ --rc-transfer-list-option-padding-block,
43
+ var(--rc-item-padding-block, 0.3em)
44
+ );
45
+ --rc-listbox-option-padding-inline: var(
46
+ --rc-transfer-list-option-padding-inline,
47
+ var(--rc-item-padding-inline, 0.75em)
48
+ );
49
+ --rc-listbox-hover-bg: var(
50
+ --rc-transfer-list-option-hover-bg,
51
+ var(--rc-transfer-list-option-selected-bg, var(--rc-highlight, Highlight))
52
+ );
53
+ --rc-listbox-hover-color: var(
54
+ --rc-transfer-list-option-hover-color,
55
+ var(--rc-transfer-list-option-selected-color, var(--rc-highlight-text, HighlightText))
56
+ );
57
+ --rc-listbox-active-bg: var(
58
+ --rc-transfer-list-option-hover-bg,
59
+ var(--rc-transfer-list-option-selected-bg, var(--rc-highlight, Highlight))
60
+ );
61
+ --rc-listbox-active-color: var(
62
+ --rc-transfer-list-option-hover-color,
63
+ var(--rc-transfer-list-option-selected-color, var(--rc-highlight-text, HighlightText))
64
+ );
65
+ --rc-listbox-selected-bg: var(
66
+ --rc-transfer-list-option-selected-bg,
67
+ var(--rc-highlight, Highlight)
68
+ );
69
+ --rc-listbox-selected-color: var(
70
+ --rc-transfer-list-option-selected-color,
71
+ var(--rc-highlight-text, HighlightText)
72
+ );
73
+ --rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);
74
+ }
75
+
76
+ rc-listbox [part~='option'][hidden] {
77
+ display: none;
78
+ }
79
+
80
+ rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {
81
+ outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));
82
+ outline-offset: -2px;
83
+ }
84
+
85
+ rc-listbox [part~='option'][aria-disabled='true'] {
86
+ cursor: not-allowed;
87
+ }
88
+
89
+ #actions {
90
+ align-self: center;
91
+ transform: translateY(
92
+ calc((1lh + var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem))) / 2)
93
+ );
94
+ }
95
+
96
+ :host([compact]) #actions {
97
+ align-self: stretch;
98
+ transform: none;
99
+ }
100
+
101
+ :host([compact]) #actions::part(root) {
102
+ flex-wrap: wrap;
103
+ justify-content: center;
104
+ }
105
+
106
+ #actions button {
107
+ white-space: nowrap;
108
+ }
109
+
110
+ slot:not([name]) {
111
+ display: none;
112
+ }
113
+ `;
114
+ var w = Object.defineProperty, n = (u, e, t, s) => {
6
115
  for (var l = void 0, i = u.length - 1, r; i >= 0; i--)
7
116
  (r = u[i]) && (l = r(e, t, l) || l);
8
- return l && y(e, t, l), l;
117
+ return l && w(e, t, l), l;
9
118
  };
10
- class o extends _ {
119
+ const b = class b extends m {
11
120
  constructor() {
12
- super(...arguments), this.multiple = !1, this.availableLabel = "Available", this.selectedLabel = "Selected", this._availableSelection = [], this._selectedSelection = [], this._select = null, this._syncing = !1, this._selectedInitialized = !1, this._hostObserver = new MutationObserver(() => this._setupSelect()), this._selectObserver = new MutationObserver(() => {
13
- this._syncing || this.requestUpdate();
121
+ super(), this.multiple = !1, this.compact = !1, this.availableLabel = "Available", this.selectedLabel = "Selected", this._availableSelection = [], this._selectedSelection = [], this._$select = null, this._syncing = !1, this._selectedInitialized = !1, this._selectObserver = new $(this, {
122
+ target: null,
123
+ disabled: !0,
124
+ callback: () => {
125
+ this._syncing || this.requestUpdate();
126
+ }
14
127
  }), this._onMoveUp = () => {
15
128
  this.moveSelected(-1);
16
129
  }, this._onMoveDown = () => {
@@ -33,19 +146,27 @@ class o extends _ {
33
146
  }
34
147
  return;
35
148
  }
36
- const t = e.target.id;
149
+ const t = this._getListboxEventTargetId(e);
37
150
  if (t === "available-list" && !this.multiple && e.key === "Enter") {
38
151
  e.preventDefault(), this.addSelected();
39
152
  return;
40
153
  }
41
154
  t === "selected-list" && e.key === "Delete" && (e.preventDefault(), this.removeSelected());
42
155
  }, this._onListboxChange = (e) => {
43
- const s = e.detail.value, l = Array.isArray(s) ? s : s ? [s] : [], i = e.target?.id;
156
+ const t = e;
157
+ if (t.detail.reason === "action") {
158
+ e.stopPropagation();
159
+ return;
160
+ }
161
+ const s = t.detail.value, l = Array.isArray(s) ? s : s ? [s] : [], i = e.currentTarget?.id;
44
162
  i === "available-list" ? this._availableSelection = l : i === "selected-list" && (this._selectedSelection = l);
45
- };
46
- }
47
- createRenderRoot() {
48
- return this;
163
+ }, new x(this, {
164
+ selector: ":scope > select[multiple]",
165
+ observe: !0,
166
+ onChange: (e) => this._setupSelect(e),
167
+ onMissing: () => {
168
+ }
169
+ });
49
170
  }
50
171
  /**
51
172
  * Available/left-hand options derived from unselected `<option>` elements.
@@ -55,18 +176,19 @@ class o extends _ {
55
176
  * skipped to prevent duplicate option values.
56
177
  */
57
178
  get available() {
58
- return this._select ? Array.from(this._select.options).filter((e) => !e.selected).map((e) => ({ value: e.value, label: e.label || e.text })) : [];
179
+ return this._$select ? Array.from(this._$select.options).filter((e) => !e.selected).map((e) => ({ value: e.value, label: e.label || e.text })) : [];
59
180
  }
60
181
  set available(e) {
61
- if (!this._select) return;
182
+ if (!this._$select)
183
+ return;
62
184
  this._syncing = !0;
63
185
  const t = new Set(
64
- Array.from(this._select.options).filter((s) => s.selected).map((s) => s.value)
186
+ Array.from(this._$select.options).filter((s) => s.selected).map((s) => s.value)
65
187
  );
66
- for (const s of Array.from(this._select.options))
188
+ for (const s of Array.from(this._$select.options))
67
189
  s.selected || s.remove();
68
190
  for (const s of e)
69
- t.has(s.value) || this._select.add(new Option(s.label, s.value));
191
+ t.has(s.value) || this._$select.add(new Option(s.label, s.value));
70
192
  this._syncing = !1, this.requestUpdate();
71
193
  }
72
194
  /**
@@ -76,15 +198,15 @@ class o extends _ {
76
198
  * `<select>`. Unselected options are left unchanged.
77
199
  */
78
200
  get selected() {
79
- return this._select ? Array.from(this._select.options).filter((e) => e.selected).map((e) => ({ value: e.value, label: e.label || e.text })) : [];
201
+ return this._$select ? Array.from(this._$select.options).filter((e) => e.selected).map((e) => ({ value: e.value, label: e.label || e.text })) : [];
80
202
  }
81
203
  set selected(e) {
82
- if (this._selectedInitialized = !0, !!this._select) {
204
+ if (this._selectedInitialized = !0, !!this._$select) {
83
205
  this._syncing = !0;
84
- for (const t of Array.from(this._select.options))
206
+ for (const t of Array.from(this._$select.options))
85
207
  t.selected && t.remove();
86
208
  for (const t of e)
87
- this._select.add(new Option(t.label, t.value, !1, !0));
209
+ this._$select.add(new Option(t.label, t.value, !1, !0));
88
210
  this._syncing = !1, this.requestUpdate();
89
211
  }
90
212
  }
@@ -93,74 +215,29 @@ class o extends _ {
93
215
  return this._defaultSelected;
94
216
  }
95
217
  set defaultSelected(e) {
96
- this._defaultSelected = e, !this._selectedInitialized && this._select && e !== void 0 && this._applyDefaultSelected(e);
218
+ this._defaultSelected = e, !this._selectedInitialized && this._$select && e !== void 0 && this._applyDefaultSelected(e);
97
219
  }
98
220
  _applyDefaultSelected(e) {
99
- if (this._select) {
221
+ if (this._$select) {
100
222
  this._syncing = !0;
101
- for (const t of Array.from(this._select.options))
223
+ for (const t of Array.from(this._$select.options))
102
224
  t.selected = e.some((s) => s.value === t.value);
103
225
  this._syncing = !1, this.requestUpdate();
104
226
  }
105
227
  }
106
- connectedCallback() {
107
- super.connectedCallback(), this._hostObserver.observe(this, { childList: !0 }), this.addEventListener("keydown", this._onKeydown), this.addEventListener("rc-listbox-change", this._onListboxChange), this._setupSelect();
108
- }
109
228
  disconnectedCallback() {
110
- super.disconnectedCallback(), this._hostObserver.disconnect(), this._teardownSelect(), this.removeEventListener("keydown", this._onKeydown), this.removeEventListener("rc-listbox-change", this._onListboxChange);
229
+ super.disconnectedCallback(), this._teardownSelect();
111
230
  }
112
231
  render() {
113
- const e = this.available, t = this.selected, s = new Set(this._selectedSelection), l = e.length === 0, i = t.length === 0, r = t.length < 2, h = this._availableSelection.length === 0, a = this._selectedSelection.length === 0, d = this._canMoveSelected(t, s, -1), v = this._canMoveSelected(t, s, 1);
114
- return m`
115
- <style>
116
- rc-transfer-list {
117
- display: block;
118
- }
119
-
120
- rc-transfer-list .rc-transfer-list-root {
121
- display: grid;
122
- grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
123
- align-items: stretch;
124
- gap: var(--rc-transfer-list-gap, var(--rc-control-gap, 0.75rem));
125
- }
126
-
127
- rc-transfer-list .rc-transfer-list-panel {
128
- display: flex;
129
- min-inline-size: 0;
130
- flex-direction: column;
131
- gap: var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem));
132
- }
133
-
134
- rc-transfer-list .rc-transfer-list-panel > rc-listbox {
135
- flex: 1 1 auto;
136
- min-block-size: var(--rc-transfer-list-listbox-min-block-size, 10rem);
137
- overflow: auto;
138
- border: var(--rc-transfer-list-listbox-border, var(--rc-border, 1px solid ButtonBorder));
139
- }
140
-
141
- rc-transfer-list .rc-transfer-list-actions {
142
- align-self: center;
143
- }
144
-
145
- rc-transfer-list .rc-transfer-list-actions button {
146
- white-space: nowrap;
147
- }
148
-
149
- @media (max-width: 42rem) {
150
- rc-transfer-list .rc-transfer-list-root {
151
- grid-template-columns: minmax(0, 1fr);
152
- }
153
-
154
- rc-transfer-list .rc-transfer-list-actions {
155
- align-self: stretch;
156
- }
157
- }
158
- </style>
232
+ const e = this.available, t = this.selected, s = new Set(this._selectedSelection), l = e.length === 0, i = t.length === 0, r = t.length < 2, h = this._availableSelection.length === 0, a = this._selectedSelection.length === 0, d = this._canMoveSelected(t, s, -1), g = this._canMoveSelected(t, s, 1);
233
+ return y`
159
234
  <div
235
+ id="root"
160
236
  part="root"
161
237
  class="rc-transfer-list-root"
162
238
  data-can-move-up=${d ? "" : c}
163
- data-can-move-down=${v ? "" : c}
239
+ data-can-move-down=${g ? "" : c}
240
+ @keydown=${this._onKeydown}
164
241
  >
165
242
  <section
166
243
  part="panel available-panel"
@@ -177,11 +254,13 @@ class o extends _ {
177
254
  tabindex="0"
178
255
  .options=${e}
179
256
  .multiple=${this.multiple}
257
+ @rc-listbox-change=${this._onListboxChange}
180
258
  ></rc-listbox>
181
259
  </section>
182
260
 
183
261
  <rc-toolbar
184
- orientation="vertical"
262
+ id="actions"
263
+ orientation=${this.compact ? "horizontal" : "vertical"}
185
264
  label="Transfer actions"
186
265
  part="actions"
187
266
  class="rc-transfer-list-actions"
@@ -191,37 +270,54 @@ class o extends _ {
191
270
  part="button add-button"
192
271
  ?disabled=${l || h}
193
272
  @click=${this.addSelected}
194
- >Add &#x2192;</button>
273
+ >
274
+ Add &#x2192;
275
+ </button>
276
+
195
277
  <button
196
278
  type="button"
197
279
  part="button add-all-button"
198
280
  ?disabled=${l}
199
281
  @click=${this.addAll}
200
- >Add all &#x2192;</button>
282
+ >
283
+ Add all &#x2192;
284
+ </button>
285
+
201
286
  <button
202
287
  type="button"
203
288
  part="button remove-button"
204
289
  ?disabled=${i || a}
205
290
  @click=${this.removeSelected}
206
- >&#x2190; Remove</button>
291
+ >
292
+ &#x2190; Remove
293
+ </button>
294
+
207
295
  <button
208
296
  type="button"
209
297
  part="button clear-button"
210
298
  ?disabled=${i}
211
299
  @click=${this.clearSelected}
212
- >&#x2190; Clear</button>
300
+ >
301
+ &#x2190; Clear
302
+ </button>
303
+
213
304
  <button
214
305
  type="button"
215
306
  part="button move-up-button"
216
307
  ?disabled=${r || a}
217
308
  @click=${this._onMoveUp}
218
- >Move up</button>
309
+ >
310
+ Move up
311
+ </button>
312
+
219
313
  <button
220
314
  type="button"
221
315
  part="button move-down-button"
222
316
  ?disabled=${r || a}
223
317
  @click=${this._onMoveDown}
224
- >Move down</button>
318
+ >
319
+ Move down
320
+ </button>
225
321
  </rc-toolbar>
226
322
 
227
323
  <section
@@ -232,6 +328,7 @@ class o extends _ {
232
328
  data-has-selection=${a ? c : ""}
233
329
  >
234
330
  <span part="label selected-label" id="selected-label">${this.selectedLabel}</span>
331
+
235
332
  <rc-listbox
236
333
  id="selected-list"
237
334
  part="listbox selected-listbox"
@@ -239,75 +336,91 @@ class o extends _ {
239
336
  tabindex="0"
240
337
  .options=${t}
241
338
  .multiple=${this.multiple}
339
+ @rc-listbox-change=${this._onListboxChange}
242
340
  ></rc-listbox>
243
341
  </section>
244
342
  </div>
343
+ <slot></slot>
245
344
  `;
246
345
  }
247
346
  /** Adds the highlighted available options to the selected list. */
248
347
  addSelected() {
249
- if (!this._select) return;
348
+ if (!this._$select)
349
+ return;
250
350
  const e = new Set(this._$availableList?.selectedValues ?? []);
251
351
  if (e.size !== 0) {
252
352
  this._syncing = !0;
253
- for (const t of Array.from(this._select.options))
353
+ for (const t of Array.from(this._$select.options))
254
354
  !t.selected && e.has(t.value) && (t.selected = !0);
255
355
  this._syncing = !1, this._availableSelection = [], this.requestUpdate(), this._dispatchChange();
256
356
  }
257
357
  }
258
358
  /** Adds every available option to the selected list. */
259
359
  addAll() {
260
- if (this._select) {
360
+ if (this._$select) {
261
361
  this._syncing = !0;
262
- for (const e of Array.from(this._select.options)) e.selected = !0;
362
+ for (const e of Array.from(this._$select.options))
363
+ e.selected = !0;
263
364
  this._syncing = !1, this._availableSelection = [], this.requestUpdate(), this._dispatchChange();
264
365
  }
265
366
  }
266
367
  /** Removes the highlighted right-hand options from the selected list. */
267
368
  removeSelected() {
268
- if (!this._select) return;
369
+ if (!this._$select)
370
+ return;
269
371
  const e = new Set(this._$selectedList?.selectedValues ?? []);
270
372
  if (e.size !== 0) {
271
373
  this._syncing = !0;
272
- for (const t of Array.from(this._select.options))
374
+ for (const t of Array.from(this._$select.options))
273
375
  t.selected && e.has(t.value) && (t.selected = !1);
274
376
  this._syncing = !1, this._selectedSelection = [], this.requestUpdate(), this._dispatchChange();
275
377
  }
276
378
  }
277
379
  /** Clears the selected/right-hand list. */
278
380
  clearSelected() {
279
- if (this._select) {
381
+ if (this._$select) {
280
382
  this._syncing = !0;
281
- for (const e of Array.from(this._select.options)) e.selected = !1;
383
+ for (const e of Array.from(this._$select.options))
384
+ e.selected = !1;
282
385
  this._syncing = !1, this._selectedSelection = [], this.requestUpdate(), this._dispatchChange();
283
386
  }
284
387
  }
285
388
  /** Moves highlighted right-hand options by `delta` rows (-1 = up, 1 = down). */
286
389
  moveSelected(e) {
287
- if (e === 0 || !this._select) return;
390
+ if (e === 0 || !this._$select)
391
+ return;
288
392
  const t = new Set(this._$selectedList?.selectedValues ?? []);
289
393
  t.size !== 0 && (this._syncing = !0, this._reorderSelectOptions(t, e), this._syncing = !1, this.requestUpdate(), this._dispatchChange(t));
290
394
  }
291
395
  _canMoveSelected(e, t, s) {
292
396
  return t.size === 0 || e.length < 2 ? !1 : e.some((l, i) => {
293
- if (!t.has(l.value)) return !1;
397
+ if (!t.has(l.value))
398
+ return !1;
294
399
  const r = i + s;
295
400
  return r < 0 || r >= e.length ? !1 : !t.has(e[r].value);
296
401
  });
297
402
  }
298
- _setupSelect() {
299
- const e = this.querySelector(":scope > select[multiple]");
300
- e !== this._select && (this._teardownSelect(), this._select = e, e && (e.style.display = "none", e.setAttribute("aria-hidden", "true"), e.tabIndex = -1, this._selectObserver.observe(e, { childList: !0 }), !this._selectedInitialized && this._defaultSelected !== void 0 && this._applyDefaultSelected(this._defaultSelected), this.requestUpdate()));
403
+ _getListboxEventTargetId(e) {
404
+ for (const t of e.composedPath())
405
+ if (t instanceof Element && (t.id === "available-list" || t.id === "selected-list"))
406
+ return t.id;
407
+ }
408
+ _setupSelect(e) {
409
+ e !== this._$select && (this._teardownSelect(), this._$select = e, e && (e.style.display = "none", e.setAttribute("aria-hidden", "true"), e.tabIndex = -1, this._selectObserver.setOptions({
410
+ target: e,
411
+ disabled: !1,
412
+ observerOptions: { childList: !0 }
413
+ }), !this._selectedInitialized && this._defaultSelected !== void 0 && this._applyDefaultSelected(this._defaultSelected), this.requestUpdate()));
301
414
  }
302
415
  _teardownSelect() {
303
- this._select && (this._select.style.display = "", this._select.removeAttribute("aria-hidden"), this._select.removeAttribute("tabindex")), this._selectObserver.disconnect(), this._select = null;
416
+ this._$select && (this._$select.style.display = "", this._$select.removeAttribute("aria-hidden"), this._$select.removeAttribute("tabindex")), this._selectObserver.setOptions({ target: null, disabled: !0 }), this._$select = null;
304
417
  }
305
418
  /**
306
419
  * Reorders selected `<option>` DOM nodes inside the backing `<select>` to
307
420
  * preserve user-determined order in form serialisation.
308
421
  */
309
422
  _reorderSelectOptions(e, t) {
310
- const s = this._select, l = Array.from(s.options).filter((a) => a.selected), i = t < 0 ? 1 : l.length - 2, r = t < 0 ? l.length : -1, h = t < 0 ? 1 : -1;
423
+ const s = this._$select, l = Array.from(s.options).filter((a) => a.selected), i = t < 0 ? 1 : l.length - 2, r = t < 0 ? l.length : -1, h = t < 0 ? 1 : -1;
311
424
  for (let a = i; a !== r; a += h) {
312
425
  const d = a + t;
313
426
  !e.has(l[a].value) || e.has(l[d].value) || (t < 0 ? s.insertBefore(l[a], l[d]) : l[d].insertAdjacentElement("afterend", l[a]));
@@ -329,10 +442,15 @@ class o extends _ {
329
442
  );
330
443
  });
331
444
  }
332
- }
445
+ };
446
+ b.styles = S;
447
+ let o = b;
333
448
  n([
334
449
  p({ type: Boolean, reflect: !0 })
335
450
  ], o.prototype, "multiple");
451
+ n([
452
+ p({ type: Boolean, reflect: !0 })
453
+ ], o.prototype, "compact");
336
454
  n([
337
455
  p({ attribute: "available-label" })
338
456
  ], o.prototype, "availableLabel");
@@ -340,18 +458,18 @@ n([
340
458
  p({ attribute: "selected-label" })
341
459
  ], o.prototype, "selectedLabel");
342
460
  n([
343
- b("#available-list")
461
+ f("#available-list")
344
462
  ], o.prototype, "_$availableList");
345
463
  n([
346
- b("#selected-list")
464
+ f("#selected-list")
347
465
  ], o.prototype, "_$selectedList");
348
466
  n([
349
- f()
467
+ v()
350
468
  ], o.prototype, "_availableSelection");
351
469
  n([
352
- f()
470
+ v()
353
471
  ], o.prototype, "_selectedSelection");
354
472
  export {
355
473
  o as R
356
474
  };
357
- //# sourceMappingURL=rc-transfer-list-DDgVWhv6.js.map
475
+ //# sourceMappingURL=rc-transfer-list-DFPxuGJ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-transfer-list-DFPxuGJ2.js","sources":["../src/rc-transfer-list.styles.ts","../src/rc-transfer-list.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const transferListStyles = css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n #root {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);\n align-items: stretch;\n gap: var(--rc-transfer-list-gap, var(--rc-control-gap, 0.75rem));\n }\n\n :host([compact]) #root {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .rc-transfer-list-panel {\n display: flex;\n min-inline-size: 0;\n flex-direction: column;\n gap: var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem));\n }\n\n .rc-transfer-list-panel > rc-listbox {\n flex: 1 1 auto;\n min-block-size: var(--rc-transfer-list-listbox-min-block-size, 10rem);\n overflow: auto;\n border: var(--rc-transfer-list-listbox-border, var(--rc-border, 1px solid ButtonBorder));\n background: var(--rc-transfer-list-listbox-bg, var(--rc-surface, Canvas));\n color: var(--rc-transfer-list-listbox-color, var(--rc-field-text, FieldText));\n --rc-listbox-option-gap: var(--rc-transfer-list-option-gap, var(--rc-item-gap, 0.4em));\n --rc-listbox-option-padding-block: var(\n --rc-transfer-list-option-padding-block,\n var(--rc-item-padding-block, 0.3em)\n );\n --rc-listbox-option-padding-inline: var(\n --rc-transfer-list-option-padding-inline,\n var(--rc-item-padding-inline, 0.75em)\n );\n --rc-listbox-hover-bg: var(\n --rc-transfer-list-option-hover-bg,\n var(--rc-transfer-list-option-selected-bg, var(--rc-highlight, Highlight))\n );\n --rc-listbox-hover-color: var(\n --rc-transfer-list-option-hover-color,\n var(--rc-transfer-list-option-selected-color, var(--rc-highlight-text, HighlightText))\n );\n --rc-listbox-active-bg: var(\n --rc-transfer-list-option-hover-bg,\n var(--rc-transfer-list-option-selected-bg, var(--rc-highlight, Highlight))\n );\n --rc-listbox-active-color: var(\n --rc-transfer-list-option-hover-color,\n var(--rc-transfer-list-option-selected-color, var(--rc-highlight-text, HighlightText))\n );\n --rc-listbox-selected-bg: var(\n --rc-transfer-list-option-selected-bg,\n var(--rc-highlight, Highlight)\n );\n --rc-listbox-selected-color: var(\n --rc-transfer-list-option-selected-color,\n var(--rc-highlight-text, HighlightText)\n );\n --rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);\n }\n\n rc-listbox [part~='option'][hidden] {\n display: none;\n }\n\n rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {\n outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));\n outline-offset: -2px;\n }\n\n rc-listbox [part~='option'][aria-disabled='true'] {\n cursor: not-allowed;\n }\n\n #actions {\n align-self: center;\n transform: translateY(\n calc((1lh + var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem))) / 2)\n );\n }\n\n :host([compact]) #actions {\n align-self: stretch;\n transform: none;\n }\n\n :host([compact]) #actions::part(root) {\n flex-wrap: wrap;\n justify-content: center;\n }\n\n #actions button {\n white-space: nowrap;\n }\n\n slot:not([name]) {\n display: none;\n }\n`;\n\nexport default transferListStyles;\n","import { LitElement, html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\n\nimport {\n MutationObserverController,\n NativeChildController,\n warnMissingDirectChild,\n} from '@rcarls/rc-common';\nimport type { ListboxOption, RCListbox, RCListboxChangeEvent } from '@rcarls/rc-listbox';\n\nimport '@rcarls/rc-listbox/define';\nimport '@rcarls/rc-toolbar/define';\n\nimport { transferListStyles } from './rc-transfer-list.styles.js';\n\nexport interface RCTransferListChangeEvent {\n /** Ordered selected/right-hand list options. */\n selected: ListboxOption[];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-transfer-list': RCTransferList;\n }\n}\n\n/**\n * Transfer list that enhances a native <select multiple> into available and selected\n * panes.\n *\n * The component renders two listboxes and transfer/reorder actions in between.\n * Available options are on the left, and selected options appear on the right. It\n * is an older enterprise/admin UI pattern, but solves the specific problem of\n * multiple selection within a large amount of options.\n *\n * The `available` and `selected` JS array properties write through to the\n * backing `<select>`, so the component can also be driven imperatively from a\n * framework without giving up form participation.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-transfer-list rc-transfer-list docs}\n *\n * @fires rc-transfer-list-change - Fires when the selected/right-hand list changes.\n *\n * @cssprop [--rc-transfer-list-gap=var(--rc-control-gap)] - Gap between panels and the action toolbar\n * @cssprop [--rc-transfer-list-panel-gap=var(--rc-control-gap)] - Gap between a panel label and its listbox\n * @cssprop [--rc-transfer-list-listbox-min-block-size=10rem] - Minimum block size for each listbox\n * @cssprop [--rc-transfer-list-listbox-border=var(--rc-border)] - Border around each listbox\n * @cssprop [--rc-transfer-list-option-gap=var(--rc-item-gap)] - Gap between option adornments and labels\n * @cssprop [--rc-transfer-list-option-padding-block=var(--rc-item-padding-block)] - Block padding for option rows\n * @cssprop [--rc-transfer-list-option-padding-inline=var(--rc-item-padding-inline)] - Inline padding for option rows\n * @cssprop [--rc-transfer-list-option-selected-bg=var(--rc-highlight)] - Selected option background\n * @cssprop [--rc-transfer-list-option-selected-color=var(--rc-highlight-text)] - Selected option foreground\n *\n * @csspart root - Root layout wrapper. Reflects data-can-move-up/down.\n * @csspart panel - Shared list panel surface.\n * @csspart available-panel - Available/left panel. Reflects data-empty and data-has-selection.\n * @csspart selected-panel - Selected/right panel. Reflects data-empty and data-has-selection.\n * @csspart actions - Transfer action toolbar.\n * @csspart button - Shared action button surface.\n */\nexport class RCTransferList extends LitElement {\n static override styles = transferListStyles;\n\n /** Enable multi-selection in both lists. */\n @property({ type: Boolean, reflect: true })\n multiple = false;\n\n /** Stack the panels and action toolbar into a compact one-column layout. */\n @property({ type: Boolean, reflect: true })\n compact = false;\n\n /** Visible label for the available list. */\n @property({ attribute: 'available-label' })\n availableLabel = 'Available';\n\n /** Visible label for the selected list. */\n @property({ attribute: 'selected-label' })\n selectedLabel = 'Selected';\n\n @query('#available-list')\n private _$availableList?: RCListbox;\n\n @query('#selected-list')\n private _$selectedList?: RCListbox;\n\n /** Values currently highlighted in the available list. Drives the \"Add →\" button disable state. */\n @state()\n private _availableSelection: string[] = [];\n\n /** Values currently highlighted in the selected list. Drives Remove/reorder button disable states. */\n @state()\n private _selectedSelection: string[] = [];\n\n private _$select: HTMLSelectElement | null = null;\n\n /** Set while the component mutates the backing <select> to prevent observer feedback loops. */\n private _syncing = false;\n\n private _defaultSelected: ListboxOption[] | undefined;\n\n private _selectedInitialized = false;\n\n private readonly _selectObserver = new MutationObserverController(this, {\n target: null,\n disabled: true,\n callback: () => {\n if (!this._syncing) {\n this.requestUpdate();\n }\n },\n });\n\n constructor() {\n super();\n\n new NativeChildController<HTMLSelectElement>(this, {\n selector: ':scope > select[multiple]',\n observe: true,\n onChange: ($select) => this._setupSelect($select),\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > select[multiple]',\n message:\n '[rc-transfer-list] No direct child <select multiple> found. ' +\n 'Add a <select multiple> child for form participation and progressive enhancement.',\n });\n }\n },\n });\n }\n\n /**\n * Available/left-hand options derived from unselected `<option>` elements.\n *\n * Setting this property replaces all unselected options in the backing\n * `<select>`. Items whose values already appear in the selected list are\n * skipped to prevent duplicate option values.\n */\n get available(): ListboxOption[] {\n if (!this._$select) {\n return [];\n }\n\n return Array.from(this._$select.options)\n .filter(($option) => !$option.selected)\n .map(($option) => ({ value: $option.value, label: $option.label || $option.text }));\n }\n\n set available(items: ListboxOption[]) {\n if (!this._$select) {\n return;\n }\n\n this._syncing = true;\n\n const selectedValues = new Set(\n Array.from(this._$select.options)\n .filter(($option) => $option.selected)\n .map(($option) => $option.value),\n );\n\n for (const $option of Array.from(this._$select.options)) {\n if (!$option.selected) {\n $option.remove();\n }\n }\n\n for (const item of items) {\n if (!selectedValues.has(item.value)) {\n this._$select.add(new Option(item.label, item.value));\n }\n }\n\n this._syncing = false;\n this.requestUpdate();\n }\n\n /**\n * Selected/right-hand options derived from selected `<option>` elements.\n *\n * Setting this property replaces all selected options in the backing\n * `<select>`. Unselected options are left unchanged.\n */\n get selected(): ListboxOption[] {\n if (!this._$select) {\n return [];\n }\n\n return Array.from(this._$select.options)\n .filter(($option) => $option.selected)\n .map(($option) => ({ value: $option.value, label: $option.label || $option.text }));\n }\n\n set selected(items: ListboxOption[]) {\n this._selectedInitialized = true;\n\n if (!this._$select) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n if ($option.selected) {\n $option.remove();\n }\n }\n\n for (const item of items) {\n this._$select.add(new Option(item.label, item.value, false, true));\n }\n\n this._syncing = false;\n this.requestUpdate();\n }\n\n /** Initial uncontrolled selected list. Applied before any `selected` write from the host. */\n get defaultSelected(): ListboxOption[] | undefined {\n return this._defaultSelected;\n }\n\n set defaultSelected(items: ListboxOption[] | undefined) {\n this._defaultSelected = items;\n\n if (!this._selectedInitialized && this._$select && items !== undefined) {\n this._applyDefaultSelected(items);\n }\n }\n\n private _applyDefaultSelected(items: ListboxOption[]): void {\n if (!this._$select) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n $option.selected = items.some((item) => item.value === $option.value);\n }\n\n this._syncing = false;\n this.requestUpdate();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._teardownSelect();\n }\n\n override render() {\n const available = this.available; // reads <select> directly; requestUpdate() always precedes render\n const selected = this.selected;\n const selectedValues = new Set(this._selectedSelection);\n const noAvailable = available.length === 0;\n const noSelected = selected.length === 0;\n const cannotReorder = selected.length < 2;\n const noAvailableSelection = this._availableSelection.length === 0;\n const noSelectedSelection = this._selectedSelection.length === 0;\n const canMoveUp = this._canMoveSelected(selected, selectedValues, -1);\n const canMoveDown = this._canMoveSelected(selected, selectedValues, 1);\n\n return html`\n <div\n id=\"root\"\n part=\"root\"\n class=\"rc-transfer-list-root\"\n data-can-move-up=${canMoveUp ? '' : nothing}\n data-can-move-down=${canMoveDown ? '' : nothing}\n @keydown=${this._onKeydown}\n >\n <section\n part=\"panel available-panel\"\n class=\"rc-transfer-list-panel\"\n aria-labelledby=\"available-label\"\n data-empty=${noAvailable ? '' : nothing}\n data-has-selection=${!noAvailableSelection ? '' : nothing}\n >\n <span part=\"label available-label\" id=\"available-label\">${this.availableLabel}</span>\n <rc-listbox\n id=\"available-list\"\n part=\"listbox available-listbox\"\n aria-labelledby=\"available-label\"\n tabindex=\"0\"\n .options=${available}\n .multiple=${this.multiple}\n @rc-listbox-change=${this._onListboxChange}\n ></rc-listbox>\n </section>\n\n <rc-toolbar\n id=\"actions\"\n orientation=${this.compact ? 'horizontal' : 'vertical'}\n label=\"Transfer actions\"\n part=\"actions\"\n class=\"rc-transfer-list-actions\"\n >\n <button\n type=\"button\"\n part=\"button add-button\"\n ?disabled=${noAvailable || noAvailableSelection}\n @click=${this.addSelected}\n >\n Add &#x2192;\n </button>\n\n <button\n type=\"button\"\n part=\"button add-all-button\"\n ?disabled=${noAvailable}\n @click=${this.addAll}\n >\n Add all &#x2192;\n </button>\n\n <button\n type=\"button\"\n part=\"button remove-button\"\n ?disabled=${noSelected || noSelectedSelection}\n @click=${this.removeSelected}\n >\n &#x2190; Remove\n </button>\n\n <button\n type=\"button\"\n part=\"button clear-button\"\n ?disabled=${noSelected}\n @click=${this.clearSelected}\n >\n &#x2190; Clear\n </button>\n\n <button\n type=\"button\"\n part=\"button move-up-button\"\n ?disabled=${cannotReorder || noSelectedSelection}\n @click=${this._onMoveUp}\n >\n Move up\n </button>\n\n <button\n type=\"button\"\n part=\"button move-down-button\"\n ?disabled=${cannotReorder || noSelectedSelection}\n @click=${this._onMoveDown}\n >\n Move down\n </button>\n </rc-toolbar>\n\n <section\n part=\"panel selected-panel\"\n class=\"rc-transfer-list-panel\"\n aria-labelledby=\"selected-label\"\n data-empty=${noSelected ? '' : nothing}\n data-has-selection=${!noSelectedSelection ? '' : nothing}\n >\n <span part=\"label selected-label\" id=\"selected-label\">${this.selectedLabel}</span>\n\n <rc-listbox\n id=\"selected-list\"\n part=\"listbox selected-listbox\"\n aria-labelledby=\"selected-label\"\n tabindex=\"0\"\n .options=${selected}\n .multiple=${this.multiple}\n @rc-listbox-change=${this._onListboxChange}\n ></rc-listbox>\n </section>\n </div>\n <slot></slot>\n `;\n }\n\n /** Adds the highlighted available options to the selected list. */\n addSelected(): void {\n if (!this._$select) {\n return;\n }\n\n const values = new Set(this._$availableList?.selectedValues ?? []);\n\n if (values.size === 0) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n if (!$option.selected && values.has($option.value)) {\n $option.selected = true;\n }\n }\n\n this._syncing = false;\n this._availableSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Adds every available option to the selected list. */\n addAll(): void {\n if (!this._$select) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n $option.selected = true;\n }\n\n this._syncing = false;\n\n this._availableSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Removes the highlighted right-hand options from the selected list. */\n removeSelected(): void {\n if (!this._$select) {\n return;\n }\n\n const values = new Set(this._$selectedList?.selectedValues ?? []);\n\n if (values.size === 0) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n if ($option.selected && values.has($option.value)) {\n $option.selected = false;\n }\n }\n\n this._syncing = false;\n this._selectedSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Clears the selected/right-hand list. */\n clearSelected(): void {\n if (!this._$select) {\n return;\n }\n\n this._syncing = true;\n\n for (const $option of Array.from(this._$select.options)) {\n $option.selected = false;\n }\n\n this._syncing = false;\n\n this._selectedSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Moves highlighted right-hand options by `delta` rows (-1 = up, 1 = down). */\n moveSelected(delta: number): void {\n if (delta === 0 || !this._$select) {\n return;\n }\n\n const values = new Set(this._$selectedList?.selectedValues ?? []);\n\n if (values.size === 0) {\n return;\n }\n\n this._syncing = true;\n this._reorderSelectOptions(values, delta);\n this._syncing = false;\n\n this.requestUpdate();\n this._dispatchChange(values);\n }\n\n private _onMoveUp = (): void => {\n this.moveSelected(-1);\n };\n\n private _onMoveDown = (): void => {\n this.moveSelected(1);\n };\n\n private _canMoveSelected(\n selected: ListboxOption[],\n selectedValues: Set<string>,\n delta: -1 | 1,\n ): boolean {\n if (selectedValues.size === 0 || selected.length < 2) {\n return false;\n }\n\n return selected.some((item, index) => {\n if (!selectedValues.has(item.value)) {\n return false;\n }\n\n const target = index + delta;\n\n if (target < 0 || target >= selected.length) {\n return false;\n }\n\n return !selectedValues.has(selected[target].value);\n });\n }\n\n private _onKeydown = (e: KeyboardEvent): void => {\n if (e.altKey) {\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n this.addSelected();\n\n break;\n\n case 'ArrowLeft':\n e.preventDefault();\n this.removeSelected();\n\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n this.moveSelected(-1);\n\n break;\n\n case 'ArrowDown':\n e.preventDefault();\n this.moveSelected(1);\n\n break;\n }\n\n return;\n }\n\n const targetId = this._getListboxEventTargetId(e);\n\n // APG shortcut: Enter in the available list adds the active item.\n\n // Restricted to single-select: in multi mode Enter toggles selection in rc-listbox\n // first, so triggering addSelected() here would immediately transfer a single item\n // and discard any multi-item selection the user was building.\n if (targetId === 'available-list' && !this.multiple && e.key === 'Enter') {\n e.preventDefault();\n this.addSelected();\n\n return;\n }\n\n // APG shortcut: Delete in the selected list removes the highlighted items.\n if (targetId === 'selected-list' && e.key === 'Delete') {\n e.preventDefault();\n\n this.removeSelected();\n }\n };\n\n private _onListboxChange = (e: Event): void => {\n const ev = e as CustomEvent<RCListboxChangeEvent>;\n\n if (ev.detail.reason === 'action') {\n e.stopPropagation();\n return;\n }\n\n const raw = ev.detail.value;\n const values = Array.isArray(raw) ? raw : raw ? [raw] : [];\n const targetId = (e.currentTarget as Element | null)?.id;\n\n if (targetId === 'available-list') {\n this._availableSelection = values;\n } else if (targetId === 'selected-list') {\n this._selectedSelection = values;\n }\n };\n\n private _getListboxEventTargetId(e: Event): string | undefined {\n for (const node of e.composedPath()) {\n if (\n node instanceof Element &&\n (node.id === 'available-list' || node.id === 'selected-list')\n ) {\n return node.id;\n }\n }\n\n return undefined;\n }\n\n private _setupSelect($select: HTMLSelectElement | null): void {\n if ($select === this._$select) {\n return;\n }\n\n this._teardownSelect();\n this._$select = $select;\n\n if (!$select) {\n return;\n }\n\n // Remove from visual display, AT, and tab order — the rc-listbox UI is the\n // accessible interface; the <select> is only present for form serialisation.\n $select.style.display = 'none';\n $select.setAttribute('aria-hidden', 'true');\n $select.tabIndex = -1;\n\n // Watch for external option additions/removals (attribute-level opt.selected\n // changes via IDL are not observable; use the component API instead).\n this._selectObserver.setOptions({\n target: $select,\n disabled: false,\n observerOptions: { childList: true },\n });\n\n if (!this._selectedInitialized && this._defaultSelected !== undefined) {\n this._applyDefaultSelected(this._defaultSelected);\n }\n\n this.requestUpdate();\n }\n\n private _teardownSelect(): void {\n if (this._$select) {\n this._$select.style.display = '';\n this._$select.removeAttribute('aria-hidden');\n this._$select.removeAttribute('tabindex');\n }\n\n this._selectObserver.setOptions({ target: null, disabled: true });\n this._$select = null;\n }\n\n /**\n * Reorders selected `<option>` DOM nodes inside the backing `<select>` to\n * preserve user-determined order in form serialisation.\n */\n private _reorderSelectOptions(movedValues: Set<string>, delta: number): void {\n const $select = this._$select!;\n const $options = Array.from($select.options).filter(($option) => $option.selected);\n\n const start = delta < 0 ? 1 : $options.length - 2;\n const end = delta < 0 ? $options.length : -1;\n const step = delta < 0 ? 1 : -1;\n\n for (let i = start; i !== end; i += step) {\n const target = i + delta;\n\n if (!movedValues.has($options[i].value) || movedValues.has($options[target].value)) {\n continue;\n }\n\n if (delta < 0) {\n $select.insertBefore($options[i], $options[target]);\n } else {\n $options[target].insertAdjacentElement('afterend', $options[i]);\n }\n }\n }\n\n // Restores selection in the selected listbox after a mutation. Options are\n // set declaratively in render() so only selection state needs imperative sync.\n private _syncSelection(selectedValues: Set<string> = new Set()): void {\n this._$selectedList?.setSelectedValues([...selectedValues]);\n }\n\n private _dispatchChange(selectedValues?: Set<string>): void {\n this.updateComplete.then(() => {\n this._syncSelection(selectedValues);\n\n this.dispatchEvent(\n new CustomEvent<RCTransferListChangeEvent>('rc-transfer-list-change', {\n bubbles: true,\n composed: true,\n detail: { selected: [...this.selected] },\n }),\n );\n });\n }\n}\n\nexport default RCTransferList;\n"],"names":["transferListStyles","css","_RCTransferList","LitElement","MutationObserverController","targetId","ev","raw","values","NativeChildController","$select","$option","items","selectedValues","item","available","selected","noAvailable","noSelected","cannotReorder","noAvailableSelection","noSelectedSelection","canMoveUp","canMoveDown","html","nothing","delta","index","target","node","movedValues","$options","start","end","step","i","RCTransferList","__decorateClass","property","query","state"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { R as t } from "./rc-transfer-list-DDgVWhv6.js";
1
+ import { R as t } from "./rc-transfer-list-DFPxuGJ2.js";
2
2
  customElements.get("rc-transfer-list") || customElements.define("rc-transfer-list", t);
3
3
  export {
4
4
  t as RCTransferList
@@ -1,4 +1,4 @@
1
- import { R as a } from "./rc-transfer-list-DDgVWhv6.js";
1
+ import { R as a } from "./rc-transfer-list-DFPxuGJ2.js";
2
2
  export {
3
3
  a as RCTransferList
4
4
  };
@@ -10,24 +10,32 @@ declare global {
10
10
  }
11
11
  }
12
12
  /**
13
- * Side-by-side transfer list built from two `rc-listbox` instances.
13
+ * Transfer list that enhances a native <select multiple> into available and selected
14
+ * panes.
14
15
  *
15
- * Requires a direct child `<select multiple>` element as the single source of
16
- * truth for option data and form participation. Unselected `<option>` elements
17
- * populate the available (left) panel; selected ones populate the selected
18
- * (right) panel. Transfers are reflected back to the `<select>` so native form
19
- * submission serialises the right-panel values in user-determined order.
16
+ * The component renders two listboxes and transfer/reorder actions in between.
17
+ * Available options are on the left, and selected options appear on the right. It
18
+ * is an older enterprise/admin UI pattern, but solves the specific problem of
19
+ * multiple selection within a large amount of options.
20
20
  *
21
21
  * The `available` and `selected` JS array properties write through to the
22
22
  * backing `<select>`, so the component can also be driven imperatively from a
23
23
  * framework without giving up form participation.
24
24
  *
25
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-transfer-list rc-transfer-list docs}
26
+ *
25
27
  * @fires rc-transfer-list-change - Fires when the selected/right-hand list changes.
26
28
  *
27
29
  * @cssprop [--rc-transfer-list-gap=var(--rc-control-gap)] - Gap between panels and the action toolbar
28
30
  * @cssprop [--rc-transfer-list-panel-gap=var(--rc-control-gap)] - Gap between a panel label and its listbox
29
31
  * @cssprop [--rc-transfer-list-listbox-min-block-size=10rem] - Minimum block size for each listbox
30
32
  * @cssprop [--rc-transfer-list-listbox-border=var(--rc-border)] - Border around each listbox
33
+ * @cssprop [--rc-transfer-list-option-gap=var(--rc-item-gap)] - Gap between option adornments and labels
34
+ * @cssprop [--rc-transfer-list-option-padding-block=var(--rc-item-padding-block)] - Block padding for option rows
35
+ * @cssprop [--rc-transfer-list-option-padding-inline=var(--rc-item-padding-inline)] - Inline padding for option rows
36
+ * @cssprop [--rc-transfer-list-option-selected-bg=var(--rc-highlight)] - Selected option background
37
+ * @cssprop [--rc-transfer-list-option-selected-color=var(--rc-highlight-text)] - Selected option foreground
38
+ *
31
39
  * @csspart root - Root layout wrapper. Reflects data-can-move-up/down.
32
40
  * @csspart panel - Shared list panel surface.
33
41
  * @csspart available-panel - Available/left panel. Reflects data-empty and data-has-selection.
@@ -36,9 +44,11 @@ declare global {
36
44
  * @csspart button - Shared action button surface.
37
45
  */
38
46
  export declare class RCTransferList extends LitElement {
39
- createRenderRoot(): this;
47
+ static styles: import('lit').CSSResult;
40
48
  /** Enable multi-selection in both lists. */
41
49
  multiple: boolean;
50
+ /** Stack the panels and action toolbar into a compact one-column layout. */
51
+ compact: boolean;
42
52
  /** Visible label for the available list. */
43
53
  availableLabel: string;
44
54
  /** Visible label for the selected list. */
@@ -49,13 +59,13 @@ export declare class RCTransferList extends LitElement {
49
59
  private _availableSelection;
50
60
  /** Values currently highlighted in the selected list. Drives Remove/reorder button disable states. */
51
61
  private _selectedSelection;
52
- private _select;
62
+ private _$select;
53
63
  /** Set while the component mutates the backing <select> to prevent observer feedback loops. */
54
64
  private _syncing;
55
65
  private _defaultSelected;
56
66
  private _selectedInitialized;
57
- private _hostObserver;
58
- private _selectObserver;
67
+ private readonly _selectObserver;
68
+ constructor();
59
69
  /**
60
70
  * Available/left-hand options derived from unselected `<option>` elements.
61
71
  *
@@ -77,7 +87,6 @@ export declare class RCTransferList extends LitElement {
77
87
  get defaultSelected(): ListboxOption[] | undefined;
78
88
  set defaultSelected(items: ListboxOption[] | undefined);
79
89
  private _applyDefaultSelected;
80
- connectedCallback(): void;
81
90
  disconnectedCallback(): void;
82
91
  render(): import('lit').TemplateResult<1>;
83
92
  /** Adds the highlighted available options to the selected list. */
@@ -95,6 +104,7 @@ export declare class RCTransferList extends LitElement {
95
104
  private _canMoveSelected;
96
105
  private _onKeydown;
97
106
  private _onListboxChange;
107
+ private _getListboxEventTargetId;
98
108
  private _setupSelect;
99
109
  private _teardownSelect;
100
110
  /**
@@ -0,0 +1,2 @@
1
+ export declare const transferListStyles: import('lit').CSSResult;
2
+ export default transferListStyles;
package/package.json CHANGED
@@ -3,14 +3,14 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.0",
7
- "description": "Headless WAI-ARIA transfer list web component built with Lit",
6
+ "version": "0.3.0",
7
+ "description": "Transfer list that enhances a native <select multiple> into available and selected panes.",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
11
  "directory": "packages/rc-transfer-list"
12
12
  },
13
- "homepage": "https://github.com/richardcarls/rc-webcomponents#readme",
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-transfer-list",
14
14
  "license": "MIT",
15
15
  "type": "module",
16
16
  "files": [
@@ -37,22 +37,20 @@
37
37
  ],
38
38
  "customElements": "dist/custom-elements.json",
39
39
  "scripts": {
40
- "dev": "vite",
41
40
  "build": "tsc && vite build && cem analyze",
42
41
  "cem:analyze": "cem analyze",
43
42
  "preview": "vite preview",
44
- "test:browser": "vitest",
45
- "test:browser:chrome": "vitest --project=chromium",
46
- "test:browser:firefox": "vitest --project=firefox",
47
- "yalc:publish": "yalc publish --push"
43
+ "test:browser": "vitest --run",
44
+ "test:browser:chrome": "vitest --run --project=chromium",
45
+ "test:browser:firefox": "vitest --run --project=firefox"
48
46
  },
49
47
  "dependencies": {
50
- "@rcarls/rc-listbox": "workspace:^",
51
- "@rcarls/rc-toolbar": "workspace:^"
48
+ "@rcarls/rc-common": "workspace:*",
49
+ "@rcarls/rc-listbox": "workspace:*",
50
+ "@rcarls/rc-toolbar": "workspace:*"
52
51
  },
53
52
  "devDependencies": {
54
53
  "@custom-elements-manifest/analyzer": "0.11.0",
55
- "@guanghechen/rollup-plugin-copy": "^6.0.9",
56
54
  "@vitest/browser-playwright": "4.1.5",
57
55
  "lit": "^3.0.0",
58
56
  "playwright": "^1.56.0",
package/dist/demo.css DELETED
@@ -1,85 +0,0 @@
1
- *, *::before, *::after {
2
- box-sizing: border-box;
3
- }
4
-
5
- :root {
6
- color-scheme: light dark;
7
- --rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
8
- }
9
-
10
- @supports (color: AccentColor) {
11
- :root {
12
- --rc-accent: AccentColor;
13
- }
14
- }
15
-
16
- [data-theme="light"] { color-scheme: light; }
17
- [data-theme="dark"] { color-scheme: dark; }
18
-
19
- [data-theme="solarized-light"] {
20
- color-scheme: light;
21
- --rc-surface: #fdf6e3;
22
- --rc-text: #657b83;
23
- --rc-border: 1px solid #93a1a1;
24
- --rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
25
- }
26
-
27
- [data-theme="solarized-dark"] {
28
- color-scheme: dark;
29
- --rc-surface: #002b36;
30
- --rc-text: #839496;
31
- --rc-border: 1px solid #586e75;
32
- --rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
33
- }
34
-
35
- body {
36
- font-family: system-ui, sans-serif;
37
- margin: 0;
38
- }
39
-
40
- .demo-page {
41
- max-width: 60rem;
42
- margin: 0 auto;
43
- padding: 2rem 1.5rem;
44
- }
45
-
46
- .demo-controls {
47
- display: flex;
48
- align-items: center;
49
- gap: 1rem;
50
- margin-bottom: 2rem;
51
- padding-bottom: 1rem;
52
- border-bottom: 1px solid ButtonBorder;
53
- }
54
-
55
- .demo-controls .demo-title {
56
- flex: 1;
57
- }
58
-
59
- .demo-controls h1 {
60
- margin: 0 0 0.15rem;
61
- font-size: 1.5rem;
62
- }
63
-
64
- .demo-controls a {
65
- font-size: 0.8rem;
66
- }
67
-
68
- .demo-section {
69
- margin-bottom: 2rem;
70
- }
71
-
72
- .demo-section h2 {
73
- margin: 0 0 0.5rem;
74
- }
75
-
76
- .theme-picker {
77
- padding: 0.35em 0.75em;
78
- font-family: inherit;
79
- font-size: 0.8rem;
80
- cursor: pointer;
81
- border: 1px solid ButtonBorder;
82
- border-radius: 4px;
83
- background: Canvas;
84
- color: CanvasText;
85
- }
package/dist/demo.js DELETED
@@ -1,40 +0,0 @@
1
- const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
2
- const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
3
-
4
- const stored = localStorage.getItem('rc-demo-theme') ?? '';
5
- applyTheme(stored);
6
-
7
- function applyTheme(theme) {
8
- if (theme) {
9
- document.documentElement.dataset.theme = theme;
10
- } else {
11
- delete document.documentElement.dataset.theme;
12
- }
13
- }
14
-
15
- function currentIndex() {
16
- const current = document.documentElement.dataset.theme ?? '';
17
- const idx = THEMES.indexOf(current);
18
- return idx === -1 ? 0 : idx;
19
- }
20
-
21
- function updateButtons() {
22
- const label = LABELS[currentIndex()];
23
- document.querySelectorAll('.theme-picker').forEach((btn) => {
24
- btn.textContent = label;
25
- });
26
- }
27
-
28
- window.cycleTheme = function () {
29
- const next = THEMES[(currentIndex() + 1) % THEMES.length];
30
- applyTheme(next);
31
- localStorage.setItem('rc-demo-theme', next);
32
- updateButtons();
33
- };
34
-
35
- document.addEventListener('DOMContentLoaded', () => {
36
- updateButtons();
37
- document.querySelectorAll('.theme-picker').forEach((btn) => {
38
- btn.addEventListener('click', window.cycleTheme);
39
- });
40
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"rc-transfer-list-DDgVWhv6.js","sources":["../src/rc-transfer-list.ts"],"sourcesContent":["import '@rcarls/rc-listbox/define';\nimport '@rcarls/rc-toolbar/define';\n\nimport { LitElement, html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport type { ListboxOption, RCListbox, RCListboxChangeEvent } from '@rcarls/rc-listbox';\n\nexport interface RCTransferListChangeEvent {\n /** Ordered selected/right-hand list options. */\n selected: ListboxOption[];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-transfer-list': RCTransferList;\n }\n}\n\n/**\n * Side-by-side transfer list built from two `rc-listbox` instances.\n *\n * Requires a direct child `<select multiple>` element as the single source of\n * truth for option data and form participation. Unselected `<option>` elements\n * populate the available (left) panel; selected ones populate the selected\n * (right) panel. Transfers are reflected back to the `<select>` so native form\n * submission serialises the right-panel values in user-determined order.\n *\n * The `available` and `selected` JS array properties write through to the\n * backing `<select>`, so the component can also be driven imperatively from a\n * framework without giving up form participation.\n *\n * @fires rc-transfer-list-change - Fires when the selected/right-hand list changes.\n *\n * @cssprop [--rc-transfer-list-gap=var(--rc-control-gap)] - Gap between panels and the action toolbar\n * @cssprop [--rc-transfer-list-panel-gap=var(--rc-control-gap)] - Gap between a panel label and its listbox\n * @cssprop [--rc-transfer-list-listbox-min-block-size=10rem] - Minimum block size for each listbox\n * @cssprop [--rc-transfer-list-listbox-border=var(--rc-border)] - Border around each listbox\n * @csspart root - Root layout wrapper. Reflects data-can-move-up/down.\n * @csspart panel - Shared list panel surface.\n * @csspart available-panel - Available/left panel. Reflects data-empty and data-has-selection.\n * @csspart selected-panel - Selected/right panel. Reflects data-empty and data-has-selection.\n * @csspart actions - Transfer action toolbar.\n * @csspart button - Shared action button surface.\n */\nexport class RCTransferList extends LitElement {\n override createRenderRoot() { return this; }\n\n /** Enable multi-selection in both lists. */\n @property({ type: Boolean, reflect: true }) multiple = false;\n\n /** Visible label for the available list. */\n @property({ attribute: 'available-label' }) availableLabel = 'Available';\n\n /** Visible label for the selected list. */\n @property({ attribute: 'selected-label' }) selectedLabel = 'Selected';\n\n @query('#available-list') private _$availableList?: RCListbox;\n @query('#selected-list') private _$selectedList?: RCListbox;\n\n /** Values currently highlighted in the available list. Drives the \"Add →\" button disable state. */\n @state() private _availableSelection: string[] = [];\n\n /** Values currently highlighted in the selected list. Drives Remove/reorder button disable states. */\n @state() private _selectedSelection: string[] = [];\n\n private _select: HTMLSelectElement | null = null;\n\n /** Set while the component mutates the backing <select> to prevent observer feedback loops. */\n private _syncing = false;\n\n private _defaultSelected: ListboxOption[] | undefined;\n private _selectedInitialized = false;\n\n private _hostObserver = new MutationObserver(() => this._setupSelect());\n private _selectObserver = new MutationObserver(() => {\n if (!this._syncing) this.requestUpdate();\n });\n\n\n /**\n * Available/left-hand options derived from unselected `<option>` elements.\n *\n * Setting this property replaces all unselected options in the backing\n * `<select>`. Items whose values already appear in the selected list are\n * skipped to prevent duplicate option values.\n */\n get available(): ListboxOption[] {\n if (!this._select) return [];\n\n return Array.from(this._select.options)\n .filter((o) => !o.selected)\n .map((o) => ({ value: o.value, label: o.label || o.text }));\n }\n\n set available(items: ListboxOption[]) {\n if (!this._select) return;\n\n this._syncing = true;\n\n const selectedValues = new Set(\n Array.from(this._select.options).filter((o) => o.selected).map((o) => o.value),\n );\n\n for (const opt of Array.from(this._select.options)) {\n if (!opt.selected) opt.remove();\n }\n\n for (const item of items) {\n if (!selectedValues.has(item.value)) {\n this._select.add(new Option(item.label, item.value));\n }\n }\n\n this._syncing = false;\n this.requestUpdate();\n }\n\n\n /**\n * Selected/right-hand options derived from selected `<option>` elements.\n *\n * Setting this property replaces all selected options in the backing\n * `<select>`. Unselected options are left unchanged.\n */\n get selected(): ListboxOption[] {\n if (!this._select) return [];\n\n return Array.from(this._select.options)\n .filter((o) => o.selected)\n .map((o) => ({ value: o.value, label: o.label || o.text }));\n }\n\n set selected(items: ListboxOption[]) {\n this._selectedInitialized = true;\n if (!this._select) return;\n\n this._syncing = true;\n\n for (const opt of Array.from(this._select.options)) {\n if (opt.selected) opt.remove();\n }\n\n for (const item of items) {\n this._select.add(new Option(item.label, item.value, false, true));\n }\n\n this._syncing = false;\n this.requestUpdate();\n }\n\n\n /** Initial uncontrolled selected list. Applied before any `selected` write from the host. */\n get defaultSelected(): ListboxOption[] | undefined {\n return this._defaultSelected;\n }\n set defaultSelected(items: ListboxOption[] | undefined) {\n this._defaultSelected = items;\n if (!this._selectedInitialized && this._select && items !== undefined) {\n this._applyDefaultSelected(items);\n }\n }\n\n private _applyDefaultSelected(items: ListboxOption[]): void {\n if (!this._select) return;\n this._syncing = true;\n for (const opt of Array.from(this._select.options)) {\n opt.selected = items.some((item) => item.value === opt.value);\n }\n this._syncing = false;\n this.requestUpdate();\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._hostObserver.observe(this, { childList: true });\n this.addEventListener('keydown', this._onKeydown);\n this.addEventListener('rc-listbox-change', this._onListboxChange);\n this._setupSelect();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._hostObserver.disconnect();\n this._teardownSelect();\n this.removeEventListener('keydown', this._onKeydown);\n this.removeEventListener('rc-listbox-change', this._onListboxChange);\n }\n\n override render() {\n const available = this.available; // reads <select> directly; requestUpdate() always precedes render\n const selected = this.selected;\n const selectedValues = new Set(this._selectedSelection);\n const noAvailable = available.length === 0;\n const noSelected = selected.length === 0;\n const cannotReorder = selected.length < 2;\n const noAvailableSelection = this._availableSelection.length === 0;\n const noSelectedSelection = this._selectedSelection.length === 0;\n const canMoveUp = this._canMoveSelected(selected, selectedValues, -1);\n const canMoveDown = this._canMoveSelected(selected, selectedValues, 1);\n\n return html`\n <style>\n rc-transfer-list {\n display: block;\n }\n\n rc-transfer-list .rc-transfer-list-root {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);\n align-items: stretch;\n gap: var(--rc-transfer-list-gap, var(--rc-control-gap, 0.75rem));\n }\n\n rc-transfer-list .rc-transfer-list-panel {\n display: flex;\n min-inline-size: 0;\n flex-direction: column;\n gap: var(--rc-transfer-list-panel-gap, var(--rc-control-gap, 0.35rem));\n }\n\n rc-transfer-list .rc-transfer-list-panel > rc-listbox {\n flex: 1 1 auto;\n min-block-size: var(--rc-transfer-list-listbox-min-block-size, 10rem);\n overflow: auto;\n border: var(--rc-transfer-list-listbox-border, var(--rc-border, 1px solid ButtonBorder));\n }\n\n rc-transfer-list .rc-transfer-list-actions {\n align-self: center;\n }\n\n rc-transfer-list .rc-transfer-list-actions button {\n white-space: nowrap;\n }\n\n @media (max-width: 42rem) {\n rc-transfer-list .rc-transfer-list-root {\n grid-template-columns: minmax(0, 1fr);\n }\n\n rc-transfer-list .rc-transfer-list-actions {\n align-self: stretch;\n }\n }\n </style>\n <div\n part=\"root\"\n class=\"rc-transfer-list-root\"\n data-can-move-up=${canMoveUp ? '' : nothing}\n data-can-move-down=${canMoveDown ? '' : nothing}\n >\n <section\n part=\"panel available-panel\"\n class=\"rc-transfer-list-panel\"\n aria-labelledby=\"available-label\"\n data-empty=${noAvailable ? '' : nothing}\n data-has-selection=${!noAvailableSelection ? '' : nothing}\n >\n <span part=\"label available-label\" id=\"available-label\">${this.availableLabel}</span>\n <rc-listbox\n id=\"available-list\"\n part=\"listbox available-listbox\"\n aria-labelledby=\"available-label\"\n tabindex=\"0\"\n .options=${available}\n .multiple=${this.multiple}\n ></rc-listbox>\n </section>\n\n <rc-toolbar\n orientation=\"vertical\"\n label=\"Transfer actions\"\n part=\"actions\"\n class=\"rc-transfer-list-actions\"\n >\n <button\n type=\"button\"\n part=\"button add-button\"\n ?disabled=${noAvailable || noAvailableSelection}\n @click=${this.addSelected}\n >Add &#x2192;</button>\n <button\n type=\"button\"\n part=\"button add-all-button\"\n ?disabled=${noAvailable}\n @click=${this.addAll}\n >Add all &#x2192;</button>\n <button\n type=\"button\"\n part=\"button remove-button\"\n ?disabled=${noSelected || noSelectedSelection}\n @click=${this.removeSelected}\n >&#x2190; Remove</button>\n <button\n type=\"button\"\n part=\"button clear-button\"\n ?disabled=${noSelected}\n @click=${this.clearSelected}\n >&#x2190; Clear</button>\n <button\n type=\"button\"\n part=\"button move-up-button\"\n ?disabled=${cannotReorder || noSelectedSelection}\n @click=${this._onMoveUp}\n >Move up</button>\n <button\n type=\"button\"\n part=\"button move-down-button\"\n ?disabled=${cannotReorder || noSelectedSelection}\n @click=${this._onMoveDown}\n >Move down</button>\n </rc-toolbar>\n\n <section\n part=\"panel selected-panel\"\n class=\"rc-transfer-list-panel\"\n aria-labelledby=\"selected-label\"\n data-empty=${noSelected ? '' : nothing}\n data-has-selection=${!noSelectedSelection ? '' : nothing}\n >\n <span part=\"label selected-label\" id=\"selected-label\">${this.selectedLabel}</span>\n <rc-listbox\n id=\"selected-list\"\n part=\"listbox selected-listbox\"\n aria-labelledby=\"selected-label\"\n tabindex=\"0\"\n .options=${selected}\n .multiple=${this.multiple}\n ></rc-listbox>\n </section>\n </div>\n `;\n }\n\n /** Adds the highlighted available options to the selected list. */\n addSelected(): void {\n if (!this._select) return;\n\n const values = new Set(this._$availableList?.selectedValues ?? []);\n if (values.size === 0) return;\n\n this._syncing = true;\n\n for (const opt of Array.from(this._select.options)) {\n if (!opt.selected && values.has(opt.value)) opt.selected = true;\n }\n\n this._syncing = false;\n this._availableSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Adds every available option to the selected list. */\n addAll(): void {\n if (!this._select) return;\n\n this._syncing = true;\n for (const opt of Array.from(this._select.options)) opt.selected = true;\n this._syncing = false;\n\n this._availableSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Removes the highlighted right-hand options from the selected list. */\n removeSelected(): void {\n if (!this._select) return;\n\n const values = new Set(this._$selectedList?.selectedValues ?? []);\n if (values.size === 0) return;\n\n this._syncing = true;\n\n for (const opt of Array.from(this._select.options)) {\n if (opt.selected && values.has(opt.value)) opt.selected = false;\n }\n\n this._syncing = false;\n this._selectedSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Clears the selected/right-hand list. */\n clearSelected(): void {\n if (!this._select) return;\n\n this._syncing = true;\n for (const opt of Array.from(this._select.options)) opt.selected = false;\n this._syncing = false;\n\n this._selectedSelection = [];\n this.requestUpdate();\n this._dispatchChange();\n }\n\n /** Moves highlighted right-hand options by `delta` rows (-1 = up, 1 = down). */\n moveSelected(delta: number): void {\n if (delta === 0 || !this._select) return;\n\n const values = new Set(this._$selectedList?.selectedValues ?? []);\n if (values.size === 0) return;\n\n this._syncing = true;\n this._reorderSelectOptions(values, delta);\n this._syncing = false;\n\n this.requestUpdate();\n this._dispatchChange(values);\n }\n\n private _onMoveUp = (): void => { this.moveSelected(-1); };\n\n private _onMoveDown = (): void => { this.moveSelected(1); };\n\n private _canMoveSelected(\n selected: ListboxOption[],\n selectedValues: Set<string>,\n delta: -1 | 1,\n ): boolean {\n if (selectedValues.size === 0 || selected.length < 2) return false;\n\n return selected.some((item, index) => {\n if (!selectedValues.has(item.value)) return false;\n\n const target = index + delta;\n if (target < 0 || target >= selected.length) return false;\n\n return !selectedValues.has(selected[target].value);\n });\n }\n\n private _onKeydown = (e: KeyboardEvent): void => {\n if (e.altKey) {\n switch (e.key) {\n case 'ArrowRight': e.preventDefault(); this.addSelected(); break;\n case 'ArrowLeft': e.preventDefault(); this.removeSelected(); break;\n case 'ArrowUp': e.preventDefault(); this.moveSelected(-1); break;\n case 'ArrowDown': e.preventDefault(); this.moveSelected(1); break;\n }\n return;\n }\n\n const targetId = (e.target as Element).id;\n\n // APG shortcut: Enter in the available list adds the active item.\n // Restricted to single-select: in multi mode Enter toggles selection in rc-listbox\n // first, so triggering addSelected() here would immediately transfer a single item\n // and discard any multi-item selection the user was building.\n if (targetId === 'available-list' && !this.multiple && e.key === 'Enter') {\n e.preventDefault();\n this.addSelected();\n return;\n }\n\n // APG shortcut: Delete in the selected list removes the highlighted items.\n if (targetId === 'selected-list' && e.key === 'Delete') {\n e.preventDefault();\n this.removeSelected();\n }\n };\n\n private _onListboxChange = (e: Event): void => {\n const ev = e as CustomEvent<RCListboxChangeEvent>;\n const raw = ev.detail.value;\n const values = Array.isArray(raw) ? raw : raw ? [raw] : [];\n const targetId = (e.target as Element | null)?.id;\n if (targetId === 'available-list') this._availableSelection = values;\n else if (targetId === 'selected-list') this._selectedSelection = values;\n };\n\n private _setupSelect(): void {\n const select = this.querySelector<HTMLSelectElement>(':scope > select[multiple]');\n\n if (select === this._select) return;\n\n this._teardownSelect();\n this._select = select;\n\n if (!select) {\n if (import.meta.env.DEV) {\n console.warn(\n '[rc-transfer-list] No direct child <select multiple> found. ' +\n 'Add a <select multiple> child for form participation and progressive enhancement.',\n this,\n );\n }\n return;\n }\n\n // Remove from visual display, AT, and tab order — the rc-listbox UI is the\n // accessible interface; the <select> is only present for form serialisation.\n select.style.display = 'none';\n select.setAttribute('aria-hidden', 'true');\n select.tabIndex = -1;\n\n // Watch for external option additions/removals (attribute-level opt.selected\n // changes via IDL are not observable; use the component API instead).\n this._selectObserver.observe(select, { childList: true });\n\n if (!this._selectedInitialized && this._defaultSelected !== undefined) {\n this._applyDefaultSelected(this._defaultSelected);\n }\n\n this.requestUpdate();\n }\n\n private _teardownSelect(): void {\n if (this._select) {\n this._select.style.display = '';\n this._select.removeAttribute('aria-hidden');\n this._select.removeAttribute('tabindex');\n }\n this._selectObserver.disconnect();\n this._select = null;\n }\n\n /**\n * Reorders selected `<option>` DOM nodes inside the backing `<select>` to\n * preserve user-determined order in form serialisation.\n */\n private _reorderSelectOptions(movedValues: Set<string>, delta: number): void {\n const select = this._select!;\n const opts = Array.from(select.options).filter((o) => o.selected);\n\n const start = delta < 0 ? 1 : opts.length - 2;\n const end = delta < 0 ? opts.length : -1;\n const step = delta < 0 ? 1 : -1;\n\n for (let i = start; i !== end; i += step) {\n const target = i + delta;\n if (!movedValues.has(opts[i].value) || movedValues.has(opts[target].value)) continue;\n\n if (delta < 0) {\n select.insertBefore(opts[i], opts[target]);\n } else {\n opts[target].insertAdjacentElement('afterend', opts[i]);\n }\n }\n }\n\n // Restores selection in the selected listbox after a mutation. Options are\n // set declaratively in render() so only selection state needs imperative sync.\n private _syncSelection(selectedValues: Set<string> = new Set()): void {\n this._$selectedList?.setSelectedValues([...selectedValues]);\n }\n\n private _dispatchChange(selectedValues?: Set<string>): void {\n this.updateComplete.then(() => {\n this._syncSelection(selectedValues);\n\n this.dispatchEvent(\n new CustomEvent<RCTransferListChangeEvent>('rc-transfer-list-change', {\n bubbles: true,\n composed: true,\n detail: { selected: [...this.selected] },\n }),\n );\n });\n }\n}\n\nexport default RCTransferList;\n"],"names":["RCTransferList","LitElement","targetId","raw","values","o","items","selectedValues","opt","item","available","selected","noAvailable","noSelected","cannotReorder","noAvailableSelection","noSelectedSelection","canMoveUp","canMoveDown","html","nothing","delta","index","target","select","movedValues","opts","start","end","step","i","__decorateClass","property","query","state"],"mappings":"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"}