Compare commits

..

18 Commits

Author SHA1 Message Date
XhmikosR a0a6128441 docs: move docs.css to StackBlitz 2024-03-18 14:55:37 +02:00
XhmikosR 3acaf6575b WIP 2024-03-18 14:55:31 +02:00
XhmikosR f030e3a153 docs: add SRI hashes 2024-03-18 14:55:31 +02:00
XhmikosR 3206c4919f docs: move algolia config to hugo config 2024-03-18 14:55:04 +02:00
XhmikosR a759e1f350 docs: use defer when possible for JS 2024-03-18 14:55:04 +02:00
Julien Déramond 4558b8b190 docs: move search style to its own CSS file 2024-03-18 14:55:04 +02:00
XhmikosR c6ed5a15cb docs: improve stackblitz.js
* tweak indentation
* stop extending the sdk object
* conditionally add `index.js`
2024-03-18 14:55:02 +02:00
XhmikosR ee7139494b docs: bundle assets with Hugo
Also:

* load any docs' third-party dependencies from node_modules
* exclude docsearch from layouts that don't use it
* preconnect to algolia only when not examples layout
* switch to `RelPermalink`
* refactor JS assets into partials
2024-03-18 14:54:30 +02:00
Caleb Albritton 4219af2c0e Fix broken comment link for reflow hack (#39791) 2024-03-18 10:32:49 +02:00
dependabot[bot] 10f9c01908 Build(deps-dev): Bump follow-redirects from 1.15.5 to 1.15.6 (#39792)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 09:30:16 +02:00
XhmikosR 202a20f561 docs: mention text transform classes (#39782) 2024-03-15 21:23:19 +02:00
Edward Ezekiel 086a0e2a1c Update masthead to HeroDevs EOL (#39749)
* feat(masthead):  Update masthead to HeroDevs EOL

Add box-arrow-up-right icon.

* Update masthead.html

* Update icons.html

---------

Co-authored-by: Mark Otto <markdotto@gmail.com>
2024-03-15 10:23:43 -07:00
XhmikosR 545a650035 docs: move fathom site to hugo config (#39781) 2024-03-13 11:43:02 +02:00
Julien Déramond 3150c6985d Docs: fix Reddit URL to avoid redirection (#39777) 2024-03-12 16:10:02 +02:00
XhmikosR ab1a765f75 examples: assorted tweaks (#39772)
* remove trailing slash
* use urls.JoinPath
* reindent
* use `imageConfig` so that we add dimensions automatically
2024-03-12 11:42:03 +02:00
XhmikosR 13f40f1791 Switch to lockfile v3 2024-03-12 09:50:25 +02:00
XhmikosR 7b8211ce64 Update globby to v14
Also:

* fix globals type
* switch to async
2024-03-12 09:50:25 +02:00
XhmikosR 2bc395b563 Update devDependencies and regenerate package-lock.json
* eslint-plugin-markdown    ^3.0.1  →    ^4.0.1
* hugo-bin                ^0.120.7  →  ^0.120.8
* rollup                   ^4.12.0  →   ^4.13.0
* terser                   ^5.27.2  →   ^5.29.1
2024-03-12 09:50:25 +02:00
65 changed files with 1399 additions and 9221 deletions
+26 -6
View File
@@ -67,6 +67,7 @@
}
],
"no-console": "error",
"no-negated-condition": "off",
"object-curly-spacing": [
"error",
"always"
@@ -86,7 +87,6 @@
"unicorn/filename-case": "off",
"unicorn/no-array-callback-reference": "off",
"unicorn/no-array-method-this-argument": "off",
"unicorn/no-negated-condition": "off",
"unicorn/no-null": "off",
"unicorn/no-typeof-undefined": "off",
"unicorn/no-unused-properties": "error",
@@ -96,6 +96,7 @@
"unicorn/prefer-dom-node-dataset": "off",
"unicorn/prefer-module": "off",
"unicorn/prefer-query-selector": "off",
"unicorn/prefer-spread": "off",
"unicorn/prefer-string-replace-all": "off",
"unicorn/prevent-abbreviations": "off"
},
@@ -112,7 +113,8 @@
"sourceType": "module"
},
"rules": {
"no-console": "off"
"no-console": "off",
"unicorn/prefer-top-level-await": "off"
}
},
{
@@ -163,7 +165,8 @@
},
"rules": {
"no-console": "off",
"no-new": "off"
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
@@ -190,7 +193,17 @@
"ecmaVersion": 2019
},
"rules": {
"no-new": "off"
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
"files": [
"site/assets/js/**"
],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2020
}
},
{
@@ -204,9 +217,16 @@
},
{
"files": [
"**/*.md/*.js"
"**/*.md/*.js",
"**/*.md/*.mjs"
],
"extends": "plugin:markdown/recommended"
"extends": "plugin:markdown/recommended-legacy",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"unicorn/prefer-node-protocol": "off"
}
}
]
}
-1
View File
@@ -1 +0,0 @@
lockfile-version=2
+1 -1
View File
@@ -177,7 +177,7 @@ Get updates on Bootstrap's development and chat with the project maintainers and
- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap).
- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/).
- Ask questions and explore [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions).
- Discuss, ask questions, and more on [the community Discord](https://discord.gg/bZUvakRU3M) or [Bootstrap subreddit](https://reddit.com/r/bootstrap).
- Discuss, ask questions, and more on [the community Discord](https://discord.gg/bZUvakRU3M) or [Bootstrap subreddit](https://www.reddit.com/r/bootstrap/).
- Chat with fellow Bootstrappers in IRC. On the `irc.libera.chat` server, in the `#bootstrap` channel.
- Implementation help may be found at Stack Overflow (tagged [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5)).
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
+18 -8
View File
@@ -9,7 +9,7 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import globby from 'globby'
import { globby } from 'globby'
import { rollup } from 'rollup'
import banner from './banner.mjs'
@@ -17,7 +17,7 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const sourcePath = path.resolve(__dirname, '../js/src/').replace(/\\/g, '/')
const jsFiles = globby.sync(`${sourcePath}/**/*.js`)
const jsFiles = await globby(`${sourcePath}/**/*.js`)
// Array which holds the resolved plugins
const resolvedPlugins = []
@@ -37,6 +37,9 @@ for (const file of jsFiles) {
}
const build = async plugin => {
/**
* @type {import('rollup').GlobalsOption}
*/
const globals = {}
const bundle = await rollup({
@@ -87,12 +90,19 @@ const build = async plugin => {
console.log(`Built ${plugin.className}`)
}
const basename = path.basename(__filename)
const timeLabel = `[${basename}] finished`
(async () => {
try {
const basename = path.basename(__filename)
const timeLabel = `[${basename}] finished`
console.log('Building individual plugins...')
console.time(timeLabel)
console.log('Building individual plugins...')
console.time(timeLabel)
await Promise.all(Object.values(resolvedPlugins).map(plugin => build(plugin)))
await Promise.all(Object.values(resolvedPlugins).map(plugin => build(plugin)))
console.timeEnd(timeLabel)
console.timeEnd(timeLabel)
} catch (error) {
console.error(error)
process.exit(1)
}
})()
+27 -19
View File
@@ -81,25 +81,33 @@ function showUsage(args) {
process.exit(1)
}
const args = process.argv.slice(2)
let [oldVersion, newVersion] = args
async function main(args) {
let [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
showUsage(args)
if (!oldVersion || !newVersion) {
showUsage(args)
}
// Strip any leading `v` from arguments because
// otherwise we will end up with duplicate `v`s
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => {
return arg.startsWith('v') ? arg.slice(1) : arg
})
if (oldVersion === newVersion) {
showUsage(args)
}
bumpNpmVersion(newVersion)
try {
await Promise.all(
FILES.map(file => replaceRecursively(file, oldVersion, newVersion))
)
} catch (error) {
console.error(error)
process.exit(1)
}
}
// Strip any leading `v` from arguments because
// otherwise we will end up with duplicate `v`s
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => {
return arg.startsWith('v') ? arg.slice(1) : arg
})
if (oldVersion === newVersion) {
showUsage(args)
}
bumpNpmVersion(newVersion)
await Promise.all(
FILES.map(file => replaceRecursively(file, oldVersion, newVersion))
)
main(process.argv.slice(2))
+13 -2
View File
@@ -8,8 +8,6 @@ security:
getenv:
- ^HUGO_
- NETLIFY
gotemplates:
allowActionJSTmpl: true
markup:
goldmark:
@@ -21,6 +19,9 @@ markup:
startLevel: 2
endLevel: 6
build:
noJSConfigInAssets: true
buildDrafts: true
buildFuture: true
@@ -44,6 +45,8 @@ module:
target: layouts
- source: site/static
target: static
- source: node_modules/@docsearch/css
target: assets/scss/@docsearch/css
- source: site/static/docs/5.3/assets/img/favicons/apple-touch-icon.png
target: static/apple-touch-icon.png
- source: site/static/docs/5.3/assets/img/favicons/favicon.ico
@@ -67,6 +70,14 @@ params:
icons: "https://icons.getbootstrap.com/"
swag: "https://cottonbureau.com/people/bootstrap"
analytics:
fathom_site: "ITUSEYJG"
algolia:
appId: "AK7KMZKZHQ"
apiKey: "3151f502c7b9e9dafd5e6372b691a24e"
indexName: "bootstrap"
download:
source: "https://github.com/twbs/bootstrap/archive/v5.3.3.zip"
dist: "https://github.com/twbs/bootstrap/releases/download/v5.3.3/bootstrap-5.3.3-dist.zip"
+1 -1
View File
@@ -23,7 +23,7 @@ export default {
// can be removed later when multiple key/instances are fine to be used
if (!instanceMap.has(key) && instanceMap.size !== 0) {
// eslint-disable-next-line no-console
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${[...instanceMap.keys()][0]}.`)
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
return
}
+2 -2
View File
@@ -34,7 +34,7 @@ const getSelector = element => {
const SelectorEngine = {
find(selector, element = document.documentElement) {
return [...Element.prototype.querySelectorAll.call(element, selector)]
return [].concat(...Element.prototype.querySelectorAll.call(element, selector))
},
findOne(selector, element = document.documentElement) {
@@ -42,7 +42,7 @@ const SelectorEngine = {
},
children(element, selector) {
return [...element.children].filter(child => child.matches(selector))
return [].concat(...element.children).filter(child => child.matches(selector))
},
parents(element, selector) {
+2 -4
View File
@@ -143,8 +143,7 @@ class Dropdown extends BaseComponent {
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
const children = [...document.body.children]
for (const element of children) {
for (const element of [].concat(...document.body.children)) {
EventHandler.on(element, 'mouseover', noop)
}
}
@@ -194,8 +193,7 @@ class Dropdown extends BaseComponent {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
const children = [...document.body.children]
for (const element of children) {
for (const element of [].concat(...document.body.children)) {
EventHandler.off(element, 'mouseover', noop)
}
}
+2 -4
View File
@@ -222,8 +222,7 @@ class Tooltip extends BaseComponent {
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
const children = [...document.body.children]
for (const element of children) {
for (const element of [].concat(...document.body.children)) {
EventHandler.on(element, 'mouseover', noop)
}
}
@@ -257,8 +256,7 @@ class Tooltip extends BaseComponent {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
const children = [...document.body.children]
for (const element of children) {
for (const element of [].concat(...document.body.children)) {
EventHandler.off(element, 'mouseover', noop)
}
}
+1 -1
View File
@@ -170,7 +170,7 @@ const noop = () => {}
* @param {HTMLElement} element
* @return void
*
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
* @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
element.offsetHeight // eslint-disable-line no-unused-expressions
+3 -3
View File
@@ -93,7 +93,7 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
const domParser = new window.DOMParser()
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
const elements = [...createdDocument.body.querySelectorAll('*')]
const elements = [].concat(...createdDocument.body.querySelectorAll('*'))
for (const element of elements) {
const elementName = element.nodeName.toLowerCase()
@@ -103,8 +103,8 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
continue
}
const attributeList = [...element.attributes]
const allowedAttributes = [...allowList['*'] || [], ...allowList[elementName] || []]
const attributeList = [].concat(...element.attributes)
const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])
for (const attribute of attributeList) {
if (!allowedAttribute(attribute, allowedAttributes)) {
+3 -3
View File
@@ -1,7 +1,7 @@
/* eslint-disable import/no-unassigned-import */
/* eslint-disable import/extensions, import/no-unassigned-import */
import Tooltip from '../../dist/tooltip.js'
import '../../dist/carousel.js'
import Tooltip from '../../dist/tooltip'
import '../../dist/carousel'
window.addEventListener('load', () => {
[].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
+1 -1
View File
@@ -1,6 +1,6 @@
import { Tooltip } from '../../../dist/js/bootstrap.esm.js'
window.addEventListener('load', () => {
[...document.querySelectorAll('[data-bs-toggle="tooltip"]')]
[].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
.map(tooltipNode => new Tooltip(tooltipNode))
})
+1 -1
View File
@@ -153,7 +153,7 @@ describe('Collapse', () => {
const collapseEl1 = fixtureEl.querySelector('#collapse1')
const collapseEl2 = fixtureEl.querySelector('#collapse2')
const collapseList = [...fixtureEl.querySelectorAll('.collapse')]
const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
.map(el => new Collapse(el, {
parent,
toggle: false
+5 -5
View File
@@ -68,7 +68,7 @@ describe('SelectorEngine', () => {
].join('')
const list = fixtureEl.querySelector('ul')
const liList = [...fixtureEl.querySelectorAll('li')]
const liList = [].concat(...fixtureEl.querySelectorAll('li'))
const result = SelectorEngine.children(list, 'li')
expect(result).toEqual(liList)
@@ -356,7 +356,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should get elements if several ids are given', () => {
@@ -368,7 +368,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should get elements if several ids with special chars are given', () => {
@@ -380,7 +380,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should get elements in array, from href if no data-bs-target set', () => {
@@ -392,7 +392,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should return empty array if elements not found', () => {
+2 -6
View File
@@ -217,13 +217,9 @@
}
}
for (const popoverEl of document.querySelectorAll('[data-bs-toggle="popover"]')) {
new bootstrap.Popover(popoverEl)
}
document.querySelectorAll('[data-bs-toggle="popover"]').forEach(popoverEl => new bootstrap.Popover(popoverEl))
for (const tooltipEl of document.querySelectorAll('[data-bs-toggle="tooltip"]')) {
new bootstrap.Tooltip(tooltipEl)
}
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl => new bootstrap.Tooltip(tooltipEl))
const tall = document.getElementById('tall')
document.getElementById('tall-toggle').addEventListener('click', () => {
+1 -3
View File
@@ -35,9 +35,7 @@
<script>
/* global bootstrap: false */
for (const popoverEl of document.querySelectorAll('[data-bs-toggle="popover"]')) {
new bootstrap.Popover(popoverEl)
}
document.querySelectorAll('[data-bs-toggle="popover"]').forEach(popoverEl => new bootstrap.Popover(popoverEl))
</script>
</body>
</html>
+3 -9
View File
@@ -55,20 +55,14 @@
/* global bootstrap: false */
window.addEventListener('load', () => {
for (const toastEl of document.querySelectorAll('.toast')) {
new bootstrap.Toast(toastEl)
}
document.querySelectorAll('.toast').forEach(toastEl => new bootstrap.Toast(toastEl))
document.getElementById('btnShowToast').addEventListener('click', () => {
for (const toastEl of document.querySelectorAll('.toast')) {
bootstrap.Toast.getInstance(toastEl).show()
}
document.querySelectorAll('.toast').forEach(toastEl => bootstrap.Toast.getInstance(toastEl).show())
})
document.getElementById('btnHideToast').addEventListener('click', () => {
for (const toastEl of document.querySelectorAll('.toast')) {
bootstrap.Toast.getInstance(toastEl).hide()
}
document.querySelectorAll('.toast').forEach(toastEl => bootstrap.Toast.getInstance(toastEl).hide())
})
})
</script>
+1 -3
View File
@@ -113,9 +113,7 @@
})
targetTooltip.show()
for (const tooltipEl of document.querySelectorAll('[data-bs-toggle="tooltip"]')) {
new bootstrap.Tooltip(tooltipEl)
}
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl => new bootstrap.Tooltip(tooltipEl))
</script>
<script>
+795 -8781
View File
File diff suppressed because it is too large Load Diff
+9 -6
View File
@@ -82,7 +82,7 @@
"docs-serve": "hugo server --port 9001 --disableFastRender --noHTTPCache --renderToMemory --printPathWarnings --printUnusedTemplates",
"docs-serve-only": "npx sirv-cli _site --port 9001",
"lockfile-lint": "lockfile-lint --allowed-hosts npm --allowed-schemes https: --empty-hostname false --type npm --path package-lock.json",
"update-deps": "ncu -u -x globby,jasmine,karma-browserstack-launcher,karma-rollup-preprocessor && echo Manually update site/assets/js/vendor",
"update-deps": "ncu -u -x jasmine,karma-browserstack-launcher,karma-rollup-preprocessor",
"release": "npm-run-all dist release-sri docs-build release-zip*",
"release-sri": "node build/generate-sri.mjs",
"release-version": "node build/change-version.mjs",
@@ -106,25 +106,28 @@
"@babel/cli": "^7.23.9",
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@docsearch/js": "^3.6.0",
"@popperjs/core": "^2.11.8",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.5",
"@stackblitz/sdk": "^1.9.0",
"autoprefixer": "^10.4.18",
"bundlewatch": "^0.3.3",
"clean-css-cli": "^5.6.3",
"clipboard": "^2.0.11",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-xo": "^0.44.0",
"eslint-plugin-html": "^8.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-markdown": "^3.0.1",
"eslint-plugin-markdown": "^4.0.1",
"eslint-plugin-unicorn": "^51.0.1",
"find-unused-sass-variables": "^5.0.0",
"globby": "^11.1.0",
"globby": "^14.0.1",
"hammer-simulator": "0.0.1",
"hugo-bin": "^0.120.7",
"hugo-bin": "^0.120.8",
"ip": "^2.0.1",
"jasmine": "^5.1.0",
"jquery": "^3.7.1",
@@ -142,7 +145,7 @@
"npm-run-all2": "^6.1.2",
"postcss": "^8.4.35",
"postcss-cli": "^11.0.0",
"rollup": "^4.12.0",
"rollup": "^4.13.0",
"rollup-plugin-istanbul": "^5.0.0",
"rtlcss": "^4.1.1",
"sass": "^1.71.1",
@@ -150,7 +153,7 @@
"shelljs": "^0.8.5",
"stylelint": "^16.2.1",
"stylelint-config-twbs-bootstrap": "^14.0.0",
"terser": "^5.27.2",
"terser": "^5.29.1",
"vnu-jar": "23.4.11"
},
"files": [
+8 -18
View File
@@ -9,22 +9,12 @@
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
(() => {
'use strict'
/* eslint-disable import/no-unresolved */
import sidebarScroll from 'js/partials/sidebar.js'
import codeExamples from 'js/partials/code-examples.js'
import snippets from 'js/partials/snippets.js'
/* eslint-enable import/no-unresolved */
// Scroll the active sidebar link into view
const sidenav = document.querySelector('.bd-sidebar')
const sidenavActiveLink = document.querySelector('.bd-links-nav .active')
if (sidenav && sidenavActiveLink) {
const sidenavHeight = sidenav.clientHeight
const sidenavActiveLinkTop = sidenavActiveLink.offsetTop
const sidenavActiveLinkHeight = sidenavActiveLink.clientHeight
const viewportTop = sidenavActiveLinkTop
const viewportBottom = viewportTop - sidenavHeight + sidenavActiveLinkHeight
if (sidenav.scrollTop > viewportTop || sidenav.scrollTop < viewportBottom) {
sidenav.scrollTop = viewportTop - (sidenavHeight / 2) + (sidenavActiveLinkHeight / 2)
}
}
})()
sidebarScroll()
codeExamples()
snippets()
@@ -2,18 +2,18 @@
// IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++
/*!
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
/* global ClipboardJS: false, bootstrap: false */
/* global bootstrap: false */
(() => {
'use strict'
import ClipboardJS from 'clipboard'
export default () => {
// Insert copy to clipboard button before .highlight
const btnTitle = 'Copy to clipboard'
const btnEdit = 'Edit on StackBlitz'
@@ -29,13 +29,14 @@
].join('')
// Wrap programmatically code blocks and add copy btn.
for (const element of document.querySelectorAll('.highlight')) {
// Ignore examples made by shortcode
if (!element.closest('.bd-example-snippet')) {
element.insertAdjacentHTML('beforebegin', btnHtml)
element.previousElementSibling.append(element)
}
}
document.querySelectorAll('.highlight')
.forEach(element => {
// Ignore examples made by shortcode
if (!element.closest('.bd-example-snippet')) {
element.insertAdjacentHTML('beforebegin', btnHtml)
element.previousElementSibling.append(element)
}
})
/**
*
@@ -43,9 +44,9 @@
* @param {string} title
*/
function snippetButtonTooltip(selector, title) {
for (const btn of document.querySelectorAll(selector)) {
document.querySelectorAll(selector).forEach(btn => {
bootstrap.Tooltip.getOrCreateInstance(btn, { title })
}
})
}
snippetButtonTooltip('.btn-clipboard', btnTitle)
@@ -86,4 +87,4 @@
tooltipBtn.setContent({ '.tooltip-inner': btnTitle })
}, { once: true })
})
})()
}
+30
View File
@@ -0,0 +1,30 @@
// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
// IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
export default () => {
// Scroll the active sidebar link into view
const sidenav = document.querySelector('.bd-sidebar')
const sidenavActiveLink = document.querySelector('.bd-links-nav .active')
if (!sidenav || !sidenavActiveLink) {
return
}
const sidenavHeight = sidenav.clientHeight
const sidenavActiveLinkTop = sidenavActiveLink.offsetTop
const sidenavActiveLinkHeight = sidenavActiveLink.clientHeight
const viewportTop = sidenavActiveLinkTop
const viewportBottom = viewportTop - sidenavHeight + sidenavActiveLinkHeight
if (sidenav.scrollTop > viewportTop || sidenav.scrollTop < viewportBottom) {
sidenav.scrollTop = viewportTop - (sidenavHeight / 2) + (sidenavActiveLinkHeight / 2)
}
}
+168
View File
@@ -0,0 +1,168 @@
// NOTICE!!! Initially embedded in our docs this JavaScript
// file contains elements that can help you create reproducible
// use cases in StackBlitz for instance.
// In a real project please adapt this content to your needs.
// ++++++++++++++++++++++++++++++++++++++++++
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
/* global bootstrap: false */
export default () => {
// --------
// Tooltips
// --------
// Instantiate all tooltips in a docs or StackBlitz
document.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach(tooltip => {
new bootstrap.Tooltip(tooltip)
})
// --------
// Popovers
// --------
// Instantiate all popovers in docs or StackBlitz
document.querySelectorAll('[data-bs-toggle="popover"]')
.forEach(popover => {
new bootstrap.Popover(popover)
})
// -------------------------------
// Toasts
// -------------------------------
// Used by 'Placement' example in docs or StackBlitz
const toastPlacement = document.getElementById('toastPlacement')
if (toastPlacement) {
document.getElementById('selectToastPlacement').addEventListener('change', function () {
if (!toastPlacement.dataset.originalClass) {
toastPlacement.dataset.originalClass = toastPlacement.className
}
toastPlacement.className = `${toastPlacement.dataset.originalClass} ${this.value}`
})
}
// Instantiate all toasts in docs pages only
document.querySelectorAll('.bd-example .toast')
.forEach(toastNode => {
const toast = new bootstrap.Toast(toastNode, {
autohide: false
})
toast.show()
})
// Instantiate all toasts in docs pages only
// js-docs-start live-toast
const toastTrigger = document.getElementById('liveToastBtn')
const toastLiveExample = document.getElementById('liveToast')
if (toastTrigger) {
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
toastTrigger.addEventListener('click', () => {
toastBootstrap.show()
})
}
// js-docs-end live-toast
// -------------------------------
// Alerts
// -------------------------------
// Used in 'Show live alert' example in docs or StackBlitz
// js-docs-start live-alert
const alertPlaceholder = document.getElementById('liveAlertPlaceholder')
const appendAlert = (message, type) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = [
`<div class="alert alert-${type} alert-dismissible" role="alert">`,
` <div>${message}</div>`,
' <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>',
'</div>'
].join('')
alertPlaceholder.append(wrapper)
}
const alertTrigger = document.getElementById('liveAlertBtn')
if (alertTrigger) {
alertTrigger.addEventListener('click', () => {
appendAlert('Nice, you triggered this alert message!', 'success')
})
}
// js-docs-end live-alert
// --------
// Carousels
// --------
// Instantiate all non-autoplaying carousels in docs or StackBlitz
document.querySelectorAll('.carousel:not([data-bs-ride="carousel"])')
.forEach(carousel => {
bootstrap.Carousel.getOrCreateInstance(carousel)
})
// -------------------------------
// Checks & Radios
// -------------------------------
// Indeterminate checkbox example in docs and StackBlitz
document.querySelectorAll('.bd-example-indeterminate [type="checkbox"]')
.forEach(checkbox => {
if (checkbox.id.includes('Indeterminate')) {
checkbox.indeterminate = true
}
})
// -------------------------------
// Links
// -------------------------------
// Disable empty links in docs examples only
document.querySelectorAll('.bd-content [href="#"]')
.forEach(link => {
link.addEventListener('click', event => {
event.preventDefault()
})
})
// -------------------------------
// Modal
// -------------------------------
// Modal 'Varying modal content' example in docs and StackBlitz
// js-docs-start varying-modal-content
const exampleModal = document.getElementById('exampleModal')
if (exampleModal) {
exampleModal.addEventListener('show.bs.modal', event => {
// Button that triggered the modal
const button = event.relatedTarget
// Extract info from data-bs-* attributes
const recipient = button.getAttribute('data-bs-whatever')
// If necessary, you could initiate an Ajax request here
// and then do the updating in a callback.
// Update the modal's content.
const modalTitle = exampleModal.querySelector('.modal-title')
const modalBodyInput = exampleModal.querySelector('.modal-body input')
modalTitle.textContent = `New message to ${recipient}`
modalBodyInput.value = recipient
})
}
// js-docs-end varying-modal-content
// -------------------------------
// Offcanvas
// -------------------------------
// 'Offcanvas components' example in docs only
const myOffcanvas = document.querySelectorAll('.bd-example-offcanvas .offcanvas')
if (myOffcanvas) {
myOffcanvas.forEach(offcanvas => {
offcanvas.addEventListener('show.bs.offcanvas', event => {
event.preventDefault()
}, false)
})
}
}
+17 -7
View File
@@ -2,21 +2,31 @@
// IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++
(() => {
'use strict'
/*!
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
import docsearch from '@docsearch/js'
// https://gohugo.io/hugo-pipes/js/#options
// eslint-disable-next-line import/no-unresolved
import { appId, apiKey, indexName } from '@params';
(() => {
const searchElement = document.getElementById('docsearch')
if (!window.docsearch || !searchElement) {
if (!searchElement) {
return
}
const siteDocsVersion = searchElement.getAttribute('data-bd-docs-version')
window.docsearch({
apiKey: '3151f502c7b9e9dafd5e6372b691a24e',
indexName: 'bootstrap',
appId: 'AK7KMZKZHQ',
docsearch({
apiKey,
indexName,
appId,
container: searchElement,
searchParameters: {
facetFilters: [`version:${siteDocsVersion}`]
+6 -154
View File
@@ -1,7 +1,5 @@
// NOTICE!!! Initially embedded in our docs this JavaScript
// file contains elements that can help you create reproducible
// use cases in StackBlitz for instance.
// In a real project please adapt this content to your needs.
// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
// IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++
/*!
@@ -11,154 +9,8 @@
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
/* global bootstrap: false */
/* eslint-disable import/no-unresolved */
import snippets from 'js/partials/snippets.js'
/* eslint-enable import/no-unresolved */
(() => {
'use strict'
// --------
// Tooltips
// --------
// Instantiate all tooltips in a docs or StackBlitz
for (const tooltip of document.querySelectorAll('[data-bs-toggle="tooltip"]')) {
new bootstrap.Tooltip(tooltip)
}
// --------
// Popovers
// --------
// Instantiate all popovers in docs or StackBlitz
for (const popover of document.querySelectorAll('[data-bs-toggle="popover"]')) {
new bootstrap.Popover(popover)
}
// -------------------------------
// Toasts
// -------------------------------
// Used by 'Placement' example in docs or StackBlitz
const toastPlacement = document.getElementById('toastPlacement')
if (toastPlacement) {
document.getElementById('selectToastPlacement').addEventListener('change', function () {
if (!toastPlacement.dataset.originalClass) {
toastPlacement.dataset.originalClass = toastPlacement.className
}
toastPlacement.className = `${toastPlacement.dataset.originalClass} ${this.value}`
})
}
// Instantiate all toasts in docs pages only
for (const toastEl of document.querySelectorAll('.bd-example .toast')) {
const toast = new bootstrap.Toast(toastEl, {
autohide: false
})
toast.show()
}
// Instantiate all toasts in docs pages only
// js-docs-start live-toast
const toastTrigger = document.getElementById('liveToastBtn')
const toastLiveExample = document.getElementById('liveToast')
if (toastTrigger) {
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
toastTrigger.addEventListener('click', () => {
toastBootstrap.show()
})
}
// js-docs-end live-toast
// -------------------------------
// Alerts
// -------------------------------
// Used in 'Show live alert' example in docs or StackBlitz
// js-docs-start live-alert
const alertPlaceholder = document.getElementById('liveAlertPlaceholder')
const appendAlert = (message, type) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = [
`<div class="alert alert-${type} alert-dismissible" role="alert">`,
` <div>${message}</div>`,
' <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>',
'</div>'
].join('')
alertPlaceholder.append(wrapper)
}
const alertTrigger = document.getElementById('liveAlertBtn')
if (alertTrigger) {
alertTrigger.addEventListener('click', () => {
appendAlert('Nice, you triggered this alert message!', 'success')
})
}
// js-docs-end live-alert
// --------
// Carousels
// --------
// Instantiate all non-autoplaying carousels in docs or StackBlitz
for (const carousel of document.querySelectorAll('.carousel:not([data-bs-ride="carousel"])')) {
bootstrap.Carousel.getOrCreateInstance(carousel)
}
// -------------------------------
// Checks & Radios
// -------------------------------
// Indeterminate checkbox example in docs and StackBlitz
for (const checkbox of document.querySelectorAll('.bd-example-indeterminate [type="checkbox"]')) {
if (checkbox.id.includes('Indeterminate')) {
checkbox.indeterminate = true
}
}
// -------------------------------
// Links
// -------------------------------
// Disable empty links in docs examples only
for (const link of document.querySelectorAll('.bd-content [href="#"]')) {
link.addEventListener('click', event => {
event.preventDefault()
})
}
// -------------------------------
// Modal
// -------------------------------
// Modal 'Varying modal content' example in docs and StackBlitz
// js-docs-start varying-modal-content
const exampleModal = document.getElementById('exampleModal')
if (exampleModal) {
exampleModal.addEventListener('show.bs.modal', event => {
// Button that triggered the modal
const button = event.relatedTarget
// Extract info from data-bs-* attributes
const recipient = button.getAttribute('data-bs-whatever')
// If necessary, you could initiate an Ajax request here
// and then do the updating in a callback.
// Update the modal's content.
const modalTitle = exampleModal.querySelector('.modal-title')
const modalBodyInput = exampleModal.querySelector('.modal-body input')
modalTitle.textContent = `New message to ${recipient}`
modalBodyInput.value = recipient
})
}
// js-docs-end varying-modal-content
// -------------------------------
// Offcanvas
// -------------------------------
// 'Offcanvas components' example in docs only
const myOffcanvas = document.querySelectorAll('.bd-example-offcanvas .offcanvas')
if (myOffcanvas) {
for (const offcanvas of myOffcanvas) {
offcanvas.addEventListener('show.bs.offcanvas', event => {
event.preventDefault()
}, false)
}
}
})()
snippets()
+67
View File
@@ -0,0 +1,67 @@
// NOTICE!!! Initially embedded in our docs this JavaScript
// file contains elements that can help you create reproducible
// use cases in StackBlitz for instance.
// In a real project please adapt this content to your needs.
// ++++++++++++++++++++++++++++++++++++++++++
/*!
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
import sdk from '@stackblitz/sdk'
// https://gohugo.io/hugo-pipes/js/#options
import {
cssCdn, cssCdnHash, docsCss, jsBundleCdn, jsBundleCdnHash, jsSnippetFile, isNetlify
} from '@params' // eslint-disable-line import/no-unresolved
// Open in StackBlitz logic
document.querySelectorAll('.btn-edit').forEach(btn => {
btn.addEventListener('click', event => {
const codeSnippet = event.target.closest('.bd-code-snippet')
const exampleEl = codeSnippet.querySelector('.bd-example')
const htmlSnippet = exampleEl.innerHTML
const jsSnippet = codeSnippet.querySelector('.btn-edit').getAttribute('data-sb-js-snippet')
// Get extra classes for this example
const classes = Array.from(exampleEl.classList).join(' ')
openBootstrapSnippet(htmlSnippet, jsSnippet, classes)
})
})
const openBootstrapSnippet = (htmlSnippet, jsSnippet, classes) => {
const indexHtml = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="${cssCdn}" rel="stylesheet"${!isNetlify ? `integrity="${cssCdnHash}" crossorigin="anonymous"` : ''}>
<link href="docs.css" rel="stylesheet">
<title>Bootstrap Example</title>
<${'script'} defer src="${jsBundleCdn}"${!isNetlify ? `integrity="${jsBundleCdnHash}" crossorigin="anonymous"` : ''}></${'script'}>
</head>
<body class="p-3 m-0 border-0 ${classes}">
<!-- Example Code Start-->
${htmlSnippet.trimStart().replace(/^/gm, ' ').replace(/^ {4}$/gm, '').trimEnd()}
<!-- Example Code End -->
</body>
</html>
`
const project = {
files: {
'index.html': indexHtml,
...(jsSnippet && { 'index.js': jsSnippetFile }),
'docs.css': docsCss
},
title: 'Bootstrap Example',
description: `Official example from ${window.location.href}`,
template: jsSnippet ? 'javascript' : 'html',
tags: ['bootstrap']
}
sdk.openProject(project, { openFile: 'index.html' })
}
File diff suppressed because one or more lines are too long
-1
View File
@@ -36,7 +36,6 @@ $enable-cssgrid: true;
// Load docs components
@import "variables";
@import "navbar";
@import "search";
@import "masthead";
@import "ads";
@import "content";
+14
View File
@@ -0,0 +1,14 @@
/*!
* Bootstrap Docs (https://getbootstrap.com/)
* Copyright 2024 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
@import "../../../scss/functions";
@import "../../../scss/variables";
@import "../../../scss/mixins";
@import "variables";
@import "@docsearch/css/dist/style";
@import "search";
+3 -3
View File
@@ -38,7 +38,7 @@ Click the button below to show an alert (hidden with inline styles to start), th
We use the following JavaScript to trigger our live alert demo:
{{< js-docs name="live-alert" file="site/assets/js/snippets.js" >}}
{{< js-docs name="live-alert" file="site/assets/js/partials/snippets.js" >}}
### Link color
@@ -215,8 +215,8 @@ This makes an alert listen for click events on descendant elements which have th
Basic usage:
```js
const myAlert = bootstrap.Alert.getOrCreateInstance('#myAlert')
myAlert.close()
const alert = bootstrap.Alert.getOrCreateInstance('#myAlert')
alert.close()
```
### Events
+2 -2
View File
@@ -223,10 +223,10 @@ const bsButton = new bootstrap.Button('#myButton')
For example, to toggle all buttons
```js
for (const buttonElement of document.querySelectorAll('.btn')) {
document.querySelectorAll('.btn').forEach(buttonElement => {
const button = bootstrap.Button.getOrCreateInstance(buttonElement)
button.toggle()
}
})
```
## CSS
@@ -413,14 +413,14 @@ Enable tabbable list item via JavaScript (each list item needs to be activated i
```js
const triggerTabList = document.querySelectorAll('#myTab a')
for (const triggerEl of triggerTabList) {
triggerTabList.forEach(triggerEl => {
const tabTrigger = new bootstrap.Tab(triggerEl)
triggerEl.addEventListener('click', event => {
event.preventDefault()
tabTrigger.show()
})
}
})
```
You can activate individual list item in several ways:
@@ -491,10 +491,10 @@ If no tab was already active, then the `hide.bs.tab` and `hidden.bs.tab` events
```js
const tabElms = document.querySelectorAll('a[data-bs-toggle="list"]')
for (const tabElm of tabElms) {
tabElms.forEach(tabElm => {
tabElm.addEventListener('shown.bs.tab', event => {
event.target // newly activated tab
event.relatedTarget // previous active tab
})
}
})
```
+1 -1
View File
@@ -481,7 +481,7 @@ Below is a live demo followed by example HTML and JavaScript. For more informati
</div>
{{< /example >}}
{{< js-docs name="varying-modal-content" file="site/assets/js/snippets.js" >}}
{{< js-docs name="varying-modal-content" file="site/assets/js/partials/snippets.js" >}}
### Toggle between modals
@@ -613,14 +613,14 @@ Enable tabbable tabs via JavaScript (each tab needs to be activated individually
```js
const triggerTabList = document.querySelectorAll('#myTab button')
for (const triggerEl of triggerTabList) {
triggerTabList.forEach(triggerEl => {
const tabTrigger = new bootstrap.Tab(triggerEl)
triggerEl.addEventListener('click', event => {
event.preventDefault()
tabTrigger.show()
})
}
})
```
You can activate individual tabs in several ways:
@@ -333,13 +333,13 @@ Scrollspy is not limited to nav components and list groups, so it will work on a
Target elements that arent visible will be ignored and their corresponding nav items won't receive an `.active` class. Scrollspy instances initialized in a non-visible wrapper will ignore all target elements. Use the `refresh` method to check for observable elements once the wrapper becomes visible.
```js
for (const el of document.querySelectorAll('#nav-tab > [data-bs-toggle="tab"]')) {
document.querySelectorAll('#nav-tab>[data-bs-toggle="tab"]').forEach(el => {
el.addEventListener('shown.bs.tab', () => {
const target = el.getAttribute('data-bs-target')
const scrollElem = document.querySelector(`${target} [data-bs-spy="scroll"]`)
bootstrap.ScrollSpy.getOrCreateInstance(scrollElem).refresh()
})
}
})
```
## Usage
@@ -406,9 +406,9 @@ Here's an example using the refresh method:
```js
const dataSpyList = document.querySelectorAll('[data-bs-spy="scroll"]')
for (const dataSpyEl of dataSpyList) {
dataSpyList.forEach(dataSpyEl => {
bootstrap.ScrollSpy.getInstance(dataSpyEl).refresh()
}
})
```
### Events
+1 -1
View File
@@ -87,7 +87,7 @@ Click the button below to show a toast (positioned with our utilities in the low
We use the following JavaScript to trigger our live toast demo:
{{< js-docs name="live-toast" file="site/assets/js/snippets.js" >}}
{{< js-docs name="live-toast" file="site/assets/js/partials/snippets.js" >}}
### Translucent
+1 -1
View File
@@ -46,4 +46,4 @@ sitemap_exclude: true
{{< scss-docs name="variable-gradient" file="scss/_variables.scss" >}}
{{< js-docs name="live-toast" file="site/assets/js/snippets.js" >}}
{{< js-docs name="live-toast" file="site/assets/js/partials/snippets.js" >}}
+18 -12
View File
@@ -21,12 +21,12 @@ aliases: "/examples/"
{{ range $i, $example := $entry.examples -}}
{{- $len := len $entry.examples -}}
{{ if (eq $i 0) }}<div class="row">{{ end }}
{{ if $entry.external }}
{{ if $entry.external -}}
<div class="col-md-6 col-lg-4 mb-3 d-flex gap-3">
<svg class="bi fs-5 flex-shrink-0 mt-1"><use xlink:href="#box-seam"></use></svg>
<div>
<h3 class="h5 mb-1">
<a class="d-block link-offset-1" href="{{ $.Site.Params.github_org }}{{ $example.url }}/" target="_blank" rel="noopener">
<a class="d-block link-offset-1" href="{{ urls.JoinPath $.Site.Params.github_org $example.url }}" target="_blank" rel="noopener">
{{ $example.name }}
</a>
</h3>
@@ -43,23 +43,29 @@ aliases: "/examples/"
</p>
</div>
</div>
{{ else }}
{{ else -}}
<div class="col-sm-6 col-md-3 mb-3">
<a class="d-block link-offset-1" href="/docs/{{ $.Site.Params.docs_version }}/examples/{{ $example.name | urlize }}/"{{ if in $example.name "RTL" }} hreflang="ar"{{ end }}>
<img class="img-thumbnail mb-3" srcset="/docs/{{ $.Site.Params.docs_version }}/assets/img/examples/{{ $example.name | urlize }}.png,
/docs/{{ $.Site.Params.docs_version }}/assets/img/examples/{{ $example.name | urlize }}@2x.png 2x"
src="/docs/{{ $.Site.Params.docs_version }}/assets/img/examples/{{ $example.name | urlize }}.png"
alt=""
width="480" height="300"
loading="lazy">
<a class="d-block link-offset-1" href="{{ urls.JoinPath "/docs" $.Site.Params.docs_version "/examples" ($example.name | urlize) "/"}}"{{ if in $example.name "RTL" }} hreflang="ar"{{ end }}>
{{ $imageBasePath := urls.JoinPath "/docs" $.Site.Params.docs_version "assets/img/examples" -}}
{{- $imgPath := urls.JoinPath $imageBasePath (printf "%s%s" ($example.name | urlize) ".png") -}}
{{- $imgPath2x := urls.JoinPath $imageBasePath (printf "%s%s" ($example.name | urlize) "@2x.png") -}}
{{- with (imageConfig (path.Join "/site/static" $imgPath)) -}}
<img class="img-thumbnail mb-3"
srcset="{{ $imgPath }}, {{ $imgPath2x }} 2x"
src="{{ $imgPath }}"
alt=""
width="{{ .Width }}"
height="{{ .Height }}"
loading="lazy">
{{- end }}
<h3 class="h5 mb-1">
{{ $example.name }}
</h3>
</a>
<p class="text-body-secondary">{{ $example.description }}</p>
</div>
{{ end }}
{{ if (eq (add $i 1) $len) }}</div>{{ end }}
{{- end }}
{{ if (eq (add $i 1) $len) }}</div>{{ end -}}
{{ end -}}
</div>
{{ end -}}
@@ -5,6 +5,7 @@ extra_css:
- "../cheatsheet/cheatsheet.rtl.css"
extra_js:
- src: "../cheatsheet/cheatsheet.js"
defer: true
body_class: "bg-body-tertiary"
direction: rtl
---
@@ -4,30 +4,34 @@
'use strict'
// Tooltip and popover demos
for (const tooltip of document.querySelectorAll('.tooltip-demo')) {
new bootstrap.Tooltip(tooltip, {
selector: '[data-bs-toggle="tooltip"]'
})
}
for (const popover of document.querySelectorAll('[data-bs-toggle="popover"]')) {
new bootstrap.Popover(popover)
}
for (const toastEl of document.querySelectorAll('.toast')) {
const toast = new bootstrap.Toast(toastEl, {
autohide: false
document.querySelectorAll('.tooltip-demo')
.forEach(tooltip => {
new bootstrap.Tooltip(tooltip, {
selector: '[data-bs-toggle="tooltip"]'
})
})
toast.show()
}
document.querySelectorAll('[data-bs-toggle="popover"]')
.forEach(popover => {
new bootstrap.Popover(popover)
})
document.querySelectorAll('.toast')
.forEach(toastNode => {
const toast = new bootstrap.Toast(toastNode, {
autohide: false
})
toast.show()
})
// Disable empty links and submit buttons
for (const link of document.querySelectorAll('[href="#"], [type="submit"]')) {
link.addEventListener('click', event => {
event.preventDefault()
document.querySelectorAll('[href="#"], [type="submit"]')
.forEach(link => {
link.addEventListener('click', event => {
event.preventDefault()
})
})
}
function setActiveItem() {
const { hash } = window.location
@@ -5,6 +5,7 @@ extra_css:
- "cheatsheet.css"
extra_js:
- src: "cheatsheet.js"
defer: true
body_class: "bg-body-tertiary"
---
@@ -6,6 +6,7 @@ extra_css:
- "../checkout/checkout.css"
extra_js:
- src: "../checkout/checkout.js"
defer: true
body_class: "bg-body-tertiary"
---
@@ -6,7 +6,7 @@
const forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
for (const form of forms) {
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
@@ -15,5 +15,5 @@
form.classList.add('was-validated')
}, false)
}
})
})()
@@ -5,6 +5,7 @@ extra_css:
- "checkout.css"
extra_js:
- src: "checkout.js"
defer: true
body_class: "bg-body-tertiary"
---
@@ -7,7 +7,9 @@ extra_css:
extra_js:
- src: "https://cdn.jsdelivr.net/npm/chart.js@4.3.2/dist/chart.umd.js"
integrity: "sha384-eI7PSr3L1XLISH8JdDII5YN/njoSsxfbrkCTnJrzXt+ENP5MOVBxD+l6sEG4zoLp"
defer: true
- src: "dashboard.js"
defer: true
---
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
@@ -6,7 +6,9 @@ extra_css:
extra_js:
- src: "https://cdn.jsdelivr.net/npm/chart.js@4.3.2/dist/chart.umd.js"
integrity: "sha384-eI7PSr3L1XLISH8JdDII5YN/njoSsxfbrkCTnJrzXt+ENP5MOVBxD+l6sEG4zoLp"
defer: true
- src: "dashboard.js"
defer: true
---
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
@@ -5,6 +5,7 @@ extra_css:
- "offcanvas-navbar.css"
extra_js:
- src: "offcanvas-navbar.js"
defer: true
body_class: "bg-body-tertiary"
aliases: "/docs/5.3/examples/offcanvas/"
---
@@ -5,6 +5,7 @@ extra_css:
- "sidebars.css"
extra_js:
- src: "sidebars.js"
defer: true
body_class: ""
---
@@ -1,8 +1,8 @@
/* global bootstrap: false */
(() => {
'use strict'
const tooltipTriggerList = [...document.querySelectorAll('[data-bs-toggle="tooltip"]')]
for (const tooltipTriggerEl of tooltipTriggerList) {
const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
tooltipTriggerList.forEach(tooltipTriggerEl => {
new bootstrap.Tooltip(tooltipTriggerEl)
}
})
})()
@@ -39,9 +39,8 @@ We provide a version of Bootstrap built as `ESM` (`bootstrap.esm.js` and `bootst
<script type="module">
import { Toast } from 'bootstrap.esm.min.js'
for (const toastEl of document.querySelectorAll('.toast')) {
new Toast(toastEl))
}
Array.from(document.querySelectorAll('.toast'))
.forEach(toastNode => new Toast(toastNode))
</script>
```
@@ -160,7 +159,7 @@ In addition to the `getInstance` and `getOrCreateInstance` methods, all plugin c
const modal = new bootstrap.Modal('#myModal')
const dropdown = new bootstrap.Dropdown('[data-bs-toggle="dropdown"]')
const offcanvas = bootstrap.Offcanvas.getInstance('#myOffcanvas')
const myAlert = bootstrap.Alert.getOrCreateInstance('#myAlert')
const alert = bootstrap.Alert.getOrCreateInstance('#myAlert')
```
### Asynchronous functions and transitions
@@ -79,7 +79,7 @@ With dependencies installed and our project folder ready for us to start coding,
```js
'use strict'
const path = require('node:path')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
@@ -154,7 +154,7 @@ Importing Bootstrap into Webpack requires the loaders we installed in the first
```js
'use strict'
const path = require('node:path')
const path = require('path')
const autoprefixer = require('autoprefixer')
const HtmlWebpackPlugin = require('html-webpack-plugin')
+1 -1
View File
@@ -58,7 +58,7 @@ Note that [breaking words isn't possible in Arabic](https://rtlstyling.com/posts
## Text transform
Transform text in components with text capitalization classes.
Transform text in components with our text capitalization classes: `text-lowercase`, `text-uppercase` or `text-capitalize`.
{{< example >}}
<p class="text-lowercase">Lowercased text.</p>
+1 -1
View File
@@ -67,7 +67,7 @@
{{ end }}
{{ define "footer" }}
{{ range .Page.Params.extra_js -}}
<script{{ with .async }} async{{ end }} src="{{ .src }}"></script>
<script{{ with .async }} async{{ end }}{{ with .defer }} defer{{ end }} src="{{ .src }}"></script>
{{- end -}}
<div class="position-fixed" aria-hidden="true"><input type="text" tabindex="-1"></div>
{{ end }}
+3 -3
View File
@@ -159,13 +159,13 @@
{{ .Content }}
{{- if hugo.IsProduction -}}
<script src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.min.js" {{ printf "integrity=%q" .Site.Params.cdn.js_bundle_hash | safeHTMLAttr }}></script>
<script defer src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.min.js" {{ printf "integrity=%q" .Site.Params.cdn.js_bundle_hash | safeHTMLAttr }}></script>
{{- else -}}
<script src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.js"></script>
<script defer src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.js"></script>
{{- end }}
{{ range .Page.Params.extra_js -}}
<script{{ with .async }} async{{ end }} src="{{ .src }}"{{ with .integrity }} {{ printf "integrity=%q" . | safeHTMLAttr }} crossorigin="anonymous"{{ end }}></script>
<script{{ with .async }} async{{ end }}{{ with .defer }} defer{{ end }} src="{{ .src }}"{{ with .integrity }} {{ printf "integrity=%q" . | safeHTMLAttr }} crossorigin="anonymous"{{ end }}></script>
{{- end -}}
</body>
</html>
+1 -1
View File
@@ -1 +1 @@
<script defer src="https://cdn.usefathom.com/script.js" data-site="ITUSEYJG"></script>
<script defer src="https://cdn.usefathom.com/script.js" data-site="{{ .Site.Params.analytics.fathom_site }}"></script>
+5 -3
View File
@@ -7,11 +7,13 @@
<meta name="docsearch:language" content="en">
<meta name="docsearch:version" content="{{ .Site.Params.docs_version }}">
<title>{{ if .IsHome }}{{ .Site.Title | markdownify }} · {{ .Site.Params.subtitle | markdownify }}{{ else }}{{ .Title | markdownify }} · {{ .Site.Title | markdownify }} v{{ .Site.Params.docs_version }}{{ end }}</title>
<link rel="canonical" href="{{ .Permalink }}">
<link rel="preconnect" href="https://AK7KMZKZHQ-dsn.algolia.net" crossorigin>
{{ if (ne .Page.Layout "examples") -}}
<link rel="preconnect" href="https://{{ .Site.Params.algolia.appId | lower }}-dsn.algolia.net" crossorigin>
{{- end }}
<title>{{ if .IsHome }}{{ .Site.Title | markdownify }} · {{ .Site.Params.subtitle | markdownify }}{{ else }}{{ .Title | markdownify }} · {{ .Site.Title | markdownify }} v{{ .Site.Params.docs_version }}{{ end }}</title>
{{ with .Params.robots -}}
<meta name="robots" content="{{ . }}">
+8 -3
View File
@@ -1,9 +1,14 @@
<div class="bd-masthead mb-3" id="content">
<div class="container-xxl bd-gutter">
<div class="col-md-8 mx-auto text-center">
<a class="d-flex flex-column flex-lg-row justify-content-center align-items-center mb-4 text-dark lh-sm text-decoration-none" href="https://blog.getbootstrap.com/">
<strong class="d-sm-inline-block p-2 me-2 mb-2 mb-lg-0 rounded-3 masthead-notice">New in v5.3</strong>
<span class="text-body-secondary">Color mode support, expanded color palette, and more!</span>
<a class="d-flex flex-column flex-lg-row justify-content-center align-items-center mb-4 text-dark lh-sm text-decoration-none" href="https://www.herodevs.com/support/nes-bootstrap?utm_source=Bootstrap_site&utm_medium=Banner&utm_campaign=v3and4_eol">
<span class="d-sm-inline-flex align-items-center gap-1 py-2 px-3 me-2 mb-2 mb-lg-0 rounded-5 masthead-notice">
<span class="fw-semibold">
New!
</span>
Never-Ending Support for Bootstrap
<svg class="bi" style="width: 20px; height: 20px;"><use xlink:href="#arrow-right-short"></use></svg>
</span>
</a>
<img class="d-none d-sm-block mx-auto mb-3"
srcset="/docs/{{ .Site.Params.docs_version }}/assets/brand/bootstrap-logo-shadow.png,
+3
View File
@@ -2,6 +2,9 @@
<symbol id="arrow-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
</symbol>
<symbol id="arrow-right-short" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8"/>
</symbol>
<symbol id="book-half" viewBox="0 0 16 16">
<path d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z"/>
</symbol>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

+36 -61
View File
@@ -1,73 +1,48 @@
{{ if hugo.IsProduction -}}
<script src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.min.js" {{ printf "integrity=%q" .Site.Params.cdn.js_bundle_hash | safeHTMLAttr }}></script>
<script defer src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.min.js" {{ printf "integrity=%q" .Site.Params.cdn.js_bundle_hash | safeHTMLAttr }}></script>
{{ else -}}
<script src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.js"></script>
<script defer src="/docs/{{ .Site.Params.docs_version }}/dist/js/bootstrap.bundle.js"></script>
{{- end }}
<script src="https://cdn.jsdelivr.net/npm/@docsearch/js@3"></script>
{{- $esbuildOptions := dict "target" "es2019" -}}
{{- $targetDocsJSPath := path.Join "/docs" .Site.Params.docs_version -}}
{{ if eq .Page.Layout "docs" -}}
<script src="https://cdn.jsdelivr.net/npm/@stackblitz/sdk@1/bundles/sdk.umd.js"></script>
{{- end }}
{{- $vendor := resources.Match "js/vendor/*.js" -}}
{{- $js := resources.Match "js/*.js" -}}
{{- $targetDocsJSPath := path.Join "/docs" .Site.Params.docs_version "assets/js/docs.js" -}}
{{- $docsJs := append $js $vendor | resources.Concat $targetDocsJSPath -}}
{{- $sassOptions := dict "outputStyle" "expanded" "precision" 6 -}}
{{- $postcssOptions := dict "use" "autoprefixer" "noMap" true -}}
{{- if hugo.IsProduction -}}
{{- $docsJs = $docsJs | resources.Minify -}}
{{- $esbuildOptions = merge $esbuildOptions (dict "minify" "true") -}}
{{- $sassOptions = merge $sassOptions (dict "outputStyle" "compressed") -}}
{{- end }}
<script src="{{ $docsJs.Permalink | relURL }}"></script>
{{- $applicationJs := resources.Get "js/application.js" | js.Build $esbuildOptions | resources.Copy (path.Join $targetDocsJSPath "/assets/js/application.js") }}
<script defer src="{{ $applicationJs.RelPermalink }}"></script>
{{- if (ne .Page.Layout "examples") -}}
{{- $esbuildParams := dict
"apiKey" .Site.Params.algolia.apiKey
"appId" .Site.Params.algolia.appId
"indexName" .Site.Params.algolia.indexName
-}}
{{- $esbuildOptions = merge $esbuildOptions (dict "params" $esbuildParams) -}}
{{- $searchJs := resources.Get "js/search.js" | js.Build $esbuildOptions | resources.Copy (path.Join $targetDocsJSPath "/assets/js/search.js") }}
<script defer src="{{ $searchJs.RelPermalink }}"></script>
{{- end -}}
{{ if eq .Page.Layout "docs" -}}
<script>
// Open in StackBlitz logic
document.querySelectorAll('.btn-edit').forEach(btn => {
btn.addEventListener('click', event => {
const htmlSnippet = event.target.closest('.bd-code-snippet').querySelector('.bd-example').innerHTML
{{- $docsCss := resources.Get "scss/docs.scss" | toCSS $sassOptions | postCSS $postcssOptions -}}
{{- $snippetsFile := resources.Get "js/snippets.js" | js.Build $esbuildOptions -}}
// Get extra classes for this example
const classes = Array.from(event.target.closest('.bd-code-snippet').querySelector('.bd-example').classList).join(' ')
const jsSnippet = event.target.closest('.bd-code-snippet').querySelector('.btn-edit').getAttribute('data-sb-js-snippet')
StackBlitzSDK.openBootstrapSnippet(htmlSnippet, jsSnippet, classes)
})
})
StackBlitzSDK.openBootstrapSnippet = (htmlSnippet, jsSnippet, classes) => {
const markup = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="{{ .Site.Params.cdn.css }}" rel="stylesheet">
<link href="https://getbootstrap.com/docs/{{ .Site.Params.docs_version }}/assets/css/docs.css" rel="stylesheet">
<title>Bootstrap Example</title>
<${'script'} src="{{ .Site.Params.cdn.js_bundle }}"></${'script'}>
</head>
<body class="p-3 m-0 border-0 ${classes}">
<!-- Example Code -->
${htmlSnippet.replace(/^/gm, ' ')}
<!-- End Example Code -->
</body>
</html>`
const jsSnippetContent = jsSnippet ? '{{ os.ReadFile "site/assets/js/snippets.js" }}' : null
const project = {
files: {
'index.html': markup,
'index.js': jsSnippetContent
},
title: 'Bootstrap Example',
description: `Official example from ${window.location.href}`,
template: jsSnippet ? 'javascript' : 'html',
tags: ['bootstrap']
}
StackBlitzSDK.openProject(project, { openFile: 'index.html' })
}
</script>
{{- end }}
{{- $esbuildParams := dict
"isNetlify" (eq (getenv "NETLIFY") "true")
"cssCdn" .Site.Params.cdn.css
"cssCdnHash" .Site.Params.cdn.css_hash
"docsCss" $docsCss.Content
"jsBundleCdn" .Site.Params.cdn.js_bundle
"jsBundleCdnHash" .Site.Params.cdn.js_bundle_hash
"jsSnippetFile" $snippetsFile.Content
-}}
{{- $esbuildOptions = merge $esbuildOptions (dict "params" $esbuildParams) -}}
{{- $stackblitzJs := resources.Get "js/stackblitz.js" | js.Build $esbuildOptions | resources.Copy (path.Join $targetDocsJSPath "/assets/js/stackblitz.js") }}
<script defer src="{{ $stackblitzJs.RelPermalink }}"></script>
{{- end -}}
+8 -6
View File
@@ -1,5 +1,3 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3">
{{ if hugo.IsProduction -}}
{{ if eq .Page.Params.direction "rtl" -}}
<link href="/docs/{{ .Site.Params.docs_version }}/dist/css/bootstrap.rtl.min.css" rel="stylesheet" {{ printf "integrity=%q" .Site.Params.cdn.css_rtl_hash | safeHTMLAttr }}>
@@ -11,15 +9,19 @@
{{- end }}
{{- if (ne .Page.Layout "examples") }}
{{- $targetDocsCssPath := path.Join "/docs" .Site.Params.docs_version "assets/css/docs.css" -}}
{{- $sassOptions := dict "targetPath" $targetDocsCssPath "outputStyle" "expanded" "precision" 6 -}}
{{- $sassOptions := dict "outputStyle" "expanded" "precision" 6 -}}
{{- $postcssOptions := dict "use" "autoprefixer" "noMap" true -}}
{{- $targetDocsCssPath := path.Join "/docs" .Site.Params.docs_version "assets/css/docs.css" -}}
{{- $targetSearchCssPath := path.Join "/docs" .Site.Params.docs_version "assets/css/search.css" -}}
{{ if hugo.IsProduction -}}
{{- $sassOptions = merge $sassOptions (dict "outputStyle" "compressed") -}}
{{- end -}}
{{- $style := resources.Get "scss/docs.scss" | toCSS $sassOptions | postCSS $postcssOptions }}
{{- $docsCss := resources.Get "scss/docs.scss" | toCSS (merge (dict "targetPath" $targetDocsCssPath) $sassOptions) | postCSS $postcssOptions -}}
{{- $searchCss := resources.Get "scss/search.scss" | toCSS (merge (dict "targetPath" $targetSearchCssPath) $sassOptions) | postCSS $postcssOptions -}}
<link href="{{ $style.Permalink | relURL }}" rel="stylesheet">
<link href="{{ $docsCss.RelPermalink }}" rel="stylesheet">
<link href="{{ $searchCss.RelPermalink }}" rel="stylesheet">
{{- end }}
+10 -9
View File
@@ -41,10 +41,10 @@
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
for (const element of document.querySelectorAll('[data-bs-theme-value]')) {
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
}
})
btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
@@ -67,13 +67,14 @@
window.addEventListener('DOMContentLoaded', () => {
showActiveTheme(getPreferredTheme())
for (const toggle of document.querySelectorAll('[data-bs-theme-value]')) {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
document.querySelectorAll('[data-bs-theme-value]')
.forEach(toggle => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})
}
})
})()
@@ -6,7 +6,7 @@
const forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
for (const form of forms) {
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault()
@@ -15,5 +15,5 @@
form.classList.add('was-validated')
}, false)
}
})
})()