diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index b88a862..29f1cc5 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -7,9 +7,9 @@ on:
paths:
- 'docs/**'
- 'js/**'
- - 'less/**'
- 'sass/**'
- - 'Gruntfile.js'
+ - 'rollup.config.js'
+ - 'scripts/**'
- 'package.json'
- 'package-lock.json'
- '.github/workflows/deploy-docs.yml'
diff --git a/.github/workflows/docs_validation.yml b/.github/workflows/docs_validation.yml
index 8d5358b..b89216d 100644
--- a/.github/workflows/docs_validation.yml
+++ b/.github/workflows/docs_validation.yml
@@ -7,9 +7,9 @@ on:
paths:
- 'docs/**'
- 'js/**'
- - 'less/**'
- 'sass/**'
- - 'Gruntfile.js'
+ - 'rollup.config.js'
+ - 'scripts/**'
- 'package.json'
- 'package-lock.json'
- '.github/workflows/docs_validation.yml'
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index d52f518..1bf3ba7 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -20,4 +20,4 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Run lint
- run: npx grunt lint
+ run: npm run lint
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index d5f48d6..206c310 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -15,7 +15,7 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Build
- run: npx grunt build
+ run: npm run build
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run Playwright tests
diff --git a/.github/workflows/publish_npm_release.yml b/.github/workflows/publish_npm_release.yml
index 2259bf3..f4bb77d 100644
--- a/.github/workflows/publish_npm_release.yml
+++ b/.github/workflows/publish_npm_release.yml
@@ -11,7 +11,7 @@ permissions:
jobs:
publish:
runs-on: ubuntu-latest
- name: Publish crestapps-bootstrap-select
+ name: Publish bootstrap-select
outputs:
version: ${{ steps.release_meta.outputs.version }}
docs_version: ${{ steps.release_meta.outputs.docs_version }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e99f0d9..969388f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-# Contributing to crestapps-bootstrap-select
+# Contributing to bootstrap-select
Thanks for contributing. This repository contains the vanilla JavaScript
CrestApps fork of `bootstrap-select`, the built distribution artifacts, the
@@ -8,12 +8,12 @@ changes.
## Project layout
- `js/` - source for the plugin runtime
-- `less/` and `sass/` - source stylesheets
+- `sass/` - source stylesheets
- `dist/` - generated distributable assets
- `docs/` - Docusaurus docs app and hosted standalone examples
- `tests/` - manual HTML fixtures, helpers, and Playwright e2e coverage
-Edit the source files in `js/`, `less/`, `sass/`, or `docs/`. Do not hand-edit
+Edit the source files in `js/`, `sass/`, or `docs/`. Do not hand-edit
generated files in `dist/`.
## Before you start
@@ -22,7 +22,7 @@ generated files in `dist/`.
2. For non-trivial changes, open or reference an issue first so the scope is
clear before implementation starts.
3. For bugs, include the browser, OS, Bootstrap version, and
- `crestapps-bootstrap-select` version you tested against.
+ `@crestapps/bootstrap-select` version you tested against.
## Local setup
@@ -44,7 +44,7 @@ generated files in `dist/`.
## Build and validation workflow
-The project uses Grunt for asset generation and Docusaurus for the docs site.
+The project uses Rollup and Sass for asset generation and Docusaurus for the docs site.
- `npm run build` - rebuilds `dist/` from the source files
- `npm run lint` - runs the configured ESLint checks
@@ -52,14 +52,8 @@ The project uses Grunt for asset generation and Docusaurus for the docs site.
- `npm run docs:prepare` - builds the plugin and copies docs assets into
`docs/static/dist/`
- `npm run docs:start` - prepares the docs assets and starts the local docs site
-- `npm run docs:build` - creates a production docs build under `docs/build/`
-
-If you prefer Grunt directly, these tasks remain available:
-
-- `grunt build-css`
-- `grunt build-js`
-- `grunt copy-docs`
-- `grunt dev-watch`
+- `npm run docs:build` - creates a production docs build under `docs/.pages-build/`
+- `npm run watch` - rebuilds the plugin whenever JS or Sass sources change
## Working on docs
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index ada3fe7..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,494 +0,0 @@
-module.exports = function (grunt) {
- function asArray (value) {
- return Array.isArray(value) ? value.reduce(function (result, item) {
- return result.concat(asArray(item));
- }, []) : [value];
- }
-
- function normalizeArchivePath (filepath) {
- return filepath.replace(/\\/g, '/').replace(/^\/+/, '');
- }
-
- // From TWBS
- RegExp.quote = function (string) {
- return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
- };
-
- // Project configuration.
- grunt.initConfig({
-
- // Metadata.
- pkg: grunt.file.readJSON('package.json'),
- banner: '/*!\n' +
- ' * Bootstrap-select v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
- ' *\n' +
- ' * CrestApps fork (vanilla JavaScript, Bootstrap 5+) of snapappointments/bootstrap-select\n' +
- ' * Copyright 2012-2018 SnapAppointments, LLC (original work)\n' +
- ' * Fork modifications Copyright 2024-<%= grunt.template.today(\'yyyy\') %> CrestApps\n' +
- ' * Licensed under <%= pkg.license %> (https://github.com/CrestApps/crestapps-bootstrap-select/blob/main/LICENSE)\n' +
- ' */\n',
-
- // Task configuration.
-
- clean: {
- css: 'dist/css',
- js: 'dist/js',
- docs: 'docs/static/dist'
- },
-
- eslint: {
- options: {
- overrideConfigFile: 'eslint.config.cjs'
- },
- gruntfile: {
- src: 'Gruntfile.js'
- },
- main: {
- src: [
- 'js/*.js',
- '!js/umd-intro.js',
- '!js/umd-outro.js',
- '!js/esm-intro.js',
- '!js/esm-outro.js'
- ]
- },
- i18n: {
- src: 'js/i18n/*.js'
- }
- },
-
- concat: {
- options: {
- stripBanners: true,
- sourceMap: true
- },
- main: {
- src: 'js/bootstrap-select.js',
- dest: 'dist/js/bootstrap-select.js',
- options: {
- banner: '<%= banner %>\n' + grunt.file.read('js/umd-intro.js'),
- footer: grunt.file.read('js/umd-outro.js')
- }
- },
- esm: {
- src: 'js/bootstrap-select.js',
- dest: 'dist/js/bootstrap-select.esm.mjs',
- options: {
- banner: '<%= banner %>\n' + grunt.file.read('js/esm-intro.js'),
- footer: grunt.file.read('js/esm-outro.js')
- }
- },
- i18n: {
- expand: true,
- src: '<%= eslint.i18n.src %>',
- dest: 'dist/',
- options: {
- banner: '<%= banner %>\n' + grunt.file.read('js/umd-intro.js'),
- footer: grunt.file.read('js/umd-outro.js')
- }
- }
- },
-
- uglify: {
- options: {
- banner: '<%= banner %>',
- output: {
- ascii_only: true
- },
- preserveComments: function (node, comment) {
- return /^!|@preserve|@license|@cc_on/i.test(comment.value);
- }
- },
- main: {
- src: '<%= concat.main.dest %>',
- dest: 'dist/js/bootstrap-select.min.js',
- options: {
- sourceMap: true,
- sourceMapIncludeSources: true,
- sourceMapIn: 'dist/js/bootstrap-select.js.map'
- }
- },
- i18n: {
- expand: true,
- src: 'dist/<%= eslint.i18n.src %>',
- ext: '.min.js'
- }
- },
-
- less: {
- options: {
- strictMath: true,
- sourceMap: true,
- outputSourceFiles: true,
- sourceMapURL: 'bootstrap-select.css.map',
- sourceMapFilename: '<%= less.css.dest %>.map'
- },
- css: {
- src: 'less/bootstrap-select.less',
- dest: 'dist/css/bootstrap-select.css'
- }
- },
-
- usebanner: {
- css: {
- options: {
- banner: '<%= banner %>'
- },
- src: '<%= less.css.dest %>'
- }
- },
-
- copy: {
- docs: {
- files: [
- {
- expand: true,
- cwd: 'dist/',
- src: [
- '**/*'
- ],
- dest: 'docs/static/dist/'
- }
- ]
- }
- },
-
- cssmin: {
- options: {
- compatibility: 'ie8',
- keepSpecialComments: '*',
- advanced: false
- },
- css: {
- src: '<%= less.css.dest %>',
- dest: 'dist/css/bootstrap-select.min.css'
- }
- },
-
- csslint: {
- options: {
- 'adjoining-classes': false,
- 'box-sizing': false,
- 'box-model': false,
- 'compatible-vendor-prefixes': false,
- 'floats': false,
- 'font-sizes': false,
- 'gradients': false,
- 'important': false,
- 'known-properties': false,
- 'outline-none': false,
- 'qualified-headings': false,
- 'regex-selectors': false,
- 'shorthand': false,
- 'text-indent': false,
- 'unique-headings': false,
- 'universal-selector': false,
- 'unqualified-attributes': false,
- 'overqualified-elements': false
- },
- css: {
- src: '<%= less.css.dest %>'
- }
- },
-
- version: {
- js: {
- options: {
- prefix: 'Selectpicker.VERSION = \''
- },
- src: [
- 'js/bootstrap-select.js'
- ]
- },
- docs: {
- options: {
- prefix: '<%= pkg.name %>/archive/v',
- replace: '[0-9a-zA-Z\\-_\\+\\.]+)([^/]+(?=\.zip+)'
- },
- src: [
- 'README.md',
- 'docs/content/index.md'
- ]
- },
- cdn: {
- options: {
- prefix: 'npm/<%= pkg.name %>@'
- },
- src: [
- 'README.md',
- 'docs/content/index.md'
- ]
- },
- nuget: {
- options: {
- prefix: '
-
+
@@ -31,13 +31,16 @@ following goals:
than Bootstrap itself).
- **Support Bootstrap 5 and later only** — older Bootstrap and jQuery
compatibility paths are intentionally out of scope.
+- **Ship modern distribution formats** — the package includes first-class ESM,
+ CommonJS, and browser-global UMD builds from the same source code.
+- **Use a modern build pipeline** — the fork no longer relies on Grunt or Less;
+ builds are produced with Rollup, Sass, and the current docs/tooling stack.
- Keep the select-enhancement feature set while prioritizing a modern, small,
forward-only API.
## Requirements
-- **Bootstrap 5+** (CSS and JS, including its bundled Popper). jQuery is **not**
- required.
+- **Bootstrap 5+** (CSS and JS, including its bundled Popper).
## Quick start
@@ -48,7 +51,7 @@ npm install @crestapps/bootstrap-select bootstrap
```
Load Bootstrap 5, then bootstrap-select's CSS and JS. **Load bootstrap-select
-after Bootstrap's JavaScript.**
+after Bootstrap's JavaScript** when using the browser-global build.
```html
@@ -74,19 +77,93 @@ jsDelivr. Prefer pinning an explicit package version in production:
-
-
+
+
```
-You can replace `@1.1.2` with the version you want to consume. During
+You can replace `@1.2.0` with the version you want to consume. During
development, `@latest` also works, but a fixed version is safer for production
deployments.
-When loaded via a `
+
+
+
+
+
+
```
## Usage
@@ -114,10 +191,34 @@ Existing bootstrap-select markup that uses `title="..."` placeholders or
```
+#### Selection indicators
+
+For single selects, use `show-tick` for the default checkmark, or set
+`data-selection-indicator="checkbox"` to render radio-style indicators
+automatically. On multiselects, `data-selection-indicator="checkbox"` renders a
+checkbox column.
+
+```html
+
+
+
+
+
+```
+
### Via JavaScript
-Initialize an instance with the `Selectpicker` class. You can pass an element or
-a CSS selector string:
+Initialize an instance with the `Selectpicker` class from your chosen module
+style. You can pass an element or a CSS selector string:
```js
// Initialize a single select
@@ -153,6 +254,27 @@ picker.destroy(); // remove the plugin and restore the original