@emuanalytics/flow-rdf-ui 4.1.0-preview.20 → 4.1.0-preview.22

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);
33
+ this.reorder = boolean(this.reorder);
34
+ this.removable = boolean(this.removable);
35
+ this.selectable = boolean(this.selectable);
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"], { 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,58 @@ 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.handleClick = this.handleClick.bind(this);
43
+ this.handleFocus = this.handleFocus.bind(this);
37
44
  }
38
45
  connectedCallback() {
39
46
  super.connectedCallback();
40
- this.readonly = boolean(this.readonly);
47
+ this.editable = boolean(this.editable);
41
48
  this.reorder = boolean(this.reorder);
42
49
  this.removable = boolean(this.removable);
43
- this.addEventListener("dragover", this.handleDragOver);
44
- this.addEventListener("drop", this.handleDrop);
50
+ this.checked = boolean(this.checked);
51
+ this.disabled = boolean(this.disabled);
52
+ this.setAttribute("tabindex", "-1");
53
+ this.addEventListeners();
45
54
  }
46
55
  disconnectedCallback() {
47
56
  super.disconnectedCallback();
48
- this.removeEventListener("dragover", this.handleDragOver);
49
- this.removeEventListener("drop", this.handleDrop);
57
+ this.removeEventListeners();
50
58
  }
51
59
  rename() {
52
60
  this.input.focus();
53
61
  }
62
+ handleCheckedChange() {
63
+ this.setAttribute("aria-checked", this.checked ? "true" : "false");
64
+ this.setAttribute("tabindex", this.checked ? "0" : "-1");
65
+ }
66
+ addEventListeners() {
67
+ this.addEventListener("blur", this.handleBlur);
68
+ this.addEventListener("click", this.handleClick);
69
+ this.addEventListener("focus", this.handleFocus);
70
+ this.addEventListener("dragover", this.handleDragOver);
71
+ this.addEventListener("drop", this.handleDrop);
72
+ }
73
+ removeEventListeners() {
74
+ this.removeEventListener("blur", this.handleBlur);
75
+ this.removeEventListener("click", this.handleClick);
76
+ this.removeEventListener("focus", this.handleFocus);
77
+ this.removeEventListener("dragover", this.handleDragOver);
78
+ this.removeEventListener("drop", this.handleDrop);
79
+ }
80
+ handleBlur() {
81
+ this.hasFocus = false;
82
+ this.emit("flow-ui-blur");
83
+ }
84
+ handleClick() {
85
+ if (!this.disabled) {
86
+ this.checked = true;
87
+ }
88
+ }
89
+ handleFocus() {
90
+ this.hasFocus = true;
91
+ this.emit("flow-ui-focus");
92
+ }
54
93
  handleMenuSelect(ev) {
55
94
  const menuItem = ev.detail.item;
56
95
  if (menuItem.value === "rename") {
@@ -104,7 +143,7 @@ class FlowUiListItem extends FlowRdfElement {
104
143
  this.emit("flow-ui-drag-drop");
105
144
  }
106
145
  renderMenu() {
107
- const hasCustomMenuDivider = (!this.readonly || this.removable) && this.hasSlotController.test("menu");
146
+ const hasCustomMenuDivider = (this.editable || this.removable) && this.hasSlotController.test("menu");
108
147
  return html`
109
148
  <flow-ui-dropdown placement="bottom-end" class="list-item__menu-dropdown">
110
149
  <flow-ui-icon-button
@@ -115,7 +154,7 @@ class FlowUiListItem extends FlowRdfElement {
115
154
  library="system"
116
155
  ></flow-ui-icon-button>
117
156
  <flow-ui-menu @flow-ui-select=${this.handleMenuSelect}>
118
- ${!this.readonly ? html`
157
+ ${this.editable ? html`
119
158
  <flow-ui-menu-item value="rename">Rename</flow-ui-menu-item>
120
159
  ` : nothing}
121
160
  ${this.removable ? html`
@@ -133,16 +172,18 @@ class FlowUiListItem extends FlowRdfElement {
133
172
  `;
134
173
  }
135
174
  render() {
136
- const hasMenu = this.readonly || this.removable || this.hasSlotController.test("menu");
175
+ const hasMenu = this.editable || this.removable || this.hasSlotController.test("menu");
137
176
  return html`
138
177
  <div
139
178
  part="base"
140
179
  class=${classMap({
141
180
  "list-item": true,
181
+ "list-item--checked": this.checked,
142
182
  "list-item--has-prefix": this.hasSlotController.test("prefix"),
143
183
  "list-item--has-suffix": this.hasSlotController.test("suffix"),
144
184
  "list-item--has-menu": this.hasSlotController.test("menu"),
145
- "list-item--readonly": this.readonly
185
+ "list-item--editable": this.editable,
186
+ "list-item--focused": this.hasFocus
146
187
  })}
147
188
  >
148
189
  ${this.reorder ? html`
@@ -162,7 +203,8 @@ class FlowUiListItem extends FlowRdfElement {
162
203
  inline
163
204
  class="list-item__input"
164
205
  .value=${this.label}
165
- .readonly=${this.readonly}
206
+ .readonly=${!this.editable}
207
+ tabindex=${this.editable ? "0" : "-1"}
166
208
  @flow-ui-change=${this.handleInputEvents}
167
209
  @flow-ui-input=${this.handleInputEvents}
168
210
  ></flow-ui-input>
@@ -175,6 +217,9 @@ class FlowUiListItem extends FlowRdfElement {
175
217
  }
176
218
  }
177
219
  FlowUiListItem.styles = styles;
220
+ __decorateClass([
221
+ state()
222
+ ], FlowUiListItem.prototype, "hasFocus", 2);
178
223
  __decorateClass([
179
224
  query(".list-item__input")
180
225
  ], FlowUiListItem.prototype, "input", 2);
@@ -186,12 +231,21 @@ __decorateClass([
186
231
  ], FlowUiListItem.prototype, "value", 2);
187
232
  __decorateClass([
188
233
  property({ type: Boolean, reflect: true })
189
- ], FlowUiListItem.prototype, "readonly", 2);
234
+ ], FlowUiListItem.prototype, "checked", 2);
235
+ __decorateClass([
236
+ property({ type: Boolean, reflect: true })
237
+ ], FlowUiListItem.prototype, "editable", 2);
190
238
  __decorateClass([
191
239
  property({ type: Boolean, reflect: true })
192
240
  ], FlowUiListItem.prototype, "reorder", 2);
193
241
  __decorateClass([
194
242
  property({ type: Boolean, reflect: true })
195
243
  ], FlowUiListItem.prototype, "removable", 2);
244
+ __decorateClass([
245
+ property({ type: Boolean, reflect: true })
246
+ ], FlowUiListItem.prototype, "disabled", 2);
247
+ __decorateClass([
248
+ watch("checked")
249
+ ], FlowUiListItem.prototype, "handleCheckedChange", 1);
196
250
 
197
251
  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 };
@@ -292,7 +292,7 @@ __decorateClass([
292
292
  watch(["majorStep", "minorStep"])
293
293
  ], FlowUiTimer.prototype, "updateParsedSteps", 1);
294
294
  __decorateClass([
295
- watch("running", { waitUntilFirstUpdate: true })
295
+ watch("running")
296
296
  ], FlowUiTimer.prototype, "updateRunning", 1);
297
297
 
298
298
  export { FlowUiTimer as default };