From 94220f5879be41c387841cb487035f2992b31823 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 6 Mar 2026 06:43:21 +0000
Subject: [PATCH 01/26] chore(deps-dev): bump @rollup/plugin-terser from 0.4.4
to 1.0.0
Bumps [@rollup/plugin-terser](https://github.com/rollup/plugins/tree/HEAD/packages/terser) from 0.4.4 to 1.0.0.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/terser/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/beep-v1.0.0/packages/terser)
---
updated-dependencies:
- dependency-name: "@rollup/plugin-terser"
dependency-version: 1.0.0
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index e1ac2ee..edb8a69 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"devDependencies": {
"@commitlint/cli": "^20.3.1",
"@commitlint/config-conventional": "^20.3.1",
- "@rollup/plugin-terser": "^0.4.4",
+ "@rollup/plugin-terser": "^1.0.0",
"@types/bootstrap": "^5.2.10",
"@types/jest": "^30.0.0",
"@types/jquery": "^3.5.33",
From e993a5a45326aa3e76268c4707a2f4c3076a9a4b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 6 Mar 2026 06:44:54 +0000
Subject: [PATCH 02/26] chore(deps-dev): bump cypress-plugin-tab from 1.0.5 to
2.0.0
Bumps [cypress-plugin-tab](https://github.com/kuceb/cypress-plugin-tab) from 1.0.5 to 2.0.0.
- [Release notes](https://github.com/kuceb/cypress-plugin-tab/releases)
- [Changelog](https://github.com/kuceb/cypress-plugin-tab/blob/master/RELEASE_NOTES.md)
- [Commits](https://github.com/kuceb/cypress-plugin-tab/compare/v1.0.5...v2.0.0)
---
updated-dependencies:
- dependency-name: cypress-plugin-tab
dependency-version: 2.0.0
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index e1ac2ee..ed9798c 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,7 @@
"commitlint": "^20.3.1",
"eslint": "^10.0.0",
"cypress": "^15.8.1",
- "cypress-plugin-tab": "^1.0.5",
+ "cypress-plugin-tab": "^2.0.0",
"doctoc": "^2.2.0",
"dompurify": "^3.0.0",
"globals": "^17.0.0",
From dcc736a030ded931b42fa2b682d3fcfb73fc729d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 Apr 2026 06:45:07 +0000
Subject: [PATCH 03/26] ci(deps): bump the minor-and-patch-updates group across
1 directory with 2 updates
Bumps the minor-and-patch-updates group with 2 updates in the / directory: [cypress-io/github-action](https://github.com/cypress-io/github-action) and [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action).
Updates `cypress-io/github-action` from 7.1.2 to 7.1.9
- [Release notes](https://github.com/cypress-io/github-action/releases)
- [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/github-action/compare/84d178e4bbce871e23f2ffa3085898cde0e4f0ec...783cb3f07983868532cabaedaa1e6c00ff4786a8)
Updates `SonarSource/sonarqube-scan-action` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases)
- [Commits](https://github.com/sonarsource/sonarqube-scan-action/compare/a31c9398be7ace6bbfaf30c0bd5d415f843d45e9...299e4b793aaa83bf2aba7c9c14bedbb485688ec4)
---
updated-dependencies:
- dependency-name: cypress-io/github-action
dependency-version: 7.1.9
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch-updates
- dependency-name: SonarSource/sonarqube-scan-action
dependency-version: 7.1.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: minor-and-patch-updates
...
Signed-off-by: dependabot[bot]
---
.github/workflows/ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 64d7320..892bc01 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -77,7 +77,7 @@ jobs:
run: npm run build
- name: Cypress run
#v6.10.8
- uses: cypress-io/github-action@84d178e4bbce871e23f2ffa3085898cde0e4f0ec
+ uses: cypress-io/github-action@783cb3f07983868532cabaedaa1e6c00ff4786a8
with:
browser: chrome
@@ -119,6 +119,6 @@ jobs:
- name: SonarQube Scan
# v7.0.0
- uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9
+ uses: SonarSource/sonarqube-scan-action@299e4b793aaa83bf2aba7c9c14bedbb485688ec4
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
\ No newline at end of file
From 926682793baa589c20a691a9457ecbab25d105b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 4 Apr 2026 15:53:01 +0200
Subject: [PATCH 04/26] chore: update readme
---
README.md | 159 ++---------------------------------------
README.template.md | 171 +--------------------------------------------
2 files changed, 7 insertions(+), 323 deletions(-)
diff --git a/README.md b/README.md
index 618be51..4d7d8bb 100644
--- a/README.md
+++ b/README.md
@@ -29,9 +29,9 @@
See EOL for each version in [Security Policy Page](https://github.com/palcarazm/bootstrap5-toggle/security/policy).
-# Demos
+# Demo and documentation
-**Demos and API Docs:** https://palcarazm.github.io/bootstrap5-toggle/
+**Please read our documentation page for a completed usage explanation:** https://palcarazm.github.io/bootstrap5-toggle/
---
@@ -50,14 +50,7 @@ See EOL for each version in [Security Policy Page](https://github.com/palcarazm/
- [Usage](#usage)
- [Initialize With HTML](#initialize-with-html)
- [Initialize With Code](#initialize-with-code)
-- [API](#api)
- - [Options](#options)
- - [Methods](#methods)
-- [Events](#events)
- - [Event Propagation](#event-propagation)
- - [Stopping Event Propagation](#stopping-event-propagation)
- - [API vs Input](#api-vs-input)
-- [Collaborators welcom!](#collaborators-welcom)
+- [Collaborators welcome!](#collaborators-welcome)
@@ -128,151 +121,7 @@ EX: Initialize id `chkToggle` with a single line of JavaScript.
```
-# API
-
-## Options
-
-- Options can be passed via data attributes or JavaScript
-- For data attributes, append the option name to `data-` (ex: `data-on="Enabled"`)
-
-```html
-
-
-
-```
-
-| Name | Type | Default | Description |
-| ---------- | ----------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `onlabel` | string/html | "On" | Text of the on toggle |
-| `offlabel` | string/html | "Off" | Text of the off toggle |
-| `size` | string | "normal" | Size of the toggle. Possible values are: `large`, `normal`, `small`, `mini`. |
-| `onstyle` | string | "primary" | Style of the on toggle. Possible values are: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark` and with `outline-` prefix |
-| `offstyle` | string | "secondary" | Style of the off toggle. Possible values are: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark` and with `outline-` prefix |
-| `onvalue` | string | _null_ | Sets on state value |
-| `offvalue` | string | _null_ | Sets off state value |
-| `ontitle` | string | _null_ | Title of the on toggle |
-| `offtitle` | string | _null_ | Title of the off toggle |
-| `style` | string | | Appends the value to the class attribute of the toggle. This can be used to apply custom styles. Refer to Custom Styles for reference. |
-| `width` | integer | _null_ | Sets the width of the toggle. if set to _null_, width will be auto-calculated. |
-| `height` | integer | _null_ | Sets the height of the toggle. if set to _null_, height will be auto-calculated. |
-| `tabindex` | integer | 0 | Sets the tabindex of the toggle. |
-| `tristate` | boolean | false | Sets tristate support |
-
-## Methods
-
-Methods can be used to control toggles directly.
-
-```html
-
-
-```
-
-| Method | Example | Description |
-| ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------- |
-| initialize | `toggleDemo.bootstrapToggle()` | Initializes the toggle plugin with options |
-| destroy | `toggleDemo.bootstrapToggle('destroy')` | Destroys the toggle |
-| rerender | `toggleDemo.bootstrapToggle('rerender')` | Rerender toggle with the appropriated size. Useful when parent is collapsed at first. |
-| on | `toggleDemo.bootstrapToggle('on')` | Sets the toggle to 'On' state |
-| off | `toggleDemo.bootstrapToggle('off')` | Sets the toggle to 'Off' state |
-| toggle | `toggleDemo.bootstrapToggle('toggle')` | Toggles the state of the toggle on/off |
-| enable | `toggleDemo.bootstrapToggle('enable')` | Enables the toggle |
-| disable | `toggleDemo.bootstrapToggle('disable')` | Disables the toggle |
-| readonly | `toggleDemo.bootstrapToggle('readonly')` | Disables the toggle but preserve checkbox enabled |
-| indeterminate | `toggleDemo.bootstrapToggle('indeterminate')` | Sets the toggle to 'indeterminate' state |
-| determinate | `toggleDemo.bootstrapToggle('determinate')` | Sets the toggle to 'determinate' state |
-
-# Events
-
-## Event Propagation
-
-Note All events are propagated to and from input element to the toggle.
-
-You should listen to events from the ` ` directly rather than look for custom events.
-
-```html
-
-
-
-```
-
-## Stopping Event Propagation
-
-Passing `true` to the on, off, toggle, determinate and indeterminate methods will enable the silent option to prevent the control from propagating the change event in cases where you want to update the controls on/off state, but do not want to fire the onChange event.
-
-```html
-
-
- On by API (silent)
-
-
- Off by API (silent)
-
-
- On by API (not silent)
-
-
- On by API (not silent)
-
-
-```
-
-## API vs Input
-
-This also means that using the API or Input to trigger events will work both ways.
-
-```html
-
-On by API
-Off by API
-On by Input
-Off by Input
-
-```
-
-# Collaborators welcom!
+# Collaborators welcome!
- :sos: Do you need some help? Open a thread in [GitHub Discussions Q&A](https://github.com/palcarazm/bootstrap5-toggle/discussions/new?category=q-a)
- :bug: Do you find a bug? Open an issue in [GitHub bug report](https://github.com/palcarazm/bootstrap5-toggle/issues/new?template=01-BUG_REPORT.yml)
diff --git a/README.template.md b/README.template.md
index 6d4de38..e4b95ad 100644
--- a/README.template.md
+++ b/README.template.md
@@ -29,9 +29,9 @@
See EOL for each version in [Security Policy Page](https://github.com/palcarazm/bootstrap5-toggle/security/policy).
-# Demos
+# Demo and documentation
-**Demos and API Docs:** https://palcarazm.github.io/bootstrap5-toggle/
+**Please read our documentation page for a completed usage explanation:** https://palcarazm.github.io/bootstrap5-toggle/
---
@@ -39,27 +39,6 @@ See EOL for each version in [Security Policy Page](https://github.com/palcarazm/
-- [Bootstrap 5 Toggle](#bootstrap-5-toggle) - [Library Distributions](#library-distributions)
-- [Demos](#demos)
-- [Related Bootstrap Plugins](#related-bootstrap-plugins)
-- [Installation](#installation)
- - [CDN](#cdn)
- - [ECMAS Interface](#ecmas-interface)
- - [jQuery Interface](#jquery-interface)
- - [Download](#download)
- - [NPM](#npm)
- - [Yarn](#yarn)
-- [Usage](#usage)
- - [Initialize With HTML](#initialize-with-html)
- - [Initialize With Code](#initialize-with-code)
-- [API](#api)
- - [Options](#options)
- - [Methods](#methods)
-- [Events](#events)
- - [Event Propagation](#event-propagation)
- - [Stopping Event Propagation](#stopping-event-propagation)
- - [API vs Input](#api-vs-input)
-- [Collaborators welcom!](#collaborators-welcom)
@@ -130,151 +109,7 @@ EX: Initialize id `chkToggle` with a single line of JavaScript.
```
-# API
-
-## Options
-
-- Options can be passed via data attributes or JavaScript
-- For data attributes, append the option name to `data-` (ex: `data-on="Enabled"`)
-
-```html
-
-
-
-```
-
-| Name | Type | Default | Description |
-| ---------- | ----------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `onlabel` | string/html | "On" | Text of the on toggle |
-| `offlabel` | string/html | "Off" | Text of the off toggle |
-| `size` | string | "normal" | Size of the toggle. Possible values are: `large`, `normal`, `small`, `mini`. |
-| `onstyle` | string | "primary" | Style of the on toggle. Possible values are: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark` and with `outline-` prefix |
-| `offstyle` | string | "secondary" | Style of the off toggle. Possible values are: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark` and with `outline-` prefix |
-| `onvalue` | string | _null_ | Sets on state value |
-| `offvalue` | string | _null_ | Sets off state value |
-| `ontitle` | string | _null_ | Title of the on toggle |
-| `offtitle` | string | _null_ | Title of the off toggle |
-| `style` | string | | Appends the value to the class attribute of the toggle. This can be used to apply custom styles. Refer to Custom Styles for reference. |
-| `width` | integer | _null_ | Sets the width of the toggle. if set to _null_, width will be auto-calculated. |
-| `height` | integer | _null_ | Sets the height of the toggle. if set to _null_, height will be auto-calculated. |
-| `tabindex` | integer | 0 | Sets the tabindex of the toggle. |
-| `tristate` | boolean | false | Sets tristate support |
-
-## Methods
-
-Methods can be used to control toggles directly.
-
-```html
-
-
-```
-
-| Method | Example | Description |
-| ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------- |
-| initialize | `toggleDemo.bootstrapToggle()` | Initializes the toggle plugin with options |
-| destroy | `toggleDemo.bootstrapToggle('destroy')` | Destroys the toggle |
-| rerender | `toggleDemo.bootstrapToggle('rerender')` | Rerender toggle with the appropriated size. Useful when parent is collapsed at first. |
-| on | `toggleDemo.bootstrapToggle('on')` | Sets the toggle to 'On' state |
-| off | `toggleDemo.bootstrapToggle('off')` | Sets the toggle to 'Off' state |
-| toggle | `toggleDemo.bootstrapToggle('toggle')` | Toggles the state of the toggle on/off |
-| enable | `toggleDemo.bootstrapToggle('enable')` | Enables the toggle |
-| disable | `toggleDemo.bootstrapToggle('disable')` | Disables the toggle |
-| readonly | `toggleDemo.bootstrapToggle('readonly')` | Disables the toggle but preserve checkbox enabled |
-| indeterminate | `toggleDemo.bootstrapToggle('indeterminate')` | Sets the toggle to 'indeterminate' state |
-| determinate | `toggleDemo.bootstrapToggle('determinate')` | Sets the toggle to 'determinate' state |
-
-# Events
-
-## Event Propagation
-
-Note All events are propagated to and from input element to the toggle.
-
-You should listen to events from the ` ` directly rather than look for custom events.
-
-```html
-
-
-
-```
-
-## Stopping Event Propagation
-
-Passing `true` to the on, off, toggle, determinate and indeterminate methods will enable the silent option to prevent the control from propagating the change event in cases where you want to update the controls on/off state, but do not want to fire the onChange event.
-
-```html
-
-
- On by API (silent)
-
-
- Off by API (silent)
-
-
- On by API (not silent)
-
-
- On by API (not silent)
-
-
-```
-
-## API vs Input
-
-This also means that using the API or Input to trigger events will work both ways.
-
-```html
-
-On by API
-Off by API
-On by Input
-Off by Input
-
-```
-
-# Collaborators welcom!
+# Collaborators welcome!
- :sos: Do you need some help? Open a thread in [GitHub Discussions Q&A](https://github.com/palcarazm/bootstrap5-toggle/discussions/new?category=q-a)
- :bug: Do you find a bug? Open an issue in [GitHub bug report](https://github.com/palcarazm/bootstrap5-toggle/issues/new?template=01-BUG_REPORT.yml)
From 46759e0642a95204b7570db606bd14d4da194abe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 4 Apr 2026 16:54:43 +0200
Subject: [PATCH 05/26] docs: move docs to main branch
---
.github/workflows/deploy-docs.yml | 9 +-
.gitignore | 34 ++-
.npmignore | 32 +-
docs/.gitignore | 6 +
docs/Gruntfile.js | 104 +++++++
docs/LICENSE | 23 ++
docs/package.json | 51 ++++
docs/rollup.config.js | 15 +
docs/src/CNAME | 1 +
docs/src/api/eol/v3 | 6 +
docs/src/api/eol/v4 | 6 +
docs/src/api/eol/v5 | 6 +
docs/src/assets/img/card.png | Bin 0 -> 20824 bytes
docs/src/assets/img/favicon.ico | Bin 0 -> 8686 bytes
docs/src/assets/img/fork-me.webp | Bin 0 -> 3010 bytes
docs/src/assets/img/logo_off.png | Bin 0 -> 6032 bytes
docs/src/assets/img/logo_on.png | Bin 0 -> 5801 bytes
docs/src/assets/img/logo_settings.json | 37 +++
docs/src/css/styles.css | 75 +++++
docs/src/index.html | 113 +++++++
docs/src/js/components/Aside.js | 122 ++++++++
docs/src/js/components/Badge.js | 23 ++
docs/src/js/components/CodeBlock.js | 18 ++
docs/src/js/components/CodePanel.js | 73 +++++
docs/src/js/components/Console.js | 80 +++++
docs/src/js/components/Documentation.js | 30 ++
docs/src/js/components/Example.js | 10 +
docs/src/js/components/Footer.js | 31 ++
docs/src/js/components/Header.js | 67 ++++
docs/src/js/components/Main.js | 21 ++
docs/src/js/components/Notice.js | 89 ++++++
docs/src/js/components/VersionPill.js | 24 ++
.../components/documentation/Accessibility.js | 266 ++++++++++++++++
docs/src/js/components/documentation/Api.js | 20 ++
.../components/documentation/Distribution.js | 105 +++++++
.../js/components/documentation/DocAlert.js | 54 ++++
.../js/components/documentation/DocArticle.js | 57 ++++
.../js/components/documentation/DocSection.js | 16 +
.../src/js/components/documentation/Events.js | 16 +
.../js/components/documentation/Features.js | 38 +++
.../components/documentation/Installation.js | 73 +++++
docs/src/js/components/documentation/Usage.js | 42 +++
.../documentation/api/InputInteraction.js | 167 ++++++++++
.../components/documentation/api/Methods.js | 151 +++++++++
.../components/documentation/api/Options.js | 286 ++++++++++++++++++
.../components/documentation/api/Rerender.js | 61 ++++
.../js/components/documentation/api/State.js | 47 +++
.../documentation/events/CustomEvents.js | 136 +++++++++
.../documentation/events/EventPropagation.js | 49 +++
.../documentation/events/SilencedActions.js | 125 ++++++++
.../documentation/features/AnimationSpeed.js | 58 ++++
.../documentation/features/Colors.js | 49 +++
.../documentation/features/CustomFormValue.js | 50 +++
.../documentation/features/CustomSize.js | 40 +++
.../documentation/features/CustomStyle.js | 63 ++++
.../documentation/features/CustomText.js | 51 ++++
.../documentation/features/CustomTitle.js | 44 +++
.../documentation/features/FormLayout.js | 102 +++++++
.../features/KeyboardInteraction.js | 36 +++
.../documentation/features/OutlineColors.js | 49 +++
.../components/documentation/features/Size.js | 36 +++
.../documentation/features/StateStatus.js | 65 ++++
.../documentation/features/Tooltip.js | 61 ++++
.../documentation/features/Tristate.js | 32 ++
docs/src/js/main.js | 16 +
docs/src/js/tasks/BootsrapDarkmodeToggler.js | 9 +
docs/src/js/tasks/BootstrapToggler.js | 9 +
docs/src/js/tasks/CodeHighlighter.js | 8 +
docs/src/js/tasks/DOMBuilder.js | 17 ++
docs/src/js/tasks/LogoSwitcher.js | 16 +
docs/src/js/tasks/TOCBuilder.js | 13 +
sonar-project.properties | 2 +-
72 files changed, 3620 insertions(+), 21 deletions(-)
create mode 100644 docs/.gitignore
create mode 100644 docs/Gruntfile.js
create mode 100644 docs/LICENSE
create mode 100644 docs/package.json
create mode 100644 docs/rollup.config.js
create mode 100644 docs/src/CNAME
create mode 100644 docs/src/api/eol/v3
create mode 100644 docs/src/api/eol/v4
create mode 100644 docs/src/api/eol/v5
create mode 100644 docs/src/assets/img/card.png
create mode 100644 docs/src/assets/img/favicon.ico
create mode 100644 docs/src/assets/img/fork-me.webp
create mode 100644 docs/src/assets/img/logo_off.png
create mode 100644 docs/src/assets/img/logo_on.png
create mode 100644 docs/src/assets/img/logo_settings.json
create mode 100644 docs/src/css/styles.css
create mode 100644 docs/src/index.html
create mode 100644 docs/src/js/components/Aside.js
create mode 100644 docs/src/js/components/Badge.js
create mode 100644 docs/src/js/components/CodeBlock.js
create mode 100644 docs/src/js/components/CodePanel.js
create mode 100644 docs/src/js/components/Console.js
create mode 100644 docs/src/js/components/Documentation.js
create mode 100644 docs/src/js/components/Example.js
create mode 100644 docs/src/js/components/Footer.js
create mode 100644 docs/src/js/components/Header.js
create mode 100644 docs/src/js/components/Main.js
create mode 100644 docs/src/js/components/Notice.js
create mode 100644 docs/src/js/components/VersionPill.js
create mode 100644 docs/src/js/components/documentation/Accessibility.js
create mode 100644 docs/src/js/components/documentation/Api.js
create mode 100644 docs/src/js/components/documentation/Distribution.js
create mode 100644 docs/src/js/components/documentation/DocAlert.js
create mode 100644 docs/src/js/components/documentation/DocArticle.js
create mode 100644 docs/src/js/components/documentation/DocSection.js
create mode 100644 docs/src/js/components/documentation/Events.js
create mode 100644 docs/src/js/components/documentation/Features.js
create mode 100644 docs/src/js/components/documentation/Installation.js
create mode 100644 docs/src/js/components/documentation/Usage.js
create mode 100644 docs/src/js/components/documentation/api/InputInteraction.js
create mode 100644 docs/src/js/components/documentation/api/Methods.js
create mode 100644 docs/src/js/components/documentation/api/Options.js
create mode 100644 docs/src/js/components/documentation/api/Rerender.js
create mode 100644 docs/src/js/components/documentation/api/State.js
create mode 100644 docs/src/js/components/documentation/events/CustomEvents.js
create mode 100644 docs/src/js/components/documentation/events/EventPropagation.js
create mode 100644 docs/src/js/components/documentation/events/SilencedActions.js
create mode 100644 docs/src/js/components/documentation/features/AnimationSpeed.js
create mode 100644 docs/src/js/components/documentation/features/Colors.js
create mode 100644 docs/src/js/components/documentation/features/CustomFormValue.js
create mode 100644 docs/src/js/components/documentation/features/CustomSize.js
create mode 100644 docs/src/js/components/documentation/features/CustomStyle.js
create mode 100644 docs/src/js/components/documentation/features/CustomText.js
create mode 100644 docs/src/js/components/documentation/features/CustomTitle.js
create mode 100644 docs/src/js/components/documentation/features/FormLayout.js
create mode 100644 docs/src/js/components/documentation/features/KeyboardInteraction.js
create mode 100644 docs/src/js/components/documentation/features/OutlineColors.js
create mode 100644 docs/src/js/components/documentation/features/Size.js
create mode 100644 docs/src/js/components/documentation/features/StateStatus.js
create mode 100644 docs/src/js/components/documentation/features/Tooltip.js
create mode 100644 docs/src/js/components/documentation/features/Tristate.js
create mode 100644 docs/src/js/main.js
create mode 100644 docs/src/js/tasks/BootsrapDarkmodeToggler.js
create mode 100644 docs/src/js/tasks/BootstrapToggler.js
create mode 100644 docs/src/js/tasks/CodeHighlighter.js
create mode 100644 docs/src/js/tasks/DOMBuilder.js
create mode 100644 docs/src/js/tasks/LogoSwitcher.js
create mode 100644 docs/src/js/tasks/TOCBuilder.js
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 7ab8d2d..73ec8ab 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -2,9 +2,9 @@ name: Deploy documentation
on:
push:
- branches: [ "gh-pages" ]
+ branches: [ "v5" ]
pull_request:
- branches: [ "gh-pages" ]
+ branches: [ "v5" ]
workflow_dispatch:
permissions:
@@ -19,6 +19,9 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: docs
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -35,7 +38,7 @@ jobs:
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
- path: './dist'
+ path: ./docs/dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 53e439d..1136f1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,30 @@
-.DS_Store
+# Node modules
node_modules
package-lock.json
+
+# Packaging
+bootstrap5-toggle-*.tgz
+
+# Debug
+debug
+
+# Dist
+js
+!src/main/js
+!docs/src/js
+css
+!docs/src/css
+
+# Logs
*.log
-bootstrap*.tgz
-cypress/videos
-cypress/screenshots
-cypress/downloads
+
+# Cache and config dirs and files
.vscode
.dccache
-js
-css
-debug
-coverage
\ No newline at end of file
+.DS_Store
+
+# Test
+coverage
+cypress/videos
+cypress/screenshots
+cypress/downloads
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
index 9406777..95284e5 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,14 +1,30 @@
-.DS_Store
+# Node modules
node_modules
-*.log
+
+# Packaging
+bootstrap5-toggle-*.tgz
+
+# Debug
debug
-img
+
+# Docs
+docs
+
+# Logs
+*.log
+
+# Cache and config dirs and files
.github
.vscode
-.prettierrc
.dccache
-test
-bootstrap*.tgz
-cypress
+.DS_Store
+.prettierrc
+
+# Cypress and testing
+test/*
+cypress/*
cypress.config.js
-README.template.md
+coverage
+
+# Templates
+*.template*
\ No newline at end of file
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..956b179
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+.dccache
+node_modules
+package-lock.json
+*.log
+dist
\ No newline at end of file
diff --git a/docs/Gruntfile.js b/docs/Gruntfile.js
new file mode 100644
index 0000000..9491fea
--- /dev/null
+++ b/docs/Gruntfile.js
@@ -0,0 +1,104 @@
+module.exports = function (grunt) {
+ const pkg = grunt.file.readJSON("package.json");
+
+ grunt.initConfig({
+ pkg,
+ clean: {
+ dist: ["dist"],
+ },
+ copy: {
+ assets: {
+ expand: true,
+ cwd: "src/assets/",
+ src: "**/*",
+ dest: "dist/assets/",
+ },
+ api: {
+ expand: true,
+ cwd: "src/api/",
+ src: "**/*",
+ dest: "dist/api/",
+ },
+ cname: {
+ src: "CNAME",
+ dest: "dist/CNAME",
+ },
+ html: {
+ src: "src/index.html",
+ dest: "dist/index.html",
+ options: {
+ process: function (content, _srcpath) {
+ return content
+ .replace(/\{\{bootstrapVersion\}\}/g, pkg.dependencies.bootstrap)
+ .replace(/\{\{jqueryVersion\}\}/g, pkg.dependencies.jquery)
+ .replace(
+ /\{\{fontawesomeVersion\}\}/g,
+ pkg.dependencies["@fortawesome/fontawesome-free"],
+ )
+ .replace(
+ /\{\{highlightjsVersion\}\}/g,
+ pkg.dependencies["@highlightjs/cdn-assets"],
+ )
+ .replace(
+ /\{\{highlightjsBadgeVersion\}\}/g,
+ pkg.dependencies["highlightjs-badge"],
+ )
+ .replace(
+ /\{\{bootstrapTocVersion\}\}/g,
+ pkg.config.externalDependencies["afeld/bootstrap-toc"],
+ )
+ .replace(
+ /\{\{bsDarkmodeToggleVersion\}\}/g,
+ pkg.dependencies["bs-darkmode-toggle"],
+ )
+ .replace(
+ /\{\{bootstrap5ToggleVersion\}\}/g,
+ pkg.dependencies["bootstrap5-toggle"],
+ );
+ },
+ },
+ },
+ },
+ exec: {
+ rollup: "npx rollup -c",
+ postcss:
+ "npx postcss src/css/styles.css -o dist/css/styles.min.css --map --env production",
+ },
+ watch: {
+ assets: {
+ files: ["src/assets/**/*"],
+ tasks: ["copy:assets"],
+ options: { spawn: false },
+ },
+ api: {
+ files: ["src/api/**/*"],
+ tasks: ["copy:api"],
+ options: { spawn: false },
+ },
+ css: {
+ files: ["src/css/styles.css"],
+ tasks: ["exec:postcss"],
+ options: { spawn: false },
+ },
+ js: {
+ files: ["src/js/**/*.js"],
+ tasks: ["exec:rollup"],
+ options: { spawn: false },
+ },
+ html: {
+ files: ["src/index.html"],
+ tasks: ["copy:html"],
+ options: { spawn: false },
+ },
+ },
+ });
+
+ grunt.loadNpmTasks("grunt-contrib-clean");
+ grunt.loadNpmTasks("grunt-contrib-copy");
+ grunt.loadNpmTasks("grunt-exec");
+ grunt.loadNpmTasks("grunt-contrib-watch");
+
+ grunt.registerTask("build", ["clean", "copy", "exec:rollup", "exec:postcss"]);
+
+ grunt.registerTask("dev", ["build", "watch"]);
+};
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644
index 0000000..8687b23
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+- Copyright (c) 2011-2014 Min Hur, The New York Times Company
+- Copyright (c) 2018-2019 Brent Ely
+- Copyright (c) 2022 Pablo Alcaraz Martínez
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..4669ea8
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "bootstrap5-toggle-doc",
+ "version": "1.0.0",
+ "description": "Documentation of bootstrap5-toggle",
+ "main": "src/js/main.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/palcarazm/bootstrap5-toggle.git"
+ },
+ "keywords": [
+ "documentation"
+ ],
+ "author": {
+ "name": "Pablo Alcaraz Martínez",
+ "url": "https://github.com/palcarazm/"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/palcarazm/bootstrap5-toggle/issues"
+ },
+ "homepage": "https://palcarazm.github.io/bootstrap5-toggle/",
+ "scripts": {
+ "build": "grunt build",
+ "dev": "grunt dev"
+ },
+ "config": {
+ "externalDependencies": {
+ "afeld/bootstrap-toc": "1.0.1"
+ }
+ },
+ "dependencies": {
+ "@fortawesome/fontawesome-free": "7.2.0",
+ "@highlightjs/cdn-assets": "11.11.1",
+ "bootstrap": "5.3.8",
+ "bootstrap5-toggle": "5.3.0",
+ "bs-darkmode-toggle": "1.0.0-rc1",
+ "highlightjs-badge": "0.1.9",
+ "jquery": "4.0.0"
+ },
+ "devDependencies": {
+ "@rollup/plugin-terser": "^1.0.0",
+ "grunt": "^1.6.1",
+ "grunt-contrib-clean": "^2.0.1",
+ "grunt-contrib-copy": "^1.0.0",
+ "grunt-contrib-watch": "^1.1.0",
+ "grunt-exec": "^3.0.0",
+ "postcss": "^8.5.6",
+ "postcss-cli": "^11.0.1",
+ "rollup": "^4.59.0"
+ }
+}
diff --git a/docs/rollup.config.js b/docs/rollup.config.js
new file mode 100644
index 0000000..dc45157
--- /dev/null
+++ b/docs/rollup.config.js
@@ -0,0 +1,15 @@
+const terser = require("@rollup/plugin-terser");
+
+module.exports = [
+ {
+ input: "src/js/main.js",
+ output: [
+ {
+ file: "dist/js/bundle.min.js",
+ format: "umd",
+ sourcemap: true,
+ plugins: [terser()]
+ }
+ ]
+ }
+];
\ No newline at end of file
diff --git a/docs/src/CNAME b/docs/src/CNAME
new file mode 100644
index 0000000..e52f285
--- /dev/null
+++ b/docs/src/CNAME
@@ -0,0 +1 @@
+https://palcarazm.github.io/bootstrap5-toggle/
diff --git a/docs/src/api/eol/v3 b/docs/src/api/eol/v3
new file mode 100644
index 0000000..efe16d8
--- /dev/null
+++ b/docs/src/api/eol/v3
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "label": "EOL",
+ "message": "ended",
+ "color": "critical"
+}
diff --git a/docs/src/api/eol/v4 b/docs/src/api/eol/v4
new file mode 100644
index 0000000..a92a108
--- /dev/null
+++ b/docs/src/api/eol/v4
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "label": "EOL",
+ "message": "critical support since 30 Jun 2026",
+ "color": "important"
+}
diff --git a/docs/src/api/eol/v5 b/docs/src/api/eol/v5
new file mode 100644
index 0000000..51ee0a9
--- /dev/null
+++ b/docs/src/api/eol/v5
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "label": "EOL",
+ "message": "active support",
+ "color": "success"
+}
diff --git a/docs/src/assets/img/card.png b/docs/src/assets/img/card.png
new file mode 100644
index 0000000000000000000000000000000000000000..da0084cc068914fc6aec0c69a4b54cc997a3e2cb
GIT binary patch
literal 20824
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
literal 0
HcmV?d00001
diff --git a/docs/src/assets/img/fork-me.webp b/docs/src/assets/img/fork-me.webp
new file mode 100644
index 0000000000000000000000000000000000000000..21c460ded9f3caaea4ba962df85e589079497252
GIT binary patch
literal 3010
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
literal 0
HcmV?d00001
diff --git a/docs/src/assets/img/logo_off.png b/docs/src/assets/img/logo_off.png
new file mode 100644
index 0000000000000000000000000000000000000000..db7bf4877b8eef2a7f2ac222bbb20d996ff2e5a1
GIT binary patch
literal 6032
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
literal 0
HcmV?d00001
diff --git a/docs/src/assets/img/logo_on.png b/docs/src/assets/img/logo_on.png
new file mode 100644
index 0000000000000000000000000000000000000000..b5fcc7c3ad6b5532399977d32a661af71874a61a
GIT binary patch
literal 5801
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
literal 0
HcmV?d00001
diff --git a/docs/src/assets/img/logo_settings.json b/docs/src/assets/img/logo_settings.json
new file mode 100644
index 0000000..7ee7aa1
--- /dev/null
+++ b/docs/src/assets/img/logo_settings.json
@@ -0,0 +1,37 @@
+{
+ "version": "v1.0.0",
+ "components": {
+ "icon": {
+ "name": "icon",
+ "text": "",
+ "color": "#7337cd",
+ "fontFamily": "\"Font Awesome 5 Free\"",
+ "fontWeight": "900",
+ "fontSize": 30,
+ "letterSpacing": 0
+ },
+ "main": {
+ "name": "main",
+ "text": "Bootstrap 5",
+ "color": "#7337cd",
+ "fontFamily": "Quicksand",
+ "fontWeight": "normal",
+ "fontSize": 20,
+ "letterSpacing": 0
+ },
+ "accent": {
+ "name": "accent",
+ "text": "Toggle",
+ "color": "#ffffff",
+ "fontFamily": "Quicksand",
+ "fontWeight": "bold",
+ "fontSize": 20,
+ "letterSpacing": 0
+ }
+ },
+ "global": {
+ "offset": { "size": 0, "color": "#e2e2e2" },
+ "layout": "HORIZONTAL",
+ "shapes": true
+ }
+}
diff --git a/docs/src/css/styles.css b/docs/src/css/styles.css
new file mode 100644
index 0000000..f47a6d2
--- /dev/null
+++ b/docs/src/css/styles.css
@@ -0,0 +1,75 @@
+a {
+ text-decoration: none;
+}
+
+code.hljs {
+ border: 1px solid #ccc;
+ padding: 1em;
+ white-space: pre;
+ margin-bottom: 10px;
+}
+
+code.hljs .hljs-tag {
+ display: inline-flex;
+}
+
+code.hljs .hljs-tag .hljs-attr {
+ margin-left: 0.3rem;
+}
+
+.example {
+ position: relative;
+ border: 1px solid #ccc;
+ padding: 1em 1em 0.5em 1em;
+ border-radius: 4px 4px 0 0;
+}
+
+.example:after {
+ content: "Example";
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ color: #9da0a4;
+ border-radius: 0px 4px 0px 4px;
+ border-width: 0px 0px 1px 1px;
+}
+
+.example + code.hljs {
+ border-top: 0;
+ border-radius: 0px 0px 4px 4px;
+}
+
+.example > * {
+ margin-bottom: 10px;
+}
+
+.example > div.toggle {
+ margin-right: 10px;
+}
+nav#toc .navbar-nav {
+ flex-direction: column;
+}
+
+nav[data-toggle="toc"] .nav > li > a {
+ color: var(--gray);
+}
+nav[data-toggle="toc"] .nav-link.active,
+nav[data-toggle="toc"] .nav-link.active:focus,
+nav[data-toggle="toc"] .nav-link.active:hover {
+ color: var(--purple);
+ border-color: var(--purple);
+}
+.bs-darkmode-dark nav[data-toggle="toc"] .nav-link.active,
+.bs-darkmode-dark nav[data-toggle="toc"] .nav-link.active:focus,
+.bs-darkmode-dark nav[data-toggle="toc"] .nav-link.active:hover {
+ filter: brightness(1.4);
+}
+
+.bs-darkmode-dark .img-toggle img {
+ filter: brightness(1.7);
+}
diff --git a/docs/src/index.html b/docs/src/index.html
new file mode 100644
index 0000000..51add85
--- /dev/null
+++ b/docs/src/index.html
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Bootstrap 5 Toggle | v5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/src/js/components/Aside.js b/docs/src/js/components/Aside.js
new file mode 100644
index 0000000..31630c0
--- /dev/null
+++ b/docs/src/js/components/Aside.js
@@ -0,0 +1,122 @@
+import Badge from "./Badge";
+
+class Aside {
+ static #badges = [
+ {
+ name: "sponsor",
+ href: "https://github.com/sponsors/palcarazm",
+ imgSrc:
+ "https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#white",
+ },
+ {
+ name: "GitHub repository",
+ href: "https://github.com/palcarazm/bootstrap5-toggle",
+ imgSrc:
+ "https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white",
+ },
+ {
+ name: "License",
+ href: "https://github.com/palcarazm/bootstrap5-toggle/blob/v5/LICENSE",
+ imgSrc:
+ "https://img.shields.io/github/license/palcarazm/bootstrap5-toggle.svg?style=for-the-badge",
+ },
+ ];
+
+ static build() {
+ const aside = document.createElement("aside");
+ aside.className = "col-md-3 col-lg-2";
+
+ const container = document.createElement("div");
+ container.className = "position-sticky";
+ container.style.top = "0rem";
+
+ container.append(
+ Aside.#logoContainer(),
+ Aside.#navBar(),
+ Aside.#badgeBar(),
+ );
+
+ aside.appendChild(container);
+ return aside;
+ }
+
+ static #logoContainer() {
+ const container = document.createElement("div");
+ container.className =
+ "d-none d-md-block position-relative pb-5 mb-3 img-toggle";
+
+ const logoOn = document.createElement("img");
+ logoOn.className =
+ "d-block mx-auto position-absolute top-50 start-50 translate-middle w-100";
+ logoOn.src = "assets/img/logo_on.png";
+ logoOn.alt = "Bootstrap 5 Toggle";
+ container.appendChild(logoOn);
+
+ const logoOff = document.createElement("img");
+ logoOff.className =
+ "d-block mx-auto position-absolute top-50 start-50 translate-middle w-100 invisible";
+ logoOff.src = "assets/img/logo_off.png";
+ logoOff.alt = "Bootstrap 5 Toggle";
+ container.appendChild(logoOff);
+
+ return container;
+ }
+
+ static #navBar() {
+ const navBar = document.createElement("nav");
+ navBar.className =
+ "navbar navbar-expand-md navbar-light flex-md-column p-0";
+ navBar.setAttribute("aria-label", "Site menu");
+
+ const button = document.createElement("button");
+ button.className = "navbar-toggler";
+ button.type = "button";
+ button.setAttribute("data-bs-toggle", "collapse");
+ button.setAttribute("data-bs-target", "#navbarSupportedContent");
+ button.setAttribute("aria-controls", "navbarSupportedContent");
+ button.setAttribute("aria-expanded", "false");
+ button.setAttribute("aria-label", "Toggle navigation");
+
+ const span = document.createElement("span");
+ span.className = "navbar-toggler-icon";
+
+ button.appendChild(span);
+ navBar.appendChild(button);
+
+ const darkModeToggle = document.createElement("div");
+ darkModeToggle.dataset.plugin = "bs-darkmode-toggle";
+ darkModeToggle.dataset.allowCookie = "";
+
+ navBar.appendChild(darkModeToggle);
+
+ const collapseDiv = document.createElement("div");
+ collapseDiv.className = "collapse navbar-collapse";
+ collapseDiv.id = "navbarSupportedContent";
+
+ const navMenu = document.createElement("nav");
+ navMenu.id = "toc";
+ navMenu.setAttribute("data-toggle", "toc");
+ navMenu.setAttribute("aria-label", "Table of contents");
+
+ collapseDiv.appendChild(navMenu);
+ navBar.appendChild(collapseDiv);
+
+ return navBar;
+ }
+
+ static #badgeBar() {
+ const badges = document.createElement("div");
+ badges.className = "d-none d-md-flex flex-column align-items-center mt-4";
+
+ Aside.#badges.forEach((badge, index) => {
+ badges.appendChild(
+ Badge.build({
+ ...badge,
+ className: index === 0 ? "mt-0" : "mt-1",
+ }),
+ );
+ });
+ return badges;
+ }
+}
+export default Aside;
diff --git a/docs/src/js/components/Badge.js b/docs/src/js/components/Badge.js
new file mode 100644
index 0000000..a925bf4
--- /dev/null
+++ b/docs/src/js/components/Badge.js
@@ -0,0 +1,23 @@
+class Badge {
+ static build({ name, href, imgSrc, className = "" }) {
+ const container = document.createElement("div");
+ container.className = className;
+
+ const badge = { name, href, imgSrc };
+ const badgeLink = document.createElement("a");
+ badgeLink.href = badge.href;
+ badgeLink.title = badge.name;
+ badgeLink.target = "_blank";
+ badgeLink.rel = "noopener noreferrer";
+
+ const badgeImg = document.createElement("img");
+ badgeImg.src = badge.imgSrc;
+ badgeImg.alt = badge.name;
+
+ badgeLink.appendChild(badgeImg);
+ container.appendChild(badgeLink);
+ return container;
+ }
+}
+
+export default Badge;
diff --git a/docs/src/js/components/CodeBlock.js b/docs/src/js/components/CodeBlock.js
new file mode 100644
index 0000000..f097664
--- /dev/null
+++ b/docs/src/js/components/CodeBlock.js
@@ -0,0 +1,18 @@
+import Example from "./Example";
+
+class CodeBlock {
+ static build({ language, code, example = [] }) {
+ const codeBlock = document.createElement("div");
+ if (example.length > 0) codeBlock.appendChild(Example.build(example));
+ const pre = document.createElement("pre");
+ const codeElement = document.createElement("code");
+ codeElement.className = `highlight language-${language}`;
+ codeElement.textContent = code;
+
+ pre.appendChild(codeElement);
+ codeBlock.appendChild(pre);
+ return codeBlock;
+ }
+}
+
+export default CodeBlock;
diff --git a/docs/src/js/components/CodePanel.js b/docs/src/js/components/CodePanel.js
new file mode 100644
index 0000000..cafeadf
--- /dev/null
+++ b/docs/src/js/components/CodePanel.js
@@ -0,0 +1,73 @@
+import CodeBlock from "./CodeBlock";
+import Example from "./Example";
+
+class CodePanel {
+ static build({
+ name,
+ language,
+ tabs = ["ECMAScript", "jQuery"],
+ contents,
+ example = [],
+ }) {
+ const codePanel = document.createElement("div");
+
+ if (tabs.length !== contents.length)
+ throw new Error("Tabs and contents arrays must have the same length.");
+
+ if (tabs.length === 0 || contents.length === 0)
+ throw new Error("Tabs and contents arrays cannot be empty.");
+
+ codePanel.appendChild(CodePanel.#navTabs(name, tabs));
+ if (example.length > 0) codePanel.appendChild(Example.build(example));
+ codePanel.appendChild(CodePanel.#navPanes(name, language, contents));
+ return codePanel;
+ }
+
+ static #navTabs(name, labels) {
+ const navTabs = document.createElement("ul");
+ navTabs.className = "nav nav-tabs";
+ navTabs.role = "tablist";
+
+ labels.forEach((tabName, index) => {
+ const navItem = document.createElement("li");
+ navItem.className = "nav-item";
+ navItem.role = "presentation";
+
+ const navLink = document.createElement("button");
+ navLink.className = `nav-link ${index === 0 ? "active" : ""}`;
+ navLink.id = `${name}-${index}-tab`;
+ navLink.role = "tab";
+ navLink.type = "button";
+ navLink.setAttribute("data-bs-toggle", "tab");
+ navLink.setAttribute("data-bs-target", `#${name}-${index}`);
+ navLink.setAttribute("aria-controls", `${name}-${index}`);
+ navLink.setAttribute("aria-selected", index === 0 ? "true" : "false");
+ navLink.textContent = tabName;
+
+ navItem.append(navLink);
+ navTabs.append(navItem);
+ });
+
+ return navTabs;
+ }
+
+ static #navPanes(name, language, contents) {
+ const navPanes = document.createElement("div");
+ navPanes.className = "tab-content";
+
+ contents.forEach((code, index) => {
+ const tabPane = document.createElement("div");
+ tabPane.className = `tab-pane fade ${index === 0 ? "show active" : ""}`;
+ tabPane.id = `${name}-${index}`;
+ tabPane.role = "tabpanel";
+ tabPane.tabindex = index === 0 ? 0 : -1;
+ tabPane.setAttribute("aria-labelledby", `${name}-${index}-tab`);
+
+ tabPane.append(CodeBlock.build({ language, code }));
+ navPanes.append(tabPane);
+ });
+ return navPanes;
+ }
+}
+
+export default CodePanel;
diff --git a/docs/src/js/components/Console.js b/docs/src/js/components/Console.js
new file mode 100644
index 0000000..796b377
--- /dev/null
+++ b/docs/src/js/components/Console.js
@@ -0,0 +1,80 @@
+class Console {
+ #element;
+ #pre;
+
+ constructor() {
+ this.#element = document.createElement("div");
+ this.#element.classList.add("d-flex", "mt-2", "align-items-start");
+
+ const aside = document.createElement("div");
+ aside.className = "d-flex flex-column me-2";
+
+ const legend = document.createElement("div");
+ legend.className = "font-monospace text-muted";
+ legend.textContent = "Console";
+ aside.append(legend);
+
+ const btnGroup = document.createElement("div");
+ btnGroup.className = "btn-group btn-group-sm btn-group-vertical";
+ btnGroup.setAttribute("role", "group");
+ aside.append(btnGroup);
+
+ const clearBtn = document.createElement("button");
+ clearBtn.className = "btn btn-outline-secondary";
+ clearBtn.title = "Clear";
+ clearBtn.innerHTML = ` `;
+ clearBtn.addEventListener("click", () => this.clear());
+ btnGroup.append(clearBtn);
+
+ const console = document.createElement("div");
+ console.className = "flex-grow-1 me-2";
+ this.#pre = document.createElement("pre");
+ console.append(this.#pre);
+
+ this.#element.append(aside, console);
+ }
+
+ get htmlElement() {
+ return this.#element;
+ }
+
+ #handleMode(mode) {
+ switch (mode) {
+ case "replace":
+ this.clear();
+ return;
+ case "append":
+ return;
+ default:
+ throw new Error("Unsupported mode");
+ }
+ }
+
+ clear() {
+ this.#pre.innerHTML = "";
+ }
+
+ json({ mode, data }) {
+ this.#handleMode(mode);
+
+ const code = document.createElement("code");
+ code.className = "highlight language-json";
+ code.textContent = JSON.stringify(data, null, 2);
+
+ this.#pre.append(code);
+ hljs.highlightElement(code);
+ }
+
+ log({ level = "info", mode, data }) {
+ this.#handleMode(mode);
+
+ const code = document.createElement("code");
+ code.className = "highlight language-shell";
+ code.textContent = `> ${new Date().toISOString()} - ${data}`;
+
+ this.#pre.append(code);
+ hljs.highlightElement(code);
+ }
+}
+
+export default Console;
diff --git a/docs/src/js/components/Documentation.js b/docs/src/js/components/Documentation.js
new file mode 100644
index 0000000..2aa0dde
--- /dev/null
+++ b/docs/src/js/components/Documentation.js
@@ -0,0 +1,30 @@
+import Distribution from "./documentation/Distribution";
+import Installation from "./documentation/Installation";
+import Usage from "./documentation/Usage";
+import Features from "./documentation/Features";
+import Api from "./documentation/Api";
+import Events from "./documentation/Events";
+import Accessibility from "./documentation/Accessibility";
+
+class Documentation {
+ static #sections = [
+ Distribution.build(),
+ Installation.build(),
+ Usage.build(),
+ Features.build(),
+ Api.build(),
+ Events.build(),
+ Accessibility.build(),
+ ];
+
+ static build() {
+ const documentation = document.createElement("div");
+ documentation.className = "col-md-9 col-lg-10";
+
+ documentation.append(...Documentation.#sections);
+
+ return documentation;
+ }
+}
+
+export default Documentation;
diff --git a/docs/src/js/components/Example.js b/docs/src/js/components/Example.js
new file mode 100644
index 0000000..0ef9eab
--- /dev/null
+++ b/docs/src/js/components/Example.js
@@ -0,0 +1,10 @@
+class Example {
+ static build(examples) {
+ const example = document.createElement("div");
+ example.className = "example";
+ example.append(...examples);
+ return example;
+ }
+}
+
+export default Example;
diff --git a/docs/src/js/components/Footer.js b/docs/src/js/components/Footer.js
new file mode 100644
index 0000000..b23566e
--- /dev/null
+++ b/docs/src/js/components/Footer.js
@@ -0,0 +1,31 @@
+class Footer {
+ static #userAttributions = [
+ `Original by Min Hur `,
+ `Bootstrap 4 by Brent Ely `,
+ `Bootstrap 5 by Pablo Alcaraz Martínez `,
+ ];
+
+ static build() {
+ const footer = document.createElement("footer");
+ footer.className = "bg-light border-top p-4";
+
+ const container = document.createElement("div");
+ container.className = "container";
+
+ const content = document.createElement("div");
+ content.className =
+ "d-flex font-weight-light flex-column flex-md-row justify-content-around align-items-center mb-4";
+
+ Footer.#userAttributions.forEach((attribution) => {
+ const span = document.createElement("span");
+ span.innerHTML = attribution;
+ content.appendChild(span);
+ });
+
+ container.appendChild(content);
+ footer.appendChild(container);
+ return footer;
+ }
+}
+
+export default Footer;
diff --git a/docs/src/js/components/Header.js b/docs/src/js/components/Header.js
new file mode 100644
index 0000000..bb0f31b
--- /dev/null
+++ b/docs/src/js/components/Header.js
@@ -0,0 +1,67 @@
+class Header {
+ static build() {
+ const header = document.createElement("header");
+ header.appendChild(Header.#forkMeLink());
+ header.appendChild(Header.#logoContainer());
+ return header;
+ }
+
+ static #forkMeLink() {
+ const forkMeLink = document.createElement("a");
+ forkMeLink.className = "position-absolute top-0 end-0 d-none d-md-inline";
+ forkMeLink.href = "https://github.com/palcarazm/bootstrap5-toggle";
+ forkMeLink.target = "_blank";
+ forkMeLink.rel = "noopener noreferrer";
+
+ const img = document.createElement("img");
+ img.loading = "lazy";
+ img.width = "149";
+ img.height = "149";
+ img.src = "assets/img/fork-me.webp";
+ img.className = "attachment-full size-full";
+ img.alt = "Fork me on GitHub";
+ img.setAttribute("data-recalc-dims", "1");
+
+ forkMeLink.appendChild(img);
+ return forkMeLink;
+ }
+
+ static #logoContainer() {
+ const container = document.createElement("div");
+ container.className = "px-4 py-5 my-5 text-center";
+
+ const logoWrapper = document.createElement("div");
+ logoWrapper.className = "position-relative mx-auto mb-4 img-toggle";
+
+ const logoOn = document.createElement("img");
+ logoOn.className =
+ "d-block mx-auto position-absolute top-50 start-50 translate-middle";
+ logoOn.src = "assets/img/logo_on.png";
+ logoOn.alt = "Bootstrap 5 Toggle";
+ logoWrapper.appendChild(logoOn);
+
+ const logoOff = document.createElement("img");
+ logoOff.className =
+ "d-block mx-auto position-absolute top-50 start-50 translate-middle invisible";
+ logoOff.src = "assets/img/logo_off.png";
+ logoOff.alt = "Bootstrap 5 Toggle";
+ logoWrapper.appendChild(logoOff);
+
+ container.appendChild(logoWrapper);
+
+ const heading = document.createElement("div");
+ heading.className = "col-lg-6 mx-auto";
+
+ const paragraph = document.createElement("p");
+ paragraph.className = "lead mb-4";
+ paragraph.textContent =
+ "Bootstrap 5 Toggle is a bootstrap plugin that converts checkboxes into responsive toggles.";
+
+ heading.appendChild(paragraph);
+ container.appendChild(heading);
+
+ return container;
+ }
+}
+
+export default Header;
diff --git a/docs/src/js/components/Main.js b/docs/src/js/components/Main.js
new file mode 100644
index 0000000..4d54c81
--- /dev/null
+++ b/docs/src/js/components/Main.js
@@ -0,0 +1,21 @@
+import Aside from "./Aside";
+import Documentation from "./Documentation";
+
+class Main {
+ static #sections = [Aside.build(), Documentation.build()];
+
+ static build() {
+ const main = document.createElement("main");
+ main.className = "container mb-3";
+
+ const row = document.createElement("div");
+ row.className = "row g5";
+
+ row.append(...Main.#sections);
+ main.appendChild(row);
+
+ return main;
+ }
+}
+
+export default Main;
diff --git a/docs/src/js/components/Notice.js b/docs/src/js/components/Notice.js
new file mode 100644
index 0000000..4fa980a
--- /dev/null
+++ b/docs/src/js/components/Notice.js
@@ -0,0 +1,89 @@
+class Notice {
+ static #HTMLTextCols = [
+ `Dark mode integration (check out Notice plugin )`,
+ `Includes a mini (-xs) size`,
+ `With jQuery and vanilla JavaScript (ECMAS) interfaces`,
+ `Supports three states toggle`,
+ ];
+
+ static build() {
+ const section = document.createElement("section");
+ section.className = "container my-5";
+ section.id = "notice";
+ section.appendChild(Notice.#noticeAlert());
+ return section;
+ }
+
+ static #noticeAlert() {
+ const notice = document.createElement("div");
+ notice.className = "alert alert-success";
+ notice.setAttribute("role", "alert");
+
+ const h1 = document.createElement("h1");
+ h1.className = "alert-heading fs-2";
+ h1.textContent = "Made for Bootstrap 5!";
+ notice.appendChild(h1);
+
+ const p = document.createElement("p");
+ p.className = "font-weight-light fs-5";
+ p.textContent = `This page and all of the switch buttons shown are running on Bootstrap v${versions.bootstrap} and Bootstrap5-toggle v${versions.bootstrap5Toggle}.`;
+ notice.appendChild(p);
+
+ const hr = document.createElement("hr");
+ notice.appendChild(hr);
+
+ notice.appendChild(Notice.#noticePanel());
+
+ return notice;
+ }
+
+ static #noticePanel() {
+ const panel = document.createElement("div");
+ panel.className = "row";
+ Notice.#HTMLTextCols.forEach((HTMLTextCol) => {
+ panel.appendChild(Notice.#noticePanelItem(HTMLTextCol));
+ });
+
+ return panel;
+ }
+
+ static #noticePanelItem(HTMLTextCol) {
+ const panelItem = document.createElement("div");
+ panelItem.className = "col-12 col-md-3 mb-1 mb-md-0";
+
+ const row = document.createElement("div");
+ row.className = "row align-items-center h-100";
+
+ const textCol = document.createElement("div");
+ textCol.className = "col";
+ textCol.innerHTML = HTMLTextCol;
+
+ row.append(Notice.#noticeIconCol(), textCol);
+ panelItem.appendChild(row);
+
+ return panelItem;
+ }
+
+ static #noticeIconCol() {
+ const iconCol = document.createElement("div");
+ iconCol.className = "col-auto p-0 p-md-2 text-right";
+
+ const iconLarge = document.createElement("i");
+ iconLarge.className =
+ "fa-solid fa-circle-check text-success d-none d-sm-none d-md-none d-lg-block";
+ iconLarge.setAttribute("style", "font-size:48px");
+ iconLarge.setAttribute("aria-hidden", "true");
+ iconCol.appendChild(iconLarge);
+
+ const iconSmall = document.createElement("i");
+ iconSmall.className =
+ "fa-solid fa-circle-check text-success d-block d-sm-block d-md-block d-lg-none ";
+ iconSmall.setAttribute("style", "font-size:24px");
+ iconSmall.setAttribute("aria-hidden", "true");
+ iconCol.appendChild(iconSmall);
+
+ return iconCol;
+ }
+}
+
+export default Notice;
diff --git a/docs/src/js/components/VersionPill.js b/docs/src/js/components/VersionPill.js
new file mode 100644
index 0000000..0a90de6
--- /dev/null
+++ b/docs/src/js/components/VersionPill.js
@@ -0,0 +1,24 @@
+class VersionPill {
+ static build({ version, action }) {
+ let color;
+ switch (action) {
+ case "SINCE":
+ color = "success";
+ break;
+ case "DEPRECATED":
+ color = "warning";
+ break;
+ case "REMOVED":
+ color = "danger";
+ break;
+ default:
+ throw new Error("Unsupported action");
+ }
+ const pill = document.createElement("span");
+ pill.className = `badge rounded-pill bg-${color} px-2`;
+ pill.textContent = `${action} v${version}`;
+ return pill;
+ }
+}
+
+export default VersionPill;
diff --git a/docs/src/js/components/documentation/Accessibility.js b/docs/src/js/components/documentation/Accessibility.js
new file mode 100644
index 0000000..83d4318
--- /dev/null
+++ b/docs/src/js/components/documentation/Accessibility.js
@@ -0,0 +1,266 @@
+import DocSection from "./DocSection";
+import DocArticle from "./DocArticle";
+
+class Accessibility extends DocSection {
+ static build() {
+ return super.build("accessibility", "ARIA Accessibility", [
+ DocArticle.build({
+ title: "Key Accessibility Features",
+ description: Accessibility.#featuresCards(),
+ versionPill: { action: "SINCE", version: "5.3.0" },
+ }),
+ DocArticle.build({
+ title: "WCAG 2.2 Compliance",
+ description: Accessibility.#wcagCompliance(),
+ className: "mt-3",
+ versionPill: { action: "SINCE", version: "5.3.0" },
+ }),
+ ]);
+ }
+
+ static #features = [
+ {
+ title: "Semantic Role Implementation",
+ description: `Each toggle switch is assigned the role="switch" attribute, allowing screen readers to identify it as a toggleable control.`,
+ },
+ {
+ title: "State Representation",
+ description: `The aria-checked attribute dynamically reflects the toggle's state (on/off), providing real-time feedback to assistive technologies.`,
+ },
+ {
+ title: "Labeling",
+ description: `Toggles can be associated with descriptive labels using the aria-labelledby or aria-label attributes, enhancing clarity for screen reader users.`,
+ },
+
+ {
+ title: "Keyboard Navigation",
+ description: `The toggle supports keyboard navigation with TAB and ENTER keys, ensuring full accessibility for users who rely on keyboard input.`,
+ },
+ {
+ title: "Indeterminate State Accessibility",
+ description: `When a toggle is in an indeterminate state, it uses the aria-checked="mixed" attribute to inform assistive technologies of its unique status.`,
+ },
+ {
+ title: "Focus Management",
+ description: `The toggle is fully focusable and provides visual focus indicators, aiding users in tracking their navigation through the interface.`,
+ },
+ ];
+
+ static #featuresCards() {
+ const fragment = document.createDocumentFragment();
+
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap Toggle is built with accessibility in mind. The implementation ensures seamless compatibility with screen readers, keyboard navigation, and other assistive technologies.`;
+ fragment.appendChild(description);
+
+ const container = document.createElement("div");
+ container.classList.add(
+ "row",
+ "row-cols-1",
+ "row-cols-md-2",
+ "row-cols-xl-3",
+ "g-4",
+ );
+ Accessibility.#features.forEach((feature) => {
+ const col = document.createElement("div");
+ col.classList.add("col");
+
+ const card = document.createElement("div");
+ card.classList.add("card");
+
+ const cardHeader = document.createElement("div");
+ cardHeader.classList.add("card-header");
+ cardHeader.textContent = feature.title;
+ card.appendChild(cardHeader);
+
+ const cardBody = document.createElement("div");
+ cardBody.classList.add("card-body");
+ const description = document.createElement("p");
+ description.classList.add("card-text");
+ description.innerHTML = feature.description;
+ cardBody.appendChild(description);
+ card.appendChild(cardBody);
+
+ col.appendChild(card);
+ container.appendChild(col);
+ });
+
+ fragment.appendChild(container);
+ return fragment;
+ }
+
+ static #wcagChecklist = [
+ {
+ criterion: "1.3.1 Info and Relationships",
+ url: "https://www.w3.org/TR/WCAG22/#info-and-relationships",
+ compliance: "Compliant",
+ evidence:
+ "role='switch', proper ARIA attributes, semantic structure",
+ },
+ {
+ criterion: "1.4.4 Resize Text",
+ url: "https://www.w3.org/TR/WCAG22/#resize-text",
+ compliance: "Compliant",
+ evidence: "Uses relative units, responsive design",
+ },
+ {
+ criterion: "2.1.1 Keyboard",
+ url: "https://www.w3.org/TR/WCAG22/#keyboard",
+ compliance: "Compliant",
+ evidence:
+ "Responds to Space and Enter keys, all functionality keyboard accessible",
+ },
+ {
+ criterion: "2.1.2 No Keyboard Trap",
+ url: "https://www.w3.org/TR/WCAG22/#no-keyboard-trap",
+ compliance: "Compliant",
+ evidence:
+ "tabindex=0 with proper focus management, users can navigate away",
+ },
+ {
+ criterion: "2.1.4 Character Key Shortcuts",
+ url: "https://www.w3.org/TR/WCAG22/#character-key-shortcuts",
+ compliance: "Compliant",
+ evidence: "No single-character shortcuts used",
+ },
+ {
+ criterion: "2.4.3 Focus Order",
+ url: "https://www.w3.org/TR/WCAG22/#focus-order",
+ compliance: "Compliant",
+ evidence: "tabindex respects DOM order",
+ notice:
+ "Component follows logical DOM order; integration depends on page structure",
+ },
+ {
+ criterion: "2.4.7 Focus Visible",
+ url: "https://www.w3.org/TR/WCAG22/#focus-visible",
+ compliance: "Partially",
+ evidence: "Programmatic focus management implemented",
+ notice:
+ "Visual focus indicators require CSS styling from consuming application, component defaults style meet criterion",
+ },
+ {
+ criterion: "2.4.11 Focus Not Obscured (Minimum)",
+ url: "https://www.w3.org/TR/WCAG22/#focus-not-obscured-minimum",
+ compliance: "Partially",
+ evidence: "Programmatic focus management implemented",
+ notice:
+ "Component doesn't obscure its own focus, but page-level layout/styling may affect visibility",
+ },
+ {
+ criterion: "2.4.12 Focus Not Obscured (Enhanced)",
+ url: "https://www.w3.org/TR/WCAG22/#focus-not-obscured-enhanced",
+ compliance: "Partially",
+ evidence: "Programmatic focus management implemented",
+ notice:
+ "Same as 2.4.11; component-level focus is clear but dependent on page context",
+ },
+ {
+ criterion: "2.5.3 Label in Name",
+ url: "https://www.w3.org/TR/WCAG22/#label-in-name",
+ compliance: "Compliant",
+ evidence: "aria-label matches visual label when provided",
+ },
+ {
+ criterion: "3.2.1 On Focus",
+ url: "https://www.w3.org/TR/WCAG22/#on-focus",
+ compliance: "Compliant",
+ evidence: "Focus does not trigger unexpected context changes",
+ },
+ {
+ criterion: "3.3.2 Labels or Instructions",
+ url: "https://www.w3.org/TR/WCAG22/#labels-or-instructions",
+ compliance: "Compliant",
+ evidence:
+ "Supports aria-label and aria-labelledby with fallback to default label",
+ },
+ {
+ criterion: "4.1.1 Parsing",
+ url: "https://www.w3.org/TR/WCAG22/#parsing",
+ compliance: "Compliant",
+ evidence: "Valid HTML, no duplicate IDs, proper nesting",
+ },
+ {
+ criterion: "4.1.2 Name, Role, Value",
+ url: "https://www.w3.org/TR/WCAG22/#name-role-value",
+ compliance: "Compliant",
+ evidence:
+ "role='switch', aria-label | aria-labelledby for name, aria-checked for value",
+ },
+ {
+ criterion: "4.1.3 Status Messages",
+ url: "https://www.w3.org/TR/WCAG22/#status-messages",
+ compliance: "Not Compliant",
+ evidence:
+ "There is not aria-live, aria-atomic, or aria-relevant for dynamic state announcements",
+ notice:
+ "State changes (ON/OFF/INDETERMINATE) are not announced via aria-live regions. Screen readers detect changes through aria-checked updates but not proactively announced.",
+ },
+ ];
+
+ static #wcagCompliance() {
+ const fragment = document.createDocumentFragment();
+
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap Toggle adheres to WCAG 2.2 AA standards by implementing proper ARIA roles and attributes, ensuring that all users, including those with disabilities, can effectively interact with toggle switches.`;
+
+ const list = document.createElement("ul");
+ list.classList.add("list-group", "list-group-flush");
+ Accessibility.#wcagChecklist.forEach((item) => {
+ const listItem = document.createElement("li");
+ listItem.classList.add("list-group-item");
+
+ const header = document.createElement("div");
+ header.classList.add(
+ "d-flex",
+ "justify-content-start",
+ "align-items-center",
+ "gap-2",
+ );
+
+ const url = document.createElement("a");
+ url.classList.add("text-decoration-none");
+ url.href = item.url;
+ url.innerHTML = ` `;
+ url.target = "_blank";
+ header.appendChild(url);
+
+ const criterionText = document.createElement("strong");
+ criterionText.innerHTML = item.criterion;
+ header.appendChild(criterionText);
+
+ const badge = document.createElement("span");
+ badge.classList.add("badge");
+ if (item.compliance === "Compliant") {
+ badge.classList.add("bg-success");
+ } else if (item.compliance === "Partially") {
+ badge.classList.add("bg-warning");
+ } else {
+ badge.classList.add("bg-danger");
+ }
+ badge.textContent = item.compliance;
+ header.appendChild(badge);
+ listItem.appendChild(header);
+
+ const evidence = document.createElement("p");
+ evidence.classList.add("m-0");
+ evidence.innerHTML = item.evidence;
+ listItem.appendChild(evidence);
+
+ if (item.notice) {
+ const notice = document.createElement("p");
+ notice.classList.add("m-0");
+ notice.innerHTML = `${item.notice} `;
+ listItem.appendChild(notice);
+ }
+
+ list.appendChild(listItem);
+ });
+
+ fragment.appendChild(description);
+ fragment.appendChild(list);
+ return fragment;
+ }
+}
+
+export default Accessibility;
diff --git a/docs/src/js/components/documentation/Api.js b/docs/src/js/components/documentation/Api.js
new file mode 100644
index 0000000..f39757f
--- /dev/null
+++ b/docs/src/js/components/documentation/Api.js
@@ -0,0 +1,20 @@
+import InputInteraction from "./api/InputInteraction";
+import Methods from "./api/Methods";
+import Options from "./api/Options";
+import Rerender from "./api/Rerender";
+import State from "./api/State";
+import DocSection from "./DocSection";
+
+class Api extends DocSection {
+ static build() {
+ return super.build("api", "API", [
+ Options.build(),
+ Methods.build(),
+ InputInteraction.build(),
+ State.build(),
+ Rerender.build(),
+ ]);
+ }
+}
+
+export default Api;
diff --git a/docs/src/js/components/documentation/Distribution.js b/docs/src/js/components/documentation/Distribution.js
new file mode 100644
index 0000000..70be81d
--- /dev/null
+++ b/docs/src/js/components/documentation/Distribution.js
@@ -0,0 +1,105 @@
+import DocArticle from "./DocArticle";
+import DocSection from "./DocSection";
+
+class Distribution extends DocSection {
+ static headers = ["Bootstrap Support", "Plugin Last Release", "End Of Life"];
+ static entries = [
+ { bootstrap: "5", plugin: "5", eol: "5" },
+ { bootstrap: "4", plugin: "4", eol: "4" },
+ { bootstrap: "3", plugin: "3", eol: "3" },
+ ];
+
+ static build() {
+ return super.build(
+ "distribution",
+ "Library Distributions",
+ [
+ DocArticle.build({
+ description: Distribution.#table(),
+ }),
+ ],
+ "mb-3"
+ );
+ }
+
+ static #table() {
+ const container = document.createElement("div");
+ container.className = "table-responsive";
+
+ const table = document.createElement("table");
+ table.className = "table table-striped table-condensed mb-0";
+
+ const caption = document.createElement("caption");
+ caption.textContent = "Library Distributions";
+
+ table.append(
+ caption,
+ Distribution.#tableHeader(),
+ Distribution.#tableBody()
+ );
+ container.appendChild(table);
+ return container;
+ }
+
+ static #tableHeader() {
+ const thead = document.createElement("thead");
+ const row = document.createElement("tr");
+
+ Distribution.headers.forEach((headerText) => {
+ const th = document.createElement("th");
+ th.textContent = headerText;
+ row.appendChild(th);
+ });
+
+ thead.appendChild(row);
+ return thead;
+ }
+
+ static #tableBody() {
+ const tbody = document.createElement("tbody");
+ Distribution.entries.forEach((entry) => {
+ const row = document.createElement("tr");
+ const tdBootstrap = document.createElement("td");
+ const aBootstrap = document.createElement("a");
+ aBootstrap.href = `https://getbootstrap.com/docs/${entry.bootstrap}.0`;
+ aBootstrap.target = "_blank";
+ aBootstrap.rel = "noopener noreferrer";
+ aBootstrap.title = `Bootstrap ${entry.bootstrap}`;
+ const imgBootstrap = document.createElement("img");
+ imgBootstrap.src = `https://img.shields.io/static/v1?label=bootstrap&message=%5Ev${entry.bootstrap}.0.0&color=informational&logo=bootstrap&logoColor=white`;
+ imgBootstrap.alt = `Bootstrap ${entry.bootstrap}`;
+ aBootstrap.appendChild(imgBootstrap);
+ tdBootstrap.appendChild(aBootstrap);
+
+ const tdPlugin = document.createElement("td");
+ const aPlugin = document.createElement("a");
+ aPlugin.href = `https://github.com/palcarazm/bootstrap5-toggle/releases`;
+ aPlugin.target = "_blank";
+ aPlugin.rel = "noopener noreferrer";
+ aPlugin.title = `bs-toggle v${entry.plugin}`;
+ const imgPlugin = document.createElement("img");
+ imgPlugin.src = `https://img.shields.io/github/package-json/v/palcarazm/bootstrap5-toggle/v${entry.plugin}?logo=github`;
+ imgPlugin.alt = `bs-toggle v${entry.plugin}`;
+ aPlugin.appendChild(imgPlugin);
+ tdPlugin.appendChild(aPlugin);
+
+ const tdEOL = document.createElement("td");
+ const aEOL = document.createElement("a");
+ aEOL.href = `https://github.com/palcarazm/bootstrap5-toggle/security/policy`;
+ aEOL.target = "_blank";
+ aEOL.rel = "noopener noreferrer";
+ aEOL.title = `End of Life`;
+ const imgEOL = document.createElement("img");
+ imgEOL.src = `https://img.shields.io/endpoint?url=https%3A%2F%2Fpalcarazm.github.io%2Fbootstrap5-toggle%2Fapi%2Feol%2Fv${entry.eol}`;
+ imgEOL.alt = `EOL v${entry.eol}`;
+ aEOL.appendChild(imgEOL);
+ tdEOL.appendChild(aEOL);
+
+ row.append(tdBootstrap, tdPlugin, tdEOL);
+ tbody.appendChild(row);
+ });
+ return tbody;
+ }
+}
+
+export default Distribution;
diff --git a/docs/src/js/components/documentation/DocAlert.js b/docs/src/js/components/documentation/DocAlert.js
new file mode 100644
index 0000000..c91d2ec
--- /dev/null
+++ b/docs/src/js/components/documentation/DocAlert.js
@@ -0,0 +1,54 @@
+import VersionPill from "../VersionPill";
+
+class DocAlert {
+ static build({ type, title, content, versionPill = null }) {
+ const alert = document.createElement("div");
+ alert.className = `alert alert-${type}`;
+
+ const alertBody = document.createElement("div");
+ alertBody.innerHTML = content;
+
+ alert.append(DocAlert.#alertHeader(type, title, versionPill), alertBody);
+ return alert;
+ }
+
+ static #alertHeader(type, title, versionPill) {
+ const header = document.createElement("div");
+ header.className =
+ "d-flex justify-content-between align-items-center alert-heading";
+
+ let icon = document.createElement("i");
+ switch (type) {
+ case "info":
+ icon.className = "fa-solid fa-info-circle";
+ break;
+ case "success":
+ icon.className = "fa-solid fa-circle-check";
+ break;
+ case "warning":
+ icon.className = "fa-solid fa-exclamation-circle";
+ break;
+ case "danger":
+ icon.className = "fa-solid fa-exclamation-triangle";
+ break;
+ default:
+ icon = null;
+ break;
+ }
+
+ const col = document.createElement("div");
+ if (icon) col.appendChild(icon);
+ const titleContainer = document.createElement("span");
+ titleContainer.className = "text-uppercase fw-bold ms-1";
+ titleContainer.textContent = title;
+ col.appendChild(titleContainer);
+
+ header.appendChild(col);
+
+ if (versionPill) header.appendChild(VersionPill.build(versionPill));
+
+ return header;
+ }
+}
+
+export default DocAlert;
diff --git a/docs/src/js/components/documentation/DocArticle.js b/docs/src/js/components/documentation/DocArticle.js
new file mode 100644
index 0000000..be1a405
--- /dev/null
+++ b/docs/src/js/components/documentation/DocArticle.js
@@ -0,0 +1,57 @@
+import Badge from "../Badge";
+import CodeBlock from "../CodeBlock";
+import CodePanel from "../CodePanel";
+import VersionPill from "../VersionPill";
+import DocAlert from "./DocAlert";
+
+class DocArticle {
+ static build({
+ title = null,
+ className = "",
+ badge = null,
+ versionPill = null,
+ description = null,
+ example = [],
+ codePanel = null,
+ codeBlock = null,
+ alert = null,
+ }) {
+ const docArticle = document.createElement("article");
+ docArticle.className = `ps-2 px-2 border-start border-2 ${className}`;
+
+ if (title)
+ docArticle.appendChild(
+ DocArticle.#articleHeader(title, badge, versionPill)
+ );
+
+ if (description) docArticle.appendChild(description);
+
+ if (codePanel && codeBlock)
+ throw new Error("CodePanel and CodeBlock are mutually exclusive.");
+ if (codePanel)
+ docArticle.appendChild(CodePanel.build({ example, ...codePanel }));
+ if (codeBlock)
+ docArticle.appendChild(CodeBlock.build({ example, ...codeBlock }));
+
+ if (alert) docArticle.appendChild(DocAlert.build(alert));
+
+ return docArticle;
+ }
+
+ static #articleHeader(title, badge, versionPill) {
+ const header = document.createElement("div");
+ header.className = "d-flex justify-content-between align-items-center";
+ const titleContainer = document.createElement("h3");
+ titleContainer.className = "text-secondary";
+ titleContainer.textContent = title;
+ header.appendChild(titleContainer);
+
+ if (badge) header.appendChild(Badge.build(badge));
+
+ if (versionPill) header.appendChild(VersionPill.build(versionPill));
+
+ return header;
+ }
+}
+
+export default DocArticle;
diff --git a/docs/src/js/components/documentation/DocSection.js b/docs/src/js/components/documentation/DocSection.js
new file mode 100644
index 0000000..3bd08ee
--- /dev/null
+++ b/docs/src/js/components/documentation/DocSection.js
@@ -0,0 +1,16 @@
+class DocSection {
+ static build(name, title, HTMLElements, className = "") {
+ const section = document.createElement("section");
+ section.id = name;
+ section.className = `container ${className}`;
+ section.append(DocSection.#header(title), ...HTMLElements);
+ return section;
+ }
+ static #header(title) {
+ const header = document.createElement("h2");
+ header.textContent = title;
+ return header;
+ }
+}
+
+export default DocSection;
diff --git a/docs/src/js/components/documentation/Events.js b/docs/src/js/components/documentation/Events.js
new file mode 100644
index 0000000..36e1646
--- /dev/null
+++ b/docs/src/js/components/documentation/Events.js
@@ -0,0 +1,16 @@
+import DocSection from "./DocSection";
+import CustomEvents from "./events/CustomEvents";
+import EventPropagation from "./events/EventPropagation";
+import SilencedActions from "./events/SilencedActions";
+
+class Events extends DocSection {
+ static build() {
+ return super.build("events", "Events", [
+ EventPropagation.build(),
+ CustomEvents.build(),
+ SilencedActions.build(),
+ ]);
+ }
+}
+
+export default Events;
diff --git a/docs/src/js/components/documentation/Features.js b/docs/src/js/components/documentation/Features.js
new file mode 100644
index 0000000..a9be466
--- /dev/null
+++ b/docs/src/js/components/documentation/Features.js
@@ -0,0 +1,38 @@
+import DocSection from "./DocSection";
+import AnimationSpeed from "./features/AnimationSpeed";
+import Colors from "./features/Colors";
+import CustomFormValue from "./features/CustomFormValue";
+import CustomSize from "./features/CustomSize";
+import CustomStyle from "./features/CustomStyle";
+import CustomText from "./features/CustomText";
+import CustomTitle from "./features/CustomTitle";
+import FormLayout from "./features/FormLayout";
+import KeyboardInteraction from "./features/KeyboardInteraction";
+import OutlineColors from "./features/OutlineColors";
+import Size from "./features/Size";
+import StateStatus from "./features/StateStatus";
+import Tooltip from "./features/Tooltip";
+import Tristate from "./features/Tristate";
+
+class Features extends DocSection {
+ static build() {
+ return super.build("features", "Features", [
+ Size.build(),
+ CustomSize.build(),
+ Tristate.build(),
+ Colors.build(),
+ OutlineColors.build(),
+ CustomText.build(),
+ CustomTitle.build(),
+ Tooltip.build(),
+ CustomStyle.build(),
+ AnimationSpeed.build(),
+ KeyboardInteraction.build(),
+ StateStatus.build(),
+ CustomFormValue.build(),
+ FormLayout.build(),
+ ]);
+ }
+}
+
+export default Features;
diff --git a/docs/src/js/components/documentation/Installation.js b/docs/src/js/components/documentation/Installation.js
new file mode 100644
index 0000000..70e07e6
--- /dev/null
+++ b/docs/src/js/components/documentation/Installation.js
@@ -0,0 +1,73 @@
+import DocArticle from "./DocArticle";
+import DocSection from "./DocSection";
+
+class Installation extends DocSection {
+ static build() {
+ return super.build("installation", "Installation", [
+ Installation.#cdn(),
+ Installation.#github(),
+ Installation.#npm(),
+ Installation.#yarn(),
+ ]);
+ }
+
+ static #cdn() {
+ const ecmas = `
+`;
+ const jquery = `
+`;
+ return DocArticle.build({
+ title: "CDN",
+ badge: {
+ name: "jsDelivr",
+ href: "https://www.jsdelivr.com/package/npm/bootstrap5-toggle",
+ imgSrc:
+ "https://img.shields.io/jsdelivr/npm/hm/bootstrap5-toggle?label=hits&logo=jsdelivr&logoColor=white",
+ },
+ codePanel: {
+ name: "cdn",
+ language: "html",
+ contents: [ecmas, jquery],
+ },
+ });
+ }
+
+ static #github() {
+ return DocArticle.build({
+ title: "Download from GitHub",
+ badge: {
+ name: "Latest release",
+ href: "https://github.com/palcarazm/bootstrap5-toggle/releases",
+ imgSrc:
+ "https://img.shields.io/github/package-json/v/palcarazm/bootstrap5-toggle/v5?logo=github",
+ },
+ });
+ }
+
+ static #npm() {
+ return DocArticle.build({
+ title: "NPM",
+ badge: {
+ name: "NPM",
+ href: "https://www.npmjs.com/package/bootstrap5-toggle",
+ imgSrc: "https://img.shields.io/npm/dm/bootstrap5-toggle?logo=npm",
+ },
+ codeBlock: {
+ language: "shell",
+ code: `npm install bootstrap5-toggle@${versions.bootstrap5Toggle}`,
+ },
+ });
+ }
+
+ static #yarn() {
+ return DocArticle.build({
+ title: "Yarn",
+ codeBlock: {
+ language: "shell",
+ code: `yarn add bootstrap5-toggle@${versions.bootstrap5Toggle}`,
+ },
+ });
+ }
+}
+
+export default Installation;
diff --git a/docs/src/js/components/documentation/Usage.js b/docs/src/js/components/documentation/Usage.js
new file mode 100644
index 0000000..a507740
--- /dev/null
+++ b/docs/src/js/components/documentation/Usage.js
@@ -0,0 +1,42 @@
+import DocSection from "./DocSection";
+import DocArticle from "./DocArticle";
+
+class Usage extends DocSection {
+ static build() {
+ return super.build("usage", "Usage", [Usage.#html(), Usage.#js()]);
+ }
+
+ static #html() {
+ const description = document.createElement("p");
+ description.innerHTML = `Simply add data-toggle="toggle" to convert checkboxes into toggles.`;
+
+ return DocArticle.build({
+ title: "Initialize with HTML",
+ description,
+ codeBlock: {
+ language: "html",
+ code: ` `,
+ },
+ });
+ }
+
+ static #js() {
+ const description = document.createElement("p");
+ description.innerHTML = `Simply call the bootstrapToggle method to convert checkboxes into toggles. See Options for additional colors, etc.
`;
+
+ const ecmas = `document.querySelector(mySelector).bootstrapToggle();`;
+ const jquery = `$(mySelector).bootstrapToggle();`;
+
+ return DocArticle.build({
+ title: "Initialize with JavaScript",
+ description,
+ codePanel: {
+ name: "usage-js",
+ language: "javascript",
+ contents: [ecmas, jquery],
+ },
+ });
+ }
+}
+
+export default Usage;
diff --git a/docs/src/js/components/documentation/api/InputInteraction.js b/docs/src/js/components/documentation/api/InputInteraction.js
new file mode 100644
index 0000000..f33d072
--- /dev/null
+++ b/docs/src/js/components/documentation/api/InputInteraction.js
@@ -0,0 +1,167 @@
+import DocArticle from "../DocArticle";
+
+class InputInteraction extends DocArticle {
+ static #interactions = [
+ {
+ action: "checked",
+ function: "myInput.checked = true",
+ description: "Sets the toggle to 'On' state",
+ demo: (input) => {
+ input.checked = true;
+ },
+ },
+ {
+ action: "unchecked",
+ function: "myInput.checked = false",
+ description: "Sets the toggle to 'Off' state",
+ demo: (input) => {
+ input.checked = false;
+ },
+ },
+ {
+ action: "indeterminate",
+ function: "myInput.indeterminate = true",
+ description: "Sets the toggle to 'indeterminate' state",
+ demo: (input) => {
+ input.indeterminate = true;
+ },
+ },
+ {
+ action: "determinate",
+ function: "myInput.indeterminate = false",
+ description:
+ "Sets the toggle to the previous 'determinate' state ('on' or 'off')",
+ demo: (input) => {
+ input.indeterminate = false;
+ },
+ },
+ {
+ action: "enable",
+ function: "myInput.disabled = false",
+ description: "Enables the toggle",
+ demo: (input) => {
+ input.disabled = false;
+ },
+ },
+ {
+ action: "disable",
+ function: "myInput.disabled = true",
+ description: "Disables the toggle",
+ demo: (input) => {
+ input.disabled = true;
+ },
+ },
+ {
+ action: "read-only",
+ function: "myInput.readOnly = true",
+ description:
+ "Disables the toggle but the checkbox stay readable in form data.",
+ demo: (input) => {
+ input.readOnly = true;
+ },
+ },
+ {
+ action: "read-write",
+ function: "myInput.readOnly = false",
+ description:
+ "Removes the readonly state, enabling user interaction with the toggle.",
+ demo: (input) => {
+ input.readOnly = false;
+ },
+ },
+ ];
+
+ static build() {
+ const input = document.createElement("input");
+ input.setAttribute("type", "checkbox");
+ input.setAttribute("id", "api-input-interaction");
+ input.setAttribute("checked", "");
+ input.setAttribute("tristate", "");
+ input.dataset.toggle = "toggle";
+
+ return super.build({
+ title: "Input Interaction",
+ description: InputInteraction.#description(input),
+ codeBlock: {
+ language: "html",
+ code: `
+`,
+ },
+ example: [input],
+ });
+ }
+
+ static #description(input) {
+ const description = document.createElement("div");
+
+ const paragraph = document.createElement("p");
+ paragraph.innerHTML =
+ "Interact with the input elements dispatch changes to the toggle:";
+ description.append(paragraph, InputInteraction.#table(input));
+
+ return description;
+ }
+
+ static #table(input) {
+ const table = document.createElement("table");
+ table.className = "table table-striped table-condensed";
+ const caption = document.createElement("caption");
+ caption.textContent = "Input interaction demo";
+ table.append(
+ caption,
+ InputInteraction.#thead(),
+ InputInteraction.#tbody(input),
+ );
+
+ return table;
+ }
+
+ static #thead() {
+ const thead = document.createElement("thead");
+ const tr = document.createElement("tr");
+ tr.append(
+ ...["Action", "Example", "Description", "Demo"].map((label) => {
+ const th = document.createElement("th");
+ th.textContent = label;
+ return th;
+ }),
+ );
+ thead.append(tr);
+ return thead;
+ }
+
+ static #tbody(input) {
+ const tbody = document.createElement("tbody");
+
+ const trs = InputInteraction.#interactions.map(
+ ({ action, function: func, description, demo }) => {
+ const td1 = document.createElement("td");
+ td1.innerHTML = `${action} `;
+
+ const td2 = document.createElement("td");
+ td2.innerHTML = `${func}`;
+
+ const td3 = document.createElement("td");
+ td3.textContent = description;
+
+ const td4 = document.createElement("td");
+ const button = document.createElement("button");
+ button.className = "btn btn-outline-dark btn-sm w-100";
+ button.textContent = action;
+ button.onclick = () => {
+ demo(input);
+ };
+ td4.append(button);
+
+ const tr = document.createElement("tr");
+ tr.append(td1, td2, td3, td4);
+ return tr;
+ },
+ );
+
+ tbody.append(...trs);
+ return tbody;
+ }
+}
+
+export default InputInteraction;
diff --git a/docs/src/js/components/documentation/api/Methods.js b/docs/src/js/components/documentation/api/Methods.js
new file mode 100644
index 0000000..cf7f008
--- /dev/null
+++ b/docs/src/js/components/documentation/api/Methods.js
@@ -0,0 +1,151 @@
+import DocArticle from "../DocArticle";
+
+class Methods extends DocArticle {
+ static #methods = [
+ {
+ method: "initialize",
+ params: null,
+ description: "Initializes the toggle plugin with options",
+ },
+ {
+ method: "destroy",
+ params: "destroy",
+ description: "Destroys the toggle",
+ },
+ {
+ method: "rerender",
+ params: "rerender",
+ description: "Rerenders the toggle",
+ },
+ {
+ method: "on",
+ params: "on",
+ description: "Sets the toggle to 'On' state",
+ },
+ {
+ method: "off",
+ params: "off",
+ description: "Sets the toggle to 'Off' state",
+ },
+ {
+ method: "toggle",
+ params: "toggle",
+ description: "Toggles the state of the toggle",
+ },
+ {
+ method: "indeterminate",
+ params: "indeterminate",
+ description: "Sets the toggle to 'indeterminate' state",
+ },
+ {
+ method: "determinate",
+ params: "determinate",
+ description:
+ "Sets the toggle to the previous 'determinate' state ('on' or 'off')",
+ },
+ {
+ method: "enable",
+ params: "enable",
+ description: "Enables the toggle",
+ },
+ {
+ method: "disable",
+ params: "disable",
+ description: "Disables the toggle",
+ },
+ {
+ method: "readonly",
+ params: "readonly",
+ description:
+ "Disables the toggle but the checkbox stay readable in form data.",
+ },
+ ];
+
+ static build() {
+ const input = document.createElement("input");
+ input.setAttribute("type", "checkbox");
+ input.setAttribute("id", "api-methods-toggle");
+ input.setAttribute("checked", "");
+
+ return super.build({
+ title: "Methods",
+ description: Methods.#description(input),
+ codeBlock: {
+ language: "html",
+ code: `
+`,
+ },
+ example: [input],
+ });
+ }
+
+ static #description(input) {
+ const description = document.createElement("div");
+
+ const paragraph = document.createElement("p");
+ paragraph.innerHTML =
+ "Methods can be used to control toggles directly. The following methods are available in the following way:";
+ description.append(paragraph, Methods.#table(input));
+
+ return description;
+ }
+
+ static #table(input) {
+ const table = document.createElement("table");
+ table.className = "table table-striped table-condensed";
+ const caption = document.createElement("caption");
+ caption.textContent = "API methods demo";
+ table.append(caption, Methods.#thead(), Methods.#tbody(input));
+
+ return table;
+ }
+
+ static #thead() {
+ const thead = document.createElement("thead");
+ const tr = document.createElement("tr");
+ tr.append(
+ ...["Method", "Example", "Description", "Demo"].map((label) => {
+ const th = document.createElement("th");
+ th.textContent = label;
+ return th;
+ })
+ );
+ thead.append(tr);
+ return thead;
+ }
+
+ static #tbody(input) {
+ const tbody = document.createElement("tbody");
+
+ const trs = Methods.#methods.map(({ method, params, description }) => {
+ const td1 = document.createElement("td");
+ td1.innerHTML = `${method} `;
+
+ const td2 = document.createElement("td");
+ td2.innerHTML = params
+ ? `myToggle.bootstrapToggle("${params}")`
+ : `myToggle.bootstrapToggle()`;
+
+ const td3 = document.createElement("td");
+ td3.textContent = description;
+
+ const td4 = document.createElement("td");
+ const button = document.createElement("button");
+ button.className = "btn btn-outline-dark btn-sm w-100";
+ button.textContent = method;
+ button.onclick = () => {
+ input.bootstrapToggle(params);
+ };
+ td4.append(button);
+
+ const tr = document.createElement("tr");
+ tr.append(td1, td2, td3, td4);
+ return tr;
+ });
+
+ tbody.append(...trs);
+ return tbody;
+ }
+}
+
+export default Methods;
diff --git a/docs/src/js/components/documentation/api/Options.js b/docs/src/js/components/documentation/api/Options.js
new file mode 100644
index 0000000..1e56a46
--- /dev/null
+++ b/docs/src/js/components/documentation/api/Options.js
@@ -0,0 +1,286 @@
+import DocArticle from "../DocArticle";
+
+class Options extends DocArticle {
+ static build() {
+ return super.build({
+ title: "Options",
+ description: Options.#description(),
+ example: Options.#example(),
+ codePanel: Options.#codePanel(),
+ alert: {
+ type: "warning",
+ title: "Deprecation notice",
+ versionPill: {
+ version: "5.0.0",
+ action: "DEPRECATED",
+ },
+ content: `Using on and off user options is deprecated and will throw a console warning. Use onlabel and offlabel user options instead.`,
+ },
+ });
+ }
+
+ static #codePanel() {
+ const ecmas = `
+`;
+ const jquery = `
+`;
+ return {
+ name: "api-options",
+ language: "javascript",
+ tabs: ["ECMAScript", "jQuery"],
+ contents: [ecmas, jquery],
+ };
+ }
+
+ static #example() {
+ const form = document.createElement("form");
+ form.id = "form_state_status";
+ form.className = "d-flex justify-content-between align-items-center";
+
+ const input = document.createElement("input");
+ input.id = "api-option-toggle";
+ input.type = "checkbox";
+ input.checked = true;
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ button.className = "btn btn-outline-secondary";
+ button.innerHTML = "Launch Bootstrap Toggle";
+
+ form.append(input, button);
+
+ form.onsubmit = (e) => {
+ e.preventDefault();
+ input.bootstrapToggle({
+ onlable: "Enabled",
+ offlabel: "Disabled",
+ onstyle: "success",
+ offstyle: "danger",
+ onvalue: "1",
+ offvalue: "0",
+ size: "lg",
+ style: null,
+ width: null,
+ height: null,
+ tabindex: -1,
+ tristate: true,
+ tooltip: {
+ placement: "top",
+ title: { on: "Enabled", off: "Disabled", mixed: "Mixed" },
+ },
+ });
+ };
+
+ return [form];
+ }
+
+ static #description() {
+ const description = document.createElement("div");
+ description.innerHTML = `Options can be passed via data attributes or JavaScript. For data attributes, append the option name to data-, as in data-on="Enabled". Data attributes will take precedence over JavaScript options.
+
+
+ API constructor options
+
+
+ Name
+ Type
+ Default
+ Description
+
+
+
+
+ onlabel
+ string | html
+ "On"
+ Text of the on toggle label.
+
+
+ offlabel
+ string | html
+ "Off"
+ Text of the off toggle label.
+
+
+ onstyle
+ string
+ "primary"
+
+ Style of the on toggle. Possible values are:
+ primary, secondary, success, danger,
+ warning, info, light, dark
+
+
+
+ offstyle
+ string
+ "secondary"
+
+ Style of the off toggle. Possible values are:
+ primary, secondary, success, danger,
+ warning, info, light, dark
+
+
+
+ onvalue
+ string
+ null
+ Sets on state value.
+
+
+ offvalue
+ string
+ null
+ Sets off state value.
+
+
+ size
+ string
+ null
+
+ Size of the toggle. If set to null , button is default/normal size.
+ Possible values are:
+ lg, sm, xs
+
+
+
+ style
+ string
+ null
+
+ Appends the provided value to the toggle's class attribute.
+ Use this to apply custom styles to the toggle.
+
+
+
+ width
+ integer
+ null
+
+ Sets the width of the toggle.
+ If set to null , width will be calculated.
+
+
+
+ height
+ integer
+ null
+
+ Sets the height of the toggle.
+ If set to null , height will be calculated.
+
+
+
+ tabindex
+ integer
+ 0
+ Sets the tabindex.
+
+
+ tristate
+ boolean
+ false
+ Sets tristate support.
+
+
+ tooltip
+ object
+ undefined
+
+
+ Tooltip options
+
+
+ Name
+ Type
+ Default
+ Description
+
+
+
+
+ placement
+ string
+ "top"
+ Placement of the tooltip. Possible values are: top, bottom, left, and right.
+
+
+ title.on
+ string | html
+ undefined
+ Title of the tooltip when the toggle is on.
+
+
+ title.off
+ string | html
+ undefined
+ Title of the tooltip when the toggle is off.
+
+
+ title.mixed
+ string | html
+ undefined
+ Title of the tooltip when the toggle is in mixed state (if tristate is enabled).
+
+
+
+
+
+
+
+
`;
+ return description;
+ }
+}
+
+export default Options;
diff --git a/docs/src/js/components/documentation/api/Rerender.js b/docs/src/js/components/documentation/api/Rerender.js
new file mode 100644
index 0000000..6f6960b
--- /dev/null
+++ b/docs/src/js/components/documentation/api/Rerender.js
@@ -0,0 +1,61 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class Rerender extends DocArticle {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle can be re-rendered with the rerender method. This can be used to update toggle when a process updates input data attributes.`;
+
+ const code = `
+`;
+
+ return super.build({
+ title: "Re-render",
+ description,
+ example: Rerender.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const form = document.createElement("form");
+ form.id = "api-state-form";
+ form.className = "d-flex justify-content-between align-items-center";
+ form.innerHTML = `
+ Change label and re-render `;
+
+ const console = new Console();
+
+ form.onsubmit = (e) => {
+ e.preventDefault();
+ const myToggle = e.target.elements["api-rerender-toggle"];
+ const onLabel = crypto.getRandomValues(new Uint32Array(1))[0];
+ myToggle.dataset.onlabel = onLabel;
+ console.log({
+ mode: "append",
+ data: `On label set to: ${onLabel}`,
+ });
+
+ myToggle.bootstrapToggle("rerender");
+ };
+
+ return [form, console.htmlElement];
+ }
+}
+
+export default Rerender;
diff --git a/docs/src/js/components/documentation/api/State.js b/docs/src/js/components/documentation/api/State.js
new file mode 100644
index 0000000..31ad0c5
--- /dev/null
+++ b/docs/src/js/components/documentation/api/State.js
@@ -0,0 +1,47 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class State extends DocArticle {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Checked state of a toggle can be set by reading checked property. Use the element property for checked state myToggle.checked (returns true or false).`;
+
+ const code = `
+`;
+
+ return super.build({
+ title: "Toggle State",
+ description,
+ example: State.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const form = document.createElement("form");
+ form.id = "api-state-form";
+ form.className = "d-flex justify-content-between align-items-center";
+ form.innerHTML = `
+ Read State `;
+
+ const console = new Console();
+
+ form.onsubmit = (e) => {
+ e.preventDefault();
+ console.log({
+ mode: "append",
+ data: `Checked: ${e.target.elements["api-state-toggle"].checked}`,
+ });
+ };
+
+ return [form, console.htmlElement];
+ }
+}
+
+export default State;
diff --git a/docs/src/js/components/documentation/events/CustomEvents.js b/docs/src/js/components/documentation/events/CustomEvents.js
new file mode 100644
index 0000000..373361b
--- /dev/null
+++ b/docs/src/js/components/documentation/events/CustomEvents.js
@@ -0,0 +1,136 @@
+import { version } from "grunt";
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class CustomEvents extends DocArticle {
+ static #events = [
+ {
+ name: "toggle:on",
+ action: "on",
+ },
+ {
+ name: "toggle:off",
+ action: "off",
+ },
+ {
+ name: "toggle:mixed",
+ action: "indeterminate",
+ },
+ {
+ name: "toggle:enabled",
+ action: "enable",
+ },
+ {
+ name: "toggle:disabled",
+ action: "disable",
+ },
+ {
+ name: "toggle:readonly",
+ action: "readonly",
+ },
+ ];
+
+ static build() {
+ const input = document.createElement("input");
+ input.setAttribute("type", "checkbox");
+ input.setAttribute("id", "custom-events-toggle");
+ input.setAttribute("checked", "");
+ input.setAttribute("tristate", "");
+ input.setAttribute("data-toggle", "toggle");
+ const console = new Console();
+
+ CustomEvents.#events.forEach((event) => {
+ input.addEventListener(event.name, (e) => {
+ console.log({
+ mode: "append",
+ data: `Event ${event.name} fired. Toggle state: ${JSON.stringify(e.detail.state, null, 2)}`,
+ });
+ });
+ });
+
+ return super.build({
+ title: "Custom Events",
+ description: CustomEvents.#description(input),
+ codeBlock: {
+ language: "html",
+ code: `
+`,
+ },
+ example: [input, console.htmlElement],
+ versionPill: { action: "SINCE", version: "5.3.0" },
+ });
+ }
+
+ static #description(input) {
+ const description = document.createElement("div");
+
+ const paragraph = document.createElement("p");
+ paragraph.innerHTML =
+ "Bootstrap Toggle emit custom events when its state or status is changed. The events details contain the current state of the toggle. The following custom events are available:";
+ description.append(paragraph, CustomEvents.#table(input));
+
+ return description;
+ }
+
+ static #table(input) {
+ const table = document.createElement("table");
+ table.className = "table table-striped table-condensed";
+ const caption = document.createElement("caption");
+ caption.textContent = "Custom events demo";
+ table.append(caption, CustomEvents.#thead(), CustomEvents.#tbody(input));
+
+ return table;
+ }
+
+ static #thead() {
+ const thead = document.createElement("thead");
+ const tr = document.createElement("tr");
+ tr.append(
+ ...["Event", "Example", "Launch"].map((label) => {
+ const th = document.createElement("th");
+ th.textContent = label;
+ return th;
+ }),
+ );
+ thead.append(tr);
+ return thead;
+ }
+
+ static #tbody(input) {
+ const tbody = document.createElement("tbody");
+
+ const trs = CustomEvents.#events.map(({ name, action }) => {
+ const td1 = document.createElement("td");
+ td1.innerHTML = `${name} `;
+
+ const td2 = document.createElement("td");
+ td2.innerHTML = `myToggle.addEventListener("${name}", (e)=>{...})`;
+
+ const td3 = document.createElement("td");
+ const defaultBtn = document.createElement("button");
+ defaultBtn.className = "btn btn-outline-dark btn-sm w-100";
+ defaultBtn.textContent = action;
+ defaultBtn.onclick = () => {
+ input.bootstrapToggle(action, false);
+ };
+ td3.append(defaultBtn);
+
+ const tr = document.createElement("tr");
+ tr.append(td1, td2, td3);
+ return tr;
+ });
+
+ tbody.append(...trs);
+ return tbody;
+ }
+}
+
+export default CustomEvents;
diff --git a/docs/src/js/components/documentation/events/EventPropagation.js b/docs/src/js/components/documentation/events/EventPropagation.js
new file mode 100644
index 0000000..8f1ead1
--- /dev/null
+++ b/docs/src/js/components/documentation/events/EventPropagation.js
@@ -0,0 +1,49 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class EventPropagation extends DocArticle {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `All events are propagated to and from input element to the toggle. Listen for events on the input directly as the toggle stays synced with the
+ input.`;
+
+ const code = `
+`;
+
+ return super.build({
+ title: "Event Propagation",
+ description,
+ example: EventPropagation.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.dataset.toggle = "toggle";
+ input.checked = true;
+ input.id = "events-propagation-toggle";
+
+ const console = new Console();
+
+ input.addEventListener("change", (e) => {
+ console.log({
+ mode: "append",
+ data: `Input change fired. Checked state: ${input.checked}`,
+ });
+ });
+
+ return [input, console.htmlElement];
+ }
+}
+
+export default EventPropagation;
diff --git a/docs/src/js/components/documentation/events/SilencedActions.js b/docs/src/js/components/documentation/events/SilencedActions.js
new file mode 100644
index 0000000..c1f382f
--- /dev/null
+++ b/docs/src/js/components/documentation/events/SilencedActions.js
@@ -0,0 +1,125 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class SilencedActions extends DocArticle {
+ static #methods = [
+ "on",
+ "off",
+ "toggle",
+ "indeterminate",
+ "determinate",
+ "enable",
+ "disable",
+ "readonly",
+ ];
+
+ static build() {
+ const input = document.createElement("input");
+ input.setAttribute("type", "checkbox");
+ input.setAttribute("id", "events-silence-toggle");
+ input.setAttribute("checked", "");
+ input.setAttribute("data-toggle", "toggle");
+ const console = new Console();
+
+ input.addEventListener("change", (e) => {
+ console.log({
+ mode: "append",
+ data: `Input change fired. Checked state: ${input.checked}`,
+ });
+ });
+
+ return super.build({
+ title: "Silenced Actions",
+ description: SilencedActions.#description(input),
+ codeBlock: {
+ language: "html",
+ code: `
+`,
+ },
+ example: [input, console.htmlElement],
+ });
+ }
+
+ static #description(input) {
+ const description = document.createElement("div");
+
+ const paragraph = document.createElement("p");
+ paragraph.innerHTML =
+ "Methods can be used in silence mode just specifying the second argument to true on the bootstrapToggle call. This is useful to prevent the control from propagating the change event in cases where you want to update the state, but do not want to fire the onChange event. The following methods are available in silence mode in the following way:";
+ description.append(paragraph, SilencedActions.#table(input));
+
+ return description;
+ }
+
+ static #table(input) {
+ const table = document.createElement("table");
+ table.className = "table table-striped table-condensed";
+ const caption = document.createElement("caption");
+ caption.textContent = "Silenced methods demo";
+ table.append(
+ caption,
+ SilencedActions.#thead(),
+ SilencedActions.#tbody(input),
+ );
+
+ return table;
+ }
+
+ static #thead() {
+ const thead = document.createElement("thead");
+ const tr = document.createElement("tr");
+ tr.append(
+ ...["Method", "Example", "Normal", "Silenced"].map((label) => {
+ const th = document.createElement("th");
+ th.textContent = label;
+ return th;
+ }),
+ );
+ thead.append(tr);
+ return thead;
+ }
+
+ static #tbody(input) {
+ const tbody = document.createElement("tbody");
+
+ const trs = SilencedActions.#methods.map((method) => {
+ const td1 = document.createElement("td");
+ td1.innerHTML = `${method} `;
+
+ const td2 = document.createElement("td");
+ td2.innerHTML = `myToggle.bootstrapToggle("${method}", true)`;
+
+ const td3 = document.createElement("td");
+ const defaultBtn = document.createElement("button");
+ defaultBtn.className = "btn btn-outline-dark btn-sm w-100";
+ defaultBtn.textContent = method;
+ defaultBtn.onclick = () => {
+ input.bootstrapToggle(method, false);
+ };
+ td3.append(defaultBtn);
+
+ const td4 = document.createElement("td");
+ const silenceBtn = document.createElement("button");
+ silenceBtn.className = "btn btn-outline-dark btn-sm w-100";
+ silenceBtn.textContent = method;
+ silenceBtn.onclick = () => {
+ input.bootstrapToggle(method, true);
+ };
+ td4.append(silenceBtn);
+
+ const tr = document.createElement("tr");
+ tr.append(td1, td2, td3, td4);
+ return tr;
+ });
+
+ tbody.append(...trs);
+ return tbody;
+ }
+}
+
+export default SilencedActions;
diff --git a/docs/src/js/components/documentation/features/AnimationSpeed.js b/docs/src/js/components/documentation/features/AnimationSpeed.js
new file mode 100644
index 0000000..fe2c1e7
--- /dev/null
+++ b/docs/src/js/components/documentation/features/AnimationSpeed.js
@@ -0,0 +1,58 @@
+import DocArticle from "../DocArticle";
+
+class AnimationSpeed {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Transition speed can be easily controlled with css transition property on .toggle-group. You can also turn animation off completely.`;
+
+ const code = `
+
+
+ `;
+
+ return DocArticle.build({
+ title: "Animation Speed",
+ description,
+ example: AnimationSpeed.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const style = document.createElement("style");
+ style.textContent = `
+ .slow .toggle-group { transition: left 0.7s; -webkit-transition: left 0.7s; }
+ .fast .toggle-group { transition: left 0.1s; -webkit-transition: left 0.1s; }
+ .none .toggle-group { transition: none; -webkit-transition: none; }`;
+
+ const inputs = ["slow", "fast", "none"].map((style) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ input.dataset.style = style;
+ return input;
+ });
+
+ return [style, ...inputs];
+ }
+}
+export default AnimationSpeed;
diff --git a/docs/src/js/components/documentation/features/Colors.js b/docs/src/js/components/documentation/features/Colors.js
new file mode 100644
index 0000000..3fd0645
--- /dev/null
+++ b/docs/src/js/components/documentation/features/Colors.js
@@ -0,0 +1,49 @@
+import DocArticle from "../DocArticle";
+
+class Colors {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle implements all Bootstrap colors. Just add the data-onstyle or data-offstyle attribute with one of the following values: primary, secondary, success, danger, warning, info, light or dark.`;
+
+ const code = `
+
+
+
+
+
+
+ `;
+
+ return DocArticle.build({
+ title: "Colors",
+ description,
+ example: Colors.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const example = [
+ "primary",
+ "secondary",
+ "success",
+ "danger",
+ "warning",
+ "info",
+ "light",
+ "dark",
+ ].map((color) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ input.dataset.onstyle = color;
+ return input;
+ });
+ return example;
+ }
+}
+export default Colors;
diff --git a/docs/src/js/components/documentation/features/CustomFormValue.js b/docs/src/js/components/documentation/features/CustomFormValue.js
new file mode 100644
index 0000000..1bb5ada
--- /dev/null
+++ b/docs/src/js/components/documentation/features/CustomFormValue.js
@@ -0,0 +1,50 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class CustomFormValue {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Form submit values can be customized with the data-onvalue and data-offvalue attributes.If original checkbox have a value set, data-onvalue will be ignored preserving the original value.`;
+
+ const code = ``;
+
+ return DocArticle.build({
+ title: "Custom form submit values",
+ description,
+ example: CustomFormValue.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ versionPill: { version: "4.3.0", action: "SINCE" },
+ });
+ }
+
+ static #example() {
+ const form = document.createElement("form");
+ form.id = "form_state_status";
+ form.className = "d-flex justify-content-between align-items-center";
+ form.innerHTML = `
+
+
+ Submit `;
+
+ const console = new Console();
+
+ form.onsubmit = (e) => {
+ e.preventDefault();
+ console.json({
+ mode: "replace",
+ data: Object.fromEntries(new FormData(e.target)),
+ });
+ };
+
+ return [form, console.htmlElement];
+ }
+}
+export default CustomFormValue;
diff --git a/docs/src/js/components/documentation/features/CustomSize.js b/docs/src/js/components/documentation/features/CustomSize.js
new file mode 100644
index 0000000..9688a52
--- /dev/null
+++ b/docs/src/js/components/documentation/features/CustomSize.js
@@ -0,0 +1,40 @@
+import DocArticle from "../DocArticle";
+
+class CustomSize {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = ` Bootstrap toggle can handle custom sizes by data-width and data-height options.`;
+
+ const code = `
+
+ `;
+
+ return DocArticle.build({
+ title: "Custom Sizes",
+ description,
+ example: CustomSize.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const example = [
+ { width: "100", height: "75" },
+ { height: "5rem" },
+ { width: "6em" },
+ ].map(({ width, height }) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ if (width) input.dataset.width = width;
+ if (height) input.dataset.height = height;
+ return input;
+ });
+ return example;
+ }
+}
+export default CustomSize;
diff --git a/docs/src/js/components/documentation/features/CustomStyle.js b/docs/src/js/components/documentation/features/CustomStyle.js
new file mode 100644
index 0000000..f34d3f5
--- /dev/null
+++ b/docs/src/js/components/documentation/features/CustomStyle.js
@@ -0,0 +1,63 @@
+import DocArticle from "../DocArticle";
+
+class CustomStyle {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle allows to style the buttons to fit an existing UX. Just add the data-style attribute to set a custom css class.`;
+
+ const code = `
+
+
+
+
+
+ `;
+
+ return DocArticle.build({
+ title: "Custom Style",
+ description,
+ example: CustomStyle.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const iosStyle = document.createElement("style");
+ iosStyle.textContent = `.toggle.ios, .toggle.ios .toggle-on, .toggle.ios .toggle-off, .toggle.ios .toggle-handle { border-radius: 20rem; }`;
+
+ const iosInput = document.createElement("input");
+ iosInput.type = "checkbox";
+ iosInput.checked = true;
+ iosInput.dataset.toggle = "toggle";
+ iosInput.dataset.style = "ios";
+
+ const androidStyle = document.createElement("style");
+ androidStyle.textContent = `.toggle.android , .toggle.android .toggle-on, .toggle.android .toggle-off, .toggle.android .toggle-handle{ border-radius: 0px; }`;
+
+ const androidInput = document.createElement("input");
+ androidInput.type = "checkbox";
+ androidInput.checked = true;
+ androidInput.dataset.toggle = "toggle";
+ androidInput.dataset.style = "android";
+
+ return [iosStyle, iosInput, androidStyle, androidInput];
+ }
+}
+export default CustomStyle;
diff --git a/docs/src/js/components/documentation/features/CustomText.js b/docs/src/js/components/documentation/features/CustomText.js
new file mode 100644
index 0000000..5ea4f21
--- /dev/null
+++ b/docs/src/js/components/documentation/features/CustomText.js
@@ -0,0 +1,51 @@
+import DocArticle from "../DocArticle";
+
+class CustomText {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle allows to set custom labels. Just add the data-onlabel or data-offlabel attribute to set a custom label. Plain text and HTML are supported.`;
+
+ const code = `
+ `;
+
+ return DocArticle.build({
+ title: "Custom Text",
+ description,
+ example: CustomText.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ alert: {
+ type: "warning",
+ title: "Deprecation notice",
+ versionPill: {
+ version: "5.0.0",
+ action: "DEPRECATED",
+ },
+ content: `Using data-on and data-off data attributes is deprecated and will throw a console warning. Use data-onlabel and data-offlabel data attributes instead.`,
+ },
+ });
+ }
+
+ static #example() {
+ const input1 = document.createElement("input");
+ input1.type = "checkbox";
+ input1.checked = true;
+ input1.dataset.toggle = "toggle";
+ input1.dataset.onlabel = "Ready";
+ input1.dataset.offlabel = "Not Ready";
+ input1.dataset.onstyle = "success";
+ input1.dataset.offstyle = "danger";
+
+ const input2 = document.createElement("input");
+ input2.type = "checkbox";
+ input2.checked = true;
+ input2.dataset.toggle = "toggle";
+ input2.dataset.onlabel = " Play";
+ input2.dataset.offlabel = " Pause";
+
+ return [input1, input2];
+ }
+}
+export default CustomText;
diff --git a/docs/src/js/components/documentation/features/CustomTitle.js b/docs/src/js/components/documentation/features/CustomTitle.js
new file mode 100644
index 0000000..9728616
--- /dev/null
+++ b/docs/src/js/components/documentation/features/CustomTitle.js
@@ -0,0 +1,44 @@
+import DocArticle from "../DocArticle";
+
+class CustomTitle {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle allows to set custom title. Just add the data-ontitle or data-offtitle attribute to set a custom label. To set the same title in
+ both parts just use HTML standard title attribute.`;
+
+ const code = `
+ `;
+
+ return DocArticle.build({
+ title: "Custom Title",
+ description,
+ versionPill: {
+ version: "5.0.0",
+ action: "SINCE",
+ },
+ example: CustomTitle.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const input1 = document.createElement("input");
+ input1.type = "checkbox";
+ input1.checked = true;
+ input1.dataset.toggle = "toggle";
+ input1.dataset.ontitle = "ON";
+ input1.dataset.offtitle = "OFF";
+
+ const input2 = document.createElement("input");
+ input2.type = "checkbox";
+ input2.checked = true;
+ input2.dataset.toggle = "toggle";
+ input2.title = "TOGGLE";
+
+ return [input1, input2];
+ }
+}
+export default CustomTitle;
diff --git a/docs/src/js/components/documentation/features/FormLayout.js b/docs/src/js/components/documentation/features/FormLayout.js
new file mode 100644
index 0000000..0633c84
--- /dev/null
+++ b/docs/src/js/components/documentation/features/FormLayout.js
@@ -0,0 +1,102 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class FormLayout {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle can be used in all bootstrap form layout.`;
+
+ const code = `
+
+
+
+
+
+
+
+
+`;
+
+ return DocArticle.build({
+ title: "Form Layout",
+ description,
+ example: FormLayout.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const formStacked = document.createElement("form");
+ formStacked.className = "border p-1 mb-1";
+ formStacked.innerHTML = `
+
+ Stacked 1
+
+
+
+ Stacked 2
+
`;
+
+ const fromInline = document.createElement("form");
+ fromInline.className = "border p-1 mb-1";
+ fromInline.innerHTML = `
+
+ Inline 1
+
+
+
+ Inline 2
+
`;
+
+ const formsInputGroup = [
+ { text: "Small", size: "sm" },
+ { text: "Default", size: "md" },
+ { text: "Large", size: "lg" },
+ ].map(({ text, size }) => {
+ const form = document.createElement("form");
+ form.className = `border p-1 ${size !== "lg" ? "mb-1" : ""}`;
+ form.classList.add("input-group", `input-group-${size}`);
+ form.innerHTML = `
+ Input Group ${text}
+
+ `;
+ return form;
+ });
+
+ return [formStacked, fromInline, ...formsInputGroup];
+ }
+}
+export default FormLayout;
diff --git a/docs/src/js/components/documentation/features/KeyboardInteraction.js b/docs/src/js/components/documentation/features/KeyboardInteraction.js
new file mode 100644
index 0000000..28c4813
--- /dev/null
+++ b/docs/src/js/components/documentation/features/KeyboardInteraction.js
@@ -0,0 +1,36 @@
+import DocArticle from "../DocArticle";
+
+class KeyboardInteraction {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle supports keyboard interaction. Toggle can be switched pressing SPACEBAR or ENTER while it's focused and support TAB navigation.
+A custom tabindex can be set by adding the tabindex attribute, default value is 0.`;
+
+ const code = `
+
+ `;
+
+ return DocArticle.build({
+ title: "Keyboard Interaction",
+ versionPill: { version: "5.0.0", action: "SINCE" },
+ description,
+ example: KeyboardInteraction.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ return ["-1", "", "1"].map((tabindex) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ if (tabindex !== "") input.setAttribute("tabindex", tabindex);
+ return input;
+ });
+ }
+}
+export default KeyboardInteraction;
diff --git a/docs/src/js/components/documentation/features/OutlineColors.js b/docs/src/js/components/documentation/features/OutlineColors.js
new file mode 100644
index 0000000..56b2ae3
--- /dev/null
+++ b/docs/src/js/components/documentation/features/OutlineColors.js
@@ -0,0 +1,49 @@
+import DocArticle from "../DocArticle";
+
+class OutlineColors {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle implements also all Bootstrap outline colors. Just add the data-onstyle or data-offstyle attribute with one of the following values: outline-primary, outline-secondary, outline-success, outline-danger, outline-warning, outline-info, outline-light or outline-dark.`;
+
+ const code = `
+
+
+
+
+
+
+ `;
+
+ return DocArticle.build({
+ title: "Outline Colors",
+ description,
+ example: OutlineColors.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const example = [
+ "outline-primary",
+ "outline-secondary",
+ "outline-success",
+ "outline-danger",
+ "outline-warning",
+ "outline-info",
+ "outline-light",
+ "outline-dark",
+ ].map((color) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ input.dataset.onstyle = color;
+ return input;
+ });
+ return example;
+ }
+}
+export default OutlineColors;
diff --git a/docs/src/js/components/documentation/features/Size.js b/docs/src/js/components/documentation/features/Size.js
new file mode 100644
index 0000000..e72b452
--- /dev/null
+++ b/docs/src/js/components/documentation/features/Size.js
@@ -0,0 +1,36 @@
+import DocArticle from "../DocArticle";
+
+class Size {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle is available in different sizes. Just add the data-size attribute with one of the following values: lg, sm or xs.`;
+
+ const code = `
+
+
+ `;
+
+ return DocArticle.build({
+ title: "Size",
+ description,
+ example: Size.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const example = ["lg", "default", "sm", "xs"].map((size) => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ if (size != "default") input.dataset.size = size;
+ return input;
+ });
+ return example;
+ }
+}
+export default Size;
diff --git a/docs/src/js/components/documentation/features/StateStatus.js b/docs/src/js/components/documentation/features/StateStatus.js
new file mode 100644
index 0000000..ba1cc0d
--- /dev/null
+++ b/docs/src/js/components/documentation/features/StateStatus.js
@@ -0,0 +1,65 @@
+import Console from "../../Console";
+import DocArticle from "../DocArticle";
+
+class StateStatus {
+ static build() {
+ const code = ``;
+
+ return DocArticle.build({
+ title: "Toggle State and Status",
+ description: StateStatus.#description(),
+ example: StateStatus.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #description() {
+ const description = document.createElement("div");
+
+ const paragraph = document.createElement("p");
+ paragraph.innerHTML = `Toggle state and status is simply set adding vanilla html attributes to the checkbox:`;
+ description.append(paragraph);
+
+ const ul = document.createElement("ul");
+ const li1 = document.createElement("li");
+ li1.innerHTML = `Simply add checked to convert checkboxes into on toggles.`;
+ const li2 = document.createElement("li");
+ li2.innerHTML = `Simply add disabled to convert checkboxes into disabled toggles. Checkboxes will not be presents in the payload of a submitted form.`;
+ const li3 = document.createElement("li");
+ li3.innerHTML = `Simply add readonly to convert checkboxes into disabled toggles but presents in the payload of a submitted form.`;
+ ul.append(li1, li2, li3);
+ description.append(ul);
+
+ return description;
+ }
+
+ static #example() {
+ const form = document.createElement("form");
+ form.className = "d-flex justify-content-between align-items-center";
+ form.innerHTML = `
+
+
+ Submit `;
+
+ const console = new Console();
+
+ form.onsubmit = (e) => {
+ e.preventDefault();
+ console.json({
+ mode: "replace",
+ data: Object.fromEntries(new FormData(e.target)),
+ });
+ };
+
+ return [form, console.htmlElement];
+ }
+}
+export default StateStatus;
diff --git a/docs/src/js/components/documentation/features/Tooltip.js b/docs/src/js/components/documentation/features/Tooltip.js
new file mode 100644
index 0000000..3a14349
--- /dev/null
+++ b/docs/src/js/components/documentation/features/Tooltip.js
@@ -0,0 +1,61 @@
+import DocArticle from "../DocArticle";
+
+class Tooltip {
+ static build() {
+ const description = document.createDocumentFragment();
+
+ const p1 = document.createElement("p");
+ p1.innerHTML = `Bootstrap toggle allows to set tooltip. Just add the data-tooltip-title-on or data-tooltip-title-off or data-tooltip-title-mixed attribute to set a custom label. Plain text and HTML are supported. The attribute data-tooltip-title is used as fallback when a title for the specific state is not provided.`;
+ description.appendChild(p1);
+
+ const p2 = document.createElement("p");
+ p2.innerHTML = `Tooltips placement can be set using the data-tooltip-placement attribute. Possible values are: top, bottom, left, and right. The default value is top.`;
+ description.appendChild(p2);
+
+ const code = `
+
+ Checked" data-tooltip-title-off=" Unchecked" />`;
+
+ return DocArticle.build({
+ title: "Tooltip",
+ description,
+ example: Tooltip.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ versionPill: { action: "SINCE", version: "5.3.0" },
+ });
+ }
+
+ static #example() {
+ const input1 = document.createElement("input");
+ input1.type = "checkbox";
+ input1.checked = true;
+ input1.dataset.toggle = "toggle";
+ input1.dataset.tooltipTitle = "Fallback";
+ input1.dataset.tooltipTitleOn = "Checked";
+ input1.dataset.tooltipTitleOff = "Unchecked";
+ input1.setAttribute("tristate", "");
+
+ const input2 = document.createElement("input");
+ input2.type = "checkbox";
+ input2.checked = true;
+ input2.dataset.toggle = "toggle";
+ input2.dataset.tooltipTitleOn = "Checked";
+ input2.dataset.tooltipTitleOff = "Unchecked";
+ input2.dataset.tooltipPlacement = "bottom";
+
+ const input3 = document.createElement("input");
+ input3.type = "checkbox";
+ input3.checked = true;
+ input3.dataset.toggle = "toggle";
+ input3.dataset.tooltipTitleOn =
+ ' Checked';
+ input3.dataset.tooltipTitleOff =
+ ' Unchecked';
+
+ return [input1, input2, input3];
+ }
+}
+export default Tooltip;
diff --git a/docs/src/js/components/documentation/features/Tristate.js b/docs/src/js/components/documentation/features/Tristate.js
new file mode 100644
index 0000000..ccb5851
--- /dev/null
+++ b/docs/src/js/components/documentation/features/Tristate.js
@@ -0,0 +1,32 @@
+import DocArticle from "../DocArticle";
+
+class Tristate {
+ static build() {
+ const description = document.createElement("p");
+ description.innerHTML = `Bootstrap toggle is available in tristate mode. Just add the tristate attribute to make a tristate toggle.`;
+
+ const code = ` `;
+
+ return DocArticle.build({
+ title: "Tristate Toggle",
+ versionPill: { version: "4.3.0", action: "SINCE" },
+ description,
+ example: Tristate.#example(),
+ codeBlock: {
+ language: "html",
+ code,
+ },
+ });
+ }
+
+ static #example() {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+ input.checked = true;
+ input.dataset.toggle = "toggle";
+ input.setAttribute("tristate", "");
+
+ return [input];
+ }
+}
+export default Tristate;
diff --git a/docs/src/js/main.js b/docs/src/js/main.js
new file mode 100644
index 0000000..f5504b8
--- /dev/null
+++ b/docs/src/js/main.js
@@ -0,0 +1,16 @@
+import BootstrapToggler from "./tasks/BootstrapToggler";
+import BootsrapDarkmodeToggler from "./tasks/BootsrapDarkmodeToggler";
+import CodeHighlighter from "./tasks/CodeHighlighter";
+import DOMBuilder from "./tasks/DOMBuilder";
+import LogoSwitcher from "./tasks/LogoSwitcher";
+import TOCBuilder from "./tasks/TOCBuilder";
+
+DOMBuilder.run();
+LogoSwitcher.run({ toggleDelay: 3000 });
+
+window.onload = () => {
+ BootstrapToggler.run();
+ BootsrapDarkmodeToggler.run();
+ CodeHighlighter.run();
+ TOCBuilder.run();
+};
diff --git a/docs/src/js/tasks/BootsrapDarkmodeToggler.js b/docs/src/js/tasks/BootsrapDarkmodeToggler.js
new file mode 100644
index 0000000..8634b73
--- /dev/null
+++ b/docs/src/js/tasks/BootsrapDarkmodeToggler.js
@@ -0,0 +1,9 @@
+class BootsrapDarkmodeToggler {
+ static run() {
+ document.querySelectorAll('[data-plugin="bs-darkmode-toggle"]').forEach((element) => {
+ element.bsDarkmodeToggle();
+ });
+ }
+}
+
+export default BootsrapDarkmodeToggler;
diff --git a/docs/src/js/tasks/BootstrapToggler.js b/docs/src/js/tasks/BootstrapToggler.js
new file mode 100644
index 0000000..fae8f10
--- /dev/null
+++ b/docs/src/js/tasks/BootstrapToggler.js
@@ -0,0 +1,9 @@
+class BootstrapToggler {
+ static run() {
+ document.querySelectorAll('[data-toggle="toggle"]').forEach((element) => {
+ element.bootstrapToggle();
+ });
+ }
+}
+
+export default BootstrapToggler;
diff --git a/docs/src/js/tasks/CodeHighlighter.js b/docs/src/js/tasks/CodeHighlighter.js
new file mode 100644
index 0000000..659136f
--- /dev/null
+++ b/docs/src/js/tasks/CodeHighlighter.js
@@ -0,0 +1,8 @@
+class CodeHighlighter {
+ static run() {
+ hljs.highlightAll();
+ window.highlightJsBadge();
+ }
+}
+
+export default CodeHighlighter;
diff --git a/docs/src/js/tasks/DOMBuilder.js b/docs/src/js/tasks/DOMBuilder.js
new file mode 100644
index 0000000..a1d9472
--- /dev/null
+++ b/docs/src/js/tasks/DOMBuilder.js
@@ -0,0 +1,17 @@
+import Footer from "../components/Footer";
+import Header from "../components/Header";
+import Main from "../components/Main";
+import Notice from "../components/Notice";
+
+class DOMBuilder {
+ static #sections = [
+ Header.build(),
+ Notice.build(),
+ Main.build(),
+ Footer.build(),
+ ];
+ static run() {
+ document.body.append(...this.#sections);
+ }
+}
+export default DOMBuilder;
diff --git a/docs/src/js/tasks/LogoSwitcher.js b/docs/src/js/tasks/LogoSwitcher.js
new file mode 100644
index 0000000..3896a1b
--- /dev/null
+++ b/docs/src/js/tasks/LogoSwitcher.js
@@ -0,0 +1,16 @@
+class LogoSwitcher {
+ /**
+ * Run the logo switcher.
+ * @param {Object} [options] - Options.
+ * @param {number} [options.toggleDelay=2000] - Delay between toggle in milliseconds.
+ */
+ static run({ toggleDelay = 2000 } = {}) {
+ document.querySelectorAll(".img-toggle img").forEach((element) => {
+ setInterval(function () {
+ element.classList.toggle("invisible");
+ }, toggleDelay);
+ });
+ }
+}
+
+export default LogoSwitcher;
diff --git a/docs/src/js/tasks/TOCBuilder.js b/docs/src/js/tasks/TOCBuilder.js
new file mode 100644
index 0000000..bd17ad0
--- /dev/null
+++ b/docs/src/js/tasks/TOCBuilder.js
@@ -0,0 +1,13 @@
+class TOCBuilder {
+ static run() {
+ $("#toc").html("");
+ Toc.init({
+ $nav: $("#toc"),
+ });
+ const _scrollSpy = new bootstrap.ScrollSpy(document.body, {
+ target: "#toc",
+ });
+ }
+}
+
+export default TOCBuilder;
diff --git a/sonar-project.properties b/sonar-project.properties
index e1ed5ec..ef1db6f 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -3,5 +3,5 @@ sonar.organization=palcarazm
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.typescript.tsconfigPath=tsconfig.json
-sonar.coverage.exclusions=test/**,cypress/**,src/test/**,src/main/js/**,src/main/ts/**/index.*.ts,src/main/ts/types/**
+sonar.coverage.exclusions=docs/**,test/**,cypress/**,src/test/**,src/main/js/**,src/main/ts/**/index.*.ts,src/main/ts/types/**
sonar.cpd.exclusions=test/**,cypress/**,src/test/**
\ No newline at end of file
From 01ea9716c8172ec123affc3844df93a499603369 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 4 Apr 2026 17:01:32 +0200
Subject: [PATCH 06/26] ci: add dependabot to docs package
---
.github/dependabot.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index fa374b0..5d3e138 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -19,6 +19,19 @@ updates:
prefix: "chore(deps): "
prefix-development: "chore(deps-dev): "
+ # ========== DOCS DEPENDENCIES ==========
+ - package-ecosystem: "npm"
+ directory: "/docs"
+ schedule:
+ interval: "weekly"
+ day: "friday"
+ groups:
+ version-updates:
+ applies-to: "version-updates"
+ commit-message:
+ prefix: "chore(docs): "
+ prefix-development: "chore(docs): "
+
# ========== GITHUB ACTIONS ==========
- package-ecosystem: "github-actions"
directory: "/"
From a1272c95c689c3ad0717e930cffeaa501ee01192 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:04:39 +0000
Subject: [PATCH 07/26] chore(deps-dev): bump @types/jquery from 3.5.34 to
4.0.0
Bumps [@types/jquery](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jquery) from 3.5.34 to 4.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jquery)
---
updated-dependencies:
- dependency-name: "@types/jquery"
dependency-version: 4.0.0
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index e1ac2ee..0091c0a 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"@rollup/plugin-terser": "^0.4.4",
"@types/bootstrap": "^5.2.10",
"@types/jest": "^30.0.0",
- "@types/jquery": "^3.5.33",
+ "@types/jquery": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0",
"commitlint": "^20.3.1",
From 95479813f120f31154c962588c554815a05b2690 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 4 Apr 2026 17:08:26 +0200
Subject: [PATCH 08/26] ci: update dependabot config
---
.github/dependabot.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5d3e138..0ad8d9f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -28,6 +28,7 @@ updates:
groups:
version-updates:
applies-to: "version-updates"
+ update-types: ["major", "minor", "patch"]
commit-message:
prefix: "chore(docs): "
prefix-development: "chore(docs): "
From 223a0a653aded7b1319832cc9df80f4d600518ae Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:08:44 +0000
Subject: [PATCH 09/26] ci(deps): bump actions/download-artifact from 7 to 8
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)
---
updated-dependencies:
- dependency-name: actions/download-artifact
dependency-version: '8'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 892bc01..8076a11 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -112,7 +112,7 @@ jobs:
fetch-depth: 0
- name: Download coverage artifact
- uses: actions/download-artifact@v7
+ uses: actions/download-artifact@v8
with:
name: coverage-report-22.x
path: coverage
From 9cccfae2b5242f34dbb1148c18e67b133afd2b9b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:08:50 +0000
Subject: [PATCH 10/26] ci(deps): bump actions/deploy-pages from 4 to 5
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5.
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v4...v5)
---
updated-dependencies:
- dependency-name: actions/deploy-pages
dependency-version: '5'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/deploy-docs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 73ec8ab..65e2871 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -41,4 +41,4 @@ jobs:
path: ./docs/dist
- name: Deploy to GitHub Pages
id: deployment
- uses: actions/deploy-pages@v4
+ uses: actions/deploy-pages@v5
From aa004a6db8d974764f5cb118d060ef5e2f2e23fc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:08:53 +0000
Subject: [PATCH 11/26] ci(deps): bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)
---
updated-dependencies:
- dependency-name: actions/upload-artifact
dependency-version: '7'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 892bc01..b57b009 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,7 +37,7 @@ jobs:
run: npm run test:unit
- name: Upload coverage report to Github
if: always()
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@v7
with:
name: coverage-report-${{ matrix.node-version }}
path: coverage
From 0a588692c5cfd66f38897a2e6e14033d8ca8289d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:08:56 +0000
Subject: [PATCH 12/26] ci(deps): bump actions/configure-pages from 5 to 6
Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 5 to 6.
- [Release notes](https://github.com/actions/configure-pages/releases)
- [Commits](https://github.com/actions/configure-pages/compare/v5...v6)
---
updated-dependencies:
- dependency-name: actions/configure-pages
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/deploy-docs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 73ec8ab..5f3d0cb 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -34,7 +34,7 @@ jobs:
- name: Build
run: npm run build
- name: Setup Pages
- uses: actions/configure-pages@v5
+ uses: actions/configure-pages@v6
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
From 1480b7b8e189af29723a0dbb40f0d8b69e440808 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Tue, 7 Apr 2026 13:14:49 +0200
Subject: [PATCH 13/26] chore(docs): upgrade bs-darkmode-toggle to stable and
switch to local storage
- Updated bs-darkmode-toggle from 1.0.0-rc1 to 1.0.0
- Replaced dataset.allowCookie with dataset.storage = "local"
to use local storage instead of cookies
- Updated footer background from bg-light to
bg-body-secondary for better theme consistency
This aligns the dark mode plugin with its stable API and
improves user preference persistence.
---
docs/package.json | 2 +-
docs/src/js/components/Aside.js | 2 +-
docs/src/js/components/Footer.js | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/package.json b/docs/package.json
index 4669ea8..013fb0c 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -33,7 +33,7 @@
"@highlightjs/cdn-assets": "11.11.1",
"bootstrap": "5.3.8",
"bootstrap5-toggle": "5.3.0",
- "bs-darkmode-toggle": "1.0.0-rc1",
+ "bs-darkmode-toggle": "1.0.0",
"highlightjs-badge": "0.1.9",
"jquery": "4.0.0"
},
diff --git a/docs/src/js/components/Aside.js b/docs/src/js/components/Aside.js
index 31630c0..373e395 100644
--- a/docs/src/js/components/Aside.js
+++ b/docs/src/js/components/Aside.js
@@ -85,7 +85,7 @@ class Aside {
const darkModeToggle = document.createElement("div");
darkModeToggle.dataset.plugin = "bs-darkmode-toggle";
- darkModeToggle.dataset.allowCookie = "";
+ darkModeToggle.dataset.storage = "local";
navBar.appendChild(darkModeToggle);
diff --git a/docs/src/js/components/Footer.js b/docs/src/js/components/Footer.js
index b23566e..e7ba84e 100644
--- a/docs/src/js/components/Footer.js
+++ b/docs/src/js/components/Footer.js
@@ -7,7 +7,7 @@ class Footer {
static build() {
const footer = document.createElement("footer");
- footer.className = "bg-light border-top p-4";
+ footer.className = "bg-body-secondary border-top p-4";
const container = document.createElement("div");
container.className = "container";
From d9b6318d383064f3ae794696cefe3a0c6824d3d7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Apr 2026 16:03:01 +0200
Subject: [PATCH 14/26] chore(deps-dev): bump typescript from 5.9.3 to 6.0.2
(#306)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* chore(deps-dev): bump typescript from 5.9.3 to 6.0.2
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2)
---
updated-dependencies:
- dependency-name: typescript
dependency-version: 6.0.2
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
* chore(deps-dev): bump typescript from 5.9.3 to 6.0.2
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2)
---
updated-dependencies:
- dependency-name: typescript
dependency-version: 6.0.2
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
* refactor: upgrade TypeScript target to ES2016 and improve config consistency
- Change target from ES5 to ES2016 across tsconfig.json and tsconfig.test.json
- Add 'types' field with 'node' in tsconfig.json, 'jest' and 'node' in tsconfig.test.json
- Add 'noEmit': true to tsconfig.test.json to prevent accidental output
- Switch tsconfig.test.json module from ESNext to commonjs (required for Jest)
- Add allowCypressEnv: false to cypress.config.js
- Upgrade @typescript-eslint/eslint-plugin and parser from 8.20.0 to 8.58.0
- Normalize indentation and add trailing commas in cypress.config.js
Why: ES2016 provides modern language features (Promise, Map, Set, classes)
without sacrificing broad compatibility. Explicit type roots and noEmit prevent
type resolution errors and accidental build artifacts. CommonJS is required
for Jest's module resolution. Disabling Cypress env vars prevents CI
environment injection from overriding test configs.
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pablo Alcaraz Martínez
---
cypress.config.js | 13 +++++++------
package.json | 6 +++---
tsconfig.json | 5 +++--
tsconfig.test.json | 8 +++++---
4 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/cypress.config.js b/cypress.config.js
index 4036afe..460e0b4 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -1,11 +1,12 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
- e2e: {
- setupNodeEvents(on, config) {
- // implement node event listeners here
+ allowCypressEnv: false,
+ e2e: {
+ setupNodeEvents(on, config) {
+ // implement node event listeners here
+ },
},
- },
- video: false,
- videoCompression: false
+ video: false,
+ videoCompression: false,
});
diff --git a/package.json b/package.json
index 9f3ff27..3d0efa2 100644
--- a/package.json
+++ b/package.json
@@ -61,8 +61,8 @@
"@types/bootstrap": "^5.2.10",
"@types/jest": "^30.0.0",
"@types/jquery": "^4.0.0",
- "@typescript-eslint/eslint-plugin": "^8.20.0",
- "@typescript-eslint/parser": "^8.20.0",
+ "@typescript-eslint/eslint-plugin": "^8.58.0",
+ "@typescript-eslint/parser": "^8.58.0",
"commitlint": "^20.3.1",
"eslint": "^10.0.0",
"cypress": "^15.8.1",
@@ -82,7 +82,7 @@
"postcss-cli": "^11.0.1",
"rollup": "^4.54.0",
"ts-jest": "^29.4.6",
- "typescript": "^5.9.3"
+ "typescript": "^6.0.2"
},
"peerDependencies": {
"bootstrap": ">=5 <6"
diff --git a/tsconfig.json b/tsconfig.json
index 7d76549..e63ca5c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
- "target": "ES5",
+ "target": "ES2016",
"module": "ESNext",
"strict": true,
"declaration": false,
@@ -8,7 +8,8 @@
"skipLibCheck": true,
"rootDir": "src/main/ts",
"outDir": "src/main/js",
- "typeRoots": [
+ "types": ["node"],
+ "typeRoots": [
"src/main/ts/types",
"node_modules/@types"
]
diff --git a/tsconfig.test.json b/tsconfig.test.json
index d0a7d50..27a2156 100644
--- a/tsconfig.test.json
+++ b/tsconfig.test.json
@@ -1,14 +1,16 @@
{
"compilerOptions": {
- "target": "ES5",
- "module": "ESNext",
+ "target": "ES2016",
+ "module": "commonjs", // CommonJS used cause of jest need
"strict": true,
"declaration": false,
"esModuleInterop": true,
"skipLibCheck": true,
"rootDir": "src/main/ts",
"outDir": "src/main/js",
- "typeRoots": [
+ "noEmit": true,
+ "types": ["jest", "node"],
+ "typeRoots": [
"src/main/ts/types",
"node_modules/@types"
]
From 0b1424edf0a663d2edb6cbec85814fcbbb5dde70 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 10 Apr 2026 06:46:44 +0000
Subject: [PATCH 15/26] chore(docs): bump bootstrap5-toggle
Bumps the version-updates group in /docs with 1 update: [bootstrap5-toggle](https://github.com/palcarazm/bootstrap5-toggle).
Updates `bootstrap5-toggle` from 5.3.0 to 5.3.3
- [Release notes](https://github.com/palcarazm/bootstrap5-toggle/releases)
- [Commits](https://github.com/palcarazm/bootstrap5-toggle/compare/v5.3.0...v5.3.3)
---
updated-dependencies:
- dependency-name: bootstrap5-toggle
dependency-version: 5.3.3
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: version-updates
...
Signed-off-by: dependabot[bot]
---
docs/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/package.json b/docs/package.json
index 013fb0c..d9abee7 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -32,7 +32,7 @@
"@fortawesome/fontawesome-free": "7.2.0",
"@highlightjs/cdn-assets": "11.11.1",
"bootstrap": "5.3.8",
- "bootstrap5-toggle": "5.3.0",
+ "bootstrap5-toggle": "5.3.3",
"bs-darkmode-toggle": "1.0.0",
"highlightjs-badge": "0.1.9",
"jquery": "4.0.0"
From 1256068856c0842f9c52da703b8d733ee1e8e67f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 17 Apr 2026 06:45:31 +0000
Subject: [PATCH 16/26] ci(deps): bump actions/upload-pages-artifact from 4 to
5
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v4...v5)
---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
dependency-version: '5'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/deploy-docs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 1136314..528d426 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -36,7 +36,7 @@ jobs:
- name: Setup Pages
uses: actions/configure-pages@v6
- name: Upload artifact
- uses: actions/upload-pages-artifact@v4
+ uses: actions/upload-pages-artifact@v5
with:
path: ./docs/dist
- name: Deploy to GitHub Pages
From 438594e2a0dbf15653c0751b307339a1a78464fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 1 May 2026 07:39:05 +0000
Subject: [PATCH 17/26] ci(deps): bump SonarSource/sonarqube-scan-action from
7.1.0 to 8.0.0
Bumps [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 7.1.0 to 8.0.0.
- [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases)
- [Commits](https://github.com/sonarsource/sonarqube-scan-action/compare/299e4b793aaa83bf2aba7c9c14bedbb485688ec4...59db25f34e16620e48ab4bb9e4a5dce155cb5432)
---
updated-dependencies:
- dependency-name: SonarSource/sonarqube-scan-action
dependency-version: 8.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index de1caa2..76ce940 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -119,6 +119,6 @@ jobs:
- name: SonarQube Scan
# v7.0.0
- uses: SonarSource/sonarqube-scan-action@299e4b793aaa83bf2aba7c9c14bedbb485688ec4
+ uses: SonarSource/sonarqube-scan-action@59db25f34e16620e48ab4bb9e4a5dce155cb5432
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
\ No newline at end of file
From a9b23e0b4d6d4c13b97c7fa00c99f71a24649701 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 5 Jun 2026 06:45:03 +0000
Subject: [PATCH 18/26] chore(deps-dev): bump @commitlint/cli from 20.5.3 to
21.0.2
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 20.5.3 to 21.0.2.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/cli)
---
updated-dependencies:
- dependency-name: "@commitlint/cli"
dependency-version: 21.0.2
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index d37298e..5843680 100644
--- a/package.json
+++ b/package.json
@@ -68,7 +68,7 @@
"npm": ">=10"
},
"devDependencies": {
- "@commitlint/cli": "^20.3.1",
+ "@commitlint/cli": "^21.0.2",
"@commitlint/config-conventional": "^20.3.1",
"@rollup/plugin-terser": "^1.0.0",
"@types/bootstrap": "^5.2.10",
From 242c7f46f209d31b11c310c6c808848e7e3f4e34 Mon Sep 17 00:00:00 2001
From: xianzuyang9-blip
Date: Tue, 9 Jun 2026 12:50:05 +0800
Subject: [PATCH 19/26] fix: export published css and js assets
---
package.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index d37298e..65514ad 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,9 @@
"types": "./dist/bootstrap5-toggle.d.ts",
"import": "./dist/bootstrap5-toggle.mjs",
"require": "./dist/bootstrap5-toggle.cjs"
- }
+ },
+ "./css/*": "./css/*",
+ "./js/*": "./js/*"
},
"scripts": {
"build": "grunt build",
From 3ddcaf0c972fd39461d999ca2dc74a7964da51f1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Jun 2026 06:43:45 +0000
Subject: [PATCH 20/26] ci(deps): bump cypress-io/github-action
Bumps the minor-and-patch-updates group with 1 update in the / directory: [cypress-io/github-action](https://github.com/cypress-io/github-action).
Updates `cypress-io/github-action` from 7.1.9 to 7.4.0
- [Release notes](https://github.com/cypress-io/github-action/releases)
- [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/github-action/compare/783cb3f07983868532cabaedaa1e6c00ff4786a8...948d67d3074f1bbb6379c8bdbb04e95d2f8e593f)
---
updated-dependencies:
- dependency-name: cypress-io/github-action
dependency-version: 7.1.10
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: minor-and-patch-updates
...
Signed-off-by: dependabot[bot]
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index de1caa2..2bb8a3d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -77,7 +77,7 @@ jobs:
run: npm run build
- name: Cypress run
#v6.10.8
- uses: cypress-io/github-action@783cb3f07983868532cabaedaa1e6c00ff4786a8
+ uses: cypress-io/github-action@948d67d3074f1bbb6379c8bdbb04e95d2f8e593f
with:
browser: chrome
From 099fb26ed6c1b972d7bf5fce810872887f960e4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 20 Jun 2026 11:31:44 +0200
Subject: [PATCH 21/26] refactor(build): replace Grunt with npm scripts and
modern tooling
Remove Grunt and its plugins (grunt, grunt-banner, grunt-contrib-clean,
grunt-contrib-copy, grunt-exec) as the build orchestration layer. Replace
with npm scripts using tsc, rollup, postcss, and rimraf.
Key changes:
- Add clean, build, watch scripts with rimraf, concurrently
- Centralize copyright banner in scripts/package-banner.js
- Move TypeScript output to dist/tmp/ (isolated from source)
- Add postcss.config.js for banner injection in CSS
- Update rollup.config.js to use banner from shared utility
- Add version:update script to sync README and docs/package.json
- Add preversion hook running lint + tests before version bump
Benefits:
- Reduced dependencies (remove 4 Grunt packages)
- Simpler, more transparent build pipeline
- Build artifacts isolated from source (dist/tmp/)
- Dynamic copyright banners from package.json
- Parallel watch mode with concurrently
BREAKING CHANGE: TypeScript declaration output moved from
src/main/@types to dist/tmp/@types. This only affects developers
building from source, not package consumers.
Closes #330
---
.npmignore | 1 +
Gruntfile.cjs | 60 ---------------
docs/package.json | 1 +
docs/postcss.config.js | 5 ++
package.json | 29 ++++---
postcss.config.js | 8 ++
rollup.config.js | 117 ++++++++++++++++-------------
scripts/package-banner.js | 14 ++++
scripts/update-version.js | 24 ++++++
src/main/css/bootstrap5-toggle.css | 13 ----
tsconfig.json | 2 +-
11 files changed, 137 insertions(+), 137 deletions(-)
delete mode 100644 Gruntfile.cjs
create mode 100644 docs/postcss.config.js
create mode 100644 postcss.config.js
create mode 100644 scripts/package-banner.js
create mode 100644 scripts/update-version.js
diff --git a/.npmignore b/.npmignore
index 95284e5..1beb4cb 100644
--- a/.npmignore
+++ b/.npmignore
@@ -3,6 +3,7 @@ node_modules
# Packaging
bootstrap5-toggle-*.tgz
+dist/tmp
# Debug
debug
diff --git a/Gruntfile.cjs b/Gruntfile.cjs
deleted file mode 100644
index 680ef7c..0000000
--- a/Gruntfile.cjs
+++ /dev/null
@@ -1,60 +0,0 @@
-module.exports = function setup(grunt) {
- "use strict";
-
- grunt.initConfig({
- pkg: grunt.file.readJSON("package.json"),
- banner:
- "/* Copyright Notice\n" +
- " * <%= pkg.name %> v<%= pkg.version %>\n" +
- " * <%= pkg.homepage %>\n" +
- " * @author 2011-2014 Min Hur (https://github.com/minhur)\n" +
- " * @author 2018-2019 Brent Ely (https://github.com/gitbrent)\n" +
- " * @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)\n" +
- " * @funding <%= pkg.funding.type %>\n" +
- " * @see <%= pkg.funding.url %>\n" +
- " * @license <%= pkg.license %>\n" +
- " * @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE\n" +
- " */\n",
- clean: ["js","css", "dist"],
- exec: {
- ts: "npx tsc",
- rollup: "npx rollup -c",
- postcss: "npx postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.css --map",
- postcssMin: "npx postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.min.css --map --env production"
- },
- usebanner: {
- taskName: {
- options: {
- position: "top",
- banner: "<%= banner %>",
- linebreak: true,
- replace: true,
- },
- files: {
- src: ["css/*.css", "js/*.js", "dist/**/*"],
- },
- },
- },
- copy: {
- main: {
- src: "README.template.md",
- dest: "README.md",
- options: {
- process: function (content, _srcpath) {
- let pkg = grunt.file.readJSON("package.json");
- return content.replaceAll("#version#", pkg.version);
- },
- },
- },
- },
- });
-
- grunt.loadNpmTasks("grunt-banner");
- grunt.loadNpmTasks("grunt-contrib-clean");
- grunt.loadNpmTasks("grunt-contrib-copy");
- grunt.loadNpmTasks("grunt-exec");
-
- grunt.registerTask("default", ["clean", "exec:ts", "exec:rollup", "exec:postcss", "exec:postcssMin", "usebanner"]);
- grunt.registerTask("build", ["clean", "exec:ts", "exec:rollup", "exec:postcss", "exec:postcssMin", "usebanner"]);
- grunt.registerTask("readme", ["copy"]);
-};
diff --git a/docs/package.json b/docs/package.json
index d9abee7..269cf04 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -39,6 +39,7 @@
},
"devDependencies": {
"@rollup/plugin-terser": "^1.0.0",
+ "cssnano": "^8.0.2",
"grunt": "^1.6.1",
"grunt-contrib-clean": "^2.0.1",
"grunt-contrib-copy": "^1.0.0",
diff --git a/docs/postcss.config.js b/docs/postcss.config.js
new file mode 100644
index 0000000..61752fc
--- /dev/null
+++ b/docs/postcss.config.js
@@ -0,0 +1,5 @@
+const cssnano = require("cssnano");
+
+module.exports = [{
+ plugins: [cssnano()],
+}];
\ No newline at end of file
diff --git a/package.json b/package.json
index 65514ad..8f31de0 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"files": [
"css/*",
"js/*",
- "dist"
+ "dist/*"
],
"exports": {
".": {
@@ -35,10 +35,19 @@
"./js/*": "./js/*"
},
"scripts": {
- "build": "grunt build",
- "postversion": "grunt build && grunt readme && doctoc README.md --github",
- "prepack": "grunt build && grunt readme && doctoc README.md --github",
- "prepublishOnly": "git add -A && git commit",
+ "clean": "rimraf js css dist",
+ "clean:tmp": "rimraf dist/tmp",
+ "prebuild": "npm run clean",
+ "build": "tsc && npm run build:js && npm run build:css",
+ "build:js": "rollup -c",
+ "build:css": "postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.css --map && postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.min.css --map --use cssnano && postcss src/main/css/bootstrap5-toggle.css -o dist/bootstrap5-toggle.css --map --use cssnano",
+ "postbuild": "npm run clean:tmp",
+ "watch": "concurrently \"tsc --watch\" \"rollup --watch\" \"postcss src/main/css/bootstrap5-toggle.css -o css/bootstrap5-toggle.css --map --watch\" \"postcss src/main/css/bootstrap5-toggle.css -o dist/bootstrap5-toggle.css --map --watch\"",
+ "preversion": "npm run lint && npm run test",
+ "version": "npm run build && npm run version:update && doctoc README.md --github && git add -A",
+ "version:update": "node scripts/update-version.js",
+ "postversion": "git push --follow-tags",
+ "prepack": "npm run build && npm run version:update && doctoc README.md --github",
"test": "npm run test:unit && npm run test:e2e",
"test:e2e": "cypress run --headless",
"test:unit": "jest --coverage",
@@ -76,25 +85,25 @@
"@types/bootstrap": "^5.2.10",
"@types/jest": "^30.0.0",
"@types/jquery": "^4.0.0",
+ "@types/node": "^26.0.0",
"@typescript-eslint/eslint-plugin": "^8.58.0",
"@typescript-eslint/parser": "^8.58.0",
"commitlint": "^20.3.1",
+ "concurrently": "^10.0.3",
+ "cssnano": "^8.0.2",
"cypress": "^15.8.1",
"cypress-plugin-tab": "^2.0.0",
"doctoc": "^2.2.0",
"dompurify": "^3.0.0",
"eslint": "^10.0.0",
"globals": "^17.0.0",
- "grunt": "^1.5.3",
- "grunt-banner": "^0.6.0",
- "grunt-contrib-clean": "^2.0.1",
- "grunt-contrib-copy": "^1.0.0",
- "grunt-exec": "^3.0.0",
"husky": "^9.0.11",
"jest": "^30.2.0",
"jest-environment-jsdom": "^30.2.0",
"postcss": "^8.5.6",
+ "postcss-banner": "^4.0.1",
"postcss-cli": "^11.0.1",
+ "rimraf": "^6.1.3",
"rollup": "^4.54.0",
"rollup-plugin-dts": "^6.4.1",
"ts-jest": "^29.4.6",
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..68c5325
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,8 @@
+import banner from "postcss-banner";
+import { bannerContent } from "./scripts/package-banner.js";
+
+export default {
+ plugins: [
+ banner({ banner: bannerContent }),
+ ]
+};
\ No newline at end of file
diff --git a/rollup.config.js b/rollup.config.js
index e51032d..27bb668 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -1,5 +1,55 @@
import terser from "@rollup/plugin-terser";
import dts from "rollup-plugin-dts";
+import { bannerContent } from "./scripts/package-banner.js";
+
+const banner = `/*
+ * ${bannerContent.replaceAll("\n", "\n * ")}
+ */`;
+
+const umdECMAS = {
+ input: "src/main/js/index.ecmas.js",
+ output: [
+ {
+ file: "js/bootstrap5-toggle.ecmas.js",
+ format: "umd",
+ sourcemap: true,
+ banner,
+ },
+ {
+ file: "js/bootstrap5-toggle.ecmas.min.js",
+ format: "umd",
+ sourcemap: true,
+ plugins: [terser()],
+ banner,
+ },
+ ],
+};
+
+const umdJquery = {
+ input: "src/main/js/index.jquery.js",
+ external: ["jquery"],
+ output: [
+ {
+ file: "js/bootstrap5-toggle.jquery.js",
+ format: "umd",
+ sourcemap: true,
+ globals: {
+ jquery: "jQuery",
+ },
+ banner,
+ },
+ {
+ file: "js/bootstrap5-toggle.jquery.min.js",
+ format: "umd",
+ sourcemap: true,
+ globals: {
+ jquery: "jQuery",
+ },
+ plugins: [terser()],
+ banner,
+ },
+ ],
+};
const mainConfig = {
input: "src/main/js/index.js",
@@ -7,71 +57,32 @@ const mainConfig = {
{
file: "dist/bootstrap5-toggle.cjs",
format: "cjs",
- sourcemap: false,
- plugins: [terser()]
+ sourcemap: true,
+ plugins: [terser()],
+ banner,
},
{
file: "dist/bootstrap5-toggle.mjs",
format: "es",
- sourcemap: false,
- plugins: [terser()]
- }
- ]
+ sourcemap: true,
+ plugins: [terser()],
+ banner,
+ },
+ ],
};
const typesConfig = {
- input: "src/main/@types/index.d.ts",
+ input: "dist/tmp/@types/index.d.ts",
output: {
file: "dist/bootstrap5-toggle.d.ts",
- format: "es"
+ format: "es",
+ banner,
},
plugins: [
dts({
respectExternal: true,
- })
- ]
+ }),
+ ],
};
-export default [
- {
- input: "src/main/js/index.ecmas.js",
- output: [
- {
- file: "js/bootstrap5-toggle.ecmas.js",
- format: "umd",
- sourcemap: true
- },
- {
- file: "js/bootstrap5-toggle.ecmas.min.js",
- format: "umd",
- sourcemap: true,
- plugins: [terser()]
- }
- ]
- },
- {
- input: "src/main/js/index.jquery.js",
- external: ["jquery"],
- output: [
- {
- file: "js/bootstrap5-toggle.jquery.js",
- format: "umd",
- sourcemap: true,
- globals: {
- jquery: "jQuery"
- }
- },
- {
- file: "js/bootstrap5-toggle.jquery.min.js",
- format: "umd",
- sourcemap: true,
- globals: {
- jquery: "jQuery"
- },
- plugins: [terser()]
- }
- ]
- },
- mainConfig,
- typesConfig,
-];
+export default [umdECMAS, umdJquery, mainConfig, typesConfig];
diff --git a/scripts/package-banner.js b/scripts/package-banner.js
new file mode 100644
index 0000000..f56ad8f
--- /dev/null
+++ b/scripts/package-banner.js
@@ -0,0 +1,14 @@
+import fs from "node:fs";
+
+const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
+
+export const bannerContent = `Copyright Notice
+${pkg.name} v${pkg.version}
+${pkg.homepage}
+@author 2011-2014 Min Hur (https://github.com/minhur)
+@author 2018-2019 Brent Ely (https://github.com/gitbrent)
+@author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
+@funding ${pkg.funding.type}
+@see ${pkg.funding.url}
+@license ${pkg.license}
+@see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE`;
\ No newline at end of file
diff --git a/scripts/update-version.js b/scripts/update-version.js
new file mode 100644
index 0000000..cb9cf86
--- /dev/null
+++ b/scripts/update-version.js
@@ -0,0 +1,24 @@
+// scripts/update-package-version.js
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const rootDir = path.resolve(__dirname, "..");
+
+const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8"));
+const version = pkg.version;
+
+// Update README
+const template = fs.readFileSync(path.join(rootDir, "README.template.md"), "utf8");
+const updatedReadme = template.replaceAll("#version#", version);
+fs.writeFileSync(path.join(rootDir, "README.md"), updatedReadme);
+console.log(`README.md updated to version ${version}`);
+
+// Update docs/package.json
+const docsPkgPath = path.join(rootDir, "docs", "package.json");
+const docsPkg = JSON.parse(fs.readFileSync(docsPkgPath, "utf8"));
+docsPkg.dependencies = docsPkg.dependencies || {};
+docsPkg.dependencies["bootstrap5-toggle"] = version;
+fs.writeFileSync(docsPkgPath, JSON.stringify(docsPkg, null, 2) + "\n");
+console.log(`docs/package.json dependency updated to ${version}`);
\ No newline at end of file
diff --git a/src/main/css/bootstrap5-toggle.css b/src/main/css/bootstrap5-toggle.css
index d8d5f3f..b8bde77 100644
--- a/src/main/css/bootstrap5-toggle.css
+++ b/src/main/css/bootstrap5-toggle.css
@@ -1,16 +1,3 @@
-/* Copyright Notice
- * bootstrap5-toggle v5.1.3
- * https://palcarazm.github.io/bootstrap5-toggle/
- * @author 2011-2014 Min Hur (https://github.com/minhur)
- * @author 2018-2019 Brent Ely (https://github.com/gitbrent)
- * @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
- * @funding GitHub Sponsors
- * @see https://github.com/sponsors/palcarazm
- * @license MIT
- * @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE
- */
-
-
/*
* @added 3.0.0: Return support for "*-xs" removed in Bootstrap-4
* @see: [Comment](https://github.com/twbs/bootstrap/issues/21881#issuecomment-341972830)
diff --git a/tsconfig.json b/tsconfig.json
index b2430f2..9d3dfe7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,7 +5,7 @@
"strict": true,
"declaration": true,
"declarationMap": false,
- "declarationDir": "./src/main/@types",
+ "declarationDir": "./dist/tmp/@types",
"esModuleInterop": true,
"skipLibCheck": true,
"rootDir": "src/main/ts",
From f428911c817a53f7ecefbf6b479b01952d6e954a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 20 Jun 2026 11:38:14 +0200
Subject: [PATCH 22/26] fix: rename Cypress configuration file as cjs
---
cypress.config.js => cypress.config.cjs | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename cypress.config.js => cypress.config.cjs (100%)
diff --git a/cypress.config.js b/cypress.config.cjs
similarity index 100%
rename from cypress.config.js
rename to cypress.config.cjs
From 60322f4a8b32f19f765568dc2d8b3e1043f31bb3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 20 Jun 2026 09:43:37 +0000
Subject: [PATCH 23/26] chore(deps-dev): bump commitlint from 20.5.3 to 21.0.2
Bumps [commitlint](https://github.com/conventional-changelog/commitlint/tree/HEAD/@alias/commitlint) from 20.5.3 to 21.0.2.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@alias/commitlint/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@alias/commitlint)
---
updated-dependencies:
- dependency-name: commitlint
dependency-version: 21.0.2
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 8f31de0..7b536cb 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
"@types/node": "^26.0.0",
"@typescript-eslint/eslint-plugin": "^8.58.0",
"@typescript-eslint/parser": "^8.58.0",
- "commitlint": "^20.3.1",
+ "commitlint": "^21.0.2",
"concurrently": "^10.0.3",
"cssnano": "^8.0.2",
"cypress": "^15.8.1",
From d54009d8b77ef28d0043c0b179f6e482bd75bf0a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 20 Jun 2026 09:49:38 +0000
Subject: [PATCH 24/26] chore(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 20.5.3 to 21.0.2.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/config-conventional)
---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
dependency-version: 21.0.2
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 40c444e..21e1ef1 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,7 @@
},
"devDependencies": {
"@commitlint/cli": "^21.0.2",
- "@commitlint/config-conventional": "^20.3.1",
+ "@commitlint/config-conventional": "^21.0.2",
"@rollup/plugin-terser": "^1.0.0",
"@types/bootstrap": "^5.2.10",
"@types/jest": "^30.0.0",
From c02fcfc8788cba340baf6555cd1f928d9feb8a38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 20 Jun 2026 12:47:36 +0200
Subject: [PATCH 25/26] refactor: preserve instance identity in
Toggle.rerender()
Replaces the previous destroy+recreate implementation with an in-place
rebuild that preserves the same object reference.
The method now follows a precise sequence:
1. Restore original input properties
2. Unbind all event listeners
3. Destroy the DOM builder
4. Re-resolve options from element and user options
5. Create a fresh StateReducer
6. Build new toggle DOM structure
7. Re-bind all event listeners
8. Re-intercept input properties
This ensures that this.element.bsToggle remains unchanged,
making external references to the instance safe.
Implements #318
---
src/main/ts/BootstrapToggle.ts | 48 +++++++++++++++++++++++------
src/test/ts/BootstrapToggle.test.ts | 29 ++++++++++++++---
2 files changed, 64 insertions(+), 13 deletions(-)
diff --git a/src/main/ts/BootstrapToggle.ts b/src/main/ts/BootstrapToggle.ts
index 607585b..4aaf8fc 100644
--- a/src/main/ts/BootstrapToggle.ts
+++ b/src/main/ts/BootstrapToggle.ts
@@ -8,9 +8,10 @@ import ToggleEvents, { ToggleEventDetail } from "./types/ToggleEvents";
export class Toggle {
private readonly element: HTMLInputElement & { bsToggle?: Toggle };
private readonly userOptions: UserOptions;
- private readonly options: ToggleOptions;
- private readonly stateReducer: StateReducer;
- private readonly domBuilder: DOMBuilder;
+
+ private options: ToggleOptions;
+ private stateReducer: StateReducer;
+ private domBuilder: DOMBuilder;
private pointer: { x: number; y: number } | null = null;
private readonly SCROLL_THRESHOLD = 10;
@@ -532,11 +533,40 @@ export class Toggle {
}
/**
- * Destroys the toggle element and reinitializes it with the same options.
- *This method is useful when you need to reinitialize the toggle element with the same options.
- */
- rerender() {
- this.destroy();
- const _ = new Toggle(this.element, this.userOptions);
+ * Rebuilds the toggle component in-place without destroying the current instance.
+ *
+ * This method is useful when you need to reinitialize the toggle element with the same options,
+ * for example after HTML attribute changes or when the DOM structure has been modified externally.
+ *
+ * The method follows this sequence:
+ * 1. Restores original input properties (checked, disabled, readOnly, indeterminate)
+ * 2. Unbinds all event listeners (pointer, keyboard, label, form reset)
+ * 3. Destroys the DOM builder (removes toggle DOM, restores original checkbox)
+ * 4. Re-resolves options from the element and user options
+ * 5. Creates a fresh StateReducer with the updated tristate value
+ * 6. Builds new toggle DOM structure
+ * 7. Re-binds all event listeners
+ * 8. Re-intercepts input properties for external change detection
+ *
+ * Important: The instance identity is preserved (this.element.bsToggle remains unchanged),
+ * making it safe for external code that holds references to this instance.
+ *
+ * @returns void
+ */
+ rerender() {
+ this.restoreInputProperties();
+ this.unbindEventListeners();
+ this.domBuilder.destroy();
+
+ this.options = OptionResolver.resolve(this.element, this.userOptions);
+ this.stateReducer = new StateReducer(this.element, this.options.tristate);
+ this.domBuilder = new DOMBuilder(
+ this.element,
+ this.options,
+ this.stateReducer.get()
+ );
+
+ this.bindEventListeners();
+ this.interceptInputProperties();
}
}
diff --git a/src/test/ts/BootstrapToggle.test.ts b/src/test/ts/BootstrapToggle.test.ts
index 91f4f27..c46d59f 100644
--- a/src/test/ts/BootstrapToggle.test.ts
+++ b/src/test/ts/BootstrapToggle.test.ts
@@ -1,7 +1,9 @@
+///
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Toggle } from "../../main/ts/BootstrapToggle";
import { DOMBuilder } from "../../main/ts/core/DOMBuilder";
+import { OptionResolver } from "../../main/ts/core/OptionResolver";
import { ToggleActionType, ToggleStateValue } from "../../main/ts/core/StateReducer.types";
import ToggleEvents from "../../main/ts/types/ToggleEvents";
@@ -709,14 +711,33 @@ describe("Toggle", () => {
});
describe("rerender()", () =>{
- it("rerender destroys and reinitializes toggle", () => {
- (input as any).bootstrapToggle = jest.fn();
-
+ it("rebuilds toggle in-place while preserving instance identity", () => {
const toggle = new Toggle(input, {});
+ const instanceRef = toggle;
+
+ const originalBsToggle = (input as any).bsToggle;
+
toggle.rerender();
-
+
+ expect(toggle).toBe(instanceRef);
+ expect((input as any).bsToggle).toBe(originalBsToggle);
+ expect((input as any).bsToggle).toBe(toggle);
+
expect(destroyMock).toHaveBeenCalled();
expect(DOMBuilder).toHaveBeenCalledTimes(2);
});
+
+ it("re-resolves options from HTML attributes after change", () => {
+
+ const toggle = new Toggle(input, { onlabel: "Old" });
+
+ const resolveSpy = jest.spyOn(OptionResolver, "resolve");
+ input.dataset.onlabel = "New";
+
+ toggle.rerender();
+
+ expect(resolveSpy).toHaveBeenCalledWith(input, toggle["userOptions"]);
+ resolveSpy.mockRestore();
+ });
});
});
From 5b3f1a2f311dad06ccad81b8223767539d1bfa89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Alcaraz=20Mart=C3=ADnez?=
Date: Sat, 20 Jun 2026 12:55:20 +0200
Subject: [PATCH 26/26] 5.4.0
---
README.md | 12 ++++++------
docs/package.json | 2 +-
package.json | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 999e3f8..d6af625 100644
--- a/README.md
+++ b/README.md
@@ -66,18 +66,18 @@ See EOL for each version in [Security Policy Page](https://github.com/palcarazm/
```html
-
+
```
### jQuery Interface
```html
-
+
```
## Download
@@ -89,13 +89,13 @@ See EOL for each version in [Security Policy Page](https://github.com/palcarazm/
[](https://www.npmjs.com/package/bootstrap5-toggle)
```ksh
-npm install bootstrap5-toggle@5.3.1
+npm install bootstrap5-toggle@5.4.0
```
## Yarn
```ksh
-yarn add bootstrap5-toggle@5.3.1
+yarn add bootstrap5-toggle@5.4.0
```
# Usage
diff --git a/docs/package.json b/docs/package.json
index 269cf04..ee5e73e 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -32,7 +32,7 @@
"@fortawesome/fontawesome-free": "7.2.0",
"@highlightjs/cdn-assets": "11.11.1",
"bootstrap": "5.3.8",
- "bootstrap5-toggle": "5.3.3",
+ "bootstrap5-toggle": "5.4.0",
"bs-darkmode-toggle": "1.0.0",
"highlightjs-badge": "0.1.9",
"jquery": "4.0.0"
diff --git a/package.json b/package.json
index c17fd6f..4964390 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bootstrap5-toggle",
- "version": "5.3.1",
+ "version": "5.4.0",
"type": "module",
"author": {
"name": "Pablo Alcaraz Martínez",