@webority/ui-elements 0.7.1 → 0.7.2

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.
@@ -69,7 +69,8 @@ var l = Symbol("wui.trustedHtml"), u = (e) => String(e ?? "").replace(/&/g, "&am
69
69
  "debounce",
70
70
  "remote",
71
71
  "size",
72
- "aria-label"
72
+ "aria-label",
73
+ "allow-custom"
73
74
  ];
74
75
  }
75
76
  constructor() {
@@ -106,6 +107,13 @@ var l = Symbol("wui.trustedHtml"), u = (e) => String(e ?? "").replace(/&/g, "&am
106
107
  get _remote() {
107
108
  return this.hasAttribute("remote");
108
109
  }
110
+ get _allowCustom() {
111
+ return this.hasAttribute("allow-custom");
112
+ }
113
+ _matchesKnownOption(e) {
114
+ let t = e.toLowerCase();
115
+ return this._readOptions().some((e) => h(e.label).toLowerCase() === t || String(e.value ?? "").toLowerCase() === t);
116
+ }
109
117
  get _minChars() {
110
118
  return Math.max(0, Number(this.getAttribute("min-chars")) || +!!this._remote);
111
119
  }
@@ -172,13 +180,22 @@ var l = Symbol("wui.trustedHtml"), u = (e) => String(e ?? "").replace(/&/g, "&am
172
180
  _renderOptions(e) {
173
181
  let t = this._refs();
174
182
  if (!t.optionsWrap) return;
175
- let n = (e ?? (t.input ? t.input.value : "")).trim().toLowerCase(), r = this._readOptions();
176
- !this._remote && n && (r = r.filter((e) => h(e.label).toLowerCase().includes(n))), t.optionsWrap.innerHTML = r.map((e) => {
183
+ let n = e ?? (t.input ? t.input.value : ""), r = n.trim().toLowerCase(), i = this._readOptions();
184
+ !this._remote && r && (i = i.filter((e) => h(e.label).toLowerCase().includes(r)));
185
+ let a = n.trim(), o = this._allowCustom && a && !this._matchesKnownOption(a) ? [...i, {
186
+ value: a,
187
+ custom: !0
188
+ }] : i;
189
+ t.optionsWrap.innerHTML = o.map((e) => {
190
+ if (e.custom) {
191
+ let t = `Add “${u(e.value)}”`;
192
+ return `<button type="button" role="option" class="search-select-option search-select-option-custom" data-value="${m(e.value)}" data-custom="true" aria-selected="false">${t}</button>`;
193
+ }
177
194
  let t = String(e.value) === String(this._value);
178
195
  return `<button type="button" role="option" class="search-select-option${t ? " selected" : ""}" data-value="${m(e.value)}" aria-selected="${t}">${f(e)}</button>`;
179
196
  }).join(""), this._active = -1;
180
- let i = n.length < this._minChars;
181
- t.hint && (t.hint.hidden = !(this._remote && i && !r.length)), t.empty && (t.empty.hidden = this._loading || r.length > 0 || this._remote && i);
197
+ let s = r.length < this._minChars;
198
+ t.hint && (t.hint.hidden = !(this._remote && s && !i.length)), t.empty && (t.empty.hidden = this._loading || o.length > 0 || this._remote && s);
182
199
  }
183
200
  _wire() {
184
201
  let e = this._refs();
@@ -193,7 +210,16 @@ var l = Symbol("wui.trustedHtml"), u = (e) => String(e ?? "").replace(/&/g, "&am
193
210
  }, l = () => {
194
211
  e.menu.hidden = !0, e.control.setAttribute("aria-expanded", "false"), e.control.classList.remove("open"), r(-1), this._syncInput();
195
212
  }, u = (t) => {
196
- this._value = t.dataset.value, this._label = h(t.innerHTML), c(this, this._value), e.input.value = this._label, this.dispatchEvent(new Event("change", { bubbles: !0 })), l(), e.input.focus();
213
+ let n = t.dataset.custom === "true";
214
+ this._value = t.dataset.value, this._label = n ? t.dataset.value : h(t.innerHTML), c(this, this._value), e.input.value = this._label, this.dispatchEvent(new CustomEvent("change", {
215
+ bubbles: !0,
216
+ detail: { isCustom: n }
217
+ })), l(), e.input.focus();
218
+ }, d = (t) => {
219
+ this._value = t, this._label = t, c(this, t), e.input.value = t, this.dispatchEvent(new CustomEvent("change", {
220
+ bubbles: !0,
221
+ detail: { isCustom: !0 }
222
+ })), l();
197
223
  };
198
224
  e.input.addEventListener("focus", () => s()), e.input.addEventListener("input", () => {
199
225
  let t = e.input.value;
@@ -206,9 +232,21 @@ var l = Symbol("wui.trustedHtml"), u = (e) => String(e ?? "").replace(/&/g, "&am
206
232
  }));
207
233
  }, this._debounceMs);
208
234
  } else this._renderOptions(t);
209
- }), e.input.addEventListener("keydown", (e) => {
210
- let t = n();
211
- e.key === "ArrowDown" ? (e.preventDefault(), o() || s(), r(Math.min(this._active + 1, t.length - 1))) : e.key === "ArrowUp" ? (e.preventDefault(), r(Math.max(this._active - 1, 0))) : e.key === "Enter" ? t[this._active] && (e.preventDefault(), u(t[this._active])) : e.key === "Escape" && l();
235
+ }), e.input.addEventListener("keydown", (t) => {
236
+ let i = n();
237
+ if (t.key === "ArrowDown") t.preventDefault(), o() || s(), r(Math.min(this._active + 1, i.length - 1));
238
+ else if (t.key === "ArrowUp") t.preventDefault(), r(Math.max(this._active - 1, 0));
239
+ else if (t.key === "Enter") {
240
+ if (i[this._active]) t.preventDefault(), u(i[this._active]);
241
+ else if (this._allowCustom) {
242
+ let n = e.input.value.trim();
243
+ n && !this._matchesKnownOption(n) && (t.preventDefault(), d(n));
244
+ }
245
+ } else t.key === "Escape" && l();
246
+ }), e.input.addEventListener("blur", (t) => {
247
+ if (!this._allowCustom || t.relatedTarget && this.contains(t.relatedTarget)) return;
248
+ let n = e.input.value.trim();
249
+ !n || n === this._label || this._matchesKnownOption(n) || d(n);
212
250
  }), e.optionsWrap.addEventListener("click", (e) => {
213
251
  let t = e.target.closest(".search-select-option");
214
252
  t && u(t);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/ui-elements",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Webority framework-agnostic UI custom elements (<wui-*>) — the single implementation of the interactive components, styled by @webority/theme. Used natively in Razor and wrapped by @webority/ui-react.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",