bootstrap/src/index.js

131 lines
5.9 KiB
JavaScript
Raw Normal View History

import * as bootstrap from 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-icons/font/bootstrap-icons.css'
import '@apigo.cc/state'
const { Hash, LocalStorage, _unsafeRefreshState } = globalThis
const GlobalStates = { Hash, LocalStorage }
/**
* @apigo.cc/bootstrap
* 自包含的 Bootstrap 5.3 集成引擎
*/
// 定义增强功能对象
const Bootstrap = {
config: (options = {}) => {
if (typeof document === 'undefined') return
const root = document.documentElement
// 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
}
// 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)
}
},
_hexToRgb: (hex) => {
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)
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null
}
}
// 挂载到全局
if (typeof globalThis !== 'undefined') {
// 小写变量保持为纯原生或接近原生的引用,供 AI 舒适使用
globalThis.bootstrap = bootstrap;
// 大写变量供需要我们额外增强能力的场景使用
globalThis.Bootstrap = Bootstrap;
}