Browse Source

feat: add ConfigProvider component for locale management

- Introduced ConfigProvider component to manage locale settings.
- Implemented useLocale hook for localization support.
- Added language JSON files for English and Chinese.
- Created utility hooks for context management and namespace handling.
- Developed various UI components with corresponding styles.
- Integrated Vite plugin for automatic style injection based on component usage.
- Established a Nuxt module for easy integration of Bolt UI components and styles.
as
npmrun 3 weeks ago
parent
commit
c1a4fce529
  1. 77
      bun.lock
  2. 9
      nuxt.config.ts
  3. 4
      package.json
  4. 5
      packages/bolt-ui/components/Button/index.ts
  5. 23
      packages/bolt-ui/components/Button/src/Button.vue
  6. 1
      packages/bolt-ui/components/Button/style/index.ts
  7. 5
      packages/bolt-ui/components/ConfigProvider/index.ts
  8. 33
      packages/bolt-ui/components/ConfigProvider/src/ConfigProvider.vue
  9. 3
      packages/bolt-ui/components/ConfigProvider/src/Token.ts
  10. 10
      packages/bolt-ui/components/ConfigProvider/src/useConfigProvider.ts
  11. 1
      packages/bolt-ui/components/ConfigProvider/style/index.ts
  12. 2
      packages/bolt-ui/components/index.ts
  13. 67
      packages/bolt-ui/hooks/createContext/index.ts
  14. 1
      packages/bolt-ui/hooks/index.ts
  15. 38
      packages/bolt-ui/hooks/useClickOutside/index.ts
  16. 2
      packages/bolt-ui/index.ts
  17. 68
      packages/bolt-ui/locales/index.ts
  18. 11
      packages/bolt-ui/locales/languages/en.json
  19. 11
      packages/bolt-ui/locales/languages/zh.json
  20. 136
      packages/bolt-ui/nuxt.ts
  21. 8
      packages/bolt-ui/package.json
  22. 67
      packages/bolt-ui/resolver.ts
  23. 50
      packages/bolt-ui/theme-chalk/src/button.scss
  24. 11
      packages/bolt-ui/theme-chalk/src/card.scss
  25. 44
      packages/bolt-ui/theme-chalk/src/core/_base.scss
  26. 51
      packages/bolt-ui/theme-chalk/src/dropdown.scss
  27. 38
      packages/bolt-ui/theme-chalk/src/empty.scss
  28. 59
      packages/bolt-ui/theme-chalk/src/float.scss
  29. 94
      packages/bolt-ui/theme-chalk/src/input.scss
  30. 144
      packages/bolt-ui/theme-chalk/src/select.scss
  31. 38
      packages/bolt-ui/theme-chalk/src/theme/index.scss
  32. 5
      packages/bolt-ui/tsconfig.json
  33. 127
      packages/bolt-ui/utils/hooks/use-namespace/index.ts
  34. 26
      packages/bolt-ui/utils/vue/install.ts
  35. 12
      packages/bolt-ui/utils/vue/types.ts

77
bun.lock

@ -6,9 +6,11 @@
"name": "person-panel",
"dependencies": {
"@libsql/client": "0.17.3",
"@module-federation/vite": "^1.15.5",
"@module-federation/vite": "1.15.5",
"@nuxt/icon": "2.2.2",
"@types/lodash-es": "4.17.12",
"bcryptjs": "3.0.3",
"bolt-ui": "workspace:*",
"cache": "workspace:*",
"common": "workspace:*",
"croner": "10.0.1",
@ -17,6 +19,7 @@
"drizzle-pkg": "workspace:*",
"drizzle-seed": "0.3.1",
"drizzle-zod": "0.8.3",
"lodash-es": "4.18.1",
"log4js": "6.9.1",
"logger": "workspace:*",
"mime": "4.1.0",
@ -34,12 +37,16 @@
"@types/multer": "2.1.0",
"@types/node": "25.8.0",
"drizzle-kit": "0.31.10",
"sass-embedded": "1.100.0",
"tsconfig": "workspace:*",
"tsx": "4.21.0",
"typescript": "6.0.2",
"vue3-toastify": "0.2.9",
},
},
"packages/bolt-ui": {
"name": "bolt-ui",
},
"packages/cache": {
"name": "cache",
"version": "0.1.0",
@ -120,6 +127,8 @@
"@bomb.sh/tab": ["@bomb.sh/tab@0.0.15", "https://registry.npmmirror.com/@bomb.sh/tab/-/tab-0.0.15.tgz", { "peerDependencies": { "cac": "^6.7.14", "citty": "^0.1.6 || ^0.2.0", "commander": "^13.1.0" }, "optionalPeers": ["cac", "citty", "commander"], "bin": { "tab": "dist/bin/cli.mjs" } }, "sha512-Y90ub44TAvbdO9P8mcD/XPyQjFhiR5xmd4Fk7JErmWmEWEUimNnjWiBrVZ16Tj3GA1rLZ+uvCN2V/pzLawv31g=="],
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.12.0", "", {}, "sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA=="],
"@clack/core": ["@clack/core@1.3.1", "https://registry.npmmirror.com/@clack/core/-/core-1.3.1.tgz", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA=="],
"@clack/prompts": ["@clack/prompts@1.4.0", "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.4.0.tgz", { "dependencies": { "@clack/core": "1.3.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA=="],
@ -590,6 +599,10 @@
"@types/jsesc": ["@types/jsesc@2.5.1", "https://registry.npmmirror.com/@types/jsesc/-/jsesc-2.5.1.tgz", {}, "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw=="],
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
"@types/lodash-es": ["@types/lodash-es@4.17.12", "", { "dependencies": { "@types/lodash": "*" } }, "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ=="],
"@types/multer": ["@types/multer@2.1.0", "https://registry.npmmirror.com/@types/multer/-/multer-2.1.0.tgz", { "dependencies": { "@types/express": "*" } }, "sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA=="],
"@types/node": ["@types/node@25.8.0", "https://registry.npmmirror.com/@types/node/-/node-25.8.0.tgz", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ=="],
@ -720,6 +733,8 @@
"bl": ["bl@4.1.0", "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"bolt-ui": ["bolt-ui@workspace:packages/bolt-ui"],
"boolbase": ["boolbase@1.0.0", "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"brace-expansion": ["brace-expansion@5.0.5", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.5.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
@ -762,6 +777,8 @@
"color-name": ["color-name@1.1.4", "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
"commander": ["commander@2.20.3", "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"common": ["common@workspace:packages/common"],
@ -1000,6 +1017,8 @@
"h3": ["h3@1.15.11", "https://registry.npmmirror.com/h3/-/h3-1.15.11.tgz", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"homedir-polyfill": ["homedir-polyfill@1.0.3", "", { "dependencies": { "parse-passwd": "^1.0.0" } }, "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA=="],
@ -1024,6 +1043,8 @@
"image-meta": ["image-meta@0.2.2", "https://registry.npmmirror.com/image-meta/-/image-meta-0.2.2.tgz", {}, "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA=="],
"immutable": ["immutable@5.1.6", "", {}, "sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ=="],
"import-meta-resolve": ["import-meta-resolve@4.2.0", "https://registry.npmmirror.com/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
"impound": ["impound@1.1.5", "https://registry.npmmirror.com/impound/-/impound-1.1.5.tgz", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "es-module-lexer": "^2.0.0", "pathe": "^2.0.3", "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1" } }, "sha512-5AUn+QE0UofqNHu5f2Skf6Svukdg4ehOIq8O0EtqIx4jta0CDZYBPqpIHt0zrlUTiFVYlLpeH39DoikXBjPKpA=="],
@ -1132,6 +1153,8 @@
"lodash": ["lodash@4.18.1", "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
"lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
"lodash.defaults": ["lodash.defaults@4.2.0", "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
"lodash.isarguments": ["lodash.isarguments@3.1.0", "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
@ -1446,10 +1469,52 @@
"run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
"safe-buffer": ["safe-buffer@5.2.1", "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sass": ["sass@1.100.0", "", { "dependencies": { "chokidar": "^5.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ=="],
"sass-embedded": ["sass-embedded@1.100.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "colorjs.io": "^0.5.0", "immutable": "^5.1.5", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-all-unknown": "1.100.0", "sass-embedded-android-arm": "1.100.0", "sass-embedded-android-arm64": "1.100.0", "sass-embedded-android-riscv64": "1.100.0", "sass-embedded-android-x64": "1.100.0", "sass-embedded-darwin-arm64": "1.100.0", "sass-embedded-darwin-x64": "1.100.0", "sass-embedded-linux-arm": "1.100.0", "sass-embedded-linux-arm64": "1.100.0", "sass-embedded-linux-musl-arm": "1.100.0", "sass-embedded-linux-musl-arm64": "1.100.0", "sass-embedded-linux-musl-riscv64": "1.100.0", "sass-embedded-linux-musl-x64": "1.100.0", "sass-embedded-linux-riscv64": "1.100.0", "sass-embedded-linux-x64": "1.100.0", "sass-embedded-unknown-all": "1.100.0", "sass-embedded-win32-arm64": "1.100.0", "sass-embedded-win32-x64": "1.100.0" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw=="],
"sass-embedded-all-unknown": ["sass-embedded-all-unknown@1.100.0", "", { "dependencies": { "sass": "1.100.0" }, "cpu": [ "!arm", "!x64", "!arm64", ] }, "sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q=="],
"sass-embedded-android-arm": ["sass-embedded-android-arm@1.100.0", "", { "os": "android", "cpu": "arm" }, "sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw=="],
"sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.100.0", "", { "os": "android", "cpu": "arm64" }, "sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA=="],
"sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.100.0", "", { "os": "android", "cpu": "none" }, "sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg=="],
"sass-embedded-android-x64": ["sass-embedded-android-x64@1.100.0", "", { "os": "android", "cpu": "x64" }, "sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA=="],
"sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.100.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA=="],
"sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.100.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg=="],
"sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.100.0", "", { "os": "linux", "cpu": "arm" }, "sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw=="],
"sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.100.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ=="],
"sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.100.0", "", { "os": "linux", "cpu": "arm" }, "sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw=="],
"sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.100.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg=="],
"sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.100.0", "", { "os": "linux", "cpu": "none" }, "sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ=="],
"sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.100.0", "", { "os": "linux", "cpu": "x64" }, "sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA=="],
"sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.100.0", "", { "os": "linux", "cpu": "none" }, "sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ=="],
"sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.100.0", "", { "os": "linux", "cpu": "x64" }, "sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA=="],
"sass-embedded-unknown-all": ["sass-embedded-unknown-all@1.100.0", "", { "dependencies": { "sass": "1.100.0" }, "os": [ "!linux", "!win32", "!darwin", "!android", ] }, "sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A=="],
"sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.100.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg=="],
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.100.0", "", { "os": "win32", "cpu": "x64" }, "sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw=="],
"sax": ["sax@1.6.0", "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"scule": ["scule@1.3.0", "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
@ -1536,7 +1601,7 @@
"stylehacks": ["stylehacks@7.0.11", "https://registry.npmmirror.com/stylehacks/-/stylehacks-7.0.11.tgz", { "dependencies": { "browserslist": "^4.28.2", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.5.13" } }, "sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg=="],
"supports-color": ["supports-color@10.2.2", "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
@ -1544,6 +1609,10 @@
"svgo": ["svgo@4.0.1", "https://registry.npmmirror.com/svgo/-/svgo-4.0.1.tgz", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
"sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="],
"sync-message-port": ["sync-message-port@1.2.0", "", {}, "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg=="],
"tagged-tag": ["tagged-tag@1.0.0", "https://registry.npmmirror.com/tagged-tag/-/tagged-tag-1.0.0.tgz", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
"tailwindcss": ["tailwindcss@4.3.0", "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.0.tgz", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="],
@ -1640,6 +1709,8 @@
"util-deprecate": ["util-deprecate@1.0.2", "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
"vite": ["vite@7.3.2", "https://registry.npmmirror.com/vite/-/vite-7.3.2.tgz", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="],
"vite-dev-rpc": ["vite-dev-rpc@1.1.0", "https://registry.npmmirror.com/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", { "dependencies": { "birpc": "^2.4.0", "vite-hot-client": "^2.1.0" }, "peerDependencies": { "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" } }, "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A=="],
@ -1764,6 +1835,8 @@
"@parcel/watcher-wasm/napi-wasm": ["napi-wasm@1.1.3", "https://registry.npmmirror.com/napi-wasm/-/napi-wasm-1.1.3.tgz", { "bundled": true }, "sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg=="],
"@poppinss/dumper/supports-color": ["supports-color@10.2.2", "https://registry.npmmirror.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
"@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@rollup/plugin-inject/estree-walker": ["estree-walker@2.0.2", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],

9
nuxt.config.ts

@ -1,10 +1,16 @@
import tailwindcss from '@tailwindcss/vite'
import { federation } from "@module-federation/vite";
// import { federation } from "@module-federation/vite";
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
modules: ['@nuxt/icon', 'bolt-ui/nuxt'],
boltUi: {
importStyles: false,
},
app: {
head: {
link: [
@ -69,7 +75,6 @@ export default defineNuxtConfig({
}
},
modules: ['@nuxt/icon'],
icon: {
mode: 'css',
cssLayer: 'base'

4
package.json

@ -22,7 +22,9 @@
"@libsql/client": "0.17.3",
"@module-federation/vite": "1.15.5",
"@nuxt/icon": "2.2.2",
"@types/lodash-es": "4.17.12",
"bcryptjs": "3.0.3",
"bolt-ui": "workspace:*",
"cache": "workspace:*",
"common": "workspace:*",
"croner": "10.0.1",
@ -31,6 +33,7 @@
"drizzle-pkg": "workspace:*",
"drizzle-seed": "0.3.1",
"drizzle-zod": "0.8.3",
"lodash-es": "4.18.1",
"log4js": "6.9.1",
"logger": "workspace:*",
"mime": "4.1.0",
@ -48,6 +51,7 @@
"@types/multer": "2.1.0",
"@types/node": "25.8.0",
"drizzle-kit": "0.31.10",
"sass-embedded": "1.100.0",
"tsconfig": "workspace:*",
"tsx": "4.21.0",
"typescript": "6.0.2",

5
packages/bolt-ui/components/Button/index.ts

@ -0,0 +1,5 @@
import { withInstall } from 'bolt-ui/utils/vue/install'
import Button from './src/Button.vue'
export const BoButton = withInstall(Button)
export default BoButton

23
packages/bolt-ui/components/Button/src/Button.vue

@ -0,0 +1,23 @@
<template>
<button :class="[b(), m(type)]">
<slot />
</button>
</template>
<script setup lang="ts">
import { useNamespace } from 'bolt-ui/utils/hooks/use-namespace'
withDefaults(
defineProps<{
type?: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'warning' | 'info' | 'success'
}>(),
{
type: 'primary'
}
)
const { b, m } = useNamespace('button')
defineOptions({
name: 'BoButton'
})
</script>

1
packages/bolt-ui/components/Button/style/index.ts

@ -0,0 +1 @@
import 'bolt-ui/theme-chalk/src/button.scss'

5
packages/bolt-ui/components/ConfigProvider/index.ts

@ -0,0 +1,5 @@
import { withInstall } from 'bolt-ui/utils/vue/install'
import ConfigProvider from './src/ConfigProvider.vue'
export const BoConfigProvider = withInstall(ConfigProvider)
export default BoConfigProvider

33
packages/bolt-ui/components/ConfigProvider/src/ConfigProvider.vue

@ -0,0 +1,33 @@
<template>
<slot />
</template>
<script setup lang="ts">
import { provide, computed, watch } from 'vue'
import { ConfigToken } from './Token'
import type { LanguagesType } from 'bolt-ui/locales'
import { useLocale } from 'bolt-ui/locales'
const props = defineProps<{
locale: string
}>()
const { setLocale } = useLocale()
watch(
() => props.locale,
(newVal) => {
setLocale(newVal as LanguagesType)
},
{
immediate: true
}
)
const state = computed(() => {
return {
locale: props.locale ?? 'zh'
}
})
provide(ConfigToken, state)
</script>

3
packages/bolt-ui/components/ConfigProvider/src/Token.ts

@ -0,0 +1,3 @@
import { ComputedRef, InjectionKey } from 'vue'
export const ConfigToken: InjectionKey<ComputedRef<{ locale: string }>> = Symbol('ConfigToken')

10
packages/bolt-ui/components/ConfigProvider/src/useConfigProvider.ts

@ -0,0 +1,10 @@
import { inject } from 'vue'
import { ConfigToken } from './Token'
export function useConfigProvider() {
const config = inject(ConfigToken)
if (!config) {
throw new Error('ConfigProvider not found')
}
return config
}

1
packages/bolt-ui/components/ConfigProvider/style/index.ts

@ -0,0 +1 @@
export {}

2
packages/bolt-ui/components/index.ts

@ -0,0 +1,2 @@
export * from './Button'
export * from './ConfigProvider'

67
packages/bolt-ui/hooks/createContext/index.ts

@ -0,0 +1,67 @@
import { getCurrentInstance, inject, provide } from 'vue'
import type { InjectionKey } from 'vue'
export interface CreateContextOptions<T> {
/** 严格模式:未找到 Provider 且没有 defaultValue 时是否抛错,默认 true */
strict?: boolean
/** 默认值工厂:当没有 Provider 时调用,用返回值兜底,避免共享状态 */
defaultValue?: () => T
/** 自定义 InjectionKey,一般不需要传 */
key?: InjectionKey<T>
}
/**
* provide/inject
*
* 使
* const FooContext = createContext<FooContext>('Foo', {
* strict: true,
* defaultValue: () => ({ ... })
* })
*
* FooContext.provideContext(...)
* const ctx = FooContext.useContext()
*/
export const createContext = <T>(name?: string, options: CreateContextOptions<T> = {}) => {
const { strict = true, defaultValue, key } = options
const contextKey: InjectionKey<T> = key ?? (Symbol(name ?? 'Context') as InjectionKey<T>)
const useContext = () => {
// 在 setup 之外调用没有意义,这里主动给出更友好的错误
if (!getCurrentInstance()) {
throw new Error('useContext 只能在 setup 或生命周期钩子中使用')
}
const ctx = inject<T | undefined>(contextKey, undefined)
if (ctx !== undefined) {
return ctx
}
if (defaultValue) {
// 每次调用 defaultValue 都返回一个“新”默认值,避免跨实例共享
return defaultValue()
}
if (strict) {
throw new Error(
`未找到上层 Provider,请确保已经调用 provideContext 或在正确的组件树中使用(${name ?? 'Context'}`
)
}
return undefined as unknown as T
}
const provideContext = (context: T) => {
provide(contextKey, context)
}
return {
/** 当前上下文使用的 InjectionKey */
key: contextKey,
/** 在上层组件中调用,用于提供上下文 */
provideContext,
/** 在子组件中调用,用于消费上下文,如果未找到会抛出错误 / 或返回默认值 */
useContext
}
}

1
packages/bolt-ui/hooks/index.ts

@ -0,0 +1 @@
export * from './useClickOutside'

38
packages/bolt-ui/hooks/useClickOutside/index.ts

@ -0,0 +1,38 @@
import { onMounted, onUnmounted, Ref } from 'vue'
export function useClickOutside(
elementRef: Ref<HTMLElement | null> | HTMLElement,
callback: (event: MouseEvent) => void,
options: {
ignore?: Ref<HTMLElement | null>[] // 需要忽略的元素
} = {}
) {
const { ignore = [] } = options
const handler = (event: MouseEvent) => {
const el = elementRef instanceof HTMLElement ? elementRef : elementRef.value
if (!el) return
// 检查点击是否在忽略的元素内
const isIgnored = ignore.some((ref) => {
const element = ref?.value
return element && (element === event.target || element.contains(event.target as Node))
})
if (isIgnored) return
// 检查点击是否在目标元素外
if (!(el === event.target || el.contains(event.target as Node))) {
callback(event)
}
}
onMounted(() => {
document.addEventListener('click', handler, true)
})
onUnmounted(() => {
document.removeEventListener('click', handler, true)
})
}

2
packages/bolt-ui/index.ts

@ -0,0 +1,2 @@
export * from './components'
export * from './hooks'

68
packages/bolt-ui/locales/index.ts

@ -0,0 +1,68 @@
import { get } from 'lodash-es'
import zh from './languages/zh.json'
import en from './languages/en.json'
import { reactive } from 'vue'
const Languages = {
zh: zh,
en: en
}
export type LanguagesType = keyof typeof Languages
const LocaleState = reactive<{
locale: LanguagesType
}>({
locale: 'zh'
})
type FlattenObject<T, Prefix extends string = ''> = T extends object
? {
[K in keyof T & (string | number)]: FlattenObject<
T[K],
Prefix extends '' ? `${K}` : `${Prefix}.${K}`
>
}[keyof T & (string | number)]
: Prefix
type FlattenKeys<T> = FlattenObject<T>
type TranslationKey = FlattenKeys<typeof zh>
function useLocale() {
function setLocale(locale: LanguagesType) {
LocaleState.locale = locale
}
function getLocale(): string {
return LocaleState.locale
}
function t(key: TranslationKey, replacements?: Record<string, string>): string {
let text: string =
LocaleState.locale in Languages
? get(Languages[LocaleState.locale], key)
: get(Languages['zh'], key)
if (!text) {
text = get(Languages['zh'], key)
if (!text) {
return key
}
}
if (replacements) {
Object.entries(replacements).forEach(([key, value]) => {
text = text.replace(new RegExp(`{${key}}`, 'g'), value)
})
}
return text
}
return {
setLocale,
getLocale,
t
}
}
export { useLocale }

11
packages/bolt-ui/locales/languages/en.json

@ -0,0 +1,11 @@
{
"input": {
"placeholder": "Please enter content"
},
"select": {
"placeholder": "Please select"
},
"empty": {
"description": "No Data"
}
}

11
packages/bolt-ui/locales/languages/zh.json

@ -0,0 +1,11 @@
{
"input": {
"placeholder": "请输入内容"
},
"select": {
"placeholder": "请选择"
},
"empty": {
"description": "暂无数据"
}
}

136
packages/bolt-ui/nuxt.ts

@ -0,0 +1,136 @@
import {
defineNuxtModule,
addComponent,
addImports,
createResolver,
} from '@nuxt/kit'
import { readdirSync, existsSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
export interface ModuleOptions {
/**
* Whether to import global base styles (CSS variables / design tokens).
* @default true
*/
importStyles?: boolean
}
function kebabCase(str: string): string {
return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '')
}
// ── 从源码中提取 export function / export const 名称 ─────────
function extractExports(filePath: string): string[] {
try {
const content = readFileSync(filePath, 'utf-8')
const names: string[] = []
const re = /export\s+(?:function|const|let|var)\s+(\w+)/g
let m: RegExpExecArray | null
while ((m = re.exec(content)) !== null) names.push(m[1] as any)
return names
} catch {
return []
}
}
function isComposable(name: string): boolean {
return name.startsWith('use') || name === 'createContext'
}
// ── Vite 插件:自动注入组件样式 ────────────────────────────
function BoltUiStylePlugin(styleMap: Record<string, string>) {
return {
name: 'bolt-ui:style-inject',
enforce: 'post',
transform(code: string, id: string) {
if (!/\.(vue|tsx?|jsx?)$/.test(id)) return null
if (id.includes('node_modules')) return null
const matchedStyles = Object.entries(styleMap)
.filter(([name]) => code.includes(name))
.map(([, style]) => style)
if (matchedStyles.length === 0) return null
const imports = [...new Set(matchedStyles)]
.map(p => `import '${p}';`)
.join('\n')
return {
code: imports + '\n' + code,
map: null,
}
},
}
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'bolt-ui',
configKey: 'boltUi',
},
defaults: {
importStyles: true,
},
setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
// ── CSS 变量层(design tokens)────────────────────────────
if (options.importStyles) {
nuxt.options.css.push(resolver.resolve('./theme-chalk/src/theme/index.scss'))
}
// ── 自动扫描组件目录 ────────────────────────────────────
const componentsDir = resolver.resolve('./components')
const componentStyleMap: Record<string, string> = {}
for (const entry of readdirSync(componentsDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue
const name = entry.name
const indexPath = join(componentsDir, name, 'index.ts')
if (!existsSync(indexPath)) continue
// 注册组件
addComponent({
name: `Bo${name}`,
export: `Bo${name}`,
filePath: resolver.resolve(`./components/${name}/index.ts`),
})
// 构建样式映射(约定:theme-chalk/src/${kebab}.scss)
const scssPath = join(componentsDir, '..', 'theme-chalk/src', `${kebabCase(name)}.scss`)
if (existsSync(scssPath)) {
componentStyleMap[`Bo${name}`] = `bolt-ui/theme-chalk/src/${kebabCase(name)}.scss`
}
}
// ── Vite 插件:按需注入组件样式 ─────────────────────────
nuxt.hook('vite:extendConfig', (config) => {
// @ts-ignore
config.plugins = config.plugins || []
config.plugins.push(BoltUiStylePlugin(componentStyleMap) as any)
})
// ── 自动扫描 composable 目录 ──────────────────────────
for (const scanDir of ['hooks', 'utils', 'locales']) {
const absDir = resolver.resolve(`./${scanDir}`)
if (!existsSync(absDir)) continue
const walk = (dir: string) => {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const fullPath = join(dir, entry.name)
if (entry.isDirectory()) {
walk(fullPath)
} else if (entry.name === 'index.ts') {
const names = extractExports(fullPath).filter(isComposable)
for (const name of names) {
addImports({ name, as: name, from: fullPath })
}
}
}
}
walk(absDir)
}
},
})

8
packages/bolt-ui/package.json

@ -0,0 +1,8 @@
{
"name": "bolt-ui",
"type": "module",
"private": true,
"scripts": {
"build": "tsc -p ./tsconfig.json"
}
}

67
packages/bolt-ui/resolver.ts

@ -0,0 +1,67 @@
const noStylesComponents: any[] = []
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default (options: any = {}): any => {
let optionsResolved: any
async function resolveOptions() {
if (optionsResolved) return optionsResolved
optionsResolved = {
exclude: undefined,
noStylesComponents: options.noStylesComponents || [],
...options
}
return optionsResolved
}
return {
type: 'component',
resolve: async (name: string) => {
const options = await resolveOptions()
if ([...options.noStylesComponents, ...noStylesComponents].includes(name))
return resolveComponent(name, { ...options, importStyle: false })
else return resolveComponent(name, options)
}
}
}
// function kebabCase(key: string) {
// const result = key.replace(/([A-Z])/g, ' $1').trim()
// return result.split(' ').join('-').toLowerCase()
// }
function pascalCase(key: string): string {
// 第一步:将所有分隔符(-、_、空格)替换为空格,统一处理
const replaced = key.replace(/[-_\s]/g, ' ')
// 第二步:处理可能的连续大写字母(如HTML),在大写字母前加空格(除了开头)
const spaced = replaced.replace(/([A-Z])/g, (_, p1, index) => {
return index === 0 ? p1 : ` ${p1}`
})
// 第三步:去除首尾空格,并按空格拆分为单词数组
const words = spaced.trim().split(/\s+/)
// 第四步:每个单词首字母大写,其余小写,然后拼接
return words
.map((word) => {
if (word.length === 0) return ''
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
})
.join('')
}
function resolveComponent(name: string, options: any) {
if (options.exclude && name.match(options.exclude)) return
if (!name.match(/^Bo[A-Z]/)) return
const partialName = pascalCase(name.slice(2))
return {
name,
from: `${'bolt-ui'}`,
sideEffects: getSideEffects(partialName)
}
}
function getSideEffects(dirName: string) {
const componentsFolder = 'bolt-ui/components'
return [`${componentsFolder}/${dirName}/style/index`, 'bolt-ui/theme-chalk/src/theme/index.scss']
}

50
packages/bolt-ui/theme-chalk/src/button.scss

@ -0,0 +1,50 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('button');
#{b()} {
display: inline-block;
white-space: nowrap;
cursor: pointer;
background: #fff;
// border: 1px solid #dcdfe6;
// border-color: #dcdfe6;
border-radius: 8px;
border: none;
padding: 0 20px;
height: 40px;
line-height: 40px;
font-size: 14px;
&#{m('primary')} {
background: var(--color-primary);
color: var(--color-primary-content);
}
&#{m('secondary')} {
background: var(--color-secondary);
color: var(--color-secondary-content);
}
&#{m('tertiary')} {
background: #f8f9fa;
color: #212529;
}
&#{m('danger')} {
background: #dc3545;
color: #fff;
}
&#{m('warning')} {
background: #ffc107;
color: #212529;
}
&:hover {
opacity: 0.9;
}
&:active {
opacity: 0.8;
}
&:disabled {
opacity: 0.5;
}
}

11
packages/bolt-ui/theme-chalk/src/card.scss

@ -0,0 +1,11 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('card');
#{b()} {
background-color: #fff;
border-radius: 4px;
padding: 10px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
}

44
packages/bolt-ui/theme-chalk/src/core/_base.scss

@ -0,0 +1,44 @@
$prefix: 'bo';
$namespace: '';
@mixin setNamespace($nc) {
$namespace: $nc !global;
}
@function b() {
@return '.#{$prefix}-#{$namespace}';
}
@function e($element) {
$block: b();
@return '#{$block}__#{$element}';
}
@function m($modifier) {
$block: b();
@return '#{$block}--#{$modifier}';
}
@function bm($blockSuffix, $modifier) {
$block: b();
@return '#{$block}-#{$blockSuffix}--#{$modifier}';
}
@function em($element, $modifier) {
$block: b();
@return '#{$block}__#{$element}--#{$modifier}';
}
@function be($blockSuffix, $element) {
$block: b();
@return '#{$block}-#{$blockSuffix}__#{$element}';
}
@function bem($blockSuffix, $element, $modifier) {
$block: b();
@return '#{$block}-#{$blockSuffix}__#{$element}--#{$modifier}';
}
@function is($state) {
@return '.is-#{$state}';
}

51
packages/bolt-ui/theme-chalk/src/dropdown.scss

@ -0,0 +1,51 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('dropdown');
#{b()} {
position: relative;
#{e('content')} {
position: absolute;
top: 100%;
left: 0;
z-index: 9999;
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.1s ease-out;
overflow: hidden;
min-width: 80px;
&#{is('hidden')} {
grid-template-rows: 0fr;
}
#{e('wrapper')} {
min-height: 0;
#{e('list')} {
box-sizing: border-box;
background-color: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
#{e('item')} {
padding: 10px;
font-size: 14px;
line-height: 1;
display: flex;
justify-content: center;
cursor: pointer;
&:hover {
background-color: #f5f5f5;
}
}
#{e('line')} {
width: 100%;
height: 1px;
background-color: #dcdfe6;
transform: scaleY(0.5);
}
}
}
}
}

38
packages/bolt-ui/theme-chalk/src/empty.scss

@ -0,0 +1,38 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('empty');
#{b()} {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
text-align: center;
color: #909399;
font-size: 14px;
#{e('image')} {
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: center;
img {
display: block;
user-select: none;
}
}
#{e('description')} {
margin-bottom: 20px;
color: #909399;
font-size: 14px;
line-height: 1.5;
}
#{e('footer')} {
margin-top: 10px;
}
}

59
packages/bolt-ui/theme-chalk/src/float.scss

@ -0,0 +1,59 @@
@use 'core/_base' as *;
@include setNamespace('float');
#{b()} {
box-sizing: border-box;
margin: 0;
min-width: 0;
/* fixed + 块级 + 100% 会占满视口宽,getBoundingClientRect 近 innerWidth,clamp 会把 x 锁死 */
width: max-content;
max-width: calc(100vw - 1rem);
border-radius: var(--radius-box, 0.5rem);
box-shadow:
0 4px 6px -1px rgb(0 0 0 / 0.08),
0 2px 4px -2px rgb(0 0 0 / 0.06);
background-color: var(--color-base-100, #fafafa);
border: var(--border, 1px) solid var(--color-base-300, oklch(92% 0.04 240));
overflow: hidden;
&#{is('dragging')} {
cursor: grabbing;
user-select: none;
touch-action: none;
}
&#{is('no-drag')} {
#{e('handle')} {
cursor: default;
touch-action: auto;
}
}
#{e('handle')} {
display: flex;
align-items: center;
justify-content: center;
min-height: 2rem;
cursor: grab;
touch-action: none;
flex-shrink: 0;
&:active {
cursor: grabbing;
}
}
#{e('handle-grip')} {
display: block;
width: 2rem;
height: 0.25rem;
border-radius: 999px;
opacity: 0.35;
}
#{e('body')} {
box-sizing: border-box;
min-height: 0;
}
}

94
packages/bolt-ui/theme-chalk/src/input.scss

@ -0,0 +1,94 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('input');
#{b()} {
position: relative;
display: inline-flex;
width: 100%;
box-sizing: border-box;
vertical-align: middle;
&#{m('mini')} #{e('wrapper')} {
height: 32px;
line-height: 32px;
font-size: 12px;
}
&#{m('small')} #{e('wrapper')} {
height: 36px;
line-height: 36px;
font-size: 14px;
}
&#{m('large')} #{e('wrapper')} {
height: 44px;
line-height: 44px;
font-size: 16px;
}
#{e('wrapper')} {
width: 100%;
display: inline-flex;
font-size: 14px;
height: 40px;
line-height: 40px;
color: #606266;
transition:
border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
background-color: #e9e9e9;
border: 1px solid transparent;
border-radius: 4px;
padding: 0 15px;
box-sizing: border-box;
&:not(:disabled):has(#{e('inner')}:focus) {
border-color: #007bff;
box-shadow: 0 0 3px 2px rgba(0, 123, 255, 0.4);
background-color: #fff;
}
}
#{e('inner')} {
-webkit-appearance: none;
background-image: none;
background: none;
border: none;
box-sizing: border-box;
line-height: inherit;
color: inherit;
font-size: inherit;
outline: none;
width: 100%;
}
#{e('prefix')} {
margin-right: 8px;
font-size: 1.5em;
}
#{e('suffix')} {
margin-left: 8px;
font-size: 1.5em;
}
&#{is('disabled')} {
#{e('wrapper')} {
// color: #ccc;
// background-color: #e9e9e9;
opacity: 0.6;
}
#{e('inner')} {
cursor: not-allowed;
}
}
&#{is('readonly')} {
#{e('inner')} {
cursor: default;
}
position: relative;
transition: width 0.2s ease-in-out;
&#{is('hover-show')}:hover #{e('wrapper')} {
position: absolute;
transform: translateY(-50%);
width: 300px;
}
}
}

144
packages/bolt-ui/theme-chalk/src/select.scss

@ -0,0 +1,144 @@
@use 'core/_base' as *;
@use 'sass:selector';
@include setNamespace('select');
#{b()} {
position: relative;
display: inline-flex;
width: 100%;
box-sizing: border-box;
vertical-align: middle;
&#{m('mini')} #{e('wrapper')} {
height: 32px;
line-height: 32px;
font-size: 12px;
}
&#{m('small')} #{e('wrapper')} {
height: 36px;
line-height: 36px;
font-size: 14px;
}
&#{m('large')} #{e('wrapper')} {
height: 44px;
line-height: 44px;
font-size: 16px;
}
#{e('wrapper')} {
width: 100%;
display: inline-flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
height: 40px;
line-height: 40px;
color: #606266;
transition:
border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
background-color: #e9e9e9;
border: 1px solid transparent;
border-radius: 4px;
padding: 0 15px;
box-sizing: border-box;
cursor: pointer;
&:hover:not(:disabled) {
background-color: #fff;
}
&:not(:disabled):has(#{e('inner')}:focus) {
border-color: #007bff;
box-shadow: 0 0 3px 2px rgba(0, 123, 255, 0.4);
background-color: #fff;
}
}
#{e('inner')} {
flex: 1;
box-sizing: border-box;
line-height: inherit;
color: inherit;
font-size: inherit;
outline: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#{e('suffix')} {
margin-left: 8px;
display: flex;
align-items: center;
color: #909399;
transition: transform 0.2s;
flex-shrink: 0;
}
#{e('dropdown')} {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
z-index: 99;
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.1s ease-out;
overflow: hidden;
&#{is('hidden')} {
grid-template-rows: 0fr;
}
#{e('dropdown-wrapper')} {
min-height: 0;
#{e('dropdown-list')} {
box-sizing: border-box;
background-color: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
max-height: 274px;
overflow-y: auto;
#{e('dropdown-item')} {
padding: 10px 15px;
font-size: 14px;
line-height: 1.5;
display: flex;
align-items: center;
cursor: pointer;
color: #606266;
&:hover {
background-color: #f5f7fa;
}
&#{is('selected')} {
color: #007bff;
background-color: #ecf5ff;
}
}
#{e('dropdown-line')} {
width: 100%;
height: 1px;
background-color: #dcdfe6;
transform: scaleY(0.5);
}
}
}
}
&#{is('disabled')} {
#{e('wrapper')} {
opacity: 0.6;
cursor: not-allowed;
}
#{e('inner')} {
cursor: not-allowed;
}
}
}

38
packages/bolt-ui/theme-chalk/src/theme/index.scss

@ -0,0 +1,38 @@
:root {
--color-base-100: oklch(98% 0.02 240);
--color-base-200: oklch(95% 0.03 240);
--color-base-300: oklch(92% 0.04 240);
--color-base-content: oklch(20% 0.05 240);
--color-primary: oklch(55% 0.3 240);
--color-primary-content: oklch(98% 0.01 240);
--color-secondary: oklch(70% 0.25 200);
--color-secondary-content: oklch(98% 0.01 200);
--color-accent: oklch(65% 0.25 160);
--color-accent-content: oklch(98% 0.01 160);
--color-neutral: oklch(50% 0.05 240);
--color-neutral-content: oklch(98% 0.01 240);
--color-info: oklch(70% 0.2 220);
--color-info-content: oklch(98% 0.01 220);
--color-success: oklch(65% 0.25 140);
--color-success-content: oklch(98% 0.01 140);
--color-warning: oklch(80% 0.25 80);
--color-warning-content: oklch(20% 0.05 80);
--color-error: oklch(65% 0.3 30);
--color-error-content: oklch(98% 0.01 30);
/* border radius */
--radius-selector: 1rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
/* base sizes */
--size-selector: 0.25rem;
--size-field: 0.25rem;
/* border size */
--border: 1px;
/* effects */
--depth: 1;
--noise: 0;
}

5
packages/bolt-ui/tsconfig.json

@ -0,0 +1,5 @@
{
"extends": "tsconfig/tsconfig.json",
"include": ["resolver.ts"],
"exclude": ["node_modules", "dist"]
}

127
packages/bolt-ui/utils/hooks/use-namespace/index.ts

@ -0,0 +1,127 @@
import { computed, getCurrentInstance, inject, isRef, ref, unref } from 'vue'
import type { InjectionKey, MaybeRef, Ref } from 'vue'
export const defaultNamespace = 'bo'
const statePrefix = 'is-'
const _bem = (
namespace: string,
block: string,
blockSuffix: string,
element: string,
modifier: string
) => {
let cls = `${namespace}-${block}`
if (blockSuffix) {
cls += `-${blockSuffix}`
}
if (element) {
cls += `__${element}`
}
if (modifier) {
cls += `--${modifier}`
}
return cls
}
export const namespaceContextKey: InjectionKey<Ref<string | undefined>> =
Symbol('namespaceContextKey')
/**
*
* @param namespaceOverrides
* @returns
*/
export const useGetDerivedNamespace = (namespaceOverrides?: Ref<string | undefined>) => {
const derivedNamespace =
namespaceOverrides ||
(getCurrentInstance()
? inject(namespaceContextKey, ref(defaultNamespace))
: ref(defaultNamespace))
const namespace = computed(() => {
return unref(derivedNamespace) || defaultNamespace
})
return namespace
}
export const useNamespace = (
block: MaybeRef<string>,
namespaceOverrides?: Ref<string | undefined>
) => {
const namespace = useGetDerivedNamespace(namespaceOverrides)
const getBlock = () => (isRef(block) ? block.value : block)
const b = (blockSuffix = '') => _bem(namespace.value, getBlock(), blockSuffix, '', '') // bo-button-test
const e = (element?: string) =>
element ? _bem(namespace.value, getBlock(), '', element, '') : '' // bo-button__test
const m = (modifier?: string) =>
modifier ? _bem(namespace.value, getBlock(), '', '', modifier) : '' // bo-button--test
const be = (blockSuffix?: string, element?: string) =>
blockSuffix && element
? _bem(namespace.value, getBlock(), blockSuffix, element, '') // bo-button-test__test
: ''
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(namespace.value, getBlock(), '', element, modifier) // bo-button__test--test
: ''
const bm = (blockSuffix?: string, modifier?: string) =>
blockSuffix && modifier
? _bem(namespace.value, getBlock(), blockSuffix, '', modifier) // bo-button-test--test
: ''
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
blockSuffix && element && modifier
? _bem(namespace.value, getBlock(), blockSuffix, element, modifier) // bo-button-test__test--test
: ''
const is: {
(name: string, state: boolean | undefined): string
(name: string): string
} = (name: string, ...args: [boolean | undefined] | []) => {
const state = args.length >= 1 ? args[0]! : true
return name && state ? `${statePrefix}${name}` : '' // is-test
}
// for css var
// --bo-xxx: value;
const cssVar = (object: Record<string, string>) => {
const styles: Record<string, string> = {}
for (const key in object) {
if (object[key]) {
styles[`--${namespace.value}-${key}`] = object[key] // --bo-test: test
}
}
return styles
}
// with block
const cssVarBlock = (object: Record<string, string>) => {
const styles: Record<string, string> = {}
for (const key in object) {
if (object[key]) {
styles[`--${namespace.value}-${getBlock()}-${key}`] = object[key] // --bo-button-test: test
}
}
return styles
}
const cssVarName = (name: string) => `--${namespace.value}-${name}` // --bo-test
const cssVarBlockName = (name: string) => `--${namespace.value}-${getBlock()}-${name}` // --bo-button-test
return {
namespace,
// bo-button-test
b,
e,
m,
be,
em,
bm,
bem,
is,
// css
cssVar,
cssVarName,
cssVarBlock,
cssVarBlockName
}
}
export type UseNamespaceReturn = ReturnType<typeof useNamespace>

26
packages/bolt-ui/utils/vue/install.ts

@ -0,0 +1,26 @@
import type { App } from 'vue'
import type { SFCWithInstall } from './types'
import { NOOP } from './types'
export const withNoopInstall = <T>(component: T) => {
;(component as SFCWithInstall<T>).install = NOOP
return component as SFCWithInstall<T>
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const withInstall = <T, E extends Record<string, any>>(main: T, extra?: E) => {
;(main as SFCWithInstall<T>).install = (app: App): void => {
for (const comp of [main, ...Object.values(extra ?? {})]) {
app.component(comp.name, comp)
}
}
if (extra) {
for (const [key, comp] of Object.entries(extra)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(main as any)[key] = comp
}
}
return main as SFCWithInstall<T> & E
}

12
packages/bolt-ui/utils/vue/types.ts

@ -0,0 +1,12 @@
import type { AppContext, Plugin } from 'vue'
import type { Ref } from 'vue'
export type SFCWithInstall<T> = T & Plugin
export type SFCInstallWithContext<T> = SFCWithInstall<T> & {
_context: AppContext | null
}
export const NOOP = () => {}
export type MaybeRef<T> = T | Ref<T>
Loading…
Cancel
Save