Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b9f1fcaab |
+12
-12
@@ -6,7 +6,7 @@
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-grid.min.css",
|
||||
"maxSize": "10.25 kB"
|
||||
"maxSize": "8.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.css",
|
||||
@@ -14,47 +14,47 @@
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-reboot.min.css",
|
||||
"maxSize": "6.75 kB"
|
||||
"maxSize": "4.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-utilities.css",
|
||||
"maxSize": "14.25 kB"
|
||||
"maxSize": "14.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap-utilities.min.css",
|
||||
"maxSize": "15.0 kB"
|
||||
"maxSize": "12.75 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.css",
|
||||
"maxSize": "37.5 kB"
|
||||
"maxSize": "36.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/bootstrap.min.css",
|
||||
"maxSize": "36.25 kB"
|
||||
"maxSize": "32.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.js",
|
||||
"maxSize": "67.75 kB"
|
||||
"maxSize": "49.75 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.bundle.min.js",
|
||||
"maxSize": "41.0 kB"
|
||||
"maxSize": "26.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.esm.js",
|
||||
"maxSize": "39.0 kB"
|
||||
"maxSize": "36.0 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.esm.min.js",
|
||||
"maxSize": "24.0 kB"
|
||||
"maxSize": "22.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.js",
|
||||
"maxSize": "39.75 kB"
|
||||
"maxSize": "36.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.min.js",
|
||||
"maxSize": "21.25 kB"
|
||||
"maxSize": "19.75 kB"
|
||||
}
|
||||
],
|
||||
"ci": {
|
||||
|
||||
@@ -2,23 +2,8 @@
|
||||
"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",
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CSS minification script using lightningcss
|
||||
*
|
||||
* This replaces clean-css which doesn't support modern CSS features
|
||||
* like light-dark(), color-mix(), @layer, etc.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { transform, browserslistToTargets } from 'lightningcss'
|
||||
|
||||
const distDir = path.join(process.cwd(), 'dist/css')
|
||||
|
||||
// Get all CSS files that need minification
|
||||
const cssFiles = fs.readdirSync(distDir)
|
||||
.filter(file => file.endsWith('.css') && !file.endsWith('.min.css'))
|
||||
|
||||
// Target browsers (matching Bootstrap's browser support)
|
||||
const targets = browserslistToTargets(['> 0.5%', 'last 2 versions', 'Firefox ESR', 'not dead'])
|
||||
|
||||
for (const file of cssFiles) {
|
||||
const inputPath = path.join(distDir, file)
|
||||
const outputPath = path.join(distDir, file.replace('.css', '.min.css'))
|
||||
const mapPath = `${outputPath}.map`
|
||||
|
||||
console.log(`Minifying ${file}...`)
|
||||
|
||||
const inputCss = fs.readFileSync(inputPath, 'utf8')
|
||||
const inputMap = fs.existsSync(`${inputPath}.map`) ?
|
||||
JSON.parse(fs.readFileSync(`${inputPath}.map`, 'utf8')) :
|
||||
undefined
|
||||
|
||||
try {
|
||||
const result = transform({
|
||||
filename: file,
|
||||
code: Buffer.from(inputCss),
|
||||
minify: true,
|
||||
sourceMap: true,
|
||||
inputSourceMap: inputMap ? JSON.stringify(inputMap) : undefined,
|
||||
targets
|
||||
})
|
||||
|
||||
// Write minified CSS with source map reference
|
||||
const minifiedCss = `${result.code.toString()}\n/*# sourceMappingURL=${path.basename(mapPath)} */`
|
||||
fs.writeFileSync(outputPath, minifiedCss)
|
||||
|
||||
// Write source map
|
||||
if (result.map) {
|
||||
fs.writeFileSync(mapPath, result.map.toString())
|
||||
}
|
||||
|
||||
console.log(` ✓ ${file} → ${path.basename(outputPath)}`)
|
||||
} catch (error) {
|
||||
console.error(` ✗ Error minifying ${file}:`, error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\nCSS minification complete!')
|
||||
Vendored
+1
-1
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
+6
-6
@@ -312,8 +312,7 @@
|
||||
|
||||
@layer colors, theme, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;
|
||||
:root {
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
color-scheme: light dark;
|
||||
--bs-primary-base: var(--bs-blue-500);
|
||||
--bs-primary-text: light-dark(var(--bs-blue-600), var(--bs-blue-400));
|
||||
--bs-primary-text-emphasis: light-dark(var(--bs-blue-800), var(--bs-blue-200));
|
||||
@@ -410,16 +409,19 @@
|
||||
--bs-border-emphasized: light-dark(var(--bs-gray-400), var(--bs-gray-600));
|
||||
--bs-border-white: var(--bs-white);
|
||||
--bs-border-black: var(--bs-black);
|
||||
color-scheme: light dark;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
color-scheme: light;
|
||||
--bs-white-rgb: to-rgb(#fff);
|
||||
--bs-black-rgb: to-rgb(#000);
|
||||
--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-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-font-size-base: 14px;
|
||||
--bs-font-size-xs: .75rem;
|
||||
@@ -477,11 +479,10 @@
|
||||
--bs-form-valid-border-color: var(--bs-success);
|
||||
--bs-form-invalid-color: var(--bs-danger);
|
||||
--bs-form-invalid-border-color: var(--bs-danger);
|
||||
color-scheme: light;
|
||||
--bs-root-font-size: 16px;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: to-rgb(#fff);
|
||||
--bs-heading-color: inherit;
|
||||
@@ -491,7 +492,6 @@
|
||||
--bs-form-valid-border-color: var(--bs-green-300);
|
||||
--bs-form-invalid-color: var(--bs-red-300);
|
||||
--bs-form-invalid-border-color: var(--bs-red-300);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@layer reboot {
|
||||
|
||||
Vendored
+1
-1
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
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+6
-6
@@ -312,8 +312,7 @@
|
||||
|
||||
@layer colors, theme, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;
|
||||
:root {
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
color-scheme: light dark;
|
||||
--bs-primary-base: var(--bs-blue-500);
|
||||
--bs-primary-text: light-dark(var(--bs-blue-600), var(--bs-blue-400));
|
||||
--bs-primary-text-emphasis: light-dark(var(--bs-blue-800), var(--bs-blue-200));
|
||||
@@ -410,16 +409,19 @@
|
||||
--bs-border-emphasized: light-dark(var(--bs-gray-400), var(--bs-gray-600));
|
||||
--bs-border-white: var(--bs-white);
|
||||
--bs-border-black: var(--bs-black);
|
||||
color-scheme: light dark;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
color-scheme: light;
|
||||
--bs-white-rgb: to-rgb(#fff);
|
||||
--bs-black-rgb: to-rgb(#000);
|
||||
--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-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-font-size-base: 14px;
|
||||
--bs-font-size-xs: .75rem;
|
||||
@@ -477,11 +479,10 @@
|
||||
--bs-form-valid-border-color: var(--bs-success);
|
||||
--bs-form-invalid-color: var(--bs-danger);
|
||||
--bs-form-invalid-border-color: var(--bs-danger);
|
||||
color-scheme: light;
|
||||
--bs-root-font-size: 16px;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: to-rgb(#fff);
|
||||
--bs-heading-color: inherit;
|
||||
@@ -491,7 +492,6 @@
|
||||
--bs-form-valid-border-color: var(--bs-green-300);
|
||||
--bs-form-invalid-color: var(--bs-red-300);
|
||||
--bs-form-invalid-border-color: var(--bs-red-300);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@layer helpers {
|
||||
|
||||
Vendored
+1
-1
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
+1
-1
File diff suppressed because one or more lines are too long
+11
-445
@@ -313,8 +313,7 @@
|
||||
|
||||
@layer colors, theme, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;
|
||||
:root {
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
color-scheme: light dark;
|
||||
--bs-primary-base: var(--bs-blue-500);
|
||||
--bs-primary-text: light-dark(var(--bs-blue-600), var(--bs-blue-400));
|
||||
--bs-primary-text-emphasis: light-dark(var(--bs-blue-800), var(--bs-blue-200));
|
||||
@@ -411,16 +410,19 @@
|
||||
--bs-border-emphasized: light-dark(var(--bs-gray-400), var(--bs-gray-600));
|
||||
--bs-border-white: var(--bs-white);
|
||||
--bs-border-black: var(--bs-black);
|
||||
color-scheme: light dark;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
color-scheme: light;
|
||||
--bs-white-rgb: to-rgb(#fff);
|
||||
--bs-black-rgb: to-rgb(#000);
|
||||
--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-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-font-size-base: 14px;
|
||||
--bs-font-size-xs: .75rem;
|
||||
@@ -478,11 +480,10 @@
|
||||
--bs-form-valid-border-color: var(--bs-success);
|
||||
--bs-form-invalid-color: var(--bs-danger);
|
||||
--bs-form-invalid-border-color: var(--bs-danger);
|
||||
color-scheme: light;
|
||||
--bs-root-font-size: 16px;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: to-rgb(#fff);
|
||||
--bs-heading-color: inherit;
|
||||
@@ -492,7 +493,6 @@
|
||||
--bs-form-valid-border-color: var(--bs-green-300);
|
||||
--bs-form-invalid-color: var(--bs-red-300);
|
||||
--bs-form-invalid-border-color: var(--bs-red-300);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@layer reboot {
|
||||
@@ -2731,12 +2731,12 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
.form-control:focus-visible {
|
||||
--bs-focus-ring-offset: -1px;
|
||||
color: var(--bs-color-body);
|
||||
background-color: var(--bs-bg-body);
|
||||
border-color: var(--bs-primary-border);
|
||||
outline: var(--bs-focus-ring);
|
||||
outline-offset: var(--bs-focus-ring-offset);
|
||||
--bs-focus-ring-offset: -1px;
|
||||
}
|
||||
.form-control::-webkit-date-and-time-value {
|
||||
min-width: 85px;
|
||||
@@ -2847,28 +2847,6 @@
|
||||
.form-control-color.form-control-lg {
|
||||
height: 3rem;
|
||||
}
|
||||
.form-ghost {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.form-ghost:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.form-ghost::placeholder {
|
||||
color: var(--bs-fg-3);
|
||||
opacity: 1;
|
||||
}
|
||||
.form-ghost:disabled {
|
||||
color: var(--bs-fg-4);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
@layer forms {
|
||||
b-checkgroup {
|
||||
@@ -3316,7 +3294,7 @@
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
}
|
||||
.input-group > :not(:first-child):not(.dropdown-menu) {
|
||||
.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {
|
||||
margin-inline-start: calc(-1 * var(--bs-border-width));
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
@@ -3458,15 +3436,15 @@
|
||||
border-color: var(--bs-form-valid-border-color);
|
||||
}
|
||||
.otp.is-valid .form-control:focus, .was-validated .otp:valid .form-control:focus {
|
||||
--bs-focus-ring-color: rgba(var(--bs-success-rgb), .25);
|
||||
border-color: var(--bs-form-valid-border-color);
|
||||
--bs-focus-ring-color: rgba(var(--bs-success-rgb), .25);
|
||||
}
|
||||
.otp.is-invalid .form-control, .was-validated .otp:invalid .form-control {
|
||||
border-color: var(--bs-form-invalid-border-color);
|
||||
}
|
||||
.otp.is-invalid .form-control:focus, .was-validated .otp:invalid .form-control:focus {
|
||||
--bs-focus-ring-color: rgba(var(--bs-danger-rgb), .25);
|
||||
border-color: var(--bs-form-invalid-border-color);
|
||||
--bs-focus-ring-color: rgba(var(--bs-danger-rgb), .25);
|
||||
}
|
||||
.otp.input-group {
|
||||
gap: 0;
|
||||
@@ -3493,90 +3471,6 @@
|
||||
--bs-otp-font-size: 21.875px;
|
||||
}
|
||||
}
|
||||
@layer forms {
|
||||
.form-adorn {
|
||||
--bs-control-min-height: 2.5rem;
|
||||
--bs-control-padding-y: 0.375rem;
|
||||
--bs-control-padding-x: 0.75rem;
|
||||
--bs-control-font-size: 14px;
|
||||
--bs-control-line-height: 1.5;
|
||||
--bs-control-color: var(--bs-color-body);
|
||||
--bs-control-bg: var(--bs-bg-body);
|
||||
--bs-control-border-width: var(--bs-border-width);
|
||||
--bs-control-border-color: var(--bs-border-color);
|
||||
--bs-control-border-radius: var(--bs-border-radius);
|
||||
--bs-form-adorn-gap: 0.375rem;
|
||||
--bs-form-adorn-icon-size: 1rem;
|
||||
--bs-form-adorn-icon-color: var(--bs-fg-2);
|
||||
display: flex;
|
||||
gap: var(--bs-form-adorn-gap);
|
||||
align-items: center;
|
||||
min-height: var(--bs-control-min-height);
|
||||
padding: var(--bs-control-padding-y) var(--bs-control-padding-x);
|
||||
font-size: var(--bs-control-font-size);
|
||||
line-height: var(--bs-control-line-height);
|
||||
color: var(--bs-control-color);
|
||||
background-color: var(--bs-control-bg);
|
||||
background-clip: padding-box;
|
||||
border: var(--bs-control-border-width) solid var(--bs-control-border-color);
|
||||
border-radius: var(--bs-control-border-radius);
|
||||
box-shadow: var(--bs-box-shadow-inset);
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.form-adorn {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
.form-adorn:focus-within {
|
||||
--bs-focus-ring-offset: -1px;
|
||||
border-color: var(--bs-primary-border);
|
||||
outline: var(--bs-focus-ring);
|
||||
outline-offset: var(--bs-focus-ring-offset);
|
||||
}
|
||||
.form-adorn > .form-ghost {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.form-adorn.form-adorn-end > .form-ghost {
|
||||
order: -1;
|
||||
}
|
||||
.form-adorn-icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--bs-form-adorn-icon-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
.form-adorn-icon > svg {
|
||||
width: var(--bs-form-adorn-icon-size);
|
||||
height: var(--bs-form-adorn-icon-size);
|
||||
}
|
||||
.form-adorn-text {
|
||||
flex-shrink: 0;
|
||||
color: var(--bs-form-adorn-icon-color);
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.form-adorn-sm {
|
||||
--bs-control-min-height: 2rem;
|
||||
--bs-control-padding-y: 0.25rem;
|
||||
--bs-control-padding-x: 0.5rem;
|
||||
--bs-control-font-size: 12.25px;
|
||||
--bs-control-line-height: 1.25;
|
||||
--bs-control-border-radius: var(--bs-border-radius-sm);
|
||||
}
|
||||
.form-adorn-lg {
|
||||
--bs-control-min-height: 3rem;
|
||||
--bs-control-padding-y: 0.5rem;
|
||||
--bs-control-padding-x: 1rem;
|
||||
--bs-control-font-size: 17.5px;
|
||||
--bs-control-line-height: 2;
|
||||
--bs-control-border-radius: var(--bs-border-radius-lg);
|
||||
}
|
||||
}
|
||||
@layer components {
|
||||
.tooltip {
|
||||
--bs-tooltip-zindex: 1080;
|
||||
@@ -4882,334 +4776,6 @@
|
||||
--bs-carousel-control-icon-filter: invert(1) grayscale(100);
|
||||
}
|
||||
}
|
||||
@layer components {
|
||||
[data-vc=calendar] {
|
||||
--bs-datepicker-padding: 1rem;
|
||||
--bs-datepicker-bg: var(--bs-bg-body);
|
||||
--bs-datepicker-color: var(--bs-fg-body);
|
||||
--bs-datepicker-border-color: var(--bs-border-color-translucent);
|
||||
--bs-datepicker-border-width: var(--bs-border-width);
|
||||
--bs-datepicker-border-radius: var(--bs-border-radius-lg);
|
||||
--bs-datepicker-box-shadow: var(--bs-box-shadow);
|
||||
--bs-datepicker-font-size: var(--bs-font-size-sm);
|
||||
--bs-datepicker-min-width: 280px;
|
||||
--bs-datepicker-zindex: 1000;
|
||||
--bs-datepicker-header-font-weight: 600;
|
||||
--bs-datepicker-weekday-color: var(--bs-fg-3);
|
||||
--bs-datepicker-day-hover-bg: var(--bs-bg-1);
|
||||
--bs-datepicker-day-selected-bg: var(--bs-primary-bg);
|
||||
--bs-datepicker-day-selected-color: var(--bs-primary-contrast);
|
||||
--bs-datepicker-day-today-bg: var(--bs-bg-2);
|
||||
--bs-datepicker-day-today-color: var(--bs-fg-1);
|
||||
--bs-datepicker-day-disabled-color: var(--bs-fg-4);
|
||||
position: absolute;
|
||||
z-index: var(--bs-datepicker-zindex);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: var(--bs-datepicker-min-width);
|
||||
padding: var(--bs-datepicker-padding);
|
||||
font-family: var(--bs-font-sans-serif);
|
||||
font-size: var(--bs-datepicker-font-size);
|
||||
color: var(--bs-datepicker-color);
|
||||
color-scheme: light dark;
|
||||
background-color: var(--bs-datepicker-bg);
|
||||
border: var(--bs-datepicker-border-width) solid var(--bs-datepicker-border-color);
|
||||
border-radius: var(--bs-datepicker-border-radius);
|
||||
box-shadow: var(--bs-datepicker-box-shadow);
|
||||
opacity: 1;
|
||||
}
|
||||
[data-vc=calendar][data-bs-theme=light] {
|
||||
color-scheme: light;
|
||||
}
|
||||
[data-vc=calendar][data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
[data-vc=calendar] button:focus-visible {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
outline: var(--bs-focus-ring);
|
||||
}
|
||||
[data-vc-calendar-hidden] {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
[data-vc=calendar]:not([data-vc-input]) {
|
||||
position: relative;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
[data-vc-position=bottom] {
|
||||
margin-block-start: 0.25rem;
|
||||
}
|
||||
[data-vc-position=top] {
|
||||
margin-block-end: -0.25rem;
|
||||
}
|
||||
[data-vc-arrow] {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: var(--bs-datepicker-color);
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-arrow]::before {
|
||||
position: absolute;
|
||||
inset: 0.25rem;
|
||||
content: "";
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%236b7280' d='M12 16c-.3 0-.5-.1-.7-.3l-6-6c-.4-.4-.4-1 0-1.4s1-.4 1.4 0l5.3 5.3 5.3-5.3c.4-.4 1-.4 1.4 0s.4 1 0 1.4l-6 6c-.2.2-.4.3-.7.3'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
[data-vc-arrow]:hover {
|
||||
background-color: var(--bs-datepicker-day-hover-bg);
|
||||
}
|
||||
[data-vc-arrow=prev]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
[data-vc-arrow=next]::before {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
[data-vc=controls] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-vc=grid] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.75rem;
|
||||
}
|
||||
[data-vc=column] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
min-width: 240px;
|
||||
}
|
||||
[data-vc=header] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
[data-vc-header=content] {
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
[data-vc=month],
|
||||
[data-vc=year] {
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin-inline: -0.125rem;
|
||||
font-size: 1rem;
|
||||
font-weight: var(--bs-datepicker-header-font-weight);
|
||||
color: var(--bs-datepicker-color);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
[data-vc=month]:disabled,
|
||||
[data-vc=year]:disabled {
|
||||
color: var(--bs-datepicker-day-disabled-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-vc=month]:hover:not(:disabled),
|
||||
[data-vc=year]:hover:not(:disabled) {
|
||||
background-color: var(--bs-datepicker-day-hover-bg);
|
||||
}
|
||||
[data-vc=content] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
[data-vc=months],
|
||||
[data-vc=years] {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
grid-template-columns: repeat(var(--bs-vc-columns, 4), minmax(0, 1fr));
|
||||
row-gap: 1rem;
|
||||
-moz-column-gap: 0.25rem;
|
||||
column-gap: 0.25rem;
|
||||
align-items: center;
|
||||
}
|
||||
[data-vc=years] {
|
||||
--bs-vc-columns: 5;
|
||||
}
|
||||
[data-vc-months-month],
|
||||
[data-vc-years-year] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 2.5rem;
|
||||
padding: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
color: var(--bs-datepicker-weekday-color);
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-months-month]:disabled,
|
||||
[data-vc-years-year]:disabled {
|
||||
color: var(--bs-datepicker-day-disabled-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-vc-months-month]:hover:not(:disabled),
|
||||
[data-vc-years-year]:hover:not(:disabled) {
|
||||
background-color: var(--bs-datepicker-day-hover-bg);
|
||||
}
|
||||
[data-vc-months-month][data-vc-months-month-selected], [data-vc-months-month][data-vc-years-year-selected],
|
||||
[data-vc-years-year][data-vc-months-month-selected],
|
||||
[data-vc-years-year][data-vc-years-year-selected] {
|
||||
color: var(--bs-datepicker-day-selected-color);
|
||||
background-color: var(--bs-datepicker-day-selected-bg);
|
||||
}
|
||||
[data-vc-months-month][data-vc-months-month-selected]:hover, [data-vc-months-month][data-vc-years-year-selected]:hover,
|
||||
[data-vc-years-year][data-vc-months-month-selected]:hover,
|
||||
[data-vc-years-year][data-vc-years-year-selected]:hover {
|
||||
color: var(--bs-datepicker-day-selected-color);
|
||||
background-color: var(--bs-datepicker-day-selected-bg);
|
||||
}
|
||||
[data-vc=week] {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
justify-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
[data-vc-week-day] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 1.875rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
color: var(--bs-datepicker-weekday-color);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
button[data-vc-week-day] {
|
||||
cursor: pointer;
|
||||
}
|
||||
[data-vc=dates] {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-vc-date] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding-top: 0.125rem;
|
||||
padding-bottom: 0.125rem;
|
||||
pointer-events: auto;
|
||||
}
|
||||
[data-vc-date]:not(:has([data-vc-date-btn])), [data-vc-date][data-vc-date-disabled], [data-vc-date][data-vc-date-disabled] [data-vc-date-btn] {
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-vc-date-btn] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 1.875rem;
|
||||
height: 100%;
|
||||
min-height: 1.875rem;
|
||||
padding: 0;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
line-height: 1rem;
|
||||
color: var(--bs-datepicker-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-date-btn]:hover {
|
||||
background-color: var(--bs-datepicker-day-hover-bg);
|
||||
}
|
||||
[data-vc-date-today] [data-vc-date-btn] {
|
||||
font-weight: 600;
|
||||
color: var(--bs-datepicker-day-today-color);
|
||||
background-color: var(--bs-datepicker-day-today-bg);
|
||||
}
|
||||
[data-vc-date-month=next] [data-vc-date-btn],
|
||||
[data-vc-date-month=prev] [data-vc-date-btn] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
[data-vc-date-disabled] [data-vc-date-btn] {
|
||||
color: var(--bs-datepicker-day-disabled-color);
|
||||
}
|
||||
[data-vc-date-hover] [data-vc-date-btn] {
|
||||
background-color: var(--bs-datepicker-day-hover-bg);
|
||||
border-radius: 0;
|
||||
}
|
||||
[data-vc-date-hover=first] [data-vc-date-btn] {
|
||||
border-start-start-radius: 0.5rem;
|
||||
border-end-start-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-date-hover=last] [data-vc-date-btn] {
|
||||
border-start-end-radius: 0.5rem;
|
||||
border-end-end-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-date-hover=first-and-last] [data-vc-date-btn] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-date-selected=middle] [data-vc-date-btn] {
|
||||
border-radius: 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
[data-vc-date-selected] [data-vc-date-btn] {
|
||||
color: var(--bs-datepicker-day-selected-color);
|
||||
background-color: var(--bs-datepicker-day-selected-bg);
|
||||
}
|
||||
[data-vc-date-selected=first] [data-vc-date-btn] {
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
}
|
||||
[data-vc-date-selected=last] [data-vc-date-btn] {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
[data-vc-date-selected=first-and-last] [data-vc-date-btn] {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
@layer components {
|
||||
.dialog-open {
|
||||
overflow: hidden;
|
||||
@@ -5855,10 +5421,10 @@
|
||||
background-color: var(--bs-nav-link-hover-bg);
|
||||
}
|
||||
.nav-link:focus-visible {
|
||||
--bs-focus-ring-offset: 1px;
|
||||
color: var(--bs-nav-link-hover-color);
|
||||
outline: var(--bs-focus-ring);
|
||||
outline-offset: var(--bs-focus-ring-offset);
|
||||
--bs-focus-ring-offset: 1px;
|
||||
}
|
||||
.nav-link.active, .nav-link:active {
|
||||
color: var(--bs-nav-link-active-color);
|
||||
|
||||
Vendored
+1
-1
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
File diff suppressed because one or more lines are too long
Vendored
+69
-501
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
+2
-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
+70
-499
@@ -1,9 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
import { Calendar } from 'vanilla-calendar-pro';
|
||||
import { computePosition, autoUpdate, offset, flip, shift, arrow } from '@floating-ui/dom';
|
||||
|
||||
/**
|
||||
@@ -792,11 +791,11 @@ const eventAction = (onEvent, stringSelector, callback) => {
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$j = 'alert';
|
||||
const DATA_KEY$e = 'bs.alert';
|
||||
const EVENT_KEY$f = `.${DATA_KEY$e}`;
|
||||
const EVENT_CLOSE = `close${EVENT_KEY$f}`;
|
||||
const EVENT_CLOSED = `closed${EVENT_KEY$f}`;
|
||||
const NAME$i = 'alert';
|
||||
const DATA_KEY$d = 'bs.alert';
|
||||
const EVENT_KEY$e = `.${DATA_KEY$d}`;
|
||||
const EVENT_CLOSE = `close${EVENT_KEY$e}`;
|
||||
const EVENT_CLOSED = `closed${EVENT_KEY$e}`;
|
||||
const CLASS_NAME_FADE$4 = 'fade';
|
||||
const CLASS_NAME_SHOW$7 = 'show';
|
||||
|
||||
@@ -807,7 +806,7 @@ const CLASS_NAME_SHOW$7 = 'show';
|
||||
class Alert extends BaseComponent {
|
||||
// Getters
|
||||
static get NAME() {
|
||||
return NAME$j;
|
||||
return NAME$i;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -847,13 +846,13 @@ enableDismissTrigger(Alert, 'close');
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$i = 'button';
|
||||
const DATA_KEY$d = 'bs.button';
|
||||
const EVENT_KEY$e = `.${DATA_KEY$d}`;
|
||||
const DATA_API_KEY$9 = '.data-api';
|
||||
const NAME$h = 'button';
|
||||
const DATA_KEY$c = 'bs.button';
|
||||
const EVENT_KEY$d = `.${DATA_KEY$c}`;
|
||||
const DATA_API_KEY$8 = '.data-api';
|
||||
const CLASS_NAME_ACTIVE$3 = 'active';
|
||||
const SELECTOR_DATA_TOGGLE$9 = '[data-bs-toggle="button"]';
|
||||
const EVENT_CLICK_DATA_API$7 = `click${EVENT_KEY$e}${DATA_API_KEY$9}`;
|
||||
const SELECTOR_DATA_TOGGLE$8 = '[data-bs-toggle="button"]';
|
||||
const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$d}${DATA_API_KEY$8}`;
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
@@ -862,7 +861,7 @@ const EVENT_CLICK_DATA_API$7 = `click${EVENT_KEY$e}${DATA_API_KEY$9}`;
|
||||
class Button extends BaseComponent {
|
||||
// Getters
|
||||
static get NAME() {
|
||||
return NAME$i;
|
||||
return NAME$h;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -876,9 +875,9 @@ class Button extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$7, SELECTOR_DATA_TOGGLE$9, event => {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$8, event => {
|
||||
event.preventDefault();
|
||||
const button = event.target.closest(SELECTOR_DATA_TOGGLE$9);
|
||||
const button = event.target.closest(SELECTOR_DATA_TOGGLE$8);
|
||||
const data = Button.getOrCreateInstance(button);
|
||||
data.toggle();
|
||||
});
|
||||
@@ -895,23 +894,23 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$7, SELECTOR_DATA_TOGGLE$9, event
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$h = 'swipe';
|
||||
const EVENT_KEY$d = '.bs.swipe';
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$d}`;
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$d}`;
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY$d}`;
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$d}`;
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY$d}`;
|
||||
const NAME$g = 'swipe';
|
||||
const EVENT_KEY$c = '.bs.swipe';
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$c}`;
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$c}`;
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY$c}`;
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$c}`;
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY$c}`;
|
||||
const POINTER_TYPE_TOUCH = 'touch';
|
||||
const POINTER_TYPE_PEN = 'pen';
|
||||
const CLASS_NAME_POINTER_EVENT = 'pointer-event';
|
||||
const SWIPE_THRESHOLD = 40;
|
||||
const Default$g = {
|
||||
const Default$f = {
|
||||
endCallback: null,
|
||||
leftCallback: null,
|
||||
rightCallback: null
|
||||
};
|
||||
const DefaultType$g = {
|
||||
const DefaultType$f = {
|
||||
endCallback: '(function|null)',
|
||||
leftCallback: '(function|null)',
|
||||
rightCallback: '(function|null)'
|
||||
@@ -936,18 +935,18 @@ class Swipe extends Config {
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$g;
|
||||
return Default$f;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$g;
|
||||
return DefaultType$f;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$h;
|
||||
return NAME$g;
|
||||
}
|
||||
|
||||
// Public
|
||||
dispose() {
|
||||
EventHandler.off(this._element, EVENT_KEY$d);
|
||||
EventHandler.off(this._element, EVENT_KEY$c);
|
||||
}
|
||||
|
||||
// Private
|
||||
@@ -1015,10 +1014,10 @@ class Swipe extends Config {
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$g = 'carousel';
|
||||
const DATA_KEY$c = 'bs.carousel';
|
||||
const EVENT_KEY$c = `.${DATA_KEY$c}`;
|
||||
const DATA_API_KEY$8 = '.data-api';
|
||||
const NAME$f = 'carousel';
|
||||
const DATA_KEY$b = 'bs.carousel';
|
||||
const EVENT_KEY$b = `.${DATA_KEY$b}`;
|
||||
const DATA_API_KEY$7 = '.data-api';
|
||||
const ARROW_LEFT_KEY$2 = 'ArrowLeft';
|
||||
const ARROW_RIGHT_KEY$2 = 'ArrowRight';
|
||||
const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
|
||||
@@ -1027,14 +1026,14 @@ const ORDER_NEXT = 'next';
|
||||
const ORDER_PREV = 'prev';
|
||||
const DIRECTION_LEFT = 'left';
|
||||
const DIRECTION_RIGHT = 'right';
|
||||
const EVENT_SLIDE = `slide${EVENT_KEY$c}`;
|
||||
const EVENT_SLID = `slid${EVENT_KEY$c}`;
|
||||
const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$c}`;
|
||||
const EVENT_MOUSEENTER$2 = `mouseenter${EVENT_KEY$c}`;
|
||||
const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$c}`;
|
||||
const EVENT_DRAG_START = `dragstart${EVENT_KEY$c}`;
|
||||
const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$c}${DATA_API_KEY$8}`;
|
||||
const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$c}${DATA_API_KEY$8}`;
|
||||
const EVENT_SLIDE = `slide${EVENT_KEY$b}`;
|
||||
const EVENT_SLID = `slid${EVENT_KEY$b}`;
|
||||
const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$b}`;
|
||||
const EVENT_MOUSEENTER$2 = `mouseenter${EVENT_KEY$b}`;
|
||||
const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$b}`;
|
||||
const EVENT_DRAG_START = `dragstart${EVENT_KEY$b}`;
|
||||
const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const CLASS_NAME_CAROUSEL = 'carousel';
|
||||
const CLASS_NAME_ACTIVE$2 = 'active';
|
||||
const CLASS_NAME_SLIDE = 'slide';
|
||||
@@ -1053,7 +1052,7 @@ const KEY_TO_DIRECTION = {
|
||||
[ARROW_LEFT_KEY$2]: DIRECTION_RIGHT,
|
||||
[ARROW_RIGHT_KEY$2]: DIRECTION_LEFT
|
||||
};
|
||||
const Default$f = {
|
||||
const Default$e = {
|
||||
interval: 5000,
|
||||
keyboard: true,
|
||||
pause: 'hover',
|
||||
@@ -1061,7 +1060,7 @@ const Default$f = {
|
||||
touch: true,
|
||||
wrap: true
|
||||
};
|
||||
const DefaultType$f = {
|
||||
const DefaultType$e = {
|
||||
interval: '(number|boolean)',
|
||||
// TODO:v6 remove boolean support
|
||||
keyboard: 'boolean',
|
||||
@@ -1092,13 +1091,13 @@ class Carousel extends BaseComponent {
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$f;
|
||||
return Default$e;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$f;
|
||||
return DefaultType$e;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$g;
|
||||
return NAME$f;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -1325,7 +1324,7 @@ class Carousel extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_SLIDE, function (event) {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {
|
||||
const target = SelectorEngine.getElementFromSelector(this);
|
||||
if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
|
||||
return;
|
||||
@@ -1365,15 +1364,15 @@ EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$f = 'collapse';
|
||||
const DATA_KEY$b = 'bs.collapse';
|
||||
const EVENT_KEY$b = `.${DATA_KEY$b}`;
|
||||
const DATA_API_KEY$7 = '.data-api';
|
||||
const EVENT_SHOW$7 = `show${EVENT_KEY$b}`;
|
||||
const EVENT_SHOWN$7 = `shown${EVENT_KEY$b}`;
|
||||
const EVENT_HIDE$7 = `hide${EVENT_KEY$b}`;
|
||||
const EVENT_HIDDEN$7 = `hidden${EVENT_KEY$b}`;
|
||||
const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const NAME$e = 'collapse';
|
||||
const DATA_KEY$a = 'bs.collapse';
|
||||
const EVENT_KEY$a = `.${DATA_KEY$a}`;
|
||||
const DATA_API_KEY$6 = '.data-api';
|
||||
const EVENT_SHOW$6 = `show${EVENT_KEY$a}`;
|
||||
const EVENT_SHOWN$6 = `shown${EVENT_KEY$a}`;
|
||||
const EVENT_HIDE$6 = `hide${EVENT_KEY$a}`;
|
||||
const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$a}`;
|
||||
const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const CLASS_NAME_SHOW$6 = 'show';
|
||||
const CLASS_NAME_COLLAPSE = 'collapse';
|
||||
const CLASS_NAME_COLLAPSING = 'collapsing';
|
||||
@@ -1383,12 +1382,12 @@ const CLASS_NAME_HORIZONTAL = 'collapse-horizontal';
|
||||
const WIDTH = 'width';
|
||||
const HEIGHT = 'height';
|
||||
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';
|
||||
const SELECTOR_DATA_TOGGLE$8 = '[data-bs-toggle="collapse"]';
|
||||
const Default$e = {
|
||||
const SELECTOR_DATA_TOGGLE$7 = '[data-bs-toggle="collapse"]';
|
||||
const Default$d = {
|
||||
parent: null,
|
||||
toggle: true
|
||||
};
|
||||
const DefaultType$e = {
|
||||
const DefaultType$d = {
|
||||
parent: '(null|element)',
|
||||
toggle: 'boolean'
|
||||
};
|
||||
@@ -1402,7 +1401,7 @@ class Collapse extends BaseComponent {
|
||||
super(element, config);
|
||||
this._isTransitioning = false;
|
||||
this._triggerArray = [];
|
||||
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$8);
|
||||
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$7);
|
||||
for (const elem of toggleList) {
|
||||
const selector = SelectorEngine.getSelectorFromElement(elem);
|
||||
const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);
|
||||
@@ -1421,13 +1420,13 @@ class Collapse extends BaseComponent {
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$e;
|
||||
return Default$d;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$e;
|
||||
return DefaultType$d;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$f;
|
||||
return NAME$e;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -1453,7 +1452,7 @@ class Collapse extends BaseComponent {
|
||||
if (activeChildren.length && activeChildren[0]._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$7);
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);
|
||||
if (startEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
@@ -1471,7 +1470,7 @@ class Collapse extends BaseComponent {
|
||||
this._element.classList.remove(CLASS_NAME_COLLAPSING);
|
||||
this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$6);
|
||||
this._element.style[dimension] = '';
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$7);
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$6);
|
||||
};
|
||||
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
|
||||
const scrollSize = `scroll${capitalizedDimension}`;
|
||||
@@ -1482,7 +1481,7 @@ class Collapse extends BaseComponent {
|
||||
if (this._isTransitioning || !this._isShown()) {
|
||||
return;
|
||||
}
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$7);
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);
|
||||
if (startEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
@@ -1502,7 +1501,7 @@ class Collapse extends BaseComponent {
|
||||
this._isTransitioning = false;
|
||||
this._element.classList.remove(CLASS_NAME_COLLAPSING);
|
||||
this._element.classList.add(CLASS_NAME_COLLAPSE);
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$7);
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$6);
|
||||
};
|
||||
this._element.style[dimension] = '';
|
||||
this._queueCallback(complete, this._element, true);
|
||||
@@ -1524,7 +1523,7 @@ class Collapse extends BaseComponent {
|
||||
if (!this._config.parent) {
|
||||
return;
|
||||
}
|
||||
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$8);
|
||||
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$7);
|
||||
for (const element of children) {
|
||||
const selected = SelectorEngine.getElementFromSelector(element);
|
||||
if (selected) {
|
||||
@@ -1552,7 +1551,7 @@ class Collapse extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$8, function (event) {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$7, function (event) {
|
||||
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
|
||||
if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {
|
||||
event.preventDefault();
|
||||
@@ -1564,434 +1563,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$8, functi
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap datepicker.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$e = 'datepicker';
|
||||
const DATA_KEY$a = 'bs.datepicker';
|
||||
const EVENT_KEY$a = `.${DATA_KEY$a}`;
|
||||
const DATA_API_KEY$6 = '.data-api';
|
||||
const EVENT_CHANGE = `change${EVENT_KEY$a}`;
|
||||
const EVENT_SHOW$6 = `show${EVENT_KEY$a}`;
|
||||
const EVENT_SHOWN$6 = `shown${EVENT_KEY$a}`;
|
||||
const EVENT_HIDE$6 = `hide${EVENT_KEY$a}`;
|
||||
const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$a}`;
|
||||
const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const EVENT_FOCUSIN_DATA_API = `focusin${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const SELECTOR_DATA_TOGGLE$7 = '[data-bs-toggle="datepicker"]';
|
||||
const HIDE_DELAY = 100; // ms delay before hiding after selection
|
||||
|
||||
const Default$d = {
|
||||
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$d = {
|
||||
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$d;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$d;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$e;
|
||||
}
|
||||
|
||||
// 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 || isDisabled(this._element) || this._isShown) {
|
||||
return;
|
||||
}
|
||||
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.show();
|
||||
this._isShown = true;
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$6);
|
||||
}
|
||||
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$6);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.hide();
|
||||
this._isShown = false;
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$6);
|
||||
}
|
||||
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 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$4, SELECTOR_DATA_TOGGLE$7, 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$7, 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$a}${DATA_API_KEY$6}`, () => {
|
||||
for (const element of document.querySelectorAll(`${SELECTOR_DATA_TOGGLE$7}[data-bs-inline="true"]`)) {
|
||||
Datepicker.getOrCreateInstance(element);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap dialog.js
|
||||
@@ -5853,5 +5424,5 @@ class Toggler extends BaseComponent {
|
||||
|
||||
eventActionOnPlugin(Toggler, EVENT_CLICK, SELECTOR_DATA_TOGGLE, 'toggle');
|
||||
|
||||
export { Alert, Button, Carousel, Collapse, Datepicker, Dialog, Dropdown, Offcanvas, OtpInput, Popover, ScrollSpy, Strength, Tab, Toast, Toggler, Tooltip };
|
||||
export { Alert, Button, Carousel, Collapse, Dialog, Dropdown, Offcanvas, OtpInput, Popover, ScrollSpy, Strength, Tab, Toast, Toggler, Tooltip };
|
||||
//# sourceMappingURL=bootstrap.esm.js.map
|
||||
|
||||
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
+73
-502
@@ -1,13 +1,13 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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('vanilla-calendar-pro'), require('@floating-ui/dom')) :
|
||||
typeof define === 'function' && define.amd ? define(['vanilla-calendar-pro', '@floating-ui/dom'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.vanillaCalendarPro, global.FloatingUIDOM));
|
||||
})(this, (function (vanillaCalendarPro, dom) { 'use strict';
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@floating-ui/dom')) :
|
||||
typeof define === 'function' && define.amd ? define(['@floating-ui/dom'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.FloatingUIDOM));
|
||||
})(this, (function (dom) { 'use strict';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
@@ -795,11 +795,11 @@
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$j = 'alert';
|
||||
const DATA_KEY$e = 'bs.alert';
|
||||
const EVENT_KEY$f = `.${DATA_KEY$e}`;
|
||||
const EVENT_CLOSE = `close${EVENT_KEY$f}`;
|
||||
const EVENT_CLOSED = `closed${EVENT_KEY$f}`;
|
||||
const NAME$i = 'alert';
|
||||
const DATA_KEY$d = 'bs.alert';
|
||||
const EVENT_KEY$e = `.${DATA_KEY$d}`;
|
||||
const EVENT_CLOSE = `close${EVENT_KEY$e}`;
|
||||
const EVENT_CLOSED = `closed${EVENT_KEY$e}`;
|
||||
const CLASS_NAME_FADE$4 = 'fade';
|
||||
const CLASS_NAME_SHOW$7 = 'show';
|
||||
|
||||
@@ -810,7 +810,7 @@
|
||||
class Alert extends BaseComponent {
|
||||
// Getters
|
||||
static get NAME() {
|
||||
return NAME$j;
|
||||
return NAME$i;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -850,13 +850,13 @@
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$i = 'button';
|
||||
const DATA_KEY$d = 'bs.button';
|
||||
const EVENT_KEY$e = `.${DATA_KEY$d}`;
|
||||
const DATA_API_KEY$9 = '.data-api';
|
||||
const NAME$h = 'button';
|
||||
const DATA_KEY$c = 'bs.button';
|
||||
const EVENT_KEY$d = `.${DATA_KEY$c}`;
|
||||
const DATA_API_KEY$8 = '.data-api';
|
||||
const CLASS_NAME_ACTIVE$3 = 'active';
|
||||
const SELECTOR_DATA_TOGGLE$9 = '[data-bs-toggle="button"]';
|
||||
const EVENT_CLICK_DATA_API$7 = `click${EVENT_KEY$e}${DATA_API_KEY$9}`;
|
||||
const SELECTOR_DATA_TOGGLE$8 = '[data-bs-toggle="button"]';
|
||||
const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$d}${DATA_API_KEY$8}`;
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
@@ -865,7 +865,7 @@
|
||||
class Button extends BaseComponent {
|
||||
// Getters
|
||||
static get NAME() {
|
||||
return NAME$i;
|
||||
return NAME$h;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -879,9 +879,9 @@
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$7, SELECTOR_DATA_TOGGLE$9, event => {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$8, event => {
|
||||
event.preventDefault();
|
||||
const button = event.target.closest(SELECTOR_DATA_TOGGLE$9);
|
||||
const button = event.target.closest(SELECTOR_DATA_TOGGLE$8);
|
||||
const data = Button.getOrCreateInstance(button);
|
||||
data.toggle();
|
||||
});
|
||||
@@ -898,23 +898,23 @@
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$h = 'swipe';
|
||||
const EVENT_KEY$d = '.bs.swipe';
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$d}`;
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$d}`;
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY$d}`;
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$d}`;
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY$d}`;
|
||||
const NAME$g = 'swipe';
|
||||
const EVENT_KEY$c = '.bs.swipe';
|
||||
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$c}`;
|
||||
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$c}`;
|
||||
const EVENT_TOUCHEND = `touchend${EVENT_KEY$c}`;
|
||||
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$c}`;
|
||||
const EVENT_POINTERUP = `pointerup${EVENT_KEY$c}`;
|
||||
const POINTER_TYPE_TOUCH = 'touch';
|
||||
const POINTER_TYPE_PEN = 'pen';
|
||||
const CLASS_NAME_POINTER_EVENT = 'pointer-event';
|
||||
const SWIPE_THRESHOLD = 40;
|
||||
const Default$g = {
|
||||
const Default$f = {
|
||||
endCallback: null,
|
||||
leftCallback: null,
|
||||
rightCallback: null
|
||||
};
|
||||
const DefaultType$g = {
|
||||
const DefaultType$f = {
|
||||
endCallback: '(function|null)',
|
||||
leftCallback: '(function|null)',
|
||||
rightCallback: '(function|null)'
|
||||
@@ -939,18 +939,18 @@
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$g;
|
||||
return Default$f;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$g;
|
||||
return DefaultType$f;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$h;
|
||||
return NAME$g;
|
||||
}
|
||||
|
||||
// Public
|
||||
dispose() {
|
||||
EventHandler.off(this._element, EVENT_KEY$d);
|
||||
EventHandler.off(this._element, EVENT_KEY$c);
|
||||
}
|
||||
|
||||
// Private
|
||||
@@ -1018,10 +1018,10 @@
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$g = 'carousel';
|
||||
const DATA_KEY$c = 'bs.carousel';
|
||||
const EVENT_KEY$c = `.${DATA_KEY$c}`;
|
||||
const DATA_API_KEY$8 = '.data-api';
|
||||
const NAME$f = 'carousel';
|
||||
const DATA_KEY$b = 'bs.carousel';
|
||||
const EVENT_KEY$b = `.${DATA_KEY$b}`;
|
||||
const DATA_API_KEY$7 = '.data-api';
|
||||
const ARROW_LEFT_KEY$2 = 'ArrowLeft';
|
||||
const ARROW_RIGHT_KEY$2 = 'ArrowRight';
|
||||
const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
|
||||
@@ -1030,14 +1030,14 @@
|
||||
const ORDER_PREV = 'prev';
|
||||
const DIRECTION_LEFT = 'left';
|
||||
const DIRECTION_RIGHT = 'right';
|
||||
const EVENT_SLIDE = `slide${EVENT_KEY$c}`;
|
||||
const EVENT_SLID = `slid${EVENT_KEY$c}`;
|
||||
const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$c}`;
|
||||
const EVENT_MOUSEENTER$2 = `mouseenter${EVENT_KEY$c}`;
|
||||
const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$c}`;
|
||||
const EVENT_DRAG_START = `dragstart${EVENT_KEY$c}`;
|
||||
const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$c}${DATA_API_KEY$8}`;
|
||||
const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$c}${DATA_API_KEY$8}`;
|
||||
const EVENT_SLIDE = `slide${EVENT_KEY$b}`;
|
||||
const EVENT_SLID = `slid${EVENT_KEY$b}`;
|
||||
const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$b}`;
|
||||
const EVENT_MOUSEENTER$2 = `mouseenter${EVENT_KEY$b}`;
|
||||
const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$b}`;
|
||||
const EVENT_DRAG_START = `dragstart${EVENT_KEY$b}`;
|
||||
const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const CLASS_NAME_CAROUSEL = 'carousel';
|
||||
const CLASS_NAME_ACTIVE$2 = 'active';
|
||||
const CLASS_NAME_SLIDE = 'slide';
|
||||
@@ -1056,7 +1056,7 @@
|
||||
[ARROW_LEFT_KEY$2]: DIRECTION_RIGHT,
|
||||
[ARROW_RIGHT_KEY$2]: DIRECTION_LEFT
|
||||
};
|
||||
const Default$f = {
|
||||
const Default$e = {
|
||||
interval: 5000,
|
||||
keyboard: true,
|
||||
pause: 'hover',
|
||||
@@ -1064,7 +1064,7 @@
|
||||
touch: true,
|
||||
wrap: true
|
||||
};
|
||||
const DefaultType$f = {
|
||||
const DefaultType$e = {
|
||||
interval: '(number|boolean)',
|
||||
// TODO:v6 remove boolean support
|
||||
keyboard: 'boolean',
|
||||
@@ -1095,13 +1095,13 @@
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$f;
|
||||
return Default$e;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$f;
|
||||
return DefaultType$e;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$g;
|
||||
return NAME$f;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -1328,7 +1328,7 @@
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_SLIDE, function (event) {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {
|
||||
const target = SelectorEngine.getElementFromSelector(this);
|
||||
if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
|
||||
return;
|
||||
@@ -1368,15 +1368,15 @@
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$f = 'collapse';
|
||||
const DATA_KEY$b = 'bs.collapse';
|
||||
const EVENT_KEY$b = `.${DATA_KEY$b}`;
|
||||
const DATA_API_KEY$7 = '.data-api';
|
||||
const EVENT_SHOW$7 = `show${EVENT_KEY$b}`;
|
||||
const EVENT_SHOWN$7 = `shown${EVENT_KEY$b}`;
|
||||
const EVENT_HIDE$7 = `hide${EVENT_KEY$b}`;
|
||||
const EVENT_HIDDEN$7 = `hidden${EVENT_KEY$b}`;
|
||||
const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$b}${DATA_API_KEY$7}`;
|
||||
const NAME$e = 'collapse';
|
||||
const DATA_KEY$a = 'bs.collapse';
|
||||
const EVENT_KEY$a = `.${DATA_KEY$a}`;
|
||||
const DATA_API_KEY$6 = '.data-api';
|
||||
const EVENT_SHOW$6 = `show${EVENT_KEY$a}`;
|
||||
const EVENT_SHOWN$6 = `shown${EVENT_KEY$a}`;
|
||||
const EVENT_HIDE$6 = `hide${EVENT_KEY$a}`;
|
||||
const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$a}`;
|
||||
const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const CLASS_NAME_SHOW$6 = 'show';
|
||||
const CLASS_NAME_COLLAPSE = 'collapse';
|
||||
const CLASS_NAME_COLLAPSING = 'collapsing';
|
||||
@@ -1386,12 +1386,12 @@
|
||||
const WIDTH = 'width';
|
||||
const HEIGHT = 'height';
|
||||
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';
|
||||
const SELECTOR_DATA_TOGGLE$8 = '[data-bs-toggle="collapse"]';
|
||||
const Default$e = {
|
||||
const SELECTOR_DATA_TOGGLE$7 = '[data-bs-toggle="collapse"]';
|
||||
const Default$d = {
|
||||
parent: null,
|
||||
toggle: true
|
||||
};
|
||||
const DefaultType$e = {
|
||||
const DefaultType$d = {
|
||||
parent: '(null|element)',
|
||||
toggle: 'boolean'
|
||||
};
|
||||
@@ -1405,7 +1405,7 @@
|
||||
super(element, config);
|
||||
this._isTransitioning = false;
|
||||
this._triggerArray = [];
|
||||
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$8);
|
||||
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$7);
|
||||
for (const elem of toggleList) {
|
||||
const selector = SelectorEngine.getSelectorFromElement(elem);
|
||||
const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);
|
||||
@@ -1424,13 +1424,13 @@
|
||||
|
||||
// Getters
|
||||
static get Default() {
|
||||
return Default$e;
|
||||
return Default$d;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$e;
|
||||
return DefaultType$d;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$f;
|
||||
return NAME$e;
|
||||
}
|
||||
|
||||
// Public
|
||||
@@ -1456,7 +1456,7 @@
|
||||
if (activeChildren.length && activeChildren[0]._isTransitioning) {
|
||||
return;
|
||||
}
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$7);
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);
|
||||
if (startEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
@@ -1474,7 +1474,7 @@
|
||||
this._element.classList.remove(CLASS_NAME_COLLAPSING);
|
||||
this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$6);
|
||||
this._element.style[dimension] = '';
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$7);
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$6);
|
||||
};
|
||||
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
|
||||
const scrollSize = `scroll${capitalizedDimension}`;
|
||||
@@ -1485,7 +1485,7 @@
|
||||
if (this._isTransitioning || !this._isShown()) {
|
||||
return;
|
||||
}
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$7);
|
||||
const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);
|
||||
if (startEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
@@ -1505,7 +1505,7 @@
|
||||
this._isTransitioning = false;
|
||||
this._element.classList.remove(CLASS_NAME_COLLAPSING);
|
||||
this._element.classList.add(CLASS_NAME_COLLAPSE);
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$7);
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$6);
|
||||
};
|
||||
this._element.style[dimension] = '';
|
||||
this._queueCallback(complete, this._element, true);
|
||||
@@ -1527,7 +1527,7 @@
|
||||
if (!this._config.parent) {
|
||||
return;
|
||||
}
|
||||
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$8);
|
||||
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$7);
|
||||
for (const element of children) {
|
||||
const selected = SelectorEngine.getElementFromSelector(element);
|
||||
if (selected) {
|
||||
@@ -1555,7 +1555,7 @@
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$8, function (event) {
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$7, function (event) {
|
||||
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
|
||||
if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {
|
||||
event.preventDefault();
|
||||
@@ -1567,434 +1567,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap datepicker.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME$e = 'datepicker';
|
||||
const DATA_KEY$a = 'bs.datepicker';
|
||||
const EVENT_KEY$a = `.${DATA_KEY$a}`;
|
||||
const DATA_API_KEY$6 = '.data-api';
|
||||
const EVENT_CHANGE = `change${EVENT_KEY$a}`;
|
||||
const EVENT_SHOW$6 = `show${EVENT_KEY$a}`;
|
||||
const EVENT_SHOWN$6 = `shown${EVENT_KEY$a}`;
|
||||
const EVENT_HIDE$6 = `hide${EVENT_KEY$a}`;
|
||||
const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$a}`;
|
||||
const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const EVENT_FOCUSIN_DATA_API = `focusin${EVENT_KEY$a}${DATA_API_KEY$6}`;
|
||||
const SELECTOR_DATA_TOGGLE$7 = '[data-bs-toggle="datepicker"]';
|
||||
const HIDE_DELAY = 100; // ms delay before hiding after selection
|
||||
|
||||
const Default$d = {
|
||||
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$d = {
|
||||
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$d;
|
||||
}
|
||||
static get DefaultType() {
|
||||
return DefaultType$d;
|
||||
}
|
||||
static get NAME() {
|
||||
return NAME$e;
|
||||
}
|
||||
|
||||
// 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 || isDisabled(this._element) || this._isShown) {
|
||||
return;
|
||||
}
|
||||
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.show();
|
||||
this._isShown = true;
|
||||
EventHandler.trigger(this._element, EVENT_SHOWN$6);
|
||||
}
|
||||
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$6);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
this._calendar.hide();
|
||||
this._isShown = false;
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN$6);
|
||||
}
|
||||
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$4, SELECTOR_DATA_TOGGLE$7, 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$7, 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$a}${DATA_API_KEY$6}`, () => {
|
||||
for (const element of document.querySelectorAll(`${SELECTOR_DATA_TOGGLE$7}[data-bs-inline="true"]`)) {
|
||||
Datepicker.getOrCreateInstance(element);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap dialog.js
|
||||
@@ -5868,7 +5440,6 @@
|
||||
Button,
|
||||
Carousel,
|
||||
Collapse,
|
||||
Datepicker,
|
||||
Dialog,
|
||||
Dropdown,
|
||||
Offcanvas,
|
||||
|
||||
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-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap base-component.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap button.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap carousel.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap collapse.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
-443
@@ -1,443 +0,0 @@
|
||||
/*!
|
||||
* 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
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap dialog.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap data.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap event-handler.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap manipulator.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap selector-engine.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap dropdown.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap offcanvas.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap otp-input.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap popover.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap scrollspy.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap strength.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap tab.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap toast.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap toggler.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap tooltip.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap backdrop.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap component-functions.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap config.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap floating-ui.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap focustrap.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap index.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap sanitizer.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap scrollbar.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap swipe.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap template-factory.js v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* 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) {
|
||||
|
||||
@@ -9,7 +9,6 @@ export { default as Alert } from './src/alert.js'
|
||||
export { default as Button } from './src/button.js'
|
||||
export { default as Carousel } from './src/carousel.js'
|
||||
export { default as Collapse } from './src/collapse.js'
|
||||
export { default as Datepicker } from './src/datepicker.js'
|
||||
export { default as Dialog } from './src/dialog.js'
|
||||
export { default as Dropdown } from './src/dropdown.js'
|
||||
export { default as Offcanvas } from './src/offcanvas.js'
|
||||
|
||||
@@ -9,7 +9,6 @@ import Alert from './src/alert.js'
|
||||
import Button from './src/button.js'
|
||||
import Carousel from './src/carousel.js'
|
||||
import Collapse from './src/collapse.js'
|
||||
import Datepicker from './src/datepicker.js'
|
||||
import Dialog from './src/dialog.js'
|
||||
import Dropdown from './src/dropdown.js'
|
||||
import Offcanvas from './src/offcanvas.js'
|
||||
@@ -27,7 +26,6 @@ export default {
|
||||
Button,
|
||||
Carousel,
|
||||
Collapse,
|
||||
Datepicker,
|
||||
Dialog,
|
||||
Dropdown,
|
||||
Offcanvas,
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap datepicker.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { Calendar } from 'vanilla-calendar-pro'
|
||||
import BaseComponent from './base-component.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import { isDisabled } from './util/index.js'
|
||||
|
||||
/**
|
||||
* 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 || 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 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)
|
||||
}
|
||||
})
|
||||
|
||||
export default Datepicker
|
||||
File diff suppressed because it is too large
Load Diff
@@ -243,40 +243,6 @@ describe('OtpInput', () => {
|
||||
|
||||
expect(inputs[0].value).toEqual('1')
|
||||
})
|
||||
|
||||
it('should distribute multi-character input across inputs', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
new OtpInput(otpEl) // eslint-disable-line no-new
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
inputs[0].focus()
|
||||
// Simulate autofill that puts multiple characters in first input
|
||||
inputs[0].value = '1234'
|
||||
inputs[0].dispatchEvent(createEvent('input'))
|
||||
|
||||
expect(inputs[0].value).toEqual('1')
|
||||
expect(inputs[1].value).toEqual('2')
|
||||
expect(inputs[2].value).toEqual('3')
|
||||
expect(inputs[3].value).toEqual('4')
|
||||
expect(document.activeElement).toEqual(inputs[3])
|
||||
})
|
||||
|
||||
it('should not advance when entering digit in last input', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
new OtpInput(otpEl) // eslint-disable-line no-new
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
inputs[5].focus()
|
||||
inputs[5].value = '9'
|
||||
inputs[5].dispatchEvent(createEvent('input'))
|
||||
|
||||
expect(inputs[5].value).toEqual('9')
|
||||
expect(document.activeElement).toEqual(inputs[5])
|
||||
})
|
||||
})
|
||||
|
||||
describe('keydown handling', () => {
|
||||
@@ -335,86 +301,6 @@ describe('OtpInput', () => {
|
||||
|
||||
expect(document.activeElement).toEqual(inputs[3])
|
||||
})
|
||||
|
||||
it('should not navigate left when at first input', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
new OtpInput(otpEl) // eslint-disable-line no-new
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
inputs[0].focus()
|
||||
|
||||
const arrowEvent = new KeyboardEvent('keydown', {
|
||||
key: 'ArrowLeft',
|
||||
bubbles: true
|
||||
})
|
||||
inputs[0].dispatchEvent(arrowEvent)
|
||||
|
||||
expect(document.activeElement).toEqual(inputs[0])
|
||||
})
|
||||
|
||||
it('should not navigate right when at last input', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
new OtpInput(otpEl) // eslint-disable-line no-new
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
inputs[5].focus()
|
||||
|
||||
const arrowEvent = new KeyboardEvent('keydown', {
|
||||
key: 'ArrowRight',
|
||||
bubbles: true
|
||||
})
|
||||
inputs[5].dispatchEvent(arrowEvent)
|
||||
|
||||
expect(document.activeElement).toEqual(inputs[5])
|
||||
})
|
||||
|
||||
it('should shift values left on Delete key', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
const otp = new OtpInput(otpEl)
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
otp.setValue('123456')
|
||||
inputs[2].focus()
|
||||
|
||||
const deleteEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Delete',
|
||||
bubbles: true
|
||||
})
|
||||
inputs[2].dispatchEvent(deleteEvent)
|
||||
|
||||
expect(inputs[0].value).toEqual('1')
|
||||
expect(inputs[1].value).toEqual('2')
|
||||
expect(inputs[2].value).toEqual('4')
|
||||
expect(inputs[3].value).toEqual('5')
|
||||
expect(inputs[4].value).toEqual('6')
|
||||
expect(inputs[5].value).toEqual('')
|
||||
})
|
||||
|
||||
it('should not move focus on backspace when current input has value', () => {
|
||||
fixtureEl.innerHTML = getOtpHtml()
|
||||
|
||||
const otpEl = fixtureEl.querySelector('.otp-input')
|
||||
new OtpInput(otpEl) // eslint-disable-line no-new
|
||||
const inputs = otpEl.querySelectorAll('input')
|
||||
|
||||
inputs[1].value = '5'
|
||||
inputs[1].focus()
|
||||
|
||||
const backspaceEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Backspace',
|
||||
bubbles: true
|
||||
})
|
||||
inputs[1].dispatchEvent(backspaceEvent)
|
||||
|
||||
// Should stay on same input (browser handles clearing the value)
|
||||
expect(document.activeElement).toEqual(inputs[1])
|
||||
})
|
||||
})
|
||||
|
||||
describe('paste handling', () => {
|
||||
|
||||
@@ -106,40 +106,6 @@ describe('Strength', () => {
|
||||
|
||||
expect(strength._input).toEqual(otherInput)
|
||||
})
|
||||
|
||||
it('should handle missing input gracefully', () => {
|
||||
fixtureEl.innerHTML = `
|
||||
<div class="strength" data-bs-strength>
|
||||
<div class="strength-segment"></div>
|
||||
<div class="strength-segment"></div>
|
||||
<div class="strength-segment"></div>
|
||||
<div class="strength-segment"></div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const strengthEl = fixtureEl.querySelector('.strength')
|
||||
const strength = new Strength(strengthEl)
|
||||
|
||||
expect(strength._input).toBeNull()
|
||||
expect(strength.getStrength()).toBeNull()
|
||||
})
|
||||
|
||||
it('should use element directly when input config is an element', () => {
|
||||
fixtureEl.innerHTML = `
|
||||
<div>
|
||||
<input type="password" id="my-password" class="form-control">
|
||||
</div>
|
||||
<div class="strength" data-bs-strength>
|
||||
<div class="strength-segment"></div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const strengthEl = fixtureEl.querySelector('.strength')
|
||||
const inputEl = fixtureEl.querySelector('#my-password')
|
||||
const strength = new Strength(strengthEl, { input: inputEl })
|
||||
|
||||
expect(strength._input).toEqual(inputEl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getStrength', () => {
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="../../../dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<title>Datepicker</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container py-5">
|
||||
<h1>Datepicker <small class="text-body-secondary">Bootstrap Visual Test</small></h1>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Basic Input Datepicker</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="basicDatepicker" class="form-label">Select a date</label>
|
||||
<input type="text" class="form-control" id="basicDatepicker" data-bs-toggle="datepicker" placeholder="Click to select">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>With Min/Max Dates</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="minMaxDatepicker" class="form-label">Only dates in 2025</label>
|
||||
<input type="text" class="form-control" id="minMaxDatepicker" data-bs-toggle="datepicker" data-bs-date-min="2025-01-01" data-bs-date-max="2025-12-31" placeholder="Select a date in 2025">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Multiple Selection</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="multipleDatepicker" class="form-label">Select multiple dates</label>
|
||||
<input type="text" class="form-control" id="multipleDatepicker" data-bs-toggle="datepicker" data-bs-selection-mode="multiple" placeholder="Click to select multiple">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Range Selection</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="rangeDatepicker" class="form-label">Select a date range</label>
|
||||
<input type="text" class="form-control" id="rangeDatepicker" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged" placeholder="Select start and end">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Week Numbers</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="weekNumbersDatepicker" class="form-label">With week numbers</label>
|
||||
<input type="text" class="form-control" id="weekNumbersDatepicker" data-bs-toggle="datepicker" data-bs-show-week-numbers="true" placeholder="Select a date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Sunday First</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="sundayFirstDatepicker" class="form-label">Week starts on Sunday</label>
|
||||
<input type="text" class="form-control" id="sundayFirstDatepicker" data-bs-toggle="datepicker" data-bs-first-weekday="0" placeholder="Select a date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Dark Mode</h2>
|
||||
<div class="row mb-4" data-bs-theme="dark">
|
||||
<div class="col-md-6">
|
||||
<div class="p-4 bg-dark rounded">
|
||||
<label for="darkDatepicker" class="form-label text-light">Dark mode datepicker</label>
|
||||
<input type="text" class="form-control" id="darkDatepicker" data-bs-toggle="datepicker" placeholder="Select a date">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>JavaScript Initialization</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="jsDatepicker" class="form-label">Initialized via JavaScript</label>
|
||||
<input type="text" class="form-control" id="jsDatepicker" placeholder="Click to select">
|
||||
<div class="mt-2">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="showBtn">Show</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="hideBtn">Hide</button>
|
||||
<button type="button" class="btn btn-info btn-sm" id="getDatesBtn">Get Dates</button>
|
||||
</div>
|
||||
<div id="selectedDatesOutput" class="mt-2 text-body-secondary"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Events Test</h2>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="eventsDatepicker" class="form-label">Events datepicker</label>
|
||||
<input type="text" class="form-control" id="eventsDatepicker" data-bs-toggle="datepicker" placeholder="Select a date">
|
||||
<div id="eventsLog" class="mt-2 p-2 bg-light border rounded" style="min-height: 100px; font-family: monospace; font-size: 12px; white-space: pre-wrap;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../../../dist/js/bootstrap.bundle.js"></script>
|
||||
<script>
|
||||
/* global bootstrap: false */
|
||||
|
||||
// Initialize via JavaScript
|
||||
const jsDatepickerEl = document.getElementById('jsDatepicker')
|
||||
const jsDatepicker = new bootstrap.Datepicker(jsDatepickerEl, {
|
||||
firstWeekday: 1,
|
||||
selectedDates: ['2025-01-15']
|
||||
})
|
||||
|
||||
document.getElementById('showBtn').addEventListener('click', () => {
|
||||
jsDatepicker.show()
|
||||
})
|
||||
|
||||
document.getElementById('hideBtn').addEventListener('click', () => {
|
||||
jsDatepicker.hide()
|
||||
})
|
||||
|
||||
document.getElementById('getDatesBtn').addEventListener('click', () => {
|
||||
const dates = jsDatepicker.getSelectedDates()
|
||||
document.getElementById('selectedDatesOutput').textContent = `Selected: ${dates.length ? dates.join(', ') : 'None'}`
|
||||
})
|
||||
|
||||
// Events test
|
||||
const eventsDatepickerEl = document.getElementById('eventsDatepicker')
|
||||
const eventsLog = document.getElementById('eventsLog')
|
||||
|
||||
function logEvent(eventName, detail) {
|
||||
const timestamp = new Date().toLocaleTimeString()
|
||||
const detailStr = detail ? ` - ${JSON.stringify(detail)}` : ''
|
||||
eventsLog.textContent += `[${timestamp}] ${eventName}${detailStr}\n`
|
||||
eventsLog.scrollTop = eventsLog.scrollHeight
|
||||
}
|
||||
|
||||
eventsDatepickerEl.addEventListener('show.bs.datepicker', () => {
|
||||
logEvent('show.bs.datepicker')
|
||||
})
|
||||
|
||||
eventsDatepickerEl.addEventListener('shown.bs.datepicker', () => {
|
||||
logEvent('shown.bs.datepicker')
|
||||
})
|
||||
|
||||
eventsDatepickerEl.addEventListener('hide.bs.datepicker', () => {
|
||||
logEvent('hide.bs.datepicker')
|
||||
})
|
||||
|
||||
eventsDatepickerEl.addEventListener('hidden.bs.datepicker', () => {
|
||||
logEvent('hidden.bs.datepicker')
|
||||
})
|
||||
|
||||
eventsDatepickerEl.addEventListener('change.bs.datepicker', event => {
|
||||
logEvent('change.bs.datepicker', { dates: event.dates })
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+128
-311
@@ -19,8 +19,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-prefix-custom-properties": "^0.1.0",
|
||||
"vanilla-calendar-pro": "^3.0.5"
|
||||
"postcss-prefix-custom-properties": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.5",
|
||||
@@ -47,6 +46,7 @@
|
||||
"autoprefixer": "^10.4.22",
|
||||
"bootstrap-vscode-theme": "^0.0.9",
|
||||
"bundlewatch": "^0.4.1",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"clipboard": "^2.0.11",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "8.57.1",
|
||||
@@ -74,7 +74,6 @@
|
||||
"karma-jasmine": "^5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.1.0",
|
||||
"karma-rollup-preprocessor": "7.0.7",
|
||||
"lightningcss": "^1.30.2",
|
||||
"lockfile-lint": "^4.14.1",
|
||||
"markdownlint-cli": "^0.45.0",
|
||||
"mime": "^4.1.0",
|
||||
@@ -94,7 +93,6 @@
|
||||
"shelljs": "^0.10.0",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-twbs-bootstrap": "^16.1.0",
|
||||
"stylelint-order": "^7.0.1",
|
||||
"terser": "^5.44.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"zod": "^4.1.12"
|
||||
@@ -6488,6 +6486,122 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"source-map": "~0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.3.tgz",
|
||||
"integrity": "sha512-MUAta8pEqA/d2DKQwtZU5nm0Og8TCyAglOx3GlWwjhGdKBwY4kVF6E5M6LU/jmmuswv+HbYqG/dKKkq5p1dD0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"clean-css": "^5.3.3",
|
||||
"commander": "7.x",
|
||||
"glob": "^7.1.6"
|
||||
},
|
||||
"bin": {
|
||||
"cleancss": "bin/cleancss"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css-cli/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-regexp": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
|
||||
@@ -7186,6 +7300,7 @@
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -11747,267 +11862,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-android-arm64": "1.30.2",
|
||||
"lightningcss-darwin-arm64": "1.30.2",
|
||||
"lightningcss-darwin-x64": "1.30.2",
|
||||
"lightningcss-freebsd-x64": "1.30.2",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.30.2",
|
||||
"lightningcss-linux-arm64-gnu": "1.30.2",
|
||||
"lightningcss-linux-arm64-musl": "1.30.2",
|
||||
"lightningcss-linux-x64-gnu": "1.30.2",
|
||||
"lightningcss-linux-x64-musl": "1.30.2",
|
||||
"lightningcss-win32-arm64-msvc": "1.30.2",
|
||||
"lightningcss-win32-x64-msvc": "1.30.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-android-arm64": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
|
||||
"integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
|
||||
"integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
|
||||
"integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
|
||||
"integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
|
||||
"integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
|
||||
"integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
|
||||
"integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
|
||||
"integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
|
||||
"integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
|
||||
"integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
|
||||
"integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
@@ -15945,9 +15799,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-sorting": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-9.1.0.tgz",
|
||||
"integrity": "sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==",
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz",
|
||||
"integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@@ -18523,30 +18377,6 @@
|
||||
"stylelint": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint-config-recess-order/node_modules/postcss-sorting": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz",
|
||||
"integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4.20"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint-config-recess-order/node_modules/stylelint-order": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz",
|
||||
"integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss": "^8.4.32",
|
||||
"postcss-sorting": "^8.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint-config-recommended": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz",
|
||||
@@ -18675,20 +18505,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint-order": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-7.0.1.tgz",
|
||||
"integrity": "sha512-GWPei1zBVDDjxM+/BmcSCiOcHNd8rSqW6FUZtqQGlTRpD0Z5nSzspzWD8rtKif5KPdzUG68DApKEV/y/I9VbTw==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz",
|
||||
"integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-sorting": "^9.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
"postcss": "^8.4.32",
|
||||
"postcss-sorting": "^8.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"stylelint": "^16.18.0 || ^17.0.0"
|
||||
"stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint-scss": {
|
||||
@@ -19910,16 +19737,6 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vanilla-calendar-pro": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-calendar-pro/-/vanilla-calendar-pro-3.0.5.tgz",
|
||||
"integrity": "sha512-4X9bmTo1/KzbZrB7B6mZXtvVXIhcKxaVSnFZuaVtps7tshKJDxgaIElkgdia6IjB5qWetWuu7kZ+ZaV1sPxy6w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://buymeacoffee.com/uvarov"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
||||
+3
-5
@@ -49,7 +49,7 @@
|
||||
"css-lint-stylelint": "stylelint \"**/*.{css,scss}\" --cache --cache-location .cache/.stylelintcache",
|
||||
"css-lint-vars": "fusv scss/ site/src/scss/",
|
||||
"css-minify": "npm-run-all --aggregate-output --parallel css-minify-*",
|
||||
"css-minify-main": "node build/css-minify.mjs",
|
||||
"css-minify-main": "cleancss -O1 --format breakWith=lf --with-rebase --source-map --source-map-inline-sources --output dist/css/ --batch --batch-suffix \".min\" \"dist/css/*.css\" \"!dist/css/*.min.css\" \"!dist/css/*.tmp.css\"",
|
||||
"css-prefix": "npm-run-all --aggregate-output --parallel css-prefix-*",
|
||||
"css-prefix-main": "postcss --config build/postcss.config.mjs --replace \"dist/css/*.css\" \"!dist/css/*.min.css\" \"!dist/css/*.tmp.css\"",
|
||||
"css-prefix-examples": "postcss --config build/postcss.config.mjs --replace \"site/src/assets/examples/**/*.css\"",
|
||||
@@ -130,7 +130,7 @@
|
||||
"autoprefixer": "^10.4.22",
|
||||
"bootstrap-vscode-theme": "^0.0.9",
|
||||
"bundlewatch": "^0.4.1",
|
||||
"lightningcss": "^1.30.2",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"clipboard": "^2.0.11",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "8.57.1",
|
||||
@@ -177,7 +177,6 @@
|
||||
"shelljs": "^0.10.0",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-twbs-bootstrap": "^16.1.0",
|
||||
"stylelint-order": "^7.0.1",
|
||||
"terser": "^5.44.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"zod": "^4.1.12"
|
||||
@@ -211,7 +210,6 @@
|
||||
"volar-service-emmet": "0.0.63"
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss-prefix-custom-properties": "^0.1.0",
|
||||
"vanilla-calendar-pro": "^3.0.5"
|
||||
"postcss-prefix-custom-properties": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
// stylelint-disable selector-max-attribute, property-disallowed-list, selector-no-qualifying-type -- VCP uses extensive data attributes and requires direct border-radius properties for range selection
|
||||
|
||||
@use "config" as *;
|
||||
@use "colors" as *;
|
||||
@use "variables" as *;
|
||||
@use "mixins/border-radius" as *;
|
||||
@use "mixins/focus-ring" as *;
|
||||
|
||||
// scss-docs-start datepicker-variables
|
||||
$datepicker-padding: 1rem !default;
|
||||
$datepicker-bg: var(--bg-body) !default;
|
||||
$datepicker-color: var(--fg-body) !default;
|
||||
$datepicker-border-color: var(--border-color-translucent) !default;
|
||||
$datepicker-border-width: var(--border-width) !default;
|
||||
$datepicker-border-radius: var(--border-radius-lg) !default;
|
||||
$datepicker-box-shadow: var(--box-shadow) !default;
|
||||
$datepicker-font-size: var(--font-size-sm) !default;
|
||||
$datepicker-min-width: 280px !default;
|
||||
|
||||
$datepicker-header-font-weight: 600 !default;
|
||||
$datepicker-weekday-color: var(--fg-3) !default;
|
||||
$datepicker-day-hover-bg: var(--bg-1) !default;
|
||||
$datepicker-day-selected-bg: var(--primary-bg) !default;
|
||||
$datepicker-day-selected-color: var(--primary-contrast) !default;
|
||||
$datepicker-day-today-bg: var(--bg-2) !default;
|
||||
$datepicker-day-today-color: var(--fg-1) !default;
|
||||
$datepicker-day-disabled-color: var(--fg-4) !default;
|
||||
// scss-docs-end datepicker-variables
|
||||
|
||||
@layer components {
|
||||
[data-vc="calendar"] {
|
||||
// scss-docs-start datepicker-css-vars
|
||||
--datepicker-padding: #{$datepicker-padding};
|
||||
--datepicker-bg: #{$datepicker-bg};
|
||||
--datepicker-color: #{$datepicker-color};
|
||||
--datepicker-border-color: #{$datepicker-border-color};
|
||||
--datepicker-border-width: #{$datepicker-border-width};
|
||||
--datepicker-border-radius: #{$datepicker-border-radius};
|
||||
--datepicker-box-shadow: #{$datepicker-box-shadow};
|
||||
--datepicker-font-size: #{$datepicker-font-size};
|
||||
--datepicker-min-width: #{$datepicker-min-width};
|
||||
--datepicker-zindex: #{$zindex-dropdown};
|
||||
|
||||
--datepicker-header-font-weight: #{$datepicker-header-font-weight};
|
||||
--datepicker-weekday-color: #{$datepicker-weekday-color};
|
||||
--datepicker-day-hover-bg: #{$datepicker-day-hover-bg};
|
||||
--datepicker-day-selected-bg: #{$datepicker-day-selected-bg};
|
||||
--datepicker-day-selected-color: #{$datepicker-day-selected-color};
|
||||
--datepicker-day-today-bg: #{$datepicker-day-today-bg};
|
||||
--datepicker-day-today-color: #{$datepicker-day-today-color};
|
||||
--datepicker-day-disabled-color: #{$datepicker-day-disabled-color};
|
||||
// scss-docs-end datepicker-css-vars
|
||||
|
||||
position: absolute;
|
||||
z-index: var(--datepicker-zindex);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: var(--datepicker-min-width);
|
||||
padding: var(--datepicker-padding);
|
||||
font-family: var(--font-sans-serif);
|
||||
font-size: var(--datepicker-font-size);
|
||||
color: var(--datepicker-color);
|
||||
color-scheme: light dark;
|
||||
background-color: var(--datepicker-bg);
|
||||
border: var(--datepicker-border-width) solid var(--datepicker-border-color);
|
||||
@include border-radius(var(--datepicker-border-radius));
|
||||
box-shadow: var(--datepicker-box-shadow);
|
||||
opacity: 1;
|
||||
|
||||
// Respond to Bootstrap's color mode system
|
||||
&[data-bs-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
&[data-bs-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
// Catch-all for focus styles
|
||||
button:focus-visible {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@include focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
[data-vc-calendar-hidden] {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// Inline calendars
|
||||
//
|
||||
// Remove popover styling for more neutral styling
|
||||
[data-vc="calendar"]:not([data-vc-input]) {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[data-vc-position="bottom"] {
|
||||
margin-block-start: .25rem;
|
||||
}
|
||||
|
||||
[data-vc-position="top"] {
|
||||
margin-block-end: -.25rem;
|
||||
}
|
||||
|
||||
[data-vc-arrow] {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: var(--datepicker-color);
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
@include border-radius($border-radius);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
inset: .25rem;
|
||||
content: "";
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%236b7280' d='M12 16c-.3 0-.5-.1-.7-.3l-6-6c-.4-.4-.4-1 0-1.4s1-.4 1.4 0l5.3 5.3 5.3-5.3c.4-.4 1-.4 1.4 0s.4 1 0 1.4l-6 6c-.2.2-.4.3-.7.3'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--datepicker-day-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
[data-vc-arrow="prev"]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
[data-vc-arrow="next"]::before {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
// Grid layout
|
||||
[data-vc="controls"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-vc="grid"] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.75rem;
|
||||
}
|
||||
|
||||
[data-vc="column"] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
//
|
||||
// Header
|
||||
//
|
||||
|
||||
[data-vc="header"] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
|
||||
// Month and year
|
||||
[data-vc-header="content"] {
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
[data-vc="month"],
|
||||
[data-vc="year"] {
|
||||
padding: .25rem .5rem;
|
||||
margin-inline: -.125rem;
|
||||
font-size: 1rem;
|
||||
font-weight: var(--datepicker-header-font-weight);
|
||||
color: var(--datepicker-color);
|
||||
// cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
@include border-radius($border-radius);
|
||||
|
||||
&:disabled {
|
||||
color: var(--datepicker-day-disabled-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--datepicker-day-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[data-vc="content"] {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
// Month/Year grids
|
||||
[data-vc="months"],
|
||||
[data-vc="years"] {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
grid-template-columns: repeat(var(--vc-columns, 4), minmax(0, 1fr));
|
||||
row-gap: 1rem;
|
||||
column-gap: .25rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[data-vc="years"] {
|
||||
--vc-columns: 5;
|
||||
}
|
||||
|
||||
[data-vc-months-month],
|
||||
[data-vc-years-year] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 2.5rem;
|
||||
padding: .25rem;
|
||||
font-size: .75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
color: var(--datepicker-weekday-color);
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
@include border-radius($border-radius);
|
||||
|
||||
&:disabled {
|
||||
color: var(--datepicker-day-disabled-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--datepicker-day-hover-bg);
|
||||
}
|
||||
|
||||
&[data-vc-months-month-selected],
|
||||
&[data-vc-years-year-selected] {
|
||||
color: var(--datepicker-day-selected-color);
|
||||
background-color: var(--datepicker-day-selected-bg);
|
||||
|
||||
&:hover {
|
||||
color: var(--datepicker-day-selected-color);
|
||||
background-color: var(--datepicker-day-selected-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Week days header
|
||||
[data-vc="week"] {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
justify-items: center;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
[data-vc-week-day] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 1.875rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: .75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
color: var(--datepicker-weekday-color);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
button[data-vc-week-day] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Dates grid
|
||||
[data-vc="dates"] {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-vc-date] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding-top: .125rem;
|
||||
padding-bottom: .125rem;
|
||||
pointer-events: auto;
|
||||
|
||||
&:not(:has([data-vc-date-btn])),
|
||||
&[data-vc-date-disabled],
|
||||
&[data-vc-date-disabled] [data-vc-date-btn] {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Date button
|
||||
[data-vc-date-btn] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 1.875rem;
|
||||
height: 100%;
|
||||
min-height: 1.875rem;
|
||||
padding: 0;
|
||||
font-size: .75rem;
|
||||
font-weight: 400;
|
||||
line-height: 1rem;
|
||||
color: var(--datepicker-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: $border-radius;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--datepicker-day-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
// Today
|
||||
[data-vc-date-today] [data-vc-date-btn] {
|
||||
font-weight: 600;
|
||||
color: var(--datepicker-day-today-color);
|
||||
background-color: var(--datepicker-day-today-bg);
|
||||
}
|
||||
|
||||
|
||||
// Outside month
|
||||
[data-vc-date-month="next"] [data-vc-date-btn],
|
||||
[data-vc-date-month="prev"] [data-vc-date-btn] {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
// Disabled
|
||||
[data-vc-date-disabled] [data-vc-date-btn] {
|
||||
color: var(--datepicker-day-disabled-color);
|
||||
}
|
||||
|
||||
// Range selection styles
|
||||
[data-vc-date-hover] [data-vc-date-btn] {
|
||||
background-color: var(--datepicker-day-hover-bg);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
[data-vc-date-hover="first"] [data-vc-date-btn] {
|
||||
border-start-start-radius: $border-radius;
|
||||
border-end-start-radius: $border-radius;
|
||||
}
|
||||
|
||||
[data-vc-date-hover="last"] [data-vc-date-btn] {
|
||||
border-start-end-radius: $border-radius;
|
||||
border-end-end-radius: $border-radius;
|
||||
}
|
||||
|
||||
[data-vc-date-hover="first-and-last"] [data-vc-date-btn] {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
[data-vc-date-selected="middle"] [data-vc-date-btn] {
|
||||
border-radius: 0;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
// Selected
|
||||
[data-vc-date-selected] [data-vc-date-btn] {
|
||||
color: var(--datepicker-day-selected-color);
|
||||
background-color: var(--datepicker-day-selected-bg);
|
||||
|
||||
}
|
||||
|
||||
[data-vc-date-selected="first"] [data-vc-date-btn] {
|
||||
border-top-left-radius: $border-radius;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: $border-radius;
|
||||
}
|
||||
|
||||
[data-vc-date-selected="last"] [data-vc-date-btn] {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: $border-radius;
|
||||
border-bottom-right-radius: $border-radius;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
[data-vc-date-selected="first-and-last"] [data-vc-date-btn] {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -89,9 +89,9 @@ $nav-underline-link-active-color: var(--emphasis-color) !default;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
--focus-ring-offset: 1px;
|
||||
color: var(--nav-link-hover-color);
|
||||
@include focus-ring(true);
|
||||
--focus-ring-offset: 1px;
|
||||
}
|
||||
|
||||
&.active,
|
||||
|
||||
+17
-15
@@ -11,10 +11,9 @@
|
||||
@layer colors, theme, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;
|
||||
|
||||
:root {
|
||||
// scss-docs-start root-theme-variables
|
||||
--black: #{$black};
|
||||
--white: #{$white};
|
||||
color-scheme: light dark;
|
||||
|
||||
// scss-docs-start root-theme-variables
|
||||
// Generate semantic theme colors
|
||||
@each $color-name, $color-map in $new-theme-colors {
|
||||
@each $key, $value in $color-map {
|
||||
@@ -33,13 +32,15 @@
|
||||
@each $color, $value in $theme-borders {
|
||||
--border-#{$color}: #{$value};
|
||||
}
|
||||
// scss-docs-end root-theme-variables
|
||||
|
||||
color-scheme: light dark;
|
||||
--black: #{$black};
|
||||
--white: #{$white};
|
||||
// scss-docs-end root-theme-variables
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-bs-theme="light"] {
|
||||
color-scheme: light;
|
||||
|
||||
// Note: Custom variable values only support SassScript inside `#{}`.
|
||||
|
||||
@@ -53,6 +54,12 @@
|
||||
--font-sans-serif: #{meta.inspect($font-family-sans-serif)};
|
||||
--font-monospace: #{meta.inspect($font-family-monospace)};
|
||||
--gradient: #{$gradient};
|
||||
|
||||
// Root and body
|
||||
// scss-docs-start root-body-variables
|
||||
@if $font-size-root != null {
|
||||
--root-font-size: #{$font-size-root};
|
||||
}
|
||||
--body-font-family: #{meta.inspect($font-family-base)};
|
||||
|
||||
// scss-docs-start root-font-size-variables
|
||||
@@ -84,9 +91,13 @@
|
||||
--body-font-size: #{$font-size-base};
|
||||
--body-font-weight: #{$font-weight-base};
|
||||
--body-line-height: #{$line-height-base};
|
||||
@if $body-text-align != null {
|
||||
--body-text-align: #{$body-text-align};
|
||||
}
|
||||
|
||||
--body-color-rgb: #{to-rgb($body-color)};
|
||||
--body-bg-rgb: #{to-rgb($body-bg)};
|
||||
// scss-docs-end root-body-variables
|
||||
|
||||
--heading-color: #{$headings-color};
|
||||
|
||||
@@ -134,20 +145,12 @@
|
||||
--form-valid-border-color: #{$form-valid-border-color};
|
||||
--form-invalid-color: #{$form-invalid-color};
|
||||
--form-invalid-border-color: #{$form-invalid-border-color};
|
||||
color-scheme: light;
|
||||
// scss-docs-end root-form-validation-variables
|
||||
|
||||
// Root and body
|
||||
@if $font-size-root != null {
|
||||
--root-font-size: #{$font-size-root};
|
||||
}
|
||||
@if $body-text-align != null {
|
||||
--body-text-align: #{$body-text-align};
|
||||
}
|
||||
}
|
||||
|
||||
@if $enable-dark-mode {
|
||||
@include color-mode(dark, true) {
|
||||
color-scheme: dark;
|
||||
|
||||
// scss-docs-start root-dark-mode-vars
|
||||
--emphasis-color: #{$body-emphasis-color-dark};
|
||||
@@ -161,7 +164,6 @@
|
||||
--form-valid-border-color: #{$form-valid-border-color-dark};
|
||||
--form-invalid-color: #{$form-invalid-color-dark};
|
||||
--form-invalid-border-color: #{$form-invalid-border-color-dark};
|
||||
color-scheme: dark;
|
||||
// scss-docs-end root-dark-mode-vars
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -17,7 +17,6 @@
|
||||
@forward "breadcrumb";
|
||||
@forward "card";
|
||||
@forward "carousel";
|
||||
@forward "datepicker";
|
||||
@forward "dialog";
|
||||
@forward "dropdown";
|
||||
@forward "list-group";
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
@use "../config" as *;
|
||||
@use "../variables" as *;
|
||||
@use "../mixins/border-radius" as *;
|
||||
@use "../mixins/box-shadow" as *;
|
||||
@use "../mixins/focus-ring" as *;
|
||||
@use "../mixins/transition" as *;
|
||||
@use "form-variables" as *;
|
||||
|
||||
// scss-docs-start form-adorn-variables
|
||||
$form-adorn-gap: .375rem !default;
|
||||
$form-adorn-icon-size: 1rem !default;
|
||||
$form-adorn-icon-color: var(--fg-2) !default;
|
||||
// scss-docs-end form-adorn-variables
|
||||
|
||||
@layer forms {
|
||||
.form-adorn {
|
||||
// Inherit form-control CSS variables for sizing
|
||||
--control-min-height: #{$control-min-height};
|
||||
--control-padding-y: #{$control-padding-y};
|
||||
--control-padding-x: #{$control-padding-x};
|
||||
--control-font-size: #{$control-font-size};
|
||||
--control-line-height: #{$control-line-height};
|
||||
--control-color: #{$control-color};
|
||||
--control-bg: #{$control-bg};
|
||||
--control-border-width: #{$control-border-width};
|
||||
--control-border-color: #{$control-border-color};
|
||||
--control-border-radius: #{$control-border-radius};
|
||||
|
||||
// Adorn-specific variables
|
||||
--form-adorn-gap: #{$form-adorn-gap};
|
||||
--form-adorn-icon-size: #{$form-adorn-icon-size};
|
||||
--form-adorn-icon-color: #{$form-adorn-icon-color};
|
||||
|
||||
// Flexbox layout
|
||||
display: flex;
|
||||
gap: var(--form-adorn-gap);
|
||||
align-items: center;
|
||||
|
||||
// Replicate .form-control styles on the wrapper
|
||||
min-height: var(--control-min-height);
|
||||
padding: var(--control-padding-y) var(--control-padding-x);
|
||||
font-size: var(--control-font-size);
|
||||
line-height: var(--control-line-height);
|
||||
color: var(--control-color);
|
||||
background-color: var(--control-bg);
|
||||
background-clip: padding-box;
|
||||
border: var(--control-border-width) solid var(--control-border-color);
|
||||
@include border-radius(var(--control-border-radius), 0);
|
||||
@include box-shadow($input-box-shadow);
|
||||
@include transition($input-transition);
|
||||
|
||||
// Focus state when ghost input is focused
|
||||
&:focus-within {
|
||||
--focus-ring-offset: -1px;
|
||||
border-color: $input-focus-border-color;
|
||||
@include focus-ring(true);
|
||||
}
|
||||
|
||||
// Ghost input fills remaining space
|
||||
> .form-ghost {
|
||||
flex: 1;
|
||||
min-width: 0; // Prevent text overflow
|
||||
}
|
||||
|
||||
|
||||
// Adornment at end (right in LTR) - input comes first visually
|
||||
&.form-adorn-end > .form-ghost {
|
||||
order: -1;
|
||||
}
|
||||
}
|
||||
|
||||
.form-adorn-icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--form-adorn-icon-color);
|
||||
pointer-events: none;
|
||||
|
||||
> svg {
|
||||
width: var(--form-adorn-icon-size);
|
||||
height: var(--form-adorn-icon-size);
|
||||
}
|
||||
}
|
||||
|
||||
.form-adorn-text {
|
||||
flex-shrink: 0;
|
||||
color: var(--form-adorn-icon-color);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// Sizing variants
|
||||
.form-adorn-sm {
|
||||
--control-min-height: #{$control-min-height-sm};
|
||||
--control-padding-y: #{$control-padding-y-sm};
|
||||
--control-padding-x: #{$control-padding-x-sm};
|
||||
--control-font-size: #{$control-font-size-sm};
|
||||
--control-line-height: #{$control-line-height-sm};
|
||||
--control-border-radius: #{$control-border-radius-sm};
|
||||
}
|
||||
|
||||
.form-adorn-lg {
|
||||
--control-min-height: #{$control-min-height-lg};
|
||||
--control-padding-y: #{$control-padding-y-lg};
|
||||
--control-padding-x: #{$control-padding-x-lg};
|
||||
--control-font-size: #{$control-font-size-lg};
|
||||
--control-line-height: #{$control-line-height-lg};
|
||||
--control-border-radius: #{$control-border-radius-lg};
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,11 @@
|
||||
|
||||
// Customize the `:focus` state to imitate native WebKit styles.
|
||||
&:focus-visible {
|
||||
--focus-ring-offset: -1px;
|
||||
color: $input-focus-color;
|
||||
background-color: $input-focus-bg;
|
||||
border-color: $input-focus-border-color;
|
||||
@include focus-ring(true);
|
||||
--focus-ring-offset: -1px;
|
||||
}
|
||||
|
||||
&::-webkit-date-and-time-value {
|
||||
@@ -243,31 +243,4 @@
|
||||
&.form-control-sm { height: $input-height-sm; }
|
||||
&.form-control-lg { height: $input-height-lg; }
|
||||
}
|
||||
|
||||
// Ghost input - removes all visual styling
|
||||
// Used inside custom wrappers that handle their own styling
|
||||
.form-ghost {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--fg-3);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: var(--fg-4);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +112,6 @@ $input-group-addon-border-color: $input-border-color !default;
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.input-group {
|
||||
|
||||
$validation-messages: "";
|
||||
&:not(.has-validation) {
|
||||
> :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
|
||||
> .dropdown-toggle:nth-last-child(n + 3),
|
||||
@@ -132,6 +130,11 @@ $input-group-addon-border-color: $input-border-color !default;
|
||||
}
|
||||
}
|
||||
|
||||
$validation-messages: "";
|
||||
@each $state in map.keys($form-validation-states) {
|
||||
$validation-messages: $validation-messages + ":not(." + string.unquote($state) + "-tooltip)" + ":not(." + string.unquote($state) + "-feedback)";
|
||||
}
|
||||
|
||||
> :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
|
||||
margin-inline-start: calc(-1 * #{$input-border-width});
|
||||
@include border-start-radius(0);
|
||||
@@ -141,8 +144,5 @@ $input-group-addon-border-color: $input-border-color !default;
|
||||
> .form-floating:not(:first-child) > .form-select {
|
||||
@include border-start-radius(0);
|
||||
}
|
||||
@each $state in map.keys($form-validation-states) {
|
||||
$validation-messages: $validation-messages + ":not(." + string.unquote($state) + "-tooltip)" + ":not(." + string.unquote($state) + "-feedback)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ $otp-input-gap: .5rem !default;
|
||||
border-color: var(--form-valid-border-color);
|
||||
|
||||
&:focus {
|
||||
--focus-ring-color: rgba(var(--success-rgb), .25);
|
||||
border-color: var(--form-valid-border-color);
|
||||
--focus-ring-color: rgba(var(--success-rgb), .25);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ $otp-input-gap: .5rem !default;
|
||||
border-color: var(--form-invalid-border-color);
|
||||
|
||||
&:focus {
|
||||
--focus-ring-color: rgba(var(--danger-rgb), .25);
|
||||
border-color: var(--form-invalid-border-color);
|
||||
--focus-ring-color: rgba(var(--danger-rgb), .25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,4 @@
|
||||
@forward "input-group";
|
||||
@forward "strength";
|
||||
@forward "otp-input";
|
||||
@forward "form-adorn";
|
||||
@forward "validation";
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
}
|
||||
|
||||
@include media-breakpoint-up($breakpoint, $grid-breakpoints) {
|
||||
// Extend each breakpoint which is smaller or equal to the current breakpoint
|
||||
$extend-breakpoint: true;
|
||||
|
||||
%responsive-container-#{$breakpoint} {
|
||||
max-width: $container-max-width;
|
||||
}
|
||||
|
||||
// Extend each breakpoint which is smaller or equal to the current breakpoint
|
||||
$extend-breakpoint: true;
|
||||
|
||||
@each $name, $width in $grid-breakpoints {
|
||||
@if ($extend-breakpoint) {
|
||||
.container#{breakpoint-infix($name, $grid-breakpoints)} {
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
- title: Floating labels
|
||||
- title: OTP input
|
||||
- title: Password strength
|
||||
- title: Form adorn
|
||||
- title: Layout
|
||||
- title: Validation
|
||||
|
||||
@@ -93,7 +92,6 @@
|
||||
- title: Carousel
|
||||
- title: Close button
|
||||
- title: Collapse
|
||||
- title: Datepicker
|
||||
- title: Dialog
|
||||
- title: Dropdown
|
||||
- title: List group
|
||||
|
||||
@@ -35,10 +35,6 @@
|
||||
d="M1.114 8.063V7.9c1.005-.102 1.497-.615 1.497-1.6V4.503c0-1.094.39-1.538 1.354-1.538h.273V2h-.376C2.25 2 1.49 2.759 1.49 4.352v1.524c0 1.094-.376 1.456-1.49 1.456v1.299c1.114 0 1.49.362 1.49 1.456v1.524c0 1.593.759 2.352 2.372 2.352h.376v-.964h-.273c-.964 0-1.354-.444-1.354-1.538V9.663c0-.984-.492-1.497-1.497-1.6ZM14.886 7.9v.164c-1.005.103-1.497.616-1.497 1.6v1.798c0 1.094-.39 1.538-1.354 1.538h-.273v.964h.376c1.613 0 2.372-.759 2.372-2.352v-1.524c0-1.094.376-1.456 1.49-1.456v-1.3c-1.114 0-1.49-.362-1.49-1.456V4.352C14.51 2.759 13.75 2 12.138 2h-.376v.964h.273c.964 0 1.354.444 1.354 1.538V6.3c0 .984.492 1.497 1.497 1.6ZM7.5 11.5V9.207l-1.621 1.621-.707-.707L6.792 8.5H4.5v-1h2.293L5.172 5.879l.707-.707L7.5 6.792V4.5h1v2.293l1.621-1.621.707.707L9.208 7.5H11.5v1H9.207l1.621 1.621-.707.707L8.5 9.208V11.5h-1Z"
|
||||
></path>
|
||||
</symbol>
|
||||
<symbol id="calendar-week" viewBox="0 0 16 16">
|
||||
<path d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-5 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z"/>
|
||||
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5M1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4z"/>
|
||||
</symbol>
|
||||
<symbol id="check2" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"
|
||||
@@ -66,9 +62,6 @@
|
||||
d="M5.854 4.854a.5.5 0 1 0-.708-.708l-3.5 3.5a.5.5 0 0 0 0 .708l3.5 3.5a.5.5 0 0 0 .708-.708L2.707 8l3.147-3.146zm4.292 0a.5.5 0 0 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L13.293 8l-3.147-3.146z"
|
||||
></path>
|
||||
</symbol>
|
||||
<symbol id="envelope" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"/>
|
||||
</symbol>
|
||||
<symbol id="file-earmark-richtext" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"
|
||||
@@ -135,9 +128,6 @@
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"
|
||||
></path>
|
||||
</symbol>
|
||||
<symbol id="search" viewBox="0 0 16 16">
|
||||
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
|
||||
</symbol>
|
||||
<symbol id="sun-fill" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
---
|
||||
title: Datepicker
|
||||
description: A flexible date picker component powered by Vanilla Calendar Pro, with Bootstrap styling and data attribute support.
|
||||
toc: true
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Bootstrap Datepicker is a wrapper around [Vanilla Calendar Pro](https://vanilla-calendar.pro/) that provides a consistent, accessible date selection experience. It supports light/dark themes, input binding, and flexible configuration via data attributes or JavaScript.
|
||||
|
||||
<Example code={`<label for="datepicker1" class="form-label">Datepicker</label>
|
||||
<input type="text" class="form-control w-12" data-bs-toggle="datepicker" placeholder="Choose date…">`} />
|
||||
|
||||
Note that we're using a width utility of `.w-12` to ensure the input is wide enough to accommodate the date format and imply some affordance for the expected type of input.
|
||||
|
||||
## How it works
|
||||
|
||||
- Add `data-bs-toggle="datepicker"` to any `<input>` element to enable the datepicker
|
||||
- Use `type="text"` to avoid conflicts with native browser date pickers
|
||||
- When focused, the calendar popup appears below the input
|
||||
- Selecting a date updates the input value and closes the picker
|
||||
- The picker respects Bootstrap's color modes (`data-bs-theme`)
|
||||
- Configurable with any [Vanilla Calendar Pro option](https://vanilla-calendar.pro/docs/reference/settings) via `vcpOptions` when initializing with JavaScript
|
||||
|
||||
## Examples
|
||||
|
||||
### With icon
|
||||
|
||||
Use the [form adorn component](/docs/forms/form-adorn) to add a calendar icon alongside the datepicker input. When the input is inside a `.form-adorn` wrapper, the calendar automatically positions relative to the wrapper instead of the input.
|
||||
|
||||
<Example code={`<label for="datepickerIconStart" class="form-label">Select date</label>
|
||||
<div class="form-adorn w-12">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#calendar-week" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" id="datepickerIconStart" data-bs-toggle="datepicker" placeholder="Choose date…">
|
||||
</div>`} />
|
||||
|
||||
### Min & Max dates
|
||||
|
||||
Restrict the selectable date range using `data-bs-date-min` and `data-bs-date-max`.
|
||||
|
||||
<Example code={`<label for="datepicker2" class="form-label">Event date (2025 only)</label>
|
||||
<input type="text" class="form-control w-12" id="datepicker2" data-bs-toggle="datepicker" data-bs-date-min="2025-01-01" data-bs-date-max="2025-12-31" placeholder="Select a date in 2025">`} />
|
||||
|
||||
### Multiple dates
|
||||
|
||||
Enable multiple date selection with `data-bs-selection-mode="multiple"`.
|
||||
|
||||
<Example code={`<label for="datepicker3" class="form-label">Select multiple dates</label>
|
||||
<input type="text" class="form-control" id="datepicker3" data-bs-toggle="datepicker" data-bs-selection-mode="multiple" placeholder="Select date range…">`} />
|
||||
|
||||
### Multiple months
|
||||
|
||||
Display multiple months side-by-side with the `displayMonthsCount` option. This is useful for date range selection where users need to see more context.
|
||||
|
||||
<Example code={`<label for="datepickerMultiMonth" class="form-label">Select date range</label>
|
||||
<input type="text" class="form-control" id="datepickerMultiMonth" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged" data-bs-display-months-count="2" placeholder="Select start and end dates">`} />
|
||||
|
||||
### Date range
|
||||
|
||||
Select a range of dates with `data-bs-selection-mode="multiple-ranged"`. Use `data-bs-selected-dates` to preselect a date range.
|
||||
|
||||
<Example code={`<label for="datepicker4" class="form-label">Select date range</label>
|
||||
<input type="text" class="form-control" id="datepicker4" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged" data-bs-selected-dates='["2025-06-10", "2025-06-18"]' placeholder="Select start and end dates…">`} />
|
||||
|
||||
### Multi-month date range
|
||||
|
||||
For selecting date ranges that span multiple months, combine `data-bs-selection-mode="multiple-ranged"` with `data-bs-display-months-count="2"` to show two months side-by-side, making it easier for users to select across month boundaries.
|
||||
|
||||
<Example code={`<label for="datepickerRangeTwoMonths" class="form-label">Select date range</label>
|
||||
<input type="text" class="form-control" id="datepickerRangeTwoMonths" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged" data-bs-display-months-count="2" data-bs-selected-dates='["2025-06-25", "2025-07-08"]' placeholder="Select start and end dates…">`} />
|
||||
|
||||
## Options
|
||||
|
||||
### First day of week
|
||||
|
||||
Set the first day of the week (0 = Sunday, 1 = Monday, etc.) with `data-bs-first-weekday`.
|
||||
|
||||
<Example code={`<label for="datepicker6" class="form-label">Week starts on Sunday</label>
|
||||
<input type="text" class="form-control w-12" id="datepicker6" data-bs-toggle="datepicker" data-bs-first-weekday="0" placeholder="Select a date">`} />
|
||||
|
||||
### Placement
|
||||
|
||||
Control where the calendar appears relative to the input with `data-bs-placement`. Options are `left` (default), `center`, `right`, and `auto`.
|
||||
|
||||
<Example code={`<div class="d-flex gap-3">
|
||||
<div>
|
||||
<label for="datepickerLeft" class="form-label">Left aligned</label>
|
||||
<input type="text" class="form-control" id="datepickerLeft" data-bs-toggle="datepicker" data-bs-placement="left" placeholder="Left">
|
||||
</div>
|
||||
<div>
|
||||
<label for="datepickerCenter" class="form-label">Center aligned</label>
|
||||
<input type="text" class="form-control" id="datepickerCenter" data-bs-toggle="datepicker" data-bs-placement="center" placeholder="Center">
|
||||
</div>
|
||||
<div>
|
||||
<label for="datepickerRight" class="form-label">Right aligned</label>
|
||||
<input type="text" class="form-control" id="datepickerRight" data-bs-toggle="datepicker" data-bs-placement="right" placeholder="Right">
|
||||
</div>
|
||||
</div>`} />
|
||||
|
||||
### Button trigger
|
||||
|
||||
Use a button instead of an input for use cases like dashboard date filters. Add `data-bs-datepicker-display` to the text element to preserve icons when the date updates.
|
||||
|
||||
<Example code={`<button type="button" class="btn btn-outline-secondary" data-bs-toggle="datepicker">
|
||||
<svg class="bi" width="16" height="16"><use href="#calendar-week" /></svg>
|
||||
<span data-bs-datepicker-display>Select date</span>
|
||||
</button>`} />
|
||||
|
||||
For date range selection (e.g., dashboard time filters), use `data-bs-selection-mode="multiple-ranged"`. The calendar will close after both start and end dates are selected.
|
||||
|
||||
<Example code={`<button type="button" class="btn btn-outline-secondary" data-bs-toggle="datepicker" data-bs-selection-mode="multiple-ranged">
|
||||
<svg class="bi" width="16" height="16"><use href="#calendar-week" /></svg>
|
||||
<span data-bs-datepicker-display>Last 7 days</span>
|
||||
</button>`} />
|
||||
|
||||
You can also display the selected date in a separate element using the `displayElement` option via JavaScript:
|
||||
|
||||
```js
|
||||
const datepicker = new bootstrap.Datepicker(buttonElement, {
|
||||
selectionMode: 'multiple-ranged',
|
||||
displayElement: '#date-display' // Selector or element
|
||||
})
|
||||
```
|
||||
|
||||
### Inline mode
|
||||
|
||||
Render the calendar inline (always visible, no popup) with `data-bs-inline="true"`. This is useful for embedding a calendar directly in the page.
|
||||
|
||||
<Example code={`<div data-bs-toggle="datepicker" data-bs-inline="true"></div>`} />
|
||||
|
||||
Inline datepickers with date range selection:
|
||||
|
||||
<Example code={`<div data-bs-toggle="datepicker" data-bs-inline="true" data-bs-selection-mode="multiple-ranged"></div>`} />
|
||||
|
||||
Multiple months inline:
|
||||
|
||||
<Example code={`<div data-bs-toggle="datepicker" data-bs-inline="true" data-bs-display-months-count="2"></div>`} />
|
||||
|
||||
To bind to a form field, include a hidden input inside the container. The value will be updated with the selected date(s) in `YYYY-MM-DD` format:
|
||||
|
||||
<Example code={`<form>
|
||||
<div data-bs-toggle="datepicker" data-bs-inline="true">
|
||||
<input type="hidden" name="selected_date">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mt-3">Submit</button>
|
||||
</form>`} />
|
||||
|
||||
### Custom date formatting
|
||||
|
||||
Control how dates are displayed using the `dateFormat` option. Pass an [`Intl.DateTimeFormat` options object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#options) or a custom function.
|
||||
|
||||
```js
|
||||
// Using Intl.DateTimeFormat options
|
||||
const datepicker = new bootstrap.Datepicker(element, {
|
||||
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' }
|
||||
// Output: "Dec 23, 2025 – Dec 28, 2025"
|
||||
})
|
||||
|
||||
// Using a custom function
|
||||
const datepicker = new bootstrap.Datepicker(element, {
|
||||
dateFormat: (date, locale) => {
|
||||
return date.toLocaleDateString(locale, { month: 'short', day: 'numeric' })
|
||||
}
|
||||
// Output: "Dec 23 – Dec 28"
|
||||
})
|
||||
```
|
||||
|
||||
## Dark mode
|
||||
|
||||
The datepicker automatically adapts to Bootstrap's color modes. When `data-bs-theme="dark"` is set on a parent element or the `<html>` tag, the calendar popup inherits that theme.
|
||||
|
||||
### Inherited from parent
|
||||
|
||||
When a parent element has a theme, both the input and calendar popup inherit it:
|
||||
|
||||
<Example code={`<div data-bs-theme="dark" class="p-3 bg-body fg-body rounded">
|
||||
<label for="datepickerDark" class="form-label">Dark mode datepicker</label>
|
||||
<input type="text" class="form-control" id="datepickerDark" data-bs-toggle="datepicker" placeholder="Select a date">
|
||||
</div>`} />
|
||||
|
||||
### Datepicker-only theme
|
||||
|
||||
Use `data-bs-datepicker-theme` to set the datepicker popup's theme independently of the input. This is useful when you want a light input with a dark datepicker, or vice versa:
|
||||
|
||||
<Example code={`<label for="datepickerTheme" class="form-label">Light input, dark datepicker</label>
|
||||
<input type="text" class="form-control w-12" id="datepickerTheme" data-bs-toggle="datepicker" data-bs-datepicker-theme="dark" placeholder="Select a date">`} />
|
||||
|
||||
## Usage
|
||||
|
||||
### Via data attributes
|
||||
|
||||
Add `data-bs-toggle="datepicker"` to any input element to initialize it as a datepicker.
|
||||
|
||||
```html
|
||||
<input type="text" class="form-control" data-bs-toggle="datepicker">
|
||||
```
|
||||
|
||||
### Via JavaScript
|
||||
|
||||
Initialize datepickers programmatically:
|
||||
|
||||
```js
|
||||
const datepickerEl = document.getElementById('myDatepicker')
|
||||
const datepicker = new bootstrap.Datepicker(datepickerEl, {
|
||||
selectionMode: 'single',
|
||||
firstWeekday: 1
|
||||
})
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
<BsTable>
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dateMin` | string, number, Date | `null` | Minimum selectable date. Format: `YYYY-MM-DD` |
|
||||
| `dateMax` | string, number, Date | `null` | Maximum selectable date. Format: `YYYY-MM-DD` |
|
||||
| `dateFormat` | object, function | `null` | Date formatting. Pass `Intl.DateTimeFormat` options or a `function(date, locale)`. |
|
||||
| `displayElement` | string, element, boolean | `null` | Element to show formatted date. For buttons, defaults to the button itself. Set to `false` to disable. |
|
||||
| `displayMonthsCount` | number | `1` | Number of months to display side-by-side in the calendar. |
|
||||
| `firstWeekday` | number | `1` | First day of week (0 = Sunday, 1 = Monday, etc.) |
|
||||
| `inline` | boolean | `false` | Render calendar inline (always visible, no popup). |
|
||||
| `locale` | string | `'default'` | Locale for date formatting (e.g., `'en-US'`, `'de-DE'`) |
|
||||
| `positionElement` | string, element | `null` | Element to position calendar relative to. Auto-detects `.form-adorn` wrapper if present. |
|
||||
| `selectedDates` | array | `[]` | Pre-selected dates in `YYYY-MM-DD` format |
|
||||
| `selectionMode` | string | `'single'` | Selection mode: `'single'`, `'multiple'`, or `'multiple-ranged'` |
|
||||
| `placement` | string | `'left'` | Calendar position relative to input: `'left'`, `'center'`, `'right'`, `'auto'` |
|
||||
| `datepickerTheme` | string | `null` | Force datepicker popup theme: `'light'`, `'dark'`, `'auto'`, or `null` to inherit from ancestor `[data-bs-theme]` |
|
||||
| `vcpOptions` | object | `{}` | Pass-through object for any [Vanilla Calendar Pro option](https://vanilla-calendar.pro/docs/reference/settings) |
|
||||
</BsTable>
|
||||
|
||||
### Advanced configuration
|
||||
|
||||
For features not directly exposed by Bootstrap's options, use `vcpOptions` to pass any Vanilla Calendar Pro setting:
|
||||
|
||||
```js
|
||||
const datepicker = new bootstrap.Datepicker(element, {
|
||||
vcpOptions: {
|
||||
disableDatesPast: true, // Disable past dates
|
||||
disableWeekdays: [0, 6], // Disable weekends
|
||||
disableDates: ['2025-12-25', '2025-12-26'], // Disable specific dates
|
||||
selectedHolidays: ['2025-01-01'], // Highlight holidays
|
||||
selectionTimeMode: 24 // Enable 24-hour time selection
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
See the [Vanilla Calendar Pro documentation](https://vanilla-calendar.pro/docs/reference/settings) for all available options.
|
||||
|
||||
### Methods
|
||||
|
||||
<BsTable>
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| `show()` | Shows the datepicker calendar |
|
||||
| `hide()` | Hides the datepicker calendar |
|
||||
| `toggle()` | Toggles the datepicker visibility |
|
||||
| `getSelectedDates()` | Returns an array of selected dates in `YYYY-MM-DD` format |
|
||||
| `setSelectedDates(dates)` | Sets the selected dates. Expects an array of `YYYY-MM-DD` strings |
|
||||
| `dispose()` | Destroys the datepicker instance |
|
||||
| `getInstance(element)` | Static method to get the datepicker instance from a DOM element |
|
||||
| `getOrCreateInstance(element)` | Static method to get or create a datepicker instance |
|
||||
</BsTable>
|
||||
|
||||
### Events
|
||||
|
||||
<BsTable>
|
||||
| Event | Description |
|
||||
| --- | --- |
|
||||
| `show.bs.datepicker` | Fires immediately when the `show` method is called |
|
||||
| `shown.bs.datepicker` | Fires when the datepicker has been made visible |
|
||||
| `hide.bs.datepicker` | Fires immediately when the `hide` method is called |
|
||||
| `hidden.bs.datepicker` | Fires when the datepicker has been hidden |
|
||||
| `change.bs.datepicker` | Fires when a date is selected. Event includes `dates` (array) and `event` properties |
|
||||
</BsTable>
|
||||
|
||||
```js
|
||||
const datepickerEl = document.getElementById('myDatepicker')
|
||||
datepickerEl.addEventListener('change.bs.datepicker', event => {
|
||||
console.log('Selected dates:', event.dates)
|
||||
})
|
||||
```
|
||||
@@ -18,11 +18,13 @@ Here are our guidelines and reasons for choosing what to override in Reboot:
|
||||
- For easier scaling across device sizes, block elements should use `rem`s for `margin`s.
|
||||
- Keep declarations of `font`-related properties to a minimum, using `inherit` whenever possible.
|
||||
|
||||
## Root CSS variables
|
||||
## CSS variables
|
||||
|
||||
We add `:root` level CSS variables to all CSS bundles to provide real-time customization without the need to always recompile Sass. For example, consider these `:root` CSS variables for common `<body>` styles:
|
||||
With v5.1.1, we standardized our required `@import`s across all our CSS bundles (including `bootstrap.css`, `bootstrap-reboot.css`, and `bootstrap-grid.css`) to include `_root.scss`. This adds `:root` level CSS variables to all bundles, regardless of how many of them are used in that bundle. Ultimately Bootstrap 5 will continue to see more [CSS variables]([[docsref:/customize/css-variables]]) added over time, in order to provide more real-time customization without the need to always recompile Sass. Our approach is to take our source Sass variables and transform them into CSS variables. That way, even if you don’t use CSS variables, you still have all the power of Sass. **This is still in-progress and will take time to fully implement.**
|
||||
|
||||
<ScssDocs name="root-border-var" file="scss/_root.scss" />
|
||||
For example, consider these `:root` CSS variables for common `<body>` styles:
|
||||
|
||||
<ScssDocs name="root-body-variables" file="scss/_root.scss" />
|
||||
|
||||
In practice, those variables are then applied in Reboot like so:
|
||||
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
---
|
||||
title: Form adorn
|
||||
description: Decorate inputs with icons, text, and more using a custom wrapper that easily handles styling and positioning.
|
||||
toc: true
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
The `.form-adorn` wrapper replicates `.form-control` styling (border, background, focus states) while using flexbox to position adornments alongside a ghost input. The `.form-ghost` input inside has no visual styling—it's transparent and inherits from the wrapper.
|
||||
|
||||
## Example
|
||||
|
||||
Wrap an icon and a `.form-ghost` input inside `.form-adorn`. Place the adornment before the input in the DOM for start position (left in LTR).
|
||||
|
||||
<Example code={`<div class="form-adorn">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#search" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" placeholder="Search...">
|
||||
</div>`} />
|
||||
|
||||
Use `.form-adorn-end` to position the adornment on the trailing side (keeps DOM order, uses CSS to flip visually):
|
||||
|
||||
<Example code={`<div class="form-adorn form-adorn-end">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#envelope" /></svg>
|
||||
</div>
|
||||
<input type="email" class="form-ghost" placeholder="you@example.com">
|
||||
</div>`} />
|
||||
|
||||
## With labels
|
||||
|
||||
Add a label outside the `.form-adorn` wrapper for proper form semantics:
|
||||
|
||||
<Example class="vstack gap-3" code={`<div>
|
||||
<label for="searchInput" class="form-label">Search</label>
|
||||
<div class="form-adorn">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#search" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" id="searchInput" placeholder="Search...">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="emailInput" class="form-label">Email address</label>
|
||||
<div class="form-adorn form-adorn-end">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#envelope" /></svg>
|
||||
</div>
|
||||
<input type="email" class="form-ghost" id="emailInput" placeholder="you@example.com">
|
||||
</div>
|
||||
</div>`} />
|
||||
|
||||
## Text adornments
|
||||
|
||||
Use `.form-adorn-text` for currency symbols, units, domain suffixes, and other text-based adornments. Text adornments auto-size to their content.
|
||||
|
||||
<Example class="vstack gap-3" code={`<div class="form-adorn">
|
||||
<span class="form-adorn-text">$</span>
|
||||
<input type="text" class="form-ghost" placeholder="0.00">
|
||||
</div>
|
||||
<div class="form-adorn form-adorn-end">
|
||||
<span class="form-adorn-text">USD</span>
|
||||
<input type="text" class="form-ghost" placeholder="Amount">
|
||||
</div>
|
||||
<div class="form-adorn">
|
||||
<span class="form-adorn-text">https://</span>
|
||||
<input type="text" class="form-ghost" placeholder="example.com">
|
||||
</div>
|
||||
<div class="form-adorn form-adorn-end">
|
||||
<span class="form-adorn-text">@example.com</span>
|
||||
<input type="text" class="form-ghost" placeholder="username">
|
||||
</div>`} />
|
||||
|
||||
## Sizing
|
||||
|
||||
Use `.form-adorn-sm` or `.form-adorn-lg` on the wrapper to adjust sizing.
|
||||
|
||||
<Example class="vstack gap-3" code={`<div class="form-adorn form-adorn-sm">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#search" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" placeholder="Small input">
|
||||
</div>
|
||||
<div class="form-adorn">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#search" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" placeholder="Default input">
|
||||
</div>
|
||||
<div class="form-adorn form-adorn-lg">
|
||||
<div class="form-adorn-icon">
|
||||
<svg class="bi" width="16" height="16"><use href="#search" /></svg>
|
||||
</div>
|
||||
<input type="text" class="form-ghost" placeholder="Large input">
|
||||
</div>`} />
|
||||
|
||||
## Ghost input
|
||||
|
||||
The `.form-ghost` class strips all visual styling from an input, making it transparent. It's designed for use inside custom wrappers like `.form-adorn` that handle their own border, background, and focus states.
|
||||
|
||||
<Example code={`<input type="text" class="form-ghost" placeholder="Ghost input (no styling)">`} />
|
||||
|
||||
## CSS
|
||||
|
||||
### Sass variables
|
||||
|
||||
<ScssDocs name="form-adorn-variables" file="scss/forms/_form-adorn.scss" />
|
||||
@@ -13,11 +13,11 @@
|
||||
}
|
||||
|
||||
.astro-code {
|
||||
--bs-font-monospace: "Geist Mono";
|
||||
display: flex;
|
||||
padding: var(--bd-example-padding);
|
||||
margin-bottom: 0;
|
||||
line-height: 20px;
|
||||
--bs-font-monospace: "Geist Mono";
|
||||
background-color: var(--bd-pre-bg) !important; // stylelint-disable-line declaration-no-important
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user