├── src
├── BsMultiSelect2.js
├── BsMultiSelect2.jquery.js
├── LayoutFactory.js
├── InitialDomFactory.js
├── OptionsAspect.js
├── plugins
│ ├── PlaceholderCssPatchPlugin.js
│ ├── WarningCssPatchPlugin.js
│ ├── WarningBs5Plugin.js
│ ├── WarningBs4Plugin.js
│ ├── FloatingLabelCssPatchBs5Plugin.js
│ ├── BsAppearanceBs4CssPatchPlugin.js
│ ├── BsAppearanceBs5CssPatchPlugin.js
│ ├── CssPatchBs5Plugin.js
│ ├── CssPatchBs4Plugin.js
│ ├── JQueryMethodsPlugin.js
│ ├── FormResetPlugin.js
│ ├── UpdateAppearancePlugin.js
│ ├── PicksApiPlugin.js
│ ├── FormRestoreOnBackwardPlugin.js
│ ├── RtlPlugin.js
│ ├── BsAppearanceBs4Plugin.js
│ ├── BsAppearanceBs5Plugin.js
│ ├── OptionsApiPlugin.js
│ ├── LabelForAttributePlugin.js
│ ├── CustomPickStylingsPlugin.js
│ ├── FloatingLabelPlugin.js
│ ├── PicksPlugin.js
│ ├── CustomChoiceStylingsPlugin.js
│ ├── ChoicesDynamicStylingPlugin.js
│ ├── PickButtonPlugin.js
│ ├── DisableComponentPlugin.js
│ ├── index.js
│ ├── WarningPlugin.js
│ ├── HighlightPlugin.js
│ ├── HiddenOptionAltPlugin.js
│ ├── HiddenOptionPlugin.js
│ ├── CreatePopperPlugin.js
│ ├── PlaceholderPlugin.js
│ ├── DisabledOptionPlugin.js
│ └── ValidationApiPlugin.js
├── OnChangeAspect.js
├── SpecialPicksEventsAspect.js
├── ResetLayoutAspect.js
├── LoadAspect.js
├── ToolSet.js
├── CreateElementAspect.js
├── CountableChoicesListInsertAspect.js
├── ChoicesEnumerableAspect.js
├── ChoicesVisibilityAspect.js
├── AppendAspect.js
├── ShowErrorAspect.js
├── UpdateDataAspect.js
├── BsMultiSelectElement.js
├── BsMultiSelect.bs4.esm.js
├── NavigateAspect.js
├── AfterInputAspect.js
├── BsMultiSelect.esm.js
├── PicksElementAspect.js
├── OptionsLoopAspect.js
├── ResetFilterListAspect.js
├── DomFactories.js
├── CreateWrapAspect.js
├── Wraps.js
├── ProducePickAspect.js
├── BsMultiSelect.bs4.jquery.js
├── BsMultiSelect.jquery.js
├── PickDomFactory.js
├── InputAspect.js
├── CreateForJQuery.js
├── BsMultiSelect.web.js
├── ChoicesDomFactory.js
├── FilterManagerAspect.js
├── StaticManagerAspect.js
├── index.js
├── ProduceChoiceAspect.js
├── MultiSelectBuilder.js
├── ModuleFactory.js
├── PluginSet.js
├── FilterDomFactory.js
├── StaticDomFactory.js
├── AddToJQueryPrototype.js
├── PicksDomFactory.js
├── BsMultiSelectDepricatedParameters.js
├── ToolsStyling.js
├── PluginManager.js
└── ToolsDom.js
├── .stylelintignore
├── BsMultiSelect.code-workspace
├── .vscode
├── notes.txt
└── launch.json
├── .eslintignore
├── .gitignore
├── index.d.ts
├── .cache
└── .stylelintcache
├── KB.md
├── babel.bundle.umd.config.js
├── babel.bundle.esm.config.js
├── babel.mjs.config.js
├── babel.mjs.ecma5.config.js
├── .stylelintrc
├── debugJsSimplePicks.html
├── dist
└── css
│ ├── BsMultiSelect.bs4.min.css
│ ├── BsMultiSelect.min.css
│ ├── BsMultiSelect.bs4.css
│ ├── BsMultiSelect.css
│ ├── BsMultiSelect.bs4.css.map
│ └── BsMultiSelect.css.map
└── npm-debug.log
/src/BsMultiSelect2.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/BsMultiSelect2.jquery.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | **/*.min.css
2 | **/dist/
3 | **/node_modules/
4 |
5 |
--------------------------------------------------------------------------------
/BsMultiSelect.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "./"
5 | }
6 | ]
7 | }
--------------------------------------------------------------------------------
/.vscode/notes.txt:
--------------------------------------------------------------------------------
1 | Possible:
2 | "url": "file:///D:/cot/DashboardCode/BsMultiSelect/snippetJsAdd.html"
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.min.js
2 | **/dist/
3 | **/vendor/
4 | /_gh_pages/
5 | /package.js
6 | /build/rollup.config.js
--------------------------------------------------------------------------------
/src/LayoutFactory.js:
--------------------------------------------------------------------------------
1 | export function LayoutFactory( eventHandlers){
2 | return {
3 |
4 | }
5 | }
--------------------------------------------------------------------------------
/src/InitialDomFactory.js:
--------------------------------------------------------------------------------
1 | // export function InitialDomFactory(initialElement){
2 | // return {
3 |
4 | // }
5 | // }
--------------------------------------------------------------------------------
/src/OptionsAspect.js:
--------------------------------------------------------------------------------
1 | // export function OptionsAspect(options){
2 | // return {
3 | // getOptions : () => options
4 | // }
5 | // }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directories
2 | node_modules/
3 |
4 | # Optional npm cache directory
5 | .npm
6 |
7 | # Optional eslint cache
8 | .eslintcache
9 |
--------------------------------------------------------------------------------
/src/plugins/PlaceholderCssPatchPlugin.js:
--------------------------------------------------------------------------------
1 | export function PlaceholderCssPatchPlugin(defaults){
2 | defaults.cssPatch.filterInput_empty = 'form-control'
3 | }
--------------------------------------------------------------------------------
/src/OnChangeAspect.js:
--------------------------------------------------------------------------------
1 | export function OnChangeAspect(staticDom, name) {
2 | return {
3 | onChange(){
4 | staticDom.trigger(name)
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/SpecialPicksEventsAspect.js:
--------------------------------------------------------------------------------
1 | export function SpecialPicksEventsAspect(){
2 | return {
3 | backSpace(pick){
4 | pick.setSelectedFalse();
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/ResetLayoutAspect.js:
--------------------------------------------------------------------------------
1 | export function ResetLayoutAspect(resetFilterAspect){
2 | return {
3 | resetLayout(){
4 | resetFilterAspect.resetFilter();
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/LoadAspect.js:
--------------------------------------------------------------------------------
1 |
2 | export function LoadAspect(optionsLoopAspect){
3 | return{
4 | load(){ // redriven in AppearancePlugin, FormRestoreOnBackwardPlugin
5 | optionsLoopAspect.loop();
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/ToolSet.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from './ToolsJs'
2 | import {EventBinder} from './ToolsDom'
3 | import {addStyling, toggleStyling} from './ToolsStyling'
4 |
5 | export let utilities = {composeSync, EventBinder, addStyling, toggleStyling}
--------------------------------------------------------------------------------
/src/plugins/WarningCssPatchPlugin.js:
--------------------------------------------------------------------------------
1 | export function WarningCssPatchPlugin(defaults){
2 | defaults.cssPatch.warning = {paddingLeft: '.25rem', paddingRight: '.25rem', zIndex: 4, fontSize:'small', backgroundColor: 'var(--bs-warning)'}
3 | }
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@dashboardcode/bsmultiselect'{
2 | // example
3 | export function ModuleFactory(environment:any): any
4 | //export declare type ModuleFactory = (environment: any) => any;
5 | }
6 |
7 |
8 | //export * from './src';
--------------------------------------------------------------------------------
/src/CreateElementAspect.js:
--------------------------------------------------------------------------------
1 | export function CreateElementAspect(createElement, createElementFromHtml, createElementFromHtmlPutAfter){
2 | return {
3 | createElement,
4 | createElementFromHtml,
5 | createElementFromHtmlPutAfter
6 | }
7 | }
--------------------------------------------------------------------------------
/.cache/.stylelintcache:
--------------------------------------------------------------------------------
1 | [{"D:\\cot\\DashboardCode\\BsMultiSelect\\scss\\BsMultiSelect.scss":"1","D:\\cot\\DashboardCode\\BsMultiSelect\\scss\\BsMultiSelect.bs4.scss":"2"},{"size":8172,"mtime":1629851082166,"hashOfConfig":"3"},{"size":8430,"mtime":1638303239148,"hashOfConfig":"3"},"xcst03"]
--------------------------------------------------------------------------------
/src/CountableChoicesListInsertAspect.js:
--------------------------------------------------------------------------------
1 | export function CountableChoicesListInsertAspect(wrapsCollection, countableChoicesList){
2 | return {
3 | countableChoicesListInsert(wrap, key){
4 | let choiceNext = wrapsCollection.getNext(key);
5 | countableChoicesList.add(wrap, choiceNext)
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/plugins/WarningBs5Plugin.js:
--------------------------------------------------------------------------------
1 | import {plug as plug2, preset as preset2} from './WarningPlugin';
2 |
3 | export function WarningBs5Plugin(defaults){
4 | preset(defaults);
5 | return {plug:plug2};
6 | }
7 |
8 | export function preset(defaults){
9 | defaults.css.warning = 'alert-warning';
10 | preset2(defaults);
11 | }
12 |
--------------------------------------------------------------------------------
/src/ChoicesEnumerableAspect.js:
--------------------------------------------------------------------------------
1 | export function ChoicesEnumerableAspect(countableChoicesList, getNext){
2 | return {
3 | forEach(f){
4 | let wrap = countableChoicesList.getHead();
5 | while(wrap){
6 | f(wrap);
7 | wrap = getNext(wrap);
8 | }
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/plugins/WarningBs4Plugin.js:
--------------------------------------------------------------------------------
1 | import {plug as plug2, preset as preset2} from './WarningPlugin';
2 |
3 | export function WarningBs4Plugin(defaults){
4 | preset(defaults);
5 | return {plug:plug2};
6 | }
7 |
8 | export function preset(defaults){
9 | defaults.css.warning = 'alert-warning bg-warning';
10 | preset2(defaults);
11 | }
12 |
--------------------------------------------------------------------------------
/src/plugins/FloatingLabelCssPatchBs5Plugin.js:
--------------------------------------------------------------------------------
1 | export function FloatingLabelCssPatchBs5Plugin(defaults){
2 | let cssPatch = defaults.cssPatch;
3 | cssPatch.label_floating_lifted = {opacity: '.65', transform : 'scale(.85) translateY(-.5rem) translateX(.15rem)'};
4 | cssPatch.picks_floating_lifted = {paddingTop: '1.625rem', paddingLeft:'0.8rem', paddingBottom : '0'};
5 | }
--------------------------------------------------------------------------------
/KB.md:
--------------------------------------------------------------------------------
1 | # RTL
2 | https://www.rtlstyling.com/posts/rtl-styling/ (RU: https://habr.com/ru/post/484886/ )
3 |
4 | # NPM
5 |
6 | npm publish --tag beta
7 |
8 |
9 | # NPM update
10 | npm outdated -g
11 | npm update -g
12 |
13 | # Git
14 | "please check out a branch to push to a remote"
15 | git branch -r // list all remote branches
16 | git branch --show-current // get current branch
--------------------------------------------------------------------------------
/src/ChoicesVisibilityAspect.js:
--------------------------------------------------------------------------------
1 | export function ChoicesVisibilityAspect(choicesElement) {
2 |
3 | return {
4 | isChoicesVisible(){
5 | return choicesElement.style.display != 'none'},
6 | setChoicesVisible(visible){
7 | choicesElement.style.display = visible ? 'block' : 'none';
8 | },
9 | updatePopupLocation(){
10 |
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 | "type": "pwa-chrome",
10 | "request": "launch",
11 | "name": "Launch Chrome against localhost",
12 | "url": "http://127.0.0.1:5500/debugJsSimple.html",
13 |
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/plugins/BsAppearanceBs4CssPatchPlugin.js:
--------------------------------------------------------------------------------
1 | export function BsAppearanceBs4CssPatchPlugin(defaults){
2 | let cssPatch = defaults.cssPatch;
3 | cssPatch.picks_def = {minHeight: 'calc(2.25rem + 2px)'};
4 | cssPatch.picks_lg = {minHeight: 'calc(2.875rem + 2px)'};
5 | cssPatch.picks_sm = {minHeight: 'calc(1.8125rem + 2px)'};
6 |
7 | cssPatch.picks_focus_valid = {borderColor: '', boxShadow: '0 0 0 0.2rem rgba(40, 167, 69, 0.25)'};
8 | cssPatch.picks_focus_invalid = {borderColor: '', boxShadow: '0 0 0 0.2rem rgba(220, 53, 69, 0.25)'};
9 | }
--------------------------------------------------------------------------------
/src/AppendAspect.js:
--------------------------------------------------------------------------------
1 | export function AppendAspect(){
2 | return {
3 | appendToContainer: (containerElement, picksDom, filterDom, choicesDom, isDisposablePicksElementFlag)=> {
4 | picksDom.pickFilterElement.appendChild(filterDom.filterInputElement);
5 | picksDom.picksElement.appendChild(picksDom.pickFilterElement);
6 | containerElement.appendChild(choicesDom.choicesElement);
7 | if (isDisposablePicksElementFlag)
8 | containerElement.appendChild(picksDom.picksElement)
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/plugins/BsAppearanceBs5CssPatchPlugin.js:
--------------------------------------------------------------------------------
1 | export function BsAppearanceBs5CssPatchPlugin(defaults){
2 | let cssPatch = defaults.cssPatch;
3 | cssPatch.picks_def = {minHeight: 'calc(2.25rem + 2px)'};
4 | cssPatch.picks_lg = {minHeight: 'calc(2.875rem + 2px)'};
5 | cssPatch.picks_sm = {minHeight: 'calc(1.8125rem + 2px)'};
6 | cssPatch.picks_floating_def = {minHeight: 'calc(3.5rem + 2px)'};
7 |
8 | cssPatch.picks_focus_valid = {borderColor: '', boxShadow: '0 0 0 0.2rem rgba(40, 167, 69, 0.25)'};
9 | cssPatch.picks_focus_invalid = {borderColor: '', boxShadow: '0 0 0 0.2rem rgba(220, 53, 69, 0.25)'};
10 | }
--------------------------------------------------------------------------------
/src/ShowErrorAspect.js:
--------------------------------------------------------------------------------
1 | export function ShowErrorAspect(staticDom){
2 | return {
3 | showError(error){
4 | let {createElementAspect, initialElement} = staticDom;
5 | let errorElement = createElementAspect.createElement('SPAN');
6 | errorElement.style.backgroundColor = 'red';
7 | errorElement.style.color = 'white';
8 | errorElement.style.block = 'inline-block';
9 | errorElement.style.padding = '0.2rem 0.5rem';
10 | errorElement.textContent = 'BsMultiSelect ' + error.toString();
11 | initialElement.parentNode.insertBefore(errorElement, initialElement.nextSibling);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/UpdateDataAspect.js:
--------------------------------------------------------------------------------
1 | export function UpdateDataAspect(choicesDom, wraps, picksList, optionsLoopAspect, resetLayoutAspect){
2 | return {
3 | updateData(){
4 | // close drop down , remove filter
5 | resetLayoutAspect.resetLayout();
6 | choicesDom.choicesListElement.innerHTML = ""; // TODO: there should better "optimization"
7 | wraps.clear();
8 | picksList.forEach(pick=>pick.dispose());
9 | picksList.reset();
10 | optionsLoopAspect.loop();
11 | }
12 | }
13 | }
14 |
15 | export function UpdateAspect(updateDataAspect){
16 | return {
17 | update(){
18 | updateDataAspect.updateData();
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/babel.bundle.umd.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //retainLines: true,
3 | "presets": [
4 | [
5 | "@babel/preset-env",
6 | {
7 | "loose": true, // ES6 to ES5
8 | "bugfixes": true,
9 | // "useBuiltIns": "usage",
10 | "modules": false,
11 | "exclude": ["transform-typeof-symbol"],
12 | "targets": {
13 | "browsers": [
14 | "chrome >= 45", "Firefox >= 38", "Explorer >= 10", "edge >= 12", "iOS >= 9","Safari >= 9","Android >= 4.4","Opera >= 30"
15 | ]
16 | },
17 | "debug": true
18 | }
19 | ]
20 | ]
21 | }
--------------------------------------------------------------------------------
/src/BsMultiSelectElement.js:
--------------------------------------------------------------------------------
1 |
2 | import {BsMultiSelect} from './BsMultiSelect.esm'
3 |
4 | export class BsMultiSelectElement extends HTMLElement {
5 | constructor() {
6 | super(); // must by spec
7 | var shadowRoot = this.attachShadow({mode: 'open'});
8 | this.multiSelect = BsMultiSelect(this, environment, settings)
9 | shadowRoot.innerHTML = `
stub text
`; // container
10 | // https://developers.google.com/web/fundamentals/web-components/shadowdom
11 | }
12 | // connectedCallback(){
13 |
14 | // }
15 | // disconnectedCallback(){
16 |
17 | // }
18 | // attributeChangedCallback(attrName, oldVal, newVal){
19 |
20 | // }
21 | // adoptedCallback(){
22 |
23 | // }
24 | }
25 |
--------------------------------------------------------------------------------
/src/plugins/CssPatchBs5Plugin.js:
--------------------------------------------------------------------------------
1 | import {CssPatchPlugin} from './CssPatchPlugin';
2 |
3 | import {PicksDomFactoryPlugCssPatchBs5} from '../PicksDomFactory' // TODO move specific styles to button plugin
4 | import {ChoiceDomFactoryPlugCssPatch} from '../ChoiceDomFactory'
5 | import {ChoicesDomFactoryPlugCssPatch} from '../ChoicesDomFactory'
6 | import {FilterDomFactoryPlugCssPatch} from '../FilterDomFactory'
7 |
8 | export function CssPatchBs5Plugin(defaults){
9 | var cssPatch = {};
10 |
11 | PicksDomFactoryPlugCssPatchBs5(cssPatch);
12 | ChoiceDomFactoryPlugCssPatch(cssPatch);
13 | ChoicesDomFactoryPlugCssPatch(cssPatch);
14 | FilterDomFactoryPlugCssPatch(cssPatch);
15 |
16 | defaults.cssPatch = cssPatch;
17 | return CssPatchPlugin(defaults)
18 | }
--------------------------------------------------------------------------------
/babel.bundle.esm.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //retainLines: true,
3 | "presets": [
4 | [
5 | "@babel/preset-env",
6 | {
7 | "loose": true, // ES6 to ES5
8 | "bugfixes": true,
9 | // "useBuiltIns": "usage",
10 | "modules": false,
11 | "exclude": ["transform-typeof-symbol"],
12 | "targets": {
13 | "browsers": [
14 | // where "script module" is possoble
15 | "chrome >= 61", "Firefox >= 60", "edge >= 16", "iOS >= 10.3","Safari >= 10.1","Android >= 93","Opera >= 48"
16 | ]
17 | },
18 | "debug": true
19 | }
20 | ]
21 | ]
22 | }
--------------------------------------------------------------------------------
/src/BsMultiSelect.bs4.esm.js:
--------------------------------------------------------------------------------
1 | import { createDefaultCssBs4 } from './DomFactories'
2 | import { Bs4PluginSet } from './PluginSet'
3 | import { ModuleFactory as ModuleFactoryImpl } from "./ModuleFactory"
4 |
5 | const defaultCss = createDefaultCssBs4();
6 |
7 | function ModuleFactory(environment){
8 | return ModuleFactoryImpl(environment, Bs4PluginSet, defaultCss);
9 | }
10 |
11 | function legacyConstructor(element, environment, settings){
12 | console.log("DashboarCode.BsMultiSelect: 'BsMultiSelect' is depricated, use - ModuleFactory(environment).BsMultiSelect(element, settings)");
13 | var {BsMultiSelect} = ModuleFactory(environment);
14 | var bsMultiSelect = BsMultiSelect(element, settings);
15 | return bsMultiSelect;
16 | }
17 |
18 | export {
19 | legacyConstructor as BsMultiSelect,
20 | ModuleFactory
21 | }
--------------------------------------------------------------------------------
/src/NavigateAspect.js:
--------------------------------------------------------------------------------
1 | export function HoveredChoiceAspect(){
2 | let hoveredChoice=null;
3 | return {
4 | getHoveredChoice: () => hoveredChoice,
5 | setHoveredChoice: (wrap) => {hoveredChoice = wrap},
6 | resetHoveredChoice() {
7 | if (hoveredChoice) {
8 | hoveredChoice.choice.setHoverIn(false)
9 | hoveredChoice = null;
10 | }
11 | }
12 | }
13 | }
14 |
15 | export function NavigateAspect(hoveredChoiceAspect, navigate){
16 | return {
17 | hoverIn(wrap){
18 | hoveredChoiceAspect.resetHoveredChoice();
19 | hoveredChoiceAspect.setHoveredChoice(wrap);
20 | wrap.choice.setHoverIn(true);
21 | },
22 | navigate: (down) => navigate(down, hoveredChoiceAspect.getHoveredChoice()),
23 | }
24 | }
--------------------------------------------------------------------------------
/src/AfterInputAspect.js:
--------------------------------------------------------------------------------
1 | export function AfterInputAspect(filterManagerAspect, navigateAspect, choicesVisibilityAspect, hoveredChoiceAspect){
2 | return {
3 | visible(showChoices, visibleCount){
4 | let panelIsVisble = choicesVisibilityAspect.isChoicesVisible();
5 | if (!panelIsVisble) {
6 | showChoices();
7 | }
8 | if (visibleCount == 1) {
9 | navigateAspect.hoverIn(filterManagerAspect.getNavigateManager().getHead())
10 | } else {
11 | if (panelIsVisble)
12 | hoveredChoiceAspect.resetHoveredChoice();
13 | }
14 | },
15 | notVisible(hideChoices){
16 | if (choicesVisibilityAspect.isChoicesVisible()){
17 | hideChoices();
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/babel.mjs.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //retainLines: true,
3 | // "presets": [
4 | // [
5 | // "@babel/preset-env",
6 | // {
7 | // "loose": false, // ES6 to ES5
8 | // "bugfixes": true,
9 | // // "useBuiltIns": "usage",
10 | // "modules": false,
11 | // "exclude": ["transform-typeof-symbol"],
12 | // // "targets": {
13 | // // "browsers": [
14 | // // // browsers that can load ESM bundles: https://caniuse.com/es6-module
15 | // // "chrome >= 61", "Firefox >= 60", "edge >= 16", "iOS >= 10.3","Safari >= 10.1","Android >= 93","Opera >= 48"]
16 | // // },
17 | // "debug": true
18 | // }
19 | // ]
20 | // ]
21 | }
--------------------------------------------------------------------------------
/src/BsMultiSelect.esm.js:
--------------------------------------------------------------------------------
1 | import {createDefaultCssBs5} from "./DomFactories";
2 | import {Bs4PluginSet} from './PluginSet'
3 | import {ModuleFactory as ModuleFactoryImpl} from "./ModuleFactory";
4 |
5 | const defaultCss = createDefaultCssBs5();
6 |
7 | function ModuleFactory(environment){
8 | return ModuleFactoryImpl(
9 | environment,
10 | Bs4PluginSet,
11 | defaultCss
12 | );
13 | }
14 |
15 | function legacyConstructor(element, environment, settings){
16 | console.log("DashboarCode.BsMultiSelect: 'BsMultiSelect' is depricated, use - ModuleFactory(environment).BsMultiSelect(element, settings)");
17 | var {BsMultiSelect} = ModuleFactory(environment);
18 | var bsMultiSelect = BsMultiSelect(element, settings);
19 | return bsMultiSelect;
20 | }
21 |
22 | export {
23 | legacyConstructor as BsMultiSelect,
24 | ModuleFactory
25 | }
--------------------------------------------------------------------------------
/src/plugins/CssPatchBs4Plugin.js:
--------------------------------------------------------------------------------
1 | import {CssPatchPlugin} from './CssPatchPlugin';
2 |
3 | //import {PickDomFactoryPlugCssPatch} from '../PickDomFactory'
4 | import {PicksDomFactoryPlugCssPatchBs4} from '../PicksDomFactory' // TODO move specific styles to button plugin
5 | import {ChoiceDomFactoryPlugCssPatch} from '../ChoiceDomFactory'
6 | import {ChoicesDomFactoryPlugCssPatch} from '../ChoicesDomFactory'
7 | import {FilterDomFactoryPlugCssPatch} from '../FilterDomFactory'
8 |
9 | export function CssPatchBs4Plugin(defaults) {
10 | var cssPatch = {};
11 |
12 | //PickDomFactoryPlugCssPatch(cssPatch);
13 | PicksDomFactoryPlugCssPatchBs4(cssPatch);
14 | ChoiceDomFactoryPlugCssPatch(cssPatch);
15 | ChoicesDomFactoryPlugCssPatch(cssPatch);
16 | FilterDomFactoryPlugCssPatch(cssPatch);
17 |
18 | defaults.cssPatch = cssPatch;
19 | return CssPatchPlugin(defaults)
20 | }
--------------------------------------------------------------------------------
/src/PicksElementAspect.js:
--------------------------------------------------------------------------------
1 | import {EventBinder, containsAndSelf} from './ToolsDom';
2 |
3 | export function PicksElementAspect(picksElement){
4 | var componentDisabledEventBinder = EventBinder();
5 | var skipoutAndResetMousedownEventBinder = EventBinder();
6 | return {
7 | containsAndSelf(element){
8 | return containsAndSelf(picksElement, element);
9 | },
10 | onClickUnbind(){
11 | componentDisabledEventBinder.unbind();
12 | },
13 | onClick(handler){
14 | componentDisabledEventBinder.bind(picksElement, "click", handler);
15 | },
16 | onMousedown(handler){
17 | skipoutAndResetMousedownEventBinder.bind(picksElement, "mousedown", handler);
18 | },
19 | unbind(){
20 | skipoutAndResetMousedownEventBinder.unbind();
21 | componentDisabledEventBinder.unbind();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/OptionsLoopAspect.js:
--------------------------------------------------------------------------------
1 | export function OptionAttachAspect(createWrapAspect, createChoiceBaseAspect, buildAndAttachChoiceAspect, wraps){
2 | return {
3 | attach(option){
4 | let wrap = createWrapAspect.createWrap(option);
5 | wrap.choice = createChoiceBaseAspect.createChoiceBase(option);
6 |
7 |
8 | wraps.push(wrap); // note: before attach because attach need it for navigation management
9 | buildAndAttachChoiceAspect.buildAndAttachChoice(wrap);
10 | //wraps.push(wrap);
11 | }
12 | }
13 | }
14 |
15 | export function OptionsLoopAspect(dataWrap, optionAttachAspect){
16 | return{
17 | loop(){
18 | let options = dataWrap.getOptions();
19 | for(let i = 0; i {
9 | return {
10 | layout: () => {
11 | let {staticDom, choicesDom, filterDom, picksList, picksDom} = aspects;
12 | return {
13 | buildApi(api){
14 | api.getContainer = () => staticDom.containerElement;
15 | api.getChoices = () => choicesDom.choicesElement;
16 | api.getChoicesList = () => choicesDom.choicesListElement;
17 | api.getFilterInput = () => filterDom.filterInputElement;
18 | api.getPicks = () => picksDom.picksElement;
19 | api.picksCount = () => picksList.getCount();
20 | }
21 | }
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/ResetFilterListAspect.js:
--------------------------------------------------------------------------------
1 | export function ResetFilterListAspect(filterDom, filterManagerAspect){
2 | return {
3 | forceResetFilter(){ // over in PlaceholderPlugin
4 | filterDom.setEmpty();
5 | filterManagerAspect.processEmptyInput(); // over in PlaceholderPlugin
6 | }
7 | }
8 | }
9 |
10 | export function ResetFilterAspect(filterDom, resetFilterListAspect){
11 | return {
12 | resetFilter(){ // call in OptionsApiPlugin
13 | if (!filterDom.isEmpty()) // call in Placeholder
14 | resetFilterListAspect.forceResetFilter(); // over in Placeholder
15 | }
16 | }
17 | }
18 |
19 | export function FocusInAspect(picksDom){
20 | return {
21 | setFocusIn(focus){ // call in OptionsApiPlugin
22 | picksDom.setIsFocusIn(focus) // unique call, call BsAppearancePlugin
23 | picksDom.toggleFocusStyling() // over BsAppearancePlugin
24 | }
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/src/DomFactories.js:
--------------------------------------------------------------------------------
1 | import {PickDomFactoryPlugCss} from './PickDomFactory'
2 | import {PicksDomFactoryPlugCss} from './PicksDomFactory'
3 | import {ChoiceDomFactoryPlugCssBs5, ChoiceDomFactoryPlugCssBs4} from './ChoiceDomFactory'
4 | import {ChoicesDomFactoryPlugCss} from './ChoicesDomFactory'
5 | import {FilterDomFactoryPlugCss} from './FilterDomFactory'
6 |
7 | export function createDefaultCssBs5(){
8 | var defaultCss={};
9 | PickDomFactoryPlugCss(defaultCss)
10 | PicksDomFactoryPlugCss(defaultCss)
11 | ChoiceDomFactoryPlugCssBs5(defaultCss)
12 | ChoicesDomFactoryPlugCss(defaultCss)
13 | FilterDomFactoryPlugCss(defaultCss)
14 | return defaultCss;
15 | }
16 |
17 | export function createDefaultCssBs4(){
18 | var defaultCss={}
19 | PickDomFactoryPlugCss(defaultCss)
20 | PicksDomFactoryPlugCss(defaultCss)
21 | ChoiceDomFactoryPlugCssBs4(defaultCss)
22 | ChoicesDomFactoryPlugCss(defaultCss)
23 | FilterDomFactoryPlugCss(defaultCss)
24 | return defaultCss;
25 | }
--------------------------------------------------------------------------------
/src/plugins/FormResetPlugin.js:
--------------------------------------------------------------------------------
1 | import {EventBinder, closestByTagName} from '../ToolsDom';
2 |
3 | export function FormResetPlugin(){
4 | return {
5 | plug
6 | }
7 | }
8 |
9 | export function plug(){
10 | return (aspects) => {
11 | return {
12 | layout: () => {
13 | var {staticDom, updateDataAspect, environment} = aspects;
14 |
15 | var eventBuilder = EventBinder();
16 | if (staticDom.selectElement){
17 | var form = closestByTagName(staticDom.selectElement, 'FORM');
18 | if (form) {
19 | eventBuilder.bind(form,
20 | 'reset',
21 | () => environment.window.setTimeout( ()=>updateDataAspect.updateData() ) );
22 | }
23 | }
24 | return {
25 | dispose(){
26 | eventBuilder.unbind();
27 | }
28 | }
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/CreateWrapAspect.js:
--------------------------------------------------------------------------------
1 |
2 | // no overrides (not an aspect, just )
3 | export function CreateChoiceBaseAspect(dataWrap){
4 | return {
5 | createChoiceBase(option){
6 | return {
7 | // navigation and filter support
8 | filteredPrev: null,
9 | filteredNext: null,
10 | searchText: dataWrap.getText(option).toLowerCase().trim(), // TODO make an index abstraction
11 |
12 | // internal state handlers, so they do not have "update semantics"
13 | isHoverIn: false,
14 |
15 | setHoverIn: null,
16 |
17 | choiceDom:null,
18 |
19 | itemPrev: null,
20 | itemNext: null,
21 |
22 | dispose: null
23 | }
24 | }
25 | }
26 | }
27 |
28 | export function CreateWrapAspect(){
29 | return {
30 | createWrap(option){
31 | return {
32 | option: option
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Wraps.js:
--------------------------------------------------------------------------------
1 | export function Wraps(wrapsCollection,
2 | listFacade_reset, listFacade_remove, listFacade_add)
3 | {
4 | return {
5 | push: (wrap) => push(wrap, wrapsCollection, listFacade_add),
6 | insert: (key, wrap) => insert(key, wrap, wrapsCollection, listFacade_add),
7 | remove: (key) => {
8 | var wrap = wrapsCollection.remove(key);
9 | listFacade_remove(wrap);
10 | return wrap;
11 | },
12 | clear: () => {
13 | wrapsCollection.reset();
14 | listFacade_reset();
15 | },
16 | dispose: () => wrapsCollection.forLoop(wrap => wrap.dispose())
17 | }
18 | }
19 |
20 | function push(wrap, wrapsCollection, listFacade_add){
21 | wrapsCollection.push(wrap);
22 | listFacade_add(wrap);
23 | }
24 |
25 | function insert(key, wrap, wrapsCollection, listFacade_add){
26 | if (key>=wrapsCollection.getCount()) {
27 | push(wrap, wrapsCollection, listFacade_add);
28 | }
29 | else {
30 | wrapsCollection.add(wrap, key);
31 | listFacade_add(wrap, key);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/plugins/UpdateAppearancePlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 |
3 | export function UpdateAppearancePlugin(){
4 | return {
5 | plug
6 | }
7 | }
8 |
9 | export function plug(){
10 | var updateAppearanceAspect = UpdateAppearanceAspect();
11 | return (aspects) => {
12 | aspects.updateAppearanceAspect = updateAppearanceAspect;
13 | return {
14 | layout: () => {
15 | var {updateAspect, loadAspect} = aspects;
16 |
17 | updateAspect.update = composeSync(updateAspect.update, ()=>updateAppearanceAspect.updateAppearance())
18 | loadAspect.load = composeSync(loadAspect.load, ()=>updateAppearanceAspect.updateAppearance())
19 |
20 | return{
21 | buildApi(api){
22 | api.updateAppearance = ()=>updateAppearanceAspect.updateAppearance();
23 | }
24 | }
25 | }
26 |
27 | }
28 | }
29 | }
30 |
31 | function UpdateAppearanceAspect(){
32 | return {
33 | updateAppearance(){}
34 | }
35 | }
--------------------------------------------------------------------------------
/src/ProducePickAspect.js:
--------------------------------------------------------------------------------
1 | export function ProducePickAspect(picksDom, pickDomFactory){
2 | return {
3 | // overrided by DisableOptionPlugin
4 | producePick(wrap){
5 | let {pickElement, attach, detach} = picksDom.createPickElement();
6 | let pickDom = {pickElement};
7 | let pickDomManagerHandlers = {attach, detach};
8 |
9 | let pick = {
10 | wrap,
11 | pickDom,
12 | pickDomManagerHandlers,
13 |
14 | dispose: () => {
15 | detach();
16 | pickDom.pickElement=null;
17 | pickDomManagerHandlers.attach=null;
18 | pick.wrap=null;
19 | pick.pickDom=null;
20 | pick.pickDomManagerHandlers=null;
21 | }
22 | }
23 |
24 | wrap.pick=pick;
25 | pickDomFactory.create(pick);
26 |
27 | pick.pickDomManagerHandlers.attach();
28 |
29 | return pick;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/plugins/PicksApiPlugin.js:
--------------------------------------------------------------------------------
1 | export function PicksApiPlugin(){
2 | return {
3 | plug
4 | }
5 | }
6 |
7 | export function plug(){
8 | return (aspects) => {
9 | return {
10 | buildApi(api){
11 | let {picksList, createWrapAspect} = aspects;
12 | api.forEachPeak = (f) =>
13 | picksList.forEach(wrap=>f(wrap.option));
14 | // TODO: getHeadPeak
15 | api.getTailPeak = () => picksList.getTail()?.option;
16 | api.countPeaks = () => picksList.getCount();
17 | api.isEmptyPeaks = () => picksList.isEmpty();
18 |
19 | api.addPick = (option) => {
20 | let wrap = createWrapAspect.createWrap(option);
21 | // TODO should be moved to specific plugins
22 | wrap.updateDisabled = () => {};
23 | wrap.updateHidden = () => {};
24 |
25 | let pickHandlers = wrap.choice.addPickForChoice();
26 | }
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/BsMultiSelect.bs4.jquery.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery'
2 | import Popper from 'popper.js'
3 |
4 | import {createForJQuery} from './CreateForJQuery'
5 |
6 | import { Bs4PluginSet, multiSelectPlugins, picksPlugins, allPlugins} from './PluginSet'
7 | import {createDefaultCssBs4} from './DomFactories'
8 |
9 |
10 | import {MultiSelectBuilder} from './MultiSelectBuilder'
11 | import {utilities} from './ToolSet'
12 |
13 | import {shallowClearClone} from './ToolsJs'
14 |
15 | const defaultCss = createDefaultCssBs4();
16 |
17 | const BsMultiSelect = (
18 | (window, jQuery, createPopper) => {
19 | let plugins = shallowClearClone(Bs4PluginSet, multiSelectPlugins);
20 | return createForJQuery(window, jQuery, createPopper, 'BsMultiSelect', plugins, defaultCss )
21 | }
22 | )(window, $, Popper)
23 |
24 | const BsPicks = (
25 | (window, jQuery, createPopper) => {
26 | let plugins = shallowClearClone(Bs4PluginSet, picksPlugins);
27 | return createForJQuery(window, jQuery, createPopper, 'BsPicks', plugins, defaultCss)
28 | }
29 | )(window, $, Popper)
30 |
31 | export default {BsMultiSelect, BsPicks , MultiSelectTools: {MultiSelectBuilder, plugins: shallowClearClone(Bs4PluginSet, allPlugins), defaultCss, utilities} }
--------------------------------------------------------------------------------
/src/BsMultiSelect.jquery.js:
--------------------------------------------------------------------------------
1 | import Popper from '@popperjs/core'
2 |
3 | import {createForJQuery} from './CreateForJQuery'
4 |
5 | import {Bs5PluginSet, multiSelectPlugins, picksPlugins, allPlugins} from './PluginSet'
6 | import {createDefaultCssBs5} from './DomFactories'
7 |
8 | import {MultiSelectBuilder} from './MultiSelectBuilder'
9 | import {utilities} from './ToolSet'
10 |
11 | import {shallowClearClone} from './ToolsJs'
12 |
13 | const defaultCss = createDefaultCssBs5();
14 |
15 | const BsMultiSelect = (
16 | (window, jQuery, globalPopper) => {
17 | let plugins = shallowClearClone(Bs5PluginSet, multiSelectPlugins);
18 | return createForJQuery(window, jQuery, globalPopper, 'BsMultiSelect', plugins, defaultCss)
19 | }
20 | )(window, window.jQuery, Popper)
21 |
22 | const BsPicks = (
23 | (window, jQuery, globalPopper) => {
24 | let plugins = shallowClearClone(Bs5PluginSet, picksPlugins);
25 | return createForJQuery(window, jQuery, globalPopper, 'BsPicks', plugins, defaultCss)
26 | }
27 | )(window, window.jQuery, Popper)
28 |
29 | export default { BsMultiSelect, BsPicks,
30 | MultiSelectTools: {MultiSelectBuilder, plugins: shallowClearClone(Bs5PluginSet, allPlugins), defaultCss, utilities}
31 | }
--------------------------------------------------------------------------------
/src/PickDomFactory.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from './ToolsJs';
2 | import {addStyling} from './ToolsStyling';
3 |
4 | // empty but can be usefull in custom development
5 | export function PickDomFactoryPlugCss(css){
6 | css.pickContent = '';
7 | }
8 |
9 | export function PickDomFactory(css, createElementAspect, dataWrap){
10 | return {
11 | create(pick){
12 | let wrap = pick.wrap;
13 | let {pickDom, pickDomManagerHandlers} = pick;
14 | let pickElement = pickDom.pickElement;
15 |
16 | let pickContentElement = createElementAspect.createElement('SPAN');
17 |
18 | pickElement.appendChild(pickContentElement);
19 | pickDom.pickContentElement=pickContentElement;
20 | pickDomManagerHandlers.updateData = () => {
21 | // this is not a generic because there could be more then one text field.
22 | pickContentElement.textContent = dataWrap.getText(wrap.option);
23 | }
24 | addStyling(pickContentElement, css.pickContent);
25 | pick.dispose=composeSync(pick.dispose, ()=>{
26 | pickDom.pickContentElement=null;
27 | pickDomManagerHandlers.updateData=null;
28 | })
29 | pickDomManagerHandlers.updateData(); // set visual text
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/plugins/FormRestoreOnBackwardPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 |
3 | export function FormRestoreOnBackwardPlugin(){
4 | return {
5 | plug
6 | }
7 | }
8 |
9 | export function plug(){
10 | return (aspects) => {
11 | return {
12 | layout: () => {
13 | let {staticDom, environment, loadAspect, updateOptionsSelectedAspect} = aspects;
14 | let window = environment.window;
15 |
16 | if (staticDom.selectElement && updateOptionsSelectedAspect){
17 | loadAspect.load = composeSync(loadAspect.load,
18 | function(){
19 | // support browser's "step backward" and form's values restore
20 | if (window.document.readyState !="complete"){
21 | window.setTimeout(function(){
22 | updateOptionsSelectedAspect.updateOptionsSelected();
23 | // there are no need to add more updates as api.updateWasValidated() because backward never trigger .was-validate
24 | // also backward never set the state to invalid
25 | });
26 | }
27 | })
28 | }
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/InputAspect.js:
--------------------------------------------------------------------------------
1 | export function InputAspect(
2 | filterDom,
3 | filterManagerAspect
4 | ){
5 |
6 | return {
7 | // overrided in SelectedOptionPlugin
8 | processInput(){
9 | let filterInputValue = filterDom.getValue();
10 | let text = filterInputValue.trim();
11 | var isEmpty=false;
12 | if (text == '')
13 | isEmpty=true;
14 | else
15 | {
16 | filterManagerAspect.setFilter(text.toLowerCase());
17 | }
18 |
19 | if (!isEmpty)
20 | {
21 | if ( filterManagerAspect.getNavigateManager().getCount() == 1)
22 | {
23 | // todo: move exact match to filterManager
24 | let fullMatchWrap = filterManagerAspect.getNavigateManager().getHead();
25 | let text = filterManagerAspect.getFilter();
26 | if (fullMatchWrap.choice.searchText == text)
27 | {
28 | let success = fullMatchWrap.choice.fullMatch();
29 | if (success) {
30 | filterDom.setEmpty();
31 | isEmpty = true;
32 | }
33 | }
34 | }
35 | }
36 |
37 | return {filterInputValue, isEmpty};
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/plugins/RtlPlugin.js:
--------------------------------------------------------------------------------
1 |
2 | import {getIsRtl, AttributeBackup} from '../ToolsDom';
3 | import {isBoolean} from '../ToolsJs';
4 |
5 | export function RtlPlugin(){
6 | return {
7 | plug
8 | }
9 | }
10 |
11 | export function plug(configuration){
12 | return (aspects) => {
13 | return {
14 | layout: () => {
15 | let {popperRtlAspect, staticDom} = aspects;
16 | let {isRtl} = configuration;
17 | let forceRtlOnContainer = false;
18 | if (isBoolean(isRtl))
19 | forceRtlOnContainer = true;
20 | else
21 | isRtl = getIsRtl(staticDom.initialElement);
22 |
23 | var attributeBackup = AttributeBackup();
24 | if (forceRtlOnContainer){
25 | attributeBackup.set(staticDom.containerElement, "dir", "rtl");
26 | }
27 | else if (staticDom.selectElement){
28 | var dirAttributeValue = staticDom.selectElement.getAttribute("dir");
29 | if (dirAttributeValue){
30 | attributeBackup.set(staticDom.containerElement, "dir", dirAttributeValue);
31 | }
32 | }
33 |
34 | if (popperRtlAspect)
35 | popperRtlAspect.getIsRtl = () => isRtl;
36 |
37 | return {
38 | dispose(){
39 | attributeBackup.restore();
40 | }
41 | }
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/CreateForJQuery.js:
--------------------------------------------------------------------------------
1 | import {addToJQueryPrototype} from './AddToJQueryPrototype'
2 |
3 | import {composeSync, ObjectValuesEx, isString} from './ToolsJs'
4 | import {composeEventTrigger} from './ToolsDom'
5 |
6 | import {MultiSelectBuilder} from './MultiSelectBuilder'
7 |
8 | export function createForJQuery(window, $, globalPopper, name, plugins, defaultCss){
9 | let trigger = null;
10 | let isJQyery = $ && !window.document.body.hasAttribute('data-bs-no-jquery');
11 | if (isJQyery) {
12 | trigger = (e, eventName) => $(e).trigger(eventName);
13 | } else {
14 | trigger = composeEventTrigger(window);
15 | }
16 |
17 | var isIE11 = !!window.MSInputMethodContext && !!window.document.documentMode;
18 |
19 | let environment = {trigger, window, globalPopper, isIE11}
20 | let pluginsArray = ObjectValuesEx(plugins)
21 | let {create, defaultSettings} = MultiSelectBuilder(environment, pluginsArray, defaultCss);
22 | let createForUmd = (element, settings) => {
23 | if (isString(element))
24 | element = window.document.querySelector(element);
25 | return create(element, settings);
26 | }
27 | createForUmd.Default = defaultSettings;
28 |
29 | if (isJQyery) {
30 | let constructorForJquery = (element, settings, removeInstanceData) => {let multiSelect = create(element, settings); multiSelect.dispose = composeSync(multiSelect.dispose, removeInstanceData); return multiSelect;}
31 | let prototypable = addToJQueryPrototype(name, constructorForJquery, $);
32 |
33 | prototypable.defaults = defaultSettings;
34 | }
35 | return createForUmd;
36 | }
37 |
--------------------------------------------------------------------------------
/src/plugins/BsAppearanceBs4Plugin.js:
--------------------------------------------------------------------------------
1 | import {closestByClassName} from '../ToolsDom'
2 | import {BsAppearancePlugin} from './BsAppearancePlugin'
3 |
4 | export function BsAppearanceBs4Plugin(defaults) {
5 | defaults.composeGetSize = composeGetSize; // BsAppearancePlugin
6 | defaults.getDefaultLabel = getDefaultLabel; // FloatingLabelPlugin, BsAppearancePlugin
7 | return BsAppearancePlugin();
8 | }
9 |
10 | function composeGetSize(element){
11 | let inputGroupElement = closestByClassName(element, 'input-group');
12 | let getSize = null;
13 | if (inputGroupElement){
14 | getSize = function(){
15 | var value = null;
16 | if (inputGroupElement.classList.contains('input-group-lg'))
17 | value = 'lg';
18 | else if (inputGroupElement.classList.contains('input-group-sm'))
19 | value = 'sm';
20 | return value;
21 | }
22 | }
23 | else{
24 | getSize = function(){
25 | var value = null;
26 | if (element.classList.contains('custom-select-lg') || element.classList.contains('form-control-lg'))
27 | value = 'lg';
28 | else if (element.classList.contains('custom-select-sm') || element.classList.contains('form-control-sm'))
29 | value = 'sm';
30 | return value;
31 | }
32 | }
33 | return getSize;
34 | }
35 |
36 | function getDefaultLabel(element){
37 | let value = null;
38 | let formGroup = closestByClassName(element,'form-group');
39 | if (formGroup)
40 | value = formGroup.querySelector(`label[for="${element.id}"]`);
41 | return value;
42 | }
--------------------------------------------------------------------------------
/src/BsMultiSelect.web.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery'
2 | import Popper from 'popper.js'
3 | import {BsMultiSelect} from './BsMultiSelect.esm'
4 |
5 | import {BsMultiSelectElement} from './BsMultiSelectElement';
6 |
7 | // import {FormResetPlugin} from './plugins/FormResetPlugin';
8 | // import {ValidationApiPlugin} from './plugins/ValidationApiPlugin';
9 | // import {BsAppearancePlugin} from './plugins/BsAppearancePlugin';
10 | // import {HiddenOptionPlugin} from './plugins/HiddenOptionPlugin';
11 |
12 | /*
13 | 1.
14 |
15 |
16 | 2.
17 |
18 | */
19 |
20 | (
21 | (window, createPopper) => {
22 | // was:
23 | // var elementPrototype = Object.create(HTMLElement.prototype);
24 | // elementPrototype.createdCallback = function() { var shadowRoot = this.createShadowRoot();... shadowRoot.appendChild(elem); }
25 | // elementPrototype.attributeChangedCallback = ...
26 | // document.registerElement('element-multiplier', {prototype: elementPrototype });
27 | window.customElements.define('dashboardcode-bsmultiselect', BsMultiSelectElement);
28 | // let createPlugin = (element, settings, onDispose) => {
29 | // let trigger = (e, eventName) => $(e).trigger(eventName);
30 | // let environment = {trigger, window, Popper}
31 | // let multiSelect = BsMultiSelect(element, environment, settings);
32 | // multiSelect.onDispose = composeSync(multiSelect.onDispose, onDispose);
33 | // return multiSelect;
34 | // }
35 | // addToJQueryPrototype('BsMultiSelect', createPlugin, defaults, $);
36 | }
37 | )(window, createPopper)
38 |
--------------------------------------------------------------------------------
/src/plugins/BsAppearanceBs5Plugin.js:
--------------------------------------------------------------------------------
1 | import {closestByClassName} from '../ToolsDom'
2 | import {BsAppearancePlugin} from './BsAppearancePlugin'
3 |
4 | export function BsAppearanceBs5Plugin(defaults) {
5 | defaults.composeGetSize = composeGetSize; // BsAppearancePlugin
6 | defaults.getDefaultLabel = getDefaultLabel; // FloatingLabelPlugin, BsAppearancePlugin
7 | return BsAppearancePlugin();
8 | }
9 |
10 | function composeGetSize(element){
11 | let inputGroupElement = closestByClassName(element, 'input-group');
12 | let getSize = null;
13 | if (inputGroupElement){
14 | getSize = function(){
15 | var value = null;
16 | if (inputGroupElement.classList.contains('input-group-lg'))
17 | value = 'lg';
18 | else if (inputGroupElement.classList.contains('input-group-sm'))
19 | value = 'sm';
20 | return value;
21 | }
22 | }
23 | else{
24 | getSize = function(){
25 | var value = null;
26 | if (element.classList.contains('form-select-lg') || element.classList.contains('form-control-lg')) // changed for BS
27 | value = 'lg';
28 | else if (element.classList.contains('form-select-sm') || element.classList.contains('form-control-sm'))
29 | value = 'sm';
30 | return value;
31 | }
32 | }
33 | return getSize;
34 | }
35 |
36 | function getDefaultLabel(element){
37 | let value = null;
38 | let query = `label[for="${element.id}"]`;
39 | let p1 = element.parentElement;
40 | value = p1.querySelector(query); // label can be wrapped into col-auto
41 | if (!value){
42 | let p2 = p1.parentElement;
43 | value = p2.querySelector(query);
44 | }
45 | return value;
46 | }
--------------------------------------------------------------------------------
/src/ChoicesDomFactory.js:
--------------------------------------------------------------------------------
1 | import {addStyling} from './ToolsStyling';
2 |
3 | export function ChoicesDomFactory(staticDom) {
4 | return {
5 | create(){
6 | let {css, createElementAspect} = staticDom;
7 | var choicesElement = createElementAspect.createElement('DIV');
8 | var choicesListElement = createElementAspect.createElement('UL');
9 |
10 | choicesElement.appendChild(choicesListElement);
11 | choicesElement.style.display = 'none';
12 |
13 | addStyling(choicesElement, css.choices);
14 | addStyling(choicesListElement, css.choicesList);
15 |
16 | return {
17 | choicesElement,
18 | choicesListElement,
19 | createChoiceElement(){
20 | var choiceElement = createElementAspect.createElement('LI');
21 | addStyling(choiceElement, css.choice);
22 | return {
23 | choiceElement,
24 | setVisible: (isVisible) => choiceElement.style.display = isVisible ? 'block': 'none',
25 | attach: (beforeElement) => choicesListElement.insertBefore(choiceElement, beforeElement),
26 | detach: () => choicesListElement.removeChild(choiceElement)
27 | }
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
34 | export function ChoicesDomFactoryPlugCss(css){
35 | css.choices = 'dropdown-menu';
36 | css.choicesList = '';
37 | css.choice = '';
38 | }
39 |
40 | export function ChoicesDomFactoryPlugCssPatch(cssPatch){
41 | cssPatch.choicesList = {listStyleType:'none', paddingLeft:'0', paddingRight:'0', marginBottom:'0'};
42 | cssPatch.choice = {classes:'px-md-2 px-1', styles:{cursor:'pointer'}};
43 | }
--------------------------------------------------------------------------------
/src/plugins/OptionsApiPlugin.js:
--------------------------------------------------------------------------------
1 | export function OptionsApiPlugin(){
2 | return {
3 | plug
4 | }
5 | }
6 |
7 | export function plug(){
8 | return (aspects) => {
9 | return {
10 | buildApi(api){
11 | let {buildAndAttachChoiceAspect, wraps, wrapsCollection, createWrapAspect, createChoiceBaseAspect,
12 | dataWrap, resetLayoutAspect} = aspects;
13 |
14 | api.updateOptionAdded = (key) => { // TODO: generalize index as key
15 | let options = dataWrap.getOptions();
16 | let option = options[key];
17 |
18 | let wrap = createWrapAspect.createWrap(option);
19 | wrap.choice= createChoiceBaseAspect.createChoiceBase(option);
20 | wraps.insert(key, wrap);
21 | let nextChoice = ()=> wrapsCollection.getNext(key, c => c.choice.choiceDom.choiceElement);
22 |
23 | buildAndAttachChoiceAspect.buildAndAttachChoice(
24 | wrap,
25 | () => nextChoice()?.choice.choiceDom.choiceElement
26 | )
27 | }
28 |
29 | api.updateOptionRemoved = (key) => { // TODO: generalize index as key
30 | resetLayoutAspect.resetLayout(); // always hide 1st, then reset filter
31 |
32 | var wrap = wraps.remove(key);
33 | wrap.choice.choiceDomManagerHandlers.detach?.();
34 | wrap.dispose?.();
35 | }
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/babel.mjs.ecma5.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //retainLines: true,
3 | "presets": [
4 | [
5 | "@babel/preset-env",
6 | {
7 | "loose": true, // ES6 to EcmaScript5
8 | "bugfixes": true,
9 | // "useBuiltIns": "usage",
10 | "modules": false,
11 | "exclude": ["transform-typeof-symbol"],
12 | "targets": {
13 | "browsers": [
14 | "chrome >= 45", "Firefox >= 38", "Explorer >= 10", "edge >= 12", "iOS >= 9","Safari >= 9","Android >= 4.4","Opera >= 30"
15 | ]
16 | },
17 | "debug": true
18 | }
19 | ]
20 | ]
21 | }
22 |
23 |
24 | // const BROWSER_COMPAT = process.env.BROWSER_COMPAT === 'true';
25 |
26 | // module.exports = {
27 | // // presets: [
28 | // // [
29 | // // '@babel/env',
30 | // // {
31 | // // loose: true,
32 | // // modules: false,
33 | // // },
34 | // // ],
35 | // // ],
36 | // plugins: [
37 | // //'@babel/plugin-transform-flow-strip-types',
38 | // 'babel-plugin-add-import-extension',
39 | // [
40 | // '@babel/plugin-proposal-object-rest-spread',
41 | // {
42 | // loose: true,
43 | // useBuiltIns: true,
44 | // },
45 | // ],
46 | // ...(BROWSER_COMPAT
47 | // ? [
48 | // [
49 | // 'inline-replace-variables',
50 | // {
51 | // __DEV__: false,
52 | // },
53 | // ],
54 | // ]
55 | // : ['dev-expression']),
56 | // 'annotate-pure-calls',
57 | // ],
58 | // env: {
59 | // test: {
60 | // presets: ['@babel/env'],
61 | // plugins: ['@babel/plugin-transform-runtime'],
62 | // },
63 | // dev: {
64 | // plugins: [
65 | // [
66 | // 'transform-inline-environment-variables',
67 | // {
68 | // include: ['NODE_ENV'],
69 | // },
70 | // ],
71 | // ],
72 | // },
73 | // },
74 | // };
--------------------------------------------------------------------------------
/src/plugins/LabelForAttributePlugin.js:
--------------------------------------------------------------------------------
1 | import {defCall, composeSync} from '../ToolsJs';
2 |
3 | export function LabelForAttributePlugin(defaults){
4 | defaults.label = null;
5 | return {
6 | plug
7 | }
8 | }
9 |
10 | export function plug(configuration){
11 | var getLabelAspect = {getLabel : ()=>defCall(configuration.label)}
12 | var createFilterInputElementIdAspect = {
13 | createFilterInputElementId : ()=>defCall(configuration.filterInputElementId),
14 | };
15 | return (aspects) => {
16 | aspects.getLabelAspect = getLabelAspect;
17 | aspects.createFilterInputElementIdAspect = createFilterInputElementIdAspect;
18 | return {
19 | layout: () => {
20 | var {filterDom, loadAspect, disposeAspect, staticDom} = aspects;
21 |
22 | loadAspect.load = composeSync(loadAspect.load, ()=>{
23 | let {filterInputElement} = filterDom;
24 |
25 | let labelElement = getLabelAspect.getLabel();
26 | if (labelElement){
27 | let backupedForAttribute = labelElement.getAttribute('for');
28 | var inputId = createFilterInputElementIdAspect.createFilterInputElementId();
29 |
30 | if (!inputId){
31 | let {containerClass} = configuration;
32 | let {containerElement} = staticDom;
33 | inputId =`${containerClass}-generated-filter-${containerElement.id}`
34 | }
35 | filterInputElement.setAttribute('id', inputId);
36 | labelElement.setAttribute('for',inputId);
37 | if (backupedForAttribute){
38 | disposeAspect.dispose = composeSync(
39 | disposeAspect.dispose,
40 | () =>labelElement.setAttribute('for', backupedForAttribute)
41 | )
42 | }
43 | }
44 | })
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/FilterManagerAspect.js:
--------------------------------------------------------------------------------
1 | export function NavigateManager(
2 | list, getPrev, getNext
3 | ){
4 | return {
5 | navigate(down, wrap /* hoveredChoice */){
6 | if (down) {
7 | return wrap?getNext(wrap): list.getHead();
8 | } else {
9 | return wrap?getPrev(wrap): list.getTail();
10 | }
11 | },
12 | getCount(){
13 | return list.getCount()
14 | },
15 | getHead(){
16 | return list.getHead()
17 | }
18 | }
19 | }
20 |
21 | export function FilterPredicateAspect(){
22 | return {
23 | filterPredicate: (wrap, text) =>
24 | wrap.choice.searchText.indexOf(text) >= 0
25 | }
26 | }
27 |
28 | export function FilterManagerAspect(
29 | emptyNavigateManager,
30 | filteredNavigateManager,
31 | filteredChoicesList,
32 | choicesEnumerableAspect,
33 | filterPredicateAspect
34 | ) {
35 | let showEmptyFilter=true;
36 | let filterText = "";
37 | return {
38 | getNavigateManager(){
39 | return (showEmptyFilter)?emptyNavigateManager:filteredNavigateManager;
40 | },
41 | processEmptyInput(){ // redefined in PlaceholderPulgin, HighlightPlugin
42 | showEmptyFilter =true;
43 | filterText ="";
44 | choicesEnumerableAspect.forEach( (wrap)=>{
45 | wrap.choice.choiceDomManagerHandlers.setVisible(true);
46 | });
47 | },
48 | getFilter(){
49 | return filterText;
50 | },
51 | setFilter(text){ // redefined in HighlightPlugin
52 | showEmptyFilter =false;
53 | filterText = text;
54 | filteredChoicesList.reset();
55 | choicesEnumerableAspect.forEach( (wrap)=>{
56 | wrap.choice.filteredPrev = wrap.choice.filteredNext = null;
57 | var v = filterPredicateAspect.filterPredicate(wrap, text)
58 | if (v)
59 | filteredChoicesList.add(wrap);
60 | wrap.choice.choiceDomManagerHandlers.setVisible(v);
61 | });
62 | }
63 | }
64 | }
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/StaticManagerAspect.js:
--------------------------------------------------------------------------------
1 | export function StaticManagerAspect(staticDom, picksDom, filterDom){
2 | return {
3 | appendToContainer(){
4 | picksDom.pickFilterElement.appendChild(filterDom.filterInputElement);
5 | picksDom.picksElement.appendChild(picksDom.pickFilterElement);
6 | containerElement.appendChild(choicesDom.choicesElement);
7 | if (isDisposablePicksElementFlag)
8 | containerElement.appendChild(configDom.picksElement);
9 | },
10 |
11 | dispose(){
12 | containerElement.removeChild(choicesDom.choicesElement);
13 | if (removableContainerClass)
14 | containerElement.classList.remove(containerClass);
15 | if (isDisposablePicksElementFlag)
16 | containerElement.removeChild(configDom.picksElement);
17 | picksDom.dispose();
18 | filterDom.dispose();
19 | }
20 |
21 |
22 | // -------------------------------
23 |
24 | // appendToContainer(){
25 | // let {selectElement} = staticDom;
26 | // picksDom.pickFilterElement.appendChild(filterDom.filterInputElement);
27 | // picksDom.picksElement.appendChild(picksDom.pickFilterElement);
28 | // if (isDisposableContainerElementFlag){
29 | // selectElement.parentNode.insertBefore(containerElement, selectElement.nextSibling)
30 | // containerElement.appendChild(choicesDom.choicesElement)
31 | // }else {
32 | // selectElement.parentNode.insertBefore(choicesDom.choicesElement, selectElement.nextSibling)
33 | // }
34 | // if (isDisposablePicksElementFlag)
35 | // containerElement.appendChild(picksDom.picksElement)
36 | // },
37 |
38 | // dispose(){
39 | // let {selectElement} = staticDom;
40 | // choicesDom.choicesElement.parentNode.removeChild(choicesDom.choicesElement);
41 | // if (isDisposableContainerElementFlag)
42 | // selectElement.parentNode.removeChild(containerElement)
43 | // if (isDisposablePicksElementFlag)
44 | // containerElement.removeChild(picksDom.picksElement)
45 | // picksDom.dispose();
46 | // filterDom.dispose();
47 | // }
48 | }
49 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { shallowClearClone, ObjectValuesEx } from "./ToolsJs.js";
2 |
3 | import { composeSync } from "./ToolsJs.js";
4 | import { EventBinder } from "./ToolsDom.js";
5 | import { addStyling, toggleStyling } from "./ToolsStyling.js";
6 |
7 | import { MultiSelectBuilder } from "./MultiSelectBuilder.js";
8 | import { BsMultiSelect, ModuleFactory } from "./BsMultiSelect.esm.js";
9 |
10 | import { BsAppearanceBs5Plugin, BsAppearanceBs4Plugin, CssPatchBs4Plugin, CssPatchBs5Plugin, SelectElementPlugin, LabelForAttributePlugin, ValidationApiPlugin, UpdateAppearancePlugin, DisableComponentPlugin,
11 | FormResetPlugin, CreatePopperPlugin, WarningCssPatchPlugin, RtlPlugin, PlaceholderPlugin, PlaceholderCssPatchPlugin,
12 | FloatingLabelPlugin, FloatingLabelCssPatchBs5Plugin, OptionsApiPlugin, JQueryMethodsPlugin, SelectedOptionPlugin,
13 | FormRestoreOnBackwardPlugin, DisabledOptionPlugin, DisabledOptionCssPatchPlugin, PicksApiPlugin, HighlightPlugin, ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin,
14 | PicksPlugin, HiddenOptionPlugin, HiddenOptionAltPlugin,
15 | BsAppearanceBs4CssPatchPlugin, BsAppearanceBs5CssPatchPlugin,
16 | WarningBs4Plugin, WarningBs5Plugin,
17 | PickButtonBs4Plugin, PickButtonBs5Plugin, PickButtonPlugCssPatchBs4, PickButtonPlugCssPatchBs5,
18 | /*,SelectedPicksPlugin*/
19 | } from "./plugins/index.js";
20 |
21 | export {
22 | BsAppearanceBs4Plugin, WarningBs4Plugin,
23 | BsAppearanceBs5Plugin, WarningBs5Plugin,
24 |
25 | CssPatchBs4Plugin, BsAppearanceBs4CssPatchPlugin,
26 | CssPatchBs5Plugin, BsAppearanceBs5CssPatchPlugin, FloatingLabelCssPatchBs5Plugin, PlaceholderCssPatchPlugin, WarningCssPatchPlugin,
27 | SelectElementPlugin, PicksPlugin,
28 |
29 | LabelForAttributePlugin, ValidationApiPlugin, UpdateAppearancePlugin,
30 |
31 | PickButtonBs4Plugin, PickButtonBs5Plugin, PickButtonPlugCssPatchBs4, PickButtonPlugCssPatchBs5,
32 |
33 | DisableComponentPlugin,
34 | FormResetPlugin, CreatePopperPlugin, RtlPlugin, PlaceholderPlugin,
35 | FloatingLabelPlugin, OptionsApiPlugin, JQueryMethodsPlugin, SelectedOptionPlugin,
36 | FormRestoreOnBackwardPlugin,
37 | DisabledOptionPlugin, DisabledOptionCssPatchPlugin,
38 | PicksApiPlugin, HighlightPlugin, ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin,
39 |
40 | HiddenOptionPlugin, HiddenOptionAltPlugin,
41 |
42 | shallowClearClone, ObjectValuesEx, composeSync, EventBinder, addStyling, toggleStyling,
43 | MultiSelectBuilder, BsMultiSelect, ModuleFactory};
--------------------------------------------------------------------------------
/src/ProduceChoiceAspect.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from './ToolsJs';
2 | export function BuildAndAttachChoiceAspect(
3 | produceChoiceAspect,
4 | ){
5 | return {
6 | buildAndAttachChoice(
7 | wrap,
8 | getNextElement
9 | )
10 | {
11 | produceChoiceAspect.produceChoice(wrap);
12 | wrap.choice.choiceDomManagerHandlers.attach(getNextElement?.());
13 | }
14 | }
15 | }
16 |
17 | export function ProduceChoiceAspect(choicesDom, choiceDomFactory) {
18 | return {
19 | // 1 overrided in highlight and option disable plugins
20 | // 2 call in HiddenPlugin (create)
21 | // 3 overrided in layout: pick created, choice.choiceDomManagerHandlers.detach updated to remove pick
22 | produceChoice(wrap) {
23 | var {choiceElement, attach, detach, setVisible} = choicesDom.createChoiceElement();
24 |
25 | let choice = wrap.choice;
26 | choice.wrap = wrap;
27 |
28 | choice.choiceDom={
29 | choiceElement
30 | };
31 |
32 | let choiceDomManagerHandlers = {
33 | attach,
34 | detach,
35 | setVisible // TODO: refactor it there should be 3 types of not visibility: for hidden, for filtered out, for optgroup, for message item
36 | }
37 | choice.choiceDomManagerHandlers = choiceDomManagerHandlers
38 | choiceDomFactory.create(choice);
39 |
40 | // added by "navigation (by mouse and arrows) plugin"
41 | choice.isHoverIn = false; // internal state
42 | choice.setHoverIn = (v) => {
43 | choice.isHoverIn =v ;
44 | choiceDomManagerHandlers.updateHoverIn();
45 | }
46 |
47 | choice.dispose = composeSync(() =>{
48 | choice.choiceDom.choiceElement = null;
49 | choice.choiceDom = null;
50 | choiceDomManagerHandlers.attach=null;
51 | choiceDomManagerHandlers.detach=null;
52 | choiceDomManagerHandlers.setVisible=null;
53 | choice.choiceDomManagerHandlers = null;
54 | choice.choiсeClick=null;
55 |
56 | choice.setHoverIn = null;
57 |
58 | choice.wrap = null;
59 | choice.dispose = null;
60 | }, choice.dispose)
61 |
62 | wrap.dispose = () => {
63 | choice.dispose();
64 | wrap.dispose = null;
65 | }
66 | return choice;
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/src/MultiSelectBuilder.js:
--------------------------------------------------------------------------------
1 | import {BsMultiSelect} from './BsMultiSelect'
2 | import {ComposePluginManagerFactory} from './PluginManager'
3 |
4 | import {adjustLegacySettings} from './BsMultiSelectDepricatedParameters'
5 |
6 | import {extendIfUndefined} from './ToolsJs'
7 |
8 | import {createCss} from './ToolsStyling'
9 |
10 | // TODO: remove environment - replace it with plugins
11 | // TODO: defaultCss should come together with DomFactories and Layout
12 | export function MultiSelectBuilder(environment, plugins, defaultCss)
13 | {
14 | const defaults = {containerClass: "dashboardcode-bsmultiselect", css: defaultCss}
15 |
16 | var pluginManagerFactory = ComposePluginManagerFactory(plugins, defaults, environment);
17 |
18 | /* NOTE: about namings
19 | defaults - defaults for module
20 | setting - object that could modify defaults (not just overwrite)
21 | options - configuration "generalization": can be buildConfiguration function or settings
22 | configuration - for control instance
23 | */
24 | let create = (element, options) => {
25 | if (options && options.plugins)
26 | console.log("DashboarCode.BsMultiSelect: 'options.plugins' is depricated, use - MultiSelectBuilder(environment, plugins) instead");
27 |
28 | let buildConfiguration;
29 | let settings;
30 | if (options instanceof Function) {
31 | buildConfiguration = options;
32 | settings = null;
33 | } else {
34 | buildConfiguration = options?.buildConfiguration;
35 | settings = options;
36 | }
37 | if (settings){
38 | adjustLegacySettings(settings);
39 | }
40 | let configuration = {};
41 |
42 | configuration.css = createCss(defaults.css, settings?.css);
43 |
44 | extendIfUndefined(configuration, settings);
45 | // next line: merging of cssPatch will be delayed to the CssPatchPlugin merge handler
46 | extendIfUndefined(configuration, defaults);
47 | let inlineBuildAspectsList = buildConfiguration?.(element, configuration);
48 | // next line merges settings.cssPatch and defaults.cssPatch also merge defaults.css and defaults.cssPatch
49 | var pluginManager = pluginManagerFactory(configuration, settings, inlineBuildAspectsList);
50 | // now we can freeze configuration object
51 | Object.freeze(configuration);
52 | let multiSelect = BsMultiSelect(element, environment, pluginManager, configuration);
53 | return multiSelect;
54 | }
55 |
56 | return {create, defaultSettings: defaults}
57 | }
--------------------------------------------------------------------------------
/src/ModuleFactory.js:
--------------------------------------------------------------------------------
1 | import {multiSelectPlugins, picksPlugins, allPlugins} from './PluginSet'
2 | import {shallowClearClone, ObjectValuesEx} from './ToolsJs'
3 | import {utilities} from './ToolSet'
4 | import {MultiSelectBuilder} from './MultiSelectBuilder'
5 |
6 | export function ModuleFactory(environment, customizationPlugins, defaultCss){
7 | if (!environment.trigger)
8 | environment.trigger = (e, name) => e.dispatchEvent(new environment.window.Event(name))
9 |
10 | if (!environment.isIE11)
11 | environment.isIE11 = !!environment.window.MSInputMethodContext && !!environment.window.document.documentMode;
12 |
13 | let multiSelectPluginsObj = shallowClearClone(customizationPlugins, multiSelectPlugins);
14 | let pluginsArray = ObjectValuesEx(multiSelectPluginsObj);
15 | let {create: BsMultiSelect, BsMultiSelectDefault} = MultiSelectBuilder(environment, pluginsArray, defaultCss)
16 | BsMultiSelect.Default = BsMultiSelectDefault;
17 |
18 | let picksPluginsObj = shallowClearClone(customizationPlugins, picksPlugins);
19 | let picksPluginsArray = ObjectValuesEx(picksPluginsObj);
20 | let {create: BsPicks, BsPicksDefault} = MultiSelectBuilder(environment, picksPluginsArray, defaultCss)
21 | BsPicks.Default = BsPicksDefault;
22 |
23 | return {
24 | BsMultiSelect,
25 | BsPicks,
26 | MultiSelectTools: {MultiSelectBuilder, plugins: shallowClearClone(customizationPlugins, allPlugins), defaultCss, utilities}
27 | }
28 | }
29 |
30 |
31 | // TEST
32 | // function areValidElements(...args) {
33 | // const result = Object.values(obj);
34 | // return !args.some(
35 | // (element) =>
36 | // !(element && typeof element.getBoundingClientRect === 'function')
37 | // );
38 | // }
39 |
40 | // function ModuleFactory(environment) {
41 | // if (!environment.trigger)
42 | // environment.trigger = (e, name) => e.dispatchEvent(new environment.window.Event(name))
43 |
44 | // let pluginsArray = ObjectValues(shallowClearClone({Bs5Plugin}, multiSelectPlugins));
45 | // let {create: BsMultiSelect, BsMultiSelectDefault} = MultiSelectBuilder(environment, pluginsArray)
46 | // BsMultiSelect.Default = BsMultiSelectDefault;
47 |
48 | // let picksPluginsArray = ObjectValues(shallowClearClone({Bs5Plugin}, picksPlugins));
49 | // let {create: BsPicks, BsPicksDefault} = MultiSelectBuilder(environment, picksPluginsArray)
50 | // BsPicks.Default = BsPicksDefault;
51 |
52 | // return {
53 | // BsMultiSelect,
54 | // BsPicks,
55 | // MultiSelectTools: {MultiSelectBuilder, plugins: shallowClearClone({Bs5Plugin}, allPlugins), utilities}
56 | // }
57 | // }
58 |
--------------------------------------------------------------------------------
/src/plugins/CustomPickStylingsPlugin.js:
--------------------------------------------------------------------------------
1 | import { composeSync } from "../ToolsJs";
2 |
3 | export function CustomPickStylingsPlugin(defaults){
4 | defaults.customPickStylings = null;
5 | return {
6 | plug
7 | }
8 | }
9 |
10 | export function plug(configuration){
11 | return (aspects) => {
12 | return {
13 | plugStaticDom: ()=> {
14 | let {disabledComponentAspect, pickDomFactory} = aspects;
15 | let customPickStylings = configuration.customPickStylings;
16 | let customPickStylingsAspect = CustomPickStylingsAspect(disabledComponentAspect, customPickStylings);
17 | ExtendPickDomFactory(pickDomFactory, customPickStylingsAspect);
18 | }
19 | }
20 | }
21 | }
22 |
23 | function ExtendPickDomFactory(pickDomFactory, customPickStylingsAspect){
24 | let origCreatePickDomFactory = pickDomFactory.create;
25 | pickDomFactory.create = function(pick){
26 | origCreatePickDomFactory(pick);
27 | customPickStylingsAspect.customize(pick);
28 | }
29 | }
30 |
31 | function CustomPickStylingsAspect(disabledComponentAspect, customPickStylings){
32 | return {
33 | customize(pick){
34 | if (customPickStylings){
35 | var handlers = customPickStylings(pick.pickDom, pick.wrap.option);
36 |
37 | if (handlers){
38 | function customPickStylingsClosure(custom){
39 | return function() {
40 | custom({
41 | isOptionDisabled: pick.wrap.isOptionDisabled,
42 | // wrap.component.getDisabled();
43 | // wrap.group.getDisabled();
44 | isComponentDisabled: disabledComponentAspect.getDisabled()
45 | });
46 | }
47 | }
48 | let pickDomManagerHandlers = pick.pickDomManagerHandlers;
49 | // TODO: automate it
50 | if (pickDomManagerHandlers.updateDisabled && handlers.updateDisabled)
51 | pickDomManagerHandlers.updateDisabled
52 | = composeSync(pickDomManagerHandlers.updateDisabled, customPickStylingsClosure(handlers.updateDisabled));
53 | if (pickDomManagerHandlers.updateComponentDisabled && handlers.updateComponentDisabled)
54 | pickDomManagerHandlers.updateComponentDisabled
55 | = composeSync(pickDomManagerHandlers.updateComponentDisabled, customPickStylingsClosure(handlers.updateComponentDisabled));
56 | }
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/PluginSet.js:
--------------------------------------------------------------------------------
1 | import {
2 | BsAppearanceBs4Plugin, BsAppearanceBs5Plugin,
3 |
4 | CssPatchBs4Plugin, CssPatchBs5Plugin,
5 | BsAppearanceBs4CssPatchPlugin, BsAppearanceBs5CssPatchPlugin,
6 |
7 | SelectElementPlugin,
8 | LabelForAttributePlugin, ValidationApiPlugin,
9 | UpdateAppearancePlugin,
10 |
11 | DisableComponentPlugin,
12 | FormResetPlugin, CreatePopperPlugin, RtlPlugin, PlaceholderPlugin, PlaceholderCssPatchPlugin,
13 | OptionsApiPlugin,
14 | JQueryMethodsPlugin,
15 | SelectedOptionPlugin, FormRestoreOnBackwardPlugin,
16 | DisabledOptionPlugin, DisabledOptionCssPatchPlugin,
17 | PicksApiPlugin, HighlightPlugin,
18 | ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin,
19 |
20 | FloatingLabelPlugin, FloatingLabelCssPatchBs5Plugin,
21 |
22 | WarningCssPatchPlugin, WarningBs4Plugin, WarningBs5Plugin,
23 |
24 | PicksPlugin,
25 | PickButtonBs4Plugin, PickButtonBs5Plugin, PickButtonPlugCssPatchBs4, PickButtonPlugCssPatchBs5,
26 | HiddenOptionPlugin,
27 | /*HiddenOptionAltPlugin as HiddenOptionPlugin*/} from './plugins/index'
28 |
29 | import {shallowClearClone} from './ToolsJs'
30 |
31 |
32 | export let Bs4PluginSet = {BsAppearanceBs4Plugin, PickButtonBs4Plugin, WarningBs4Plugin, CssPatchBs4Plugin, BsAppearanceBs4CssPatchPlugin, PickButtonPlugCssPatchBs4}
33 |
34 | export let Bs5PluginSet = {BsAppearanceBs5Plugin, PickButtonBs5Plugin, WarningBs5Plugin, CssPatchBs5Plugin, BsAppearanceBs5CssPatchPlugin, PickButtonPlugCssPatchBs5, FloatingLabelCssPatchBs5Plugin}
35 |
36 | export let multiSelectPlugins = {SelectElementPlugin,
37 | LabelForAttributePlugin, HiddenOptionPlugin, ValidationApiPlugin,
38 | UpdateAppearancePlugin,
39 | DisableComponentPlugin,
40 | FormResetPlugin, CreatePopperPlugin, WarningCssPatchPlugin, RtlPlugin, PlaceholderPlugin, PlaceholderCssPatchPlugin, FloatingLabelPlugin, OptionsApiPlugin,
41 | JQueryMethodsPlugin,
42 | SelectedOptionPlugin, FormRestoreOnBackwardPlugin,
43 | DisabledOptionPlugin, DisabledOptionCssPatchPlugin, PicksApiPlugin, HighlightPlugin,
44 | ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin};
45 |
46 | export let picksPlugins = {PicksPlugin,
47 | LabelForAttributePlugin, ValidationApiPlugin,
48 | UpdateAppearancePlugin,
49 | DisableComponentPlugin,
50 | CreatePopperPlugin, WarningCssPatchPlugin, RtlPlugin, PlaceholderPlugin, PlaceholderCssPatchPlugin, FloatingLabelPlugin, OptionsApiPlugin,
51 | JQueryMethodsPlugin, PicksApiPlugin, HighlightPlugin,
52 | ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin};
53 |
54 | export let allPlugins = shallowClearClone(multiSelectPlugins, {PicksPlugin});
55 |
56 |
57 |
58 |
59 | // var defaultConfig = {
60 | // plugins: multiSelectPlugins
61 | // }
62 |
63 | // var picksConfig = {
64 | // plugins: picksPlugins
65 | // }
66 |
67 | // export function createConfig(arg){
68 | // return config;
69 | // }
--------------------------------------------------------------------------------
/src/FilterDomFactory.js:
--------------------------------------------------------------------------------
1 | import {addStyling} from './ToolsStyling';
2 | import {EventBinder} from './ToolsDom';
3 |
4 | export function FilterDomFactory(staticDom){
5 | return {
6 | create(){
7 | let {isDisposablePicksElementFlag, css, createElementAspect} = staticDom;
8 | var filterInputElement = createElementAspect.createElement('INPUT');
9 | addStyling(filterInputElement, css.filterInput);
10 |
11 | filterInputElement.setAttribute("type","search");
12 | filterInputElement.setAttribute("autocomplete","off");
13 | var eventBinder = EventBinder();
14 |
15 | return {
16 | filterInputElement,
17 | isEmpty(){return filterInputElement.value ? false : true},
18 | setEmpty(){
19 | filterInputElement.value ='';
20 | },
21 | getValue(){
22 | return filterInputElement.value;
23 | },
24 | setFocus(){
25 | filterInputElement.focus();
26 | },
27 | setWidth(text){
28 | filterInputElement.style.width = text.length * 1.3 + 2 + "ch"
29 | },
30 | // TODO: check why I need this comparision?
31 | setFocusIfNotTarget(target){
32 | if (target != filterInputElement)
33 | filterInputElement.focus();
34 | },
35 | onInput(onFilterInputInput){
36 | eventBinder.bind(filterInputElement,'input', onFilterInputInput);
37 | },
38 | onFocusIn(onFocusIn){
39 | eventBinder.bind(filterInputElement,'focusin', onFocusIn);
40 | },
41 | onFocusOut(onFocusOut){
42 | eventBinder.bind(filterInputElement,'focusout', onFocusOut);
43 | },
44 | onKeyDown(onfilterInputKeyDown){
45 | eventBinder.bind(filterInputElement,'keydown', onfilterInputKeyDown);
46 | },
47 | onKeyUp(onFilterInputKeyUp){
48 | eventBinder.bind(filterInputElement,'keyup', onFilterInputKeyUp);
49 | },
50 | dispose(){
51 | eventBinder.unbind();
52 | if (!isDisposablePicksElementFlag){
53 | if (filterInputElement.parentNode)
54 | filterInputElement.parentNode.removeChild(filterInputElement)
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
62 | export function FilterDomFactoryPlugCss(css){
63 | css.filterInput = '';
64 | }
65 |
66 | export function FilterDomFactoryPlugCssPatch(cssPatch){
67 | cssPatch.filterInput = {
68 | border:'0px', height: 'auto', boxShadow:'none',
69 | padding:'0', margin:'0',
70 | outline:'none', backgroundColor:'transparent',
71 | backgroundImage: 'none' // otherwise BS .was-validated set its image
72 | };
73 | }
74 |
--------------------------------------------------------------------------------
/src/plugins/FloatingLabelPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 | import {toggleStyling} from '../ToolsStyling';
3 |
4 | export function FloatingLabelPlugin(defaults){
5 | defaults.css.label_floating_lifted = 'floating-lifted';
6 | defaults.css.picks_floating_lifted = 'floating-lifted';
7 | return {
8 | plug
9 | }
10 | }
11 |
12 | export function plug(configuration){
13 | return (aspects) => {
14 | return {
15 | plugStaticDom: ()=> {
16 | aspects.floatingLabelAspect = FloatingLabelAspect();
17 | },
18 | layout: () => {
19 | let {picksList, picksDom, filterDom,
20 | updateDataAspect, resetFilterListAspect, floatingLabelAspect, getLabelAspect} = aspects;
21 | let {css} = configuration;
22 |
23 | if (floatingLabelAspect.isFloatingLabel() ){
24 | let labelElement = getLabelAspect.getLabel();
25 | let picksElement = picksDom.picksElement;
26 |
27 | var liftToggleStyling1 = toggleStyling(labelElement, css.label_floating_lifted);
28 | var liftToggleStyling2 = toggleStyling(picksElement, css.picks_floating_lifted);
29 |
30 | function liftedLabel(isEmpty){
31 | liftToggleStyling1(isEmpty);
32 | liftToggleStyling2(isEmpty);
33 | }
34 |
35 | let isEmpty = () => picksList.isEmpty() && filterDom.isEmpty() && !picksDom.getIsFocusIn();;
36 |
37 | function updateLiftedLabel(){
38 | liftedLabel(!isEmpty());
39 | };
40 |
41 | updateLiftedLabel();
42 |
43 | resetFilterListAspect.forceResetFilter = composeSync(resetFilterListAspect.forceResetFilter, updateLiftedLabel);
44 |
45 | let origAdd = picksList.add;
46 | picksList.add = (pick) => {
47 | let returnValue = origAdd(pick);
48 | if (picksList.getCount()==1)
49 | updateLiftedLabel()
50 | pick.dispose = composeSync(pick.dispose, ()=>
51 | {
52 | if (picksList.getCount()==0)
53 | updateLiftedLabel()
54 | })
55 | return returnValue;
56 | };
57 |
58 | var origToggleFocusStyling = picksDom.toggleFocusStyling;
59 | picksDom.toggleFocusStyling = () => {
60 | var isFocusIn = picksDom.getIsFocusIn();
61 | origToggleFocusStyling(isFocusIn);
62 | updateLiftedLabel();
63 | }
64 |
65 | updateDataAspect.updateData = composeSync(updateDataAspect.updateData, updateLiftedLabel);
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | function FloatingLabelAspect() {
73 | return {
74 | isFloatingLabel(){},
75 | }
76 | }
--------------------------------------------------------------------------------
/src/StaticDomFactory.js:
--------------------------------------------------------------------------------
1 | import {findDirectChildByTagName, closestByClassName} from './ToolsDom';
2 |
3 | export function StaticDomFactory(staticDom){
4 | return {
5 | createStaticDom(){
6 | let {createElementAspect, initialElement, containerClass} = staticDom;
7 |
8 | let containerElement, picksElement;
9 | let removableContainerClass= false;
10 | if (initialElement.tagName == 'DIV') {
11 | containerElement = initialElement;
12 | if (!containerElement.classList.contains(containerClass)){
13 | containerElement.classList.add(containerClass);
14 | removableContainerClass = true;
15 | }
16 | picksElement = findDirectChildByTagName(containerElement, 'UL');
17 | }
18 | else if (initialElement.tagName == 'UL') {
19 | picksElement = initialElement;
20 | containerElement = closestByClassName(initialElement, containerClass);
21 | if (!containerElement){
22 | throw new Error('BsMultiSelect: defined on UL but precedentant DIV for container not found; class='+containerClass);
23 | }
24 | }
25 | else if (initialElement.tagName=="INPUT") {
26 | throw new Error('BsMultiSelect: INPUT element is not supported');
27 | }
28 |
29 |
30 | let isDisposablePicksElementFlag=false;
31 | if (!picksElement) {
32 | picksElement = createElementAspect.createElement('UL');
33 | isDisposablePicksElementFlag = true;
34 | }
35 | staticDom.containerElement = containerElement;
36 | staticDom.isDisposablePicksElementFlag = isDisposablePicksElementFlag;
37 | staticDom.picksElement = picksElement;
38 |
39 | return {
40 | staticManager: {
41 | appendToContainer(){
42 | let {containerElement, isDisposablePicksElementFlag, choicesDom, picksDom, filterDom} = staticDom;
43 | picksDom.pickFilterElement.appendChild(filterDom.filterInputElement);
44 | picksDom.picksElement.appendChild(picksDom.pickFilterElement);
45 | containerElement.appendChild(choicesDom.choicesElement);
46 | if (isDisposablePicksElementFlag)
47 | containerElement.appendChild(picksDom.picksElement)
48 | },
49 | dispose(){
50 | let {containerElement, containerClass, isDisposablePicksElementFlag, choicesDom, picksDom, filterDom} = staticDom;
51 | containerElement.removeChild(choicesDom.choicesElement);
52 | if (removableContainerClass)
53 | containerElement.classList.remove(containerClass);
54 | if (isDisposablePicksElementFlag)
55 | containerElement.removeChild(picksDom.picksElement)
56 | picksDom.dispose();
57 | filterDom.dispose();
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/plugins/PicksPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 |
3 | export function PicksPlugin(){
4 | return {
5 | plug
6 | }
7 | }
8 |
9 | export function plug(configuration){
10 | return (aspects) => {
11 | return {
12 | plugStaticDom: ()=> {
13 | let {picksList} = aspects;
14 | let {picks} = configuration;
15 | if (picks) {
16 | let {add: origAdd, reset: origReset} = picksList;
17 |
18 | picksList.add = (e) => {
19 | let {remove, index} = origAdd(e);
20 | picks.push(e);
21 | return { remove: composeSync(remove, () => void picks.splice(index(), 1)), index};
22 | }
23 |
24 | picksList.reset = () => {
25 | origReset();
26 | picks.length = 0;
27 | }
28 | }
29 | },
30 | layout: () => {
31 | let {inputAspect, filterDom, filterManagerAspect} = aspects;
32 | let {picks, addOptionPicked} = configuration;
33 | /*
34 | if (!addOptionPicked){
35 | addOptionPicked = (option, index, value) => {
36 | if (value)
37 | picks.push(option);
38 | else
39 | picks.splice(index, 1);
40 | return true;
41 | };
42 | }
43 |
44 | function trySetWrapSelected(option, updateSelected, booleanValue){
45 | let success = false;
46 | var confirmed = setIsOptionSelected(option, booleanValue);
47 | if (!(confirmed===false)) {
48 | updateSelected();
49 | success = true;
50 | }
51 | return success;
52 | }
53 |
54 | let origProcessInput = inputAspect.processInput;
55 | inputAspect.processInput = () => {
56 | let origResult = origProcessInput();
57 | if (!origResult.isEmpty)
58 | {
59 | if ( filterManagerAspect.getNavigateManager().getCount() == 1)
60 | {
61 | // todo: move exact match to filterManager
62 | let fullMatchWrap = filterManagerAspect.getNavigateManager().getHead();
63 | let text = filterManagerAspect.getFilter();
64 | if (fullMatchWrap.choice.searchText == text)
65 | {
66 | let success = trySetWrapSelected(fullMatchWrap, true);
67 | if (success) {
68 | filterDom.setEmpty();
69 | origResult.isEmpty = true;
70 | }
71 | }
72 | }
73 | }
74 | return origResult;
75 | }*/
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/src/plugins/CustomChoiceStylingsPlugin.js:
--------------------------------------------------------------------------------
1 | import { composeSync } from "../ToolsJs";
2 |
3 | export function CustomChoiceStylingsPlugin(defaults){
4 | defaults.customChoiceStylings = null;
5 | return {
6 | plug
7 | }
8 | }
9 |
10 | export function plug(configuration){
11 | return (aspects) => {
12 | return {
13 | plugStaticDom: ()=> {
14 | let {choiceDomFactory} = aspects;
15 | let customChoiceStylings = configuration.customChoiceStylings;
16 | if (customChoiceStylings) {
17 |
18 | let customChoiceStylingsAspect = CustomChoiceStylingsAspect(customChoiceStylings);
19 | ExtendChoiceDomFactory(choiceDomFactory, customChoiceStylingsAspect);
20 | }
21 | }
22 | }
23 | }
24 | }
25 |
26 | function ExtendChoiceDomFactory(choiceDomFactory, customChoiceStylingsAspect){
27 | let origChoiceDomFactoryCreate = choiceDomFactory.create;
28 | choiceDomFactory.create = function(choice){
29 | origChoiceDomFactoryCreate(choice);
30 | customChoiceStylingsAspect.customize(choice.wrap, choice.choiceDom, choice.choiceDomManagerHandlers);
31 | }
32 | }
33 |
34 | function CustomChoiceStylingsAspect(customChoiceStylings){
35 | return {
36 | customize(choice){
37 | var handlers = customChoiceStylings(choice.choiceDom, choice.wrap.option);
38 |
39 | if (handlers){
40 | function customChoiceStylingsClosure(custom){
41 | return function() {
42 | custom({
43 | isOptionSelected: choice.wrap.isOptionSelected,
44 | isOptionDisabled: choice.wrap.isOptionDisabled,
45 | isHoverIn: choice.isHoverIn,
46 | //isHighlighted: wrap.choice.isHighlighted // TODO isHighlighted should be developed
47 | });
48 | }
49 | }
50 | let choiceDomManagerHandlers = choice.choiceDomManagerHandlers;
51 | if (choiceDomManagerHandlers.updateHoverIn && handlers.updateHoverIn)
52 | choiceDomManagerHandlers.updateHoverIn
53 | = composeSync(choiceDomManagerHandlers.updateHoverIn, customChoiceStylingsClosure(handlers.updateHoverIn) );
54 | if (choiceDomManagerHandlers.updateSelected && handlers.updateSelected)
55 | choiceDomManagerHandlers.updateSelected
56 | = composeSync(choiceDomManagerHandlers.updateSelected, customChoiceStylingsClosure(handlers.updateSelected));
57 | if (choiceDomManagerHandlers.updateDisabled && handlers.updateDisabled)
58 | choiceDomManagerHandlers.updateDisabled
59 | = composeSync(choiceDomManagerHandlers.updateDisabled, customChoiceStylingsClosure(handlers.updateDisabled));
60 | if (choiceDomManagerHandlers.updateHighlighted && handlers.updateHighlighted)
61 | choiceDomManagerHandlers.updateHighlighted
62 | = composeSync(choiceDomManagerHandlers.updateHighlighted, customChoiceStylingsClosure(handlers.updateHighlighted));
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/AddToJQueryPrototype.js:
--------------------------------------------------------------------------------
1 | export function addToJQueryPrototype(pluginName, createPlugin, $){
2 | const firstChar = pluginName.charAt(0);
3 | const firstCharLower = firstChar.toLowerCase();
4 | if (firstCharLower == firstChar) {
5 | throw new Error(`Plugin name '${pluginName}' should be started from upper case char`)
6 | }
7 | const prototypableName = firstCharLower + pluginName.slice(1)
8 | const noConflictPrototypable = $.fn[prototypableName];
9 | const noConflictPrototypableForInstance = $.fn[pluginName];
10 | const dataKey = `DashboardCode.${pluginName}`;
11 |
12 | function createInstance(options, e, $e){
13 | const optionsRef = (typeof options === 'object') || options instanceof Function ? options:null;
14 | let instance = createPlugin(e, optionsRef,
15 | () => {
16 | $e.removeData(dataKey)
17 | });
18 | $e.data(dataKey, instance);
19 | return instance;
20 | }
21 |
22 | function prototypable(options){
23 | let output=[];
24 | this.each( function (i, e) {
25 | let $e = $(e);
26 | let instance = $e.data(dataKey)
27 | let isMethodName = typeof options === 'string';
28 | if (!instance) {
29 | if (isMethodName && /Dispose/.test(options))
30 | return;
31 | instance = createInstance(options, e, $e);
32 | }
33 | if (isMethodName) {
34 | let methodName = options;
35 | if (typeof instance[methodName] === 'undefined') {
36 | let lMethodName = methodName.charAt(0).toLowerCase() + methodName.slice(1)
37 | if ( typeof instance[lMethodName] === 'undefined') {
38 | throw new Error(`No method named '${methodName}'`)
39 | } else {
40 | methodName = lMethodName;
41 | }
42 | }
43 | let result = instance[methodName]();
44 | if (result !== undefined){
45 | output.push(result);
46 | }
47 | }
48 | })
49 | if (output.length==0)
50 | return this;
51 | else if (output.length==1)
52 | return output[0];
53 | else
54 | return output;
55 | }
56 |
57 | function prototypableForInstance(options){
58 | let instance = this.data(dataKey);
59 | if (instance)
60 | return instance;
61 | else if (this.length === 1){
62 | return createInstance(options, this.get(0), this);
63 | } else if (this.length > 1){
64 | let output=[];
65 | this.each(function(i, e){
66 | output.push(createInstance(options, e, $(e)));
67 | })
68 | return output;
69 | }
70 | }
71 |
72 | $.fn[prototypableName] = prototypable;
73 | $.fn[prototypableName].noConflict = function () {
74 | $.fn[prototypableName] = noConflictPrototypable
75 | return prototypable;
76 | }
77 |
78 | $.fn[pluginName] = prototypableForInstance;
79 | $.fn[pluginName].noConflict = function () {
80 | $.fn[pluginName] = noConflictPrototypableForInstance
81 | return prototypableForInstance;
82 | }
83 |
84 | return prototypable;
85 | }
--------------------------------------------------------------------------------
/src/plugins/ChoicesDynamicStylingPlugin.js:
--------------------------------------------------------------------------------
1 | // aka auto height and scrolling
2 | export function ChoicesDynamicStylingPlugin(defaults, environment){
3 | preset(defaults)
4 | return {
5 | plug
6 | }
7 | }
8 |
9 | export function preset(o){
10 | o.useChoicesDynamicStyling = false;
11 | o.choicesDynamicStyling = (aspects)=>choicesDynamicStyling(aspects, window);
12 | o.minimalChoicesDynamicStylingMaxHeight = 20;
13 | }
14 |
15 | export function plug(configuration){
16 | let {choicesDynamicStyling, useChoicesDynamicStyling} = configuration;
17 | return (aspects) => {
18 | return {
19 | layout: () => {
20 |
21 | if (useChoicesDynamicStyling) {
22 | let {choicesVisibilityAspect, specialPicksEventsAspect} = aspects;
23 | var origSetChoicesVisible = choicesVisibilityAspect.setChoicesVisible;
24 | choicesVisibilityAspect.setChoicesVisible =
25 | function(visible){
26 | if (visible)
27 | choicesDynamicStyling(aspects);
28 | origSetChoicesVisible(visible);
29 | };
30 | var origBackSpace = specialPicksEventsAspect.backSpace;
31 | specialPicksEventsAspect.backSpace = (pick) => {origBackSpace(pick); choicesDynamicStyling(aspects);};
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
38 | function choicesDynamicStyling(aspects, window){
39 | let {choicesDom, navigateAspect, configuration} = aspects;
40 | let choicesElement = choicesDom.choicesElement;
41 | let minimalChoicesDynamicStylingMaxHeight = configuration.minimalChoicesDynamicStylingMaxHeight;
42 |
43 | //find height of the browser window
44 | var g = window.document.getElementsByTagName('body')[0],
45 | e = window.document.documentElement,
46 | y = window.innerHeight || e.clientHeight || g.clientHeight;
47 |
48 | //find position of choicesElement, if it's at the bottom of the page make the choicesElement shorter
49 | var pos = choicesElement.parentNode.getBoundingClientRect();
50 | var new_y = y - pos.top;
51 |
52 | //calculate multi select max-height
53 | var msHeight = Math.max(minimalChoicesDynamicStylingMaxHeight, Math.round((new_y * 0.85))); // Michalek: 0.85 is empiric value, without it list was longer than footer height ; TODO: propose better way
54 |
55 | //add css height value
56 | choicesElement.style.setProperty("max-height", msHeight+"px");
57 | choicesElement.style.setProperty("overflow-y", "auto");
58 |
59 | if (!choicesDom.ChoicesDynamicStylingPlugin_scrollHandle){
60 | choicesDom.ChoicesDynamicStylingPlugin_scrollHandle = true;
61 | var origNavigateAspectNavigate = navigateAspect.navigate;
62 | navigateAspect.navigate = function(down){
63 | var wrap = origNavigateAspectNavigate(down);
64 | if (wrap!= null && wrap.choice!=null && wrap.choice.choiceDom.choiceElement!=null)
65 | wrap.choice.choiceDom.choiceElement.scrollIntoView(false); // alignTo false - scroll to the top bottom of dropdown first
66 | // TODO: BUG if mouse left on the dropdow scroll to bottom and one after doesn't work properly
67 | return wrap;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"],
3 | "plugins": [
4 | "stylelint-order"
5 | ],
6 | "rules": {
7 | "selector-combinator-space-before" : null,
8 | "block-closing-brace-newline-before": null,
9 | "no-invalid-position-at-import-rule": null,
10 | "selector-descendant-combinator-no-non-space":null,
11 | "declaration-colon-space-after":null,
12 | "at-rule-empty-line-before": null,
13 | "at-rule-name-space-after": "always",
14 | "at-rule-no-vendor-prefix": true,
15 | "at-rule-semicolon-space-before": "never",
16 | "block-closing-brace-empty-line-before": null,
17 | "block-closing-brace-newline-after": null,
18 | "block-opening-brace-space-before": null,
19 | "color-named": null,
20 | "declaration-block-semicolon-newline-after": null,
21 | "declaration-block-semicolon-newline-before": "never-multi-line",
22 | "declaration-block-semicolon-space-after": "always-single-line",
23 | "declaration-empty-line-before": null,
24 | "declaration-no-important": true,
25 | "font-family-name-quotes": "always-where-recommended",
26 | "font-weight-notation": "numeric",
27 | "function-url-no-scheme-relative": true,
28 | "function-url-quotes": "always",
29 | "length-zero-no-unit": true,
30 | "max-empty-lines": 2,
31 | "max-line-length": null,
32 | "media-feature-name-no-vendor-prefix": true,
33 | "media-feature-parentheses-space-inside": "never",
34 | "media-feature-range-operator-space-after": "always",
35 | "media-feature-range-operator-space-before": "never",
36 | "no-descending-specificity": null,
37 | "no-duplicate-selectors": true,
38 | "number-leading-zero": null,
39 | "indentation": null,
40 | "media-feature-name-no-unknown": [true, {
41 | "ignoreMediaFeatureNames": ["prefers-reduced-motion"]
42 | }],
43 | "order/properties-order": null,
44 | "property-no-vendor-prefix": true,
45 | "rule-empty-line-before": null,
46 | "scss/dollar-variable-default": [true, { "ignore": "local" }],
47 | "selector-attribute-quotes": "always",
48 | "selector-list-comma-newline-after": null,
49 | "selector-list-comma-newline-before": "never-multi-line",
50 | "selector-list-comma-space-after": "always-single-line",
51 | "selector-list-comma-space-before": "never-single-line",
52 | "selector-max-attribute": 2,
53 | "selector-max-class": 7,
54 | "selector-max-combinators": 7,
55 | "selector-max-compound-selectors": 7,
56 | "selector-max-empty-lines": 1,
57 | "selector-max-id": 0,
58 | "selector-max-specificity": null,
59 | "selector-max-type": 3,
60 | "selector-max-universal": 1,
61 | "selector-no-qualifying-type": [true, {"ignore": ["attribute", "class", "id"]}],
62 | "selector-no-vendor-prefix": true,
63 | "string-quotes": "double",
64 | "value-keyword-case": "lower",
65 | "value-list-comma-newline-after": "never-multi-line",
66 | "value-list-comma-newline-before": "never-multi-line",
67 | "value-list-comma-space-after": "always",
68 | "value-no-vendor-prefix": true,
69 | "no-eol-whitespace": null,
70 | "block-opening-brace-newline-after":null,
71 | "no-missing-end-of-source-newline":null,
72 | "comment-empty-line-before": null,
73 | "comment-whitespace-inside": null,
74 | "scss/at-import-no-partial-leading-underscore": null,
75 | "color-function-notation": null
76 | }
77 | }
--------------------------------------------------------------------------------
/src/plugins/PickButtonPlugin.js:
--------------------------------------------------------------------------------
1 | import {EventBinder} from '../ToolsDom';
2 | import {addStyling} from '../ToolsStyling';
3 | import {composeSync} from '../ToolsJs';
4 |
5 | export function PickButtonPlugCssPatchBs4(defaults){
6 | // increase font and limit the line
7 | defaults.cssPatch.pickButton = {float : "none", verticalAlign: "text-top", fontSize:'1.8em', lineHeight: '0.5em', fontWeight:'400' };
8 |
9 | }
10 |
11 | export function PickButtonPlugCssPatchBs5(defaults){
12 | defaults.cssPatch.pickButton = {float : "none", verticalAlign: "text-top", fontSize:'0.8em'};
13 | }
14 |
15 | export function PickButtonBs4Plugin(defaults){
16 | defaults.pickButtonHTML = '';
17 | defaults.css.pickButton = 'close';
18 | return PickButtonPlugin()
19 | }
20 |
21 | export function PickButtonBs5Plugin(defaults){
22 | defaults.pickButtonHTML = '';
23 | defaults.css.pickButton = 'btn-close';
24 | return PickButtonPlugin()
25 | }
26 |
27 | export function PickButtonPlugin(){
28 | return {plug}
29 | }
30 |
31 | export function plug(configuration){
32 | return (aspects) => {
33 | return {
34 | plugStaticDom: () => {
35 | var {pickDomFactory, staticDom} = aspects;
36 | ExtendPickDomFactory(pickDomFactory, staticDom.createElementAspect, configuration.pickButtonHTML, configuration.css);
37 | },
38 | layout: ()=>{
39 | var {producePickAspect} = aspects;
40 | ExtendProducePickAspect(producePickAspect);
41 |
42 | }
43 | }
44 | }
45 | }
46 |
47 | function ExtendProducePickAspect(producePickAspect){
48 | let origProducePickPickAspect = producePickAspect.producePick;
49 | producePickAspect.producePick = (wrap)=>{
50 | let pick = origProducePickPickAspect(wrap)
51 | pick.removeOnButton = (event) => {
52 | pick.setSelectedFalse();
53 | }
54 | pick.dispose = composeSync(
55 | pick.dispose,
56 | ()=>{pick.removeOnButton=null}
57 | );
58 | return pick;
59 | }
60 | }
61 |
62 | function ExtendPickDomFactory(pickDomFactory, createElementAspect, pickButtonHTML, css){
63 | var origCreatePickDomFactory = pickDomFactory.create;
64 | pickDomFactory.create = (pick) => {
65 |
66 | origCreatePickDomFactory(pick);
67 | let {pickDom,pickDomManagerHandlers} = pick;
68 | createElementAspect.createElementFromHtmlPutAfter(pickDom.pickContentElement, pickButtonHTML);
69 | let pickButtonElement = pickDom.pickElement.querySelector('BUTTON');
70 | pickDom.pickButtonElement=pickButtonElement;
71 | pickDomManagerHandlers.disableButton = (val)=> {
72 | pickButtonElement.disabled = val;
73 | }
74 |
75 | let eventBinder = EventBinder();
76 | eventBinder.bind(pickButtonElement, "click", (event)=>pick.removeOnButton(event));
77 |
78 | addStyling(pickButtonElement, css.pickButton);
79 |
80 | pick.dispose = composeSync(
81 | pick.dispose,
82 | ()=>{
83 | eventBinder.unbind();
84 | pickDom.pickButtonElement=null;
85 | pickDomManagerHandlers.disableButton = null;
86 | }
87 | )
88 | }
89 | }
--------------------------------------------------------------------------------
/src/plugins/DisableComponentPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 |
3 | export function DisableComponentPlugin(defaults){
4 | preset(defaults)
5 | return {
6 | plug
7 | }
8 | }
9 |
10 | export function preset(defaults){
11 | defaults.getDisabled = () => null;
12 | }
13 |
14 | export function plug(configuration){
15 | let disabledComponentAspect = DisabledComponentAspect(configuration.getDisabled);
16 | return (aspects) => {
17 | aspects.disabledComponentAspect=disabledComponentAspect;
18 | return {
19 | plugStaticDom: () => {
20 | var {pickDomFactory} = aspects;
21 | ExtendPickDomFactory(pickDomFactory, disabledComponentAspect);
22 | },
23 | layout: () => {
24 | var {updateAppearanceAspect, picksList, picksDom, picksElementAspect} = aspects;
25 |
26 | var disableComponent = (isComponentDisabled)=>{
27 | picksList.forEach(pick=>pick.pickDomManagerHandlers.updateComponentDisabled())
28 | picksDom.disable(isComponentDisabled);
29 | }
30 |
31 | var origOnClick = picksElementAspect.onClick;
32 | picksElementAspect.onClick = (handler)=>{
33 | disableComponent = (isComponentDisabled)=>{
34 | picksList.forEach(pick=>pick.pickDomManagerHandlers.updateComponentDisabled())
35 | picksDom.disable(isComponentDisabled);
36 | if (isComponentDisabled)
37 | picksElementAspect.onClickUnbind(); //componentDisabledEventBinder.unbind();
38 | else
39 | origOnClick(handler); //componentDisabledEventBinder.bind(picksElement, "click", handler);
40 | }
41 | }
42 |
43 | let isComponentDisabled; // state!
44 | function updateDisabled(){
45 | let newIsComponentDisabled = disabledComponentAspect.getDisabled()??false;
46 | if (isComponentDisabled!==newIsComponentDisabled){
47 | isComponentDisabled=newIsComponentDisabled;
48 | disableComponent(newIsComponentDisabled);
49 | }
50 | }
51 |
52 | updateAppearanceAspect.updateAppearance = composeSync(updateAppearanceAspect.updateAppearance, updateDisabled);
53 |
54 | return{
55 | buildApi(api){
56 | api.updateDisabled = updateDisabled;
57 | }
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
64 | export function DisabledComponentAspect(getDisabled) {
65 | return {
66 | getDisabled
67 | }
68 | }
69 |
70 | function ExtendPickDomFactory(pickDomFactory, disabledComponentAspect){
71 | var origCreatePickDomFactory = pickDomFactory.create;
72 | pickDomFactory.create = (pick) => {
73 | origCreatePickDomFactory(pick);
74 | let pickDomManagerHandlers = pick.pickDomManagerHandlers;
75 | pickDomManagerHandlers.updateComponentDisabled = () => {
76 | if (pickDomManagerHandlers.disableButton)
77 | pickDomManagerHandlers.disableButton(disabledComponentAspect.getDisabled()??false)
78 | };
79 | pickDomManagerHandlers.updateComponentDisabled();
80 | }
81 | }
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | //import {BsAppearancePlugin} from './BsAppearancePlugin'
2 | import {BsAppearanceBs4Plugin} from './BsAppearanceBs4Plugin'
3 | import {BsAppearanceBs5Plugin} from './BsAppearanceBs5Plugin'
4 | import {BsAppearanceBs4CssPatchPlugin} from './BsAppearanceBs4CssPatchPlugin'
5 | import {BsAppearanceBs5CssPatchPlugin} from './BsAppearanceBs5CssPatchPlugin'
6 |
7 | import {LabelForAttributePlugin} from './LabelForAttributePlugin'
8 | import {RtlPlugin} from './RtlPlugin'
9 | import {FormResetPlugin} from './FormResetPlugin'
10 | import {ValidationApiPlugin} from './ValidationApiPlugin'
11 |
12 | import {HiddenOptionPlugin} from './HiddenOptionPlugin'
13 | import {HiddenOptionAltPlugin} from './HiddenOptionAltPlugin'
14 |
15 | import {CssPatchBs4Plugin} from './CssPatchBs4Plugin'
16 | import {CssPatchBs5Plugin} from './CssPatchBs5Plugin'
17 |
18 | import {JQueryMethodsPlugin} from './JQueryMethodsPlugin'
19 | import {OptionsApiPlugin} from './OptionsApiPlugin'
20 | import {FormRestoreOnBackwardPlugin} from './FormRestoreOnBackwardPlugin'
21 | import {SelectElementPlugin} from './SelectElementPlugin'
22 | import {SelectedOptionPlugin} from './SelectedOptionPlugin'
23 | import {DisabledOptionPlugin, DisabledOptionCssPatchPlugin} from './DisabledOptionPlugin'
24 | import {PicksApiPlugin} from './PicksApiPlugin'
25 | import {PicksPlugin} from './PicksPlugin'
26 |
27 | import {CreatePopperPlugin} from './CreatePopperPlugin'
28 | import {ChoicesDynamicStylingPlugin} from './ChoicesDynamicStylingPlugin'
29 |
30 | import {HighlightPlugin} from './HighlightPlugin'
31 |
32 | import {CustomChoiceStylingsPlugin} from './CustomChoiceStylingsPlugin'
33 | import {CustomPickStylingsPlugin} from './CustomPickStylingsPlugin'
34 |
35 | import {UpdateAppearancePlugin} from './UpdateAppearancePlugin'
36 | import {DisableComponentPlugin} from './DisableComponentPlugin'
37 |
38 | import {PlaceholderPlugin} from './PlaceholderPlugin'
39 | import {PlaceholderCssPatchPlugin} from './PlaceholderCssPatchPlugin'
40 | import {FloatingLabelPlugin} from './FloatingLabelPlugin'
41 | import {FloatingLabelCssPatchBs5Plugin} from './FloatingLabelCssPatchBs5Plugin'
42 |
43 | import {WarningCssPatchPlugin} from './WarningCssPatchPlugin'
44 | import {WarningBs4Plugin} from './WarningBs4Plugin'
45 | import {WarningBs5Plugin} from './WarningBs5Plugin'
46 |
47 | import {PickButtonBs4Plugin, PickButtonBs5Plugin, PickButtonPlugCssPatchBs4, PickButtonPlugCssPatchBs5} from './PickButtonPlugin'
48 |
49 |
50 | export {
51 | BsAppearanceBs4Plugin, BsAppearanceBs5Plugin,
52 | BsAppearanceBs4CssPatchPlugin, BsAppearanceBs5CssPatchPlugin,
53 |
54 | CssPatchBs4Plugin, CssPatchBs5Plugin,
55 |
56 | SelectElementPlugin,
57 | LabelForAttributePlugin, HiddenOptionPlugin, ValidationApiPlugin,
58 | UpdateAppearancePlugin,
59 |
60 | PickButtonBs4Plugin, PickButtonBs5Plugin, PickButtonPlugCssPatchBs4, PickButtonPlugCssPatchBs5,
61 |
62 | DisableComponentPlugin,
63 | FormResetPlugin, CreatePopperPlugin, RtlPlugin, PlaceholderPlugin, PlaceholderCssPatchPlugin,
64 | OptionsApiPlugin,
65 | JQueryMethodsPlugin,
66 | SelectedOptionPlugin, FormRestoreOnBackwardPlugin,
67 | DisabledOptionPlugin, DisabledOptionCssPatchPlugin,
68 | PicksApiPlugin, HighlightPlugin,
69 | ChoicesDynamicStylingPlugin, CustomPickStylingsPlugin, CustomChoiceStylingsPlugin,
70 |
71 | FloatingLabelPlugin, FloatingLabelCssPatchBs5Plugin,
72 | WarningBs5Plugin, WarningBs4Plugin, WarningCssPatchPlugin,
73 |
74 | PicksPlugin,
75 |
76 | HiddenOptionAltPlugin
77 | }
--------------------------------------------------------------------------------
/src/PicksDomFactory.js:
--------------------------------------------------------------------------------
1 | import {addStyling, toggleStyling} from './ToolsStyling';
2 |
3 | export function PicksDomFactory(staticDom){
4 | return {
5 | create(){
6 | var {picksElement, isDisposablePicksElementFlag, css, createElementAspect} = staticDom;
7 | var pickFilterElement = createElementAspect.createElement('LI');
8 |
9 | addStyling(picksElement, css.picks);
10 | addStyling(pickFilterElement, css.pickFilter);
11 |
12 | let disableToggleStyling = toggleStyling(picksElement, css.picks_disabled);
13 | let focusToggleStyling = toggleStyling(picksElement, css.picks_focus);
14 | let isFocusIn = false;
15 |
16 | return {
17 | picksElement,
18 | isDisposablePicksElementFlag,
19 | pickFilterElement,
20 |
21 | createPickElement(){
22 | var pickElement = createElementAspect.createElement('LI');
23 | addStyling(pickElement, css.pick);
24 | return {
25 | pickElement,
26 | attach: (beforeElement) => picksElement.insertBefore(pickElement, beforeElement??pickFilterElement),
27 | detach: () => picksElement.removeChild(pickElement)
28 | };
29 | },
30 | disable(isComponentDisabled){
31 | disableToggleStyling(isComponentDisabled)
32 | },
33 | toggleFocusStyling(){
34 | focusToggleStyling(isFocusIn)
35 | },
36 | getIsFocusIn(){
37 | return isFocusIn;
38 | },
39 | setIsFocusIn(newIsFocusIn){
40 | isFocusIn = newIsFocusIn
41 | },
42 | dispose(){
43 | if (!isDisposablePicksElementFlag){
44 | disableToggleStyling(false)
45 | focusToggleStyling(false)
46 |
47 | if (pickFilterElement.parentNode)
48 | pickFilterElement.parentNode.removeChild(pickFilterElement)
49 | }
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | export function PicksDomFactoryPlugCss(css){
57 | css.picks = 'form-control';
58 | css.pickFilter = '';
59 | css.picks_disabled = 'disabled';
60 | css.picks_focus = 'focus';
61 | css.pick = 'badge';
62 | }
63 |
64 | function PicksDomFactoryPlugCssPatch(cssPatch){
65 | cssPatch.picks = {listStyleType:'none', display:'flex', flexWrap:'wrap', height: 'auto', marginBottom: '0',cursor: 'text'},
66 | cssPatch.picks_disabled = {backgroundColor: '#e9ecef'};
67 | cssPatch.picks_focus = {borderColor: '#80bdff', boxShadow: '0 0 0 0.2rem rgba(0, 123, 255, 0.25)'};
68 | cssPatch.pick = {paddingLeft: '0', paddingRight: '.5rem', paddingInlineStart:'0', paddingInlineEnd:'0.5rem'}
69 | }
70 |
71 | export function PicksDomFactoryPlugCssPatchBs4(cssPatch){
72 | PicksDomFactoryPlugCssPatch(cssPatch)
73 | // TODO: this is done for button and should be moved to button plugin
74 | //cssPatch.pick.lineHeight = '1.5em';
75 | cssPatch.pick.paddingTop = '0.35em';
76 | cssPatch.pick.paddingBottom = '0.35em';
77 | }
78 |
79 | export function PicksDomFactoryPlugCssPatchBs5(cssPatch){
80 | PicksDomFactoryPlugCssPatch(cssPatch)
81 | // TODO: this is done for button and should be moved to button plugin
82 | cssPatch.pick.color ='var(--bs-dark)';
83 | }
--------------------------------------------------------------------------------
/src/plugins/WarningPlugin.js:
--------------------------------------------------------------------------------
1 | import {addStyling} from "../ToolsStyling";
2 | import {composeSync} from '../ToolsJs';
3 |
4 | const defNoResultsWarningMessage = 'No results found';
5 |
6 | export function preset(o){o.noResultsWarning=defNoResultsWarningMessage; o.isNoResultsWarningEnabled=false;}
7 |
8 | export function plug(configuration){
9 | return (aspects) => {
10 | return {
11 | layout: () => {
12 | let {choicesDom, staticManager, afterInputAspect, filterManagerAspect, resetLayoutAspect, staticDom} = aspects;
13 | let {createElementAspect} = staticDom;
14 | let {css, noResultsWarning} = configuration;
15 |
16 | if (configuration.isNoResultsWarningEnabled){
17 | let warningAspect = WarningAspect(choicesDom, createElementAspect, staticManager, css);
18 | aspects.warningAspect = warningAspect;
19 |
20 | ExtendAfterInputAspect(afterInputAspect, warningAspect, filterManagerAspect, noResultsWarning);
21 |
22 | resetLayoutAspect.resetLayout = composeSync(() => warningAspect.hide(), resetLayoutAspect.resetLayout);
23 | }
24 | },
25 | append: ()=> {
26 | let {createPopperAspect, filterDom, warningAspect, staticManager, disposeAspect} = aspects;
27 | if (warningAspect){
28 | let filterInputElement = filterDom.filterInputElement;
29 |
30 | let pop2 = createPopperAspect.createPopper(warningAspect.warningElement, filterInputElement, false);
31 | staticManager.appendToContainer = composeSync(staticManager.appendToContainer, pop2.init);
32 |
33 | var origWarningAspectShow = warningAspect.show;
34 | warningAspect.show = (msg) => {
35 | pop2.update();
36 | origWarningAspectShow(msg);
37 | }
38 | disposeAspect.dispose = composeSync(disposeAspect.dispose, pop2.dispose);
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
45 | function ExtendAfterInputAspect(afterInputAspect, warningAspect, filterManagerAspect, noResultsWarning){
46 | var origVisible = afterInputAspect.visible;
47 | afterInputAspect.visible = (showChoices, visibleCount) => {
48 | warningAspect.hide();
49 | origVisible(showChoices, visibleCount)
50 | }
51 |
52 | var origNotVisible = afterInputAspect.notVisible;
53 | afterInputAspect.notVisible = (hideChoices) => {
54 | origNotVisible(hideChoices);
55 |
56 | if (filterManagerAspect.getFilter())
57 | warningAspect.show(noResultsWarning);
58 | else
59 | warningAspect.hide();
60 | }
61 | }
62 |
63 | function WarningAspect(choicesDom, createElementAspect, staticManager, css){
64 | let choicesElement = choicesDom.choicesElement;
65 |
66 | var warningElement = createElementAspect.createElement('DIV');
67 | staticManager.appendToContainer = composeSync(staticManager.appendToContainer, ()=>
68 | choicesElement.parentNode.insertBefore(warningElement, choicesElement.nextSibling));
69 |
70 | warningElement.style.display = 'none';
71 | addStyling(warningElement, css.warning);
72 |
73 |
74 | return {
75 | warningElement,
76 | show(message){
77 | warningElement.style.display = 'block';
78 | warningElement.innerHTML = message;
79 | },
80 | hide(){
81 | warningElement.style.display = 'none';
82 | warningElement.innerHTML = "";
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/src/plugins/HighlightPlugin.js:
--------------------------------------------------------------------------------
1 | export function HighlightPlugin(defaults){
2 | defaults.useHighlighting = false;
3 | return {
4 | plug
5 | }
6 | }
7 |
8 | function ExtendChoiceDomFactory(choiceDomFactory, dataWrap){
9 | var origChoiceDomFactoryCreate = choiceDomFactory.create;
10 | choiceDomFactory.create = (choice) => {
11 | origChoiceDomFactoryCreate(choice);
12 | let choiceElement = choice.choiceDom.choiceElement;
13 | choice.choiceDomManagerHandlers.updateHighlighted = () => {
14 | var text = dataWrap.getText(choice.wrap.option);
15 | var highlighter = aspects.highlightAspect.getHighlighter();
16 | if (highlighter)
17 | highlighter(choiceElement, choice.choiceDom, text);
18 | else
19 | choiceElement.textContent = text;
20 | };
21 | }
22 | }
23 |
24 | export function plug(configuration){
25 | return (aspects) => {
26 | if (configuration.useHighlighting)
27 | aspects.highlightAspect = HighlightAspect();
28 | return {
29 | plugStaticDom(){
30 | var {choiceDomFactory, dataWrap} = aspects;
31 | ExtendChoiceDomFactory(choiceDomFactory, dataWrap);
32 | },
33 | layout(){
34 | let {highlightAspect, filterManagerAspect, produceChoiceAspect} = aspects;
35 | if (highlightAspect){
36 | let origProcessEmptyInput = filterManagerAspect.processEmptyInput;
37 | filterManagerAspect.processEmptyInput = function(){
38 | highlightAspect.reset();
39 | origProcessEmptyInput();
40 | }
41 | let origSetFilter = filterManagerAspect.setFilter;
42 | filterManagerAspect.setFilter = function(text){
43 | highlightAspect.set(text);
44 | origSetFilter(text);
45 | }
46 | let origProduceChoice = produceChoiceAspect.produceChoice;
47 | produceChoiceAspect.produceChoice = function(wrap){
48 | origProduceChoice(wrap);
49 | let origSetVisible = wrap.choice.choiceDomManagerHandlers.setVisible;
50 | wrap.choice.choiceDomManagerHandlers.setVisible = function(v){
51 | origSetVisible(v);
52 | wrap.choice.choiceDomManagerHandlers.updateHighlighted();
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | function HighlightAspect(){
62 | let highlighter = null;
63 | return {
64 | getHighlighter(){
65 | return highlighter;
66 | },
67 | set(filter){
68 | var guarded = filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
69 | var regex = new RegExp("("+guarded+")","gi");
70 | highlighter = function(e, choiceDom, text){
71 | // TODO replace with
72 | // var pos = text.indexOf(filter);
73 | e.innerHTML = text.replace(regex,"$1");
74 | // TODO to method
75 | // var nodes = e.querySelectorAll('u');
76 | // var array = Array.prototype.slice.call(nodes);
77 | // if (choiceDom.highlightedElements)
78 | // choiceDom.highlightedElements.concat(array);
79 | // else
80 | // choiceDom.highlightedElements = array;
81 | }
82 | },
83 | reset(){
84 | highlighter = null;
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/src/plugins/HiddenOptionAltPlugin.js:
--------------------------------------------------------------------------------
1 | export function HiddenOptionAltPlugin(){
2 | return {
3 | plug
4 | }
5 | }
6 |
7 | export function plug(configuration){
8 | return (aspects) => {
9 | return {
10 | layout: () => {
11 | let {createWrapAspect, isChoiceSelectableAspect,
12 | wrapsCollection, buildAndAttachChoiceAspect, countableChoicesListInsertAspect, countableChoicesList
13 | } = aspects;
14 |
15 | countableChoicesListInsertAspect.countableChoicesListInsert = (wrap, key) => {
16 | if ( !wrap.isOptionHidden ){
17 | let choiceNext = wrapsCollection.getNext(key, c=>!c.isOptionHidden );
18 | countableChoicesList.add(wrap, choiceNext)
19 | }
20 | }
21 |
22 | let origBuildAndAttachChoice = buildAndAttachChoiceAspect.buildAndAttachChoice;
23 | buildAndAttachChoiceAspect.buildAndAttachChoice=(wrap, getNextElement) => {
24 | origBuildAndAttachChoice(wrap, getNextElement);
25 | wrap.choice.choiceDomManagerHandlers.setVisible(!wrap.isOptionHidden)
26 | }
27 |
28 | var origIsSelectable = isChoiceSelectableAspect.isSelectable;
29 | isChoiceSelectableAspect.isSelectable = (wrap) => origIsSelectable(wrap) && !wrap.isOptionHidden;
30 |
31 | let {getIsOptionHidden, options} = configuration;
32 | if (options) {
33 | if (!getIsOptionHidden)
34 | getIsOptionHidden = (option) => (option.hidden===undefined)?false:option.hidden;
35 | } else {
36 | if (!getIsOptionHidden)
37 | getIsOptionHidden = (option) => {
38 | return option.hidden;
39 | }
40 | }
41 |
42 | var origCreateWrap = createWrapAspect.createWrap;
43 | createWrapAspect.createWrap = (option) => {
44 | let wrap = origCreateWrap(option);
45 | wrap.isOptionHidden = getIsOptionHidden(option);
46 | return wrap;
47 | };
48 |
49 | return {
50 | buildApi(api){
51 | let getNextNonHidden = (key) => wrapsCollection.getNext(key, c => !c.isOptionHidden );
52 | api.updateOptionsHidden = () =>
53 | wrapsCollection.forLoop( (wrap, key) =>
54 | updateChoiceHidden(wrap, key, getNextNonHidden, countableChoicesList, getIsOptionHidden)
55 | );
56 | api.updateOptionHidden = (key) => updateChoiceHidden(wrapsCollection.get(key), key, getNextNonHidden, countableChoicesList, getIsOptionHidden);
57 | }
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
64 | function updateChoiceHidden(wrap, key, getNextNonHidden, countableChoicesList, getIsOptionHidden){
65 | let newIsOptionHidden = getIsOptionHidden(wrap.option);
66 | if (newIsOptionHidden != wrap.isOptionHidden)
67 | {
68 | wrap.isOptionHidden= newIsOptionHidden;
69 | if (wrap.isOptionHidden)
70 | countableChoicesList.remove(wrap)
71 | else{
72 | let nextChoice = getNextNonHidden(key); // TODO: should not rely on element but do
73 | countableChoicesList.add(wrap, nextChoice);
74 | }
75 | wrap.choice.choiceDomManagerHandlers.setVisible(!wrap.isOptionHidden)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/BsMultiSelectDepricatedParameters.js:
--------------------------------------------------------------------------------
1 | const transformStyles = [
2 | {old:'selectedPanelDisabledBackgroundColor', opt:'picks_disabled', style:"backgroundColor"},
3 | {old:'selectedPanelFocusValidBoxShadow', opt:'picks_focus_valid', style:"boxShadow"},
4 | {old:'selectedPanelFocusInvalidBoxShadow', opt:'picks_focus_invalid', style:"boxShadow"},
5 | {old:'selectedPanelDefMinHeight', opt:'picks_def', style:"minHeight"},
6 | {old:'selectedPanelLgMinHeight', opt:'picks_lg', style:"minHeight"},
7 | {old:'selectedPanelSmMinHeight', opt:'picks_sm', style:"minHeight"},
8 | {old:'selectedItemContentDisabledOpacity', opt:'choiceLabel_disabled', style:"opacity"}
9 | ]
10 |
11 | const transformClasses = [
12 | {old:'dropDownMenuClass', opt:'choices'},
13 | {old:'dropDownItemClass', opt:'choice'},
14 | {old:'dropDownItemHoverClass', opt:'choice_hover'},
15 | {old:'selectedPanelClass', opt:'picks'},
16 | {old:'selectedItemClass', opt:'pick'},
17 | {old:'removeSelectedItemButtonClass', opt:'pickButton'},
18 | {old:'filterInputItemClass', opt:'pickFilter'},
19 | {old:'filterInputClass', opt:'filterInput'},
20 | {old:'selectedPanelFocusClass', opt:'picks_focus'},
21 | {old:'selectedPanelDisabledClass', opt:'picks_disabled'},
22 | {old:'selectedItemContentDisabledClass', opt:'pick_disabled'}
23 | ]
24 |
25 | export function adjustLegacySettings(settings){
26 | if (!settings.css)
27 | settings.css={}
28 | var css =settings.css;
29 |
30 | if (!settings.cssPatch)
31 | settings.cssPatch={}
32 | var cssPatch =settings.cssPatch;
33 |
34 | if (settings.selectedPanelFocusBorderColor || settings.selectedPanelFocusBoxShadow){
35 | console.log("DashboarCode.BsMultiSelect: selectedPanelFocusBorderColor and selectedPanelFocusBoxShadow are depricated, use - cssPatch:{picks_focus:{borderColor:'myValue', boxShadow:'myValue'}}");
36 | if(!cssPatch.picks_focus){
37 | cssPatch.picks_focus = {boxShadow: settings.selectedPanelFocusBoxShadow, borderColor: settings.selectedPanelFocusBorderColor}
38 | }
39 | delete settings.selectedPanelFocusBorderColor;
40 | delete settings.selectedPanelFocusBoxShadow;
41 | }
42 |
43 | transformStyles.forEach(
44 | (i)=>{
45 | if (settings[i.old]){
46 | console.log(`DashboarCode.BsMultiSelect: ${i.old} is depricated, use - cssPatch:{${i.opt}:{${i.style}:'myValue'}}`);
47 | if(!settings[i.opt]){
48 | let opt = {}
49 | opt[i.style] = settings[i.old]
50 | settings.cssPatch[i.opt]=opt;
51 | }
52 | delete settings[i.old];
53 | }
54 | }
55 | )
56 |
57 | transformClasses.forEach( (i) => {
58 | if (settings[i.old]){
59 | console.log(`DashboarCode.BsMultiSelect: ${i.old} is depricated, use - css:{${i.opt}:'myValue'}`);
60 | if(!css[i.opt]){
61 | css[i.opt]= settings[i.old]
62 | }
63 | delete settings[i.old];
64 | }
65 | })
66 |
67 | if (settings.inputColor){
68 | console.log("DashboarCode.BsMultiSelect: inputColor is depricated, remove parameter");
69 | delete settings.inputColor;
70 | }
71 |
72 | if (settings.useCss){
73 | console.log("DashboarCode.BsMultiSelect: 'useCss: true' is depricated, use - 'useCssPatch: false'");
74 | if(!css.pick_disabled){
75 | settings.useCssPatch = !settings.useCss
76 | }
77 | delete settings.useCss;
78 | }
79 |
80 | if (settings.getIsValid || settings.getIsInValid){
81 | throw "DashboarCode.BsMultiSelect: parameters getIsValid and getIsInValid are depricated and removed, use - getValidity that should return (true|false|null) "
82 | }
83 | }
--------------------------------------------------------------------------------
/debugJsSimplePicks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | DashboardCode Multiselect Plugin for Bootstrap 5
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.bs4.min.css:
--------------------------------------------------------------------------------
1 | .dashboardcode-bsmultiselect ul.form-control{display:flex;flex-wrap:wrap;height:auto;min-height:calc(1.5em + .75rem + 2px);margin-bottom:0;cursor:text;list-style-type:none}.dashboardcode-bsmultiselect ul.form-control input{height:auto;padding:0;margin:0;font-weight:inherit;color:inherit;background-color:transparent;border:0;outline:0;box-shadow:none}.dashboardcode-bsmultiselect ul.form-control.disabled{background-color:#e9ecef}.dashboardcode-bsmultiselect ul.form-control::-moz-placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control::placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control>li.badge{padding-left:0;-webkit-padding-start:0;padding-inline-start:0;-webkit-padding-end:0.5rem;padding-inline-end:0.5rem;padding-top:.35em;padding-bottom:.35em;line-height:1.5em}.dashboardcode-bsmultiselect ul.form-control>li.badge button.close{float:none;font-size:1.8em;line-height:.5em;font-weight:400}.dashboardcode-bsmultiselect ul.form-control>li.badge span.disabled{opacity:.65}.dashboardcode-bsmultiselect ul.form-control.focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.dashboardcode-bsmultiselect ul.form-control.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}.dashboardcode-bsmultiselect ul.form-control.form-control-sm input{font-size:.875rem}.dashboardcode-bsmultiselect ul.form-control.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.dashboardcode-bsmultiselect ul.form-control.form-control-lg input{font-size:1.25rem}.dashboardcode-bsmultiselect ul.form-control.is-valid,.was-validated .dashboardcode-bsmultiselect ul.form-control:valid{border-color:#28a745}.dashboardcode-bsmultiselect ul.form-control.is-valid.focus,.was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.dashboardcode-bsmultiselect ul.form-control.is-invalid,.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid{border-color:#dc3545}.dashboardcode-bsmultiselect ul.form-control.is-invalid.focus,.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.dashboardcode-bsmultiselect div.dropdown-menu{list-style-type:none}.dashboardcode-bsmultiselect div.dropdown-menu>ul{padding-left:0;padding-right:0;margin-bottom:0}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li{display:block;width:100%;padding:0 .5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0;cursor:pointer}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control{justify-content:flex-start}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control .custom-control-input,.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control .custom-control-label{cursor:inherit}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.disabled .custom-control-label{color:#6c757d}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover{color:var(--primary);background-color:#e9ecef}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover:not(.disabled){color:var(--primary)}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover.selected{color:var(--primary)}.dashboardcode-bsmultiselect div.dropdown-menu+div.alert-warning{padding-left:.25rem;padding-right:.25rem;z-index:4;font-size:small}.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control{min-height:calc(1.5em + .5rem + 2px)}.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input{font-size:.875rem}.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control{min-height:calc(1.5em + 1rem + 2px)}.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input{font-size:1.25rem}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control-input:valid:checked~.custom-control-label{color:#212529}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control-input:valid:not(:checked)~.custom-control-label{color:#212529}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover .custom-control-input:valid:checked~.custom-control-label{color:var(--primary)}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover .custom-control-input:valid:not(:checked)~.custom-control-label{color:var(--primary)}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control-input:valid:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .custom-control-input:valid:not(:checked)~.custom-control-label::before{border:#adb5bd solid 1px}
2 | /*# sourceMappingURL=BsMultiSelect.bs4.min.css.map */
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.min.css:
--------------------------------------------------------------------------------
1 | .dashboardcode-bsmultiselect ul.form-control{display:flex;flex-wrap:wrap;height:auto;min-height:calc(1.5em + .75rem + 2px);margin-bottom:0;cursor:text;list-style-type:none}.dashboardcode-bsmultiselect ul.form-control input{height:auto;padding:0;margin:0;font-weight:inherit;color:inherit;background-color:transparent;border:0;outline:0;box-shadow:none}.dashboardcode-bsmultiselect ul.form-control.disabled{background-color:#e9ecef}.dashboardcode-bsmultiselect ul.form-control::-moz-placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control::placeholder{color:#6c757d;opacity:1}.dashboardcode-bsmultiselect ul.form-control>li.badge{padding-left:0;-webkit-padding-start:0;padding-inline-start:0;-webkit-padding-end:0.5rem;padding-inline-end:0.5rem;color:var(--bs-dark)}.dashboardcode-bsmultiselect ul.form-control>li.badge button.btn-close{float:none;font-size:.8em}.dashboardcode-bsmultiselect ul.form-control>li.badge span.disabled{opacity:.65}.dashboardcode-bsmultiselect ul.form-control.focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.dashboardcode-bsmultiselect ul.form-control.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}.dashboardcode-bsmultiselect ul.form-control.form-control-sm input{font-size:.875rem}.dashboardcode-bsmultiselect ul.form-control.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.dashboardcode-bsmultiselect ul.form-control.form-control-lg input{font-size:1.25rem}.dashboardcode-bsmultiselect ul.form-control.is-valid,.was-validated .dashboardcode-bsmultiselect ul.form-control:valid{border-color:#198754}.dashboardcode-bsmultiselect ul.form-control.is-valid.focus,.was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.dashboardcode-bsmultiselect ul.form-control.is-invalid,.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid{border-color:#dc3545}.dashboardcode-bsmultiselect ul.form-control.is-invalid.focus,.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.dashboardcode-bsmultiselect div.dropdown-menu>ul{list-style-type:none;padding-left:0;padding-right:0;margin-bottom:0}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li{display:block;width:100%;padding:0 .5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0;cursor:pointer}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check{cursor:inherit;justify-content:flex-start}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check .form-check-input,.dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check .form-check-label{cursor:inherit}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.disabled .form-check-label{opacity:.5}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover{background-color:#e9ecef}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover:not(.disabled){color:var(--bs-primary)}.dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover.selected{color:var(--bs-primary)}.dashboardcode-bsmultiselect div.dropdown-menu+div.alert-warning{padding-left:.25rem;padding-right:.25rem;z-index:4;font-size:small}.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control{min-height:calc(1.5em + .5rem + 2px)}.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input{font-size:.875rem}.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control{min-height:calc(1.5em + 1rem + 2px)}.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input{font-size:1.25rem}.form-floating .dashboardcode-bsmultiselect ul.form-control{min-height:calc(3.5rem + 2px)}.form-floating .dashboardcode-bsmultiselect ul.form-control.floating-lifted{padding-top:1.625rem;padding-left:.7rem;padding-bottom:0}.form-floating .dashboardcode-bsmultiselect+label.floating-lifted{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check-input:valid:checked~.form-check-label,.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check-input:valid:not(:checked)~.form-check-label{color:#212529}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover .form-check-input:valid:checked~.form-check-label,.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li.hover .form-check-input:valid:not(:checked)~.form-check-label{color:var(--bs-primary)}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check-input:valid:checked{border-color:var(--bs-primary);background-color:var(--bs-primary)}.was-validated .dashboardcode-bsmultiselect div.dropdown-menu>ul>li .form-check-input:valid:not(:checked){border:1px solid rgba(0,0,0,.25)}
2 | /*# sourceMappingURL=BsMultiSelect.min.css.map */
--------------------------------------------------------------------------------
/src/plugins/HiddenOptionPlugin.js:
--------------------------------------------------------------------------------
1 | export function HiddenOptionPlugin(){
2 | return {
3 | plug
4 | }
5 | }
6 |
7 | export function plug(configuration){
8 | return (aspects) => {
9 | return {
10 | layout: () => {
11 | let {createWrapAspect, isChoiceSelectableAspect,
12 | wrapsCollection, produceChoiceAspect, buildAndAttachChoiceAspect,
13 | countableChoicesListInsertAspect, countableChoicesList} = aspects;
14 |
15 | countableChoicesListInsertAspect.countableChoicesListInsert = (wrap, key) => {
16 | if ( !wrap.isOptionHidden ){
17 | let choiceNext = wrapsCollection.getNext(key, c=>!c.isOptionHidden );
18 | countableChoicesList.add(wrap, choiceNext)
19 | }
20 | }
21 |
22 | let origBuildAndAttachChoice = buildAndAttachChoiceAspect.buildAndAttachChoice;
23 | buildAndAttachChoiceAspect.buildAndAttachChoice=(wrap, getNextElement)=>{
24 | if (wrap.isOptionHidden){
25 | buildHiddenChoice(wrap);
26 | }
27 | else{
28 | origBuildAndAttachChoice(wrap, getNextElement);
29 | }
30 | }
31 |
32 | var origIsSelectable = isChoiceSelectableAspect.isSelectable;
33 | isChoiceSelectableAspect.isSelectable = (wrap) => origIsSelectable(wrap) && !wrap.isOptionHidden;
34 |
35 | let {getIsOptionHidden, options} = configuration;
36 | if (options) {
37 | if (!getIsOptionHidden)
38 | getIsOptionHidden = (option) => (option.hidden===undefined)?false:option.hidden;
39 | } else {
40 | if (!getIsOptionHidden)
41 | getIsOptionHidden = (option) => {
42 | return option.hidden;
43 | }
44 | }
45 |
46 | var origCreateWrap = createWrapAspect.createWrap;
47 | createWrapAspect.createWrap = (option) => {
48 | let wrap = origCreateWrap(option);
49 | wrap.isOptionHidden = getIsOptionHidden(option);
50 | return wrap;
51 | };
52 |
53 | return {
54 | buildApi(api){
55 | let getNextNonHidden = (key) => wrapsCollection.getNext(key, c => !c.isOptionHidden );
56 |
57 | api.updateOptionsHidden = () =>
58 | wrapsCollection.forLoop( (wrap, key) =>
59 | updateChoiceHidden(wrap, key, getNextNonHidden, countableChoicesList, getIsOptionHidden, produceChoiceAspect)
60 | );
61 |
62 | api.updateOptionHidden = (key) =>
63 | updateChoiceHidden(wrapsCollection.get(key), key, getNextNonHidden, countableChoicesList, getIsOptionHidden, produceChoiceAspect);
64 | // TODO create updateHidden ?
65 | // it is too complex since we need to find the next non hidden, when this depends on key
66 | // there should be the backreference "wrap -> index" invited before
67 | // api.updateOptionHidden = (key) => wrapsCollection.get(key).updateHidden();
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | function buildHiddenChoice(wrap){
76 | wrap.updateSelected = () => void 0;
77 |
78 | wrap.choice.choicesDom = {};
79 | wrap.choice.choiceDomManagerHandlers ={}
80 | wrap.choice.choiceDomManagerHandlers.setVisible = null;
81 | wrap.choice.setHoverIn = null;
82 |
83 | wrap.choice.dispose = () => {
84 | wrap.choice.dispose = null;
85 | };
86 |
87 | wrap.dispose = () => {
88 | wrap.choice.dispose();
89 | wrap.dispose = null;
90 | };
91 | }
92 |
93 | function updateChoiceHidden(wrap, key, getNextNonHidden, countableChoicesList, getIsOptionHidden, produceChoiceAspect){
94 | let newIsOptionHidden = getIsOptionHidden(wrap.option);
95 | if (newIsOptionHidden != wrap.isOptionHidden)
96 | {
97 | wrap.isOptionHidden= newIsOptionHidden;
98 | if (wrap.isOptionHidden) {
99 |
100 | countableChoicesList.remove(wrap);
101 | wrap.choice.choiceDomManagerHandlers.detach();
102 | buildHiddenChoice(wrap);
103 | } else {
104 | let nextChoice = getNextNonHidden(key);
105 | countableChoicesList.add(wrap, nextChoice);
106 | produceChoiceAspect.produceChoice(wrap);
107 | wrap.choice.choiceDomManagerHandlers.attach(nextChoice?.choice.choiceDom.choiceElement);
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/src/ToolsStyling.js:
--------------------------------------------------------------------------------
1 | import {shallowClearClone, isString} from './ToolsJs';
2 |
3 | export function addStyling(element, styling){
4 | var backupStyling = {classes:[], styles:{}}
5 | if (styling) {
6 | var {classes, styles} = styling;
7 | classes.forEach(e => element.classList.add(e)) // todo use add(classes)
8 | backupStyling.classes = classes.slice();
9 | for (let property in styles){
10 | backupStyling.styles[property] = element.style[property];
11 | element.style[property] = styles[property]; // todo use Object.assign (need polyfill for IE11)
12 | }
13 | }
14 | return backupStyling;
15 | }
16 |
17 | function removeStyling(element, styling){
18 | if (styling) {
19 | var {classes, styles} = styling;
20 | classes.forEach(e=>element.classList.remove(e)) // todo use remove(classes)
21 | for (let property in styles)
22 | element.style[property] = styles[property]; // todo use Object.assign (need polyfill for IE11)
23 | }
24 | }
25 |
26 | export function toggleStyling(element, styling){
27 | var backupStyling = {classes:[], styles:{}};
28 | var isOn=false;
29 | var isF = styling instanceof Function;
30 | return (value, force)=>{
31 | if (value) {
32 | if (isOn===false){
33 | backupStyling = addStyling(element, isF?styling():styling)
34 | isOn=true;
35 | } else if (force){
36 | removeStyling(element, backupStyling);
37 | backupStyling =addStyling(element, isF?styling():styling)
38 | }
39 | } else {
40 | if (isOn===true || force===true){
41 | removeStyling(element, backupStyling);
42 | isOn=false;
43 | }
44 | }
45 | }
46 | }
47 |
48 | function extendClasses(out, param, actionStr, actionArr, isRemoveEmptyClasses){
49 | if (isString(param)){
50 | if (param === ""){
51 | if (isRemoveEmptyClasses){
52 | out.classes = [];
53 | }
54 | } else {
55 | let c = param.split(' ');
56 | out.classes = actionStr(c);
57 | }
58 | return true;
59 | } else if (param instanceof Array){
60 | if (param.length==0){
61 | if (isRemoveEmptyClasses){
62 | out.classes = [];
63 | }
64 | }
65 | else{
66 | out.classes = actionArr(param);
67 | }
68 | return true;
69 | }
70 | return false;
71 | }
72 |
73 | function extend(value, param, actionStr, actionArr, actionObj, isRemoveEmptyClasses){
74 | var success = extendClasses(value, param, actionStr, actionArr, isRemoveEmptyClasses);
75 | if (success === false){
76 | if (param instanceof Object){
77 | var {classes, styles} = param;
78 | extendClasses(value, classes, actionStr, actionArr, isRemoveEmptyClasses);
79 |
80 | if (styles) {
81 | value.styles = actionObj(styles);
82 | } else if (!classes) {
83 | value.styles = actionObj(param)
84 | }
85 | }
86 | }
87 | }
88 |
89 | export function Styling(param){
90 | var value = {classes:[], styles:{}};
91 | if (param){
92 | extend(value, param, a=>a, a=>a.slice(), o=>shallowClearClone(o), true);
93 | }
94 | return Object.freeze(value);
95 | }
96 |
97 | function createStyling(isReplace, param, ...params){
98 | var value = {classes:[], styles:{}};
99 | if (param){
100 | extend(value, param, a=>a, a=>a.slice(), o=>shallowClearClone(o),true);
101 | if (params){
102 | var {classes, styles} = value;
103 | var extendInt = isReplace? (p)=>extend(value, p, s=>s, a=>a.slice(), o=> shallowClearClone(styles, o),true):
104 | (p)=>extend(value, p, a=>classes.concat(a), a=>classes.concat(a), o=>shallowClearClone(styles, o),false)
105 | params.forEach(p=> extendInt(p));
106 | }
107 | }
108 | return Styling(value);
109 | }
110 |
111 | export function createCss(stylings1, stylings2){
112 | var destination = {};
113 | for (let property in stylings1) {
114 | let param1 = stylings1[property];
115 |
116 | let param2 = stylings2?stylings2[property]:undefined;
117 | if (param2===undefined)
118 | destination[property] = Styling(param1)
119 | else{
120 | destination[property] = createStyling(true, param1, param2);
121 | }
122 | }
123 | if (stylings2)
124 | for (let property in stylings2) {
125 | if (!stylings1[property])
126 | destination[property] = Styling(stylings2[property])
127 | }
128 | return destination;
129 | }
130 |
131 | export function extendCss(stylings1, stylings2){
132 | for (let property in stylings2) {
133 | let param2 = stylings2[property];
134 | let param1 = stylings1[property];
135 | if (param1 === undefined)
136 | stylings1[property] = Styling(param2)
137 | else{
138 | stylings1[property] = createStyling(false, param1, param2);
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/npm-debug.log:
--------------------------------------------------------------------------------
1 | 0 info it worked if it ends with ok
2 | 1 verbose cli [ 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Web\\External\\Node.exe',
3 | 1 verbose cli 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Web\\External\\node_modules\\npm\\bin\\npm-cli.js',
4 | 1 verbose cli 'run',
5 | 1 verbose cli 'report' ]
6 | 2 info using npm@3.3.4
7 | 3 info using node@v5.4.1
8 | 4 verbose run-script [ 'prereport', 'report', 'postreport' ]
9 | 5 info lifecycle dashboardcode.bsmutltiselect@0.1.15~prereport: dashboardcode.bsmutltiselect@0.1.15
10 | 6 silly lifecycle dashboardcode.bsmutltiselect@0.1.15~prereport: no script for prereport, continuing
11 | 7 info lifecycle dashboardcode.bsmutltiselect@0.1.15~report: dashboardcode.bsmutltiselect@0.1.15
12 | 8 verbose lifecycle dashboardcode.bsmutltiselect@0.1.15~report: unsafe-perm in lifecycle true
13 | 9 verbose lifecycle dashboardcode.bsmutltiselect@0.1.15~report: PATH: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Web\External\node_modules\npm\bin\node-gyp-bin;C:\cot\DashboardCode\BsMultiSelect\node_modules\.bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\dotnet\;C:\Program Files (x86)\Skype\Phone\;C:\Program Files\Git\cmd;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Web\External;C:\Program Files\nodejs\;C:\Users\pokrorom\.dnx\bin;C:\Program Files\Microsoft VS Code\bin;C:\Users\pokrorom\AppData\Roaming\npm
14 | 10 verbose lifecycle dashboardcode.bsmutltiselect@0.1.15~report: CWD: C:\cot\DashboardCode\BsMultiSelect
15 | 11 silly lifecycle dashboardcode.bsmutltiselect@0.1.15~report: Args: [ '/d /s /c',
16 | 11 silly lifecycle 'echo.cd & cd & echo. & where node & echo.node -v & node -v & echo. & where npm & echo.npm -version & npm -version & echo. & where eslint & echo.eslint -v & eslint -v & echo. & where rollup & echo.rollup -version & rollup -version & echo. & echo.babel -version & node ./node_modules/@babel/cli/bin/babel --version' ]
17 | 12 silly lifecycle dashboardcode.bsmutltiselect@0.1.15~report: Returned: code: 1 signal: null
18 | 13 info lifecycle dashboardcode.bsmutltiselect@0.1.15~report: Failed to exec report script
19 | 14 verbose stack Error: dashboardcode.bsmutltiselect@0.1.15 report: `echo.cd & cd & echo. & where node & echo.node -v & node -v & echo. & where npm & echo.npm -version & npm -version & echo. & where eslint & echo.eslint -v & eslint -v & echo. & where rollup & echo.rollup -version & rollup -version & echo. & echo.babel -version & node ./node_modules/@babel/cli/bin/babel --version`
20 | 14 verbose stack Exit status 1
21 | 14 verbose stack at EventEmitter. (C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Web\External\node_modules\npm\lib\utils\lifecycle.js:233:16)
22 | 14 verbose stack at emitTwo (events.js:87:13)
23 | 14 verbose stack at EventEmitter.emit (events.js:172:7)
24 | 14 verbose stack at ChildProcess. (C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Web\External\node_modules\npm\lib\utils\spawn.js:24:14)
25 | 14 verbose stack at emitTwo (events.js:87:13)
26 | 14 verbose stack at ChildProcess.emit (events.js:172:7)
27 | 14 verbose stack at maybeClose (internal/child_process.js:821:16)
28 | 14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
29 | 15 verbose pkgid dashboardcode.bsmutltiselect@0.1.15
30 | 16 verbose cwd C:\cot\DashboardCode\BsMultiSelect
31 | 17 error Windows_NT 6.1.7601
32 | 18 error argv "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Web\\External\\Node.exe" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Web\\External\\node_modules\\npm\\bin\\npm-cli.js" "run" "report"
33 | 19 error node v5.4.1
34 | 20 error npm v3.3.4
35 | 21 error code ELIFECYCLE
36 | 22 error dashboardcode.bsmutltiselect@0.1.15 report: `echo.cd & cd & echo. & where node & echo.node -v & node -v & echo. & where npm & echo.npm -version & npm -version & echo. & where eslint & echo.eslint -v & eslint -v & echo. & where rollup & echo.rollup -version & rollup -version & echo. & echo.babel -version & node ./node_modules/@babel/cli/bin/babel --version`
37 | 22 error Exit status 1
38 | 23 error Failed at the dashboardcode.bsmutltiselect@0.1.15 report script 'echo.cd & cd & echo. & where node & echo.node -v & node -v & echo. & where npm & echo.npm -version & npm -version & echo. & where eslint & echo.eslint -v & eslint -v & echo. & where rollup & echo.rollup -version & rollup -version & echo. & echo.babel -version & node ./node_modules/@babel/cli/bin/babel --version'.
39 | 23 error This is most likely a problem with the dashboardcode.bsmutltiselect package,
40 | 23 error not with npm itself.
41 | 23 error Tell the author that this fails on your system:
42 | 23 error echo.cd & cd & echo. & where node & echo.node -v & node -v & echo. & where npm & echo.npm -version & npm -version & echo. & where eslint & echo.eslint -v & eslint -v & echo. & where rollup & echo.rollup -version & rollup -version & echo. & echo.babel -version & node ./node_modules/@babel/cli/bin/babel --version
43 | 23 error You can get their info via:
44 | 23 error npm owner ls dashboardcode.bsmutltiselect
45 | 23 error There is likely additional logging output above.
46 | 24 verbose exit [ 1, true ]
47 |
--------------------------------------------------------------------------------
/src/PluginManager.js:
--------------------------------------------------------------------------------
1 | import {extendIfUndefined} from './ToolsJs';
2 |
3 | function parseEventHandler(key, eventHandler, doms, plugStaticDoms, preLayouts, layouts, appends, buildApis, disposes){
4 | if (eventHandler) {
5 | if (eventHandler.dom)
6 | doms.push({key, value:eventHandler.dom});
7 | if (eventHandler.plugStaticDom)
8 | plugStaticDoms.push({key, value:eventHandler.plugStaticDom});
9 | if (eventHandler.preLayout)
10 | preLayouts.push({key, value:eventHandler.preLayout});
11 | if (eventHandler.layout)
12 | layouts.push({key, value:eventHandler.layout});
13 | if (eventHandler.append)
14 | appends.push({key, value:eventHandler.append});
15 | if (eventHandler.buildApi)
16 | buildApis.push({key, value:eventHandler.buildApi});
17 | if (eventHandler.dispose)
18 | disposes.push({key, value:eventHandler.dispose});
19 | }
20 | }
21 |
22 | export function ComposePluginManagerFactory(plugins, defaults, environment){
23 | let plugedList = [];
24 | let mergeList = [];
25 | for(let i = 0; i {
36 | let buildAspectsList = [];
37 | for(let i = 0; i {
58 | extendIfUndefined(aspects, newAspects)
59 |
60 | var doms = [];
61 | var plugStaticDoms = [];
62 | var preLayouts = [];
63 | var layouts = [];
64 | var appends = [];
65 | var buildApis = [];
66 | let disposes = [];
67 | for(let k = 0; k {
12 | aspects.popperRtlAspect = popperRtlAspect;
13 |
14 | let {environment} = aspects;
15 |
16 | let {createPopper, Popper, globalPopper} = environment;
17 | let createModifiersVX = null;
18 | let createPopperVX = null;
19 | if (Popper) { // V2
20 | createPopperVX = createPopper = (function(createPopperConstructor) {
21 | return function(anchorElement, element, popperConfiguration) {
22 | return new createPopperConstructor(anchorElement, element, popperConfiguration);
23 | }
24 | })(Popper);;
25 | createModifiersVX = CreateModifiersV1;
26 | } else if (createPopper) {
27 | createPopperVX = createPopper;
28 | createModifiersVX = CreateModifiersV2;
29 | } else if (globalPopper) {
30 | if (globalPopper.createPopper) {
31 | createPopperVX = globalPopper.createPopper;
32 | createModifiersVX = CreateModifiersV2;
33 | } else {
34 | createPopperVX = createPopper = (function(createPopperConstructor) {
35 | return function(anchorElement, element, popperConfiguration) {
36 | return new createPopperConstructor(anchorElement, element, popperConfiguration);
37 | }
38 | })(globalPopper);
39 | createModifiersVX = CreateModifiersV1;
40 | }
41 | } else {
42 | throw new Error("BsMultiSelect: Popper component (https://popper.js.org) is required");
43 | }
44 | var createPopperConfigurationAspect = CreatePopperConfigurationAspect(createModifiersVX);
45 | var createPopperAspect = CreatePopperAspect(createPopperVX, popperRtlAspect, createPopperConfigurationAspect);
46 | aspects.createPopperAspect = createPopperAspect;
47 |
48 | return {
49 | append(){
50 | let {filterDom, choicesDom, disposeAspect, staticManager, choicesVisibilityAspect, specialPicksEventsAspect} = aspects;
51 |
52 | let filterInputElement = filterDom.filterInputElement;
53 | let choicesElement = choicesDom.choicesElement;
54 |
55 | let pop = createPopperAspect.createPopper(choicesElement, filterInputElement, true);
56 |
57 | staticManager.appendToContainer = composeSync(staticManager.appendToContainer, pop.init);
58 |
59 | var origBackSpace = specialPicksEventsAspect.backSpace;
60 | specialPicksEventsAspect.backSpace = (pick) => {origBackSpace(pick); pop.update();};
61 |
62 | disposeAspect.dispose = composeSync(disposeAspect.dispose, pop.dispose);
63 |
64 | choicesVisibilityAspect.updatePopupLocation = composeSync(
65 | choicesVisibilityAspect.updatePopupLocation,
66 | function(){pop.update();}
67 | );
68 | }
69 | }
70 | }
71 | }
72 |
73 | function PopperRtlAspect(){
74 | return {
75 | getIsRtl(){
76 | return false;
77 | }
78 | }
79 | }
80 |
81 | function CreateModifiersV1(preventOverflow){
82 | return {
83 | preventOverflow: {enabled:preventOverflow},
84 | hide: {enabled:false},
85 | flip: {enabled:false}
86 | };
87 | }
88 |
89 | function CreateModifiersV2(preventOverflow){
90 | var modifiers = [{
91 | name: 'flip',
92 | options: {
93 | fallbackPlacements: ['bottom'],
94 | },
95 | }
96 | ];
97 | if (preventOverflow) {
98 | modifiers.push({name: 'preventOverflow'});
99 | }
100 | return modifiers;
101 | }
102 |
103 | function CreatePopperAspect(createPopperVX, popperRtlAspect, createPopperConfigurationAspect){
104 | return {
105 | createPopper(element, anchorElement, preventOverflow){
106 | let popper = null;
107 | return {
108 | init(){
109 | var isRtl = popperRtlAspect.getIsRtl();
110 | var popperConfiguration = createPopperConfigurationAspect.createConfiguration(preventOverflow, isRtl);
111 | popper = createPopperVX(anchorElement, element, popperConfiguration);
112 | },
113 | update(){
114 | popper.update(); // become async in popper 2; use forceUpdate if sync is needed?
115 | },
116 | dispose(){
117 | popper.destroy();
118 | }
119 | }
120 | }
121 | }
122 | }
123 |
124 | function CreatePopperConfigurationAspect(createModifiersVX){
125 | return {
126 | createConfiguration(preventOverflow, isRtl){
127 | let modifiers = createModifiersVX(preventOverflow);
128 |
129 | let popperConfiguration = {
130 | placement: 'bottom-start',
131 | modifiers: modifiers
132 | };
133 |
134 | if (isRtl){
135 | popperConfiguration.placement = 'bottom-end';
136 | }
137 | return popperConfiguration;
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/src/ToolsDom.js:
--------------------------------------------------------------------------------
1 | export function findDirectChildByTagName(element, tagName){
2 | let value = null;
3 | for (let i = 0; i{
19 | var event = new window.Event(eventName);
20 | e.dispatchEvent(event);
21 | }
22 | }
23 | else
24 | trigger = (e, eventName) => { // IE 11 polyfill
25 | let event = window.document.createEvent("CustomEvent");
26 | event.initCustomEvent(eventName, false, false, undefined);
27 | e.dispatchEvent(event);
28 | }
29 | return trigger;
30 | }
31 |
32 | export function closestByTagName(element, tagName){
33 | return closest(element, e => e.tagName === tagName) // TODO support xhtml? e.tagName.toUpperCase() ?
34 | }
35 |
36 | export function closestByClassName(element, className){
37 | return closest(element, e => e.classList.contains(className))
38 | }
39 |
40 | export function closestByAttribute(element, attributeName, attribute){
41 | return closest(element, e => e.getAttribute(attributeName)===attribute )
42 | }
43 |
44 | export function containsAndSelf(node, otherNode ){
45 | return node === otherNode || node.contains(otherNode);
46 | }
47 |
48 | export function getDataGuardedWithPrefix(element, prefix, name){
49 | var tmp1 = element.getAttribute('data-' + prefix + '-' + name);
50 | if (tmp1) {
51 | return tmp1;
52 | } else {
53 | var tmp2 = element.getAttribute('data-' + name);
54 | if (tmp2)
55 | return tmp2;
56 | }
57 | return null;
58 | }
59 |
60 | function closest(element, predicate){
61 | if (!element || !(element instanceof Element)) return null; // should be element, not document (TODO: check iframe)
62 |
63 | if (predicate(element)) return element;
64 | return closest(element.parentNode, predicate);
65 | }
66 |
67 | export function siblingsAsArray(element){
68 | var value = []
69 | if (element.parentNode){
70 | var children = element.parentNode.children;
71 | var l = element.parentNode.children.length;
72 | if (children.length>1){
73 | for (var i=0; i < l; ++i){
74 | var e = children[i];
75 | if (e!=element)
76 | value.push(e);
77 |
78 | }
79 | }
80 | }
81 | return value;
82 | }
83 |
84 | export function getIsRtl(element){
85 | var isRtl = false;
86 | var e = closestByAttribute(element,"dir","rtl");
87 | if (e)
88 | isRtl = true;
89 | return isRtl;
90 | }
91 |
92 | export function EventBinder(){
93 | var list = [];
94 | return {
95 | bind(element, eventName, handler) {
96 | element.addEventListener(eventName, handler)
97 | list.push( {element, eventName, handler} )
98 | },
99 | unbind() {
100 | list.forEach( e=> {
101 | let {element, eventName, handler}=e;
102 | element.removeEventListener(eventName, handler)
103 | })
104 | }
105 | }
106 | }
107 |
108 | export function EventTumbler(element, eventName, handler){
109 | return {
110 | on() {
111 | element.addEventListener(eventName, handler)
112 | },
113 | off() {
114 | element.removeEventListener(eventName, handler)
115 | }
116 | }
117 | }
118 |
119 | export function AttributeBackup(){
120 | var list = [];
121 | return {
122 | set(element, attributeName, attribute){
123 | var currentAtribute = element.getAttribute(attributeName);
124 | list.push( {element, currentAtribute, attribute} )
125 | element.setAttribute(attributeName, attribute)
126 | },
127 | restore(){
128 | list.forEach(e=>{
129 | let {element, attributeName, attribute} = e;
130 | if (attributeName)
131 | element.setAttribute(attributeName, attribute)
132 | else
133 | element.removeAttribute(attributeName)
134 | })
135 | }
136 | }
137 | }
138 |
139 | export function EventLoopFlag(window) {
140 | var flag = false;
141 | return {
142 | get(){
143 | return flag;
144 | },
145 | set(){
146 | flag = true;
147 | pr = window.setTimeout(
148 | () => {
149 | flag = false;
150 | })
151 |
152 | }
153 | }
154 | }
155 |
156 | export function EventLoopProlongableFlag(window) {
157 | var flag = false;
158 | var pr = null;
159 | return {
160 | get(){
161 | return flag;
162 | },
163 | set(timeout){
164 | if (flag && pr){
165 | window.clearTimeout(pr);
166 | }
167 | flag = true;
168 | pr = window.setTimeout(
169 | () => {
170 | flag = false;
171 | pr=null;
172 | }, timeout?timeout:0)
173 |
174 | }
175 | }
176 | }
177 |
178 | export function ResetableFlag() {
179 | var flag = false;
180 | return {
181 | get(){
182 | return flag;
183 | },
184 | set(){
185 | flag = true;
186 | },
187 | reset(){
188 | flag = false;
189 | }
190 | }
191 | }
--------------------------------------------------------------------------------
/src/plugins/PlaceholderPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 | import {getDataGuardedWithPrefix} from '../ToolsDom';
3 | import {toggleStyling} from '../ToolsStyling';
4 | import {ResetableFlag} from '../ToolsDom'
5 |
6 | export function PlaceholderPlugin(){
7 | return {
8 | plug
9 | }
10 | }
11 |
12 | export function plug(configuration){
13 | return (aspects) => {
14 | return {
15 | layout: () => {
16 | let {staticManager, picksList, picksDom, filterDom, updateDataAspect,
17 | resetFilterListAspect, filterManagerAspect, environment, staticDom} = aspects;
18 | let isIE11 = environment.isIE11;
19 | let {placeholder, css} = configuration;
20 | let {picksElement} = picksDom;
21 | let filterInputElement = filterDom.filterInputElement;
22 |
23 | function setPlaceholder(placeholder){
24 | filterInputElement.placeholder = placeholder;
25 | }
26 | if (isIE11){
27 | var ignoreNextInputResetableFlag = ResetableFlag();
28 | let placeholderStopInputAspect = PlaceholderStopInputAspect(ignoreNextInputResetableFlag);
29 | var setPlaceholderOrig = setPlaceholder;
30 | setPlaceholder = function(placeholder){
31 | ignoreNextInputResetableFlag.set();
32 | setPlaceholderOrig(placeholder);
33 | }
34 | var origOnInput = filterDom.onInput;
35 | filterDom.onInput = (handler) => {
36 | origOnInput(
37 | ()=>{if (placeholderStopInputAspect.get()){
38 | placeholderStopInputAspect.reset();
39 | } else {
40 | handler();
41 | }
42 | });
43 | }
44 | }
45 |
46 | if (!placeholder){
47 | placeholder = getDataGuardedWithPrefix(staticDom.initialElement,"bsmultiselect","placeholder");
48 | }
49 |
50 | function setEmptyInputWidth(isVisible){
51 | if(isVisible)
52 | filterInputElement.style.width ='100%';
53 | else
54 | filterInputElement.style.width ='2ch';
55 | }
56 | var emptyToggleStyling = toggleStyling(filterInputElement, css.filterInput_empty);
57 | function showPlacehodler(isVisible){
58 | if (isVisible)
59 | {
60 | setPlaceholder(placeholder?placeholder:'');
61 | picksElement.style.display = 'block';
62 | }
63 | else
64 | {
65 | setPlaceholder('');
66 | picksElement.style.display = 'flex';
67 | }
68 | emptyToggleStyling(isVisible);
69 | setEmptyInputWidth(isVisible);
70 | }
71 | showPlacehodler(true);
72 |
73 | function setDisabled(isComponentDisabled){
74 | filterInputElement.disabled = isComponentDisabled;
75 | };
76 | let isEmpty = () => picksList.isEmpty() && filterDom.isEmpty()
77 |
78 | function updatePlacehodlerVisibility(){
79 | showPlacehodler(isEmpty());
80 | };
81 | function updateEmptyInputWidth(){
82 | setEmptyInputWidth(isEmpty())
83 | };
84 |
85 | let origDisable = picksDom.disable;
86 | picksDom.disable = (isComponentDisabled)=>{
87 | setDisabled(isComponentDisabled);
88 | origDisable(isComponentDisabled);
89 | };
90 |
91 | staticManager.appendToContainer = composeSync(staticManager.appendToContainer, updateEmptyInputWidth);
92 |
93 | filterManagerAspect.processEmptyInput = composeSync(updateEmptyInputWidth, filterManagerAspect.processEmptyInput);
94 | resetFilterListAspect.forceResetFilter = composeSync(resetFilterListAspect.forceResetFilter, updatePlacehodlerVisibility);
95 |
96 | let origAdd = picksList.add;
97 | picksList.add = (pick) => {
98 | let returnValue = origAdd(pick);
99 | if (picksList.getCount()==1 ){ // make flex
100 | if (filterDom.isEmpty()){
101 | setPlaceholder('');
102 | picksElement.style.display = 'flex';
103 | emptyToggleStyling(false);
104 | filterInputElement.style.width ='2ch';
105 | } else {
106 | picksElement.style.display = 'flex';
107 | }
108 | }
109 | pick.dispose = composeSync(pick.dispose, function()
110 | {
111 | if (isEmpty()) {
112 | showPlacehodler(true);
113 | }
114 | });
115 | return returnValue;
116 | };
117 |
118 | updateDataAspect.updateData = composeSync(updateDataAspect.updateData, updatePlacehodlerVisibility);
119 | }
120 | }
121 | }
122 | }
123 |
124 | // ie11 support
125 | function PlaceholderStopInputAspect(resetableFlag){
126 | return{
127 | get(){
128 | return resetableFlag.get();
129 | },
130 | reset(){
131 | return resetableFlag.reset();
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/src/plugins/DisabledOptionPlugin.js:
--------------------------------------------------------------------------------
1 | import {composeSync} from '../ToolsJs';
2 | import {toggleStyling} from '../ToolsStyling';
3 |
4 | export function DisabledOptionCssPatchPlugin(defaults){
5 | defaults.cssPatch.pickContent_disabled = {opacity: '.65'};
6 | }
7 |
8 | export function DisabledOptionPlugin(defaults){
9 | defaults.css.pickContent_disabled = 'disabled';
10 | return {
11 | plug
12 | }
13 | }
14 |
15 | export function plug(configuration){
16 | return (aspects) => {
17 | return {
18 | layout: () => {
19 | let {isChoiceSelectableAspect, createWrapAspect, produceChoiceAspect,
20 | filterPredicateAspect, wrapsCollection, producePickAspect, pickDomFactory } = aspects;
21 |
22 | let {getIsOptionDisabled, options, css} = configuration;
23 | if (options) {
24 | if (!getIsOptionDisabled)
25 | getIsOptionDisabled = (option) => (option.disabled===undefined) ? false : option.disabled;
26 | } else { // selectElement
27 | if (!getIsOptionDisabled)
28 | getIsOptionDisabled = (option) => option.disabled;
29 | }
30 |
31 | // TODO check this instead of wrap.updateDisabled
32 | // function updateDisabled(wrap){
33 | // wrap?.choice?.choiceDomManagerHandlers?.updateDisabled?.();
34 | // wrap?.pick?.pickDomManagerHandlers?.updateDisabled?.();
35 | // }
36 |
37 | let origCreateWrap = createWrapAspect.createWrap;
38 | createWrapAspect.createWrap = (option) => {
39 | let wrap = origCreateWrap(option);
40 | wrap.isOptionDisabled = getIsOptionDisabled(option); // TODO: remove usage wrap.isOptionDisabled
41 | wrap.updateDisabled = null;
42 | return wrap;
43 | };
44 |
45 |
46 |
47 | let origIsSelectable = isChoiceSelectableAspect.isSelectable;
48 | isChoiceSelectableAspect.isSelectable = (wrap) => {
49 | return origIsSelectable(wrap) && !wrap.isOptionDisabled;
50 | };
51 |
52 | let origFilterPredicate = filterPredicateAspect.filterPredicate;
53 | filterPredicateAspect.filterPredicate = (wrap, text) => {
54 | return !wrap.isOptionDisabled && origFilterPredicate(wrap, text) ;
55 | };
56 |
57 | ExtendProduceChoiceAspectProduceChoice(produceChoiceAspect);
58 |
59 | ExtendProducePickAspectProducePick(producePickAspect);
60 |
61 | ExtendPickDomFactoryCreate(pickDomFactory, css);
62 |
63 | return {
64 | buildApi(api){
65 | api.updateOptionsDisabled = () => wrapsCollection.forLoop( wrap => updateChoiceDisabled(wrap, getIsOptionDisabled))
66 | api.updateOptionDisabled = (key) => updateChoiceDisabled(wrapsCollection.get(key), getIsOptionDisabled)
67 | }
68 | };
69 | }
70 | }
71 | }
72 | }
73 |
74 | function ExtendProduceChoiceAspectProduceChoice(produceChoiceAspect){
75 | let orig = produceChoiceAspect.produceChoice;
76 | produceChoiceAspect.produceChoice = (wrap) => {
77 | let val = orig(wrap);
78 | wrap.choice.choiceDomManagerHandlers.updateDisabled();
79 | wrap.updateDisabled = wrap.choice.choiceDomManagerHandlers.updateDisabled
80 | wrap.choice.dispose = composeSync(()=>{wrap.updateDisabled=null;}, wrap.choice.dispose);
81 |
82 | let origToggle = wrap.choice.tryToggleChoice;
83 | wrap.choice.tryToggleChoice = () => {
84 | let success = false;
85 | if (wrap.isOptionSelected!==undefined){
86 | if (wrap.isOptionSelected || !wrap.isOptionDisabled) // TODO: declare dependency on SelectedOptionPlugin
87 | success = origToggle(wrap);
88 | }
89 | else{
90 | if (!wrap.isOptionDisabled) {
91 | success = origToggle(wrap);
92 | }
93 | }
94 | return success;
95 | };
96 |
97 | return val;
98 | }
99 | }
100 |
101 | function ExtendProducePickAspectProducePick(producePickAspect){
102 | let orig = producePickAspect.producePick;
103 | producePickAspect.producePick = (wrap) => {
104 | let val = orig(wrap);
105 | let pick = wrap.pick;
106 | let choiceUpdateDisabledBackup = wrap.updateDisabled; // backup disable only choice
107 | wrap.updateDisabled = composeSync(choiceUpdateDisabledBackup, () => pick.pickDomManagerHandlers.updateDisabled()); // add pickDisabled
108 | pick.dispose = composeSync(pick.dispose,
109 | ()=>{
110 | wrap.updateDisabled = choiceUpdateDisabledBackup; // remove pickDisabled
111 | wrap.updateDisabled(); // make "true disabled" without it checkbox only looks disabled
112 | }
113 | )
114 | return val;
115 | }
116 | }
117 |
118 | function ExtendPickDomFactoryCreate(pickDomFactory, css){
119 | let orig = pickDomFactory.create;
120 | pickDomFactory.create = (pick) => {
121 | orig(pick);
122 | let {pickDom, pickDomManagerHandlers} = pick;
123 | let disableToggle = toggleStyling(pickDom.pickContentElement, css.pickContent_disabled);
124 | pickDomManagerHandlers.updateDisabled = ()=>{
125 | disableToggle(pick.wrap.isOptionDisabled);
126 | }
127 | pickDomManagerHandlers.updateDisabled();
128 | }
129 | }
130 |
131 | function updateChoiceDisabled(wrap, getIsOptionDisabled){
132 | let newIsDisabled = getIsOptionDisabled(wrap.option);
133 | if (newIsDisabled != wrap.isOptionDisabled)
134 | {
135 | wrap.isOptionDisabled= newIsDisabled;
136 | wrap.updateDisabled?.(); // some hidden oesn't have element (and need to be updated)
137 | }
138 | }
--------------------------------------------------------------------------------
/src/plugins/ValidationApiPlugin.js:
--------------------------------------------------------------------------------
1 | import {ObservableValue, ObservableLambda, defCall, isBoolean, composeSync} from '../ToolsJs';
2 | import {getDataGuardedWithPrefix} from '../ToolsDom';
3 |
4 | const defValueMissingMessage = 'Please select an item in the list'
5 |
6 | export function ValidationApiPlugin(defaults){
7 | preset(defaults);
8 | return {
9 | plug
10 | }
11 | }
12 |
13 | export function preset(defaults){defaults.getValueRequired=() => false; defaults.valueMissingMessage='';}
14 |
15 | export function plug(configuration){
16 | let {required, getValueRequired, getIsValueMissing, valueMissingMessage} = configuration;
17 | var getValueRequiredAspect = GetValueRequiredAspect(required, getValueRequired);
18 | return (aspects) => {
19 | aspects.getValueRequiredAspect = getValueRequiredAspect;
20 | return {
21 | plugStaticDom: ()=>{
22 | var {dataWrap, staticDom} = aspects;
23 |
24 | var valueMissingMessageEx = defCall(valueMissingMessage,
25 | ()=> getDataGuardedWithPrefix(staticDom.initialElement,"bsmultiselect","value-missing-message"),
26 | defValueMissingMessage)
27 |
28 | if (!getIsValueMissing) {
29 | getIsValueMissing = () => {
30 | let count = 0;
31 | let optionsArray = dataWrap.getOptions();
32 | for (var i=0; i < optionsArray.length; i++) {
33 | if (optionsArray[i].selected)
34 | count++;
35 | }
36 | return count===0;
37 | }
38 | }
39 |
40 | return {
41 | preLayout() {
42 | // getValueRequiredAspect redefined on appendToContainer, so this can't be called on prelayout and layout
43 | var isValueMissingObservable = ObservableLambda(()=>getValueRequiredAspect.getValueRequired() && getIsValueMissing());
44 | var validationApiObservable = ObservableValue(!isValueMissingObservable.getValue());
45 | aspects.validationApiAspect = ValidationApiAspect(validationApiObservable); // used in BsAppearancePlugin layout, possible races
46 |
47 | return {
48 | layout: () => {
49 | var {onChangeAspect, updateDataAspect} = aspects;
50 | // TODO: required could be a function
51 | //let {valueMissingMessage} = configuration;
52 |
53 | onChangeAspect.onChange = composeSync(isValueMissingObservable.call, onChangeAspect.onChange);
54 | updateDataAspect.updateData = composeSync(isValueMissingObservable.call, updateDataAspect.updateData);
55 |
56 | return {
57 | buildApi(api){
58 | var {staticDom, filterDom} = aspects;
59 | api.validationApi = ValidityApi(
60 | filterDom.filterInputElement, // !!
61 | isValueMissingObservable,
62 | valueMissingMessageEx,
63 | (isValid)=>validationApiObservable.setValue(isValid),
64 | staticDom.trigger
65 | );
66 | }
67 | }
68 | },
69 | dispose(){
70 | isValueMissingObservable.detachAll();
71 | validationApiObservable.detachAll();
72 | }
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | function GetValueRequiredAspect(required, getValueRequiredCfg){
82 | return {
83 | getValueRequired(){
84 | let value = false;
85 | if (!isBoolean(required))
86 | if (getValueRequiredCfg)
87 | value = getValueRequiredCfg();
88 | return value;
89 | }
90 | }
91 | }
92 |
93 | function ValidationApiAspect(validationApiObservable){
94 | return {
95 | getValue(){
96 | return validationApiObservable.getValue();
97 | },
98 | attach(f){
99 | validationApiObservable.attach(f);
100 | }
101 | }
102 | }
103 |
104 | function ValidityApi(visibleElement, isValueMissingObservable, valueMissingMessage, onValid, trigger){
105 | var customValidationMessage = "";
106 | var validationMessage = "";
107 | var validity = null;
108 | var willValidate = true;
109 |
110 | function setMessage(valueMissing, customError){
111 | validity = Object.freeze({
112 | valueMissing,
113 | customError,
114 | valid: !(valueMissing || customError),
115 | });
116 | validationMessage = customError?customValidationMessage:(valueMissing?valueMissingMessage:"")
117 | visibleElement.setCustomValidity(validationMessage);
118 | onValid(validity.valid);
119 | }
120 |
121 | setMessage(isValueMissingObservable.getValue(), false);
122 |
123 | isValueMissingObservable.attach(
124 | (value) => {
125 | setMessage(value, validity.customError);
126 | }
127 | );
128 | var checkValidity = () => {
129 | if (!validity.valid)
130 | trigger('dashboardcode.multiselect:invalid')
131 | return validity.valid;
132 | }
133 | return {
134 | validationMessage,
135 | willValidate,
136 | validity,
137 | setCustomValidity(message){
138 | customValidationMessage = message;
139 | setMessage(validity.valueMissing, customValidationMessage?true:false);
140 | },
141 | checkValidity,
142 | reportValidity(){
143 | visibleElement.reportValidity();
144 | return checkValidity();
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.bs4.css:
--------------------------------------------------------------------------------
1 | .dashboardcode-bsmultiselect ul.form-control {
2 | display: flex;
3 | flex-wrap: wrap;
4 | height: auto;
5 | min-height: calc(1.5em + 0.75rem + 2px);
6 | margin-bottom: 0;
7 | cursor: text;
8 | list-style-type: none;
9 | }
10 | .dashboardcode-bsmultiselect ul.form-control input {
11 | height: auto;
12 | padding: 0;
13 | margin: 0;
14 | font-weight: inherit;
15 | color: inherit;
16 | background-color: transparent;
17 | border: 0;
18 | outline: none;
19 | box-shadow: none;
20 | }
21 | .dashboardcode-bsmultiselect ul.form-control.disabled {
22 | background-color: #e9ecef;
23 | }
24 | .dashboardcode-bsmultiselect ul.form-control::-moz-placeholder {
25 | color: #6c757d;
26 | opacity: 1;
27 | }
28 | .dashboardcode-bsmultiselect ul.form-control:-ms-input-placeholder {
29 | color: #6c757d;
30 | opacity: 1;
31 | }
32 | .dashboardcode-bsmultiselect ul.form-control::placeholder {
33 | color: #6c757d;
34 | opacity: 1;
35 | }
36 | .dashboardcode-bsmultiselect ul.form-control > li.badge {
37 | padding-left: 0;
38 | -webkit-padding-start: 0;
39 | padding-inline-start: 0;
40 | -webkit-padding-end: 0.5rem;
41 | padding-inline-end: 0.5rem;
42 | padding-top: 0.35em;
43 | padding-bottom: 0.35em;
44 | line-height: 1.5em;
45 | }
46 | .dashboardcode-bsmultiselect ul.form-control > li.badge button.close {
47 | float: none;
48 | font-size: 1.8em;
49 | line-height: 0.5em;
50 | font-weight: 400;
51 | }
52 | .dashboardcode-bsmultiselect ul.form-control > li.badge span.disabled {
53 | opacity: 0.65;
54 | }
55 | .dashboardcode-bsmultiselect ul.form-control.focus {
56 | color: #495057;
57 | background-color: #fff;
58 | border-color: #80bdff;
59 | outline: 0;
60 | box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
61 | }
62 | .dashboardcode-bsmultiselect ul.form-control.form-control-sm {
63 | min-height: calc(1.5em + 0.5rem + 2px);
64 | }
65 | .dashboardcode-bsmultiselect ul.form-control.form-control-sm input {
66 | font-size: 0.875rem;
67 | }
68 | .dashboardcode-bsmultiselect ul.form-control.form-control-lg {
69 | min-height: calc(1.5em + 1rem + 2px);
70 | }
71 | .dashboardcode-bsmultiselect ul.form-control.form-control-lg input {
72 | font-size: 1.25rem;
73 | }
74 | .was-validated .dashboardcode-bsmultiselect ul.form-control:valid, .dashboardcode-bsmultiselect ul.form-control.is-valid {
75 | border-color: #28a745;
76 | }
77 | .was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus, .dashboardcode-bsmultiselect ul.form-control.is-valid.focus {
78 | border-color: #28a745;
79 | box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
80 | }
81 | .was-validated .dashboardcode-bsmultiselect ul.form-control:invalid, .dashboardcode-bsmultiselect ul.form-control.is-invalid {
82 | border-color: #dc3545;
83 | }
84 | .was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus, .dashboardcode-bsmultiselect ul.form-control.is-invalid.focus {
85 | border-color: #dc3545;
86 | box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
87 | }
88 | .dashboardcode-bsmultiselect div.dropdown-menu {
89 | list-style-type: none;
90 | }
91 | .dashboardcode-bsmultiselect div.dropdown-menu > ul {
92 | padding-left: 0;
93 | padding-right: 0;
94 | margin-bottom: 0;
95 | }
96 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li {
97 | display: block;
98 | width: 100%;
99 | padding: 0 0.5rem;
100 | clear: both;
101 | font-weight: 400;
102 | color: #212529;
103 | text-align: inherit;
104 | white-space: nowrap;
105 | background-color: transparent;
106 | border: 0;
107 | cursor: pointer;
108 | }
109 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control {
110 | justify-content: flex-start;
111 | }
112 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control .custom-control-input, .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control .custom-control-label {
113 | cursor: inherit;
114 | }
115 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.disabled .custom-control-label {
116 | color: #6c757d;
117 | }
118 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover {
119 | color: var(--primary);
120 | background-color: #e9ecef;
121 | }
122 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover:not(.disabled) {
123 | color: var(--primary);
124 | }
125 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover.selected {
126 | color: var(--primary);
127 | }
128 | .dashboardcode-bsmultiselect div.dropdown-menu + div.alert-warning {
129 | padding-left: 0.25rem;
130 | padding-right: 0.25rem;
131 | z-index: 4;
132 | font-size: small;
133 | }
134 | .dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control {
135 | min-height: calc(1.5em + 0.5rem + 2px);
136 | }
137 | .dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input {
138 | font-size: 0.875rem;
139 | }
140 | .dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control {
141 | min-height: calc(1.5em + 1rem + 2px);
142 | }
143 | .dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input {
144 | font-size: 1.25rem;
145 | }
146 |
147 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:checked ~ .custom-control-label {
148 | color: #212529;
149 | }
150 |
151 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:not(:checked) ~ .custom-control-label {
152 | color: #212529;
153 | }
154 |
155 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .custom-control-input:valid:checked ~ .custom-control-label {
156 | color: var(--primary);
157 | }
158 |
159 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .custom-control-input:valid:not(:checked) ~ .custom-control-label {
160 | color: var(--primary);
161 | }
162 |
163 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:checked ~ .custom-control-label::before {
164 | color: #fff;
165 | border-color: #007bff;
166 | background-color: #007bff;
167 | }
168 |
169 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:not(:checked) ~ .custom-control-label::before {
170 | border: #adb5bd solid 1px;
171 | }
172 |
173 | /*# sourceMappingURL=BsMultiSelect.bs4.css.map */
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.css:
--------------------------------------------------------------------------------
1 | .dashboardcode-bsmultiselect ul.form-control {
2 | display: flex;
3 | flex-wrap: wrap;
4 | height: auto;
5 | min-height: calc(1.5em + 0.75rem + 2px);
6 | margin-bottom: 0;
7 | cursor: text;
8 | list-style-type: none;
9 | }
10 | .dashboardcode-bsmultiselect ul.form-control input {
11 | height: auto;
12 | padding: 0;
13 | margin: 0;
14 | font-weight: inherit;
15 | color: inherit;
16 | background-color: transparent;
17 | border: 0;
18 | outline: none;
19 | box-shadow: none;
20 | }
21 | .dashboardcode-bsmultiselect ul.form-control.disabled {
22 | background-color: #e9ecef;
23 | }
24 | .dashboardcode-bsmultiselect ul.form-control::-moz-placeholder {
25 | color: #6c757d;
26 | opacity: 1;
27 | }
28 | .dashboardcode-bsmultiselect ul.form-control:-ms-input-placeholder {
29 | color: #6c757d;
30 | opacity: 1;
31 | }
32 | .dashboardcode-bsmultiselect ul.form-control::placeholder {
33 | color: #6c757d;
34 | opacity: 1;
35 | }
36 | .dashboardcode-bsmultiselect ul.form-control > li.badge {
37 | padding-left: 0;
38 | -webkit-padding-start: 0;
39 | padding-inline-start: 0;
40 | -webkit-padding-end: 0.5rem;
41 | padding-inline-end: 0.5rem;
42 | color: var(--bs-dark);
43 | }
44 | .dashboardcode-bsmultiselect ul.form-control > li.badge button.btn-close {
45 | float: none;
46 | font-size: 0.8em;
47 | }
48 | .dashboardcode-bsmultiselect ul.form-control > li.badge span.disabled {
49 | opacity: 0.65;
50 | }
51 | .dashboardcode-bsmultiselect ul.form-control.focus {
52 | color: #212529;
53 | background-color: #fff;
54 | border-color: #86b7fe;
55 | outline: 0;
56 | box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
57 | }
58 | .dashboardcode-bsmultiselect ul.form-control.form-control-sm {
59 | min-height: calc(1.5em + 0.5rem + 2px);
60 | }
61 | .dashboardcode-bsmultiselect ul.form-control.form-control-sm input {
62 | font-size: 0.875rem;
63 | }
64 | .dashboardcode-bsmultiselect ul.form-control.form-control-lg {
65 | min-height: calc(1.5em + 1rem + 2px);
66 | }
67 | .dashboardcode-bsmultiselect ul.form-control.form-control-lg input {
68 | font-size: 1.25rem;
69 | }
70 | .was-validated .dashboardcode-bsmultiselect ul.form-control:valid, .dashboardcode-bsmultiselect ul.form-control.is-valid {
71 | border-color: #198754;
72 | }
73 | .was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus, .dashboardcode-bsmultiselect ul.form-control.is-valid.focus {
74 | border-color: #198754;
75 | box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
76 | }
77 | .was-validated .dashboardcode-bsmultiselect ul.form-control:invalid, .dashboardcode-bsmultiselect ul.form-control.is-invalid {
78 | border-color: #dc3545;
79 | }
80 | .was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus, .dashboardcode-bsmultiselect ul.form-control.is-invalid.focus {
81 | border-color: #dc3545;
82 | box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
83 | }
84 | .dashboardcode-bsmultiselect div.dropdown-menu > ul {
85 | list-style-type: none;
86 | padding-left: 0;
87 | padding-right: 0;
88 | margin-bottom: 0;
89 | }
90 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li {
91 | display: block;
92 | width: 100%;
93 | padding: 0 0.5rem;
94 | clear: both;
95 | font-weight: 400;
96 | color: #212529;
97 | text-align: inherit;
98 | white-space: nowrap;
99 | background-color: transparent;
100 | border: 0;
101 | cursor: pointer;
102 | }
103 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check {
104 | cursor: inherit;
105 | justify-content: flex-start;
106 | }
107 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check .form-check-label, .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check .form-check-input {
108 | cursor: inherit;
109 | }
110 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.disabled .form-check-label {
111 | opacity: 0.5;
112 | }
113 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover {
114 | background-color: #e9ecef;
115 | }
116 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover:not(.disabled) {
117 | color: var(--bs-primary);
118 | }
119 | .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover.selected {
120 | color: var(--bs-primary);
121 | }
122 | .dashboardcode-bsmultiselect div.dropdown-menu + div.alert-warning {
123 | padding-left: 0.25rem;
124 | padding-right: 0.25rem;
125 | z-index: 4;
126 | font-size: small;
127 | }
128 | .dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control {
129 | min-height: calc(1.5em + 0.5rem + 2px);
130 | }
131 | .dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input {
132 | font-size: 0.875rem;
133 | }
134 | .dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control {
135 | min-height: calc(1.5em + 1rem + 2px);
136 | }
137 | .dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input {
138 | font-size: 1.25rem;
139 | }
140 |
141 | .form-floating .dashboardcode-bsmultiselect ul.form-control {
142 | min-height: calc(3.5rem + 2px);
143 | }
144 | .form-floating .dashboardcode-bsmultiselect ul.form-control.floating-lifted {
145 | padding-top: 1.625rem;
146 | padding-left: 0.7rem;
147 | padding-bottom: 0;
148 | }
149 | .form-floating .dashboardcode-bsmultiselect + label.floating-lifted {
150 | opacity: 0.65;
151 | transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
152 | }
153 |
154 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:checked ~ .form-check-label,
155 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:not(:checked) ~ .form-check-label {
156 | color: #212529;
157 | }
158 |
159 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .form-check-input:valid:checked ~ .form-check-label,
160 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .form-check-input:valid:not(:checked) ~ .form-check-label {
161 | color: var(--bs-primary);
162 | }
163 |
164 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:checked {
165 | border-color: var(--bs-primary);
166 | background-color: var(--bs-primary);
167 | }
168 |
169 | .was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:not(:checked) {
170 | border: 1px solid rgba(0, 0, 0, 0.25);
171 | }
172 |
173 | /*# sourceMappingURL=BsMultiSelect.css.map */
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.bs4.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../scss/BsMultiSelect.bs4.scss","BsMultiSelect.bs4.css","../../node_modules/bootstrap-4/scss/_variables.scss","../../node_modules/bootstrap-4/scss/mixins/_gradients.scss"],"names":[],"mappings":"AA2CI;EACI,aAAA;EACA,eAAA;EACA,YAAA;EACA,uCAAA;EACA,gBAAA;EACA,YAAA;EACA,qBAAA;AC1CR;AD4CQ;EACI,YAAA;EACA,UAAA;EACA,SAAA;EACA,oBAAA;EACA,cAAA;EACA,6BAAA;EACA,SAAA;EACA,aAAA;EACA,gBAAA;AC1CZ;AD4CQ;EACI,yBEvDD;ADaX;AD4CQ;EACI,cEtDD;EFwDC,UAAA;AC3CZ;ADwCQ;EACI,cEtDD;EFwDC,UAAA;AC3CZ;ADwCQ;EACI,cEtDD;EFwDC,UAAA;AC3CZ;AD+CQ;EACI,eAAA;EACA,wBAAA;EAAA,uBAAA;EACA,2BAAA;EAAA,0BAAA;EACA,mBAAA;EACA,sBAAA;EACA,kBAAA;AC7CZ;AD8CY;EACI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,gBAAA;AC5ChB;ADgDY;EACI,aE2Vc;ADzY9B;AD9BI;EACI,cAAA;EACA,sBERG;EFSH,qBEqdgC;EFpdhC,UAAA;EAII,gDEqXkB;ADxV9B;AD6CQ;EACQ,sCAAA;AC3ChB;AD4CgB;EACI,mBEyLU;ADnO9B;AD8CQ;EACQ,oCAAA;AC5ChB;AD6CgB;EACI,kBEiLU;AD5N9B;ADhCQ;EAEI,qBEgnBwB;AD/kBpC;ADhCY;EACI,qBE8mBoB;EF7mBpB,gDAAA;ACkChB;ADvCQ;EAEI,qBEinBwB;ADzkBpC;ADvCY;EACI,qBE+mBoB;EF9mBpB,gDAAA;ACyChB;ADqCI;EACI,qBAAA;ACnCR;ADoCQ;EACI,eAAA;EACA,gBAAA;EACA,gBAAA;AClCZ;ADoCY;EAEI,cAAA;EACA,WAAA;EACA,iBAAA;EACA,WAAA;EACA,gBEiKc;EFhKd,cE/GL;EFgHK,mBAAA;EAEA,mBAAA;EACA,6BAAA;EACA,SAAA;EACA,eAAA;ACpChB;ADqCgB;EACI,2BAAA;ACnCpB;ADoCoB;EACI,eAAA;AClCxB;ADsCgB;EACI,cEjIT;AD6FX;ADsCgB;EACI,qBAAA;EG3IhB,yBDGO;ADqGX;ADsCgB;EACI,qBAAA;ACpCpB;ADsCgB;EACI,qBAAA;ACpCpB;AD0CQ;EACI,qBAAA;EACA,sBAAA;EACA,UAAA;EACA,gBAAA;ACxCZ;AD6CQ;EACI,sCAAA;AC3CZ;AD4CY;EACI,mBEiHc;AD3J9B;ADgDQ;EACI,oCAAA;AC9CZ;AD+CY;EACI,kBEuGc;ADpJ9B;;ADwDA;EACI,cE/KO;AD0HX;;ADuDA;EACI,cElLO;AD8HX;;ADwDA;EACI,qBAAA;ACrDJ;;ADuDA;EACI,qBAAA;ACpDJ;;ADwDA;EACI,WExMO;EFyMP,qBE0C0B;ECpP1B,yBDoP0B;AD9F9B;;ADwDA;EACI,yBAAA;ACrDJ","file":"BsMultiSelect.bs4.css","sourcesContent":[null,".dashboardcode-bsmultiselect ul.form-control {\n display: flex;\n flex-wrap: wrap;\n height: auto;\n min-height: calc(1.5em + 0.75rem + 2px);\n margin-bottom: 0;\n cursor: text;\n list-style-type: none;\n}\n.dashboardcode-bsmultiselect ul.form-control input {\n height: auto;\n padding: 0;\n margin: 0;\n font-weight: inherit;\n color: inherit;\n background-color: transparent;\n border: 0;\n outline: none;\n box-shadow: none;\n}\n.dashboardcode-bsmultiselect ul.form-control.disabled {\n background-color: #e9ecef;\n}\n.dashboardcode-bsmultiselect ul.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge {\n padding-left: 0;\n padding-inline-start: 0;\n padding-inline-end: 0.5rem;\n padding-top: 0.35em;\n padding-bottom: 0.35em;\n line-height: 1.5em;\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge button.close {\n float: none;\n font-size: 1.8em;\n line-height: 0.5em;\n font-weight: 400;\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge span.disabled {\n opacity: 0.65;\n}\n.dashboardcode-bsmultiselect ul.form-control.focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-sm {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-sm input {\n font-size: 0.875rem;\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-lg {\n min-height: calc(1.5em + 1rem + 2px);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-lg input {\n font-size: 1.25rem;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:valid, .dashboardcode-bsmultiselect ul.form-control.is-valid {\n border-color: #28a745;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus, .dashboardcode-bsmultiselect ul.form-control.is-valid.focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid, .dashboardcode-bsmultiselect ul.form-control.is-invalid {\n border-color: #dc3545;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus, .dashboardcode-bsmultiselect ul.form-control.is-invalid.focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu {\n list-style-type: none;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul {\n padding-left: 0;\n padding-right: 0;\n margin-bottom: 0;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li {\n display: block;\n width: 100%;\n padding: 0 0.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n cursor: pointer;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control {\n justify-content: flex-start;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control .custom-control-input, .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control .custom-control-label {\n cursor: inherit;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.disabled .custom-control-label {\n color: #6c757d;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover {\n color: var(--primary);\n background-color: #e9ecef;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover:not(.disabled) {\n color: var(--primary);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover.selected {\n color: var(--primary);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu + div.alert-warning {\n padding-left: 0.25rem;\n padding-right: 0.25rem;\n z-index: 4;\n font-size: small;\n}\n.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\n.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input {\n font-size: 0.875rem;\n}\n.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control {\n min-height: calc(1.5em + 1rem + 2px);\n}\n.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input {\n font-size: 1.25rem;\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:checked ~ .custom-control-label {\n color: #212529;\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:not(:checked) ~ .custom-control-label {\n color: #212529;\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .custom-control-input:valid:checked ~ .custom-control-label {\n color: var(--primary);\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .custom-control-input:valid:not(:checked) ~ .custom-control-label {\n color: var(--primary);\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:checked ~ .custom-control-label::before {\n color: #fff;\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .custom-control-input:valid:not(:checked) ~ .custom-control-label::before {\n border: #adb5bd solid 1px;\n}\n\n/*# sourceMappingURL=BsMultiSelect.bs4.css.map */\n",null,null]}
--------------------------------------------------------------------------------
/dist/css/BsMultiSelect.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../scss/BsMultiSelect.scss","BsMultiSelect.css","../../node_modules/bootstrap/scss/_variables.scss","../../node_modules/bootstrap/scss/mixins/_gradients.scss"],"names":[],"mappings":"AA2CI;EACI,aAAA;EACA,eAAA;EACA,YAAA;EACA,uCAAA;EACA,gBAAA;EACA,YAAA;EACA,qBAAA;AC1CR;AD4CQ;EACI,YAAA;EACA,UAAA;EACA,SAAA;EACA,oBAAA;EACA,cAAA;EACA,6BAAA;EACA,SAAA;EACA,aAAA;EACA,gBAAA;AC1CZ;AD4CQ;EACI,yBEtDD;ADYX;AD4CQ;EACI,cErDD;EFuDC,UAAA;AC3CZ;ADwCQ;EACI,cErDD;EFuDC,UAAA;AC3CZ;ADwCQ;EACI,cErDD;EFuDC,UAAA;AC3CZ;ADgDQ;EACI,eAAA;EACA,wBAAA;EAAA,uBAAA;EACA,2BAAA;EAAA,0BAAA;EACA,qBAAA;AC9CZ;AD+CY;EACI,WAAA;EACA,gBAAA;AC7ChB;AD+CY;EACI,aEqsBc;ADlvB9B;AD1BI;EACI,cEGG;EFFH,sBEPG;EFQH,qBEk1BgC;EFj1BhC,UAAA;EAII,kDE4tBoB;ADnsBhC;AD4CQ;EACI,sCAAA;AC1CZ;AD2CY;EACI,mBE6ec;ADthB9B;AD6CQ;EACI,oCAAA;AC3CZ;AD4CY;EACI,kBEuec;ADjhB9B;AD5BQ;EAEI,qBEaF;ADgBV;AD5BY;EACI,qBEWN;EFVM,iDAAA;AC8BhB;ADnCQ;EAEI,qBEUF;AD0BV;ADnCY;EACI,qBEQN;EFPM,iDAAA;ACqChB;ADqCQ;EACI,qBAAA;EACA,eAAA;EACA,gBAAA;EACA,gBAAA;ACnCZ;ADqCY;EAEI,cAAA;EACA,WAAA;EACA,iBAAA;EACA,WAAA;EACA,gBEsdc;EFrdd,cEzGL;EF0GK,mBAAA;EAEA,mBAAA;EACA,6BAAA;EACA,SAAA;EACA,eAAA;ACrChB;ADsCgB;EACI,eAAA;EACA,2BAAA;ACpCpB;ADqCoB;EACI,eAAA;ACnCxB;ADsCgB;EACI,YE6wBuB;ADjzB3C;ADsCgB;EGvId,yBDMS;AD8FX;ADsCgB;EACI,wBAAA;ACpCpB;ADsCgB;EACI,wBAAA;ACpCpB;ADyCQ;EACI,qBAAA;EACA,sBAAA;EACA,UAAA;EACA,gBAAA;ACvCZ;AD4CQ;EACI,sCAAA;AC1CZ;AD2CY;EACI,mBEuac;ADhd9B;AD+CQ;EACI,oCAAA;AC7CZ;AD8CY;EACI,kBE+Zc;AD3c9B;;ADoDQ;EACI,8BAAA;ACjDZ;ADmDY;EACI,qBAAA;EACA,oBAAA;EACA,iBAAA;ACjDhB;ADqDQ;EACI,aAAA;EACA,8DAAA;ACnDZ;;AD6DA;;EAEI,cE3LO;ADiIX;;AD8DA;;EAEI,wBAAA;AC3DJ;;AD+DA;EACI,+BAAA;EACA,mCAAA;AC5DJ;;AD8DA;EACI,qCE0qBsC;ADruB1C","file":"BsMultiSelect.css","sourcesContent":[null,".dashboardcode-bsmultiselect ul.form-control {\n display: flex;\n flex-wrap: wrap;\n height: auto;\n min-height: calc(1.5em + 0.75rem + 2px);\n margin-bottom: 0;\n cursor: text;\n list-style-type: none;\n}\n.dashboardcode-bsmultiselect ul.form-control input {\n height: auto;\n padding: 0;\n margin: 0;\n font-weight: inherit;\n color: inherit;\n background-color: transparent;\n border: 0;\n outline: none;\n box-shadow: none;\n}\n.dashboardcode-bsmultiselect ul.form-control.disabled {\n background-color: #e9ecef;\n}\n.dashboardcode-bsmultiselect ul.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge {\n padding-left: 0;\n padding-inline-start: 0;\n padding-inline-end: 0.5rem;\n color: var(--bs-dark);\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge button.btn-close {\n float: none;\n font-size: 0.8em;\n}\n.dashboardcode-bsmultiselect ul.form-control > li.badge span.disabled {\n opacity: 0.65;\n}\n.dashboardcode-bsmultiselect ul.form-control.focus {\n color: #212529;\n background-color: #fff;\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-sm {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-sm input {\n font-size: 0.875rem;\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-lg {\n min-height: calc(1.5em + 1rem + 2px);\n}\n.dashboardcode-bsmultiselect ul.form-control.form-control-lg input {\n font-size: 1.25rem;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:valid, .dashboardcode-bsmultiselect ul.form-control.is-valid {\n border-color: #198754;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:valid.focus, .dashboardcode-bsmultiselect ul.form-control.is-valid.focus {\n border-color: #198754;\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid, .dashboardcode-bsmultiselect ul.form-control.is-invalid {\n border-color: #dc3545;\n}\n.was-validated .dashboardcode-bsmultiselect ul.form-control:invalid.focus, .dashboardcode-bsmultiselect ul.form-control.is-invalid.focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul {\n list-style-type: none;\n padding-left: 0;\n padding-right: 0;\n margin-bottom: 0;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li {\n display: block;\n width: 100%;\n padding: 0 0.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n cursor: pointer;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check {\n cursor: inherit;\n justify-content: flex-start;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check .form-check-label, .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check .form-check-input {\n cursor: inherit;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.disabled .form-check-label {\n opacity: 0.5;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover {\n background-color: #e9ecef;\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover:not(.disabled) {\n color: var(--bs-primary);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover.selected {\n color: var(--bs-primary);\n}\n.dashboardcode-bsmultiselect div.dropdown-menu + div.alert-warning {\n padding-left: 0.25rem;\n padding-right: 0.25rem;\n z-index: 4;\n font-size: small;\n}\n.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\n.dashboardcode-bsmultiselect.input-group.input-group-sm ul.form-control input {\n font-size: 0.875rem;\n}\n.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control {\n min-height: calc(1.5em + 1rem + 2px);\n}\n.dashboardcode-bsmultiselect.input-group.input-group-lg ul.form-control input {\n font-size: 1.25rem;\n}\n\n.form-floating .dashboardcode-bsmultiselect ul.form-control {\n min-height: calc(3.5rem + 2px);\n}\n.form-floating .dashboardcode-bsmultiselect ul.form-control.floating-lifted {\n padding-top: 1.625rem;\n padding-left: 0.7rem;\n padding-bottom: 0;\n}\n.form-floating .dashboardcode-bsmultiselect + label.floating-lifted {\n opacity: 0.65;\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:checked ~ .form-check-label,\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:not(:checked) ~ .form-check-label {\n color: #212529;\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .form-check-input:valid:checked ~ .form-check-label,\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li.hover .form-check-input:valid:not(:checked) ~ .form-check-label {\n color: var(--bs-primary);\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:checked {\n border-color: var(--bs-primary);\n background-color: var(--bs-primary);\n}\n\n.was-validated .dashboardcode-bsmultiselect div.dropdown-menu > ul > li .form-check-input:valid:not(:checked) {\n border: 1px solid rgba(0, 0, 0, 0.25);\n}\n\n/*# sourceMappingURL=BsMultiSelect.css.map */\n",null,null]}
--------------------------------------------------------------------------------