Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 418529f336 | |||
| 65a4072b55 | |||
| 627a295ef6 | |||
| 7cfdeeabef | |||
| 6b27acfc85 | |||
| 1438f19f7f | |||
| 7f774f4f99 | |||
| 540a2ed46c | |||
| a79ca065d8 | |||
| eebf75b98b | |||
| f0788d5f98 | |||
| 8cfc08d23f | |||
| 5b1a686d3a | |||
| 983f589253 | |||
| d67059f02d | |||
| 31f30256f7 | |||
| 4b90546519 | |||
| 2d1e26710b | |||
| 3b9e01f4d1 | |||
| c3c3a9f815 | |||
| e8516ff87b | |||
| 975a8a5230 | |||
| 498de08235 | |||
| cd7474164c | |||
| b82c8e4018 | |||
| 64d106f104 | |||
| 82b0a25b17 | |||
| 3d9900bd04 | |||
| 7f8ba319c8 | |||
| 5d30bd84fb | |||
| 7c99bef822 | |||
| fd77fa3a25 | |||
| 047932ad58 | |||
| 2bf91e5434 | |||
| 4ad3c27893 | |||
| d57273bb91 | |||
| 37161d79bf | |||
| 2f20144270 | |||
| 5645c69826 | |||
| 0ab2ffbe35 | |||
| 353324bd94 | |||
| 94bfbe5528 | |||
| 23381465c3 | |||
| a95c4fcbcf | |||
| a98aacc9b6 | |||
| 54808a9187 | |||
| 881ad1d055 | |||
| acdda008c3 | |||
| 43a07ccc19 |
+4
-6
@@ -1,12 +1,10 @@
|
||||
# https://github.com/browserslist/browserslist#readme
|
||||
|
||||
>= 0.5%
|
||||
last 2 major versions
|
||||
not dead
|
||||
Chrome >= 60
|
||||
Firefox >= 60
|
||||
Firefox ESR
|
||||
iOS >= 12
|
||||
Safari >= 12
|
||||
Chrome >= 120
|
||||
Firefox >= 121
|
||||
iOS >= 15.6
|
||||
Safari >= 15.6
|
||||
not Explorer <= 11
|
||||
not kaios <= 2.5 # fix floating label issues in Firefox (see https://github.com/postcss/autoprefixer/issues/1533)
|
||||
|
||||
+14
-14
@@ -2,59 +2,59 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "./dist/css/bootstrap-grid.css",
|
||||
"maxSize": "10.0 kB"
|
||||
"maxSize": "9.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-grid.min.css",
|
||||
"maxSize": "9.0 kB"
|
||||
"maxSize": "8.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.css",
|
||||
"maxSize": "5.5 kB"
|
||||
"maxSize": "5.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.min.css",
|
||||
"maxSize": "4.5 kB"
|
||||
"maxSize": "4.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-utilities.css",
|
||||
"maxSize": "15.25 kB"
|
||||
"maxSize": "14.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-utilities.min.css",
|
||||
"maxSize": "13.5 kB"
|
||||
"maxSize": "12.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.css",
|
||||
"maxSize": "37.5 kB"
|
||||
"maxSize": "37.75 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.min.css",
|
||||
"maxSize": "33.75 kB"
|
||||
"maxSize": "34.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.js",
|
||||
"maxSize": "43.0 kB"
|
||||
"maxSize": "67.75 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.min.js",
|
||||
"maxSize": "23.5 kB"
|
||||
"maxSize": "41.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.esm.js",
|
||||
"maxSize": "28.0 kB"
|
||||
"maxSize": "39.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.esm.min.js",
|
||||
"maxSize": "18.25 kB"
|
||||
"maxSize": "24.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.js",
|
||||
"maxSize": "28.75 kB"
|
||||
"maxSize": "39.75 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.min.js",
|
||||
"maxSize": "16.25 kB"
|
||||
"maxSize": "21.25 kB"
|
||||
}
|
||||
],
|
||||
"ci": {
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ jobs:
|
||||
name: calibreapp/image-actions
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# allow calibreapp/image-actions to update PRs
|
||||
# allow calibreapp/image-actions to update PRs and commit compressed images
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Clone repository
|
||||
|
||||
@@ -5,11 +5,13 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- v4-dev
|
||||
- v6-dev
|
||||
- "!dependabot/**"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v4-dev
|
||||
- v6-dev
|
||||
- "!dependabot/**"
|
||||
schedule:
|
||||
- cron: "0 2 * * 4"
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v6-dev
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
schedule:
|
||||
- cron: '27 12 * * 2'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "v6-dev" ]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
@@ -2,8 +2,23 @@
|
||||
"extends": [
|
||||
"stylelint-config-twbs-bootstrap"
|
||||
],
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"reportInvalidScopeDisables": true,
|
||||
"reportNeedlessDisables": true,
|
||||
"rules": {
|
||||
"order/order": [
|
||||
[
|
||||
{ "type": "at-rule", "name": "use" },
|
||||
{ "type": "at-rule", "name": "forward" },
|
||||
"dollar-variables",
|
||||
"custom-properties",
|
||||
"declarations",
|
||||
"rules"
|
||||
]
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": "**/*.scss",
|
||||
|
||||
@@ -52,7 +52,7 @@ Several quick start options are available:
|
||||
- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.3.8`
|
||||
- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass`
|
||||
|
||||
Read the [Getting started page](https://getbootstrap.com/docs/5.3/getting-started/introduction/) for information on the framework contents, templates, examples, and more.
|
||||
Read the [Getting started page](https://getbootstrap.com/docs/5.3/getting-started/) for information on the framework contents, templates, examples, and more.
|
||||
|
||||
|
||||
## Status
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execSync } from 'node:child_process'
|
||||
|
||||
// Run bundlewatch and capture output
|
||||
let stdout
|
||||
let exitCode = 0
|
||||
|
||||
try {
|
||||
stdout = execSync('npx bundlewatch --config .bundlewatch.config.json', {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
})
|
||||
} catch (error) {
|
||||
stdout = error.stdout || ''
|
||||
exitCode = error.status || 1
|
||||
}
|
||||
|
||||
// Parse lines that contain PASS or FAIL
|
||||
const lines = stdout.split('\n').filter(l => l.startsWith('PASS') || l.startsWith('FAIL'))
|
||||
|
||||
if (lines.length === 0) {
|
||||
console.log(stdout)
|
||||
process.exit(exitCode)
|
||||
}
|
||||
|
||||
// Parse size string to number (KB)
|
||||
const parseSize = str => Number.parseFloat(str.replace('KB', ''))
|
||||
|
||||
// Calculate column widths and headroom
|
||||
const rows = lines.map(line => {
|
||||
const match = line.match(/(PASS|FAIL)\s+(.+?):\s+([\d.]+KB)\s+([<>])\s+([\d.]+KB)/)
|
||||
if (match) {
|
||||
const sizeNum = parseSize(match[3])
|
||||
const maxNum = parseSize(match[5])
|
||||
const headroomNum = maxNum - sizeNum
|
||||
const headroom = `${headroomNum.toFixed(2)}KB`
|
||||
return {
|
||||
status: match[1],
|
||||
file: match[2],
|
||||
size: match[3],
|
||||
max: match[5],
|
||||
headroomNum,
|
||||
headroom: match[1] === 'PASS' ? `+${headroom}` : `-${Math.abs(headroomNum).toFixed(2)}KB`
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}).filter(Boolean)
|
||||
|
||||
const maxFileLen = Math.max(...rows.map(r => r.file.length), 4)
|
||||
const maxSizeLen = Math.max(...rows.map(r => r.size.length), 4)
|
||||
const maxMaxLen = Math.max(...rows.map(r => r.max.length), 3)
|
||||
const maxHeadroomLen = Math.max(...rows.map(r => r.headroom.length), 8)
|
||||
|
||||
// Build table
|
||||
const hr = `+-${'-'.repeat(maxFileLen)}-+-${'-'.repeat(maxSizeLen)}-+-${'-'.repeat(maxMaxLen)}-+-${'-'.repeat(maxHeadroomLen)}-+`
|
||||
|
||||
console.log('')
|
||||
console.log('bundlewatch results')
|
||||
console.log(hr)
|
||||
console.log(`| ${'File'.padEnd(maxFileLen)} | ${'Size'.padStart(maxSizeLen)} | ${'Max'.padStart(maxMaxLen)} | ${'Headroom'.padStart(maxHeadroomLen)} |`)
|
||||
console.log(hr)
|
||||
|
||||
const green = '\u001B[32m'
|
||||
const red = '\u001B[31m'
|
||||
const reset = '\u001B[0m'
|
||||
|
||||
for (const row of rows) {
|
||||
const sizeColor = row.status === 'PASS' ? green : red
|
||||
const coloredSize = `${sizeColor}${row.size.padStart(maxSizeLen)}${reset}`
|
||||
const headroomColor = row.headroomNum > 0.25 ? red : ''
|
||||
const headroomReset = row.headroomNum > 0.25 ? reset : ''
|
||||
const coloredHeadroom = `${headroomColor}${row.headroom.padStart(maxHeadroomLen)}${headroomReset}`
|
||||
console.log(`| ${row.file.padEnd(maxFileLen)} | ${coloredSize} | ${row.max.padStart(maxMaxLen)} | ${coloredHeadroom} |`)
|
||||
}
|
||||
|
||||
console.log(hr)
|
||||
|
||||
// Summary
|
||||
const passed = rows.filter(r => r.status === 'PASS').length
|
||||
const failed = rows.filter(r => r.status === 'FAIL').length
|
||||
|
||||
console.log('')
|
||||
if (failed > 0) {
|
||||
console.log(`\u001B[31mbundlewatch FAIL\u001B[0m - ${passed} passed, ${failed} failed`)
|
||||
} else {
|
||||
console.log(`\u001B[32mbundlewatch PASS\u001B[0m - ${passed}/${rows.length} files within limits`)
|
||||
}
|
||||
|
||||
console.log('')
|
||||
|
||||
process.exit(exitCode)
|
||||
@@ -38,8 +38,8 @@ const files = [
|
||||
configPropertyName: 'js_bundle_hash'
|
||||
},
|
||||
{
|
||||
file: 'node_modules/@popperjs/core/dist/umd/popper.min.js',
|
||||
configPropertyName: 'popper_hash'
|
||||
file: 'node_modules/@floating-ui/dom/dist/floating-ui.dom.umd.min.js',
|
||||
configPropertyName: 'floating_ui_hash'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -27,13 +27,22 @@ $total-utilities: list.length(map.keys($utilities-map)) !default;
|
||||
// Skip if utility is null or false (disabled)
|
||||
@if $utility {
|
||||
// Extract class prefix
|
||||
$class: if(map.has-key($utility, "class"), map.get($utility, "class"), $key);
|
||||
$class: $key;
|
||||
@if map.has-key($utility, "class") {
|
||||
$class: map.get($utility, "class");
|
||||
}
|
||||
|
||||
// Extract property
|
||||
$property: if(map.has-key($utility, "property"), map.get($utility, "property"), null);
|
||||
$property: null;
|
||||
@if map.has-key($utility, "property") {
|
||||
$property: map.get($utility, "property");
|
||||
}
|
||||
|
||||
// Extract values
|
||||
$values: if(map.has-key($utility, "values"), map.get($utility, "values"), null);
|
||||
$values: null;
|
||||
@if map.has-key($utility, "values") {
|
||||
$values: map.get($utility, "values");
|
||||
}
|
||||
|
||||
// Generate class list
|
||||
$classes: "";
|
||||
@@ -45,7 +54,10 @@ $total-utilities: list.length(map.keys($utilities-map)) !default;
|
||||
@if not $first {
|
||||
$classes: $classes + ", ";
|
||||
}
|
||||
$class-name: if($value-key == "null" or $value-key == null, $class, "#{$class}-#{$value-key}");
|
||||
$class-name: "#{$class}-#{$value-key}";
|
||||
@if $value-key == "null" or $value-key == null {
|
||||
$class-name: $class;
|
||||
}
|
||||
$classes: $classes + '"' + $class-name + '"';
|
||||
$first: false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import postcssPrefixCustomProperties from 'postcss-prefix-custom-properties'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
|
||||
const mapConfig = {
|
||||
inline: false,
|
||||
annotation: true,
|
||||
@@ -7,10 +10,12 @@ const mapConfig = {
|
||||
export default context => {
|
||||
return {
|
||||
map: context.file.dirname.includes('examples') ? false : mapConfig,
|
||||
plugins: {
|
||||
autoprefixer: {
|
||||
cascade: false
|
||||
}
|
||||
}
|
||||
plugins: [
|
||||
postcssPrefixCustomProperties({
|
||||
prefix: 'bs-',
|
||||
ignore: [/^--bs-/, /^--bd-/]
|
||||
}),
|
||||
autoprefixer({ cascade: false })
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const BUNDLE = process.env.BUNDLE === 'true'
|
||||
const ESM = process.env.ESM === 'true'
|
||||
|
||||
let destinationFile = `bootstrap${ESM ? '.esm' : ''}`
|
||||
const external = ['@popperjs/core']
|
||||
const external = ['@floating-ui/dom']
|
||||
const plugins = [
|
||||
babel({
|
||||
// Only transpile our source code
|
||||
@@ -22,14 +22,14 @@ const plugins = [
|
||||
})
|
||||
]
|
||||
const globals = {
|
||||
'@popperjs/core': 'Popper'
|
||||
'@floating-ui/dom': 'FloatingUIDOM'
|
||||
}
|
||||
|
||||
if (BUNDLE) {
|
||||
destinationFile += '.bundle'
|
||||
// Remove last entry in external array to bundle Popper
|
||||
// Remove last entry in external array to bundle Floating UI
|
||||
external.pop()
|
||||
delete globals['@popperjs/core']
|
||||
delete globals['@floating-ui/dom']
|
||||
plugins.push(
|
||||
replace({
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
|
||||
@@ -26,9 +26,7 @@ const docsDir = `${rootDocsDir}/docs/${versionShort}/`
|
||||
// these are the files we need in the examples
|
||||
const cssFiles = [
|
||||
'bootstrap.min.css',
|
||||
'bootstrap.min.css.map',
|
||||
'bootstrap.rtl.min.css',
|
||||
'bootstrap.rtl.min.css.map'
|
||||
'bootstrap.min.css.map'
|
||||
]
|
||||
const jsFiles = [
|
||||
'bootstrap.bundle.min.js',
|
||||
|
||||
+6
-6
@@ -35,14 +35,14 @@ download:
|
||||
cdn:
|
||||
# See https://www.srihash.org for info on how to generate the hashes
|
||||
css: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
|
||||
css_hash: "sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
|
||||
css_hash: "sha384-TDmpFhAO5TwSQwPF95I/odgwpTUuv0aaVm9/0fL7b+kKe7hFBp/+9cBCMkydgGOi"
|
||||
js: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"
|
||||
js_hash: "sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y"
|
||||
js_hash: "sha384-Php492snRLTR5p+hMyxpV6gYwp1avWXn4AaX31MgANrvsjr9Dpodl3Nw60L7Pewl"
|
||||
js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
||||
js_bundle_hash: "sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
||||
popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
|
||||
popper_hash: "sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
|
||||
popper_esm: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/esm/popper.min.js"
|
||||
js_bundle_hash: "sha384-I2J4jlw924JZXHU9un9Mcuixq/rKhd5A8/B1NQ6ifPAiBFacZjwNcec8d6L38jQv"
|
||||
floating_ui: "https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.0/dist/floating-ui.dom.umd.min.js"
|
||||
floating_ui_hash: "sha384-R7p1RqabZNhI+RdPNIzTouzd/LBVorZ0Tn3ApcogSOk+HF3o+P0HIenrUw/n0MOj"
|
||||
floating_ui_esm: "https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.0/dist/floating-ui.dom.esm.min.js"
|
||||
|
||||
anchors:
|
||||
min: 2
|
||||
|
||||
Vendored
+4401
-3852
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-4084
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-6
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
Vendored
+803
-561
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-4
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-598
@@ -1,598 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: inherit;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
[type=search]::-webkit-search-cancel-button {
|
||||
cursor: pointer;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||
-1
File diff suppressed because one or more lines are too long
-6
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
Vendored
+5057
-4848
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+59
-32
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"aspect-ratio": {
|
||||
"class": "ratio",
|
||||
"property": "--bs-ratio",
|
||||
"property": "--ratio",
|
||||
"classes": [
|
||||
"ratio-auto",
|
||||
"ratio-1x1",
|
||||
@@ -120,7 +120,7 @@
|
||||
},
|
||||
"focus-ring": {
|
||||
"class": "focus-ring",
|
||||
"property": "--bs-focus-ring-color",
|
||||
"property": "--focus-ring-color",
|
||||
"classes": [
|
||||
"focus-ring-primary",
|
||||
"focus-ring-accent",
|
||||
@@ -522,7 +522,7 @@
|
||||
},
|
||||
"grid-column-counts": {
|
||||
"class": "grid-cols",
|
||||
"property": "--bs-columns",
|
||||
"property": "--columns",
|
||||
"classes": [
|
||||
"grid-cols-3",
|
||||
"grid-cols-4",
|
||||
@@ -574,7 +574,7 @@
|
||||
},
|
||||
"margin-x": {
|
||||
"class": "mx",
|
||||
"property": "margin-right margin-left",
|
||||
"property": "margin-inline",
|
||||
"classes": [
|
||||
"mx-0",
|
||||
"mx-1",
|
||||
@@ -587,7 +587,7 @@
|
||||
},
|
||||
"margin-y": {
|
||||
"class": "my",
|
||||
"property": "margin-top margin-bottom",
|
||||
"property": "margin-block",
|
||||
"classes": [
|
||||
"my-0",
|
||||
"my-1",
|
||||
@@ -600,7 +600,7 @@
|
||||
},
|
||||
"margin-top": {
|
||||
"class": "mt",
|
||||
"property": "margin-top",
|
||||
"property": "margin-block-start",
|
||||
"classes": [
|
||||
"mt-0",
|
||||
"mt-1",
|
||||
@@ -613,7 +613,7 @@
|
||||
},
|
||||
"margin-end": {
|
||||
"class": "me",
|
||||
"property": "margin-right",
|
||||
"property": "margin-inline-end",
|
||||
"classes": [
|
||||
"me-0",
|
||||
"me-1",
|
||||
@@ -626,7 +626,7 @@
|
||||
},
|
||||
"margin-bottom": {
|
||||
"class": "mb",
|
||||
"property": "margin-bottom",
|
||||
"property": "margin-block-end",
|
||||
"classes": [
|
||||
"mb-0",
|
||||
"mb-1",
|
||||
@@ -639,7 +639,7 @@
|
||||
},
|
||||
"margin-start": {
|
||||
"class": "ms",
|
||||
"property": "margin-left",
|
||||
"property": "margin-inline-start",
|
||||
"classes": [
|
||||
"ms-0",
|
||||
"ms-1",
|
||||
@@ -664,7 +664,7 @@
|
||||
},
|
||||
"padding-x": {
|
||||
"class": "px",
|
||||
"property": "padding-right padding-left",
|
||||
"property": "padding-inline",
|
||||
"classes": [
|
||||
"px-0",
|
||||
"px-1",
|
||||
@@ -676,7 +676,7 @@
|
||||
},
|
||||
"padding-y": {
|
||||
"class": "py",
|
||||
"property": "padding-top padding-bottom",
|
||||
"property": "padding-block",
|
||||
"classes": [
|
||||
"py-0",
|
||||
"py-1",
|
||||
@@ -688,7 +688,7 @@
|
||||
},
|
||||
"padding-top": {
|
||||
"class": "pt",
|
||||
"property": "padding-top",
|
||||
"property": "padding-block-start",
|
||||
"classes": [
|
||||
"pt-0",
|
||||
"pt-1",
|
||||
@@ -700,7 +700,7 @@
|
||||
},
|
||||
"padding-end": {
|
||||
"class": "pe",
|
||||
"property": "padding-right",
|
||||
"property": "padding-inline-end",
|
||||
"classes": [
|
||||
"pe-0",
|
||||
"pe-1",
|
||||
@@ -712,7 +712,7 @@
|
||||
},
|
||||
"padding-bottom": {
|
||||
"class": "pb",
|
||||
"property": "padding-bottom",
|
||||
"property": "padding-block-end",
|
||||
"classes": [
|
||||
"pb-0",
|
||||
"pb-1",
|
||||
@@ -724,7 +724,7 @@
|
||||
},
|
||||
"padding-start": {
|
||||
"class": "ps",
|
||||
"property": "padding-left",
|
||||
"property": "padding-inline-start",
|
||||
"classes": [
|
||||
"ps-0",
|
||||
"ps-1",
|
||||
@@ -899,6 +899,7 @@
|
||||
"fg-1",
|
||||
"fg-2",
|
||||
"fg-3",
|
||||
"fg-4",
|
||||
"fg-white",
|
||||
"fg-black",
|
||||
"fg-inherit"
|
||||
@@ -917,6 +918,19 @@
|
||||
"fg-emphasis-secondary"
|
||||
]
|
||||
},
|
||||
"fg-contrast": {
|
||||
"class": "fg-contrast",
|
||||
"classes": [
|
||||
"fg-contrast-primary",
|
||||
"fg-contrast-accent",
|
||||
"fg-contrast-success",
|
||||
"fg-contrast-danger",
|
||||
"fg-contrast-warning",
|
||||
"fg-contrast-info",
|
||||
"fg-contrast-inverse",
|
||||
"fg-contrast-secondary"
|
||||
]
|
||||
},
|
||||
"fg-opacity": {
|
||||
"class": "fg",
|
||||
"property": "color",
|
||||
@@ -933,19 +947,6 @@
|
||||
"fg-100"
|
||||
]
|
||||
},
|
||||
"fg-contrast": {
|
||||
"class": "fg-contrast",
|
||||
"classes": [
|
||||
"fg-contrast-primary",
|
||||
"fg-contrast-accent",
|
||||
"fg-contrast-success",
|
||||
"fg-contrast-danger",
|
||||
"fg-contrast-warning",
|
||||
"fg-contrast-info",
|
||||
"fg-contrast-inverse",
|
||||
"fg-contrast-secondary"
|
||||
]
|
||||
},
|
||||
"link-opacity": {
|
||||
"class": "link",
|
||||
"property": "color",
|
||||
@@ -1027,6 +1028,7 @@
|
||||
"bg-1",
|
||||
"bg-2",
|
||||
"bg-3",
|
||||
"bg-4",
|
||||
"bg-white",
|
||||
"bg-black",
|
||||
"bg-transparent",
|
||||
@@ -1075,6 +1077,31 @@
|
||||
"bg-100"
|
||||
]
|
||||
},
|
||||
"theme-contrast": {
|
||||
"class": "theme-contrast",
|
||||
"classes": [
|
||||
"theme-contrast"
|
||||
]
|
||||
},
|
||||
"theme-subtle": {
|
||||
"class": "theme-subtle",
|
||||
"classes": [
|
||||
"theme-subtle"
|
||||
]
|
||||
},
|
||||
"theme-muted": {
|
||||
"class": "theme-muted",
|
||||
"classes": [
|
||||
"theme-muted"
|
||||
]
|
||||
},
|
||||
"theme-border": {
|
||||
"class": "theme-border",
|
||||
"property": "border",
|
||||
"classes": [
|
||||
"theme-border"
|
||||
]
|
||||
},
|
||||
"gradient": {
|
||||
"class": "bg",
|
||||
"property": "background-image",
|
||||
@@ -1116,7 +1143,7 @@
|
||||
},
|
||||
"rounded-top": {
|
||||
"class": "rounded-top",
|
||||
"property": "border-top-left-radius border-top-right-radius",
|
||||
"property": "border-start-start-radius border-start-end-radius",
|
||||
"classes": [
|
||||
"rounded-top",
|
||||
"rounded-top-0",
|
||||
@@ -1131,7 +1158,7 @@
|
||||
},
|
||||
"rounded-end": {
|
||||
"class": "rounded-end",
|
||||
"property": "border-top-right-radius border-bottom-right-radius",
|
||||
"property": "border-end-end-radius border-end-start-radius",
|
||||
"classes": [
|
||||
"rounded-end",
|
||||
"rounded-end-0",
|
||||
@@ -1146,7 +1173,7 @@
|
||||
},
|
||||
"rounded-bottom": {
|
||||
"class": "rounded-bottom",
|
||||
"property": "border-bottom-right-radius border-bottom-left-radius",
|
||||
"property": "border-end-end-radius border-end-start-radius",
|
||||
"classes": [
|
||||
"rounded-bottom",
|
||||
"rounded-bottom-0",
|
||||
@@ -1161,7 +1188,7 @@
|
||||
},
|
||||
"rounded-start": {
|
||||
"class": "rounded-start",
|
||||
"property": "border-bottom-left-radius border-top-left-radius",
|
||||
"property": "border-start-start-radius border-start-end-radius",
|
||||
"classes": [
|
||||
"rounded-start",
|
||||
"rounded-start-0",
|
||||
|
||||
Vendored
+4
-4
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-5397
File diff suppressed because it is too large
Load Diff
-1
File diff suppressed because one or more lines are too long
-6
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+11669
-11251
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-12021
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-6
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+3889
-2394
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2391
-676
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2397
-698
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap alert.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap base-component.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap button.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap carousel.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap collapse.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+443
@@ -0,0 +1,443 @@
|
||||
/*!
|
||||
* Bootstrap datepicker.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vanilla-calendar-pro'), require('./base-component.js'), require('./dom/event-handler.js'), require('./util/index.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['vanilla-calendar-pro', './base-component', './dom/event-handler', './util/index'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Datepicker = factory(global["vanilla-calendar-pro"], global.BaseComponent, global.EventHandler, global.Index));
|
||||
})(this, (function (vanillaCalendarPro, BaseComponent, EventHandler, index_js) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap datepicker.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'datepicker';
|
||||
const DATA_KEY = 'bs.datepicker';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const DATA_API_KEY = '.data-api';
|
||||
const EVENT_CHANGE = `change${EVENT_KEY}`;
|
||||
const EVENT_SHOW = `show${EVENT_KEY}`;
|
||||
const EVENT_SHOWN = `shown${EVENT_KEY}`;
|
||||
const EVENT_HIDE = `hide${EVENT_KEY}`;
|
||||
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
|
||||
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const EVENT_FOCUSIN_DATA_API = `focusin${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="datepicker"]';
|
||||
const HIDE_DELAY = 100; // ms delay before hiding after selection
|
||||
|
||||
const Default = {
|
||||
datepickerTheme: null,
|
||||
// 'light', 'dark', 'auto' - explicit theme for datepicker popover only
|
||||
dateMin: null,
|
||||
dateMax: null,
|
||||
dateFormat: null,
|
||||
// Intl.DateTimeFormat options, or function(date, locale) => string
|
||||
displayElement: null,
|
||||
// Element to show formatted date (defaults to element for buttons)
|
||||
displayMonthsCount: 1,
|
||||
// Number of months to display side-by-side
|
||||
firstWeekday: 1,
|
||||
// Monday
|
||||
inline: false,
|
||||
// Render calendar inline (no popup)
|
||||
locale: 'default',
|
||||
positionElement: null,
|
||||
// Element to position calendar relative to (defaults to input)
|
||||
selectedDates: [],
|
||||
selectionMode: 'single',
|
||||
// 'single', 'multiple', 'multiple-ranged'
|
||||
placement: 'left',
|
||||
// 'left', 'center', 'right', 'auto'
|
||||
vcpOptions: {} // Pass-through for any VCP option
|
||||
};
|
||||
const DefaultType = {
|
||||
datepickerTheme: '(null|string)',
|
||||
dateMin: '(null|string|number|object)',
|
||||
dateMax: '(null|string|number|object)',
|
||||
dateFormat: '(null|object|function)',
|
||||
displayElement: '(null|string|element|boolean)',
|
||||
displayMonthsCount: 'number',
|
||||
firstWeekday: 'number',
|
||||
inline: 'boolean',
|
||||
locale: 'string',
|
||||
positionElement: '(null|string|element)',
|
||||
selectedDates: 'array',
|
||||
selectionMode: 'string',
|
||||
placement: 'string',
|
||||
vcpOptions: 'object'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class Datepicker extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config);
|
||||
this._calendar = null;
|
||||
this._isShown = false;
|
||||
this._initCalendar();
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
toggle() {
|
||||
if (this._config.inline) {
|
||||
return; // Inline calendars are always visible
|
||||
}
|
||||
return this._isShown ? this.hide() : this.show();
|
||||
}
|
||||
show() {
|
||||
if (this._config.inline) {
|
||||
return; // Inline calendars are always visible
|
||||
}
|
||||
if (!this._calendar || index_js.isDisabled(this._element) || this._isShown) {
|
||||
return;
|
||||
}
|
||||
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.show();
|
||||
this._isShown = true;
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN);
|
||||
}
|
||||
hide() {
|
||||
if (this._config.inline) {
|
||||
return; // Inline calendars are always visible
|
||||
}
|
||||
if (!this._calendar || !this._isShown) {
|
||||
return;
|
||||
}
|
||||
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.hide();
|
||||
this._isShown = false;
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN);
|
||||
}
|
||||
dispose() {
|
||||
if (this._themeObserver) {
|
||||
this._themeObserver.disconnect();
|
||||
this._themeObserver = null;
|
||||
}
|
||||
if (this._calendar) {
|
||||
this._calendar.destroy();
|
||||
}
|
||||
this._calendar = null;
|
||||
super.dispose();
|
||||
}
|
||||
getSelectedDates() {
|
||||
const dates = this._calendar?.context?.selectedDates;
|
||||
return dates ? [...dates] : [];
|
||||
}
|
||||
setSelectedDates(dates) {
|
||||
if (this._calendar) {
|
||||
this._calendar.set({
|
||||
selectedDates: dates
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
_initCalendar() {
|
||||
this._isInput = this._element.tagName === 'INPUT';
|
||||
this._isInline = this._config.inline;
|
||||
|
||||
// For inline mode, look for a hidden input child to bind to
|
||||
if (this._isInline && !this._isInput) {
|
||||
this._boundInput = this._element.querySelector('input[type="hidden"], input[name]');
|
||||
}
|
||||
this._positionElement = this._resolvePositionElement();
|
||||
this._displayElement = this._resolveDisplayElement();
|
||||
const calendarOptions = this._buildCalendarOptions();
|
||||
|
||||
// Create calendar on the position element (for correct popup positioning)
|
||||
// but value updates still go to this._element (the input)
|
||||
this._calendar = new vanillaCalendarPro.Calendar(this._positionElement, calendarOptions);
|
||||
this._calendar.init();
|
||||
|
||||
// Watch for theme changes on ancestor elements (for live theme switching)
|
||||
this._setupThemeObserver();
|
||||
|
||||
// Set initial value if input has a value
|
||||
if (this._isInput && this._element.value) {
|
||||
this._parseInputValue();
|
||||
}
|
||||
|
||||
// Populate input/display with preselected dates
|
||||
this._updateDisplayWithSelectedDates();
|
||||
}
|
||||
_updateDisplayWithSelectedDates() {
|
||||
const {
|
||||
selectedDates
|
||||
} = this._config;
|
||||
if (!selectedDates || selectedDates.length === 0) {
|
||||
return;
|
||||
}
|
||||
const formattedDate = this._formatDateForInput(selectedDates);
|
||||
if (this._isInput) {
|
||||
this._element.value = formattedDate;
|
||||
}
|
||||
if (this._boundInput) {
|
||||
this._boundInput.value = selectedDates.join(',');
|
||||
}
|
||||
if (this._displayElement) {
|
||||
this._displayElement.textContent = formattedDate;
|
||||
}
|
||||
}
|
||||
_resolvePositionElement() {
|
||||
let {
|
||||
positionElement
|
||||
} = this._config;
|
||||
if (typeof positionElement === 'string') {
|
||||
positionElement = document.querySelector(positionElement);
|
||||
}
|
||||
|
||||
// Use input's parent if in form-adorn
|
||||
if (!positionElement && this._isInput && !this._isInline) {
|
||||
const parent = this._element.closest('.form-adorn');
|
||||
if (parent) {
|
||||
positionElement = parent;
|
||||
}
|
||||
}
|
||||
return positionElement || this._element;
|
||||
}
|
||||
_resolveDisplayElement() {
|
||||
const {
|
||||
displayElement
|
||||
} = this._config;
|
||||
if (typeof displayElement === 'string') {
|
||||
return document.querySelector(displayElement);
|
||||
}
|
||||
|
||||
// For buttons/non-inputs (not inline), look for a [data-bs-datepicker-display] child
|
||||
if (displayElement === true || displayElement === null && !this._isInput && !this._isInline) {
|
||||
const displayChild = this._element.querySelector('[data-bs-datepicker-display]');
|
||||
return displayChild || this._element;
|
||||
}
|
||||
return displayElement;
|
||||
}
|
||||
_getThemeAncestor() {
|
||||
return this._element.closest('[data-bs-theme]');
|
||||
}
|
||||
_getEffectiveTheme() {
|
||||
// Priority: explicit datepickerTheme config > inherited from ancestor > none
|
||||
const {
|
||||
datepickerTheme
|
||||
} = this._config;
|
||||
if (datepickerTheme) {
|
||||
return datepickerTheme;
|
||||
}
|
||||
const ancestor = this._getThemeAncestor();
|
||||
return ancestor?.getAttribute('data-bs-theme') || null;
|
||||
}
|
||||
_syncThemeAttribute(element) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
const theme = this._getEffectiveTheme();
|
||||
if (theme) {
|
||||
// Copy theme to popover (needed because VCP appends to body, breaking CSS inheritance)
|
||||
element.setAttribute('data-bs-theme', theme);
|
||||
} else {
|
||||
// No theme - remove attribute to allow natural inheritance
|
||||
element.removeAttribute('data-bs-theme');
|
||||
}
|
||||
}
|
||||
_setupThemeObserver() {
|
||||
// Watch for theme changes on ancestor elements
|
||||
const ancestor = this._getThemeAncestor();
|
||||
if (!ancestor || this._config.datepickerTheme) {
|
||||
// No ancestor to watch, or explicit datepickerTheme overrides
|
||||
return;
|
||||
}
|
||||
this._themeObserver = new MutationObserver(() => {
|
||||
this._syncThemeAttribute(this._calendar?.context?.mainElement);
|
||||
});
|
||||
this._themeObserver.observe(ancestor, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-bs-theme']
|
||||
});
|
||||
}
|
||||
_buildCalendarOptions() {
|
||||
// Get theme for VCP - use 'system' for auto-detection if no explicit theme
|
||||
const theme = this._getEffectiveTheme();
|
||||
// VCP uses 'system' for auto, Bootstrap uses 'auto'
|
||||
const vcpTheme = !theme || theme === 'auto' ? 'system' : theme;
|
||||
const calendarOptions = {
|
||||
...this._config.vcpOptions,
|
||||
inputMode: !this._isInline,
|
||||
positionToInput: this._config.placement,
|
||||
firstWeekday: this._config.firstWeekday,
|
||||
locale: this._config.locale,
|
||||
selectionDatesMode: this._config.selectionMode,
|
||||
selectedDates: this._config.selectedDates,
|
||||
displayMonthsCount: this._config.displayMonthsCount,
|
||||
type: this._config.displayMonthsCount > 1 ? 'multiple' : 'default',
|
||||
selectedTheme: vcpTheme,
|
||||
themeAttrDetect: '[data-bs-theme]',
|
||||
onClickDate: (self, event) => this._handleDateClick(self, event),
|
||||
onInit: self => {
|
||||
this._syncThemeAttribute(self.context.mainElement);
|
||||
},
|
||||
onShow: () => {
|
||||
this._isShown = true;
|
||||
this._syncThemeAttribute(this._calendar.context.mainElement);
|
||||
},
|
||||
onHide: () => {
|
||||
this._isShown = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Navigate to the month of the first selected date
|
||||
if (this._config.selectedDates.length > 0) {
|
||||
const firstDate = this._parseDate(this._config.selectedDates[0]);
|
||||
calendarOptions.selectedMonth = firstDate.getMonth();
|
||||
calendarOptions.selectedYear = firstDate.getFullYear();
|
||||
}
|
||||
if (this._config.dateMin) {
|
||||
calendarOptions.dateMin = this._config.dateMin;
|
||||
}
|
||||
if (this._config.dateMax) {
|
||||
calendarOptions.dateMax = this._config.dateMax;
|
||||
}
|
||||
return calendarOptions;
|
||||
}
|
||||
_handleDateClick(self, event) {
|
||||
const selectedDates = [...self.context.selectedDates];
|
||||
if (selectedDates.length > 0) {
|
||||
const formattedDate = this._formatDateForInput(selectedDates);
|
||||
if (this._isInput) {
|
||||
this._element.value = formattedDate;
|
||||
}
|
||||
if (this._boundInput) {
|
||||
this._boundInput.value = selectedDates.join(',');
|
||||
}
|
||||
if (this._displayElement) {
|
||||
this._displayElement.textContent = formattedDate;
|
||||
}
|
||||
}
|
||||
EventHandler.trigger(this._element, EVENT_CHANGE, {
|
||||
dates: selectedDates,
|
||||
event
|
||||
});
|
||||
this._maybeHideAfterSelection(selectedDates);
|
||||
}
|
||||
_maybeHideAfterSelection(selectedDates) {
|
||||
if (this._isInline) {
|
||||
return;
|
||||
}
|
||||
const shouldHide = this._config.selectionMode === 'single' && selectedDates.length > 0 || this._config.selectionMode === 'multiple-ranged' && selectedDates.length >= 2;
|
||||
if (shouldHide) {
|
||||
setTimeout(() => this.hide(), HIDE_DELAY);
|
||||
}
|
||||
}
|
||||
_parseDate(dateStr) {
|
||||
const [year, month, day] = dateStr.split('-');
|
||||
return new Date(year, month - 1, day);
|
||||
}
|
||||
_formatDate(dateStr) {
|
||||
const date = this._parseDate(dateStr);
|
||||
const locale = this._config.locale === 'default' ? undefined : this._config.locale;
|
||||
const {
|
||||
dateFormat
|
||||
} = this._config;
|
||||
|
||||
// Custom function formatter
|
||||
if (typeof dateFormat === 'function') {
|
||||
return dateFormat(date, locale);
|
||||
}
|
||||
|
||||
// Intl.DateTimeFormat options object
|
||||
if (dateFormat && typeof dateFormat === 'object') {
|
||||
return new Intl.DateTimeFormat(locale, dateFormat).format(date);
|
||||
}
|
||||
|
||||
// Default: locale-aware formatting
|
||||
return date.toLocaleDateString(locale);
|
||||
}
|
||||
_formatDateForInput(dates) {
|
||||
if (dates.length === 0) {
|
||||
return '';
|
||||
}
|
||||
if (dates.length === 1) {
|
||||
return this._formatDate(dates[0]);
|
||||
}
|
||||
|
||||
// For date ranges, use en-dash; for multiple dates, use comma
|
||||
const separator = this._config.selectionMode === 'multiple-ranged' ? ' – ' : ', ';
|
||||
return dates.map(d => this._formatDate(d)).join(separator);
|
||||
}
|
||||
_parseInputValue() {
|
||||
// Try to parse the input value as a date
|
||||
const value = this._element.value.trim();
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const date = new Date(value);
|
||||
if (!Number.isNaN(date.getTime())) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const formatted = `${year}-${month}-${day}`;
|
||||
this._calendar.set({
|
||||
selectedDates: [formatted]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
// Only handle if not an input (inputs use focus)
|
||||
// Skip inline datepickers (they're always visible)
|
||||
if (this.tagName === 'INPUT' || this.dataset.bsInline === 'true') {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
Datepicker.getOrCreateInstance(this).toggle();
|
||||
});
|
||||
EventHandler.on(document, EVENT_FOCUSIN_DATA_API, SELECTOR_DATA_TOGGLE, function () {
|
||||
// Handle focus for input elements
|
||||
if (this.tagName !== 'INPUT') {
|
||||
return;
|
||||
}
|
||||
Datepicker.getOrCreateInstance(this).show();
|
||||
});
|
||||
|
||||
// Auto-initialize inline datepickers on DOMContentLoaded
|
||||
EventHandler.on(document, `DOMContentLoaded${EVENT_KEY}${DATA_API_KEY}`, () => {
|
||||
for (const element of document.querySelectorAll(`${SELECTOR_DATA_TOGGLE}[data-bs-inline="true"]`)) {
|
||||
Datepicker.getOrCreateInstance(element);
|
||||
}
|
||||
});
|
||||
|
||||
return Datepicker;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=datepicker.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+242
@@ -0,0 +1,242 @@
|
||||
/*!
|
||||
* Bootstrap dialog.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/component-functions.js'), require('./util/index.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/component-functions', './util/index'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dialog = factory(global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.ComponentFunctions, global.Index));
|
||||
})(this, (function (BaseComponent, EventHandler, Manipulator, SelectorEngine, componentFunctions_js, index_js) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap dialog.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'dialog';
|
||||
const DATA_KEY = 'bs.dialog';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const DATA_API_KEY = '.data-api';
|
||||
const EVENT_SHOW = `show${EVENT_KEY}`;
|
||||
const EVENT_SHOWN = `shown${EVENT_KEY}`;
|
||||
const EVENT_HIDE = `hide${EVENT_KEY}`;
|
||||
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
|
||||
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
|
||||
const EVENT_CANCEL = `cancel${EVENT_KEY}`;
|
||||
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const CLASS_NAME_STATIC = 'dialog-static';
|
||||
const CLASS_NAME_OPEN = 'dialog-open';
|
||||
const CLASS_NAME_NONMODAL = 'dialog-nonmodal';
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dialog"]';
|
||||
const SELECTOR_OPEN_MODAL_DIALOG = 'dialog.dialog[open]:not(.dialog-nonmodal)';
|
||||
const Default = {
|
||||
backdrop: true,
|
||||
// true (click dismisses) or 'static' (click does nothing) - only applies to modal dialogs
|
||||
keyboard: true,
|
||||
modal: true // true uses showModal(), false uses show() for non-modal dialogs
|
||||
};
|
||||
const DefaultType = {
|
||||
backdrop: '(boolean|string)',
|
||||
keyboard: 'boolean',
|
||||
modal: 'boolean'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class Dialog extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config);
|
||||
this._isTransitioning = false;
|
||||
this._addEventListeners();
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
toggle(relatedTarget) {
|
||||
return this._element.open ? this.hide() : this.show(relatedTarget);
|
||||
}
|
||||
show(relatedTarget) {
|
||||
if (this._element.open || this._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
|
||||
relatedTarget
|
||||
});
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._isTransitioning = true;
|
||||
if (this._config.modal) {
|
||||
// Modal dialog: use showModal() for focus trapping, backdrop, and top layer
|
||||
this._element.showModal();
|
||||
// Prevent body scroll for modal dialogs
|
||||
document.body.classList.add(CLASS_NAME_OPEN);
|
||||
} else {
|
||||
// Non-modal dialog: use show() - no backdrop, no focus trap, no top layer
|
||||
this._element.classList.add(CLASS_NAME_NONMODAL);
|
||||
this._element.show();
|
||||
}
|
||||
this._queueCallback(() => {
|
||||
this._isTransitioning = false;
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN, {
|
||||
relatedTarget
|
||||
});
|
||||
}, this._element, this._isAnimated());
|
||||
}
|
||||
hide() {
|
||||
if (!this._element.open || this._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._isTransitioning = true;
|
||||
this._queueCallback(() => this._hideDialog(), this._element, this._isAnimated());
|
||||
}
|
||||
dispose() {
|
||||
EventHandler.off(this._element, EVENT_KEY);
|
||||
super.dispose();
|
||||
}
|
||||
handleUpdate() {
|
||||
// Provided for API consistency with Modal.
|
||||
// Native dialogs handle their own positioning.
|
||||
}
|
||||
|
||||
// Private
|
||||
_hideDialog() {
|
||||
this._element.close();
|
||||
this._element.classList.remove(CLASS_NAME_NONMODAL);
|
||||
this._isTransitioning = false;
|
||||
|
||||
// Only restore body scroll if no other modal dialogs are open
|
||||
if (!document.querySelector(SELECTOR_OPEN_MODAL_DIALOG)) {
|
||||
document.body.classList.remove(CLASS_NAME_OPEN);
|
||||
}
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN);
|
||||
}
|
||||
_isAnimated() {
|
||||
return this._element.classList.contains('fade');
|
||||
}
|
||||
_triggerBackdropTransition() {
|
||||
const hidePreventedEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
|
||||
if (hidePreventedEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._element.classList.add(CLASS_NAME_STATIC);
|
||||
this._queueCallback(() => {
|
||||
this._element.classList.remove(CLASS_NAME_STATIC);
|
||||
}, this._element);
|
||||
}
|
||||
_addEventListeners() {
|
||||
// Handle native cancel event (Escape key) - only fires for modal dialogs
|
||||
EventHandler.on(this._element, 'cancel', event => {
|
||||
// Prevent native close behavior - we'll handle it
|
||||
event.preventDefault();
|
||||
if (!this._config.keyboard) {
|
||||
this._triggerBackdropTransition();
|
||||
return;
|
||||
}
|
||||
EventHandler.trigger(this._element, EVENT_CANCEL);
|
||||
this.hide();
|
||||
});
|
||||
|
||||
// Handle Escape key for non-modal dialogs (native cancel doesn't fire for show())
|
||||
EventHandler.on(this._element, 'keydown', event => {
|
||||
if (event.key !== 'Escape' || this._config.modal) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
if (!this._config.keyboard) {
|
||||
return;
|
||||
}
|
||||
EventHandler.trigger(this._element, EVENT_CANCEL);
|
||||
this.hide();
|
||||
});
|
||||
|
||||
// Handle backdrop clicks (only applies to modal dialogs)
|
||||
// Native <dialog> fires click on the dialog element when backdrop is clicked
|
||||
EventHandler.on(this._element, 'click', event => {
|
||||
// Only handle clicks directly on the dialog (backdrop area)
|
||||
// Non-modal dialogs don't have a backdrop
|
||||
if (event.target !== this._element || !this._config.modal) {
|
||||
return;
|
||||
}
|
||||
if (this._config.backdrop === 'static') {
|
||||
this._triggerBackdropTransition();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: click backdrop to dismiss
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = SelectorEngine.getElementFromSelector(this);
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
EventHandler.one(target, EVENT_SHOW, showEvent => {
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
EventHandler.one(target, EVENT_HIDDEN, () => {
|
||||
if (index_js.isVisible(this)) {
|
||||
this.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Get config from trigger's data attributes
|
||||
const config = Manipulator.getDataAttributes(this);
|
||||
|
||||
// Check if trigger is inside an open dialog
|
||||
const currentDialog = this.closest('dialog[open]');
|
||||
const shouldSwap = currentDialog && currentDialog !== target;
|
||||
if (shouldSwap) {
|
||||
// Open new dialog first (its backdrop appears over current)
|
||||
const newDialog = Dialog.getOrCreateInstance(target, config);
|
||||
newDialog.show(this);
|
||||
|
||||
// Close the current dialog (no backdrop flash since new one is already open)
|
||||
const currentInstance = Dialog.getInstance(currentDialog);
|
||||
if (currentInstance) {
|
||||
currentInstance.hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const data = Dialog.getOrCreateInstance(target, config);
|
||||
data.toggle(this);
|
||||
});
|
||||
componentFunctions_js.enableDismissTrigger(Dialog);
|
||||
|
||||
return Dialog;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=dialog.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap data.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap event-handler.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
@@ -196,7 +196,7 @@
|
||||
for (const [key, value] of Object.entries(meta)) {
|
||||
try {
|
||||
obj[key] = value;
|
||||
} catch (_unused) {
|
||||
} catch {
|
||||
Object.defineProperty(obj, key, {
|
||||
configurable: true,
|
||||
get() {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap manipulator.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
@@ -34,7 +34,7 @@
|
||||
}
|
||||
try {
|
||||
return JSON.parse(decodeURIComponent(value));
|
||||
} catch (_unused) {
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"manipulator.js","sources":["../../src/dom/manipulator.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n"],"names":["normalizeData","value","Number","toString","JSON","parse","decodeURIComponent","_unused","normalizeDataKey","key","replace","chr","toLowerCase","Manipulator","setDataAttribute","element","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","Object","keys","dataset","filter","startsWith","pureKey","charAt","slice","getDataAttribute","getAttribute"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA,SAASA,aAAaA,CAACC,KAAK,EAAE;IAC5B,IAAIA,KAAK,KAAK,MAAM,EAAE;EACpB,IAAA,OAAO,IAAI;EACb,EAAA;IAEA,IAAIA,KAAK,KAAK,OAAO,EAAE;EACrB,IAAA,OAAO,KAAK;EACd,EAAA;IAEA,IAAIA,KAAK,KAAKC,MAAM,CAACD,KAAK,CAAC,CAACE,QAAQ,EAAE,EAAE;MACtC,OAAOD,MAAM,CAACD,KAAK,CAAC;EACtB,EAAA;EAEA,EAAA,IAAIA,KAAK,KAAK,EAAE,IAAIA,KAAK,KAAK,MAAM,EAAE;EACpC,IAAA,OAAO,IAAI;EACb,EAAA;EAEA,EAAA,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;EAC7B,IAAA,OAAOA,KAAK;EACd,EAAA;IAEA,IAAI;MACF,OAAOG,IAAI,CAACC,KAAK,CAACC,kBAAkB,CAACL,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,OAAAM,OAAA,EAAM;EACN,IAAA,OAAON,KAAK;EACd,EAAA;EACF;EAEA,SAASO,gBAAgBA,CAACC,GAAG,EAAE;EAC7B,EAAA,OAAOA,GAAG,CAACC,OAAO,CAAC,QAAQ,EAAEC,GAAG,IAAI,CAAA,CAAA,EAAIA,GAAG,CAACC,WAAW,EAAE,EAAE,CAAC;EAC9D;AAEA,QAAMC,WAAW,GAAG;EAClBC,EAAAA,gBAAgBA,CAACC,OAAO,EAAEN,GAAG,EAAER,KAAK,EAAE;MACpCc,OAAO,CAACC,YAAY,CAAC,CAAA,QAAA,EAAWR,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,EAAER,KAAK,CAAC;IACjE,CAAC;EAEDgB,EAAAA,mBAAmBA,CAACF,OAAO,EAAEN,GAAG,EAAE;MAChCM,OAAO,CAACG,eAAe,CAAC,CAAA,QAAA,EAAWV,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,CAAC;IAC7D,CAAC;IAEDU,iBAAiBA,CAACJ,OAAO,EAAE;MACzB,IAAI,CAACA,OAAO,EAAE;EACZ,MAAA,OAAO,EAAE;EACX,IAAA;MAEA,MAAMK,UAAU,GAAG,EAAE;EACrB,IAAA,MAAMC,MAAM,GAAGC,MAAM,CAACC,IAAI,CAACR,OAAO,CAACS,OAAO,CAAC,CAACC,MAAM,CAAChB,GAAG,IAAIA,GAAG,CAACiB,UAAU,CAAC,IAAI,CAAC,IAAI,CAACjB,GAAG,CAACiB,UAAU,CAAC,UAAU,CAAC,CAAC;EAE9G,IAAA,KAAK,MAAMjB,GAAG,IAAIY,MAAM,EAAE;QACxB,IAAIM,OAAO,GAAGlB,GAAG,CAACC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;EACpCiB,MAAAA,OAAO,GAAGA,OAAO,CAACC,MAAM,CAAC,CAAC,CAAC,CAAChB,WAAW,EAAE,GAAGe,OAAO,CAACE,KAAK,CAAC,CAAC,CAAC;EAC5DT,MAAAA,UAAU,CAACO,OAAO,CAAC,GAAG3B,aAAa,CAACe,OAAO,CAACS,OAAO,CAACf,GAAG,CAAC,CAAC;EAC3D,IAAA;EAEA,IAAA,OAAOW,UAAU;IACnB,CAAC;EAEDU,EAAAA,gBAAgBA,CAACf,OAAO,EAAEN,GAAG,EAAE;EAC7B,IAAA,OAAOT,aAAa,CAACe,OAAO,CAACgB,YAAY,CAAC,CAAA,QAAA,EAAWvB,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,CAAC,CAAC;EAChF,EAAA;EACF;;;;;;;;"}
|
||||
{"version":3,"file":"manipulator.js","sources":["../../src/dom/manipulator.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n"],"names":["normalizeData","value","Number","toString","JSON","parse","decodeURIComponent","normalizeDataKey","key","replace","chr","toLowerCase","Manipulator","setDataAttribute","element","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","Object","keys","dataset","filter","startsWith","pureKey","charAt","slice","getDataAttribute","getAttribute"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA,SAASA,aAAaA,CAACC,KAAK,EAAE;IAC5B,IAAIA,KAAK,KAAK,MAAM,EAAE;EACpB,IAAA,OAAO,IAAI;EACb,EAAA;IAEA,IAAIA,KAAK,KAAK,OAAO,EAAE;EACrB,IAAA,OAAO,KAAK;EACd,EAAA;IAEA,IAAIA,KAAK,KAAKC,MAAM,CAACD,KAAK,CAAC,CAACE,QAAQ,EAAE,EAAE;MACtC,OAAOD,MAAM,CAACD,KAAK,CAAC;EACtB,EAAA;EAEA,EAAA,IAAIA,KAAK,KAAK,EAAE,IAAIA,KAAK,KAAK,MAAM,EAAE;EACpC,IAAA,OAAO,IAAI;EACb,EAAA;EAEA,EAAA,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;EAC7B,IAAA,OAAOA,KAAK;EACd,EAAA;IAEA,IAAI;MACF,OAAOG,IAAI,CAACC,KAAK,CAACC,kBAAkB,CAACL,KAAK,CAAC,CAAC;EAC9C,EAAA,CAAC,CAAC,MAAM;EACN,IAAA,OAAOA,KAAK;EACd,EAAA;EACF;EAEA,SAASM,gBAAgBA,CAACC,GAAG,EAAE;EAC7B,EAAA,OAAOA,GAAG,CAACC,OAAO,CAAC,QAAQ,EAAEC,GAAG,IAAI,CAAA,CAAA,EAAIA,GAAG,CAACC,WAAW,EAAE,EAAE,CAAC;EAC9D;AAEA,QAAMC,WAAW,GAAG;EAClBC,EAAAA,gBAAgBA,CAACC,OAAO,EAAEN,GAAG,EAAEP,KAAK,EAAE;MACpCa,OAAO,CAACC,YAAY,CAAC,CAAA,QAAA,EAAWR,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,EAAEP,KAAK,CAAC;IACjE,CAAC;EAEDe,EAAAA,mBAAmBA,CAACF,OAAO,EAAEN,GAAG,EAAE;MAChCM,OAAO,CAACG,eAAe,CAAC,CAAA,QAAA,EAAWV,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,CAAC;IAC7D,CAAC;IAEDU,iBAAiBA,CAACJ,OAAO,EAAE;MACzB,IAAI,CAACA,OAAO,EAAE;EACZ,MAAA,OAAO,EAAE;EACX,IAAA;MAEA,MAAMK,UAAU,GAAG,EAAE;EACrB,IAAA,MAAMC,MAAM,GAAGC,MAAM,CAACC,IAAI,CAACR,OAAO,CAACS,OAAO,CAAC,CAACC,MAAM,CAAChB,GAAG,IAAIA,GAAG,CAACiB,UAAU,CAAC,IAAI,CAAC,IAAI,CAACjB,GAAG,CAACiB,UAAU,CAAC,UAAU,CAAC,CAAC;EAE9G,IAAA,KAAK,MAAMjB,GAAG,IAAIY,MAAM,EAAE;QACxB,IAAIM,OAAO,GAAGlB,GAAG,CAACC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;EACpCiB,MAAAA,OAAO,GAAGA,OAAO,CAACC,MAAM,CAAC,CAAC,CAAC,CAAChB,WAAW,EAAE,GAAGe,OAAO,CAACE,KAAK,CAAC,CAAC,CAAC;EAC5DT,MAAAA,UAAU,CAACO,OAAO,CAAC,GAAG1B,aAAa,CAACc,OAAO,CAACS,OAAO,CAACf,GAAG,CAAC,CAAC;EAC3D,IAAA;EAEA,IAAA,OAAOW,UAAU;IACnB,CAAC;EAEDU,EAAAA,gBAAgBA,CAACf,OAAO,EAAEN,GAAG,EAAE;EAC7B,IAAA,OAAOR,aAAa,CAACc,OAAO,CAACgB,YAAY,CAAC,CAAA,QAAA,EAAWvB,gBAAgB,CAACC,GAAG,CAAC,CAAA,CAAE,CAAC,CAAC;EAChF,EAAA;EACF;;;;;;;;"}
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap selector-engine.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+607
-124
@@ -1,32 +1,13 @@
|
||||
/*!
|
||||
* Bootstrap dropdown.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global["@popperjs/core"], global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index));
|
||||
})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js) { 'use strict';
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
|
||||
if (e) {
|
||||
for (const k in e) {
|
||||
if (k !== 'default') {
|
||||
const d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: () => e[k]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@floating-ui/dom'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js'), require('./util/floating-ui.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['@floating-ui/dom', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index', './util/floating-ui'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global["@floating-ui/dom"], global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index, global.FloatingUi));
|
||||
})(this, (function (dom, BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js, floatingUi_js) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
@@ -48,8 +29,16 @@
|
||||
const TAB_KEY = 'Tab';
|
||||
const ARROW_UP_KEY = 'ArrowUp';
|
||||
const ARROW_DOWN_KEY = 'ArrowDown';
|
||||
const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button
|
||||
const ARROW_LEFT_KEY = 'ArrowLeft';
|
||||
const ARROW_RIGHT_KEY = 'ArrowRight';
|
||||
const HOME_KEY = 'Home';
|
||||
const END_KEY = 'End';
|
||||
const ENTER_KEY = 'Enter';
|
||||
const SPACE_KEY = ' ';
|
||||
const RIGHT_MOUSE_BUTTON = 2;
|
||||
|
||||
// Hover intent delay (ms) - grace period before closing submenu
|
||||
const SUBMENU_CLOSE_DELAY = 100;
|
||||
const EVENT_HIDE = `hide${EVENT_KEY}`;
|
||||
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
|
||||
const EVENT_SHOW = `show${EVENT_KEY}`;
|
||||
@@ -58,40 +47,54 @@
|
||||
const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const CLASS_NAME_SHOW = 'show';
|
||||
const CLASS_NAME_DROPUP = 'dropup';
|
||||
const CLASS_NAME_DROPEND = 'dropend';
|
||||
const CLASS_NAME_DROPSTART = 'dropstart';
|
||||
const CLASS_NAME_DROPUP_CENTER = 'dropup-center';
|
||||
const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)';
|
||||
const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`;
|
||||
const SELECTOR_MENU = '.dropdown-menu';
|
||||
const SELECTOR_NAVBAR = '.navbar';
|
||||
const SELECTOR_SUBMENU = '.dropdown-submenu';
|
||||
const SELECTOR_SUBMENU_TOGGLE = '.dropdown-submenu > .dropdown-item';
|
||||
const SELECTOR_NAVBAR_NAV = '.navbar-nav';
|
||||
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';
|
||||
const PLACEMENT_TOP = index_js.isRTL() ? 'top-end' : 'top-start';
|
||||
const PLACEMENT_TOPEND = index_js.isRTL() ? 'top-start' : 'top-end';
|
||||
const PLACEMENT_BOTTOM = index_js.isRTL() ? 'bottom-end' : 'bottom-start';
|
||||
const PLACEMENT_BOTTOMEND = index_js.isRTL() ? 'bottom-start' : 'bottom-end';
|
||||
const PLACEMENT_RIGHT = index_js.isRTL() ? 'left-start' : 'right-start';
|
||||
const PLACEMENT_LEFT = index_js.isRTL() ? 'right-start' : 'left-start';
|
||||
const PLACEMENT_TOPCENTER = 'top';
|
||||
const PLACEMENT_BOTTOMCENTER = 'bottom';
|
||||
const SELECTOR_VISIBLE_ITEMS = '.dropdown-item:not(.disabled):not(:disabled)';
|
||||
|
||||
// Default logical placement (uses start/end which get resolved to left/right based on RTL)
|
||||
const DEFAULT_PLACEMENT = 'bottom-start';
|
||||
const SUBMENU_PLACEMENT = 'end-start';
|
||||
|
||||
// Resolve logical placement (start/end) to physical (left/right) based on RTL
|
||||
const resolveLogicalPlacement = placement => {
|
||||
if (index_js.isRTL()) {
|
||||
// RTL: start → right, end → left
|
||||
return placement.replace(/^start(?=-|$)/, 'right').replace(/^end(?=-|$)/, 'left');
|
||||
}
|
||||
|
||||
// LTR: start → left, end → right
|
||||
return placement.replace(/^start(?=-|$)/, 'left').replace(/^end(?=-|$)/, 'right');
|
||||
};
|
||||
|
||||
// Helper for barycentric coordinate calculation (point in triangle check)
|
||||
const triangleSign = (p1, p2, p3) => (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
|
||||
const Default = {
|
||||
autoClose: true,
|
||||
boundary: 'clippingParents',
|
||||
display: 'dynamic',
|
||||
offset: [0, 2],
|
||||
popperConfig: null,
|
||||
reference: 'toggle'
|
||||
floatingConfig: null,
|
||||
placement: DEFAULT_PLACEMENT,
|
||||
reference: 'toggle',
|
||||
// Submenu options
|
||||
submenuTrigger: 'both',
|
||||
// 'click', 'hover', or 'both'
|
||||
submenuDelay: SUBMENU_CLOSE_DELAY
|
||||
};
|
||||
const DefaultType = {
|
||||
autoClose: '(boolean|string)',
|
||||
boundary: '(string|element)',
|
||||
display: 'string',
|
||||
offset: '(array|string|function)',
|
||||
popperConfig: '(null|object|function)',
|
||||
reference: '(string|element|object)'
|
||||
floatingConfig: '(null|object|function)',
|
||||
placement: 'string',
|
||||
reference: '(string|element|object)',
|
||||
submenuTrigger: 'string',
|
||||
submenuDelay: 'number'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -100,12 +103,27 @@
|
||||
|
||||
class Dropdown extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
if (typeof dom.computePosition === 'undefined') {
|
||||
throw new TypeError('Bootstrap\'s dropdowns require Floating UI (https://floating-ui.com)');
|
||||
}
|
||||
super(element, config);
|
||||
this._popper = null;
|
||||
this._floatingCleanup = null;
|
||||
this._mediaQueryListeners = [];
|
||||
this._responsivePlacements = null;
|
||||
this._parent = this._element.parentNode; // dropdown wrapper
|
||||
this._isSubmenu = this._parent.classList.contains('dropdown-submenu');
|
||||
this._openSubmenus = new Map(); // Map of submenu element -> cleanup function
|
||||
this._submenuCloseTimeouts = new Map(); // Map of submenu element -> timeout ID
|
||||
this._hoverIntentData = null; // For safe triangle calculation
|
||||
|
||||
// TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
|
||||
this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);
|
||||
this._inNavbar = this._detectNavbar();
|
||||
|
||||
// Parse responsive placements on init
|
||||
this._parseResponsivePlacements();
|
||||
|
||||
// Set up submenu event listeners
|
||||
this._setupSubmenuListeners();
|
||||
}
|
||||
|
||||
// Getters
|
||||
@@ -134,7 +152,7 @@
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._createPopper();
|
||||
this._createFloating();
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
@@ -146,9 +164,10 @@
|
||||
}
|
||||
}
|
||||
this._element.focus();
|
||||
this._element.setAttribute('aria-expanded', true);
|
||||
this._element.setAttribute('aria-expanded', 'true');
|
||||
this._menu.classList.add(CLASS_NAME_SHOW);
|
||||
this._element.classList.add(CLASS_NAME_SHOW);
|
||||
this._parent.classList.add(CLASS_NAME_SHOW);
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget);
|
||||
}
|
||||
hide() {
|
||||
@@ -161,15 +180,15 @@
|
||||
this._completeHide(relatedTarget);
|
||||
}
|
||||
dispose() {
|
||||
if (this._popper) {
|
||||
this._popper.destroy();
|
||||
}
|
||||
this._disposeFloating();
|
||||
this._disposeMediaQueryListeners();
|
||||
this._closeAllSubmenus();
|
||||
this._clearAllSubmenuTimeouts();
|
||||
super.dispose();
|
||||
}
|
||||
update() {
|
||||
this._inNavbar = this._detectNavbar();
|
||||
if (this._popper) {
|
||||
this._popper.update();
|
||||
if (this._floatingCleanup) {
|
||||
this._updateFloatingPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +199,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Close all open submenus first
|
||||
this._closeAllSubmenus();
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
@@ -187,26 +209,27 @@
|
||||
EventHandler.off(element, 'mouseover', index_js.noop);
|
||||
}
|
||||
}
|
||||
if (this._popper) {
|
||||
this._popper.destroy();
|
||||
}
|
||||
this._disposeFloating();
|
||||
this._menu.classList.remove(CLASS_NAME_SHOW);
|
||||
this._element.classList.remove(CLASS_NAME_SHOW);
|
||||
this._parent.classList.remove(CLASS_NAME_SHOW);
|
||||
this._element.setAttribute('aria-expanded', 'false');
|
||||
Manipulator.removeDataAttribute(this._menu, 'popper');
|
||||
Manipulator.removeDataAttribute(this._menu, 'placement');
|
||||
Manipulator.removeDataAttribute(this._menu, 'display');
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget);
|
||||
}
|
||||
_getConfig(config) {
|
||||
config = super._getConfig(config);
|
||||
if (typeof config.reference === 'object' && !index_js.isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {
|
||||
// Popper virtual elements require a getBoundingClientRect method
|
||||
// Floating UI virtual elements require a getBoundingClientRect method
|
||||
throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
_createPopper() {
|
||||
if (typeof Popper__namespace === 'undefined') {
|
||||
throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org/docs/v2/)');
|
||||
_createFloating() {
|
||||
if (this._config.display === 'static') {
|
||||
Manipulator.setDataAttribute(this._menu, 'display', 'static');
|
||||
return;
|
||||
}
|
||||
let referenceElement = this._element;
|
||||
if (this._config.reference === 'parent') {
|
||||
@@ -216,83 +239,430 @@
|
||||
} else if (typeof this._config.reference === 'object') {
|
||||
referenceElement = this._config.reference;
|
||||
}
|
||||
const popperConfig = this._getPopperConfig();
|
||||
this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig);
|
||||
|
||||
// Initial position update
|
||||
this._updateFloatingPosition(referenceElement);
|
||||
|
||||
// Set up auto-update for scroll/resize
|
||||
this._floatingCleanup = dom.autoUpdate(referenceElement, this._menu, () => this._updateFloatingPosition(referenceElement));
|
||||
}
|
||||
async _updateFloatingPosition(referenceElement = null) {
|
||||
if (!this._menu) {
|
||||
return;
|
||||
}
|
||||
if (!referenceElement) {
|
||||
if (this._config.reference === 'parent') {
|
||||
referenceElement = this._parent;
|
||||
} else if (index_js.isElement(this._config.reference)) {
|
||||
referenceElement = index_js.getElement(this._config.reference);
|
||||
} else if (typeof this._config.reference === 'object') {
|
||||
referenceElement = this._config.reference;
|
||||
} else {
|
||||
referenceElement = this._element;
|
||||
}
|
||||
}
|
||||
const placement = this._getPlacement();
|
||||
const middleware = this._getFloatingMiddleware();
|
||||
const floatingConfig = this._getFloatingConfig(placement, middleware);
|
||||
await this._applyFloatingPosition(referenceElement, this._menu, floatingConfig.placement, floatingConfig.middleware);
|
||||
}
|
||||
_isShown() {
|
||||
return this._menu.classList.contains(CLASS_NAME_SHOW);
|
||||
}
|
||||
_getPlacement() {
|
||||
const parentDropdown = this._parent;
|
||||
if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
|
||||
return PLACEMENT_RIGHT;
|
||||
}
|
||||
if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
|
||||
return PLACEMENT_LEFT;
|
||||
}
|
||||
if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {
|
||||
return PLACEMENT_TOPCENTER;
|
||||
}
|
||||
if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {
|
||||
return PLACEMENT_BOTTOMCENTER;
|
||||
}
|
||||
// If we have responsive placements, find the appropriate one for current viewport
|
||||
const placement = this._responsivePlacements ? floatingUi_js.getResponsivePlacement(this._responsivePlacements, DEFAULT_PLACEMENT) : this._config.placement;
|
||||
|
||||
// We need to trim the value because custom properties can also include spaces
|
||||
const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';
|
||||
if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
|
||||
return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;
|
||||
}
|
||||
return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;
|
||||
// Resolve logical placements (start/end) to physical (left/right) based on RTL
|
||||
return resolveLogicalPlacement(placement);
|
||||
}
|
||||
_detectNavbar() {
|
||||
return this._element.closest(SELECTOR_NAVBAR) !== null;
|
||||
_parseResponsivePlacements() {
|
||||
this._responsivePlacements = floatingUi_js.parseResponsivePlacement(this._config.placement, DEFAULT_PLACEMENT);
|
||||
if (this._responsivePlacements) {
|
||||
this._setupMediaQueryListeners();
|
||||
}
|
||||
}
|
||||
_setupMediaQueryListeners() {
|
||||
this._disposeMediaQueryListeners();
|
||||
this._mediaQueryListeners = floatingUi_js.createBreakpointListeners(() => {
|
||||
if (this._isShown()) {
|
||||
this._updateFloatingPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
_disposeMediaQueryListeners() {
|
||||
floatingUi_js.disposeBreakpointListeners(this._mediaQueryListeners);
|
||||
this._mediaQueryListeners = [];
|
||||
}
|
||||
_getOffset() {
|
||||
const {
|
||||
offset
|
||||
offset: offsetConfig
|
||||
} = this._config;
|
||||
if (typeof offset === 'string') {
|
||||
return offset.split(',').map(value => Number.parseInt(value, 10));
|
||||
if (typeof offsetConfig === 'string') {
|
||||
return offsetConfig.split(',').map(value => Number.parseInt(value, 10));
|
||||
}
|
||||
if (typeof offset === 'function') {
|
||||
return popperData => offset(popperData, this._element);
|
||||
if (typeof offsetConfig === 'function') {
|
||||
// Floating UI passes different args, adapt the interface for offset function callbacks
|
||||
return ({
|
||||
placement,
|
||||
rects
|
||||
}) => {
|
||||
const result = offsetConfig({
|
||||
placement,
|
||||
reference: rects.reference,
|
||||
floating: rects.floating
|
||||
}, this._element);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
return offset;
|
||||
return offsetConfig;
|
||||
}
|
||||
_getPopperConfig() {
|
||||
const defaultBsPopperConfig = {
|
||||
placement: this._getPlacement(),
|
||||
modifiers: [{
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
boundary: this._config.boundary
|
||||
}
|
||||
}, {
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: this._getOffset()
|
||||
}
|
||||
}]
|
||||
_getFloatingMiddleware() {
|
||||
const offsetValue = this._getOffset();
|
||||
const middleware = [
|
||||
// Offset middleware - handles distance from reference
|
||||
dom.offset(typeof offsetValue === 'function' ? offsetValue : {
|
||||
mainAxis: offsetValue[1] || 0,
|
||||
crossAxis: offsetValue[0] || 0
|
||||
}),
|
||||
// Flip middleware - handles fallback placements
|
||||
dom.flip({
|
||||
fallbackPlacements: this._getFallbackPlacements()
|
||||
}),
|
||||
// Shift middleware - prevents overflow
|
||||
dom.shift({
|
||||
boundary: this._config.boundary === 'clippingParents' ? 'clippingAncestors' : this._config.boundary
|
||||
})];
|
||||
return middleware;
|
||||
}
|
||||
_getFallbackPlacements() {
|
||||
// Get appropriate fallback placements based on current placement
|
||||
// Fallbacks should preserve alignment (start/end) when possible
|
||||
const placement = this._getPlacement();
|
||||
|
||||
// Handle all possible Floating UI placements
|
||||
const fallbackMap = {
|
||||
bottom: ['top', 'bottom-start', 'bottom-end', 'top-start', 'top-end'],
|
||||
'bottom-start': ['top-start', 'bottom-end', 'top-end'],
|
||||
'bottom-end': ['top-end', 'bottom-start', 'top-start'],
|
||||
top: ['bottom', 'top-start', 'top-end', 'bottom-start', 'bottom-end'],
|
||||
'top-start': ['bottom-start', 'top-end', 'bottom-end'],
|
||||
'top-end': ['bottom-end', 'top-start', 'bottom-start'],
|
||||
right: ['left', 'right-start', 'right-end', 'left-start', 'left-end'],
|
||||
'right-start': ['left-start', 'right-end', 'left-end', 'top-start', 'bottom-start'],
|
||||
'right-end': ['left-end', 'right-start', 'left-start', 'top-end', 'bottom-end'],
|
||||
left: ['right', 'left-start', 'left-end', 'right-start', 'right-end'],
|
||||
'left-start': ['right-start', 'left-end', 'right-end', 'top-start', 'bottom-start'],
|
||||
'left-end': ['right-end', 'left-start', 'right-start', 'top-end', 'bottom-end']
|
||||
};
|
||||
return fallbackMap[placement] || ['top', 'bottom', 'right', 'left'];
|
||||
}
|
||||
_getFloatingConfig(placement, middleware) {
|
||||
const defaultConfig = {
|
||||
placement,
|
||||
middleware
|
||||
};
|
||||
return {
|
||||
...defaultConfig,
|
||||
...index_js.execute(this._config.floatingConfig, [undefined, defaultConfig])
|
||||
};
|
||||
}
|
||||
_disposeFloating() {
|
||||
if (this._floatingCleanup) {
|
||||
this._floatingCleanup();
|
||||
this._floatingCleanup = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Shared helper for positioning any floating element
|
||||
async _applyFloatingPosition(reference, floating, placement, middleware) {
|
||||
if (!floating.isConnected) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
placement: finalPlacement
|
||||
} = await dom.computePosition(reference, floating, {
|
||||
placement,
|
||||
middleware
|
||||
});
|
||||
if (!floating.isConnected) {
|
||||
return null;
|
||||
}
|
||||
Object.assign(floating.style, {
|
||||
position: 'absolute',
|
||||
left: `${x}px`,
|
||||
top: `${y}px`,
|
||||
margin: '0'
|
||||
});
|
||||
Manipulator.setDataAttribute(floating, 'placement', finalPlacement);
|
||||
return finalPlacement;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Submenu handling
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
_setupSubmenuListeners() {
|
||||
// Set up hover listeners for submenu triggers
|
||||
if (this._config.submenuTrigger === 'hover' || this._config.submenuTrigger === 'both') {
|
||||
EventHandler.on(this._menu, 'mouseenter', SELECTOR_SUBMENU_TOGGLE, event => {
|
||||
this._onSubmenuTriggerEnter(event);
|
||||
});
|
||||
EventHandler.on(this._menu, 'mouseleave', SELECTOR_SUBMENU, event => {
|
||||
this._onSubmenuLeave(event);
|
||||
});
|
||||
|
||||
// Track mouse movement for safe triangle calculation
|
||||
EventHandler.on(this._menu, 'mousemove', event => {
|
||||
this._trackMousePosition(event);
|
||||
});
|
||||
}
|
||||
|
||||
// Set up click listener for submenu triggers
|
||||
if (this._config.submenuTrigger === 'click' || this._config.submenuTrigger === 'both') {
|
||||
EventHandler.on(this._menu, 'click', SELECTOR_SUBMENU_TOGGLE, event => {
|
||||
this._onSubmenuTriggerClick(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
_onSubmenuTriggerEnter(event) {
|
||||
const trigger = event.target.closest(SELECTOR_SUBMENU_TOGGLE);
|
||||
if (!trigger) {
|
||||
return;
|
||||
}
|
||||
const submenuWrapper = trigger.closest(SELECTOR_SUBMENU);
|
||||
const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper);
|
||||
if (!submenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel any pending close timeout for this submenu
|
||||
this._cancelSubmenuCloseTimeout(submenu);
|
||||
|
||||
// Close other open submenus at the same level
|
||||
this._closeSiblingSubmenus(submenuWrapper);
|
||||
|
||||
// Open this submenu
|
||||
this._openSubmenu(trigger, submenu, submenuWrapper);
|
||||
}
|
||||
_onSubmenuLeave(event) {
|
||||
const submenuWrapper = event.target.closest(SELECTOR_SUBMENU);
|
||||
const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper);
|
||||
if (!submenu || !this._openSubmenus.has(submenu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're moving toward the submenu (safe triangle)
|
||||
if (this._isMovingTowardSubmenu(event, submenu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule submenu close with delay
|
||||
this._scheduleSubmenuClose(submenu, submenuWrapper);
|
||||
}
|
||||
_onSubmenuTriggerClick(event) {
|
||||
const trigger = event.target.closest(SELECTOR_SUBMENU_TOGGLE);
|
||||
if (!trigger) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const submenuWrapper = trigger.closest(SELECTOR_SUBMENU);
|
||||
const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper);
|
||||
if (!submenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle submenu
|
||||
if (this._openSubmenus.has(submenu)) {
|
||||
this._closeSubmenu(submenu, submenuWrapper);
|
||||
} else {
|
||||
this._closeSiblingSubmenus(submenuWrapper);
|
||||
this._openSubmenu(trigger, submenu, submenuWrapper);
|
||||
}
|
||||
}
|
||||
_openSubmenu(trigger, submenu, submenuWrapper) {
|
||||
if (this._openSubmenus.has(submenu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set ARIA attributes
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
trigger.setAttribute('aria-haspopup', 'true');
|
||||
|
||||
// Position and show submenu
|
||||
submenu.classList.add(CLASS_NAME_SHOW);
|
||||
submenuWrapper.classList.add(CLASS_NAME_SHOW);
|
||||
|
||||
// Set up Floating UI positioning for submenu
|
||||
const cleanup = this._createSubmenuFloating(trigger, submenu, submenuWrapper);
|
||||
this._openSubmenus.set(submenu, cleanup);
|
||||
|
||||
// Set up mouseenter on submenu to cancel close timeout
|
||||
EventHandler.on(submenu, 'mouseenter', () => {
|
||||
this._cancelSubmenuCloseTimeout(submenu);
|
||||
});
|
||||
}
|
||||
_closeSubmenu(submenu, submenuWrapper) {
|
||||
if (!this._openSubmenus.has(submenu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close any nested submenus first
|
||||
const nestedSubmenus = SelectorEngine.find(`${SELECTOR_SUBMENU} ${SELECTOR_MENU}.${CLASS_NAME_SHOW}`, submenu);
|
||||
for (const nested of nestedSubmenus) {
|
||||
const nestedWrapper = nested.closest(SELECTOR_SUBMENU);
|
||||
this._closeSubmenu(nested, nestedWrapper);
|
||||
}
|
||||
|
||||
// Get the trigger
|
||||
const trigger = SelectorEngine.findOne(SELECTOR_SUBMENU_TOGGLE, submenuWrapper);
|
||||
|
||||
// Clean up Floating UI
|
||||
const cleanup = this._openSubmenus.get(submenu);
|
||||
if (cleanup) {
|
||||
cleanup();
|
||||
}
|
||||
this._openSubmenus.delete(submenu);
|
||||
|
||||
// Remove event listeners
|
||||
EventHandler.off(submenu, 'mouseenter');
|
||||
|
||||
// Update ARIA and visibility
|
||||
if (trigger) {
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
submenu.classList.remove(CLASS_NAME_SHOW);
|
||||
submenuWrapper.classList.remove(CLASS_NAME_SHOW);
|
||||
|
||||
// Clear inline styles
|
||||
submenu.style.position = '';
|
||||
submenu.style.left = '';
|
||||
submenu.style.top = '';
|
||||
submenu.style.margin = '';
|
||||
}
|
||||
_closeAllSubmenus() {
|
||||
for (const [submenu] of this._openSubmenus) {
|
||||
const submenuWrapper = submenu.closest(SELECTOR_SUBMENU);
|
||||
this._closeSubmenu(submenu, submenuWrapper);
|
||||
}
|
||||
}
|
||||
_closeSiblingSubmenus(currentSubmenuWrapper) {
|
||||
// Find all sibling submenu wrappers and close their menus
|
||||
const parent = currentSubmenuWrapper.parentNode;
|
||||
const siblingSubmenus = SelectorEngine.find(`${SELECTOR_SUBMENU} > ${SELECTOR_MENU}.${CLASS_NAME_SHOW}`, parent);
|
||||
for (const siblingMenu of siblingSubmenus) {
|
||||
const siblingWrapper = siblingMenu.closest(SELECTOR_SUBMENU);
|
||||
if (siblingWrapper !== currentSubmenuWrapper) {
|
||||
this._closeSubmenu(siblingMenu, siblingWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
_createSubmenuFloating(trigger, submenu, submenuWrapper) {
|
||||
const referenceElement = submenuWrapper;
|
||||
const placement = resolveLogicalPlacement(SUBMENU_PLACEMENT);
|
||||
const middleware = [dom.offset({
|
||||
mainAxis: 0,
|
||||
crossAxis: -4
|
||||
}), dom.flip({
|
||||
fallbackPlacements: [resolveLogicalPlacement('start-start'), resolveLogicalPlacement('end-end'), resolveLogicalPlacement('start-end')]
|
||||
}), dom.shift({
|
||||
padding: 8
|
||||
})];
|
||||
const updatePosition = () => this._applyFloatingPosition(referenceElement, submenu, placement, middleware);
|
||||
updatePosition();
|
||||
return dom.autoUpdate(referenceElement, submenu, updatePosition);
|
||||
}
|
||||
_scheduleSubmenuClose(submenu, submenuWrapper) {
|
||||
this._cancelSubmenuCloseTimeout(submenu);
|
||||
const timeoutId = setTimeout(() => {
|
||||
this._closeSubmenu(submenu, submenuWrapper);
|
||||
this._submenuCloseTimeouts.delete(submenu);
|
||||
}, this._config.submenuDelay);
|
||||
this._submenuCloseTimeouts.set(submenu, timeoutId);
|
||||
}
|
||||
_cancelSubmenuCloseTimeout(submenu) {
|
||||
const timeoutId = this._submenuCloseTimeouts.get(submenu);
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
this._submenuCloseTimeouts.delete(submenu);
|
||||
}
|
||||
}
|
||||
_clearAllSubmenuTimeouts() {
|
||||
for (const timeoutId of this._submenuCloseTimeouts.values()) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
this._submenuCloseTimeouts.clear();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Hover intent / Safe triangle
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
_trackMousePosition(event) {
|
||||
this._hoverIntentData = {
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
}
|
||||
_isMovingTowardSubmenu(event, submenu) {
|
||||
if (!this._hoverIntentData) {
|
||||
return false;
|
||||
}
|
||||
const submenuRect = submenu.getBoundingClientRect();
|
||||
const currentPos = {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
};
|
||||
const lastPos = {
|
||||
x: this._hoverIntentData.x,
|
||||
y: this._hoverIntentData.y
|
||||
};
|
||||
|
||||
// Disable Popper if we have a static display or Dropdown is in Navbar
|
||||
if (this._inNavbar || this._config.display === 'static') {
|
||||
Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove
|
||||
defaultBsPopperConfig.modifiers = [{
|
||||
name: 'applyStyles',
|
||||
enabled: false
|
||||
}];
|
||||
}
|
||||
return {
|
||||
...defaultBsPopperConfig,
|
||||
...index_js.execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
|
||||
// Create a triangle from current position to submenu edges
|
||||
// The triangle represents the "safe zone" for diagonal movement
|
||||
const isRtl = index_js.isRTL();
|
||||
|
||||
// Determine which edge of the submenu to target based on direction
|
||||
const targetX = isRtl ? submenuRect.right : submenuRect.left;
|
||||
const topCorner = {
|
||||
x: targetX,
|
||||
y: submenuRect.top
|
||||
};
|
||||
const bottomCorner = {
|
||||
x: targetX,
|
||||
y: submenuRect.bottom
|
||||
};
|
||||
|
||||
// Check if cursor is moving toward the submenu
|
||||
// by checking if the current position is within the safe triangle
|
||||
return this._pointInTriangle(currentPos, lastPos, topCorner, bottomCorner);
|
||||
}
|
||||
_pointInTriangle(point, v1, v2, v3) {
|
||||
// Barycentric coordinate method to check if point is inside triangle
|
||||
const d1 = triangleSign(point, v1, v2);
|
||||
const d2 = triangleSign(point, v2, v3);
|
||||
const d3 = triangleSign(point, v3, v1);
|
||||
const hasNeg = d1 < 0 || d2 < 0 || d3 < 0;
|
||||
const hasPos = d1 > 0 || d2 > 0 || d3 > 0;
|
||||
return !(hasNeg && hasPos);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Keyboard navigation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
_selectMenuItem({
|
||||
key,
|
||||
target
|
||||
}) {
|
||||
const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => index_js.isVisible(element));
|
||||
// Get items only from the current menu level (not nested submenus)
|
||||
// If target is inside a menu, use that menu; otherwise use the main menu
|
||||
const currentMenu = target.closest(SELECTOR_MENU) || this._menu;
|
||||
const items = SelectorEngine.find(`:scope > li > ${SELECTOR_VISIBLE_ITEMS}, :scope > ${SELECTOR_VISIBLE_ITEMS}`, currentMenu).filter(element => index_js.isVisible(element));
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
@@ -301,6 +671,89 @@
|
||||
// allow cycling to get the last item in case key equals ARROW_UP_KEY
|
||||
index_js.getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus();
|
||||
}
|
||||
_handleSubmenuKeydown(event) {
|
||||
const {
|
||||
key,
|
||||
target
|
||||
} = event;
|
||||
const isRtl = index_js.isRTL();
|
||||
|
||||
// Determine the "enter submenu" and "exit submenu" keys based on RTL
|
||||
const enterKey = isRtl ? ARROW_LEFT_KEY : ARROW_RIGHT_KEY;
|
||||
const exitKey = isRtl ? ARROW_RIGHT_KEY : ARROW_LEFT_KEY;
|
||||
|
||||
// Check if target is a submenu trigger
|
||||
const submenuWrapper = target.closest(SELECTOR_SUBMENU);
|
||||
const isSubmenuTrigger = submenuWrapper && target.matches(SELECTOR_SUBMENU_TOGGLE);
|
||||
|
||||
// Handle Enter/Space on submenu trigger
|
||||
if ((key === ENTER_KEY || key === SPACE_KEY) && isSubmenuTrigger) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper);
|
||||
if (submenu) {
|
||||
this._closeSiblingSubmenus(submenuWrapper);
|
||||
this._openSubmenu(target, submenu, submenuWrapper);
|
||||
// Focus first item in submenu
|
||||
requestAnimationFrame(() => {
|
||||
const firstItem = SelectorEngine.findOne(SELECTOR_VISIBLE_ITEMS, submenu);
|
||||
if (firstItem) {
|
||||
firstItem.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle Right arrow (or Left in RTL) - enter submenu
|
||||
if (key === enterKey && isSubmenuTrigger) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const submenu = SelectorEngine.findOne(SELECTOR_MENU, submenuWrapper);
|
||||
if (submenu) {
|
||||
this._closeSiblingSubmenus(submenuWrapper);
|
||||
this._openSubmenu(target, submenu, submenuWrapper);
|
||||
// Focus first item in submenu
|
||||
requestAnimationFrame(() => {
|
||||
const firstItem = SelectorEngine.findOne(SELECTOR_VISIBLE_ITEMS, submenu);
|
||||
if (firstItem) {
|
||||
firstItem.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle Left arrow (or Right in RTL) - exit submenu
|
||||
if (key === exitKey) {
|
||||
const currentMenu = target.closest(SELECTOR_MENU);
|
||||
const parentSubmenuWrapper = currentMenu?.closest(SELECTOR_SUBMENU);
|
||||
if (parentSubmenuWrapper) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const parentTrigger = SelectorEngine.findOne(SELECTOR_SUBMENU_TOGGLE, parentSubmenuWrapper);
|
||||
this._closeSubmenu(currentMenu, parentSubmenuWrapper);
|
||||
if (parentTrigger) {
|
||||
parentTrigger.focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Home/End keys
|
||||
if (key === HOME_KEY || key === END_KEY) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const currentMenu = target.closest(SELECTOR_MENU);
|
||||
const items = SelectorEngine.find(`:scope > li > ${SELECTOR_VISIBLE_ITEMS}, :scope > ${SELECTOR_VISIBLE_ITEMS}`, currentMenu).filter(element => index_js.isVisible(element));
|
||||
if (items.length) {
|
||||
const targetItem = key === HOME_KEY ? items[0] : items[items.length - 1];
|
||||
targetItem.focus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static clearMenus(event) {
|
||||
if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY) {
|
||||
return;
|
||||
@@ -331,32 +784,62 @@
|
||||
}
|
||||
}
|
||||
static dataApiKeydownHandler(event) {
|
||||
// If not an UP | DOWN | ESCAPE key => not a dropdown command
|
||||
// If input/textarea && if key is other than ESCAPE => not a dropdown command
|
||||
|
||||
// If not a relevant key => not a dropdown command
|
||||
const isInput = /input|textarea/i.test(event.target.tagName);
|
||||
const isEscapeEvent = event.key === ESCAPE_KEY;
|
||||
const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key);
|
||||
if (!isUpOrDownEvent && !isEscapeEvent) {
|
||||
const isLeftOrRightEvent = [ARROW_LEFT_KEY, ARROW_RIGHT_KEY].includes(event.key);
|
||||
const isHomeOrEndEvent = [HOME_KEY, END_KEY].includes(event.key);
|
||||
const isEnterOrSpaceEvent = [ENTER_KEY, SPACE_KEY].includes(event.key);
|
||||
|
||||
// Allow Enter/Space only on submenu triggers
|
||||
const isSubmenuTrigger = event.target.matches(SELECTOR_SUBMENU_TOGGLE);
|
||||
if (!isUpOrDownEvent && !isEscapeEvent && !isLeftOrRightEvent && !isHomeOrEndEvent && !(isEnterOrSpaceEvent && isSubmenuTrigger)) {
|
||||
return;
|
||||
}
|
||||
if (isInput && !isEscapeEvent) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
// TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
|
||||
const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode);
|
||||
if (!getToggleButton) {
|
||||
return;
|
||||
}
|
||||
const instance = Dropdown.getOrCreateInstance(getToggleButton);
|
||||
|
||||
// Handle submenu navigation first
|
||||
if ((isLeftOrRightEvent || isHomeOrEndEvent || isEnterOrSpaceEvent && isSubmenuTrigger) && instance._handleSubmenuKeydown(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Up/Down navigation
|
||||
if (isUpOrDownEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
instance.show();
|
||||
instance._selectMenuItem(event);
|
||||
return;
|
||||
}
|
||||
if (instance._isShown()) {
|
||||
// else is escape and we check if it is shown
|
||||
|
||||
// Handle Escape
|
||||
if (isEscapeEvent && instance._isShown()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// If in a submenu, close just that submenu
|
||||
const currentMenu = event.target.closest(SELECTOR_MENU);
|
||||
const parentSubmenuWrapper = currentMenu?.closest(SELECTOR_SUBMENU);
|
||||
if (parentSubmenuWrapper && instance._openSubmenus.size > 0) {
|
||||
const parentTrigger = SelectorEngine.findOne(SELECTOR_SUBMENU_TOGGLE, parentSubmenuWrapper);
|
||||
instance._closeSubmenu(currentMenu, parentSubmenuWrapper);
|
||||
if (parentTrigger) {
|
||||
parentTrigger.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise close the whole dropdown
|
||||
instance.hide();
|
||||
getToggleButton.focus();
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-300
@@ -1,300 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap modal.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar));
|
||||
})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap modal.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'modal';
|
||||
const DATA_KEY = 'bs.modal';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const DATA_API_KEY = '.data-api';
|
||||
const ESCAPE_KEY = 'Escape';
|
||||
const EVENT_HIDE = `hide${EVENT_KEY}`;
|
||||
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
|
||||
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
|
||||
const EVENT_SHOW = `show${EVENT_KEY}`;
|
||||
const EVENT_SHOWN = `shown${EVENT_KEY}`;
|
||||
const EVENT_RESIZE = `resize${EVENT_KEY}`;
|
||||
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
|
||||
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;
|
||||
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
|
||||
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
|
||||
const CLASS_NAME_OPEN = 'modal-open';
|
||||
const CLASS_NAME_FADE = 'fade';
|
||||
const CLASS_NAME_SHOW = 'show';
|
||||
const CLASS_NAME_STATIC = 'modal-static';
|
||||
const OPEN_SELECTOR = '.modal.show';
|
||||
const SELECTOR_DIALOG = '.modal-dialog';
|
||||
const SELECTOR_MODAL_BODY = '.modal-body';
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
|
||||
const Default = {
|
||||
backdrop: true,
|
||||
focus: true,
|
||||
keyboard: true
|
||||
};
|
||||
const DefaultType = {
|
||||
backdrop: '(boolean|string)',
|
||||
focus: 'boolean',
|
||||
keyboard: 'boolean'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class Modal extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config);
|
||||
this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);
|
||||
this._backdrop = this._initializeBackDrop();
|
||||
this._focustrap = this._initializeFocusTrap();
|
||||
this._isShown = false;
|
||||
this._isTransitioning = false;
|
||||
this._scrollBar = new ScrollBarHelper();
|
||||
this._addEventListeners();
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
toggle(relatedTarget) {
|
||||
return this._isShown ? this.hide() : this.show(relatedTarget);
|
||||
}
|
||||
show(relatedTarget) {
|
||||
if (this._isShown || this._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
|
||||
relatedTarget
|
||||
});
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._isShown = true;
|
||||
this._isTransitioning = true;
|
||||
this._scrollBar.hide();
|
||||
document.body.classList.add(CLASS_NAME_OPEN);
|
||||
this._adjustDialog();
|
||||
this._backdrop.show(() => this._showElement(relatedTarget));
|
||||
}
|
||||
hide() {
|
||||
if (!this._isShown || this._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._isShown = false;
|
||||
this._isTransitioning = true;
|
||||
this._focustrap.deactivate();
|
||||
this._element.classList.remove(CLASS_NAME_SHOW);
|
||||
this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());
|
||||
}
|
||||
dispose() {
|
||||
EventHandler.off(window, EVENT_KEY);
|
||||
EventHandler.off(this._dialog, EVENT_KEY);
|
||||
this._backdrop.dispose();
|
||||
this._focustrap.deactivate();
|
||||
super.dispose();
|
||||
}
|
||||
handleUpdate() {
|
||||
this._adjustDialog();
|
||||
}
|
||||
|
||||
// Private
|
||||
_initializeBackDrop() {
|
||||
return new Backdrop({
|
||||
isVisible: Boolean(this._config.backdrop),
|
||||
// 'static' option will be translated to true, and booleans will keep their value,
|
||||
isAnimated: this._isAnimated()
|
||||
});
|
||||
}
|
||||
_initializeFocusTrap() {
|
||||
return new FocusTrap({
|
||||
trapElement: this._element
|
||||
});
|
||||
}
|
||||
_showElement(relatedTarget) {
|
||||
// try to append dynamic modal
|
||||
if (!document.body.contains(this._element)) {
|
||||
document.body.append(this._element);
|
||||
}
|
||||
this._element.style.display = 'block';
|
||||
this._element.removeAttribute('aria-hidden');
|
||||
this._element.setAttribute('aria-modal', true);
|
||||
this._element.setAttribute('role', 'dialog');
|
||||
this._element.scrollTop = 0;
|
||||
const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);
|
||||
if (modalBody) {
|
||||
modalBody.scrollTop = 0;
|
||||
}
|
||||
index_js.reflow(this._element);
|
||||
this._element.classList.add(CLASS_NAME_SHOW);
|
||||
const transitionComplete = () => {
|
||||
if (this._config.focus) {
|
||||
this._focustrap.activate();
|
||||
}
|
||||
this._isTransitioning = false;
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN, {
|
||||
relatedTarget
|
||||
});
|
||||
};
|
||||
this._queueCallback(transitionComplete, this._dialog, this._isAnimated());
|
||||
}
|
||||
_addEventListeners() {
|
||||
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
|
||||
if (event.key !== ESCAPE_KEY) {
|
||||
return;
|
||||
}
|
||||
if (this._config.keyboard) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
this._triggerBackdropTransition();
|
||||
});
|
||||
EventHandler.on(window, EVENT_RESIZE, () => {
|
||||
if (this._isShown && !this._isTransitioning) {
|
||||
this._adjustDialog();
|
||||
}
|
||||
});
|
||||
EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
|
||||
// a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
|
||||
EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
|
||||
if (this._element !== event.target || this._element !== event2.target) {
|
||||
return;
|
||||
}
|
||||
if (this._config.backdrop === 'static') {
|
||||
this._triggerBackdropTransition();
|
||||
return;
|
||||
}
|
||||
if (this._config.backdrop) {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
_hideModal() {
|
||||
this._element.style.display = 'none';
|
||||
this._element.setAttribute('aria-hidden', true);
|
||||
this._element.removeAttribute('aria-modal');
|
||||
this._element.removeAttribute('role');
|
||||
this._isTransitioning = false;
|
||||
this._backdrop.hide(() => {
|
||||
document.body.classList.remove(CLASS_NAME_OPEN);
|
||||
this._resetAdjustments();
|
||||
this._scrollBar.reset();
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN);
|
||||
});
|
||||
}
|
||||
_isAnimated() {
|
||||
return this._element.classList.contains(CLASS_NAME_FADE);
|
||||
}
|
||||
_triggerBackdropTransition() {
|
||||
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
|
||||
const initialOverflowY = this._element.style.overflowY;
|
||||
// return if the following background transition hasn't yet completed
|
||||
if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {
|
||||
return;
|
||||
}
|
||||
if (!isModalOverflowing) {
|
||||
this._element.style.overflowY = 'hidden';
|
||||
}
|
||||
this._element.classList.add(CLASS_NAME_STATIC);
|
||||
this._queueCallback(() => {
|
||||
this._element.classList.remove(CLASS_NAME_STATIC);
|
||||
this._queueCallback(() => {
|
||||
this._element.style.overflowY = initialOverflowY;
|
||||
}, this._dialog);
|
||||
}, this._dialog);
|
||||
this._element.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* The following methods are used to handle overflowing modals
|
||||
*/
|
||||
|
||||
_adjustDialog() {
|
||||
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
|
||||
const scrollbarWidth = this._scrollBar.getWidth();
|
||||
const isBodyOverflowing = scrollbarWidth > 0;
|
||||
if (isBodyOverflowing && !isModalOverflowing) {
|
||||
const property = index_js.isRTL() ? 'paddingLeft' : 'paddingRight';
|
||||
this._element.style[property] = `${scrollbarWidth}px`;
|
||||
}
|
||||
if (!isBodyOverflowing && isModalOverflowing) {
|
||||
const property = index_js.isRTL() ? 'paddingRight' : 'paddingLeft';
|
||||
this._element.style[property] = `${scrollbarWidth}px`;
|
||||
}
|
||||
}
|
||||
_resetAdjustments() {
|
||||
this._element.style.paddingLeft = '';
|
||||
this._element.style.paddingRight = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = SelectorEngine.getElementFromSelector(this);
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
EventHandler.one(target, EVENT_SHOW, showEvent => {
|
||||
if (showEvent.defaultPrevented) {
|
||||
// only register focus restorer if modal will actually get shown
|
||||
return;
|
||||
}
|
||||
EventHandler.one(target, EVENT_HIDDEN, () => {
|
||||
if (index_js.isVisible(this)) {
|
||||
this.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// avoid conflict when clicking modal toggler while another one is open
|
||||
const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);
|
||||
if (alreadyOpen) {
|
||||
Modal.getInstance(alreadyOpen).hide();
|
||||
}
|
||||
const data = Modal.getOrCreateInstance(target);
|
||||
data.toggle(this);
|
||||
});
|
||||
componentFunctions_js.enableDismissTrigger(Modal);
|
||||
|
||||
return Modal;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=modal.js.map
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap offcanvas.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
/*!
|
||||
* Bootstrap otp-input.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.OtpInput = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine));
|
||||
})(this, (function (BaseComponent, EventHandler, SelectorEngine) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap otp-input.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'otpInput';
|
||||
const DATA_KEY = 'bs.otp-input';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const DATA_API_KEY = '.data-api';
|
||||
const EVENT_COMPLETE = `complete${EVENT_KEY}`;
|
||||
const EVENT_INPUT = `input${EVENT_KEY}`;
|
||||
const SELECTOR_DATA_OTP = '[data-bs-otp]';
|
||||
const SELECTOR_INPUT = 'input';
|
||||
const Default = {
|
||||
length: 6,
|
||||
mask: false
|
||||
};
|
||||
const DefaultType = {
|
||||
length: 'number',
|
||||
mask: 'boolean'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class OtpInput extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config);
|
||||
this._inputs = SelectorEngine.find(SELECTOR_INPUT, this._element);
|
||||
this._setupInputs();
|
||||
this._addEventListeners();
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
getValue() {
|
||||
return this._inputs.map(input => input.value).join('');
|
||||
}
|
||||
setValue(value) {
|
||||
const chars = String(value).split('');
|
||||
for (const [index, input] of this._inputs.entries()) {
|
||||
input.value = chars[index] || '';
|
||||
}
|
||||
this._checkComplete();
|
||||
}
|
||||
clear() {
|
||||
for (const input of this._inputs) {
|
||||
input.value = '';
|
||||
}
|
||||
this._inputs[0]?.focus();
|
||||
}
|
||||
focus() {
|
||||
// Focus first empty input, or last input if all filled
|
||||
const emptyInput = this._inputs.find(input => !input.value);
|
||||
if (emptyInput) {
|
||||
emptyInput.focus();
|
||||
} else {
|
||||
this._inputs.at(-1)?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
_setupInputs() {
|
||||
for (const input of this._inputs) {
|
||||
// Set attributes for proper OTP handling
|
||||
input.setAttribute('maxlength', '1');
|
||||
input.setAttribute('inputmode', 'numeric');
|
||||
input.setAttribute('pattern', '\\d*');
|
||||
|
||||
// First input gets autocomplete for browser OTP autofill
|
||||
if (input === this._inputs[0]) {
|
||||
input.setAttribute('autocomplete', 'one-time-code');
|
||||
} else {
|
||||
input.setAttribute('autocomplete', 'off');
|
||||
}
|
||||
|
||||
// Mask input if configured
|
||||
if (this._config.mask) {
|
||||
input.setAttribute('type', 'password');
|
||||
}
|
||||
}
|
||||
}
|
||||
_addEventListeners() {
|
||||
for (const [index, input] of this._inputs.entries()) {
|
||||
EventHandler.on(input, 'input', event => this._handleInput(event, index));
|
||||
EventHandler.on(input, 'keydown', event => this._handleKeydown(event, index));
|
||||
EventHandler.on(input, 'paste', event => this._handlePaste(event));
|
||||
EventHandler.on(input, 'focus', event => this._handleFocus(event));
|
||||
}
|
||||
}
|
||||
_handleInput(event, index) {
|
||||
const input = event.target;
|
||||
|
||||
// Only allow digits
|
||||
if (!/^\d*$/.test(input.value)) {
|
||||
input.value = input.value.replace(/\D/g, '');
|
||||
}
|
||||
const {
|
||||
value
|
||||
} = input;
|
||||
|
||||
// Handle multi-character input (some browsers/autofill)
|
||||
if (value.length > 1) {
|
||||
// Distribute characters across inputs
|
||||
const chars = value.split('');
|
||||
input.value = chars[0] || '';
|
||||
for (let i = 1; i < chars.length && index + i < this._inputs.length; i++) {
|
||||
this._inputs[index + i].value = chars[i];
|
||||
}
|
||||
|
||||
// Focus appropriate input
|
||||
const nextIndex = Math.min(index + chars.length, this._inputs.length - 1);
|
||||
this._inputs[nextIndex].focus();
|
||||
} else if (value && index < this._inputs.length - 1) {
|
||||
// Auto-advance to next input
|
||||
this._inputs[index + 1].focus();
|
||||
}
|
||||
EventHandler.trigger(this._element, EVENT_INPUT, {
|
||||
value: this.getValue(),
|
||||
index
|
||||
});
|
||||
this._checkComplete();
|
||||
}
|
||||
_handleKeydown(event, index) {
|
||||
const {
|
||||
key
|
||||
} = event;
|
||||
switch (key) {
|
||||
case 'Backspace':
|
||||
{
|
||||
if (!this._inputs[index].value && index > 0) {
|
||||
// Move to previous input and clear it
|
||||
event.preventDefault();
|
||||
this._inputs[index - 1].value = '';
|
||||
this._inputs[index - 1].focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Delete':
|
||||
{
|
||||
// Clear current and shift remaining values left
|
||||
event.preventDefault();
|
||||
for (let i = index; i < this._inputs.length - 1; i++) {
|
||||
this._inputs[i].value = this._inputs[i + 1].value;
|
||||
}
|
||||
this._inputs.at(-1).value = '';
|
||||
break;
|
||||
}
|
||||
case 'ArrowLeft':
|
||||
{
|
||||
if (index > 0) {
|
||||
event.preventDefault();
|
||||
this._inputs[index - 1].focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ArrowRight':
|
||||
{
|
||||
if (index < this._inputs.length - 1) {
|
||||
event.preventDefault();
|
||||
this._inputs[index + 1].focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// No default
|
||||
}
|
||||
}
|
||||
_handlePaste(event) {
|
||||
event.preventDefault();
|
||||
const pastedData = (event.clipboardData || window.clipboardData).getData('text');
|
||||
const digits = pastedData.replace(/\D/g, '').slice(0, this._inputs.length);
|
||||
if (digits) {
|
||||
this.setValue(digits);
|
||||
|
||||
// Focus last filled input or last input
|
||||
const lastIndex = Math.min(digits.length, this._inputs.length) - 1;
|
||||
this._inputs[lastIndex].focus();
|
||||
}
|
||||
}
|
||||
_handleFocus(event) {
|
||||
// Select the content on focus for easy replacement
|
||||
event.target.select();
|
||||
}
|
||||
_checkComplete() {
|
||||
const value = this.getValue();
|
||||
const isComplete = value.length === this._inputs.length && this._inputs.every(input => input.value !== '');
|
||||
if (isComplete) {
|
||||
EventHandler.trigger(this._element, EVENT_COMPLETE, {
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, `DOMContentLoaded${EVENT_KEY}${DATA_API_KEY}`, () => {
|
||||
for (const element of SelectorEngine.find(SELECTOR_DATA_OTP)) {
|
||||
OtpInput.getOrCreateInstance(element);
|
||||
}
|
||||
});
|
||||
|
||||
return OtpInput;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=otp-input.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+41
-5
@@ -1,13 +1,13 @@
|
||||
/*!
|
||||
* Bootstrap popover.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./tooltip.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./tooltip'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Popover = factory(global.Tooltip));
|
||||
})(this, (function (Tooltip) { 'use strict';
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./tooltip.js'), require('./dom/event-handler.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./tooltip', './dom/event-handler'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Popover = factory(global.Tooltip, global.EventHandler));
|
||||
})(this, (function (Tooltip, EventHandler) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
@@ -24,6 +24,10 @@
|
||||
const NAME = 'popover';
|
||||
const SELECTOR_TITLE = '.popover-header';
|
||||
const SELECTOR_CONTENT = '.popover-body';
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="popover"]';
|
||||
const EVENT_CLICK = 'click';
|
||||
const EVENT_FOCUSIN = 'focusin';
|
||||
const EVENT_MOUSEENTER = 'mouseenter';
|
||||
const Default = {
|
||||
...Tooltip.Default,
|
||||
content: '',
|
||||
@@ -70,6 +74,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation - auto-initialize popovers
|
||||
*/
|
||||
|
||||
const initPopover = event => {
|
||||
const target = event.target.closest(SELECTOR_DATA_TOGGLE);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default for click events to avoid navigation
|
||||
if (event.type === 'click') {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Get or create instance
|
||||
const popover = Popover.getOrCreateInstance(target);
|
||||
|
||||
// Trigger the appropriate action based on event type
|
||||
if (event.type === 'click') {
|
||||
popover.toggle();
|
||||
} else if (event.type === 'focusin') {
|
||||
popover._activeTrigger.focus = true;
|
||||
popover._enter();
|
||||
}
|
||||
};
|
||||
|
||||
// Support click (default), hover, and focus triggers
|
||||
EventHandler.on(document, EVENT_CLICK, SELECTOR_DATA_TOGGLE, initPopover);
|
||||
EventHandler.on(document, EVENT_FOCUSIN, SELECTOR_DATA_TOGGLE, initPopover);
|
||||
EventHandler.on(document, EVENT_MOUSEENTER, SELECTOR_DATA_TOGGLE, initPopover);
|
||||
|
||||
return Popover;
|
||||
|
||||
}));
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap scrollspy.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+245
@@ -0,0 +1,245 @@
|
||||
/*!
|
||||
* Bootstrap strength.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Strength = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine));
|
||||
})(this, (function (BaseComponent, EventHandler, SelectorEngine) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap strength.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'strength';
|
||||
const DATA_KEY = 'bs.strength';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const DATA_API_KEY = '.data-api';
|
||||
const EVENT_STRENGTH_CHANGE = `strengthChange${EVENT_KEY}`;
|
||||
const SELECTOR_DATA_STRENGTH = '[data-bs-strength]';
|
||||
const STRENGTH_LEVELS = ['weak', 'fair', 'good', 'strong'];
|
||||
const Default = {
|
||||
input: null,
|
||||
// Selector or element for password input
|
||||
minLength: 8,
|
||||
messages: {
|
||||
weak: 'Weak',
|
||||
fair: 'Fair',
|
||||
good: 'Good',
|
||||
strong: 'Strong'
|
||||
},
|
||||
weights: {
|
||||
minLength: 1,
|
||||
extraLength: 1,
|
||||
lowercase: 1,
|
||||
uppercase: 1,
|
||||
numbers: 1,
|
||||
special: 1,
|
||||
multipleSpecial: 1,
|
||||
longPassword: 1
|
||||
},
|
||||
thresholds: [2, 4, 6],
|
||||
// weak ≤2, fair ≤4, good ≤6, strong >6
|
||||
scorer: null // Custom scoring function (password) => number
|
||||
};
|
||||
const DefaultType = {
|
||||
input: '(string|element|null)',
|
||||
minLength: 'number',
|
||||
messages: 'object',
|
||||
weights: 'object',
|
||||
thresholds: 'array',
|
||||
scorer: '(function|null)'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class Strength extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config);
|
||||
this._input = this._getInput();
|
||||
this._segments = SelectorEngine.find('.strength-segment', this._element);
|
||||
this._textElement = SelectorEngine.findOne('.strength-text', this._element.parentElement);
|
||||
this._currentStrength = null;
|
||||
if (this._input) {
|
||||
this._addEventListeners();
|
||||
// Check initial value
|
||||
this._evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
getStrength() {
|
||||
return this._currentStrength;
|
||||
}
|
||||
evaluate() {
|
||||
this._evaluate();
|
||||
}
|
||||
|
||||
// Private
|
||||
_getInput() {
|
||||
if (this._config.input) {
|
||||
return typeof this._config.input === 'string' ? SelectorEngine.findOne(this._config.input) : this._config.input;
|
||||
}
|
||||
|
||||
// Look for preceding password input
|
||||
const parent = this._element.parentElement;
|
||||
return SelectorEngine.findOne('input[type="password"]', parent);
|
||||
}
|
||||
_addEventListeners() {
|
||||
EventHandler.on(this._input, 'input', () => this._evaluate());
|
||||
EventHandler.on(this._input, 'change', () => this._evaluate());
|
||||
}
|
||||
_evaluate() {
|
||||
const password = this._input.value;
|
||||
const score = this._calculateScore(password);
|
||||
const strength = this._scoreToStrength(score);
|
||||
if (strength !== this._currentStrength) {
|
||||
this._currentStrength = strength;
|
||||
this._updateUI(strength, score);
|
||||
EventHandler.trigger(this._element, EVENT_STRENGTH_CHANGE, {
|
||||
strength,
|
||||
score,
|
||||
password: password.length > 0 ? '***' : '' // Don't expose actual password
|
||||
});
|
||||
}
|
||||
}
|
||||
_calculateScore(password) {
|
||||
if (!password) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use custom scorer if provided
|
||||
if (typeof this._config.scorer === 'function') {
|
||||
return this._config.scorer(password);
|
||||
}
|
||||
const {
|
||||
weights
|
||||
} = this._config;
|
||||
let score = 0;
|
||||
|
||||
// Length scoring
|
||||
if (password.length >= this._config.minLength) {
|
||||
score += weights.minLength;
|
||||
}
|
||||
if (password.length >= this._config.minLength + 4) {
|
||||
score += weights.extraLength;
|
||||
}
|
||||
|
||||
// Character variety
|
||||
if (/[a-z]/.test(password)) {
|
||||
score += weights.lowercase;
|
||||
}
|
||||
if (/[A-Z]/.test(password)) {
|
||||
score += weights.uppercase;
|
||||
}
|
||||
if (/\d/.test(password)) {
|
||||
score += weights.numbers;
|
||||
}
|
||||
|
||||
// Special characters
|
||||
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
||||
score += weights.special;
|
||||
}
|
||||
|
||||
// Extra points for more special chars or length
|
||||
if (/[!@#$%^&*(),.?":{}|<>].*[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
||||
score += weights.multipleSpecial;
|
||||
}
|
||||
if (password.length >= 16) {
|
||||
score += weights.longPassword;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
_scoreToStrength(score) {
|
||||
if (score === 0) {
|
||||
return null;
|
||||
}
|
||||
const [weak, fair, good] = this._config.thresholds;
|
||||
if (score <= weak) {
|
||||
return 'weak';
|
||||
}
|
||||
if (score <= fair) {
|
||||
return 'fair';
|
||||
}
|
||||
if (score <= good) {
|
||||
return 'good';
|
||||
}
|
||||
return 'strong';
|
||||
}
|
||||
_updateUI(strength) {
|
||||
// Update data attribute on element
|
||||
if (strength) {
|
||||
this._element.dataset.bsStrength = strength;
|
||||
} else {
|
||||
delete this._element.dataset.bsStrength;
|
||||
}
|
||||
|
||||
// Update segmented meter
|
||||
const strengthIndex = strength ? STRENGTH_LEVELS.indexOf(strength) : -1;
|
||||
for (const [index, segment] of this._segments.entries()) {
|
||||
if (index <= strengthIndex) {
|
||||
segment.classList.add('active');
|
||||
} else {
|
||||
segment.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Update text feedback
|
||||
if (this._textElement) {
|
||||
if (strength && this._config.messages[strength]) {
|
||||
this._textElement.textContent = this._config.messages[strength];
|
||||
this._textElement.dataset.bsStrength = strength;
|
||||
|
||||
// Also set the color via inheriting from parent or using CSS variable
|
||||
const colorMap = {
|
||||
weak: 'danger',
|
||||
fair: 'warning',
|
||||
good: 'info',
|
||||
strong: 'success'
|
||||
};
|
||||
this._textElement.style.setProperty('--strength-color', `var(--${colorMap[strength]}-text)`);
|
||||
} else {
|
||||
this._textElement.textContent = '';
|
||||
delete this._textElement.dataset.bsStrength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, `DOMContentLoaded${EVENT_KEY}${DATA_API_KEY}`, () => {
|
||||
for (const element of SelectorEngine.find(SELECTOR_DATA_STRENGTH)) {
|
||||
Strength.getOrCreateInstance(element);
|
||||
}
|
||||
});
|
||||
|
||||
return Strength;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=strength.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap tab.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap toast.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
* Bootstrap toggler.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Toggler = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions));
|
||||
})(this, (function (BaseComponent, EventHandler, componentFunctions_js) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap toggler.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'toggler';
|
||||
const DATA_KEY = 'bs.toggler';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const EVENT_TOGGLE = `toggle${EVENT_KEY}`;
|
||||
const EVENT_TOGGLED = `toggled${EVENT_KEY}`;
|
||||
const EVENT_CLICK = 'click';
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="toggler"]';
|
||||
const DefaultType = {
|
||||
attribute: 'string',
|
||||
value: '(string|number|boolean)'
|
||||
};
|
||||
const Default = {
|
||||
attribute: 'class',
|
||||
value: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class Toggler extends BaseComponent {
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
// Public
|
||||
toggle() {
|
||||
const toggleEvent = EventHandler.trigger(this._element, EVENT_TOGGLE);
|
||||
if (toggleEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._execute();
|
||||
EventHandler.trigger(this._element, EVENT_TOGGLED);
|
||||
}
|
||||
|
||||
// Private
|
||||
_execute() {
|
||||
const {
|
||||
attribute,
|
||||
value
|
||||
} = this._config;
|
||||
if (attribute === 'id') {
|
||||
return; // You have to be kidding
|
||||
}
|
||||
if (attribute === 'class') {
|
||||
this._element.classList.toggle(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare as strings since getAttribute() always returns a string
|
||||
if (this._element.getAttribute(attribute) === String(value)) {
|
||||
this._element.removeAttribute(attribute);
|
||||
return;
|
||||
}
|
||||
this._element.setAttribute(attribute, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
componentFunctions_js.eventActionOnPlugin(Toggler, EVENT_CLICK, SELECTOR_DATA_TOGGLE, 'toggle');
|
||||
|
||||
return Toggler;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=toggler.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"toggler.js","sources":["../src/toggler.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap toggler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { eventActionOnPlugin } from './util/component-functions.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toggler'\nconst DATA_KEY = 'bs.toggler'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_TOGGLE = `toggle${EVENT_KEY}`\nconst EVENT_TOGGLED = `toggled${EVENT_KEY}`\nconst EVENT_CLICK = 'click'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"toggler\"]'\n\nconst DefaultType = {\n attribute: 'string',\n value: '(string|number|boolean)'\n}\n\nconst Default = {\n attribute: 'class',\n value: null\n}\n\n/**\n * Class definition\n */\n\nclass Toggler extends BaseComponent {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n const toggleEvent = EventHandler.trigger(this._element, EVENT_TOGGLE)\n\n if (toggleEvent.defaultPrevented) {\n return\n }\n\n this._execute()\n\n EventHandler.trigger(this._element, EVENT_TOGGLED)\n }\n\n // Private\n _execute() {\n const { attribute, value } = this._config\n\n if (attribute === 'id') {\n return // You have to be kidding\n }\n\n if (attribute === 'class') {\n this._element.classList.toggle(value)\n return\n }\n\n // Compare as strings since getAttribute() always returns a string\n if (this._element.getAttribute(attribute) === String(value)) {\n this._element.removeAttribute(attribute)\n return\n }\n\n this._element.setAttribute(attribute, value)\n }\n}\n\n/**\n * Data API implementation\n */\n\neventActionOnPlugin(Toggler, EVENT_CLICK, SELECTOR_DATA_TOGGLE, 'toggle')\n\nexport default Toggler\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_TOGGLE","EVENT_TOGGLED","EVENT_CLICK","SELECTOR_DATA_TOGGLE","DefaultType","attribute","value","Default","Toggler","BaseComponent","toggle","toggleEvent","EventHandler","trigger","_element","defaultPrevented","_execute","_config","classList","getAttribute","String","removeAttribute","setAttribute","eventActionOnPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAMA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,SAAS;EACtB,MAAMC,QAAQ,GAAG,YAAY;EAC7B,MAAMC,SAAS,GAAG,CAAA,CAAA,EAAID,QAAQ,CAAA,CAAE;EAEhC,MAAME,YAAY,GAAG,CAAA,MAAA,EAASD,SAAS,CAAA,CAAE;EACzC,MAAME,aAAa,GAAG,CAAA,OAAA,EAAUF,SAAS,CAAA,CAAE;EAC3C,MAAMG,WAAW,GAAG,OAAO;EAE3B,MAAMC,oBAAoB,GAAG,4BAA4B;EAEzD,MAAMC,WAAW,GAAG;EAClBC,EAAAA,SAAS,EAAE,QAAQ;EACnBC,EAAAA,KAAK,EAAE;EACT,CAAC;EAED,MAAMC,OAAO,GAAG;EACdF,EAAAA,SAAS,EAAE,OAAO;EAClBC,EAAAA,KAAK,EAAE;EACT,CAAC;;EAED;EACA;EACA;;EAEA,MAAME,OAAO,SAASC,aAAa,CAAC;EAClC;IACA,WAAWF,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO;EAChB,EAAA;IAEA,WAAWH,WAAWA,GAAG;EACvB,IAAA,OAAOA,WAAW;EACpB,EAAA;IAEA,WAAWP,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI;EACb,EAAA;;EAEA;EACAa,EAAAA,MAAMA,GAAG;MACP,MAAMC,WAAW,GAAGC,YAAY,CAACC,OAAO,CAAC,IAAI,CAACC,QAAQ,EAAEd,YAAY,CAAC;MAErE,IAAIW,WAAW,CAACI,gBAAgB,EAAE;EAChC,MAAA;EACF,IAAA;MAEA,IAAI,CAACC,QAAQ,EAAE;MAEfJ,YAAY,CAACC,OAAO,CAAC,IAAI,CAACC,QAAQ,EAAEb,aAAa,CAAC;EACpD,EAAA;;EAEA;EACAe,EAAAA,QAAQA,GAAG;MACT,MAAM;QAAEX,SAAS;EAAEC,MAAAA;OAAO,GAAG,IAAI,CAACW,OAAO;MAEzC,IAAIZ,SAAS,KAAK,IAAI,EAAE;EACtB,MAAA,OAAM;EACR,IAAA;MAEA,IAAIA,SAAS,KAAK,OAAO,EAAE;QACzB,IAAI,CAACS,QAAQ,CAACI,SAAS,CAACR,MAAM,CAACJ,KAAK,CAAC;EACrC,MAAA;EACF,IAAA;;EAEA;EACA,IAAA,IAAI,IAAI,CAACQ,QAAQ,CAACK,YAAY,CAACd,SAAS,CAAC,KAAKe,MAAM,CAACd,KAAK,CAAC,EAAE;EAC3D,MAAA,IAAI,CAACQ,QAAQ,CAACO,eAAe,CAAChB,SAAS,CAAC;EACxC,MAAA;EACF,IAAA;MAEA,IAAI,CAACS,QAAQ,CAACQ,YAAY,CAACjB,SAAS,EAAEC,KAAK,CAAC;EAC9C,EAAA;EACF;;EAEA;EACA;EACA;;AAEAiB,2CAAmB,CAACf,OAAO,EAAEN,WAAW,EAAEC,oBAAoB,EAAE,QAAQ,CAAC;;;;;;;;"}
|
||||
Vendored
+188
-82
@@ -1,32 +1,13 @@
|
||||
/*!
|
||||
* Bootstrap tooltip.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./util/index.js'), require('./util/sanitizer.js'), require('./util/template-factory.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './util/index', './util/sanitizer', './util/template-factory'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global["@popperjs/core"], global.BaseComponent, global.EventHandler, global.Manipulator, global.Index, global.Sanitizer, global.TemplateFactory));
|
||||
})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, index_js, sanitizer_js, TemplateFactory) { 'use strict';
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
|
||||
if (e) {
|
||||
for (const k in e) {
|
||||
if (k !== 'default') {
|
||||
const d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: () => e[k]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@floating-ui/dom'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./util/index.js'), require('./util/sanitizer.js'), require('./util/template-factory.js'), require('./util/floating-ui.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['@floating-ui/dom', './base-component', './dom/event-handler', './dom/manipulator', './util/index', './util/sanitizer', './util/template-factory', './util/floating-ui'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global["@floating-ui/dom"], global.BaseComponent, global.EventHandler, global.Manipulator, global.Index, global.Sanitizer, global.TemplateFactory, global.FloatingUi));
|
||||
})(this, (function (dom, BaseComponent, EventHandler, Manipulator, index_js, sanitizer_js, TemplateFactory, floatingUi_js) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
@@ -47,6 +28,7 @@
|
||||
const CLASS_NAME_SHOW = 'show';
|
||||
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
|
||||
const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tooltip"]';
|
||||
const EVENT_MODAL_HIDE = 'hide.bs.modal';
|
||||
const TRIGGER_HOVER = 'hover';
|
||||
const TRIGGER_FOCUS = 'focus';
|
||||
@@ -80,7 +62,7 @@
|
||||
html: false,
|
||||
offset: [0, 6],
|
||||
placement: 'top',
|
||||
popperConfig: null,
|
||||
floatingConfig: null,
|
||||
sanitize: true,
|
||||
sanitizeFn: null,
|
||||
selector: false,
|
||||
@@ -99,7 +81,7 @@
|
||||
html: 'boolean',
|
||||
offset: '(array|string|function)',
|
||||
placement: '(string|function)',
|
||||
popperConfig: '(null|object|function)',
|
||||
floatingConfig: '(null|object|function)',
|
||||
sanitize: 'boolean',
|
||||
sanitizeFn: '(null|function)',
|
||||
selector: '(string|boolean)',
|
||||
@@ -114,8 +96,8 @@
|
||||
|
||||
class Tooltip extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
if (typeof Popper__namespace === 'undefined') {
|
||||
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org/docs/v2/)');
|
||||
if (typeof dom.computePosition === 'undefined') {
|
||||
throw new TypeError('Bootstrap\'s tooltips require Floating UI (https://floating-ui.com)');
|
||||
}
|
||||
super(element, config);
|
||||
|
||||
@@ -124,12 +106,15 @@
|
||||
this._timeout = 0;
|
||||
this._isHovered = null;
|
||||
this._activeTrigger = {};
|
||||
this._popper = null;
|
||||
this._floatingCleanup = null;
|
||||
this._templateFactory = null;
|
||||
this._newContent = null;
|
||||
this._mediaQueryListeners = [];
|
||||
this._responsivePlacements = null;
|
||||
|
||||
// Protected
|
||||
this.tip = null;
|
||||
this._parseResponsivePlacements();
|
||||
this._setListeners();
|
||||
if (!this._config.selector) {
|
||||
this._fixTitle();
|
||||
@@ -173,10 +158,11 @@
|
||||
if (this._element.getAttribute('data-bs-original-title')) {
|
||||
this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));
|
||||
}
|
||||
this._disposePopper();
|
||||
this._disposeFloating();
|
||||
this._disposeMediaQueryListeners();
|
||||
super.dispose();
|
||||
}
|
||||
show() {
|
||||
async show() {
|
||||
if (this._element.style.display === 'none') {
|
||||
throw new Error('Please use show on visible elements');
|
||||
}
|
||||
@@ -189,9 +175,7 @@
|
||||
if (showEvent.defaultPrevented || !isInTheDom) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: v6 remove this or make it optional
|
||||
this._disposePopper();
|
||||
this._disposeFloating();
|
||||
const tip = this._getTipElement();
|
||||
this._element.setAttribute('aria-describedby', tip.getAttribute('id'));
|
||||
const {
|
||||
@@ -201,7 +185,7 @@
|
||||
container.append(tip);
|
||||
EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));
|
||||
}
|
||||
this._popper = this._createPopper(tip);
|
||||
await this._createFloating(tip);
|
||||
tip.classList.add(CLASS_NAME_SHOW);
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
@@ -250,7 +234,7 @@
|
||||
return;
|
||||
}
|
||||
if (!this._isHovered) {
|
||||
this._disposePopper();
|
||||
this._disposeFloating();
|
||||
}
|
||||
this._element.removeAttribute('aria-describedby');
|
||||
EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN));
|
||||
@@ -258,8 +242,8 @@
|
||||
this._queueCallback(complete, this.tip, this._isAnimated());
|
||||
}
|
||||
update() {
|
||||
if (this._popper) {
|
||||
this._popper.update();
|
||||
if (this._floatingCleanup && this.tip) {
|
||||
this._updateFloatingPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +277,7 @@
|
||||
setContent(content) {
|
||||
this._newContent = content;
|
||||
if (this._isShown()) {
|
||||
this._disposePopper();
|
||||
this._disposeFloating();
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
@@ -330,10 +314,103 @@
|
||||
_isShown() {
|
||||
return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW);
|
||||
}
|
||||
_createPopper(tip) {
|
||||
_getPlacement(tip) {
|
||||
// If we have responsive placements, get the one for current viewport
|
||||
if (this._responsivePlacements) {
|
||||
const placement = floatingUi_js.getResponsivePlacement(this._responsivePlacements, 'top');
|
||||
return AttachmentMap[placement.toUpperCase()] || placement;
|
||||
}
|
||||
|
||||
// Execute placement (can be a function)
|
||||
const placement = index_js.execute(this._config.placement, [this, tip, this._element]);
|
||||
const attachment = AttachmentMap[placement.toUpperCase()];
|
||||
return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
|
||||
return AttachmentMap[placement.toUpperCase()] || placement;
|
||||
}
|
||||
_parseResponsivePlacements() {
|
||||
// Only parse if placement is a string (not a function)
|
||||
if (typeof this._config.placement !== 'string') {
|
||||
this._responsivePlacements = null;
|
||||
return;
|
||||
}
|
||||
this._responsivePlacements = floatingUi_js.parseResponsivePlacement(this._config.placement, 'top');
|
||||
if (this._responsivePlacements) {
|
||||
this._setupMediaQueryListeners();
|
||||
}
|
||||
}
|
||||
_setupMediaQueryListeners() {
|
||||
this._disposeMediaQueryListeners();
|
||||
this._mediaQueryListeners = floatingUi_js.createBreakpointListeners(() => {
|
||||
if (this._isShown()) {
|
||||
this._updateFloatingPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
_disposeMediaQueryListeners() {
|
||||
floatingUi_js.disposeBreakpointListeners(this._mediaQueryListeners);
|
||||
this._mediaQueryListeners = [];
|
||||
}
|
||||
async _createFloating(tip) {
|
||||
const placement = this._getPlacement(tip);
|
||||
const arrowElement = tip.querySelector(`.${this.constructor.NAME}-arrow`);
|
||||
|
||||
// Initial position update
|
||||
await this._updateFloatingPosition(tip, placement, arrowElement);
|
||||
|
||||
// Set up auto-update for scroll/resize
|
||||
this._floatingCleanup = dom.autoUpdate(this._element, tip, () => this._updateFloatingPosition(tip, null, arrowElement));
|
||||
}
|
||||
async _updateFloatingPosition(tip = this.tip, placement = null, arrowElement = null) {
|
||||
if (!tip) {
|
||||
return;
|
||||
}
|
||||
if (!placement) {
|
||||
placement = this._getPlacement(tip);
|
||||
}
|
||||
if (!arrowElement) {
|
||||
arrowElement = tip.querySelector(`.${this.constructor.NAME}-arrow`);
|
||||
}
|
||||
const middleware = this._getFloatingMiddleware(arrowElement);
|
||||
const floatingConfig = this._getFloatingConfig(placement, middleware);
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
placement: finalPlacement,
|
||||
middlewareData
|
||||
} = await dom.computePosition(this._element, tip, floatingConfig);
|
||||
|
||||
// Apply position to tooltip
|
||||
Object.assign(tip.style, {
|
||||
position: 'absolute',
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
});
|
||||
|
||||
// Ensure arrow is absolutely positioned within tooltip
|
||||
if (arrowElement) {
|
||||
arrowElement.style.position = 'absolute';
|
||||
}
|
||||
|
||||
// Set placement attribute for CSS arrow styling
|
||||
Manipulator.setDataAttribute(tip, 'placement', finalPlacement);
|
||||
|
||||
// Position arrow along the edge (center it) if present
|
||||
// The CSS handles which edge to place it on via data-bs-placement
|
||||
if (arrowElement && middlewareData.arrow) {
|
||||
const {
|
||||
x: arrowX,
|
||||
y: arrowY
|
||||
} = middlewareData.arrow;
|
||||
const isVertical = finalPlacement.startsWith('top') || finalPlacement.startsWith('bottom');
|
||||
|
||||
// Only set the cross-axis position (centering along the edge)
|
||||
// The main-axis position (which edge) is handled by CSS
|
||||
Object.assign(arrowElement.style, {
|
||||
left: isVertical && arrowX !== null ? `${arrowX}px` : '',
|
||||
top: !isVertical && arrowY !== null ? `${arrowY}px` : '',
|
||||
// Reset the other axis to let CSS handle it
|
||||
right: '',
|
||||
bottom: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
_getOffset() {
|
||||
const {
|
||||
@@ -343,50 +420,57 @@
|
||||
return offset.split(',').map(value => Number.parseInt(value, 10));
|
||||
}
|
||||
if (typeof offset === 'function') {
|
||||
return popperData => offset(popperData, this._element);
|
||||
// Floating UI passes different args, adapt the interface for offset function callbacks
|
||||
return ({
|
||||
placement,
|
||||
rects
|
||||
}) => {
|
||||
const result = offset({
|
||||
placement,
|
||||
reference: rects.reference,
|
||||
floating: rects.floating
|
||||
}, this._element);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
_resolvePossibleFunction(arg) {
|
||||
return index_js.execute(arg, [this._element, this._element]);
|
||||
}
|
||||
_getPopperConfig(attachment) {
|
||||
const defaultBsPopperConfig = {
|
||||
placement: attachment,
|
||||
modifiers: [{
|
||||
name: 'flip',
|
||||
options: {
|
||||
fallbackPlacements: this._config.fallbackPlacements
|
||||
}
|
||||
}, {
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: this._getOffset()
|
||||
}
|
||||
}, {
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
boundary: this._config.boundary
|
||||
}
|
||||
}, {
|
||||
name: 'arrow',
|
||||
options: {
|
||||
element: `.${this.constructor.NAME}-arrow`
|
||||
}
|
||||
}, {
|
||||
name: 'preSetPlacement',
|
||||
enabled: true,
|
||||
phase: 'beforeMain',
|
||||
fn: data => {
|
||||
// Pre-set Popper's placement attribute in order to read the arrow sizes properly.
|
||||
// Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
|
||||
this._getTipElement().setAttribute('data-popper-placement', data.state.placement);
|
||||
}
|
||||
}]
|
||||
_getFloatingMiddleware(arrowElement) {
|
||||
const offsetValue = this._getOffset();
|
||||
const middleware = [
|
||||
// Offset middleware - handles distance from reference
|
||||
dom.offset(typeof offsetValue === 'function' ? offsetValue : {
|
||||
mainAxis: offsetValue[1] || 0,
|
||||
crossAxis: offsetValue[0] || 0
|
||||
}),
|
||||
// Flip middleware - handles fallback placements
|
||||
dom.flip({
|
||||
fallbackPlacements: this._config.fallbackPlacements
|
||||
}),
|
||||
// Shift middleware - prevents overflow
|
||||
dom.shift({
|
||||
boundary: this._config.boundary === 'clippingParents' ? 'clippingAncestors' : this._config.boundary
|
||||
})];
|
||||
|
||||
// Arrow middleware - positions the arrow element
|
||||
if (arrowElement) {
|
||||
middleware.push(dom.arrow({
|
||||
element: arrowElement
|
||||
}));
|
||||
}
|
||||
return middleware;
|
||||
}
|
||||
_getFloatingConfig(placement, middleware) {
|
||||
const defaultConfig = {
|
||||
placement,
|
||||
middleware
|
||||
};
|
||||
return {
|
||||
...defaultBsPopperConfig,
|
||||
...index_js.execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
|
||||
...defaultConfig,
|
||||
...index_js.execute(this._config.floatingConfig, [undefined, defaultConfig])
|
||||
};
|
||||
}
|
||||
_setListeners() {
|
||||
@@ -508,10 +592,10 @@
|
||||
// `Object.fromEntries(keysWithDifferentValues)`
|
||||
return config;
|
||||
}
|
||||
_disposePopper() {
|
||||
if (this._popper) {
|
||||
this._popper.destroy();
|
||||
this._popper = null;
|
||||
_disposeFloating() {
|
||||
if (this._floatingCleanup) {
|
||||
this._floatingCleanup();
|
||||
this._floatingCleanup = null;
|
||||
}
|
||||
if (this.tip) {
|
||||
this.tip.remove();
|
||||
@@ -520,6 +604,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data API implementation - auto-initialize tooltips
|
||||
*/
|
||||
|
||||
const initTooltip = event => {
|
||||
const target = event.target.closest(SELECTOR_DATA_TOGGLE);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get or create instance and trigger the appropriate action
|
||||
const tooltip = Tooltip.getOrCreateInstance(target);
|
||||
|
||||
// For focus events, manually trigger enter to show
|
||||
if (event.type === 'focusin') {
|
||||
tooltip._activeTrigger.focus = true;
|
||||
tooltip._enter();
|
||||
}
|
||||
};
|
||||
EventHandler.on(document, EVENT_FOCUSIN, SELECTOR_DATA_TOGGLE, initTooltip);
|
||||
EventHandler.on(document, EVENT_MOUSEENTER, SELECTOR_DATA_TOGGLE, initTooltip);
|
||||
|
||||
return Tooltip;
|
||||
|
||||
}));
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap backdrop.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user