Compare commits
No commits in common. "main" and "v1.0.4" have entirely different histories.
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,22 +0,0 @@
|
|||||||
# 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.
|
|
||||||
90
README.md
90
README.md
@ -1,48 +1,68 @@
|
|||||||
# @apigo.cc/bootstrap API 手册 (AI Optimized)
|
# @apigo.cc/bootstrap
|
||||||
|
|
||||||
Bootstrap 5.3 自包含集成引擎。
|
`@apigo.cc/bootstrap` 是一个自包含的 Bootstrap 5.3 引擎集成模块。它内置了 Bootstrap 及其图标库(Bootstrap Icons),并提供了简单的主题配色与暗色模式绑定机制。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、 集成方式
|
||||||
|
|
||||||
|
推荐使用 `@apigo.cc/loader` 自动调度:
|
||||||
|
|
||||||
## 0. 快速开始 (Quick Start)
|
|
||||||
直接在 HTML 中引入(无需打包,完全非 ESM 注入):
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.6/dist/bootstrap.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/loader@1.0.0/dist/loader.min.js"></script>
|
||||||
|
<script>
|
||||||
|
Loader.load('state:1.0.11', 'bootstrap:1.0.2');
|
||||||
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
## 1. 运行时接口
|
---
|
||||||
- **`bootstrap`**: (Object) 官方原生 Bootstrap 镜像。已挂载至 `globalThis.bootstrap`。
|
|
||||||
- **`Bootstrap`**: (Object) 增强控制接口。已挂载至 `globalThis.Bootstrap`。
|
|
||||||
|
|
||||||
*(注意:本库自 v1.0.6 起全面废弃 ESM 导出,改为纯全局注入模式。)*
|
## 二、 核心 API
|
||||||
|
|
||||||
## 2. API 参考
|
### 1. 修改主题配色 (`Bootstrap.config`)
|
||||||
|
|
||||||
### `Bootstrap.config(options)`
|
您可以通过调用 `Bootstrap.config(colors)` 方法动态修改 Bootstrap 的核心主题颜色。传入十六进制(Hex)值,方法会自动为您计算并设置相应的 CSS 变量以及 RGB 变量:
|
||||||
统一配置入口。未定义的 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
|
```javascript
|
||||||
// 1. 设置颜色与暗黑模式简写
|
import { Bootstrap } from '@apigo.cc/bootstrap';
|
||||||
Bootstrap.config({
|
|
||||||
primary: '#a855f7',
|
// 动态修改 primary 和 danger 的配色
|
||||||
bindDarkMode: [LocalStorage, 'isDark']
|
Bootstrap.config({
|
||||||
|
primary: '#6366f1',
|
||||||
|
danger: '#f43f5e'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. 手动模式
|
|
||||||
Bootstrap.config({ darkMode: true });
|
|
||||||
|
|
||||||
// 3. 仅更新单一颜色 (自动处理相关组件补丁)
|
|
||||||
Bootstrap.config({ success: '#22c55e' });
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 核心机制 (Internal)
|
### 2. 绑定暗色模式 (`Bootstrap.bindDarkMode`)
|
||||||
- **运行时样式补丁**: `Bootstrap.config` 动态更新 `id="bs-config-patch"` 的 `<style>` 标签。通过 `!important` 覆盖 Bootstrap 内部硬编码的组件颜色(如 `.btn`, `.form-switch`, `.form-range`, `.progress-bar`, `.list-group-item` 等)。
|
|
||||||
- **零构建支持**: 无需 Sass 重新编译,在纯 ESM 环境下即可实现全量主题定制。
|
`Bootstrap.bindDarkMode(state, key)` 可以将响应式状态(如 `LocalStorage` 或您自定义的 `NewState`)的某个属性绑定为暗色模式的控制器。
|
||||||
|
|
||||||
|
当该属性发生变化时,它会自动在 `<html>` 标签上设置 `data-bs-theme="dark"` 或 `data-bs-theme="light"`,从而无缝切换 Bootstrap 5.3 的深色主题模式:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { LocalStorage } from '@apigo.cc/state';
|
||||||
|
import { Bootstrap } from '@apigo.cc/bootstrap';
|
||||||
|
|
||||||
|
// 绑定 LocalStorage.darkMode 状态
|
||||||
|
Bootstrap.bindDarkMode(LocalStorage, 'darkMode');
|
||||||
|
|
||||||
|
// 示例:切换为暗色模式
|
||||||
|
LocalStorage.darkMode = true;
|
||||||
|
|
||||||
|
// 示例:切换回亮色模式
|
||||||
|
LocalStorage.darkMode = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、 使用原生 Bootstrap 实例
|
||||||
|
|
||||||
|
`@apigo.cc/bootstrap` 导出了原生的 Bootstrap 全量实例,您可以直接调用原生 Bootstrap 的组件方法(如 Modal, Popover 等):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { Bootstrap } from '@apigo.cc/bootstrap';
|
||||||
|
|
||||||
|
// 使用原生的 Modal 组件
|
||||||
|
const myModal = new Bootstrap.Modal(document.getElementById('myModal'));
|
||||||
|
myModal.show();
|
||||||
|
```
|
||||||
|
|||||||
9835
dist/bootstrap.js
vendored
9835
dist/bootstrap.js
vendored
File diff suppressed because it is too large
Load Diff
10
dist/bootstrap.min.js
vendored
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
4
node_modules/.package-lock.json
generated
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@apigo.cc/bootstrap",
|
"name": "bootstrap",
|
||||||
"version": "1.0.1",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
5188
node_modules/.vite/deps_temp_ee6d9627/bootstrap.js
generated
vendored
Normal file
5188
node_modules/.vite/deps_temp_ee6d9627/bootstrap.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/.vite/deps_temp_ee6d9627/bootstrap.js.map
generated
vendored
Normal file
7
node_modules/.vite/deps_temp_ee6d9627/bootstrap.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3
node_modules/.vite/deps_temp_ee6d9627/package.json
generated
vendored
Normal file
3
node_modules/.vite/deps_temp_ee6d9627/package.json
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@apigo.cc/bootstrap",
|
"name": "@apigo.cc/bootstrap",
|
||||||
"version": "1.0.7",
|
"version": "1.0.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/bootstrap.js",
|
"main": "dist/bootstrap.js",
|
||||||
|
"module": "dist/bootstrap.js",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
|
|||||||
156
src/index.js
156
src/index.js
@ -1,131 +1,63 @@
|
|||||||
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 '@apigo.cc/state'
|
import { Hash, LocalStorage, RefreshState } from '@web/state'
|
||||||
|
|
||||||
const GlobalStates = { Hash, LocalStorage }
|
const GlobalStates = { Hash, LocalStorage }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @apigo.cc/bootstrap
|
* @web/bootstrap
|
||||||
* 自包含的 Bootstrap 5.3 集成引擎
|
* 自包含的 Bootstrap 5.3 集成引擎
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 导出增强功能对象
|
const Bootstrap = {
|
||||||
export const Bootstrap = {
|
// 原始 Bootstrap 实例引用
|
||||||
config: (options = {}) => {
|
...bootstrap,
|
||||||
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']
|
* 动态配置主题变量
|
||||||
let cssPatch = ''
|
* @param {Object} config { primary: '#6366f1', ... }
|
||||||
|
*/
|
||||||
colors.forEach(name => {
|
config: (config = {}) => {
|
||||||
const hex = options[name]
|
if (typeof document === 'undefined') return
|
||||||
if (hex) {
|
const root = document.documentElement
|
||||||
root.style.setProperty(`--bs-${name}`, hex)
|
const colors = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
|
||||||
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) {
|
colors.forEach(name => {
|
||||||
let styleEl = document.getElementById('bs-config-patch')
|
const hex = config[name]
|
||||||
if (!styleEl) {
|
if (hex) {
|
||||||
styleEl = document.createElement('style')
|
root.style.setProperty(`--bs-${name}`, hex)
|
||||||
styleEl.id = 'bs-config-patch'
|
const rgb = Bootstrap._hexToRgb(hex)
|
||||||
document.head.appendChild(styleEl)
|
if (rgb) root.style.setProperty(`--bs-${name}-rgb`, `${rgb.r}, ${rgb.g}, ${rgb.b}`)
|
||||||
}
|
|
||||||
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 ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null
|
return result ? {
|
||||||
|
r: parseInt(result[1], 16),
|
||||||
|
g: parseInt(result[2], 16),
|
||||||
|
b: parseInt(result[3], 16)
|
||||||
|
} : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 挂载到全局
|
export { Bootstrap }
|
||||||
if (typeof globalThis !== 'undefined') {
|
export default Bootstrap
|
||||||
// 小写变量保持为纯原生或接近原生的引用,供 AI 舒适使用
|
|
||||||
globalThis.bootstrap = bootstrap;
|
|
||||||
// 大写变量供需要我们额外增强能力的场景使用
|
|
||||||
globalThis.Bootstrap = Bootstrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Bootstrap;
|
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"@apigo.cc/state": "../state/src/index.js",
|
"@web/state": "../state/src/index.js",
|
||||||
"@apigo.cc/bootstrap": "./src/index.js"
|
"@web/bootstrap": "./src/index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -22,8 +22,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { Bootstrap } from '@apigo.cc/bootstrap';
|
import { Bootstrap } from '@web/bootstrap';
|
||||||
import { LocalStorage } from '@apigo.cc/state';
|
import { LocalStorage } from '@web/state';
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
LocalStorage.darkMode = false;
|
LocalStorage.darkMode = false;
|
||||||
|
|||||||
@ -9,34 +9,32 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@apigo.cc/state': resolve(__dirname, '../state/src/index.js'),
|
'@web/state': resolve(__dirname, '../state/src/index.js'),
|
||||||
'@apigo.cc/bootstrap': resolve(__dirname, 'src/index.js')
|
'@web/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: 'ApigoBootstrap',
|
name: 'Bootstrap',
|
||||||
formats: ['iife']
|
formats: ['es']
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: ['@apigo.cc/state'],
|
external: ['@web/state'],
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
format: 'iife',
|
format: 'es',
|
||||||
name: 'ApigoBootstrap',
|
|
||||||
entryFileNames: 'bootstrap.js',
|
entryFileNames: 'bootstrap.js',
|
||||||
globals: {
|
minifyInternalExports: false
|
||||||
'@apigo.cc/state': 'ApigoState'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
format: 'iife',
|
format: 'es',
|
||||||
name: 'ApigoBootstrap',
|
|
||||||
entryFileNames: 'bootstrap.min.js',
|
entryFileNames: 'bootstrap.min.js',
|
||||||
globals: {
|
|
||||||
'@apigo.cc/state': 'ApigoState'
|
|
||||||
},
|
|
||||||
plugins: [terser()]
|
plugins: [terser()]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user