- commit
- a23b7af67a09d7320cc69a8df9b41cecd8aaafc3
- parent
- f58e4f62abddd65707c39fcedb2ff22f13498c7a
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2023-08-08 11:48
Merge pull request #2 from xi/feature-inline-multi Display values inside of input
Diffstat
| M | package.json | 1 | + |
| M | select.css | 19 | ++++++++++++++++++- |
| M | select.js | 24 | +++++++----------------- |
| M | tags.js | 30 | +++++++++--------------------- |
| A | values.js | 82 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 117 insertions, 39 deletions
diff --git a/package.json b/package.json
@@ -13,6 +13,7 @@ 13 13 "files": [ 14 14 "tags.js", 15 15 "select.js", -1 16 "values.js", 16 17 "utils.js", 17 18 "select.css", 18 19 "README.md",
diff --git a/select.css b/select.css
@@ -9,7 +9,7 @@ 9 9 10 10 .select__dropdown { 11 11 position: absolute;12 -1 z-index: 1;-1 12 z-index: 2; 13 13 list-style: none; 14 14 margin: 0; 15 15 padding: 0; @@ -64,13 +64,23 @@ 64 64 padding-left: 2em; 65 65 } 66 66 -1 67 .select__input { -1 68 position: relative; -1 69 } -1 70 .select__input input { -1 71 position: relative; /* on top of values */ -1 72 } -1 73 67 74 .select__values { -1 75 position: absolute; 68 76 display: inline; 69 77 padding: 0; 70 78 margin: 0; 71 79 } 72 80 .select__values li { 73 81 display: inline-block; -1 82 position: relative; -1 83 z-index: 1; /* on top of input */ 74 84 margin: 0.1em 0.2em; 75 85 margin-inline-start: 0; 76 86 cursor: default; @@ -82,3 +92,10 @@ 82 92 border: 1px solid ThreeDShadow; 83 93 border-radius: 0.3em; 84 94 } -1 95 -1 96 .select__measure { -1 97 width: 0; -1 98 height: 0; -1 99 overflow: hidden; -1 100 white-space: pre; -1 101 }
diff --git a/select.js b/select.js
@@ -1,4 +1,5 @@ 1 1 import { KEYS, randomString, create } from './utils.js'; -1 2 import { Values } from './values.js'; 2 3 3 4 export class Select { 4 5 constructor(id, original) { @@ -11,6 +12,8 @@ export class Select { 11 12 this.createElements(); 12 13 original.hidden = true; 13 14 original.before(this.wrapper); -1 15 -1 16 this.updateValue(); 14 17 } 15 18 16 19 createElements() { @@ -20,9 +23,8 @@ export class Select { 20 23 21 24 if (this.original.multiple) { 22 25 var inputWrapper = create('<div class="select__input">');23 -1 this.values = create('<ul class="select__values" aria-live="polite">')24 -1 inputWrapper.append(this.values);25 26 inputWrapper.append(this.input); -1 27 this.values = new Values(this.input, this.original.dataset.selectValueClass); 26 28 this.wrapper.append(inputWrapper); 27 29 } else { 28 30 this.wrapper.append(this.input); @@ -58,8 +60,6 @@ export class Select { 58 60 this.dropdown.onmousedown = event => { 59 61 event.preventDefault(); 60 62 };61 -162 -1 this.updateValue();63 63 } 64 64 65 65 isMatch(s) { @@ -90,19 +90,9 @@ export class Select { 90 90 this.input.value = ''; 91 91 this.inputDirty = false; 92 92 this.updateValidity();93 -1 this.values.innerHTML = '';94 -1 Array.from(this.original.options).forEach(op => {95 -1 if (op.selected && op.label) {96 -1 var li = document.createElement('li');97 -1 li.textContent = op.label;98 -1 li.className = this.original.dataset.selectValueClass || 'select__value';99 -1 li.onclick = () => {100 -1 op.selected = false;101 -1 this.updateValue();102 -1 this.input.focus();103 -1 };104 -1 this.values.append(li);105 -1 }-1 93 this.values.update(this.original, () => { -1 94 this.updateValue(); -1 95 this.input.focus(); 106 96 }); 107 97 } else { 108 98 if (this.original.selectedOptions.length) {
diff --git a/tags.js b/tags.js
@@ -1,4 +1,5 @@ 1 1 import { KEYS, randomString, create } from './utils.js'; -1 2 import { Values } from './values.js'; 2 3 3 4 export class TagInput { 4 5 constructor(id, original) { @@ -9,18 +10,19 @@ export class TagInput { 9 10 this.createElements(); 10 11 original.hidden = true; 11 12 original.before(this.wrapper); -1 13 -1 14 this.updateValue(); 12 15 } 13 16 14 17 createElements() {15 -1 this.wrapper = document.createElement('div');16 -117 -1 this.values = create('<ul class="select__values" aria-live="polite">');18 -1 this.wrapper.append(this.values);-1 18 this.wrapper = create('<div class="select__input">'); 19 19 20 20 this.input = document.createElement('input'); 21 21 this.input.className = this.original.dataset.tagsInputClass || ''; 22 22 this.wrapper.append(this.input); 23 23 -1 24 this.values = new Values(this.input, this.original.dataset.tagsValueClass); -1 25 24 26 this.datalist = document.createElement('datalist'); 25 27 this.datalist.innerHTML = this.original.innerHTML; 26 28 this.datalist.id = this.id + '-list'; @@ -39,27 +41,13 @@ export class TagInput { 39 41 this.original.onchange = () => { 40 42 this.input.setCustomValidity(this.original.validationMessage); 41 43 };42 -143 -1 this.updateValue();44 44 } 45 45 46 46 updateValue() { 47 47 this.input.value = '';48 -1 this.values.innerHTML = '';49 -1 Array.from(this.original.options).forEach(op => {50 -1 if (op.selected && op.label) {51 -1 var li = document.createElement('li');52 -1 li.textContent = op.label;53 -1 li.className = this.original.dataset.tagsValueClass || 'select__value';54 -1 li.onclick = () => {55 -1 op.selected = false;56 -1 this.updateValue();57 -1 this.input.focus();58 -1 };59 -1 this.values.append(li);60 -1 } else if (!op.selected && op.hasAttribute('data-tag-custom')) {61 -1 op.remove();62 -1 }-1 48 this.values.update(this.original, () => { -1 49 this.updateValue(); -1 50 this.input.focus(); 63 51 }); 64 52 } 65 53
diff --git a/values.js b/values.js
@@ -0,0 +1,82 @@
-1 1 import { create } from './utils.js';
-1 2
-1 3 var createMeasure = function(target) {
-1 4 var wrapper = create('<div class="select__measure" aria-hidden="true">');
-1 5 var span = document.createElement('span');
-1 6 target.after(wrapper);
-1 7 wrapper.append(span);
-1 8
-1 9 return text => {
-1 10 span.textContent = text;
-1 11 var rect = span.getBoundingClientRect();
-1 12 return rect.width;
-1 13 };
-1 14 };
-1 15
-1 16 export class Values {
-1 17 constructor(input, valueClass) {
-1 18 this.gap = 4;
-1 19 this.input = input;
-1 20 this.valueClass = valueClass || 'select__value';
-1 21
-1 22 this.measure = createMeasure(input);
-1 23 this.el = create('<ul class="select__values" aria-live="polite">');
-1 24 input.before(this.el);
-1 25
-1 26 input.addEventListener('input', this.updateSize.bind(this));
-1 27 window.addEventListener('resize', this.updateSize.bind(this));
-1 28 }
-1 29
-1 30 updateSize() {
-1 31 var style = getComputedStyle(this.input);
-1 32
-1 33 // We may already have changed paddingTop, so we assume that original
-1 34 // paddingTop and paddingBottom are the same
-1 35 var paddingTop = parseInt(style.paddingBottom, 10);
-1 36
-1 37 this.el.style.top = paddingTop + 'px';
-1 38 this.el.style.bottom = style.paddingBottom;
-1 39 this.el.style.left = style.paddingLeft;
-1 40 this.el.style.right = style.paddingRight;
-1 41
-1 42 var n = this.el.children.length;
-1 43 if (n > 0) {
-1 44 var first = this.el.children[0].getBoundingClientRect();
-1 45 var last = this.el.children[n - 1].getBoundingClientRect();
-1 46 var height = last.top - first.top;
-1 47 var width = style.direction === 'ltr'
-1 48 ? last.right - first.left
-1 49 : first.right - last.left;
-1 50
-1 51 if (width + this.gap + this.measure(this.input.value) < this.el.clientWidth) {
-1 52 this.input.style.paddingTop = `${paddingTop + height}px`;
-1 53 this.input.style.textIndent = `${width + this.gap}px`;
-1 54 } else {
-1 55 this.input.style.paddingTop = `${paddingTop + height + last.height + this.gap}px`;
-1 56 this.input.style.textIndent = '0';
-1 57 }
-1 58 } else {
-1 59 this.input.style.paddingTop = `${paddingTop}px`;
-1 60 this.input.style.textIndent = '0';
-1 61 }
-1 62 }
-1 63
-1 64 update(original, onChange) {
-1 65 this.el.innerHTML = '';
-1 66 Array.from(original.options).forEach(op => {
-1 67 if (op.selected && op.label) {
-1 68 var li = document.createElement('li');
-1 69 li.textContent = op.label;
-1 70 li.className = this.valueClass;
-1 71 li.onclick = () => {
-1 72 op.selected = false;
-1 73 onChange();
-1 74 };
-1 75 this.el.append(li);
-1 76 } else if (!op.selected && op.hasAttribute('data-tag-custom')) {
-1 77 op.remove();
-1 78 }
-1 79 });
-1 80 this.updateSize();
-1 81 }
-1 82 }