2026-05-29 23:55:18 +08:00
|
|
|
import * as bootstrap from 'bootstrap'
|
|
|
|
|
import 'bootstrap/dist/css/bootstrap.min.css'
|
|
|
|
|
import 'bootstrap-icons/font/bootstrap-icons.css'
|
2026-06-09 20:20:25 +08:00
|
|
|
import '@apigo.cc/state'
|
2026-05-29 23:55:18 +08:00
|
|
|
|
2026-06-09 20:20:25 +08:00
|
|
|
const { Hash, LocalStorage, _unsafeRefreshState } = globalThis
|
2026-05-29 23:55:18 +08:00
|
|
|
const GlobalStates = { Hash, LocalStorage }
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-05 15:35:57 +08:00
|
|
|
* @apigo.cc/bootstrap
|
2026-05-29 23:55:18 +08:00
|
|
|
* 自包含的 Bootstrap 5.3 集成引擎
|
|
|
|
|
*/
|
|
|
|
|
|
2026-06-09 20:20:25 +08:00
|
|
|
// 定义增强功能对象
|
|
|
|
|
const Bootstrap = {
|
2026-06-05 15:35:57 +08:00
|
|
|
config: (options = {}) => {
|
|
|
|
|
if (typeof document === 'undefined') return
|
|
|
|
|
const root = document.documentElement
|
2026-05-29 23:55:18 +08:00
|
|
|
|
2026-06-05 15:35:57 +08:00
|
|
|
// 1. 处理颜色主题 (Colors)
|
|
|
|
|
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
|
|
|
|
|
let cssPatch = ''
|
|
|
|
|
|
|
|
|
|
colors.forEach(name => {
|
|
|
|
|
const hex = options[name]
|
|
|
|
|
if (hex) {
|
|
|
|
|
root.style.setProperty(`--bs-${name}`, hex)
|
|
|
|
|
const rgb = Bootstrap._hexToRgb(hex)
|
|
|
|
|
if (rgb) {
|
|
|
|
|
const rgbStr = `${rgb.r}, ${rgb.g}, ${rgb.b}`
|
|
|
|
|
root.style.setProperty(`--bs-${name}-rgb`, rgbStr)
|
|
|
|
|
|
|
|
|
|
// 深度补丁:覆盖组件内部硬编码的变量和状态样式
|
|
|
|
|
cssPatch += `
|
|
|
|
|
.btn-${name} {
|
|
|
|
|
--bs-btn-bg: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-border-color: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-hover-bg: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-hover-border-color: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-active-bg: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-active-border-color: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-disabled-bg: var(--bs-${name}) !important;
|
|
|
|
|
--bs-btn-disabled-border-color: var(--bs-${name}) !important;
|
|
|
|
|
}
|
|
|
|
|
.bg-${name} { background-color: var(--bs-${name}) !important; }
|
|
|
|
|
.text-${name} { color: var(--bs-${name}) !important; }
|
|
|
|
|
.border-${name} { border-color: var(--bs-${name}) !important; }
|
|
|
|
|
.badge.bg-${name} { background-color: var(--bs-${name}) !important; }
|
|
|
|
|
|
|
|
|
|
/* 表单控件补丁 (Form Controls) */
|
|
|
|
|
.form-check-input:checked {
|
|
|
|
|
background-color: var(--bs-primary) !important;
|
|
|
|
|
border-color: var(--bs-primary) !important;
|
|
|
|
|
}
|
|
|
|
|
.form-check-input:focus {
|
|
|
|
|
border-color: var(--bs-primary) !important;
|
|
|
|
|
box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25) !important;
|
|
|
|
|
}
|
|
|
|
|
.form-switch .form-check-input:checked {
|
|
|
|
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e") !important;
|
|
|
|
|
}
|
|
|
|
|
.form-range::-webkit-slider-thumb { background: var(--bs-primary) !important; }
|
|
|
|
|
.form-range::-moz-range-thumb { background: var(--bs-primary) !important; }
|
|
|
|
|
.form-range::-webkit-slider-thumb:active { background-color: rgba(var(--bs-primary-rgb), 0.5) !important; }
|
|
|
|
|
|
|
|
|
|
/* 进度条与分页 (Progress & Pagination) */
|
|
|
|
|
.progress-bar { background-color: var(--bs-primary) !important; }
|
|
|
|
|
.page-link { color: var(--bs-primary); }
|
|
|
|
|
.active > .page-link, .page-link.active {
|
|
|
|
|
background-color: var(--bs-primary) !important;
|
|
|
|
|
border-color: var(--bs-primary) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 列表组 (List Group) */
|
|
|
|
|
.list-group-item.active {
|
|
|
|
|
background-color: var(--bs-primary) !important;
|
|
|
|
|
border-color: var(--bs-primary) !important;
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (cssPatch) {
|
|
|
|
|
let styleEl = document.getElementById('bs-config-patch')
|
|
|
|
|
if (!styleEl) {
|
|
|
|
|
styleEl = document.createElement('style')
|
|
|
|
|
styleEl.id = 'bs-config-patch'
|
|
|
|
|
document.head.appendChild(styleEl)
|
|
|
|
|
}
|
|
|
|
|
styleEl.innerHTML = cssPatch
|
2026-05-29 23:55:18 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-05 15:35:57 +08:00
|
|
|
// 2. 处理暗黑模式 (Dark Mode)
|
|
|
|
|
let { bindDarkMode, darkMode } = options
|
|
|
|
|
const updateTheme = (val) => {
|
|
|
|
|
root.setAttribute('data-bs-theme', val ? 'dark' : 'light')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 支持 [state, key] 简写
|
|
|
|
|
if (Array.isArray(bindDarkMode)) {
|
|
|
|
|
bindDarkMode = { state: bindDarkMode[0], key: bindDarkMode[1] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bindDarkMode && bindDarkMode.state && bindDarkMode.key) {
|
|
|
|
|
const { state, key } = bindDarkMode
|
|
|
|
|
if (state.__watch) {
|
|
|
|
|
state.__watch(key, updateTheme)
|
|
|
|
|
updateTheme(state[key])
|
|
|
|
|
}
|
|
|
|
|
} else if (darkMode !== undefined) {
|
|
|
|
|
updateTheme(darkMode)
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-05-29 23:55:18 +08:00
|
|
|
_hexToRgb: (hex) => {
|
2026-06-05 15:35:57 +08:00
|
|
|
let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
|
|
|
|
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b)
|
2026-05-29 23:55:18 +08:00
|
|
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
2026-06-05 15:35:57 +08:00
|
|
|
return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null
|
2026-05-29 23:55:18 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-05 15:35:57 +08:00
|
|
|
// 挂载到全局
|
|
|
|
|
if (typeof globalThis !== 'undefined') {
|
|
|
|
|
// 小写变量保持为纯原生或接近原生的引用,供 AI 舒适使用
|
|
|
|
|
globalThis.bootstrap = bootstrap;
|
|
|
|
|
// 大写变量供需要我们额外增强能力的场景使用
|
|
|
|
|
globalThis.Bootstrap = Bootstrap;
|
|
|
|
|
}
|
|
|
|
|
|