diff --git a/.config/babel.config.json b/.config/babel.config.json index 5d321a38f..3bc495084 100644 --- a/.config/babel.config.json +++ b/.config/babel.config.json @@ -4,14 +4,11 @@ [ "@babel/preset-env", { - loose: true, - bugfixes: true, - modules: false + "loose": true, + "bugfixes": true, + "modules": false } ], "@babel/typescript" - ], - "plugins": [ - ["@babel/plugin-proposal-class-properties", { "loose": true }] - ] + ] } diff --git a/.config/eleventy.csp.js b/.config/eleventy.csp.cjs similarity index 97% rename from .config/eleventy.csp.js rename to .config/eleventy.csp.cjs index 82b4a9b9c..ac0521468 100644 --- a/.config/eleventy.csp.js +++ b/.config/eleventy.csp.cjs @@ -45,7 +45,7 @@ module.exports = function( eleventyConfig, config = {} ) { dom.window.document.querySelectorAll(type).forEach( (element) => { if( element.hasAttribute('csp-hash') ){ const hash = 'sha256-'+crypto.createHash('sha256').update(element.textContent).digest('base64') - element.setAttribute("csp-hash", hash); + element.removeAttribute('csp-hash'); hashes.push(`'${hash}'`); }else if( element.textContent.trim() == '' && element.getAttribute('src') === null ){ element.remove(); diff --git a/.config/eleventy.js b/.config/eleventy.js index aca471254..a58e7c7a2 100644 --- a/.config/eleventy.js +++ b/.config/eleventy.js @@ -1,16 +1,18 @@ +import { IdAttributePlugin } from "@11ty/eleventy"; +import syntaxHighlight from '@11ty/eleventy-plugin-syntaxhighlight'; +import markdownIt from 'markdown-it'; +import csp_plugin from './eleventy.csp.cjs'; - -module.exports = function(eleventyConfig) { +export default function(eleventyConfig) { // Aliases are in relation to the _includes folder eleventyConfig.addLayoutAlias('about', 'layouts/about.html'); eleventyConfig.addPassthroughCopy({'doc_src/css':'css'}); eleventyConfig.addPassthroughCopy({'doc_src/js':'js'}); - eleventyConfig.addPassthroughCopy({'build/js':'js'}); - eleventyConfig.addPassthroughCopy({'build/css':'css'}); - eleventyConfig.addPassthroughCopy({'build/esm':'esm'}); + eleventyConfig.addPassthroughCopy({'dist/js':'js'}); + eleventyConfig.addPassthroughCopy({'dist/css':'css'}); + eleventyConfig.addPassthroughCopy({'dist/esm':'esm'}); // content security policy - const csp_plugin = require('./eleventy.csp.js'); eleventyConfig.addPlugin(csp_plugin,{ csp:{ 'default-src': ["'self'"], @@ -23,23 +25,9 @@ module.exports = function(eleventyConfig) { }); // header anchors - const anchors_plugin = require('@orchidjs/eleventy-plugin-ids'); - eleventyConfig.addPlugin(anchors_plugin,{ - prefix:'', - selectors:[ - '.container h1', - '.container h2', - '.container h3', - '.container h4', - '.container h5', - '.container h6', - '.container td:first-child', - ] - }); - + eleventyConfig.addPlugin(IdAttributePlugin); // syntax highlighting - const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); eleventyConfig.addPlugin(syntaxHighlight); function GlobCollection(name, glob){ @@ -81,8 +69,7 @@ module.exports = function(eleventyConfig) { - let markdownIt = require('markdown-it'); - md = markdownIt({ + const md = markdownIt({ html: true, breaks: false, //linkify: true @@ -109,7 +96,7 @@ module.exports = function(eleventyConfig) { dir: { data: '../data', // relative to input path input: 'doc_src/pages', // relative to project root - output: 'build/docs', // relative to project root + output: 'build-docs', // relative to project root includes: '../includes', // relative to input path } }; diff --git a/.config/karma.conf.js b/.config/karma.conf.cjs similarity index 82% rename from .config/karma.conf.js rename to .config/karma.conf.cjs index aeb867639..5efd023d2 100644 --- a/.config/karma.conf.js +++ b/.config/karma.conf.cjs @@ -102,7 +102,7 @@ module.exports = function(config) { flags: [ '--disable-translate', '--disable-extensions', - '--remote-debugging-port=9223' + '--remote-debugging-port=9223', ] }; } @@ -113,12 +113,28 @@ module.exports = function(config) { 'HeadlessChrome': ['HeadlessChrome'] }; - var reporters = ['mocha','coverage','aChecker']; + var reporters = [ + 'mocha', + 'coverage', + //'aChecker' + ]; if( process.env.TRAVIS_CI ){ - reporters = ['mocha', 'coverage', 'coveralls','aChecker'] + reporters = [ + 'mocha', + 'coverage', + 'coveralls', + //'aChecker' + ] } - var browsers = targets[process.env.TARGET || 'HeadlessFirefox']; + var target = process.env.TARGET; + if( !target ){ + target = 'HeadlessChrome'; + process.env.CHROME_BIN = require('puppeteer').executablePath(); + } + + + var browsers = targets[target]; if( process.env.BROWSERS ){ browsers = process.env.BROWSERS.split(','); } @@ -126,7 +142,11 @@ module.exports = function(config) { config.set({ basePath: '../', - frameworks: ['mocha', 'chai','aChecker'], + frameworks: [ + 'mocha', + 'chai', + //'aChecker' + ], files: [ { pattern: 'test/tests/esm-module.js', @@ -134,21 +154,20 @@ module.exports = function(config) { included: true, }, - 'build/js/tom-select.complete.js', + 'dist/js/tom-select.complete.js', 'node_modules/syn/dist/global/syn.js', - 'node_modules/jquery/dist/jquery.js', - 'build/css/tom-select.default.css', + 'dist/css/tom-select.default.css', 'test/support/*.js', config.test_one ? 'test/tests/interaction.js' : 'test/tests/**/*.js', { - pattern: 'build/**/*', + pattern: 'dist/**/*', included: false, }, ], preprocessors: { - 'build/**/*.js': ['sourcemap','coverage'], + 'dist/**/*.js': ['sourcemap','coverage'], }, coverageReporter: { reporters:[ @@ -171,7 +190,8 @@ module.exports = function(config) { browsers: browsers, singleRun: true, browserDisconnectTolerance: 3, - browserDisconnectTimeout: 10000, - browserNoActivityTimeout: 120000 + browserDisconnectTimeout: 15000, + browserNoActivityTimeout: 120000, + concurrency: 3, }); }; diff --git a/.config/rollup.config.js b/.config/rollup.config.mjs similarity index 69% rename from .config/rollup.config.js rename to .config/rollup.config.mjs index 125285e56..028d5a6fb 100644 --- a/.config/rollup.config.js +++ b/.config/rollup.config.mjs @@ -1,15 +1,21 @@ -import resolve from '@rollup/plugin-node-resolve'; // so Rollup can resolve imports without file extensions and `node_modules` -import commonjs from '@rollup/plugin-commonjs'; // so Rollup can convert commonjs to an ES module +import {nodeResolve} from '@rollup/plugin-node-resolve'; // so Rollup can resolve imports without file extensions and `node_modules` import babel from '@rollup/plugin-babel'; -import { terser } from 'rollup-plugin-terser'; -import pkg from '../package.json'; +import terser from '@rollup/plugin-terser'; import path from 'path'; import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; const tom_select_path_js = path.resolve( 'src/tom-select.js' ); const tom_select_path_ts = path.resolve( 'src/tom-select.ts' ); const configs = []; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname,'../package.json'),'utf-8')); + const banner = `/** * Tom Select v${pkg.version} * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +23,7 @@ const banner = `/** `; const extensions = [ - '.js', '.jsx', '.ts', '.tsx', + '.js', '.jsx', '.ts', '.tsx', '.mjs', ]; var babel_config = babel({ @@ -27,53 +33,10 @@ var babel_config = babel({ exclude:'node_modules/**/*.js' }); -var resolve_config = resolve({ +var resolve_config = nodeResolve({ + //browser: true, extensions: extensions, -}); - - -// esm & cjs -const inputs = [ - 'tom-select.ts', - 'tom-select.complete.ts', - 'tom-select.popular.ts', - 'utils.ts', -]; - -inputs.forEach((slug)=>{ - - let input = path.resolve(__dirname,'../src',slug) - - // esm - configs.push({ - input: input, - output:{ - //file: path.resolve(__dirname,'../build/esm',slug), - dir: path.resolve(__dirname,'../build/esm'), - format: 'esm', - preserveModules: false, - sourcemap: true, - banner: banner, - }, - plugins:[babel_config,resolve_config,], - //external: ['@orchidjs/sifter/dist/esm/sifter.js'], - }); - - // cjs - configs.push({ - input: input, - output:{ - dir: path.resolve(__dirname,'../build/cjs'), - format: 'cjs', - preserveModules: false, - sourcemap: true, - banner: banner, - exports: "auto", - }, - plugins:[babel_config,resolve_config], - //external: ['@orchidjs/sifter/dist/esm/sifter.js'], - }); - + mainFields: ['module'], }); @@ -110,8 +73,7 @@ function createConfig( input, output, plugins ){ config.plugins = [ resolve_config, - babel_config, - commonjs(), + babel_config ]; config.plugins = config.plugins.concat(plugins); @@ -123,7 +85,7 @@ function configCore( input, filename, plugins ){ var output = { name: 'TomSelect', - file: `build/js/${filename}`, + file: `dist/js/${filename}`, footer: 'var tomSelect=function(el,opts){return new TomSelect(el,opts);} ', }; @@ -151,7 +113,7 @@ var plugin_dir = path.resolve(__dirname,'../src/plugins'); var files = fs.readdirSync( plugin_dir ); files.map(function(file){ let input = path.resolve(__dirname,'../src/plugins',file,'plugin.ts'); - let output = {file:`build/js/plugins/${file}.js`,'name':file}; + let output = {file:`dist/js/plugins/${file}.js`,'name':file}; pluginConfig( input, output); @@ -159,7 +121,7 @@ files.map(function(file){ configs.push({ input: input, output:{ - file: path.resolve(__dirname,'../build/esm/plugins',file,'plugin.js'), + file: path.resolve(__dirname,'../dist/esm/plugins',file,'plugin.js'), format: 'esm', preserveModules: false, sourcemap: true, diff --git a/.config/rollup.docs.js b/.config/rollup.docs.mjs similarity index 80% rename from .config/rollup.docs.js rename to .config/rollup.docs.mjs index fc9c2f44c..f6bcfd015 100644 --- a/.config/rollup.docs.js +++ b/.config/rollup.docs.mjs @@ -1,8 +1,13 @@ import alias from '@rollup/plugin-alias'; import resolve from '@rollup/plugin-node-resolve'; // so Rollup can resolve imports without file extensions and `node_modules` import babel from '@rollup/plugin-babel'; -import { terser } from 'rollup-plugin-terser'; +import terser from '@rollup/plugin-terser'; import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); var configs = []; @@ -21,6 +26,7 @@ var resolve_config = resolve({ extensions: extensions, }); + var terser_config = terser({ mangle: true, toplevel: true, // removes tomSelect footer @@ -29,11 +35,12 @@ var terser_config = terser({ }, }); + // bootstrap tabs for docs configs.push({ input: 'doc_src/js/index.js', output: { - file: path.resolve(__dirname,'../build/docs/js/index.bundle.js'), + file: path.resolve(__dirname,'../build-docs/js/index.bundle.js'), name: 'bootstrap', format: 'umd', sourcemap: true, diff --git a/.config/stylelintrc.json b/.config/stylelintrc.json new file mode 100644 index 000000000..77b9f9120 --- /dev/null +++ b/.config/stylelintrc.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "stylelint-config-standard-scss", + "stylelint-config-prettier-scss" + ], + + "rules": { + "color-function-notation": "legacy", + "selector-class-pattern": null, + "shorthand-property-no-redundant-values": null, + "scss/dollar-variable-pattern": null, + "scss/load-partial-extension": null, + "scss/no-global-function-names": null + } +} diff --git a/.config/tsconfig.cjs.json b/.config/tsconfig.cjs.json new file mode 100644 index 000000000..0f4283aa1 --- /dev/null +++ b/.config/tsconfig.cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "../dist/cjs" + } +} diff --git a/.config/tsconfig.esm.json b/.config/tsconfig.esm.json new file mode 100644 index 000000000..222434514 --- /dev/null +++ b/.config/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "NodeNext", + "outDir": "../dist/esm" + } +} diff --git a/.config/tsconfig.json b/.config/tsconfig.json index 5567e626d..57962440f 100644 --- a/.config/tsconfig.json +++ b/.config/tsconfig.json @@ -4,13 +4,20 @@ "allowJs": true, "checkJs": true, "strict": true, - "target": "esnext", - "module": "esnext", + "target": "ES6", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "alwaysStrict": true, + "strictNullChecks": true, + "noImplicitReturns": true, "noUnusedLocals": true, - + "allowUnreachableCode": false, + "noUncheckedIndexedAccess": true, "declaration": true, - "declarationDir": "../dist/types", "isolatedModules": true, - "moduleResolution": "node" + "sourceMap": true, + "rewriteRelativeImportExtensions": true, + + "lib": ["ESNext", "dom"], }, } diff --git a/.config/tsconfig.types.json b/.config/tsconfig.types.json new file mode 100644 index 000000000..646244432 --- /dev/null +++ b/.config/tsconfig.types.json @@ -0,0 +1,11 @@ +{ + // This exists for backwards compatibility. In the wild, folks are importing + // paths like `tom-select/dist/types/...`. + // + // Consider removing this in the next major version. + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../dist/types", + "emitDeclarationOnly": true + } +} diff --git a/.editorconfig b/.editorconfig index 13ef9afb7..c72e4e324 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,15 @@ [*] indent_style = tab tab_width = 4 + +[package.json] +indent_style = space +indent_size = 2 + +[.github/workflows/*] +indent_style = space +indent_size = 2 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml new file mode 100644 index 000000000..f66155c08 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -0,0 +1,52 @@ +name: "Tom Select Bug report" +description: "Submit a report and help us improve our free and open-source project" +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + ### Thank you for contributing to our project! + Before submitting, we'd appreciate it if you: + - Verify that your issue is not [already reported on GitHub](https://github.com/orchidjs/tom-select/issues?q=is%3Aissue). + - Check if your TomSelect is up to date. If not, we recommend that you update first. + - type: textarea + id: bug-description + attributes: + label: Bug description + description: Provide a description of the bug you're experiencing. Please include any relevant error messages. + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: Describe what you expected to happen. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: | + Create an example on JSFiddle, CodePen or similar service and outline the steps for reproducing the bug. + **Tip** go to tom-select.js.org and click "edit" -> "JSFiddle" or "CodePen" to start a example) + value: | + 1. Go to ... + 2. Click on .... + ... + X. See error + validations: + required: true + - type: textarea + id: additional-info + attributes: + label: Additional context + description: Add any other context about the problem here + value: | + - OS: [e.g. iOS, Windows] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + - Device: [e.g. iPhone6] + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e6911ee6e..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior. -If possible, please provide a JSFiddle or CodePen example. -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Additional context** -Add any other context about the problem here. - - OS: [e.g. iOS, Windows] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - - Device: [e.g. iPhone6] - - - - ``` + +
+

Multiple instances using a .class selector

+ +```html + + + ... + +``` +
+ + ### Glossary -- Config / configuration: settings passed to the object constructor -- Settings: the current settings. Accessible with the `settings` property of the select object. -- Options: the list of objects to display. - Each object must have a property with an unique **value** to identify the option; the property name is defined by the `valueField` setting. - Option objects must also have a property with the **label** to display (as tag, in the drop down, etc.); the property name is defined by the `labelField` setting. - The options can have other properties, ignored, unless referenced by other settings, like `sortField` or `searchField`. -- Items: the list of selected options. Or more exactly, the list of the values of the selected options. +
+
Settings
+
Configuration parameters passed to the TomSelect constructor and accessible with the settings property of the select object
+
Options
+
The list of objects to display. + Each object must have a property with an unique value to identify the option; the property name is defined by the valueField setting. + Option objects must also have a property with the label to display (as tag, in the drop down, etc.); the property name is defined by the labelField setting. + The options can have other properties, ignored, unless referenced by other settings, like sortField or searchField. +
+
Items
+
The list of selected options. Or more exactly, the list of the values of the selected options.
+
NodeDefinition
+
An HTMLElement or DOMString. + If a DOMString is used, it should either be a CSS Selector or a string of HTML compatible with innerHTML. +
+
+ ## General Configuration @@ -87,7 +119,7 @@ create: function(input,callback){ } ``` -boolean/function +boolean|function false @@ -138,13 +170,13 @@ create: function(input,callback){ maxOptions - The max number of options to display in the dropdown. + The max number of options to display in the dropdown. Set maxOptions to null for an unlimited number of options. int 50 maxItems - The max number of items the user can select. 1 makes the control mono-selection, null allows an unlimited number of items. + The max number of items the user can select. A value of 1 makes the control mono-selection, null allows an unlimited number of items. int null @@ -158,7 +190,7 @@ create: function(input,callback){ closeAfterSelect After a selection is made, the dropdown will remain open if in a multi-selection control or will close in a single-selection control. Setting closeAfterSelect to true will force the dropdown to close after selections are made. - Setting closeAfterSelect to false will keep the dropdown open after selections are made. + Setting closeAfterSelect to false will keep the dropdown open after selections are made. boolean undefined @@ -183,7 +215,15 @@ create: function(input,callback){ placeholder - The placeholder of the control. Defaults to input element's placeholder, unless this one is specified. + The placeholder of the control. Defaults to input element's placeholder, unless this one is specified. + To update the placeholder setting after initialization, call inputState() + +```js +const tom = new TomSelect('#input-id'); +tom.settings.placeholder = "New placeholder"; +tom.inputState(); +``` + string undefined @@ -197,7 +237,7 @@ create: function(input,callback){ preload If true, the load function will be called upon control initialization (with an empty search). Alternatively it can be set to 'focus' to call the load function when control receives focus. - boolean/string + boolean|string false @@ -226,9 +266,11 @@ create: function(input,callback){ controlInput - Supply a custom <input> element - <input> element - null + Supply a custom <input> element. + Supplying a null value will disable the default functionality. + + NodeDefinition|null + <input...> duplicates @@ -312,12 +354,16 @@ create: function(input,callback){ sortField -

A single field or an array of fields to sort by. Each item in the array should be an object containing at least a field property. Optionally, direction can be set to 'asc' or 'desc'. The order of the array defines the sort precedence.

-

Unless present, a special `$score` field will be automatically added to the beginning of the sort list. This will make results sorted primarily by match quality (descending).

-

You can override the `$score` function. For more information, see the sifter documentation.

- - string|array - '$order' +

sortField maps directly to the sort setting in Sifter.

+

By default, results will be sorted by their $score first, then by the original order of options. + To disable sorting entirely and maintain the original order of options, use: + +```js +sortField:[{field:'$order'},{field:'$score'}] +``` + + string
array
function
+ [{field:'$score'}, {field:'$order'}] searchField @@ -333,6 +379,12 @@ Weights can be given to each field to improve search results searchField: [{field:'text',weight:2},{field:'text2',weight:0.5}] ``` +To completely disable the client side filtering (if youre getting the search results from an external source), set the `searchField` to an empty array. + +```js +searchField: [] +``` + array ['text'] @@ -414,7 +466,7 @@ new TomSelect('#select',{ Invoked when an item is selected. - onItemRemove(value) + onItemRemove(value, $item) Invoked when an item is deselected. @@ -454,7 +506,7 @@ new TomSelect('#select',{ ## Render Templates Nearly every piece of HTML in Tom Select is customizable with a render template. -Each template is defined by a function that is passed two arguments (data and escape) and returns HTML (string or DOM element) with a single root element. The escape argument is a function that takes a string and escapes all special HTML characters. This is very important to use to prevent XSS vulnerabilities. +Each template is defined by a function that is passed two arguments (data and escape) and returns NodeDefinition with a single root element. The escape argument is a function that takes a string and escapes all special HTML characters. This is very important to use to prevent XSS vulnerabilities. ```js new TomSelect('#input',{ diff --git a/doc_src/pages/docs/migration.md b/doc_src/pages/docs/migration.md index f4cd42dd6..ea6bd4741 100644 --- a/doc_src/pages/docs/migration.md +++ b/doc_src/pages/docs/migration.md @@ -22,5 +22,5 @@ Review changes to the Tom Select API to help you migrate from v1 to v2. * Multiple CSS classes are now toggled on the wrapper element instead of the control element: ```.focus```, ```.disabled```, ```.required```, ```.invalid```, ```.locked```, ```.full```, ```.not-full```, ```.input-active```, ```.dropdown-active```, ```.has-options```, ```.has-items``` * Removed bootstrap3 style -## Options +## Settings * ```copyClassesToDropdown``` defaults to false diff --git a/doc_src/pages/docs/plugins.md b/doc_src/pages/docs/plugins.md index 1cc33c390..47d0ed2f8 100644 --- a/doc_src/pages/docs/plugins.md +++ b/doc_src/pages/docs/plugins.md @@ -45,10 +45,30 @@ Save some bandwidth with a bundle that's about 4kb smaller. tom-select.pop #### tom-select.base.js If you don't need any plugins, or want to load plugins individually, use tom-select.base.js. -Add plugins to your project by including their js files: /js/plugins/remove_button.js, /js/plugins/dropdown_header.js, etc. + +Add plugins to your project by including their js files and calling `TomSelect.define`. + +```js +import TomSelect from 'tom-select/base'; +import TomSelect_remove_button from 'tom-select/plugins/remove_button.js'; +import TomSelect_dropdown_header from 'tom-select/dropdown_header.js'; + +TomSelect.define('remove_button', TomSelect_remove_button); +TomSelect.define('dropdown_header', TomSelect_dropdown_header); +``` + +Alternatively you can `require` plugins directly if your build tool supports it. + +```js +import TomSelect from 'tom-select/base'; + +TomSelect.define('remove_button', require('tom-select/plugins/remove_button.js')); +TomSelect.define('dropdown_header', require('tom-select/plugins/dropdown_header.js')); +``` + #### tom-select.custom.js -Use NPM to hand-pick plugins and create /build/js/tom-select.custom.js +Use NPM to hand-pick plugins and create /dist/js/tom-select.custom.js ```shell # clone the repo @@ -58,11 +78,10 @@ cd tom-select # install dev dependencies npm install -# create /build/js/tom-select.custom.js +# create /dist/js/tom-select.custom.js npm run build -- --plugins=remove_button,restore_on_backspace ``` - ## Creating Plugins **A few notes:** @@ -70,6 +89,7 @@ npm run build -- --plugins=remove_button,restore_on_backspace - Plugin names should follow the format: `/[a-z_]+$` - JS source should live in a "plugin.js" file (required). - CSS should live in a "plugin.scss" file (optional). It will be bundled at build time. +- Plugins should not call `TomSelect.define` directly, this is done when importing the plugin. - Plugins are initialized right before the control is setup. This means that if you want to listen for events on any of the control's elements, you should override the `setup()` method (see ["DOM Events"](#dom-events)). @@ -78,18 +98,20 @@ npm run build -- --plugins=remove_button,restore_on_backspace ### Boilerplate ```js -TomSelect.define('plugin_name', function(plugin_options) { - // options: plugin-specific options +// in src/plugins/plugin_name/plugin.js +export default function(plugin_options) { + // plugin_options: plugin-specific options // this: TomSelect instance -}); +}; ``` #### Adding Dependencies ```js -TomSelect.define('plugin_name', function(plugin_options) { +// in src/plugins/plugin_name/plugin.js +export default function(plugin_options) { this.require('another_plugin'); -}); +}; ``` #### Method Hooks @@ -97,11 +119,12 @@ TomSelect.define('plugin_name', function(plugin_options) { Execute plugin code 'before' or 'after' existing methods ```js -TomSelect.define('plugin_name', function(plugin_options) { - this.hook('after','setup',function(){ +// in src/plugins/plugin_name/plugin.js +export default function(plugin_options) { + this.hook('after', 'setup', function() { // .. additional setup }); -}); +}; ``` #### Overriding Methods @@ -111,13 +134,14 @@ Use the 'instead' hook to override existing methods. overridden function returns a value as well. ```js -TomSelect.define('plugin_name', function(plugin_options) { +// in src/plugins/plugin_name/plugin.js +export default function(plugin_options) { var original_setup = this.setup; - this.hook('instead','setup',function(){ + this.hook('instead', 'setup', function() { // .. custom setup return original_setup.apply(this, arguments); }); -}); +}; ``` @@ -125,11 +149,12 @@ TomSelect.define('plugin_name', function(plugin_options) { If you want to add event listeners to dom elements, add them after the `setup()` method. ```js -TomSelect.define('plugin_name', function(plugin_options) { - this.hook('after','setup',function(){ +// in src/plugins/plugin_name/plugin.js +export default function(plugin_options) { + this.hook('after', 'setup', function() { this.control.addEventListener('click',function(evt){ alert('the control was clicked'); }); }); -}); +}; ``` diff --git a/doc_src/pages/examples/api.njk b/doc_src/pages/examples/api.njk index b27f20fd3..73e5b68aa 100644 --- a/doc_src/pages/examples/api.njk +++ b/doc_src/pages/examples/api.njk @@ -15,7 +15,7 @@ tags: demo {% set html %} -

+
diff --git a/doc_src/pages/examples/customization.njk b/doc_src/pages/examples/customization.njk index 1acf3f877..fcb6c78e1 100644 --- a/doc_src/pages/examples/customization.njk +++ b/doc_src/pages/examples/customization.njk @@ -1,6 +1,6 @@ --- -title: Customizing HTML -nav_title: Custom HTML +title: Customizing +nav_title: Customizing tags: demo --- @@ -13,7 +13,7 @@ tags: demo {% set label %}

This example provides a simple demonstration of how to override the default templates for options and items along with proper use of the escape() method. @@ -66,3 +66,67 @@ new TomSelect('#select-links',{ {{ demo( label, html, script, style) }} + + + + + +{% set label %} + +

+ There are a number of ways to customize the JavaScript functionality. + Plugins are a great example but sometimes you just want to add some functionality to items or options. + The example below shows how to add a clickable button within an option but the same concept can be applied to items. +

+{% endset %} + +{% set html %} + +{% endset %} + + + + + +{{ demo( label, html, script, style) }} diff --git a/doc_src/pages/examples/index.njk b/doc_src/pages/examples/index.njk index 7f758b759..85d30fcbe 100644 --- a/doc_src/pages/examples/index.njk +++ b/doc_src/pages/examples/index.njk @@ -61,7 +61,7 @@ tags: demo {{ demo('', - ' diff --git a/doc_src/pages/examples/options.njk b/doc_src/pages/examples/options.njk index 5865964c5..99727a533 100644 --- a/doc_src/pages/examples/options.njk +++ b/doc_src/pages/examples/options.njk @@ -43,7 +43,7 @@ new TomSelect('#select-tools',{ {% set label %} -

Enhance options with data-* attributes

+

Images can be added to option and item elements with custom render templates and data-* attributes

{% endset %} {% set html %} diff --git a/doc_src/pages/examples/styling.njk b/doc_src/pages/examples/styling.njk index 23ad2f304..2fd109220 100644 --- a/doc_src/pages/examples/styling.njk +++ b/doc_src/pages/examples/styling.njk @@ -213,4 +213,4 @@ new TomSelect('#input-group-first',{create:true}); {% endset %} -{{ demo_col('input-group', left_col, html, script) }} +{{ demo_col('input-group-first', left_col, html, script) }} diff --git a/doc_src/pages/examples/validation.njk b/doc_src/pages/examples/validation.njk index 46d965c6b..6ec758ac6 100644 --- a/doc_src/pages/examples/validation.njk +++ b/doc_src/pages/examples/validation.njk @@ -104,7 +104,7 @@ Then, within your submit listener, you can either add the was-validated - @@ -123,16 +123,7 @@ Then, within your submit listener, you can either add the was-validated {% set script %} -var my_select = new TomSelect('#select-bootstrap',{ - create: true, - sortField: { - field: 'text', - direction: 'asc' - }, - onChange:function(){ - this.wrapper.classList.toggle('is-invalid',!this.isValid); - } -}); +var my_select = new TomSelect('#select-bootstrap'); // Example starter JavaScript for disabling form submissions if there are invalid fields @@ -142,9 +133,6 @@ form.addEventListener('submit', function (event){ // add was-validated to display custom colors form.classList.add('was-validated') - // or, add is-invalid to the wrapper to display custom colors and invalid-feedback messages - my_select.wrapper.classList.toggle('is-invalid',!my_select.isValid); - if (!form.checkValidity()) { event.preventDefault() event.stopPropagation() diff --git a/doc_src/pages/index.njk b/doc_src/pages/index.njk index 152110b04..c8db54ad1 100644 --- a/doc_src/pages/index.njk +++ b/doc_src/pages/index.njk @@ -1,5 +1,5 @@ --- -description: Tom Select is a versatile and dynamic " UI control. With au GitHub Repo stars jsDelivr hits (npm) npmjs.org -travis-ci.com +tests Coveralls Coverage GitHub Issues {# Browserstack Status #} @@ -21,7 +21,7 @@ description: Tom Select is a versatile and dynamic " + UI control. With au
-Tom Select is a versatile and dynamic <select> UI control. +Tom Select is a dynamic, framework agnostic, and lightweight (~16kb gzipped) <select> UI control. With autocomplete and native-feeling keyboard navigation, it's useful for tagging, contact lists, country selectors, and so on. Tom Select was forked from selectize.js with four main objectives: modernizing the code base, decoupling from jQuery, expanding functionality, and addressing issue backlogs.
@@ -90,8 +90,8 @@ Tom Select was forked from selectize.js with f {% endhighlight %} @@ -143,27 +143,19 @@ new TomSelect('#tom-select-it',config); -

Platinum Sponsors

+

Sponsors

Many thanks to all our sponsors who help make development possible. Become a sponsor.

-
-

- -

-
- - -

Sponsors

-

Trust My Paper Logo +WiseEssays.com


diff --git a/doc_src/pages/plugins/checkbox-options.njk b/doc_src/pages/plugins/checkbox-options.njk index dcc71a08b..793c79a41 100644 --- a/doc_src/pages/plugins/checkbox-options.njk +++ b/doc_src/pages/plugins/checkbox-options.njk @@ -6,6 +6,7 @@ tags: demo {% from "demo.njk" import demo %} +{% from "macro_config.njk" import config_table %} {% set label %} @@ -30,7 +31,12 @@ tags: demo @@ -39,4 +45,9 @@ new TomSelect('#ex-checkbox-options',{

Plugin Configuration

-

No additional configuration settings for this plugin

+{{ config_table([ + {name:'className',desc:'

Search CSS class for the checkbox

',type:'string',default:'tomselect-checkbox'}, + {name:'checkedClassNames',desc:'

The CSS classes the checkbox has if it was checked

',type:'string[]',default:undefined}, + {name:'uncheckedClassNames',desc:'

The CSS classes the checkbox has if it was not checked

',type:'string[]',default:undefined} + ]) +}} \ No newline at end of file diff --git a/doc_src/pages/plugins/drag-drop.njk b/doc_src/pages/plugins/drag-drop.njk index b14c43d33..72b960b24 100644 --- a/doc_src/pages/plugins/drag-drop.njk +++ b/doc_src/pages/plugins/drag-drop.njk @@ -2,7 +2,6 @@ title: Drag 'n Drop nav_title: Drag 'n Drop tags: demo -script: --- @@ -16,7 +15,7 @@ script: -{{ demo( label, html, script,'',true) }} +{{ demo( label, html, script,'') }}

Plugin Configuration

diff --git a/doc_src/pages/plugins/index.njk b/doc_src/pages/plugins/index.njk index e3c102537..9d3c654e0 100644 --- a/doc_src/pages/plugins/index.njk +++ b/doc_src/pages/plugins/index.njk @@ -4,7 +4,6 @@ nav_title: Plugins tags: demo exclude: true layout: layout_default -script: --- @@ -66,23 +65,25 @@ script: - {{ demo_small( html, script, '', true ) }} + {{ demo_small( html, script, '' ) }}
diff --git a/doc_src/pages/plugins/input-autogrow.njk b/doc_src/pages/plugins/input-autogrow.njk index 6905f99bf..a7602c099 100644 --- a/doc_src/pages/plugins/input-autogrow.njk +++ b/doc_src/pages/plugins/input-autogrow.njk @@ -14,9 +14,13 @@ tags: demo +

+ The input_autogrow plugin will increase the width of the input as users type. +

{% endset %} {% set html %} +
+
{% endset %} + diff --git a/test/html/tab.html b/test/html/tab.html index d44645016..0dfc62d40 100644 --- a/test/html/tab.html +++ b/test/html/tab.html @@ -2,9 +2,9 @@ - + - +