diff --git a/demo/src/app-routing.ts b/demo/src/app-routing.ts
index 4a93b80dc..1cbd9478d 100644
--- a/demo/src/app-routing.ts
+++ b/demo/src/app-routing.ts
@@ -11,6 +11,7 @@ import Example09 from './examples/example09';
import Example10 from './examples/example10';
import Example11 from './examples/example11';
import Example12 from './examples/example12';
+import Example13 from './examples/example13';
import Options01 from './options/options01';
import Options02 from './options/options02';
import Options03 from './options/options03';
@@ -78,6 +79,7 @@ export const exampleRouting = [
{ name: 'example10', view: '/src/examples/example10.html', viewModel: Example10, title: 'Large Select' },
{ name: 'example11', view: '/src/examples/example11.html', viewModel: Example11, title: 'The Themes' },
{ name: 'example12', view: '/src/examples/example12.html', viewModel: Example12, title: 'Checkbox/Radio Icons' },
+ { name: 'example13', view: '/src/examples/example13.html', viewModel: Example13, title: 'Dynamically Create Select' },
],
},
{
diff --git a/demo/src/examples/example13.html b/demo/src/examples/example13.html
new file mode 100644
index 000000000..9f8f88c6b
--- /dev/null
+++ b/demo/src/examples/example13.html
@@ -0,0 +1,44 @@
+
+
+
+ Dynamically create Multiple-Select with Data collection
+
+ Code
+
+ html
+ |
+ ts
+
+
+
+
+ Dynamically create a Multiple-Select instance with data
property.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/examples/example13.ts b/demo/src/examples/example13.ts
new file mode 100644
index 000000000..56158ba5b
--- /dev/null
+++ b/demo/src/examples/example13.ts
@@ -0,0 +1,87 @@
+import { multipleSelect, MultipleSelectInstance } from 'multiple-select-vanilla';
+
+export default class Example {
+ createBtnElm?: HTMLButtonElement | null;
+ destroyBtnElm?: HTMLButtonElement | null;
+ ms1?: MultipleSelectInstance;
+
+ mount() {
+ this.createBtnElm = document.querySelector('#createBtn');
+ this.destroyBtnElm = document.querySelector('#destroyBtn');
+ this.createBtnElm!.addEventListener('click', this.createMultipleSelect.bind(this));
+ this.destroyBtnElm!.addEventListener('click', this.destroyMultiSelect.bind(this));
+ }
+
+ createMultipleSelect() {
+ this.ms1 = multipleSelect('#select1', {
+ name: 'my-select',
+ single: false,
+ useSelectOptionLabelToHtml: true,
+ data: [
+ {
+ text: ' January',
+ value: 1,
+ },
+ {
+ text: 'February',
+ value: 2,
+ },
+ {
+ text: 'March',
+ value: 3,
+ },
+ {
+ text: 'April',
+ value: 4,
+ },
+ {
+ text: 'May',
+ value: 5,
+ },
+ {
+ text: 'June',
+ value: 6,
+ },
+ {
+ text: 'July',
+ value: 7,
+ },
+ {
+ text: 'August',
+ value: 8,
+ },
+ {
+ text: 'September',
+ value: 9,
+ },
+ {
+ text: 'October',
+ value: 10,
+ },
+ {
+ text: 'November',
+ value: 11,
+ },
+ {
+ text: 'December',
+ value: 12,
+ },
+ ],
+ }) as MultipleSelectInstance;
+
+ this.ms1.setSelects([1, 3, 4]);
+ }
+
+ destroyMultiSelect() {
+ console.log('destroy');
+ this.ms1?.destroy();
+ this.ms1 = undefined; // remove detached element
+ }
+
+ unmount() {
+ // destroy ms instance(s) to avoid DOM leaks
+ this.destroyMultiSelect();
+ this.createBtnElm!.removeEventListener('click', this.createMultipleSelect.bind(this));
+ this.destroyBtnElm!.removeEventListener('click', this.destroyMultiSelect.bind(this));
+ }
+}
diff --git a/demo/src/methods/methods11.ts b/demo/src/methods/methods11.ts
index 4b7bb7eb7..ae53957a7 100644
--- a/demo/src/methods/methods11.ts
+++ b/demo/src/methods/methods11.ts
@@ -1,24 +1,32 @@
import { MultipleSelectInstance, multipleSelect } from 'multiple-select-vanilla';
export default class Example {
+ buildBtnElm?: HTMLButtonElement | null;
+ destroyBtnElm?: HTMLButtonElement | null;
ms1?: MultipleSelectInstance | null;
mount() {
+ this.buildBtnElm = document.querySelector('#buildBtn');
+ this.destroyBtnElm = document.querySelector('#destroyBtn');
+ this.destroyBtnElm!.addEventListener('click', this.destroyMultiSelect.bind(this));
+ this.buildBtnElm!.addEventListener('click', this.createMultipleSelect.bind(this));
+
this.ms1 = multipleSelect('select') as MultipleSelectInstance | null;
+ }
- document.querySelector('#destroyBtn')!.addEventListener('click', () => {
- this.ms1?.destroy();
- this.ms1 = null; // remove detached element
- });
+ createMultipleSelect() {
+ this.ms1 = multipleSelect('select') as MultipleSelectInstance;
+ }
- document.querySelector('#buildBtn')!.addEventListener('click', () => {
- this.ms1 = multipleSelect('select') as MultipleSelectInstance;
- });
+ destroyMultiSelect() {
+ this.ms1?.destroy();
+ this.ms1 = null; // remove detached element
}
unmount() {
// destroy ms instance(s) to avoid DOM leaks
- this.ms1?.destroy();
- this.ms1 = undefined;
+ this.destroyMultiSelect();
+ this.buildBtnElm!.removeEventListener('click', this.destroyMultiSelect.bind(this));
+ this.destroyBtnElm!.removeEventListener('click', this.createMultipleSelect.bind(this));
}
}
diff --git a/demo/src/options/options17.html b/demo/src/options/options17.html
index 4166d8627..a1810451b 100644
--- a/demo/src/options/options17.html
+++ b/demo/src/options/options17.html
@@ -92,4 +92,27 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/options/options17.ts b/demo/src/options/options17.ts
index 1260ee7e0..3437678ba 100644
--- a/demo/src/options/options17.ts
+++ b/demo/src/options/options17.ts
@@ -4,11 +4,13 @@ export default class Example {
ms1?: MultipleSelectInstance;
ms2?: MultipleSelectInstance;
ms3?: MultipleSelectInstance;
+ ms4?: MultipleSelectInstance;
mount() {
this.ms1 = multipleSelect('.select1') as MultipleSelectInstance;
this.ms2 = multipleSelect('.select2') as MultipleSelectInstance;
this.ms3 = multipleSelect('.select3', { container: '.my-container' }) as MultipleSelectInstance;
+ this.ms4 = multipleSelect('.select4', { autoAdjustDropPosition: true, container: 'body' }) as MultipleSelectInstance;
}
unmount() {
@@ -16,8 +18,10 @@ export default class Example {
this.ms1?.destroy();
this.ms2?.destroy();
this.ms3?.destroy();
+ this.ms4?.destroy();
this.ms1 = undefined;
this.ms2 = undefined;
this.ms3 = undefined;
+ this.ms4 = undefined;
}
}
diff --git a/lib/package.json b/lib/package.json
index 5b32e9709..b01838a5a 100644
--- a/lib/package.json
+++ b/lib/package.json
@@ -8,7 +8,8 @@
".": {
"import": "./dist/esm/multiple-select.js",
"require": "./dist/cjs/multiple-select.js",
- "default": "./dist/esm/multiple-select.js"
+ "default": "./dist/esm/multiple-select.js",
+ "types": "./dist/types/index.d.ts"
},
"./*": "./*"
},
diff --git a/lib/src/MultipleSelectInstance.ts b/lib/src/MultipleSelectInstance.ts
index 5e269d41b..43c963245 100644
--- a/lib/src/MultipleSelectInstance.ts
+++ b/lib/src/MultipleSelectInstance.ts
@@ -58,8 +58,8 @@ export class MultipleSelectInstance {
this._bindEventService = new BindingEventService({ distinctEvent: true });
}
- async init() {
- await this.initLocale();
+ init() {
+ this.initLocale();
this.initContainer();
this.initData();
this.initSelected(true);
@@ -88,6 +88,7 @@ export class MultipleSelectInstance {
}
this.virtualScroll?.destroy();
+ this.dropElm?.remove();
this.parentElm.parentNode?.removeChild(this.parentElm);
if (this.fromHtml) {
@@ -104,7 +105,7 @@ export class MultipleSelectInstance {
}
}
- protected async initLocale() {
+ protected initLocale() {
if (this.options.locale) {
const locales = window.multipleSelect.locales;
const parts = this.options.locale.split(/-|_/);
@@ -152,9 +153,14 @@ export class MultipleSelectInstance {
// restore class and title from select element
this.parentElm = createDomElement('div', {
className: `ms-parent ${this.elm.className || ''}`,
- title: this.elm.getAttribute('title') || '',
});
+ // add tooltip title only when provided
+ const parentTitle = this.elm.getAttribute('title') || '';
+ if (parentTitle) {
+ this.parentElm.title = parentTitle;
+ }
+
// add placeholder to choice button
this.options.placeholder = this.options.placeholder || this.elm.getAttribute('placeholder') || '';
@@ -200,6 +206,11 @@ export class MultipleSelectInstance {
className: `ms-drop ${this.options.position}`,
});
+ // add name attribute when defined
+ if (name) {
+ this.dropElm.setAttribute('name', name);
+ }
+
this.closeElm = this.choiceElm.querySelector('.icon-close');
if (this.options.dropWidth) {
@@ -328,7 +339,7 @@ export class MultipleSelectInstance {
this.update(true);
if (this.options.isOpen) {
- setTimeout(() => this.open(), 50);
+ setTimeout(() => this.open(), 10);
}
if (this.options.openOnHover && this.parentElm) {
@@ -604,7 +615,7 @@ export class MultipleSelectInstance {
}
this.parentElm.style.width = `${this.options.width || computedWidth}px`;
- this.elm.style.display = 'block';
+ // this.elm.style.display = 'inline-block';
this.elm.classList.add('ms-offscreen');
}
@@ -817,6 +828,7 @@ export class MultipleSelectInstance {
this.dropElm.style.width = `${getElementSize(this.parentElm, 'outer', 'width')}px`;
}
+ let minHeight = this.options.minHeight;
let maxHeight = this.options.maxHeight;
if (this.options.maxHeightUnit === 'row') {
const liElm = this.dropElm.querySelector('ul>li');
@@ -824,6 +836,9 @@ export class MultipleSelectInstance {
}
const ulElm = this.dropElm.querySelector('ul');
if (ulElm) {
+ if (minHeight) {
+ ulElm.style.minHeight = `${minHeight}px`;
+ }
ulElm.style.maxHeight = `${maxHeight}px`;
}
const multElms = this.dropElm.querySelectorAll('.multiple');
@@ -890,7 +905,7 @@ export class MultipleSelectInstance {
const getSelectOptionHtml = () => {
if (this.options.useSelectOptionLabel || this.options.useSelectOptionLabelToHtml) {
- const labels = valueSelects.join(this.options.delimiter);
+ const labels = valueSelects.join(this.options.displayDelimiter);
return this.options.useSelectOptionLabelToHtml ? stripScripts(labels) : labels;
} else {
return textSelects.join(this.options.displayDelimiter);
@@ -928,7 +943,7 @@ export class MultipleSelectInstance {
console.warn('[Multiple-Select-Vanilla] Please note that the `addTitle` option was replaced with `displayTitle`.');
}
const selectType = this.options.useSelectOptionLabel || this.options.useSelectOptionLabelToHtml ? 'value' : 'text';
- spanElm.title = this.getSelects(selectType).join('');
+ spanElm.title = this.getSelects(selectType).join(this.options.displayDelimiter);
}
}
@@ -1005,6 +1020,14 @@ export class MultipleSelectInstance {
this.init();
}
+ getDropElement() {
+ return this.dropElm;
+ }
+
+ getParentElement() {
+ return this.parentElm;
+ }
+
// value html, or text, default: 'value'
getSelects(type = 'value') {
const values = [];
@@ -1319,7 +1342,7 @@ export class MultipleSelectInstance {
}
protected adjustDropWidthByText() {
- const parentWidth = this.parentElm.clientWidth;
+ const parentWidth = this.parentElm.scrollWidth;
// keep the dropWidth/width as reference, if our new calculated width is below then we will re-adjust (else do nothing)
let currentDefinedWidth: number | string = parentWidth;
@@ -1328,16 +1351,12 @@ export class MultipleSelectInstance {
}
// calculate the "Select All" element width, this text is configurable which is why we recalculate every time
- const selectAllSpanElm = this.dropElm.querySelector('.ms-select-all span') as HTMLSpanElement;
+ const selectAllSpanElm = this.dropElm.querySelector('.ms-select-all span');
const dropUlElm = this.dropElm.querySelector('ul') as HTMLUListElement;
- let liPadding = 0;
- const firstLiElm = this.dropElm.querySelector('li'); // get padding of 1st element
- if (firstLiElm) {
- const { paddingLeft, paddingRight } = window.getComputedStyle(firstLiElm);
- liPadding = parseFloat(paddingLeft) + parseFloat(paddingRight);
- }
- const selectAllElmWidth = selectAllSpanElm.clientWidth + liPadding;
+ let liPadding = 26; // there are multiple padding involved, let's fix it at 26px
+
+ const selectAllElmWidth = selectAllSpanElm?.clientWidth ?? 0 + liPadding;
const hasScrollbar = dropUlElm.scrollHeight > dropUlElm.clientHeight;
const scrollbarWidth = hasScrollbar ? this.getScrollbarWidth() : 0;
let contentWidth = 0;
diff --git a/lib/src/interfaces/multipleSelectOption.interface.ts b/lib/src/interfaces/multipleSelectOption.interface.ts
index cf568b788..97349ea66 100644
--- a/lib/src/interfaces/multipleSelectOption.interface.ts
+++ b/lib/src/interfaces/multipleSelectOption.interface.ts
@@ -28,9 +28,6 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
/** provide custom data */
data?: any | any[];
- /** Delimiter to use when display the selected options. By default this option is set to `,` */
- delimiter?: string;
-
/** Delimiter to be displayed between each option */
displayDelimiter: string;
@@ -82,6 +79,9 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
/** maxHeight unit type */
maxHeightUnit?: string;
+ /** Defaults to 150, define the minimum height property of the dropdown list. */
+ minHeight: number;
+
/** Defaults to 500, define the maximum width of the drop when using the "autoAdjustDropWidthByTextSize: true" flag. */
maxWidth?: number;
diff --git a/lib/src/styles/_variables.scss b/lib/src/styles/_variables.scss
index 36aec7eb5..a8e985956 100644
--- a/lib/src/styles/_variables.scss
+++ b/lib/src/styles/_variables.scss
@@ -29,6 +29,8 @@ $ms-drop-hide-radio-selected-bgcolor: #007bff !default;
$ms-drop-input-margin-left: -1.25rem !default;
$ms-drop-input-margin-top: 0.3rem !default;
$ms-drop-optgroup-font-weight: bold !default;
+$ms-drop-list-margin: 0px !default;
+$ms-drop-list-padding: 0px !default;
$ms-drop-list-item-level1-padding-left: 28px !default;
$ms-drop-option-divider-padding: 0 !default;
$ms-drop-option-divider-border-top: 1px solid #e9ecef !default;
@@ -39,7 +41,7 @@ $ms-drop-list-item-disabled-filter: Alpha(Opacity = 35) !default;
$ms-drop-list-item-disabled-opacity: 0.35 !default;
$ms-drop-zindex: 1050 !default;
$ms-label-margin-bottom: 0 !default;
-$ms-label-padding-left: 1.25rem !default;
+$ms-label-padding: 0 0 0 1.25rem !default;
$ms-ok-button-bg-color: #fff !default;
$ms-ok-button-bg-hover-color: #f9f9f9 !default;
$ms-ok-button-border-color: #ccc !default;
@@ -67,7 +69,7 @@ $ms-select-all-label-border: $ms-item-border !default;
$ms-select-all-label-hover-border: 1px solid transparent !default;
$ms-select-all-label-hover-bg-color: $ms-checkbox-hover-bg-color !default;
$ms-select-all-label-padding: 4px !default;
-$ms-select-all-label-span-padding-left: $ms-label-padding-left !default;
+$ms-select-all-label-span-padding: 0 0 0 20px !default;
$ms-select-all-line-height: 18px !default;
$ms-select-all-padding: 4px !default;
$ms-select-all-text-color: darken($primary-color, 5%) !default;
diff --git a/lib/src/styles/multiple-select.scss b/lib/src/styles/multiple-select.scss
index bd9063491..958b8303d 100644
--- a/lib/src/styles/multiple-select.scss
+++ b/lib/src/styles/multiple-select.scss
@@ -154,7 +154,7 @@
margin-left: 0;
}
span {
- padding-left: var(--ms-select-all-label-span-padding-left, $ms-select-all-label-span-padding-left);
+ padding: var(--ms-select-all-label-span-padding, $ms-select-all-label-span-padding);
}
}
}
@@ -208,8 +208,8 @@
.ms-drop {
ul {
overflow: auto;
- margin: 0;
- padding: 0;
+ margin: var(--ms-drop-list-margin, $ms-drop-list-margin);
+ padding: var(--ms-drop-list-padding, $ms-drop-list-padding);
> li {
background-image: none;
@@ -249,7 +249,7 @@
position: relative;
white-space: nowrap;
margin-bottom: var(--ms-label-margin-bottom, $ms-label-margin-bottom);
- padding-left: var(--ms-label-padding-left, $ms-label-padding-left);
+ padding: var(--ms-label-padding, $ms-label-padding);
&.optgroup {
font-weight: var(--ms-drop-optgroup-font-weight, $ms-drop-optgroup-font-weight);