select

Better select widgets in vanilla javascript.  https://p.ce9e.org/select/demo/
git clone https://git.ce9e.org/select.git

commit
f81416682f28548e9422585c83bd758a9bdd774c
parent
09bca6cff2d8edcb9abde017ba19b52f0b5fe6d9
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-08-08 11:18
display values inside of input

This is achived by setting textIndent and paddingTop on the input to
leave space for the absolutely positioned values.

Diffstat

M select.css 19 ++++++++++++++++++-
M values.js 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++

2 files changed, 71 insertions, 1 deletions


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/values.js b/values.js

@@ -1,12 +1,64 @@
    1     1 import { create } from './utils.js';
    2     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 
    3    16 export class Values {
    4    17 	constructor(input, valueClass) {
   -1    18 		this.gap = 4;
    5    19 		this.input = input;
    6    20 		this.valueClass = valueClass || 'select__value';
    7    21 
   -1    22 		this.measure = createMeasure(input);
    8    23 		this.el = create('<ul class="select__values" aria-live="polite">');
    9    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 		}
   10    62 	}
   11    63 
   12    64 	update(original, onChange) {
@@ -25,5 +77,6 @@ export class Values {
   25    77 				op.remove();
   26    78 			}
   27    79 		});
   -1    80 		this.updateSize();
   28    81 	}
   29    82 }