Add Tom-Select lib

This commit is contained in:
jf-cbd
2025-11-13 10:48:06 +01:00
parent cef8fbc859
commit 7733f13d14
354 changed files with 53014 additions and 2 deletions

13
node_modules/tom-select/src/constants.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
export const KEY_A = 65;
export const KEY_RETURN = 13;
export const KEY_ESC = 27;
export const KEY_LEFT = 37;
export const KEY_UP = 38;
export const KEY_RIGHT = 39;
export const KEY_DOWN = 40;
export const KEY_BACKSPACE = 8;
export const KEY_DELETE = 46;
export const KEY_TAB = 9;
export const IS_MAC = typeof navigator === 'undefined' ? false : /Mac/.test(navigator.userAgent);
export const KEY_SHORTCUT = IS_MAC ? 'metaKey' : 'ctrlKey'; // ctrl key or apple key for ma

81
node_modules/tom-select/src/contrib/highlight.ts generated vendored Normal file
View File

@@ -0,0 +1,81 @@
/**
* highlight v3 | MIT license | Johann Burkard <jb@eaio.com>
* Highlights arbitrary terms in a node.
*
* - Modified by Marshal <beatgates@gmail.com> 2011-6-24 (added regex)
* - Modified by Brian Reavis <brian@thirdroute.com> 2012-8-27 (cleanup)
*/
import {replaceNode} from '../vanilla.ts';
export const highlight = (element:HTMLElement, regex:string|RegExp) => {
if( regex === null ) return;
// convet string to regex
if( typeof regex === 'string' ){
if( !regex.length ) return;
regex = new RegExp(regex, 'i');
}
// Wrap matching part of text node with highlighting <span>, e.g.
// Soccer -> <span class="highlight">Soc</span>cer for regex = /soc/i
const highlightText = ( node:Text ):number => {
var match = node.data.match(regex);
if( match && node.data.length > 0 ){
var spannode = document.createElement('span');
spannode.className = 'highlight';
var middlebit = node.splitText(match.index as number);
middlebit.splitText(match[0]!.length);
var middleclone = middlebit.cloneNode(true);
spannode.appendChild(middleclone);
replaceNode(middlebit, spannode);
return 1;
}
return 0;
};
// Recurse element node, looking for child text nodes to highlight, unless element
// is childless, <script>, <style>, or already highlighted: <span class="hightlight">
const highlightChildren = ( node:Element ):void => {
if( node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && ( node.className !== 'highlight' || node.tagName !== 'SPAN' ) ){
Array.from(node.childNodes).forEach(element => {
highlightRecursive(element);
});
}
};
const highlightRecursive = ( node:Node|Element ):number => {
if( node.nodeType === 3 ){
return highlightText(node as Text);
}
highlightChildren(node as Element);
return 0;
};
highlightRecursive( element );
};
/**
* removeHighlight fn copied from highlight v5 and
* edited to remove with(), pass js strict mode, and use without jquery
*/
export const removeHighlight = (el:HTMLElement) => {
var elements = el.querySelectorAll("span.highlight");
Array.prototype.forEach.call(elements, function(el:HTMLElement){
var parent = el.parentNode as Node;
parent.replaceChild(el.firstChild as Node, el);
parent.normalize();
});
};

73
node_modules/tom-select/src/contrib/microevent.ts generated vendored Normal file
View File

@@ -0,0 +1,73 @@
/**
* MicroEvent - to make any js object an event emitter
*
* - pure javascript - server compatible, browser compatible
* - dont rely on the browser doms
* - super simple - you get it immediatly, no mistery, no magic involved
*
* @author Jerome Etienne (https://github.com/jeromeetienne)
*/
type TCallback = (...args:any) => any;
/**
* Execute callback for each event in space separated list of event names
*
*/
function forEvents(events:string,callback:(event:string)=>any){
events.split(/\s+/).forEach((event) =>{
callback(event);
});
}
export default class MicroEvent{
public _events: {[key:string]:TCallback[]};
constructor(){
this._events = {};
}
on(events:string, fct:TCallback){
forEvents(events,(event) => {
const event_array = this._events[event] || [];
event_array.push(fct);
this._events[event] = event_array;
});
}
off(events:string, fct:TCallback){
var n = arguments.length;
if( n === 0 ){
this._events = {};
return;
}
forEvents(events,(event) => {
if (n === 1){
delete this._events[event];
return
}
const event_array = this._events[event];
if( event_array === undefined ) return;
event_array.splice(event_array.indexOf(fct), 1);
this._events[event] = event_array;
});
}
trigger(events:string, ...args:any){
var self = this;
forEvents(events,(event) => {
const event_array = self._events[event];
if( event_array === undefined ) return;
event_array.forEach(fct => {
fct.apply(self, args );
});
});
}
};

137
node_modules/tom-select/src/contrib/microplugin.ts generated vendored Normal file
View File

@@ -0,0 +1,137 @@
/**
* microplugin.js
* Copyright (c) 2013 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
type TSettings = {
[key:string]:any
}
type TPlugins = {
names: string[],
settings: TSettings,
requested: {[key:string]:boolean},
loaded: {[key:string]:any}
};
export type TPluginItem = {name:string,options:{}};
export type TPluginHash = {[key:string]:{}};
export default function MicroPlugin(Interface: any ){
Interface.plugins = {};
return class extends Interface{
public plugins:TPlugins = {
names : [],
settings : {},
requested : {},
loaded : {}
};
/**
* Registers a plugin.
*
* @param {function} fn
*/
static define(name:string, fn:(this:any,settings:TSettings)=>any){
Interface.plugins[name] = {
'name' : name,
'fn' : fn
};
}
/**
* Initializes the listed plugins (with options).
* Acceptable formats:
*
* List (without options):
* ['a', 'b', 'c']
*
* List (with options):
* [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
*
* Hash (with options):
* {'a': { ... }, 'b': { ... }, 'c': { ... }}
*
* @param {array|object} plugins
*/
initializePlugins(plugins:string[]|TPluginItem[]|TPluginHash) {
var key, name;
const self = this;
const queue:string[] = [];
if (Array.isArray(plugins)) {
plugins.forEach((plugin:string|TPluginItem)=>{
if (typeof plugin === 'string') {
queue.push(plugin);
} else {
self.plugins.settings[plugin.name] = plugin.options;
queue.push(plugin.name);
}
});
} else if (plugins) {
for (key in plugins) {
if (plugins.hasOwnProperty(key)) {
self.plugins.settings[key] = plugins[key];
queue.push(key);
}
}
}
while( name = queue.shift() ){
self.require(name);
}
}
loadPlugin(name:string) {
var self = this;
var plugins = self.plugins;
var plugin = Interface.plugins[name];
if (!Interface.plugins.hasOwnProperty(name)) {
throw new Error('Unable to find "' + name + '" plugin');
}
plugins.requested[name] = true;
plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
plugins.names.push(name);
}
/**
* Initializes a plugin.
*
*/
require(name:string) {
var self = this;
var plugins = self.plugins;
if (!self.plugins.loaded.hasOwnProperty(name)) {
if (plugins.requested[name]) {
throw new Error('Plugin has circular dependency ("' + name + '")');
}
self.loadPlugin(name);
}
return plugins.loaded[name];
}
};
}

94
node_modules/tom-select/src/defaults.ts generated vendored Normal file
View File

@@ -0,0 +1,94 @@
export default {
options: [],
optgroups: [],
plugins: [],
delimiter: ',',
splitOn: null, // regexp or string for splitting up values from a paste command
persist: true,
diacritics: true,
create: null,
createOnBlur: false,
createFilter: null,
highlight: true,
openOnFocus: true,
shouldOpen: null,
maxOptions: 50,
maxItems: null,
hideSelected: null,
duplicates: false,
addPrecedence: false,
selectOnTab: false,
preload: null,
allowEmptyOption: false,
//closeAfterSelect: false,
refreshThrottle: 300,
loadThrottle: 300,
loadingClass: 'loading',
dataAttr: null, //'data-data',
optgroupField: 'optgroup',
valueField: 'value',
labelField: 'text',
disabledField: 'disabled',
optgroupLabelField: 'label',
optgroupValueField: 'value',
lockOptgroupOrder: false,
sortField: '$order',
searchField: ['text'],
searchConjunction: 'and',
mode: null,
wrapperClass: 'ts-wrapper',
controlClass: 'ts-control',
dropdownClass: 'ts-dropdown',
dropdownContentClass: 'ts-dropdown-content',
itemClass: 'item',
optionClass: 'option',
dropdownParent: null,
controlInput: '<input type="text" autocomplete="off" size="1" />',
copyClassesToDropdown: false,
placeholder: null,
hidePlaceholder: null,
shouldLoad: function(query:string):boolean{
return query.length > 0;
},
/*
load : null, // function(query, callback) { ... }
score : null, // function(search) { ... }
onInitialize : null, // function() { ... }
onChange : null, // function(value) { ... }
onItemAdd : null, // function(value, $item) { ... }
onItemRemove : null, // function(value) { ... }
onClear : null, // function() { ... }
onOptionAdd : null, // function(value, data) { ... }
onOptionRemove : null, // function(value) { ... }
onOptionClear : null, // function() { ... }
onOptionGroupAdd : null, // function(id, data) { ... }
onOptionGroupRemove : null, // function(id) { ... }
onOptionGroupClear : null, // function() { ... }
onDropdownOpen : null, // function(dropdown) { ... }
onDropdownClose : null, // function(dropdown) { ... }
onType : null, // function(str) { ... }
onDelete : null, // function(values) { ... }
*/
render: {
/*
item: null,
optgroup: null,
optgroup_header: null,
option: null,
option_create: null
*/
}
};

176
node_modules/tom-select/src/getSettings.ts generated vendored Normal file
View File

@@ -0,0 +1,176 @@
import defaults from './defaults.ts';
import { hash_key, iterate } from './utils.ts';
import { TomOption, TomSettings, RecursivePartial } from './types/index.ts';
import { TomInput } from './types/index.ts';
export default function getSettings( input:TomInput, settings_user:RecursivePartial<TomSettings>):TomSettings{
var settings:TomSettings = Object.assign({}, defaults, settings_user);
var attr_data = settings.dataAttr;
var field_label = settings.labelField;
var field_value = settings.valueField;
var field_disabled = settings.disabledField;
var field_optgroup = settings.optgroupField;
var field_optgroup_label = settings.optgroupLabelField;
var field_optgroup_value = settings.optgroupValueField;
var tag_name = input.tagName.toLowerCase();
var placeholder = input.getAttribute('placeholder') || input.getAttribute('data-placeholder');
if (!placeholder && !settings.allowEmptyOption) {
let option = input.querySelector('option[value=""]');
if( option ){
placeholder = option.textContent;
}
}
var settings_element:{
placeholder : null|string,
options : TomOption[],
optgroups : TomOption[],
items : string[],
maxItems : null|number,
} = {
placeholder : placeholder,
options : [],
optgroups : [],
items : [],
maxItems : null,
};
/**
* Initialize from a <select> element.
*
*/
var init_select = () => {
var tagName;
var options = settings_element.options;
var optionsMap:{[key:string]:any} = {};
var group_count = 1;
let $order = 0;
var readData = (el:HTMLElement):TomOption => {
var data = Object.assign({},el.dataset); // get plain object from DOMStringMap
var json = attr_data && data[attr_data];
if( typeof json === 'string' && json.length ){
data = Object.assign(data,JSON.parse(json));
}
return data;
};
var addOption = (option:HTMLOptionElement, group?:string) => {
var value = hash_key(option.value);
if ( value == null ) return;
if ( !value && !settings.allowEmptyOption) return;
// if the option already exists, it's probably been
// duplicated in another optgroup. in this case, push
// the current group to the "optgroup" property on the
// existing option so that it's rendered in both places.
if (optionsMap.hasOwnProperty(value)) {
if (group) {
var arr = optionsMap[value][field_optgroup];
if (!arr) {
optionsMap[value][field_optgroup] = group;
} else if (!Array.isArray(arr)) {
optionsMap[value][field_optgroup] = [arr, group];
} else {
arr.push(group);
}
}
}else{
var option_data = readData(option);
option_data[field_label] = option_data[field_label] || option.textContent;
option_data[field_value] = option_data[field_value] || value;
option_data[field_disabled] = option_data[field_disabled] || option.disabled;
option_data[field_optgroup] = option_data[field_optgroup] || group;
option_data.$option = option;
option_data.$order = option_data.$order || ++$order;
optionsMap[value] = option_data;
options.push(option_data);
}
if( option.selected ){
settings_element.items.push(value);
}
};
var addGroup = ( optgroup:HTMLOptGroupElement ) => {
var id:string, optgroup_data
optgroup_data = readData(optgroup);
optgroup_data[field_optgroup_label] = optgroup_data[field_optgroup_label] || optgroup.getAttribute('label') || '';
optgroup_data[field_optgroup_value] = optgroup_data[field_optgroup_value] || group_count++;
optgroup_data[field_disabled] = optgroup_data[field_disabled] || optgroup.disabled;
optgroup_data.$order = optgroup_data.$order || ++$order;
settings_element.optgroups.push(optgroup_data);
id = optgroup_data[field_optgroup_value];
iterate(optgroup.children, (option)=>{
addOption(option as HTMLOptionElement, id);
});
};
settings_element.maxItems = input.hasAttribute('multiple') ? null : 1;
iterate(input.children,(child)=>{
tagName = child.tagName.toLowerCase();
if (tagName === 'optgroup') {
addGroup(child as HTMLOptGroupElement);
} else if (tagName === 'option') {
addOption(child as HTMLOptionElement);
}
});
};
/**
* Initialize from a <input type="text"> element.
*
*/
var init_textbox = () => {
const data_raw = input.getAttribute(attr_data);
if (!data_raw) {
var value = input.value.trim() || '';
if (!settings.allowEmptyOption && !value.length) return;
const values = value.split(settings.delimiter);
iterate( values, (value) => {
const option:TomOption = {};
option[field_label] = value;
option[field_value] = value;
settings_element.options.push(option);
});
settings_element.items = values;
} else {
settings_element.options = JSON.parse(data_raw);
iterate( settings_element.options, (opt) => {
settings_element.items.push(opt[field_value]);
});
}
};
if (tag_name === 'select') {
init_select();
} else {
init_textbox();
}
return Object.assign( {}, defaults, settings_element, settings_user) as TomSettings;
};

View File

@@ -0,0 +1,73 @@
/**
* Plugin: "dropdown_input" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { nodeIndex, removeClasses } from '../../vanilla.ts';
export default function(this:TomSelect) {
var self = this;
/**
* Moves the caret to the specified index.
*
* The input must be moved by leaving it in place and moving the
* siblings, due to the fact that focus cannot be restored once lost
* on mobile webkit devices
*
*/
self.hook('instead','setCaret',(new_pos:number) => {
if( self.settings.mode === 'single' || !self.control.contains(self.control_input) ) {
new_pos = self.items.length;
} else {
new_pos = Math.max(0, Math.min(self.items.length, new_pos));
if( new_pos != self.caretPos && !self.isPending ){
self.controlChildren().forEach((child,j) => {
if( j < new_pos ){
self.control_input.insertAdjacentElement('beforebegin', child );
} else {
self.control.appendChild( child );
}
});
}
}
self.caretPos = new_pos;
});
self.hook('instead','moveCaret',(direction:number) => {
if( !self.isFocused ) return;
// move caret before or after selected items
const last_active = self.getLastActive(direction);
if( last_active ){
const idx = nodeIndex(last_active);
self.setCaret(direction > 0 ? idx + 1: idx);
self.setActiveItem();
removeClasses(last_active as HTMLElement,'last-active');
// move caret left or right of current position
}else{
self.setCaret(self.caretPos + direction);
}
});
};

View File

@@ -0,0 +1,23 @@
/**
* Plugin: "change_listener" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { addEvent } from '../../utils.ts';
export default function(this:TomSelect) {
addEvent(this.input,'change',()=>{
this.sync();
});
};

View File

@@ -0,0 +1,11 @@
.plugin-checkbox_options:not(.rtl) {
.option input {
margin-right: 0.5rem;
}
}
.plugin-checkbox_options.rtl {
.option input {
margin-left: 0.5rem;
}
}

View File

@@ -0,0 +1,130 @@
/**
* Plugin: "checkbox_options" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { TomTemplate } from '../../types/index.ts';
import { preventDefault, hash_key } from '../../utils.ts';
import { getDom } from '../../vanilla.ts';
import { CBOptions } from './types.ts';
export default function(this:TomSelect, userOptions:CBOptions) {
var self = this;
var orig_onOptionSelect = self.onOptionSelect;
self.settings.hideSelected = false;
const cbOptions : CBOptions = Object.assign({
// so that the user may add different ones as well
className : "tomselect-checkbox",
// the following default to the historic plugin's values
checkedClassNames : undefined,
uncheckedClassNames : undefined,
}, userOptions);
var UpdateChecked = function(checkbox:HTMLInputElement, toCheck : boolean) {
if( toCheck ){
checkbox.checked = true;
if (cbOptions.uncheckedClassNames) {
checkbox.classList.remove(...cbOptions.uncheckedClassNames);
}
if (cbOptions.checkedClassNames) {
checkbox.classList.add(...cbOptions.checkedClassNames);
}
}else{
checkbox.checked = false;
if (cbOptions.checkedClassNames) {
checkbox.classList.remove(...cbOptions.checkedClassNames);
}
if (cbOptions.uncheckedClassNames) {
checkbox.classList.add(...cbOptions.uncheckedClassNames);
}
}
}
// update the checkbox for an option
var UpdateCheckbox = function(option:HTMLElement){
setTimeout(()=>{
var checkbox = option.querySelector('input.' + cbOptions.className);
if( checkbox instanceof HTMLInputElement ){
UpdateChecked(checkbox, option.classList.contains('selected'));
}
},1);
};
// add checkbox to option template
self.hook('after','setupTemplates',() => {
var orig_render_option = self.settings.render.option;
self.settings.render.option = ((data, escape_html) => {
var rendered = getDom(orig_render_option.call(self, data, escape_html));
var checkbox = document.createElement('input');
if (cbOptions.className) {
checkbox.classList.add(cbOptions.className);
}
checkbox.addEventListener('click',function(evt){
preventDefault(evt);
});
checkbox.type = 'checkbox';
const hashed = hash_key(data[self.settings.valueField]);
UpdateChecked(checkbox, !!(hashed && self.items.indexOf(hashed) > -1) );
rendered.prepend(checkbox);
return rendered;
}) satisfies TomTemplate;
});
// uncheck when item removed
self.on('item_remove',(value:string) => {
var option = self.getOption(value);
if( option ){ // if dropdown hasn't been opened yet, the option won't exist
option.classList.remove('selected'); // selected class won't be removed yet
UpdateCheckbox(option);
}
});
// check when item added
self.on('item_add',(value:string) => {
var option = self.getOption(value);
if( option ){ // if dropdown hasn't been opened yet, the option won't exist
UpdateCheckbox(option);
}
});
// remove items when selected option is clicked
self.hook('instead','onOptionSelect',( evt:KeyboardEvent, option:HTMLElement )=>{
if( option.classList.contains('selected') ){
option.classList.remove('selected')
self.removeItem(option.dataset.value);
self.refreshOptions();
preventDefault(evt,true);
return;
}
orig_onOptionSelect.call(self, evt, option);
UpdateCheckbox(option);
});
};

View File

@@ -0,0 +1,15 @@
export type CBOptions = {
/**
* a unique class name for the checkbox to find the input
*/
className ?: string;
/**
* class name to add if checkbox is checked and remove otherwise
*/
checkedClassNames ?: string[],
/**
* class name to add if checkbox was not checked and remove otherwise
*/
uncheckedClassNames ?: string[],
};

View File

@@ -0,0 +1,33 @@
/* stylelint-disable function-name-case */
.plugin-clear_button {
--ts-pr-clear-button: 1em;
.clear-button{
opacity: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
right: calc(#{$select-padding-x} - #{$select-padding-item-x});
margin-right: 0 !important;
background: transparent !important;
transition: opacity 0.5s;
cursor: pointer;
}
&.form-select .clear-button,
&.single .clear-button {
@if variable-exists(select-padding-dropdown-item-x) {
right: Max(var(--ts-pr-caret), #{$select-padding-dropdown-item-x});
}
@else{
right: Max(var(--ts-pr-caret), calc(#{$select-padding-x} - #{$select-padding-item-x}));
}
}
&.focus.has-items .clear-button,
&:not(.disabled):hover.has-items .clear-button{
opacity: 1;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Plugin: "dropdown_header" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { getDom } from '../../vanilla.ts';
import { CBOptions } from './types.ts';
export default function(this:TomSelect, userOptions:CBOptions) {
const self = this;
const options = Object.assign({
className: 'clear-button',
title: 'Clear All',
html: (data:CBOptions) => {
return `<div class="${data.className}" title="${data.title}">&#10799;</div>`;
}
}, userOptions);
self.on('initialize',()=>{
var button = getDom(options.html(options));
button.addEventListener('click',(evt)=>{
if( self.isLocked ) return;
self.clear();
if( self.settings.mode === 'single' && self.settings.allowEmptyOption ){
self.addItem('');
}
evt.preventDefault();
evt.stopPropagation();
});
self.control.appendChild(button);
});
};

View File

@@ -0,0 +1,6 @@
export type CBOptions = {
className ?:string,
title ?:string,
html ?: (data:CBOptions) => string,
}

View File

@@ -0,0 +1,10 @@
.#{$select-ns}-wrapper.plugin-drag_drop {
.ts-dragging{
color:transparent !important;
}
.ts-dragging > * {
visibility:hidden !important;
}
}

143
node_modules/tom-select/src/plugins/drag_drop/plugin.ts generated vendored Normal file
View File

@@ -0,0 +1,143 @@
/**
* Plugin: "drag_drop" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { TomOption, TomItem } from '../../types/index.ts';
import { escape_html, preventDefault, addEvent } from '../../utils.ts';
import { getDom, setAttr } from '../../vanilla.ts';
const insertAfter = (referenceNode:Element, newNode:Element) => {
referenceNode.parentNode?.insertBefore(newNode, referenceNode.nextSibling);
}
const insertBefore = (referenceNode:Element, newNode:Element) => {
referenceNode.parentNode?.insertBefore(newNode, referenceNode);
}
const isBefore = (referenceNode:Element|undefined|null, newNode:Element|undefined|null) =>{
do{
newNode = newNode?.previousElementSibling;
if( referenceNode == newNode ){
return true;
}
}while( newNode && newNode.previousElementSibling );
return false;
}
export default function(this:TomSelect) {
var self = this;
if (self.settings.mode !== 'multi') return;
var orig_lock = self.lock;
var orig_unlock = self.unlock;
let sortable = true;
let drag_item:TomItem|undefined;
/**
* Add draggable attribute to item
*/
self.hook('after','setupTemplates',() => {
var orig_render_item = self.settings.render.item;
self.settings.render.item = (data:TomOption, escape:typeof escape_html) => {
const item = getDom(orig_render_item.call(self, data, escape)) as TomItem;
setAttr(item,{'draggable':'true'});
// prevent doc_mousedown (see tom-select.ts)
const mousedown = (evt:Event) => {
if( !sortable ) preventDefault(evt);
evt.stopPropagation();
}
const dragStart = (evt:Event) => {
drag_item = item;
setTimeout(() => {
item.classList.add('ts-dragging');
}, 0);
}
const dragOver = (evt:Event) =>{
evt.preventDefault();
item.classList.add('ts-drag-over');
moveitem(item,drag_item);
}
const dragLeave = () => {
item.classList.remove('ts-drag-over');
}
const moveitem = (targetitem:TomItem, dragitem:TomItem|undefined) => {
if( dragitem === undefined ) return;
if( isBefore(dragitem,item) ){
insertAfter(targetitem,dragitem);
}else{
insertBefore(targetitem,dragitem);
}
}
const dragend = () => {
document.querySelectorAll('.ts-drag-over').forEach(el=> el.classList.remove('ts-drag-over'));
drag_item?.classList.remove('ts-dragging');
drag_item = undefined;
var values:string[] = [];
self.control.querySelectorAll(`[data-value]`).forEach((el:Element)=> {
if( (<HTMLOptionElement>el).dataset.value ){
let value = (<HTMLOptionElement>el).dataset.value;
if( value ){
values.push(value);
}
}
});
self.setValue(values);
}
addEvent(item,'mousedown', mousedown);
addEvent(item,'dragstart', dragStart);
addEvent(item,'dragenter', dragOver)
addEvent(item,'dragover', dragOver);
addEvent(item,'dragleave', dragLeave);
addEvent(item,'dragend', dragend);
return item;
}
});
self.hook('instead','lock',()=>{
sortable = false;
return orig_lock.call(self);
});
self.hook('instead','unlock',()=>{
sortable = true;
return orig_unlock.call(self);
});
};

View File

@@ -0,0 +1,24 @@
.#{$select-ns}-wrapper{
.dropdown-header {
position: relative;
padding: ($select-padding-dropdown-item-y * 2) $select-padding-dropdown-item-x;
border-bottom: 1px solid $select-color-border;
background: color-mix($select-color-dropdown, $select-color-border, 85%);
border-radius: $select-border-radius $select-border-radius 0 0;
}
.dropdown-header-close {
position: absolute;
right: $select-padding-dropdown-item-x;
top: 50%;
color: $select-color-text;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.dropdown-header-close:hover {
color: darken($select-color-text, 25%);
}
}

View File

@@ -0,0 +1,57 @@
/**
* Plugin: "dropdown_header" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { getDom } from '../../vanilla.ts';
import { preventDefault } from '../../utils.ts';
import { DHOptions } from './types.ts';
export default function(this:TomSelect, userOptions:DHOptions) {
const self = this;
const options = Object.assign({
title : 'Untitled',
headerClass : 'dropdown-header',
titleRowClass : 'dropdown-header-title',
labelClass : 'dropdown-header-label',
closeClass : 'dropdown-header-close',
html: (data:DHOptions) => {
return (
'<div class="' + data.headerClass + '">' +
'<div class="' + data.titleRowClass + '">' +
'<span class="' + data.labelClass + '">' + data.title + '</span>' +
'<a class="' + data.closeClass + '">&times;</a>' +
'</div>' +
'</div>'
);
}
}, userOptions);
self.on('initialize',()=>{
var header = getDom(options.html(options));
var close_link = header.querySelector('.'+options.closeClass);
if( close_link ){
close_link.addEventListener('click',(evt)=>{
preventDefault(evt,true);
self.close();
});
}
self.dropdown.insertBefore(header, self.dropdown.firstChild);
});
};

View File

@@ -0,0 +1,9 @@
export type DHOptions = {
title ?: string,
headerClass ?: string,
titleRowClass ?: string,
labelClass ?: string,
closeClass ?: string,
html ?: (data:DHOptions) => string,
};

View File

@@ -0,0 +1,43 @@
.plugin-dropdown_input{
&.focus.dropdown-active .#{$select-ns}-control{
box-shadow: none;
border: $select-border;
@if variable-exists(input-box-shadow) {
box-shadow: $input-box-shadow;
}
}
.dropdown-input {
border: 1px solid $select-color-border;
border-width: 0 0 1px;
display: block;
padding: $select-padding-y $select-padding-x;
box-shadow: $select-shadow-input;
width: 100%;
background: transparent;
}
&.focus .#{$select-ns}-dropdown .dropdown-input{
@if variable-exists(input-focus-border-color) {
border-color: $input-focus-border-color;
outline: 0;
@if $enable-shadows {
box-shadow: $input-box-shadow, $input-focus-box-shadow;
} @else {
box-shadow: $input-focus-box-shadow;
}
}
}
.items-placeholder{
border: 0 none !important;
box-shadow: none !important;
width: 100%;
}
&.has-items .items-placeholder,
&.dropdown-active .items-placeholder{
display: none !important;
}
}

View File

@@ -0,0 +1,92 @@
/**
* Plugin: "dropdown_input" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import * as constants from '../../constants.ts';
import { getDom, addClasses } from '../../vanilla.ts';
import { addEvent, preventDefault } from '../../utils.ts';
export default function(this:TomSelect) {
const self = this;
self.settings.shouldOpen = true; // make sure the input is shown even if there are no options to display in the dropdown
self.hook('before','setup',()=>{
self.focus_node = self.control;
addClasses( self.control_input, 'dropdown-input');
const div = getDom('<div class="dropdown-input-wrap">');
div.append(self.control_input);
self.dropdown.insertBefore(div, self.dropdown.firstChild);
// set a placeholder in the select control
const placeholder = getDom('<input class="items-placeholder" tabindex="-1" />') as HTMLInputElement;
placeholder.placeholder = self.settings.placeholder ||'';
self.control.append(placeholder);
});
self.on('initialize',()=>{
// set tabIndex on control to -1, otherwise [shift+tab] will put focus right back on control_input
self.control_input.addEventListener('keydown',(evt:KeyboardEvent) =>{
//addEvent(self.control_input,'keydown' as const,(evt:KeyboardEvent) =>{
switch( evt.keyCode ){
case constants.KEY_ESC:
if (self.isOpen) {
preventDefault(evt,true);
self.close();
}
self.clearActiveItems();
return;
case constants.KEY_TAB:
self.focus_node.tabIndex = -1;
break;
}
return self.onKeyDown.call(self,evt);
});
self.on('blur',()=>{
self.focus_node.tabIndex = self.isDisabled ? -1 : self.tabIndex;
});
// give the control_input focus when the dropdown is open
self.on('dropdown_open',() =>{
self.control_input.focus();
});
// prevent onBlur from closing when focus is on the control_input
const orig_onBlur = self.onBlur;
self.hook('instead','onBlur',(evt?:FocusEvent)=>{
if( evt && evt.relatedTarget == self.control_input ) return;
return orig_onBlur.call(self);
});
addEvent(self.control_input,'blur', () => self.onBlur() );
// return focus to control to allow further keyboard input
self.hook('before','close',() =>{
if( !self.isOpen ) return;
self.focus_node.focus({preventScroll: true});
});
});
};

View File

@@ -0,0 +1,15 @@
.#{$select-ns}-wrapper.plugin-input_autogrow{
&.has-items .#{$select-ns}-control > input {
min-width: 0;
}
&.has-items.focus .#{$select-ns}-control > input {
flex: none;
min-width: 4px;
&::placeholder {
color:transparent;
}
}
}

View File

@@ -0,0 +1,56 @@
/**
* Plugin: "input_autogrow" (Tom Select)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { addEvent } from '../../utils.ts';
export default function(this:TomSelect) {
var self = this;
self.on('initialize',()=>{
var test_input = document.createElement('span');
var control = self.control_input;
test_input.style.cssText = 'position:absolute; top:-99999px; left:-99999px; width:auto; padding:0; white-space:pre; ';
self.wrapper.appendChild(test_input);
var transfer_styles = [ 'letterSpacing', 'fontSize', 'fontFamily', 'fontWeight', 'textTransform' ];
for( const style_name of transfer_styles ){
// @ts-ignore TS7015 https://stackoverflow.com/a/50506154/697576
test_input.style[style_name] = control.style[style_name];
}
/**
* Set the control width
*
*/
var resize = ()=>{
test_input.textContent = control.value;
control.style.width = test_input.clientWidth+'px';
};
resize();
self.on('update item_add item_remove',resize);
addEvent(control,'input', resize );
addEvent(control,'keyup', resize );
addEvent(control,'blur', resize );
addEvent(control,'update', resize );
});
};

View File

@@ -0,0 +1,20 @@
/**
* Plugin: "no_active_items" (Tom Select)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
export default function(this:TomSelect) {
this.hook('instead','setActiveItem',() => {});
this.hook('instead','selectAll',() => {});
};

View File

@@ -0,0 +1,30 @@
/**
* Plugin: "input_autogrow" (Tom Select)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
export default function(this:TomSelect) {
var self = this;
var orig_deleteSelection = self.deleteSelection;
this.hook('instead','deleteSelection',(evt:KeyboardEvent) => {
if( self.activeItems.length ){
return orig_deleteSelection.call(self, evt);
}
return false;
});
};

View File

@@ -0,0 +1,25 @@
.#{$select-ns}-dropdown.plugin-optgroup_columns {
.ts-dropdown-content{
display: flex;
}
.optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
flex-grow: 1;
flex-basis: 0;
min-width: 0;
}
.optgroup:last-child {
border-right: 0 none;
}
.optgroup::before {
display: none;
}
.optgroup-header {
border-top: 0 none;
}
}

View File

@@ -0,0 +1,59 @@
/**
* Plugin: "optgroup_columns" (Tom Select.js)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import * as constants from '../../constants.ts';
import { parentMatch, nodeIndex } from '../../vanilla.ts';
export default function(this:TomSelect) {
var self = this;
var orig_keydown = self.onKeyDown;
self.hook('instead','onKeyDown',(evt:KeyboardEvent)=>{
var index, option, options, optgroup;
if( !self.isOpen || !(evt.keyCode === constants.KEY_LEFT || evt.keyCode === constants.KEY_RIGHT)) {
return orig_keydown.call(self,evt);
}
self.ignoreHover = true;
optgroup = parentMatch(self.activeOption,'[data-group]');
index = nodeIndex(self.activeOption,'[data-selectable]');
if( !optgroup ){
return;
}
if( evt.keyCode === constants.KEY_LEFT ){
optgroup = optgroup.previousSibling;
} else {
optgroup = optgroup.nextSibling;
}
if( !optgroup ){
return;
}
options = (<HTMLOptGroupElement>optgroup).querySelectorAll('[data-selectable]');
option = options[ Math.min(options.length - 1, index) ] as HTMLElement;
if( option ){
self.setActiveOption(option);
}
});
};

View File

@@ -0,0 +1,70 @@
.#{$select-ns}-wrapper.plugin-remove_button{
.item {
display: inline-flex;
align-items: center;
}
.item .remove {
color: inherit;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 0 $select-padding-item-x;
border-radius: 0 2px 2px 0;
box-sizing: border-box;
}
.item .remove:hover {
background: rgba(0, 0, 0, 5%);
}
&.disabled .item .remove:hover {
background: none;
}
.remove-single {
position: absolute;
right: 0;
top: 0;
font-size: 23px;
}
}
.#{$select-ns}-wrapper.plugin-remove_button:not(.rtl){
.item {
padding-right: 0 !important;
}
.item .remove {
border-left: 1px solid $select-color-item-border;
margin-left: $select-padding-item-x;
}
.item.active .remove {
border-left-color: $select-color-item-active-border;
}
&.disabled .item .remove {
border-left-color: lighten(desaturate($select-color-item-border, 100%), $select-lighten-disabled-item-border);
}
}
.#{$select-ns}-wrapper.plugin-remove_button.rtl {
.item {
padding-left: 0 !important;
}
.item .remove {
border-right: 1px solid $select-color-item-border;
margin-right: $select-padding-item-x;
}
.item.active .remove {
border-right-color: $select-color-item-active-border;
}
&.disabled .item .remove {
border-right-color: lighten(desaturate($select-color-item-border, 100%), $select-lighten-disabled-item-border);
}
}

View File

@@ -0,0 +1,78 @@
/**
* Plugin: "remove_button" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { getDom } from '../../vanilla.ts';
import { escape_html, preventDefault, addEvent } from '../../utils.ts';
import { TomOption, TomItem } from '../../types/index.ts';
import { RBOptions } from './types.ts';
export default function(this:TomSelect, userOptions:RBOptions) {
const options = Object.assign({
label : '&times;',
title : 'Remove',
className : 'remove',
append : true
}, userOptions);
//options.className = 'remove-single';
var self = this;
// override the render method to add remove button to each item
if( !options.append ){
return;
}
var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
self.hook('after','setupTemplates',() => {
var orig_render_item = self.settings.render.item;
self.settings.render.item = (data:TomOption, escape:typeof escape_html) => {
var item = getDom(orig_render_item.call(self, data, escape)) as TomItem;
var close_button = getDom(html);
item.appendChild(close_button);
addEvent(close_button,'mousedown',(evt) => {
preventDefault(evt,true);
});
addEvent(close_button,'click',(evt) => {
if( self.isLocked ) return;
// propagating will trigger the dropdown to show for single mode
preventDefault(evt,true);
if( self.isLocked ) return;
if( !self.shouldDelete([item],evt as MouseEvent) ) return;
self.removeItem(item);
self.refreshOptions(false);
self.inputState();
});
return item;
};
});
};

View File

@@ -0,0 +1,7 @@
export type RBOptions = {
label ?: string,
title ?: string,
className ?: string,
append ?: boolean
};

View File

@@ -0,0 +1,44 @@
/**
* Plugin: "restore_on_backspace" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { TomOption } from '../../types/index.ts';
type TPluginOptions = {
text?:(option:TomOption)=>string,
};
export default function(this:TomSelect, userOptions:TPluginOptions) {
const self = this;
const options = Object.assign({
text: (option:TomOption) => {
return option[self.settings.labelField];
}
},userOptions);
self.on('item_remove',function(value:string){
if( !self.isFocused ){
return;
}
if( self.control_input.value.trim() === '' ){
var option = self.options[value];
if( option ){
self.setTextboxValue(options.text.call(self, option));
}
}
});
};

View File

@@ -0,0 +1,219 @@
/**
* Plugin: "restore_on_backspace" (Tom Select)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
import type TomSelect from '../../tom-select.ts';
import { TomOption } from '../../types/index.ts';
import { addClasses } from '../../vanilla.ts';
export default function(this:TomSelect) {
const self = this;
const orig_canLoad = self.canLoad;
const orig_clearActiveOption = self.clearActiveOption;
const orig_loadCallback = self.loadCallback;
var pagination:{[key:string]:any} = {};
var dropdown_content:HTMLElement;
var loading_more = false;
var load_more_opt:HTMLElement;
var default_values: string[] = [];
if( !self.settings.shouldLoadMore ){
// return true if additional results should be loaded
self.settings.shouldLoadMore = ():boolean=>{
const scroll_percent = dropdown_content.clientHeight / (dropdown_content.scrollHeight - dropdown_content.scrollTop);
if( scroll_percent > 0.9 ){
return true;
}
if( self.activeOption ){
var selectable = self.selectable();
var index = Array.from(selectable).indexOf(self.activeOption);
if( index >= (selectable.length-2) ){
return true;
}
}
return false;
}
}
if( !self.settings.firstUrl ){
throw 'virtual_scroll plugin requires a firstUrl() method';
}
// in order for virtual scrolling to work,
// options need to be ordered the same way they're returned from the remote data source
self.settings.sortField = [{field:'$order'},{field:'$score'}];
// can we load more results for given query?
const canLoadMore = (query:string):boolean => {
if( typeof self.settings.maxOptions === 'number' && dropdown_content.children.length >= self.settings.maxOptions ){
return false;
}
if( (query in pagination) && pagination[query] ){
return true;
}
return false;
};
const clearFilter = (option:TomOption, value:string):boolean => {
if( self.items.indexOf(value) >= 0 || default_values.indexOf(value) >= 0 ){
return true;
}
return false;
};
// set the next url that will be
self.setNextUrl = (value:string,next_url:any):void => {
pagination[value] = next_url;
};
// getUrl() to be used in settings.load()
self.getUrl = (query:string):any =>{
if( query in pagination ){
const next_url = pagination[query];
pagination[query] = false;
return next_url;
}
// if the user goes back to a previous query
// we need to load the first page again
self.clearPagination();
return self.settings.firstUrl.call(self,query);
};
// clear pagination
self.clearPagination = ():void =>{
pagination = {};
};
// don't clear the active option (and cause unwanted dropdown scroll)
// while loading more results
self.hook('instead','clearActiveOption',()=>{
if( loading_more ){
return;
}
return orig_clearActiveOption.call(self);
});
// override the canLoad method
self.hook('instead','canLoad',(query:string)=>{
// first time the query has been seen
if( !(query in pagination) ){
return orig_canLoad.call(self,query);
}
return canLoadMore(query);
});
// wrap the load
self.hook('instead','loadCallback',( options:TomOption[], optgroups:TomOption[])=>{
if( !loading_more ){
self.clearOptions(clearFilter);
}else if( load_more_opt ){
const first_option = options[0];
if( first_option !== undefined ){
load_more_opt.dataset.value = first_option[self.settings.valueField];
}
}
orig_loadCallback.call( self, options, optgroups);
loading_more = false;
});
// add templates to dropdown
// loading_more if we have another url in the queue
// no_more_results if we don't have another url in the queue
self.hook('after','refreshOptions',()=>{
const query = self.lastValue;
var option;
if( canLoadMore(query) ){
option = self.render('loading_more',{query:query});
if( option ){
option.setAttribute('data-selectable',''); // so that navigating dropdown with [down] keypresses can navigate to this node
load_more_opt = option;
}
}else if( (query in pagination) && !dropdown_content.querySelector('.no-results') ){
option = self.render('no_more_results',{query:query});
}
if( option ){
addClasses(option,self.settings.optionClass);
dropdown_content.append( option );
}
});
// add scroll listener and default templates
self.on('initialize',()=>{
default_values = Object.keys(self.options);
dropdown_content = self.dropdown_content;
// default templates
self.settings.render = Object.assign({}, {
loading_more:() => {
return `<div class="loading-more-results">Loading more results ... </div>`;
},
no_more_results:() =>{
return `<div class="no-more-results">No more results</div>`;
}
},self.settings.render);
// watch dropdown content scroll position
dropdown_content.addEventListener('scroll',()=>{
if( !self.settings.shouldLoadMore.call(self) ){
return;
}
// !important: this will get checked again in load() but we still need to check here otherwise loading_more will be set to true
if( !canLoadMore(self.lastValue) ){
return;
}
// don't call load() too much
if( loading_more ) return;
loading_more = true;
self.load.call(self,self.lastValue);
});
});
};

View File

@@ -0,0 +1,4 @@
@import "../../node_modules/bootstrap/scss/functions";
@import "../../node_modules/bootstrap/scss/variables";
@import "../../node_modules/bootstrap/scss/mixins";
@import 'tom-select.bootstrap4';

View File

@@ -0,0 +1,4 @@
@import "../../node_modules/bootstrap5/scss/functions";
@import "../../node_modules/bootstrap5/scss/variables";
@import "../../node_modules/bootstrap5/scss/mixins";
@import 'tom-select.bootstrap5';

99
node_modules/tom-select/src/scss/_dropdown.scss generated vendored Normal file
View File

@@ -0,0 +1,99 @@
.#{$select-ns}-dropdown {
position: absolute;
top: 100%;
left: 0;
width: 100%;
z-index: 10;
border: $select-dropdown-border;
background: $select-color-dropdown;
margin: 0.25rem 0 0;
border-top: 0 none;
box-sizing: border-box;
box-shadow: 0 1px 3px rgba(0, 0, 0, 10%);
border-radius: 0 0 $select-border-radius $select-border-radius;
[data-selectable] {
cursor: pointer;
overflow: hidden;
.highlight {
background: $select-color-highlight;
border-radius: 1px;
}
}
.option,
.optgroup-header,
.no-results,
.create {
padding: $select-padding-dropdown-item-y $select-padding-dropdown-item-x;
}
.option, [data-disabled], [data-disabled] [data-selectable].option {
cursor: inherit;
opacity: 0.5;
}
[data-selectable].option {
opacity: 1;
cursor: pointer;
}
.optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.optgroup-header {
color: $select-color-optgroup-text;
background: $select-color-optgroup;
cursor: default;
}
.active {
background-color: $select-color-dropdown-item-active;
color: $select-color-dropdown-item-active-text;
&.create {
color: $select-color-dropdown-item-create-active-text;
}
}
.create {
color: $select-color-dropdown-item-create-text;
}
.spinner{
display: inline-block;
width: $select-spinner-size;
height: $select-spinner-size;
margin: $select-padding-dropdown-item-y $select-padding-dropdown-item-x;
&::after {
content: " ";
display: block;
width: $select-spinner-size * .8;
height: $select-spinner-size * .8;
margin: $select-spinner-size * .1;
border-radius: 50%;
border: $select-spinner-border-size solid $select-spinner-border-color;
border-color: $select-spinner-border-color transparent $select-spinner-border-color transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}
}
.#{$select-ns}-dropdown-content {
overflow: hidden auto;
max-height: $select-max-height-dropdown;
scroll-behavior: smooth;
}

113
node_modules/tom-select/src/scss/_items.scss generated vendored Normal file
View File

@@ -0,0 +1,113 @@
.#{$select-ns}-control {
border: $select-border;
padding: $select-padding-y $select-padding-x;
width: 100%;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
box-shadow: $select-shadow-input;
border-radius: $select-border-radius;
display: flex;
flex-wrap: wrap;
.#{$select-ns}-wrapper.multi.has-items & {
$padding-x: $select-padding-x;
$padding-top: calc( #{$select-padding-y} - #{$select-padding-item-y} - #{$select-width-item-border});
$padding-bottom: calc( #{$select-padding-y} - #{$select-padding-item-y} - #{$select-margin-item-y} - #{$select-width-item-border});
padding: $padding-top $padding-x $padding-bottom;
}
.full & {
background-color: $select-color-input-full;
}
.disabled &,
.disabled & * {
cursor: default !important;
}
.focus & {
box-shadow: $select-shadow-input-focus;
}
> * {
vertical-align: baseline;
display: inline-block;
}
.#{$select-ns}-wrapper.multi & > div {
cursor: pointer;
margin: 0 $select-margin-item-x $select-margin-item-y 0;
padding: $select-padding-item-y $select-padding-item-x;
background: $select-color-item;
color: $select-color-item-text;
border: $select-width-item-border solid $select-color-item-border;
&.active {
background: $select-color-item-active;
color: $select-color-item-active-text;
border: $select-width-item-border solid $select-color-item-active-border;
}
}
.#{$select-ns}-wrapper.multi.disabled & > div {
&, &.active {
color: lighten(desaturate($select-color-item-text, 100%), $select-lighten-disabled-item-text);
background: lighten(desaturate($select-color-item, 100%), $select-lighten-disabled-item);
border: $select-width-item-border solid lighten(desaturate($select-color-item-border, 100%), $select-lighten-disabled-item-border);
}
}
> input {
flex: 1 1 auto;
min-width: 7rem;
display: inline-block !important;
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
margin: 0 !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
user-select: auto !important;
box-shadow: none !important;
&::-ms-clear {
display: none;
}
&:focus { outline: none !important; }
}
.has-items & > input{
margin: $select-caret-margin !important;
}
&.rtl {
text-align: right;
&.single .#{$select-ns}-control:after {
left: $select-arrow-offset;
right: auto;
}
.#{$select-ns}-control > input {
margin: $select-caret-margin-rtl !important;
}
}
.disabled & {
opacity: $select-opacity-disabled;
background-color: $select-color-disabled;
}
// hide input, while retaining its focus, and maintain layout so users can still click on the space to bring the display back
// visibility:hidden can prevent the input from receiving focus
.input-hidden & > input {
opacity: 0;
position: absolute;
left: -10000px;
}
}

View File

@@ -0,0 +1,213 @@
/**
* Tom Select Bootstrap 4
*/
// Import Bootstrap 4 functions and variables
$state-valid: map-get($form-validation-states,'valid') !default;
$state-invalid: map-get($form-validation-states,'invalid') !default;
$enable-shadows: true !default;
$select-font-family: inherit !default;
$select-font-size: inherit !default;
$select-line-height: $input-btn-line-height !default; // formerly line-height-computed
$select-color-text: gray("800") !default; // $gray-800
$select-color-highlight: rgba(255, 237, 40, 40%) !default;
$select-color-input: $input-bg !default;
$select-color-input-full: $input-bg !default;
$select-color-input-error: map-get($state-invalid,'color') !default;
$select-color-input-error-focus: darken($select-color-input-error, 10%) !default;
$select-color-disabled: $input-disabled-bg !default;
$select-color-item: #efefef !default;
$select-color-item-border: $border-color !default;
$select-color-item-active: $component-active-bg !default;
$select-color-item-active-text: #fff !default;
$select-color-item-active-border: rgba(0, 0, 0, 0%) !default;
$select-color-optgroup: $dropdown-bg !default;
$select-color-optgroup-text: $dropdown-header-color !default;
$select-color-optgroup-border: $dropdown-divider-bg !default;
$select-color-dropdown: $dropdown-bg !default;
$select-color-dropdown-border-top: mix($input-border-color, $input-bg, 80%) !default;
$select-color-dropdown-item-active: $dropdown-link-hover-bg !default;
$select-color-dropdown-item-active-text: $dropdown-link-hover-color !default;
$select-color-dropdown-item-create-active-text: $dropdown-link-hover-color !default;
$select-opacity-disabled: 0.5 !default;
$select-border: 1px solid $input-border-color !default;
$select-border-radius: $input-border-radius !default;
$select-width-item-border: 0 !default;
$select-padding-x: $input-btn-padding-x !default;
$select-padding-y: $input-btn-padding-y !default;
$select-padding-dropdown-item-x: $input-btn-padding-x !default;
$select-padding-dropdown-item-y: 3px !default;
$select-padding-item-x: 5px !default;
$select-padding-item-y: 1px !default;
$select-margin-item-x: 3px !default;
$select-margin-item-y: 3px !default;
$select-arrow-size: 5px !default;
$select-arrow-color: $select-color-text !default;
$select-arrow-offset: calc(#{$select-padding-x} + 5px) !default;
@import "tom-select";
@include ts-caret;
.#{$select-ns}-wrapper.form-control,
.#{$select-ns}-wrapper.form-select {
padding:0 !important;
}
.#{$select-ns}-dropdown,
.#{$select-ns}-dropdown.form-control {
height: auto;
padding: 0;
z-index: $zindex-dropdown;
background: $select-color-dropdown;
border: 1px solid $dropdown-border-color; // $dropdown-fallback-border
border-radius: $border-radius;
box-shadow: 0 6px 12px rgba(0, 0, 0, 17.5%);
}
.#{$select-ns}-dropdown {
.optgroup-header {
font-size: $font-size-sm;
line-height: $line-height-base;
}
.optgroup:first-child::before {
display: none;
}
.optgroup::before {
content: ' ';
display: block;
height: 0;
margin: $dropdown-divider-margin-y 0;
overflow: hidden;
border-top: 1px solid $dropdown-divider-bg;
margin-left: $select-padding-dropdown-item-x * -1;
margin-right: $select-padding-dropdown-item-x * -1;
}
.create {
padding-left: $select-padding-dropdown-item-x;
}
}
.#{$select-ns}-dropdown-content {
padding: 5px 0;
}
.#{$select-ns}-control {
min-height: $input-height;
@include box-shadow($input-box-shadow);
@include transition($input-transition);
display:flex;
align-items: center;
.focus & {
border-color: $input-focus-border-color;
outline: 0;
@if $enable-shadows {
box-shadow: $input-box-shadow, $input-focus-box-shadow;
} @else {
box-shadow: $input-focus-box-shadow;
}
}
}
.is-invalid .#{$select-ns}-control,
.was-validated .invalid .#{$select-ns}-control{
border-color: $select-color-input-error;
.focus & {
border-color: $select-color-input-error-focus;
box-shadow: 0 0 0 $input-focus-width rgba($select-color-input-error, .25);
}
}
.is-valid .#{$select-ns}-control {
$_color: map-get($state-valid,'color');
border-color: $_color;
.focus & {
border-color: $_color;
box-shadow: 0 0 0 $input-focus-width rgba($_color, .25);
}
}
.#{$select-ns}-wrapper {
.input-group-sm > &,
&.form-control-sm {
.#{$select-ns}-control {
min-height: $input-height-sm;
padding: 0 .75rem;
// padding: $input-padding-y-sm $input-padding-x-sm;
@include border-radius($input-border-radius-sm);
@include font-size($input-font-size-sm);
}
&.has-items .#{$select-ns}-control {
min-height: $input-height-sm !important;
font-size: $input-font-size-sm;
padding-bottom: 0;
}
}
.input-group-sm > &.multi.has-items,
&.form-control-sm.multi.has-items {
.#{$select-ns}-control {
// padding-top = ($input-height-sm - border-width - item-height) / 2;
// item-height = ($select-line-height * $input-font-size-sm) + ($select-padding-item-y * 2)
$border-and-padding: add($input-border-width,$select-padding-item-y) * 2;
$ts-select-padding-sm: calc( (#{$input-height-sm} - (#{$select-line-height} * #{$input-font-size-sm}) - #{$border-and-padding})/2);
padding-top: $ts-select-padding-sm !important;
}
}
&.multi {
&.has-items .#{$select-ns}-control {
padding-left: calc(#{$select-padding-x} - #{$select-padding-item-x});
--ts-pr-min: calc(#{$select-padding-x} - #{$select-padding-item-x});
}
.#{$select-ns}-control > div {
border-radius: calc(#{$select-border-radius} - 1px);
}
}
.input-group-lg > & >,
&.form-control-lg {
.#{$select-ns}-control {
min-height: $input-height-lg;
@include border-radius($input-border-radius-lg);
@include font-size($input-font-size-lg);
}
}
}
.form-control.#{$select-ns}-wrapper {
padding: 0;
height: auto;
border: none;
background: none;
border-radius: 0;
}
.input-group {
& > .#{$select-ns}-wrapper {
flex-grow: 1;
}
& > .#{$select-ns}-wrapper:not(:nth-child(2)) > .#{$select-ns}-control {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
& > .#{$select-ns}-wrapper:not(:last-child) > .#{$select-ns}-control {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}

View File

@@ -0,0 +1,258 @@
/**
* Tom Select Bootstrap 5
*/
// Import Bootstrap 5 functions and variables
$state-valid: map-get($form-validation-states,'valid') !default;
$state-invalid: map-get($form-validation-states,'invalid') !default;
$enable-shadows: true !default;
$select-font-family: inherit !default;
$select-font-size: inherit !default;
$select-line-height: $input-btn-line-height !default; // formerly line-height-computed
$select-color-text: $gray-800 !default;
$select-color-highlight: rgba(255, 237, 40, 40%) !default;
$select-color-input: $input-bg !default;
$select-color-input-full: $input-bg !default;
$select-color-disabled: $input-disabled-bg !default;
$select-color-item: #efefef !default;
$select-color-item-border: $border-color !default;
$select-color-item-active: $component-active-bg !default;
$select-color-item-active-text: #fff !default;
$select-color-item-active-border: rgba(0, 0, 0, 0%) !default;
$select-color-optgroup: $dropdown-bg !default;
$select-color-optgroup-text: $dropdown-header-color !default;
$select-color-optgroup-border: $dropdown-divider-bg !default;
$select-color-dropdown: $dropdown-bg !default;
$select-color-dropdown-border-top: color-mix($input-border-color, $input-bg, 80%) !default;
$select-color-dropdown-item-active: $dropdown-link-hover-bg !default;
$select-color-dropdown-item-active-text: $dropdown-link-hover-color !default;
$select-color-dropdown-item-create-active-text: $dropdown-link-hover-color !default;
$select-opacity-disabled: 0.5 !default;
$select-border: 1px solid $input-border-color !default;
$select-border-radius: $input-border-radius !default;
$select-width-item-border: 0 !default;
$select-padding-x: $input-padding-x !default;
$select-padding-y: $input-padding-y !default;
$select-padding-dropdown-item-x: $input-btn-padding-x !default;
$select-padding-dropdown-item-y: 3px !default;
$select-padding-item-x: 5px !default;
$select-padding-item-y: 1px !default;
$select-margin-item-x: 3px !default;
$select-margin-item-y: 3px !default;
$select-arrow-size: 5px !default;
$select-arrow-color: $select-color-text !default;
$select-arrow-offset: calc(#{$select-padding-x} + 5px) !default;
@import "tom-select";
@mixin ts-form-validation-state-selector($state) {
$state-map: map-get($form-validation-states,$state);
.#{$select-ns}-wrapper.is-#{$state},
.was-validated .#{$state},
.was-validated :#{$state} + .#{$select-ns}-wrapper {
$color: map-get($state-map,'color');
$icon: map-get($state-map,'icon');
border-color: $color;
&:not(.single) {
background-image: escape-svg($icon);
background-position: right $input-height-inner-quarter center;
background-size: $input-height-inner-half $input-height-inner-half;
background-repeat: no-repeat;
}
&.single {
background-image: escape-svg($form-select-indicator), escape-svg($icon);
background-position: $form-select-bg-position, $form-select-feedback-icon-position;
background-size: $form-select-bg-size, $form-select-feedback-icon-size;
background-repeat: no-repeat;
}
&.focus .#{$select-ns}-control {
border-color: $color;
box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity);
}
}
}
.#{$select-ns}-dropdown,
.#{$select-ns}-dropdown.form-control,
.#{$select-ns}-dropdown.form-select {
height: auto;
padding: 0;
z-index: $zindex-dropdown;
background: $select-color-dropdown;
border: 1px solid $dropdown-border-color; // $dropdown-fallback-border
border-radius: $border-radius;
box-shadow: 0 6px 12px rgba(0, 0, 0, 17.5%);
}
.#{$select-ns}-dropdown {
.optgroup-header {
font-size: $font-size-sm;
line-height: $line-height-base;
}
.optgroup:first-child::before {
display: none;
}
.optgroup::before {
content: ' ';
display: block;
height: 0;
margin: $dropdown-divider-margin-y 0;
overflow: hidden;
border-top: 1px solid $dropdown-divider-bg;
margin-left: $select-padding-dropdown-item-x * -1;
margin-right: $select-padding-dropdown-item-x * -1;
}
.create {
padding-left: $select-padding-dropdown-item-x;
}
}
.#{$select-ns}-dropdown-content {
padding: 5px 0;
}
.#{$select-ns}-control {
@include box-shadow($input-box-shadow);
@include transition($input-transition);
display:flex;
align-items: center;
.focus & {
border-color: $input-focus-border-color;
outline: 0;
@if $enable-shadows {
box-shadow: $input-box-shadow, $input-focus-box-shadow;
} @else {
box-shadow: $input-focus-box-shadow;
}
}
.item {
display: flex;
align-items: center;
}
}
@include ts-form-validation-state-selector('invalid');
@include ts-form-validation-state-selector('valid');
.#{$select-ns}-wrapper {
min-height: $input-height;
display:flex;
.input-group-sm > &,
&.form-select-sm,
&.form-control-sm {
min-height: $input-height-sm;
.#{$select-ns}-control {
@include border-radius($input-border-radius-sm);
@include font-size($input-font-size-sm);
}
&.has-items .#{$select-ns}-control {
font-size: $input-font-size-sm;
padding-bottom: 0;
}
}
.input-group-sm > &.multi.has-items,
&.form-select-sm.multi.has-items,
&.form-control-sm.multi.has-items {
.#{$select-ns}-control {
// padding-top = ($input-height-sm - border-width - item-height) / 2;
// item-height = ($select-line-height * $input-font-size-sm) + ($select-padding-item-y * 2)
$border-and-padding: calc(($input-border-width + $select-padding-item-y) * 2);
$ts-select-padding-sm: calc((#{$input-height-sm} - (#{$select-line-height} * #{$input-font-size-sm}) - #{$border-and-padding})/2);
padding-top: $ts-select-padding-sm !important;
}
}
&.multi {
&.has-items .#{$select-ns}-control {
padding-left: calc(#{$select-padding-x} - #{$select-padding-item-x});
--ts-pr-min: calc(#{$select-padding-x} - #{$select-padding-item-x});
}
.#{$select-ns}-control > div {
border-radius: calc(#{$select-border-radius} - 1px);
}
}
.input-group-lg > &,
&.form-control-lg,
&.form-select-lg {
min-height: $input-height-lg;
.#{$select-ns}-control{
@include border-radius($input-border-radius-lg);
@include font-size($input-font-size-lg);
}
}
&:not(.form-control, .form-select) {
padding: 0;
border: none;
height: auto;
box-shadow: none;
background: none;
&.single .#{$select-ns}-control {
background-image: escape-svg($form-select-indicator);
background-repeat: no-repeat;
background-position: $form-select-bg-position;
background-size: $form-select-bg-size;
}
}
&.form-select,
&.single {
--ts-pr-caret: #{$form-select-indicator-padding};
}
&.form-control,
&.form-select {
padding:0 !important;
height: auto;
box-shadow: none;
display: flex;
.#{$select-ns}-control,
&.single.input-active .#{$select-ns}-control {
border: none !important;
}
&:not(.disabled) .#{$select-ns}-control,
&:not(.disabled).single.input-active .#{$select-ns}-control {
background: transparent !important; // let the background of .form-select show through
}
}
}
.input-group{
& > .#{$select-ns}-wrapper {
flex-grow: 1;
width: 1%;
}
& > .#{$select-ns}-wrapper:not(:nth-child(2)) > .#{$select-ns}-control {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
& > .#{$select-ns}-wrapper:not(:last-child) > .#{$select-ns}-control {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}

View File

@@ -0,0 +1,89 @@
$select-color-item: #1da7ee;
$select-color-item-text: #fff;
$select-color-item-active-text: #fff;
$select-color-item-border: #0073bb;
$select-color-item-active: #92c836;
$select-color-item-active-border: #00578d;
$select-width-item-border: 1px;
$select-shadow-input: inset 0 1px 1px rgba(0, 0, 0, 10%) !default;
$select-shadow-input-focus: inset 0 1px 2px rgba(0, 0, 0, 15%) !default;
@import "tom-select";
@include ts-caret;
.#{$select-ns}-wrapper {
display:flex;
min-height:$select-line-height + ($select-padding-y*2) + ($select-border-width *2);
&.multi {
&.has-items .#{$select-ns}-control {
$padding-x: $select-padding-x - 3px;
padding-left: $padding-x;
--ts-pr-min: $padding-x;
}
.#{$select-ns}-control {
[data-value] {
text-shadow: 0 1px 0 rgba(0, 51, 83, 30%);
border-radius: 3px;
@include selectize-vertical-gradient(#1da7ee, #178ee9);
box-shadow: 0 1px 0 rgba(0, 0, 0, 20%),inset 0 1px rgba(255, 255, 255, 3%);
&.active {
@include selectize-vertical-gradient(#008fd8, #0075cf);
}
}
}
&.disabled .#{$select-ns}-control [data-value] {
color: #999;
text-shadow: none;
background: none;
box-shadow: none;
&, .remove {
border-color: #e6e6e6;
}
.remove {
background: none;
}
}
}
&.single {
.#{$select-ns}-control {
box-shadow: 0 1px 0 rgba(0, 0, 0, 5%), inset 0 1px 0 rgba(255, 255, 255, 80%);
@include selectize-vertical-gradient(#fefefe, #f2f2f2);
}
}
}
.#{$select-ns}-wrapper.single .#{$select-ns}-control, .#{$select-ns}-dropdown.single {
border-color: #b8b8b8;
}
.#{$select-ns}-control {
.dropdown-active & {
border-radius: $select-border-radius $select-border-radius 0 0;
}
}
.#{$select-ns}-dropdown {
.optgroup-header {
padding-top: $select-padding-dropdown-item-y + 2px;
font-weight: bold;
font-size: 0.85em;
}
.optgroup {
border-top: 1px solid $select-color-dropdown-border-top;
&:first-child {
border-top: 0 none;
}
}
}

179
node_modules/tom-select/src/scss/tom-select.scss generated vendored Normal file
View File

@@ -0,0 +1,179 @@
/**
* tom-select.css (v//@@version)
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
*/
// base styles
$select-ns: 'ts' !default;
$select-font-family: inherit !default;
$select-font-size: 13px !default;
$select-line-height: 18px !default;
$select-color-text: #303030 !default;
$select-color-border: #d0d0d0 !default;
$select-color-highlight: rgba(125, 168, 208, 20%) !default;
$select-color-input: #fff !default;
$select-color-input-full: $select-color-input !default;
$select-color-disabled: #fafafa !default;
$select-color-item: #f2f2f2 !default;
$select-color-item-text: $select-color-text !default;
$select-color-item-border: #d0d0d0 !default;
$select-color-item-active: #e8e8e8 !default;
$select-color-item-active-text: $select-color-text !default;
$select-color-item-active-border: #cacaca !default;
$select-color-dropdown: #fff !default;
$select-color-dropdown-border: $select-color-border !default;
$select-color-dropdown-border-top: #f0f0f0 !default;
$select-color-dropdown-item-active: #f5fafd !default;
$select-color-dropdown-item-active-text: #495c68 !default;
$select-color-dropdown-item-create-text: rgba(red($select-color-text), green($select-color-text), blue($select-color-text), 50%) !default;
$select-color-dropdown-item-create-active-text: $select-color-dropdown-item-active-text !default;
$select-color-optgroup: $select-color-dropdown !default;
$select-color-optgroup-text: $select-color-text !default;
$select-lighten-disabled-item: 30% !default;
$select-lighten-disabled-item-text: 30% !default;
$select-lighten-disabled-item-border: 30% !default;
$select-opacity-disabled: 0.5 !default;
$select-shadow-input: none !default;
$select-shadow-input-focus: none !default;
$select-border-width: 1px !default;
$select-border: $select-border-width solid $select-color-border !default;
$select-dropdown-border: 1px solid $select-color-dropdown-border !default;
$select-border-radius: 3px !default;
$select-width-item-border: 0 !default;
$select-max-height-dropdown: 200px !default;
$select-padding-x: 8px !default;
$select-padding-y: 8px !default;
$select-padding-item-x: 6px !default;
$select-padding-item-y: 2px !default;
$select-padding-dropdown-item-x: $select-padding-x !default;
$select-padding-dropdown-item-y: 5px !default;
$select-margin-item-x: 3px !default;
$select-margin-item-y: 3px !default;
$select-arrow-size: 5px !default;
$select-arrow-color: #808080 !default;
$select-arrow-offset: 15px !default;
$select-caret-margin: 0 4px !default;
$select-caret-margin-rtl: 0 4px 0 -2px !default;
$select-spinner-size: 30px !default;
$select-spinner-border-size: 5px !default;
$select-spinner-border-color: $select-color-border !default;
@import 'items';
@import 'dropdown';
@import "../plugins/drag_drop/plugin.scss";
@import "../plugins/checkbox_options/plugin.scss";
@import "../plugins/clear_button/plugin.scss";
@import "../plugins/dropdown_header/plugin.scss";
@import "../plugins/dropdown_input/plugin.scss";
@import "../plugins/input_autogrow/plugin.scss";
@import "../plugins/optgroup_columns/plugin.scss";
@import "../plugins/remove_button/plugin.scss";
:root {
--ts-pr-clear-button: 0px;
--ts-pr-caret: 0px;
--ts-pr-min: .75rem;
}
@mixin selectize-vertical-gradient($color-top, $color-bottom) {
background-color: color-mix($color-top, $color-bottom, 60%);
background-image: linear-gradient(to bottom, $color-top, $color-bottom);
background-repeat: repeat-x;
}
.#{$select-ns}-wrapper.single {
.#{$select-ns}-control {
&, input {
cursor: pointer;
}
}
}
.#{$select-ns}-control:not(.rtl) {
padding-right: max( var(--ts-pr-min), calc( var(--ts-pr-clear-button) + var(--ts-pr-caret)) ) !important;
}
.#{$select-ns}-control.rtl {
padding-left: max( var(--ts-pr-min), calc( var(--ts-pr-clear-button) + var(--ts-pr-caret)) ) !important;
}
@mixin ts-caret() {
.#{$select-ns}-wrapper.single {
.#{$select-ns}-control {
--ts-pr-caret: 2rem;
&::after {
content: ' ';
display: block;
position: absolute;
top: 50%;
margin-top: round(-0.5 * $select-arrow-size);
width: 0;
height: 0;
border-style: solid;
border-width: $select-arrow-size $select-arrow-size 0 $select-arrow-size;
border-color: $select-arrow-color transparent transparent transparent;
}
&:not(.rtl)::after {
right: $select-arrow-offset;
}
&.rtl::after {
left: $select-arrow-offset;
}
}
&.dropdown-active .#{$select-ns}-control::after {
margin-top: $select-arrow-size * -0.8;
border-width: 0 $select-arrow-size $select-arrow-size $select-arrow-size;
border-color: transparent transparent $select-arrow-color transparent;
}
&.input-active .#{$select-ns}-control,
&.input-active .#{$select-ns}-control input {
cursor: text;
}
}
}
.#{$select-ns}-wrapper {
position: relative;
}
.#{$select-ns}-dropdown,
.#{$select-ns}-control,
.#{$select-ns}-control input {
color: $select-color-text;
font-family: $select-font-family;
font-size: $select-font-size;
line-height: $select-line-height;
}
.#{$select-ns}-control,
.#{$select-ns}-wrapper.single.input-active .#{$select-ns}-control {
background: $select-color-input;
cursor: text;
}
.ts-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
clip-path: inset(50%) !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important;
}

33
node_modules/tom-select/src/tom-select.complete.ts generated vendored Normal file
View File

@@ -0,0 +1,33 @@
import TomSelect from './tom-select.ts';
import change_listener from './plugins/change_listener/plugin.ts';
import checkbox_options from './plugins/checkbox_options/plugin.ts';
import clear_button from './plugins/clear_button/plugin.ts';
import drag_drop from './plugins/drag_drop/plugin.ts';
import dropdown_header from './plugins/dropdown_header/plugin.ts';
import caret_position from './plugins/caret_position/plugin.ts';
import dropdown_input from './plugins/dropdown_input/plugin.ts';
import input_autogrow from './plugins/input_autogrow/plugin.ts';
import no_backspace_delete from './plugins/no_backspace_delete/plugin.ts';
import no_active_items from './plugins/no_active_items/plugin.ts';
import optgroup_columns from './plugins/optgroup_columns/plugin.ts';
import remove_button from './plugins/remove_button/plugin.ts';
import restore_on_backspace from './plugins/restore_on_backspace/plugin.ts';
import virtual_scroll from './plugins/virtual_scroll/plugin.ts';
TomSelect.define('change_listener', change_listener);
TomSelect.define('checkbox_options', checkbox_options);
TomSelect.define('clear_button', clear_button);
TomSelect.define('drag_drop', drag_drop);
TomSelect.define('dropdown_header', dropdown_header);
TomSelect.define('caret_position', caret_position);
TomSelect.define('dropdown_input', dropdown_input);
TomSelect.define('input_autogrow', input_autogrow);
TomSelect.define('no_backspace_delete', no_backspace_delete);
TomSelect.define('no_active_items', no_active_items);
TomSelect.define('optgroup_columns', optgroup_columns);
TomSelect.define('remove_button', remove_button);
TomSelect.define('restore_on_backspace', restore_on_backspace);
TomSelect.define('virtual_scroll', virtual_scroll);
export default TomSelect;

15
node_modules/tom-select/src/tom-select.popular.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import TomSelect from './tom-select.ts';
import caret_position from './plugins/caret_position/plugin.ts';
import dropdown_input from './plugins/dropdown_input/plugin.ts';
import no_backspace_delete from './plugins/no_backspace_delete/plugin.ts';
import remove_button from './plugins/remove_button/plugin.ts';
import restore_on_backspace from './plugins/restore_on_backspace/plugin.ts';
TomSelect.define('caret_position', caret_position);
TomSelect.define('dropdown_input', dropdown_input);
TomSelect.define('no_backspace_delete', no_backspace_delete);
TomSelect.define('remove_button', remove_button);
TomSelect.define('restore_on_backspace', restore_on_backspace);
export default TomSelect;

2782
node_modules/tom-select/src/tom-select.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

66
node_modules/tom-select/src/types/core.ts generated vendored Normal file
View File

@@ -0,0 +1,66 @@
import type TomSelect from '../tom-select.ts';
import { escape_html } from '../utils.ts';
export interface TomInput extends HTMLElement{
tomselect ?: TomSelect;
disabled : boolean;
readOnly ?: boolean;
required : boolean;
value : string;
type : string;
validity : ValidityState;
}
export type TomArgObject = {
silent?: boolean,
}
export type TomOption = {[key:string]:any}
export type TomOptions = {[key: string]: TomOption };
export type TomCreateFilter = (input:string) => boolean;
export type TomCreateCallback = (data?:TomOption)=>void;
export type TomCreate = (input:string,create:TomCreateCallback) => boolean;
export interface TomItem extends HTMLElement{
dataset:{
value: string;
}
}
export type TomLoadCallback = TomSelect['loadCallback'];
export type TomTemplate = (data:TomOption, escape:typeof escape_html) => string|HTMLElement;
export type TomTemplateNull = (data:TomOption, escape:typeof escape_html) => null|string|HTMLElement;
export type TomTemplates = {
'dropdown' : TomTemplate,
'optgroup' : TomTemplate,
'optgroup_header' : TomTemplate,
'option' : TomTemplate,
'item' : TomTemplate,
'option_create' : TomTemplate,
'no_results' : TomTemplate,
'loading' : TomTemplate,
'not_loading' : TomTemplateNull,
'loading_more' : TomTemplateNull,
'no_more_results' : TomTemplateNull,
}
export type TomTemplateNames = keyof TomTemplates;
export type TomClearFilter = (option:TomOption,value:string) => boolean;
// https://stackoverflow.com/questions/41980195/recursive-partialt-in-typescript
export type RecursivePartial<T> = {
[P in keyof T]?:
T[P] extends (infer U)[] ? RecursivePartial<U>[] :
T[P] extends object | undefined ? RecursivePartial<T[P]> :
T[P];
};

3
node_modules/tom-select/src/types/index.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export * from './core.ts';
export * from './settings.ts';

96
node_modules/tom-select/src/types/settings.ts generated vendored Normal file
View File

@@ -0,0 +1,96 @@
import { TomCreateFilter, TomCreate, TomLoadCallback, TomTemplates, TomOption } from './index.ts';
import { TPluginItem, TPluginHash } from '../contrib/microplugin.ts';
import { type Sort as SifterSort, type SortFn as SifterSortFn } from '@orchidjs/sifter';
export type TomSettings = {
options ?: any[],
optgroups ?: any[],
items ?: string[],
plugins : string[]|TPluginItem[]|TPluginHash,
delimiter : string,
splitOn : RegExp|string, // regexp or string for splitting up values from a paste command
persist : boolean,
diacritics : boolean,
create : boolean|TomCreate,
createOnBlur : boolean,
createFilter : RegExp|string|TomCreateFilter,
highlight : boolean,
openOnFocus : boolean,
shouldOpen : boolean,
maxOptions : number,
maxItems : null|number,
hideSelected : boolean,
duplicates : boolean,
addPrecedence : boolean,
selectOnTab : boolean,
preload : boolean|string,
allowEmptyOption : boolean,
closeAfterSelect : boolean,
refreshThrottle : number,
loadThrottle : number,
loadingClass : string,
dataAttr : string, //'data-data',
optgroupField : string,
valueField : string,
labelField : string,
disabledField : string,
optgroupLabelField : string,
optgroupValueField : string,
lockOptgroupOrder : boolean,
sortField : string|SifterSort[]|SifterSortFn,
searchField : string[],
searchConjunction : string,
nesting : boolean,
mode : string,
wrapperClass : string,
controlClass : string,
dropdownClass : string,
dropdownContentClass : string,
itemClass : string,
optionClass : string,
dropdownParent : string,
controlInput : string|HTMLInputElement,
copyClassesToDropdown : boolean,
placeholder : string,
hidePlaceholder : boolean,
load : (value:string, callback:TomLoadCallback) => void,
score ?: (query:string) => () => any,
shouldLoad : (query:string) => boolean,
onInitialize : () => void,
onChange : (value:string|number) => void,
onItemAdd : (value:string|number,item:HTMLDivElement) => void,
onItemRemove : (value:string|number,item:HTMLDivElement) => void,
onClear : () => void,
onOptionAdd : (value:string|number,data:TomOption) => void,
onOptionRemove : (value:string|number) => void,
onOptionClear : () => void,
onOptionGroupAdd : (value:string|number,data:TomOption) => void,
onOptionGroupRemove : (value:string|number) => void,
onOptionGroupClear : () => void,
onDropdownOpen : (dropdown:HTMLDivElement) => void,
onDropdownClose : (dropdown:HTMLDivElement) => void,
onType : (str:string) => void,
onLoad : (options:TomOption[],optgroups:TomOption[]) => void,
onFocus : () => void,
onBlur : () => void,
onDelete : (values:string[], evt:KeyboardEvent|MouseEvent) => boolean,
render : TomTemplates,
// virtual scroll plugin
firstUrl : (query:string)=>any
shouldLoadMore : () => boolean,
};

230
node_modules/tom-select/src/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,230 @@
import type TomSelect from './tom-select.ts';
import { TomLoadCallback } from './types/index.ts';
/**
* Converts a scalar to its best string representation
* for hash keys and HTML attribute values.
*
* Transformations:
* 'str' -> 'str'
* null -> ''
* undefined -> ''
* true -> '1'
* false -> '0'
* 0 -> '0'
* 1 -> '1'
*
*/
export const hash_key = (value:undefined|null|boolean|string|number):string|null => {
if (typeof value === 'undefined' || value === null) return null;
return get_hash(value);
};
export const get_hash = (value:boolean|string|number):string => {
if (typeof value === 'boolean') return value ? '1' : '0';
return value + '';
};
/**
* Escapes a string for use within HTML.
*
*/
export const escape_html = (str:string):string => {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
/**
* use setTimeout if timeout > 0
*/
export const timeout = (fn:()=>void,timeout:number): number | null => {
if( timeout > 0 ){
return window.setTimeout(fn,timeout);
}
fn.call(null);
return null;
}
/**
* Debounce the user provided load function
*
*/
export const loadDebounce = (fn:(value:string,callback:TomLoadCallback) => void,delay:number) => {
var timeout: null|ReturnType<typeof setTimeout>;
return function(this:TomSelect, value:string,callback:TomLoadCallback) {
var self = this;
if( timeout ){
self.loading = Math.max(self.loading - 1, 0);
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = null;
self.loadedSearches[value] = true;
fn.call(self, value, callback);
}, delay);
};
};
/**
* Debounce all fired events types listed in `types`
* while executing the provided `fn`.
*
*/
export const debounce_events = ( self:TomSelect, types:string[], fn:() => void ) => {
var type:string;
var trigger = self.trigger;
var event_args:{ [key: string]: any } = {};
// override trigger method
self.trigger = function(){
var type = arguments[0];
if (types.indexOf(type) !== -1) {
event_args[type] = arguments;
} else {
return trigger.apply(self, arguments);
}
};
// invoke provided function
fn.apply(self, []);
self.trigger = trigger;
// trigger queued events
for( type of types ){
if( type in event_args ){
trigger.apply(self, event_args[type]);
}
}
};
/**
* Determines the current selection within a text input control.
* Returns an object containing:
* - start
* - length
*
* Note: "selectionStart, selectionEnd ... apply only to inputs of types text, search, URL, tel and password"
* - https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
*/
export const getSelection = (input:HTMLInputElement):{ start: number; length: number } => {
return {
start : input.selectionStart || 0,
length : (input.selectionEnd||0) - (input.selectionStart||0),
};
};
/**
* Prevent default
*
*/
export const preventDefault = (evt?:Event, stop:boolean=false):void => {
if( evt ){
evt.preventDefault();
if( stop ){
evt.stopPropagation();
}
}
}
/**
* Add event helper
*
*/
export const addEvent = (target:EventTarget, type:string, callback:EventListenerOrEventListenerObject, options?:object):void => {
target.addEventListener(type,callback,options);
};
/**
* Return true if the requested key is down
* Will return false if more than one control character is pressed ( when [ctrl+shift+a] != [ctrl+a] )
* The current evt may not always set ( eg calling advanceSelection() )
*
*/
export const isKeyDown = ( key_name:keyof (KeyboardEvent|MouseEvent), evt?:KeyboardEvent|MouseEvent ) => {
if( !evt ){
return false;
}
if( !evt[key_name] ){
return false;
}
var count = (evt.altKey?1:0) + (evt.ctrlKey?1:0) + (evt.shiftKey?1:0) + (evt.metaKey?1:0);
if( count === 1 ){
return true;
}
return false;
};
/**
* Get the id of an element
* If the id attribute is not set, set the attribute with the given id
*
*/
export const getId = (el:Element,id:string) => {
const existing_id = el.getAttribute('id');
if( existing_id ){
return existing_id;
}
el.setAttribute('id',id);
return id;
};
/**
* Returns a string with backslashes added before characters that need to be escaped.
*/
export const addSlashes = (str:string):string => {
return str.replace(/[\\"']/g, '\\$&');
};
/**
*
*/
export const append = ( parent:Element|DocumentFragment, node: string|Node|null|undefined ):void =>{
if( node ) parent.append(node);
};
/**
* Iterates over arrays and hashes.
*
* ```
* iterate(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
*/
export const iterate = (object:[]|{[key:string]:any}, callback:(value:any,key:any)=>any) => {
if ( Array.isArray(object)) {
object.forEach(callback);
}else{
for (var key in object) {
if (object.hasOwnProperty(key)) {
callback(object[key], key);
}
}
}
};

210
node_modules/tom-select/src/vanilla.ts generated vendored Normal file
View File

@@ -0,0 +1,210 @@
import { iterate } from './utils.ts';
/**
* Return a dom element from either a dom query string, jQuery object, a dom element or html string
* https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
*
* param query should be {}
*/
export const getDom = ( query:any ):HTMLElement => {
if( query.jquery ){
return query[0];
}
if( query instanceof HTMLElement ){
return query;
}
if( isHtmlString(query) ){
var tpl = document.createElement('template');
tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
return tpl.content.firstChild as HTMLElement;
}
return document.querySelector(query);
};
export const isHtmlString = (arg:any): boolean => {
if( typeof arg === 'string' && arg.indexOf('<') > -1 ){
return true;
}
return false;
}
export const escapeQuery = (query:string):string => {
return query.replace(/['"\\]/g, '\\$&');
}
/**
* Dispatch an event
*
*/
export const triggerEvent = ( dom_el:HTMLElement, event_name:string ):void => {
var event = document.createEvent('HTMLEvents');
event.initEvent(event_name, true, false);
dom_el.dispatchEvent(event)
};
/**
* Apply CSS rules to a dom element
*
*/
export const applyCSS = ( dom_el:HTMLElement, css:{ [key: string]: string|number }):void => {
Object.assign(dom_el.style, css);
}
/**
* Add css classes
*
*/
export const addClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {
var norm_classes = classesArray(classes);
elmts = castAsArray(elmts);
elmts.map( el => {
norm_classes.map( cls => {
el.classList.add( cls );
});
});
}
/**
* Remove css classes
*
*/
export const removeClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {
var norm_classes = classesArray(classes);
elmts = castAsArray(elmts);
elmts.map( el => {
norm_classes.map(cls => {
el.classList.remove( cls );
});
});
}
/**
* Return arguments
*
*/
export const classesArray = (args:string[]|string[][]):string[] => {
var classes:string[] = [];
iterate( args, (_classes) =>{
if( typeof _classes === 'string' ){
_classes = _classes.trim().split(/[\t\n\f\r\s]/);
}
if( Array.isArray(_classes) ){
classes = classes.concat(_classes);
}
});
return classes.filter(Boolean);
}
/**
* Create an array from arg if it's not already an array
*
*/
export const castAsArray = (arg:any):Array<any> => {
if( !Array.isArray(arg) ){
arg = [arg];
}
return arg;
}
/**
* Get the closest node to the evt.target matching the selector
* Stops at wrapper
*
*/
export const parentMatch = ( target:null|HTMLElement, selector:string, wrapper?:HTMLElement ):HTMLElement|void => {
if( wrapper && !wrapper.contains(target) ){
return;
}
while( target && target.matches ){
if( target.matches(selector) ){
return target;
}
target = target.parentNode as HTMLElement;
}
}
/**
* Get the first or last item from an array
*
* > 0 - right (last)
* <= 0 - left (first)
*
*/
export const getTail = ( list:Array<any>|NodeList, direction:number=0 ):any => {
if( direction > 0 ){
return list[list.length-1];
}
return list[0];
}
/**
* Return true if an object is empty
*
*/
export const isEmptyObject = (obj:object):boolean => {
return (Object.keys(obj).length === 0);
}
/**
* Get the index of an element amongst sibling nodes of the same type
*
*/
export const nodeIndex = ( el:null|Element, amongst?:string ):number => {
if (!el) return -1;
amongst = amongst || el.nodeName;
var i = 0;
while( el = el.previousElementSibling ){
if( el.matches(amongst) ){
i++;
}
}
return i;
}
/**
* Set attributes of an element
*
*/
export const setAttr = (el:Element,attrs:{ [key: string]: null|string|number }) => {
iterate( attrs,(val,attr) => {
if( val == null ){
el.removeAttribute(attr as string);
}else{
el.setAttribute(attr as string, ''+val);
}
});
}
/**
* Replace a node
*/
export const replaceNode = ( existing:Node, replacement:Node ) => {
if( existing.parentNode ) existing.parentNode.replaceChild(replacement, existing);
}