@emuanalytics/flow-rdf-ui 4.1.0-preview.21 → 4.1.0-preview.23

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.
@@ -1,7 +1,9 @@
1
1
  import { html } from 'lit';
2
2
  import { property } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
+ import { boolean } from '../../internal/boolean.mjs';
4
5
  import FlowRdfElement from '../../internal/flow-ui-rdf-element.mjs';
6
+ import { watch } from '../../internal/watch.mjs';
5
7
  import styles from './list.styles.mjs';
6
8
 
7
9
  var __defProp = Object.defineProperty;
@@ -20,12 +22,17 @@ class FlowUiList extends FlowRdfElement {
20
22
  super();
21
23
  this.draggedItem = null;
22
24
  this.draggedOverItem = null;
23
- this.readonly = false;
25
+ this.selectable = false;
26
+ this.editable = false;
24
27
  this.reorder = false;
25
28
  this.removable = false;
26
29
  }
27
30
  connectedCallback() {
28
31
  super.connectedCallback();
32
+ this.editable = boolean(this.editable, true);
33
+ this.reorder = boolean(this.reorder, true);
34
+ this.removable = boolean(this.removable, true);
35
+ this.selectable = boolean(this.selectable, true);
29
36
  }
30
37
  disconnectedCallback() {
31
38
  super.disconnectedCallback();
@@ -33,6 +40,83 @@ class FlowUiList extends FlowRdfElement {
33
40
  get listItems() {
34
41
  return [...this.querySelectorAll("flow-ui-list-item")];
35
42
  }
43
+ handleStyleChange() {
44
+ this.syncItems();
45
+ }
46
+ handleValueChange() {
47
+ if (this.hasUpdated) {
48
+ this.updateCheckedItem();
49
+ }
50
+ }
51
+ handleItemClick(event) {
52
+ if (!this.selectable) {
53
+ return;
54
+ }
55
+ const target = event.target.closest("flow-ui-list-item");
56
+ const items = this.listItems;
57
+ const oldValue = this.value;
58
+ if (target.disabled) {
59
+ return;
60
+ }
61
+ this.value = target.value;
62
+ items.forEach((item) => item.checked = item === target);
63
+ if (this.value !== oldValue) {
64
+ this.emit("flow-ui-change");
65
+ this.emit("flow-ui-input");
66
+ }
67
+ }
68
+ handleKeyDown(event) {
69
+ if (!this.selectable) {
70
+ return;
71
+ }
72
+ if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", " "].includes(event.key)) {
73
+ return;
74
+ }
75
+ const items = this.listItems.filter((item) => !item.disabled);
76
+ const checkedItem = items.find((item) => item.checked) ?? items[0];
77
+ const incr = event.key === " " ? 0 : ["ArrowUp", "ArrowLeft"].includes(event.key) ? -1 : 1;
78
+ const oldValue = this.value;
79
+ let index = items.indexOf(checkedItem) + incr;
80
+ if (index < 0) {
81
+ index = items.length - 1;
82
+ }
83
+ if (index > items.length - 1) {
84
+ index = 0;
85
+ }
86
+ this.listItems.forEach((item) => {
87
+ item.checked = false;
88
+ item.tabIndex = -1;
89
+ });
90
+ this.value = items[index].value;
91
+ items[index].checked = true;
92
+ items[index].tabIndex = 0;
93
+ items[index].focus();
94
+ if (this.value !== oldValue) {
95
+ this.emit("flow-ui-change");
96
+ this.emit("flow-ui-input");
97
+ }
98
+ event.preventDefault();
99
+ }
100
+ syncItems() {
101
+ if (customElements.get("flow-ui-list-item")) {
102
+ const items = this.listItems;
103
+ items.forEach((item) => {
104
+ item.checked = item.value === this.value;
105
+ item.removable = this.removable;
106
+ item.editable = this.editable;
107
+ item.reorder = this.reorder;
108
+ });
109
+ if (!items.some((item) => item.checked)) {
110
+ items[0].tabIndex = 0;
111
+ }
112
+ } else {
113
+ customElements.whenDefined("flow-ui-list-item").then(() => this.syncItems());
114
+ }
115
+ }
116
+ updateCheckedItem() {
117
+ const items = this.listItems;
118
+ items.forEach((item) => item.checked = item.value === this.value);
119
+ }
36
120
  handleDragStart(ev) {
37
121
  this.draggedItem = ev.target;
38
122
  console.log(`parent drag start: ${this.draggedItem.value}`, this.draggedItem);
@@ -71,6 +155,9 @@ class FlowUiList extends FlowRdfElement {
71
155
  this.draggedOverItem = null;
72
156
  }
73
157
  render() {
158
+ const defaultSlot = html`
159
+ <slot @click=${this.handleItemClick} @keydown=${this.handleKeyDown} @slotchange=${this.syncItems}></slot>
160
+ `;
74
161
  return html`
75
162
  <div
76
163
  part="base"
@@ -82,20 +169,32 @@ class FlowUiList extends FlowRdfElement {
82
169
  @flow-ui-drag-drop=${this.handleDrop}
83
170
  @flow-ui-drag-end=${this.handleDragEnd}
84
171
  >
85
- <slot></slot>
172
+ ${defaultSlot}
86
173
  </div>
87
174
  `;
88
175
  }
89
176
  }
90
177
  FlowUiList.styles = styles;
178
+ __decorateClass([
179
+ property({ reflect: true })
180
+ ], FlowUiList.prototype, "value", 2);
91
181
  __decorateClass([
92
182
  property({ type: Boolean })
93
- ], FlowUiList.prototype, "readonly", 2);
183
+ ], FlowUiList.prototype, "selectable", 2);
184
+ __decorateClass([
185
+ property({ type: Boolean })
186
+ ], FlowUiList.prototype, "editable", 2);
94
187
  __decorateClass([
95
188
  property({ type: Boolean })
96
189
  ], FlowUiList.prototype, "reorder", 2);
97
190
  __decorateClass([
98
191
  property({ type: Boolean })
99
192
  ], FlowUiList.prototype, "removable", 2);
193
+ __decorateClass([
194
+ watch(["reorder", "editable", "removable", "selectable"], { waitUntilFirstUpdate: true })
195
+ ], FlowUiList.prototype, "handleStyleChange", 1);
196
+ __decorateClass([
197
+ watch("value")
198
+ ], FlowUiList.prototype, "handleValueChange", 1);
100
199
 
101
200
  export { FlowUiList as default };
@@ -7,7 +7,7 @@ const styles = css`
7
7
  :host {
8
8
  display: block;
9
9
 
10
- --divider-color: var(--flow-ui-divider-color);
10
+ --divider-color: var(--flow-ui-list-item-divider-color);
11
11
  }
12
12
 
13
13
  .list {
@@ -1,9 +1,10 @@
1
1
  import { html, nothing } from 'lit';
2
- import { query, property } from 'lit/decorators.js';
2
+ import { state, query, property } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import { boolean } from '../../internal/boolean.mjs';
5
5
  import FlowRdfElement from '../../internal/flow-ui-rdf-element.mjs';
6
6
  import { HasSlotController } from '../../internal/slot.mjs';
7
+ import { watch } from '../../internal/watch.mjs';
7
8
  import styles from './list-item.styles.mjs';
8
9
 
9
10
  var __defProp = Object.defineProperty;
@@ -21,9 +22,12 @@ class FlowUiListItem extends FlowRdfElement {
21
22
  constructor() {
22
23
  super();
23
24
  this.hasSlotController = new HasSlotController(this, "prefix", "suffix", "menu");
24
- this.readonly = false;
25
+ this.hasFocus = false;
26
+ this.checked = false;
27
+ this.editable = false;
25
28
  this.reorder = false;
26
29
  this.removable = false;
30
+ this.disabled = false;
27
31
  this.handleDragStart = (ev) => {
28
32
  ev.stopPropagation();
29
33
  ev.dataTransfer.setData("application/flow-ui-list-item", this.value);
@@ -34,23 +38,50 @@ class FlowUiListItem extends FlowRdfElement {
34
38
  this.handleDragOver = this.handleDragOver.bind(this);
35
39
  this.handleDrop = this.handleDrop.bind(this);
36
40
  this.handleDragEnd = this.handleDragEnd.bind(this);
41
+ this.handleBlur = this.handleBlur.bind(this);
42
+ this.handleFocus = this.handleFocus.bind(this);
37
43
  }
38
44
  connectedCallback() {
39
45
  super.connectedCallback();
40
- this.readonly = boolean(this.readonly);
41
- this.reorder = boolean(this.reorder);
42
- this.removable = boolean(this.removable);
43
- this.addEventListener("dragover", this.handleDragOver);
44
- this.addEventListener("drop", this.handleDrop);
46
+ this.editable = boolean(this.editable, true);
47
+ this.reorder = boolean(this.reorder, true);
48
+ this.removable = boolean(this.removable, true);
49
+ this.checked = boolean(this.checked, true);
50
+ this.disabled = boolean(this.disabled, true);
51
+ this.setAttribute("tabindex", "-1");
52
+ this.addEventListeners();
45
53
  }
46
54
  disconnectedCallback() {
47
55
  super.disconnectedCallback();
48
- this.removeEventListener("dragover", this.handleDragOver);
49
- this.removeEventListener("drop", this.handleDrop);
56
+ this.removeEventListeners();
50
57
  }
51
58
  rename() {
52
59
  this.input.focus();
53
60
  }
61
+ handleCheckedChange() {
62
+ this.setAttribute("aria-checked", this.checked ? "true" : "false");
63
+ this.setAttribute("tabindex", this.checked ? "0" : "-1");
64
+ }
65
+ addEventListeners() {
66
+ this.addEventListener("blur", this.handleBlur);
67
+ this.addEventListener("focus", this.handleFocus);
68
+ this.addEventListener("dragover", this.handleDragOver);
69
+ this.addEventListener("drop", this.handleDrop);
70
+ }
71
+ removeEventListeners() {
72
+ this.removeEventListener("blur", this.handleBlur);
73
+ this.removeEventListener("focus", this.handleFocus);
74
+ this.removeEventListener("dragover", this.handleDragOver);
75
+ this.removeEventListener("drop", this.handleDrop);
76
+ }
77
+ handleBlur() {
78
+ this.hasFocus = false;
79
+ this.emit("flow-ui-blur");
80
+ }
81
+ handleFocus() {
82
+ this.hasFocus = true;
83
+ this.emit("flow-ui-focus");
84
+ }
54
85
  handleMenuSelect(ev) {
55
86
  const menuItem = ev.detail.item;
56
87
  if (menuItem.value === "rename") {
@@ -104,7 +135,7 @@ class FlowUiListItem extends FlowRdfElement {
104
135
  this.emit("flow-ui-drag-drop");
105
136
  }
106
137
  renderMenu() {
107
- const hasCustomMenuDivider = (!this.readonly || this.removable) && this.hasSlotController.test("menu");
138
+ const hasCustomMenuDivider = (this.editable || this.removable) && this.hasSlotController.test("menu");
108
139
  return html`
109
140
  <flow-ui-dropdown placement="bottom-end" class="list-item__menu-dropdown">
110
141
  <flow-ui-icon-button
@@ -115,7 +146,7 @@ class FlowUiListItem extends FlowRdfElement {
115
146
  library="system"
116
147
  ></flow-ui-icon-button>
117
148
  <flow-ui-menu @flow-ui-select=${this.handleMenuSelect}>
118
- ${!this.readonly ? html`
149
+ ${this.editable ? html`
119
150
  <flow-ui-menu-item value="rename">Rename</flow-ui-menu-item>
120
151
  ` : nothing}
121
152
  ${this.removable ? html`
@@ -133,16 +164,18 @@ class FlowUiListItem extends FlowRdfElement {
133
164
  `;
134
165
  }
135
166
  render() {
136
- const hasMenu = this.readonly || this.removable || this.hasSlotController.test("menu");
167
+ const hasMenu = this.editable || this.removable || this.hasSlotController.test("menu");
137
168
  return html`
138
169
  <div
139
170
  part="base"
140
171
  class=${classMap({
141
172
  "list-item": true,
173
+ "list-item--checked": this.checked,
142
174
  "list-item--has-prefix": this.hasSlotController.test("prefix"),
143
175
  "list-item--has-suffix": this.hasSlotController.test("suffix"),
144
176
  "list-item--has-menu": this.hasSlotController.test("menu"),
145
- "list-item--readonly": this.readonly
177
+ "list-item--editable": this.editable,
178
+ "list-item--focused": this.hasFocus
146
179
  })}
147
180
  >
148
181
  ${this.reorder ? html`
@@ -162,7 +195,8 @@ class FlowUiListItem extends FlowRdfElement {
162
195
  inline
163
196
  class="list-item__input"
164
197
  .value=${this.label}
165
- .readonly=${this.readonly}
198
+ .readonly=${!this.editable}
199
+ tabindex=${this.editable ? "0" : "-1"}
166
200
  @flow-ui-change=${this.handleInputEvents}
167
201
  @flow-ui-input=${this.handleInputEvents}
168
202
  ></flow-ui-input>
@@ -175,6 +209,9 @@ class FlowUiListItem extends FlowRdfElement {
175
209
  }
176
210
  }
177
211
  FlowUiListItem.styles = styles;
212
+ __decorateClass([
213
+ state()
214
+ ], FlowUiListItem.prototype, "hasFocus", 2);
178
215
  __decorateClass([
179
216
  query(".list-item__input")
180
217
  ], FlowUiListItem.prototype, "input", 2);
@@ -186,12 +223,21 @@ __decorateClass([
186
223
  ], FlowUiListItem.prototype, "value", 2);
187
224
  __decorateClass([
188
225
  property({ type: Boolean, reflect: true })
189
- ], FlowUiListItem.prototype, "readonly", 2);
226
+ ], FlowUiListItem.prototype, "checked", 2);
227
+ __decorateClass([
228
+ property({ type: Boolean, reflect: true })
229
+ ], FlowUiListItem.prototype, "editable", 2);
190
230
  __decorateClass([
191
231
  property({ type: Boolean, reflect: true })
192
232
  ], FlowUiListItem.prototype, "reorder", 2);
193
233
  __decorateClass([
194
234
  property({ type: Boolean, reflect: true })
195
235
  ], FlowUiListItem.prototype, "removable", 2);
236
+ __decorateClass([
237
+ property({ type: Boolean, reflect: true })
238
+ ], FlowUiListItem.prototype, "disabled", 2);
239
+ __decorateClass([
240
+ watch("checked")
241
+ ], FlowUiListItem.prototype, "handleCheckedChange", 1);
196
242
 
197
243
  export { FlowUiListItem as default };
@@ -11,6 +11,15 @@ const styles = css`
11
11
  --color: var(--flow-ui-list-item-color);
12
12
  --accessory-color: var(--flow-ui-list-item-accessory-color);
13
13
  --background-color: var(--flow-ui-list-item-background-color);
14
+
15
+ --selection-width: 6px;
16
+ --selection-color: var(--flow-ui-list-item-selected-color, var(--flow-ui-color-primary-500));
17
+
18
+ --background-color-selected: var(--flow-ui-list-item-background-color-selected, var(--flow-ui-color-neutral-500));
19
+ }
20
+
21
+ :host(:focus-visible) {
22
+ outline: 0px;
14
23
  }
15
24
 
16
25
  :host([draggable='true']) {
@@ -24,12 +33,19 @@ const styles = css`
24
33
 
25
34
  height: var(--height);
26
35
  padding: var(--padding);
36
+ padding-right: calc(var(--padding) / 2);
37
+ border-right: calc(var(--padding) / 2) solid transparent;
27
38
 
28
39
  color: var(--color);
29
40
  background-color: var(--background-color);
30
41
  user-select: none;
31
42
  }
32
43
 
44
+ .list-item--checked {
45
+ border-right-color: var(--selection-color);
46
+ background-color: var(--background-color-selected);
47
+ }
48
+
33
49
  .list-item__prefix,
34
50
  .list-item__suffix,
35
51
  .list-item__menu-button,
@@ -41,7 +57,7 @@ const styles = css`
41
57
  flex: 1;
42
58
  }
43
59
 
44
- .list-item.list-item--readonly .list-item__input::part(form-control) {
60
+ .list-item:not(.list-item--editable) .list-item__input::part(form-control) {
45
61
  --control-background-color-hover: transparent;
46
62
  --control-border-color-hover: transparent;
47
63
  --control-background-color-focus: transparent;
@@ -58,6 +74,10 @@ const styles = css`
58
74
  .list-item__reorder-icon {
59
75
  cursor: move;
60
76
  }
77
+
78
+ .list-item__input {
79
+ margin-left: calc(var(--padding) * -0.5);
80
+ }
61
81
  `;
62
82
 
63
83
  export { styles as default };