Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.5.1-beta07</Version>
<Version>10.5.1-beta08</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
101 changes: 25 additions & 76 deletions src/BootstrapBlazor/wwwroot/modules/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EventHandler from "./event-handler.js"
const vibrate = () => {
if ('vibrate' in window.navigator) {
window.navigator.vibrate([200, 100, 200])
const handler = window.setTimeout(function () {
const handler = window.setTimeout(function() {
window.clearTimeout(handler)
window.navigator.vibrate([])
}, 1000)
Expand Down Expand Up @@ -175,16 +175,10 @@ const normalizeLink = link => {
return url
}

/**
* 添加 script 标签到 head
* @param {string} content
* @returns
*/
const addScript = content => {
// content 文件名
const scripts = [...document.getElementsByTagName('script')]
const url = normalizeLink(content)
let link = scripts.filter(function (link) {
let link = scripts.filter(function(link) {
return link.src.indexOf(url) > -1
})
if (link.length === 0) {
Expand All @@ -207,72 +201,41 @@ const addScript = content => {
})
}

/**
* 从 head 移除 script 标签
* @param {string} content
*/
const removeScript = content => {
const links = [...document.getElementsByTagName('script')]
const url = normalizeLink(content)
const nodes = links.filter(function (link) {
const nodes = links.filter(function(link) {
return link.src.indexOf(url) > -1
})
for (let index = 0; index < nodes.length; index++) {
document.body.removeChild(nodes[index])
}
}

/**
* 批量添加 script 标签到 head
* @param {string[]} content
* @returns
*/
const addScriptBatch = content => {
const promises = content.map(item => addScript(item));
return Promise.all(promises);
}

/**
* 从 head 批量移除 script 标签
* @param {string[]} content
* @returns
*/
const removeScriptBatch = (content) => {
const promises = content.map(item => removeScript(item));
return Promise.all(promises);
}

/**
* 批量添加 link 标签到 head
* @param {string[]} href
* @param {string} rel
* @returns
*/
const addLinkBatch = (href, rel = "stylesheet") => {
const promises = href.map(item => addLink(item, rel));
return Promise.all(promises);
}

/**
* 从 head 批量移除 link 标签
* @param {string[]} href
* @returns
*/
const removeLinkBatch = (href) => {
const promises = href.map(item => removeLink(item));
return Promise.all(promises);
}

/**
* 添加 link 标签到 head
* @param {string} href
* @param {string} rel
* @returns
*/
const addLink = (href, rel = "stylesheet") => {
const links = [...document.getElementsByTagName('link')]
const url = normalizeLink(href)
let link = links.filter(function (link) {
let link = links.filter(function(link) {
return link.href.indexOf(url) > -1
})
if (link.length === 0) {
Expand All @@ -296,25 +259,17 @@ const addLink = (href, rel = "stylesheet") => {
})
}

/**
* 从 head 移除 link 标签
* @param {string} href
*/
const removeLink = href => {
const links = [...document.getElementsByTagName('link')]
const url = normalizeLink(href)
const nodes = links.filter(function (link) {
const nodes = links.filter(function(link) {
return link.href.indexOf(url) > -1
})
for (let index = 0; index < nodes.length; index++) {
document.getElementsByTagName("head")[0].removeChild(nodes[index])
}
}

/**
* 自动识别 css 或者 js 链接并添加到 head
* @param {string[]} fileList
*/
const autoAdd = (fileList) => {
const promises = fileList.map(async (item) => {
const extension = item.match(/\.(\w+)(\?|$)/)[1];
Expand All @@ -329,10 +284,6 @@ const autoAdd = (fileList) => {
return Promise.all(promises);
}

/**
* 自动识别 css 或者 js 链接并从 head 中移除
* @param {string[]} fileList
*/
const autoRemove = (fileList) => {
const promises = fileList.map(async (item) => {
const extension = item.match(/\.(\w+)(\?|$)/)[1];
Expand Down Expand Up @@ -456,7 +407,6 @@ const isElement = object => {
}

const getElement = object => {
// it's a jQuery object or a node element
if (isElement(object)) {
return object.jquery ? object[0] : object
}
Expand Down Expand Up @@ -495,18 +445,15 @@ const getTransitionDurationFromElement = (element) => {
return 0
}

// Get transition-duration of the element
let { transitionDuration, transitionDelay } = window.getComputedStyle(element)

const floatTransitionDuration = Number.parseFloat(transitionDuration)
const floatTransitionDelay = Number.parseFloat(transitionDelay)

// Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0
}

// If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0]
transitionDelay = transitionDelay.split(',')[0]

Expand All @@ -519,7 +466,6 @@ const isVisible = element => {
}

const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'
// Handle `details` element as its content may falsie appear visible when it is closed
const closedDetails = element.closest('details:not([open])')

if (!closedDetails) {
Expand Down Expand Up @@ -572,10 +518,10 @@ const hackPopover = (popover, css) => {
}
}

const hackTooltip = function () {
const hackTooltip = function() {
const mock = () => {
const originalDispose = bootstrap.Tooltip.prototype.dispose;
bootstrap.Tooltip.prototype.dispose = function () {
bootstrap.Tooltip.prototype.dispose = function() {
originalDispose.call(this);
// fix https://github.com/twbs/bootstrap/issues/37474
this._activeTrigger = {};
Expand Down Expand Up @@ -608,14 +554,9 @@ const getOverflowParent = element => {
return parent
}

/*
* @param {function} fn - 原函数
* @param {number} duration - 防抖时长
* @return {function} - 条件回调返回真时立即执行
*/
const debounce = function (fn, duration = 200, callback = null) {
const debounce = function(fn, duration = 200, callback = null) {
let handler = null
return function () {
return function() {
if (handler) {
clearTimeout(handler)
}
Expand Down Expand Up @@ -716,8 +657,20 @@ export function getHtml(options) {
return html;
}

export function getTheme() {
return localStorage.getItem('theme') || document.documentElement.getAttribute('data-bs-theme') || getAutoThemeValue();
export function getTheme(useLocalstorage = true) {
useLocalstorage = useLocalstorage ?? true;
let theme = null;
if (useLocalstorage) {
Comment on lines +660 to +663
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter name useLocalstorage is inconsistent with the established localStorage casing (and with the existing useLocalStorage string used by ThemeValue.UseLocalStorage). Renaming it to useLocalStorage would improve readability and reduce confusion when this exported API is used directly from JS.

Suggested change
export function getTheme(useLocalstorage = true) {
useLocalstorage = useLocalstorage ?? true;
let theme = null;
if (useLocalstorage) {
export function getTheme(useLocalStorage = true) {
useLocalStorage = useLocalStorage ?? true;
let theme = null;
if (useLocalStorage) {

Copilot uses AI. Check for mistakes.
theme = localStorage.getItem('theme');
}
else {
theme = document.documentElement.getAttribute('data-bs-theme');
}

if (theme === null || theme === 'auto') {
theme = getAutoThemeValue();
}
return theme;
Comment on lines +660 to +673
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTheme() now resolves a stored/attribute value of 'auto' into 'dark'/'light'. However ThemeProvider.razor.js calls getTheme() when ThemeValue === 'useLocalStorage' and then immediately calls setTheme(currentTheme, true) (which persists back to localStorage). With this change, a previously saved 'auto' preference will be overwritten as 'dark' or 'light', breaking future auto-switching on prefers-color-scheme changes.

Also, when useLocalstorage is true and localStorage has no value (or an empty string), the function no longer falls back to data-bs-theme like the previous implementation did.

Consider keeping getTheme() returning the persisted theme value ('auto'|'dark'|'light'|null) and adding an explicit option/new API to return the effective theme (resolving 'auto' via getAutoThemeValue()), then update only the consumers that need the effective theme.

Copilot uses AI. Check for mistakes.
}

export function saveTheme(theme) {
Expand Down Expand Up @@ -806,7 +759,7 @@ export function registerBootstrapBlazorModule(name, identifier, callback) {
window.BootstrapBlazor[name] = window.BootstrapBlazor[name] || {
_init: false,
_items: [],
register: function (id, cb) {
register: function(id, cb) {
if (id) {
this._items.push(id);
}
Expand All @@ -818,7 +771,7 @@ export function registerBootstrapBlazorModule(name, identifier, callback) {
}
return this;
},
dispose: function (id, cb) {
dispose: function(id, cb) {
if (id) {
this._items = this._items.filter(item => item !== id);
}
Expand Down Expand Up @@ -876,10 +829,6 @@ export function drawImage(canvas, image, offsetWidth, offsetHeight) {
context.drawImage(image, 0, 0, offsetWidth, offsetHeight);
}

/**
* @param {File} file
* @returns {Blob}
*/
export function readFileAsync(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
Expand Down
Loading