Compare commits

..

7 Commits
v1.0.2 ... main

22 changed files with 5211 additions and 10212 deletions

1
.npmrc Normal file
View File

@ -0,0 +1 @@
//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}

22
CHANGELOG.md Normal file
View File

@ -0,0 +1,22 @@
# Changelog
## 1.0.6 (2026-06-09)
### Breaking Changes
- **ESM Deprecation**: Completely removed ESM exports to align with the "zero-boilerplate" project philosophy. The library now relies solely on global injection via `globalThis`.
- **Build Format**: Switched build output to IIFE format only.
### Improvements
- **Integration**: Simplified dependency handling for `@apigo.cc/state` by using global variables.
- **Docs**: Updated README with new versioning and non-ESM usage instructions.
## 1.0.5 (2026-06-05)
### Features
- **API**: Refactored `Bootstrap.config` to be the unified entry point.
- **Shorthand**: Added support for `[state, key]` array shorthand in `bindDarkMode`.
- **Theme Engine**: Expanded CSS patch coverage to include form switches, checkboxes, range inputs, progress bars, and more.
- **AI Optimized**: Documentation overhaul for better AI usability and zero-config setup.
### Fixes
- Fixed an issue where theme colors did not propagate to component-specific variables like buttons and switches.

48
README.md Normal file
View File

@ -0,0 +1,48 @@
# @apigo.cc/bootstrap API 手册 (AI Optimized)
Bootstrap 5.3 自包含集成引擎。
## 0. 快速开始 (Quick Start)
直接在 HTML 中引入(无需打包,完全非 ESM 注入):
```html
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.6/dist/bootstrap.min.js"></script>
```
## 1. 运行时接口
- **`bootstrap`**: (Object) 官方原生 Bootstrap 镜像。已挂载至 `globalThis.bootstrap`
- **`Bootstrap`**: (Object) 增强控制接口。已挂载至 `globalThis.Bootstrap`
*(注意:本库自 v1.0.6 起全面废弃 ESM 导出,改为纯全局注入模式。)*
## 2. API 参考
### `Bootstrap.config(options)`
统一配置入口。未定义的 key 将被忽略。
#### 参数 `options`:
- **主题色**: `primary`, `secondary`, `success`, `info`, `warning`, `danger`, `light`, `dark` (Hex 字符串)。
- *行为*: 自动更新 CSS 变量并注入深度样式补丁(覆盖按钮、表单控件、开关、进度条等硬编码样式)。
- **暗黑模式**:
- `darkMode`: (Boolean) 直接设置主题。`true` 为 dark`false` 为 light。
- `bindDarkMode`: (Array) `[state, key]`。绑定响应式状态。
- `state`: 具有 `__watch(key, callback)` 方法的状态对象。
- `key`: 状态对象中的键名。
#### 示例 (Examples):
```javascript
// 1. 设置颜色与暗黑模式简写
Bootstrap.config({
primary: '#a855f7',
bindDarkMode: [LocalStorage, 'isDark']
});
// 2. 手动模式
Bootstrap.config({ darkMode: true });
// 3. 仅更新单一颜色 (自动处理相关组件补丁)
Bootstrap.config({ success: '#22c55e' });
```
## 3. 核心机制 (Internal)
- **运行时样式补丁**: `Bootstrap.config` 动态更新 `id="bs-config-patch"``<style>` 标签。通过 `!important` 覆盖 Bootstrap 内部硬编码的组件颜色(如 `.btn`, `.form-switch`, `.form-range`, `.progress-bar`, `.list-group-item` 等)。
- **零构建支持**: 无需 Sass 重新编译,在纯 ESM 环境下即可实现全量主题定制。

9945
dist/bootstrap.js vendored

File diff suppressed because it is too large Load Diff

10
dist/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

4
node_modules/.package-lock.json generated vendored
View File

@ -1,6 +1,6 @@
{ {
"name": "bootstrap", "name": "@apigo.cc/bootstrap",
"version": "1.0.0", "version": "1.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@ -1,9 +1,8 @@
{ {
"name": "@apigo.cc/bootstrap", "name": "@apigo.cc/bootstrap",
"version": "1.0.1", "version": "1.0.7",
"type": "module", "type": "module",
"main": "dist/bootstrap.js", "main": "dist/bootstrap.js",
"module": "dist/bootstrap.js",
"files": [ "files": [
"dist" "dist"
], ],

View File

@ -1,63 +1,131 @@
import * as bootstrap from 'bootstrap' import * as bootstrap from 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-icons/font/bootstrap-icons.css' import 'bootstrap-icons/font/bootstrap-icons.css'
import { Hash, LocalStorage, RefreshState } from '@web/state' import { Hash, LocalStorage, RefreshState } from '@apigo.cc/state'
const GlobalStates = { Hash, LocalStorage } const GlobalStates = { Hash, LocalStorage }
/** /**
* @web/bootstrap * @apigo.cc/bootstrap
* 自包含的 Bootstrap 5.3 集成引擎 * 自包含的 Bootstrap 5.3 集成引擎
*/ */
const Bootstrap = { // 导出增强功能对象
// 原始 Bootstrap 实例引用 export const Bootstrap = {
...bootstrap, config: (options = {}) => {
/** if (typeof document === 'undefined') return
* 绑定暗色模式 const root = document.documentElement
* @param {Object} state NewState 对象 ( LocalStorage)
* @param {string} key 键名 ( 'darkMode')
*/
bindDarkMode: (state, key) => {
if (typeof document === 'undefined') return
const htmlNode = document.documentElement
const updateTheme = (val) => {
htmlNode.setAttribute('data-bs-theme', val ? 'dark' : 'light')
}
if (state && key) {
state.__watch(key, updateTheme)
updateTheme(state[key])
}
},
/** // 1. 处理颜色主题 (Colors)
* 动态配置主题变量 const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
* @param {Object} config { primary: '#6366f1', ... } let cssPatch = ''
*/
config: (config = {}) => { colors.forEach(name => {
if (typeof document === 'undefined') return const hex = options[name]
const root = document.documentElement if (hex) {
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark'] 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;
}
`
}
}
})
colors.forEach(name => { if (cssPatch) {
const hex = config[name] let styleEl = document.getElementById('bs-config-patch')
if (hex) { if (!styleEl) {
root.style.setProperty(`--bs-${name}`, hex) styleEl = document.createElement('style')
const rgb = Bootstrap._hexToRgb(hex) styleEl.id = 'bs-config-patch'
if (rgb) root.style.setProperty(`--bs-${name}-rgb`, `${rgb.r}, ${rgb.g}, ${rgb.b}`) 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) => { _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) const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? { return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null
} }
} }
export { Bootstrap } // 挂载到全局
export default Bootstrap if (typeof globalThis !== 'undefined') {
// 小写变量保持为纯原生或接近原生的引用,供 AI 舒适使用
globalThis.bootstrap = bootstrap;
// 大写变量供需要我们额外增强能力的场景使用
globalThis.Bootstrap = Bootstrap;
}
export default Bootstrap;

View File

@ -5,8 +5,8 @@
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
"@web/state": "../state/src/index.js", "@apigo.cc/state": "../state/src/index.js",
"@web/bootstrap": "./src/index.js" "@apigo.cc/bootstrap": "./src/index.js"
} }
} }
</script> </script>
@ -22,8 +22,8 @@
</div> </div>
<script type="module"> <script type="module">
import { Bootstrap } from '@web/bootstrap'; import { Bootstrap } from '@apigo.cc/bootstrap';
import { LocalStorage } from '@web/state'; import { LocalStorage } from '@apigo.cc/state';
// 初始化 // 初始化
LocalStorage.darkMode = false; LocalStorage.darkMode = false;

View File

@ -9,32 +9,34 @@ export default defineConfig({
], ],
resolve: { resolve: {
alias: { alias: {
'@web/state': resolve(__dirname, '../state/src/index.js'), '@apigo.cc/state': resolve(__dirname, '../state/src/index.js'),
'@web/bootstrap': resolve(__dirname, 'src/index.js') '@apigo.cc/bootstrap': resolve(__dirname, 'src/index.js')
}
},
server: {
fs: {
allow: ['..']
} }
}, },
build: { build: {
lib: { lib: {
entry: resolve(__dirname, 'src/index.js'), entry: resolve(__dirname, 'src/index.js'),
name: 'Bootstrap', name: 'ApigoBootstrap',
formats: ['es'] formats: ['iife']
}, },
rollupOptions: { rollupOptions: {
external: ['@web/state'], external: ['@apigo.cc/state'],
output: [ output: [
{ {
format: 'es', format: 'iife',
name: 'ApigoBootstrap',
entryFileNames: 'bootstrap.js', entryFileNames: 'bootstrap.js',
minifyInternalExports: false globals: {
'@apigo.cc/state': 'ApigoState'
}
}, },
{ {
format: 'es', format: 'iife',
name: 'ApigoBootstrap',
entryFileNames: 'bootstrap.min.js', entryFileNames: 'bootstrap.min.js',
globals: {
'@apigo.cc/state': 'ApigoState'
},
plugins: [terser()] plugins: [terser()]
} }
] ]