選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

682 行
26 KiB

  1. import config from '../config/config.js'
  2. import color from '../config/color.js'
  3. import index from '../function/index.js'
  4. import { trySetTabBarStyle } from './runtime.js'
  5. const DEFAULT_LIGHT_THEME_COLORS = Object.freeze({
  6. primary: '#3c9cff',
  7. info: '#909399',
  8. warning: '#f9ae3d',
  9. error: '#f56c6c',
  10. success: '#5ac725',
  11. mainColor: '#303133',
  12. contentColor: '#606266',
  13. tipsColor: '#909193',
  14. lightColor: '#c0c4cc',
  15. borderColor: '#dadbde',
  16. bgColor: '#f3f4f6',
  17. disabledColor: '#c8c9cc',
  18. primaryDark: '#398ade',
  19. primaryDisabled: '#9acafc',
  20. primaryLight: '#ecf5ff',
  21. warningDark: '#f1a532',
  22. warningDisabled: '#f9d39b',
  23. warningLight: '#fdf6ec',
  24. successDark: '#53c21d',
  25. successDisabled: '#a9e08f',
  26. successLight: '#f5fff0',
  27. errorDark: '#e45656',
  28. errorDisabled: '#f7b2b2',
  29. errorLight: '#fef0f0',
  30. infoDark: '#767a82',
  31. infoDisabled: '#c4c6c9',
  32. infoLight: '#f4f4f5'
  33. })
  34. const DEFAULT_DARK_THEME_COLORS = Object.freeze({
  35. primary: '#3c9cff',
  36. info: '#909399',
  37. warning: '#f9ae3d',
  38. error: '#f56c6c',
  39. success: '#5ac725',
  40. mainColor: '#f5f5f5',
  41. contentColor: '#d1d5db',
  42. tipsColor: '#9ca3af',
  43. lightColor: '#6b7280',
  44. borderColor: '#3a3a3c',
  45. bgColor: '#1f1f1f',
  46. disabledColor: '#4b5563',
  47. primaryDark: '#5aa8ff',
  48. primaryDisabled: '#4c6f92',
  49. primaryLight: '#10243a',
  50. warningDark: '#ffbf66',
  51. warningDisabled: '#8a6a3a',
  52. warningLight: '#3d2f1b',
  53. successDark: '#7ad94b',
  54. successDisabled: '#5f7f4f',
  55. successLight: '#1f3316',
  56. errorDark: '#ff8a8a',
  57. errorDisabled: '#8d5858',
  58. errorLight: '#3a2222',
  59. infoDark: '#b0b3b8',
  60. infoDisabled: '#5f6368',
  61. infoLight: '#2f3238'
  62. })
  63. const DEFAULT_THEME_EXTRA_VARS = Object.freeze({
  64. light: Object.freeze({
  65. '--up-table2-header-bg-color': '#f5f7fa',
  66. '--up-table2-zebra-bg-color': '#fafafa',
  67. '--up-table2-highlight-bg-color': '#f5f7fa',
  68. '--up-gap-bg-color': '#f3f4f6',
  69. '--up-skeleton-bg-color': '#f1f2f4',
  70. '--up-skeleton-shimmer-color': '#e6e6e6',
  71. '--up-swipe-action-button-bg-color': '#c7c6cd',
  72. '--up-index-list-indicator-bg-color': '#c9c9c9',
  73. '--up-calendar-month-mark-color': 'rgba(231, 232, 234, 0.83)'
  74. }),
  75. dark: Object.freeze({
  76. '--up-table2-header-bg-color': '#2a2d33',
  77. '--up-table2-zebra-bg-color': '#23262b',
  78. '--up-table2-highlight-bg-color': '#2f3440',
  79. '--up-gap-bg-color': '#111111',
  80. '--up-skeleton-bg-color': '#2f3135',
  81. '--up-skeleton-shimmer-color': 'rgba(255, 255, 255, 0.12)',
  82. '--up-swipe-action-button-bg-color': '#4b5563',
  83. '--up-index-list-indicator-bg-color': '#4b5563',
  84. '--up-calendar-month-mark-color': 'rgba(255, 255, 255, 0.04)'
  85. })
  86. })
  87. export const themeState = {
  88. preference: 'system',
  89. mode: 'light',
  90. version: 0,
  91. vars: {}
  92. }
  93. const THEME_MODE_STORAGE_KEY = 'u-theme-mode'
  94. const THEME_MODE_SYSTEM = 'system'
  95. const THEME_MODE_MANUAL = ['light', 'dark']
  96. const LIGHT_THEME_TOKEN_FIELD_MAP = Object.freeze({
  97. 'primary': 'primary',
  98. 'primary-dark': 'primaryDark',
  99. 'primary-disabled': 'primaryDisabled',
  100. 'primary-light': 'primaryLight',
  101. 'warning': 'warning',
  102. 'warning-dark': 'warningDark',
  103. 'warning-disabled': 'warningDisabled',
  104. 'warning-light': 'warningLight',
  105. 'success': 'success',
  106. 'success-dark': 'successDark',
  107. 'success-disabled': 'successDisabled',
  108. 'success-light': 'successLight',
  109. 'error': 'error',
  110. 'error-dark': 'errorDark',
  111. 'error-disabled': 'errorDisabled',
  112. 'error-light': 'errorLight',
  113. 'info': 'info',
  114. 'info-dark': 'infoDark',
  115. 'info-disabled': 'infoDisabled',
  116. 'info-light': 'infoLight',
  117. 'main-color': 'mainColor',
  118. 'content-color': 'contentColor',
  119. 'tips-color': 'tipsColor',
  120. 'light-color': 'lightColor',
  121. 'border-color': 'borderColor',
  122. 'bg-color': 'bgColor',
  123. 'disabled-color': 'disabledColor'
  124. })
  125. const LIGHT_THEME_FIELD_TOKEN_MAP = Object.freeze(
  126. Object.fromEntries(
  127. Object.entries(LIGHT_THEME_TOKEN_FIELD_MAP).map(([token, field]) => [field, token])
  128. )
  129. )
  130. const runtimeThemeOverrideState = {
  131. color: Object.create(null),
  132. configColor: Object.create(null)
  133. }
  134. let cachedLightThemeColors = null
  135. let hasRegisterThemeListener = false
  136. let currentThemePreference = THEME_MODE_SYSTEM
  137. function normalizeThemeMode(theme = 'light') {
  138. return theme === 'dark' ? 'dark' : 'light'
  139. }
  140. function normalizeThemePreference(mode = THEME_MODE_SYSTEM) {
  141. if (THEME_MODE_MANUAL.includes(mode)) return mode
  142. return THEME_MODE_SYSTEM
  143. }
  144. function getLightBridgeVar(token, fallback) {
  145. return `var(--up-light-${token}, ${fallback})`
  146. }
  147. function clearOverrideBucket(bucket) {
  148. Object.keys(bucket).forEach((key) => {
  149. delete bucket[key]
  150. })
  151. }
  152. function normalizeLightThemeToken(token = '') {
  153. if (typeof token !== 'string') return ''
  154. if (token.indexOf('up-') === 0) return token.slice(3)
  155. if (token.indexOf('u-') === 0) return token.slice(2)
  156. return token
  157. }
  158. function isLightThemeConfigColorKey(token = '') {
  159. return token.indexOf('up-') === 0 || token.indexOf('u-') === 0
  160. }
  161. export function syncThemeColorOverrideState({
  162. color: colorOverrides,
  163. configColor: configColorOverrides,
  164. reset = false
  165. } = {}) {
  166. if (reset) {
  167. clearOverrideBucket(runtimeThemeOverrideState.color)
  168. clearOverrideBucket(runtimeThemeOverrideState.configColor)
  169. }
  170. if (colorOverrides && typeof colorOverrides === 'object') {
  171. Object.keys(LIGHT_THEME_FIELD_TOKEN_MAP).forEach((field) => {
  172. if (!Object.prototype.hasOwnProperty.call(colorOverrides, field)) return
  173. const value = colorOverrides[field]
  174. if (typeof value === 'string' && value) {
  175. runtimeThemeOverrideState.color[field] = true
  176. return
  177. }
  178. delete runtimeThemeOverrideState.color[field]
  179. })
  180. }
  181. if (configColorOverrides && typeof configColorOverrides === 'object') {
  182. Object.keys(configColorOverrides).forEach((key) => {
  183. const token = normalizeLightThemeToken(key)
  184. if (!Object.prototype.hasOwnProperty.call(LIGHT_THEME_TOKEN_FIELD_MAP, token)) return
  185. const value = configColorOverrides[key]
  186. if (typeof value === 'string' && value) {
  187. const overrideKey = isLightThemeConfigColorKey(key) ? key : `up-${token}`
  188. runtimeThemeOverrideState.configColor[overrideKey] = true
  189. return
  190. }
  191. delete runtimeThemeOverrideState.configColor[key]
  192. delete runtimeThemeOverrideState.configColor[`u-${token}`]
  193. delete runtimeThemeOverrideState.configColor[`up-${token}`]
  194. })
  195. }
  196. }
  197. function getExplicitRuntimeColorValue(token, runtimeColorMap = {}) {
  198. const field = LIGHT_THEME_TOKEN_FIELD_MAP[token]
  199. if (!field) return ''
  200. if (runtimeThemeOverrideState.color[field]) {
  201. const value = color[field]
  202. if (typeof value === 'string' && value) return value
  203. }
  204. const upKey = `up-${token}`
  205. const uKey = `u-${token}`
  206. if (!runtimeThemeOverrideState.configColor[upKey] && !runtimeThemeOverrideState.configColor[uKey]) return ''
  207. const upValue = runtimeColorMap[upKey]
  208. const uValue = runtimeColorMap[uKey]
  209. if (runtimeThemeOverrideState.configColor[upKey] && typeof upValue === 'string' && upValue) return upValue
  210. if (runtimeThemeOverrideState.configColor[uKey] && typeof uValue === 'string' && uValue) return uValue
  211. return ''
  212. }
  213. function readThemePreferenceFromStorage() {
  214. try {
  215. if (typeof uni !== 'undefined' && typeof uni.getStorageSync === 'function') {
  216. const mode = uni.getStorageSync(THEME_MODE_STORAGE_KEY)
  217. return normalizeThemePreference(mode)
  218. }
  219. } catch (e) {}
  220. return THEME_MODE_SYSTEM
  221. }
  222. function writeThemePreferenceToStorage(mode) {
  223. try {
  224. if (typeof uni !== 'undefined' && typeof uni.setStorageSync === 'function') {
  225. uni.setStorageSync(THEME_MODE_STORAGE_KEY, mode)
  226. }
  227. } catch (e) {}
  228. }
  229. export function getSystemTheme() {
  230. let theme = 'light'
  231. try {
  232. if (typeof uni !== 'undefined' && typeof uni.getAppBaseInfo === 'function') {
  233. const appBaseInfo = uni.getAppBaseInfo() || {}
  234. if (appBaseInfo.theme) {
  235. theme = appBaseInfo.theme
  236. }
  237. }
  238. if (typeof uni !== 'undefined' && typeof uni.getSystemInfoSync === 'function') {
  239. const systemInfo = uni.getSystemInfoSync() || {}
  240. if (systemInfo.theme) {
  241. theme = systemInfo.theme
  242. }
  243. }
  244. } catch (e) {
  245. theme = 'light'
  246. }
  247. return normalizeThemeMode(theme)
  248. }
  249. function getCurrentLightThemeColors() {
  250. const runtimeColorMap = config.color || {}
  251. const lightThemeColors = {
  252. ...DEFAULT_LIGHT_THEME_COLORS
  253. }
  254. Object.keys(LIGHT_THEME_TOKEN_FIELD_MAP).forEach((token) => {
  255. const explicitValue = getExplicitRuntimeColorValue(token, runtimeColorMap)
  256. if (!explicitValue) return
  257. lightThemeColors[LIGHT_THEME_TOKEN_FIELD_MAP[token]] = explicitValue
  258. })
  259. return lightThemeColors
  260. }
  261. function getThemeColorsByMode(mode) {
  262. if (!cachedLightThemeColors) {
  263. cachedLightThemeColors = getCurrentLightThemeColors()
  264. }
  265. const themeMode = normalizeThemeMode(mode)
  266. if (themeMode === 'dark') {
  267. return {
  268. ...DEFAULT_DARK_THEME_COLORS,
  269. primary: cachedLightThemeColors.primary,
  270. info: cachedLightThemeColors.info,
  271. warning: cachedLightThemeColors.warning,
  272. error: cachedLightThemeColors.error,
  273. success: cachedLightThemeColors.success
  274. }
  275. }
  276. return {
  277. ...cachedLightThemeColors
  278. }
  279. }
  280. function buildConfigColorMap(themeColors) {
  281. return {
  282. 'u-primary': themeColors.primary,
  283. 'u-primary-dark': themeColors.primaryDark,
  284. 'u-primary-disabled': themeColors.primaryDisabled,
  285. 'u-primary-light': themeColors.primaryLight,
  286. 'u-warning': themeColors.warning,
  287. 'u-warning-dark': themeColors.warningDark,
  288. 'u-warning-disabled': themeColors.warningDisabled,
  289. 'u-warning-light': themeColors.warningLight,
  290. 'u-success': themeColors.success,
  291. 'u-success-dark': themeColors.successDark,
  292. 'u-success-disabled': themeColors.successDisabled,
  293. 'u-success-light': themeColors.successLight,
  294. 'u-error': themeColors.error,
  295. 'u-error-dark': themeColors.errorDark,
  296. 'u-error-disabled': themeColors.errorDisabled,
  297. 'u-error-light': themeColors.errorLight,
  298. 'u-info': themeColors.info,
  299. 'u-info-dark': themeColors.infoDark,
  300. 'u-info-disabled': themeColors.infoDisabled,
  301. 'u-info-light': themeColors.infoLight,
  302. 'u-main-color': themeColors.mainColor,
  303. 'u-content-color': themeColors.contentColor,
  304. 'u-tips-color': themeColors.tipsColor,
  305. 'u-light-color': themeColors.lightColor,
  306. 'u-border-color': themeColors.borderColor,
  307. 'u-bg-color': themeColors.bgColor,
  308. 'u-disabled-color': themeColors.disabledColor,
  309. 'up-primary': themeColors.primary,
  310. 'up-primary-dark': themeColors.primaryDark,
  311. 'up-primary-disabled': themeColors.primaryDisabled,
  312. 'up-primary-light': themeColors.primaryLight,
  313. 'up-warning': themeColors.warning,
  314. 'up-warning-dark': themeColors.warningDark,
  315. 'up-warning-disabled': themeColors.warningDisabled,
  316. 'up-warning-light': themeColors.warningLight,
  317. 'up-success': themeColors.success,
  318. 'up-success-dark': themeColors.successDark,
  319. 'up-success-disabled': themeColors.successDisabled,
  320. 'up-success-light': themeColors.successLight,
  321. 'up-error': themeColors.error,
  322. 'up-error-dark': themeColors.errorDark,
  323. 'up-error-disabled': themeColors.errorDisabled,
  324. 'up-error-light': themeColors.errorLight,
  325. 'up-info': themeColors.info,
  326. 'up-info-dark': themeColors.infoDark,
  327. 'up-info-disabled': themeColors.infoDisabled,
  328. 'up-info-light': themeColors.infoLight,
  329. 'up-main-color': themeColors.mainColor,
  330. 'up-content-color': themeColors.contentColor,
  331. 'up-tips-color': themeColors.tipsColor,
  332. 'up-light-color': themeColors.lightColor,
  333. 'up-border-color': themeColors.borderColor,
  334. 'up-bg-color': themeColors.bgColor,
  335. 'up-disabled-color': themeColors.disabledColor
  336. }
  337. }
  338. function buildAliasCssVars(vars = {}) {
  339. const aliasVars = {}
  340. Object.keys(vars).forEach((key) => {
  341. if (typeof key !== 'string') return
  342. if (key.indexOf('--up-') === 0) {
  343. aliasVars[key.replace('--up-', '--u-')] = vars[key]
  344. return
  345. }
  346. if (key.indexOf('--u-') === 0) {
  347. aliasVars[key.replace('--u-', '--up-')] = vars[key]
  348. }
  349. })
  350. return aliasVars
  351. }
  352. function buildThemeCssVars(themeColors, mode = 'light') {
  353. const themeMode = normalizeThemeMode(mode)
  354. const isDark = themeMode === 'dark'
  355. const useBridge = !isDark
  356. const runtimeColorMap = config.color || {}
  357. const defaultExtraVars = DEFAULT_THEME_EXTRA_VARS[themeMode] || DEFAULT_THEME_EXTRA_VARS.light
  358. const pageBgColor = themeColors.bgColor || (isDark ? '#1f1f1f' : '#f3f4f6')
  359. const hoverBgColor = runtimeColorMap['up-hover-bg-color']
  360. || runtimeColorMap['u-hover-bg-color']
  361. || (isDark ? '#343741' : '#e7ebf0')
  362. const navbarBgColor = runtimeColorMap['up-navbar-bg-color']
  363. || runtimeColorMap['u-navbar-bg-color']
  364. || (isDark ? '#1c1c1e' : '#ffffff')
  365. const resolveLightTokenValue = (token, fallback) => {
  366. if (!useBridge) return fallback
  367. const explicitValue = getExplicitRuntimeColorValue(token, runtimeColorMap)
  368. return explicitValue || getLightBridgeVar(token, fallback)
  369. }
  370. const resolvedMainColor = resolveLightTokenValue('main-color', themeColors.mainColor)
  371. const resolvedContentColor = resolveLightTokenValue('content-color', themeColors.contentColor)
  372. const resolvedTipsColor = resolveLightTokenValue('tips-color', themeColors.tipsColor)
  373. const resolvedLightColor = resolveLightTokenValue('light-color', themeColors.lightColor)
  374. const resolvedBorderColor = resolveLightTokenValue('border-color', themeColors.borderColor)
  375. const resolvedBgColor = resolveLightTokenValue('bg-color', themeColors.bgColor)
  376. const resolvedDisabledColor = resolveLightTokenValue('disabled-color', themeColors.disabledColor)
  377. const resolvedPrimary = resolveLightTokenValue('primary', themeColors.primary)
  378. const resolvedPrimaryDark = resolveLightTokenValue('primary-dark', themeColors.primaryDark)
  379. const resolvedPrimaryDisabled = resolveLightTokenValue('primary-disabled', themeColors.primaryDisabled)
  380. const resolvedPrimaryLight = resolveLightTokenValue('primary-light', themeColors.primaryLight)
  381. const resolvedWarning = resolveLightTokenValue('warning', themeColors.warning)
  382. const resolvedWarningDark = resolveLightTokenValue('warning-dark', themeColors.warningDark)
  383. const resolvedWarningDisabled = resolveLightTokenValue('warning-disabled', themeColors.warningDisabled)
  384. const resolvedWarningLight = resolveLightTokenValue('warning-light', themeColors.warningLight)
  385. const resolvedSuccess = resolveLightTokenValue('success', themeColors.success)
  386. const resolvedSuccessDark = resolveLightTokenValue('success-dark', themeColors.successDark)
  387. const resolvedSuccessDisabled = resolveLightTokenValue('success-disabled', themeColors.successDisabled)
  388. const resolvedSuccessLight = resolveLightTokenValue('success-light', themeColors.successLight)
  389. const resolvedError = resolveLightTokenValue('error', themeColors.error)
  390. const resolvedErrorDark = resolveLightTokenValue('error-dark', themeColors.errorDark)
  391. const resolvedErrorDisabled = resolveLightTokenValue('error-disabled', themeColors.errorDisabled)
  392. const resolvedErrorLight = resolveLightTokenValue('error-light', themeColors.errorLight)
  393. const resolvedInfo = resolveLightTokenValue('info', themeColors.info)
  394. const resolvedInfoDark = resolveLightTokenValue('info-dark', themeColors.infoDark)
  395. const resolvedInfoDisabled = resolveLightTokenValue('info-disabled', themeColors.infoDisabled)
  396. const resolvedInfoLight = resolveLightTokenValue('info-light', themeColors.infoLight)
  397. const coreVars = {
  398. '--u-main-color': resolvedMainColor,
  399. '--u-content-color': resolvedContentColor,
  400. '--u-tips-color': resolvedTipsColor,
  401. '--u-light-color': resolvedLightColor,
  402. '--u-border-color': resolvedBorderColor,
  403. '--u-bg-color': resolvedBgColor,
  404. '--u-hover-bg-color': hoverBgColor,
  405. '--u-disabled-color': resolvedDisabledColor,
  406. '--u-primary': resolvedPrimary,
  407. '--u-primary-dark': resolvedPrimaryDark,
  408. '--u-primary-disabled': resolvedPrimaryDisabled,
  409. '--u-primary-light': resolvedPrimaryLight,
  410. '--u-warning': resolvedWarning,
  411. '--u-warning-dark': resolvedWarningDark,
  412. '--u-warning-disabled': resolvedWarningDisabled,
  413. '--u-warning-light': resolvedWarningLight,
  414. '--u-success': resolvedSuccess,
  415. '--u-success-dark': resolvedSuccessDark,
  416. '--u-success-disabled': resolvedSuccessDisabled,
  417. '--u-success-light': resolvedSuccessLight,
  418. '--u-error': resolvedError,
  419. '--u-error-dark': resolvedErrorDark,
  420. '--u-error-disabled': resolvedErrorDisabled,
  421. '--u-error-light': resolvedErrorLight,
  422. '--u-info': resolvedInfo,
  423. '--u-info-dark': resolvedInfoDark,
  424. '--u-info-disabled': resolvedInfoDisabled,
  425. '--u-info-light': resolvedInfoLight,
  426. '--up-main-color': resolvedMainColor,
  427. '--up-content-color': resolvedContentColor,
  428. '--up-tips-color': resolvedTipsColor,
  429. '--up-light-color': resolvedLightColor,
  430. '--up-border-color': resolvedBorderColor,
  431. '--up-bg-color': resolvedBgColor,
  432. '--up-hover-bg-color': hoverBgColor,
  433. '--up-disabled-color': resolvedDisabledColor,
  434. '--up-primary': resolvedPrimary,
  435. '--up-primary-dark': resolvedPrimaryDark,
  436. '--up-primary-disabled': resolvedPrimaryDisabled,
  437. '--up-primary-light': resolvedPrimaryLight,
  438. '--up-warning': resolvedWarning,
  439. '--up-warning-dark': resolvedWarningDark,
  440. '--up-warning-disabled': resolvedWarningDisabled,
  441. '--up-warning-light': resolvedWarningLight,
  442. '--up-success': resolvedSuccess,
  443. '--up-success-dark': resolvedSuccessDark,
  444. '--up-success-disabled': resolvedSuccessDisabled,
  445. '--up-success-light': resolvedSuccessLight,
  446. '--up-error': resolvedError,
  447. '--up-error-dark': resolvedErrorDark,
  448. '--up-error-disabled': resolvedErrorDisabled,
  449. '--up-error-light': resolvedErrorLight,
  450. '--up-info': resolvedInfo,
  451. '--up-info-dark': resolvedInfoDark,
  452. '--up-info-disabled': resolvedInfoDisabled,
  453. '--up-info-light': resolvedInfoLight,
  454. '--up-page-bg-color': pageBgColor,
  455. '--up-card-bg-color': isDark ? '#1c1c1e' : '#ffffff',
  456. '--up-navbar-bg-color': navbarBgColor
  457. }
  458. const extraVars = {}
  459. Object.keys(runtimeColorMap).forEach((key) => {
  460. if (typeof key !== 'string') return
  461. const isThemeToken = key.indexOf('up-') === 0 || key.indexOf('u-') === 0
  462. if (!isThemeToken) return
  463. const cssVarName = `--${key}`
  464. if (Object.prototype.hasOwnProperty.call(coreVars, cssVarName)) return
  465. const value = runtimeColorMap[key]
  466. if (typeof value === 'string' && value) {
  467. extraVars[cssVarName] = value
  468. }
  469. })
  470. return {
  471. ...coreVars,
  472. ...defaultExtraVars,
  473. ...buildAliasCssVars(defaultExtraVars),
  474. ...extraVars,
  475. ...buildAliasCssVars(extraVars)
  476. }
  477. }
  478. export function getThemeVars(mode) {
  479. if (mode) {
  480. return buildThemeCssVars(getThemeColorsByMode(mode), mode)
  481. }
  482. if (themeState.vars && Object.keys(themeState.vars).length > 0) {
  483. return { ...themeState.vars }
  484. }
  485. return buildThemeCssVars(getThemeColorsByMode(themeState.mode), themeState.mode)
  486. }
  487. function syncThemeToH5(mode) {
  488. // #ifdef H5
  489. if (typeof document !== 'undefined' && document.documentElement) {
  490. document.documentElement.setAttribute('data-up-theme', mode)
  491. }
  492. // #endif
  493. }
  494. function hasActiveRuntimePage() {
  495. try {
  496. if (typeof getCurrentPages === 'function') {
  497. const pages = getCurrentPages()
  498. return Array.isArray(pages) && pages.length > 0
  499. }
  500. } catch (e) {}
  501. return false
  502. }
  503. function trySetNavigationBarColor(options) {
  504. if (typeof uni === 'undefined' || typeof uni.setNavigationBarColor !== 'function') return
  505. if (!hasActiveRuntimePage()) return
  506. try {
  507. const result = uni.setNavigationBarColor(options)
  508. if (result && typeof result.catch === 'function') {
  509. result.catch(() => {})
  510. }
  511. } catch (e) {}
  512. }
  513. function applyNativeThemeUI(mode, themeColors, themeVars = {}) {
  514. if (typeof uni === 'undefined') return
  515. if (config.nativeThemeSync !== true) return
  516. const isDark = normalizeThemeMode(mode) === 'dark'
  517. const pageBg = themeColors?.bgColor || (isDark ? '#1f1f1f' : '#f3f4f6')
  518. const navBg = themeVars?.['--up-navbar-bg-color']
  519. || themeVars?.['--u-navbar-bg-color']
  520. || config.color?.['up-navbar-bg-color']
  521. || config.color?.['u-navbar-bg-color']
  522. || (isDark ? '#1c1c1e' : '#ffffff')
  523. trySetNavigationBarColor({
  524. frontColor: isDark ? '#ffffff' : '#000000',
  525. backgroundColor: navBg,
  526. animation: {
  527. duration: 0,
  528. timingFunc: 'linear'
  529. }
  530. })
  531. if (typeof uni.setBackgroundColor === 'function') {
  532. uni.setBackgroundColor({
  533. backgroundColor: pageBg,
  534. backgroundColorTop: pageBg,
  535. backgroundColorBottom: pageBg
  536. })
  537. }
  538. trySetTabBarStyle({
  539. color: isDark ? '#8e8e93' : '#909399',
  540. selectedColor: isDark ? '#f2f2f7' : '#303133',
  541. backgroundColor: isDark ? '#111111' : '#ffffff',
  542. borderStyle: isDark ? 'white' : 'black'
  543. })
  544. }
  545. function applyTheme(mode = 'light') {
  546. const themeMode = normalizeThemeMode(mode)
  547. const themeColors = getThemeColorsByMode(themeMode)
  548. const themeVars = buildThemeCssVars(themeColors, themeMode)
  549. index.shallowMerge(color, {
  550. primary: themeColors.primary,
  551. primaryDark: themeColors.primaryDark,
  552. primaryDisabled: themeColors.primaryDisabled,
  553. primaryLight: themeColors.primaryLight,
  554. info: themeColors.info,
  555. infoDark: themeColors.infoDark,
  556. infoDisabled: themeColors.infoDisabled,
  557. infoLight: themeColors.infoLight,
  558. default: themeColors.info,
  559. warning: themeColors.warning,
  560. warningDark: themeColors.warningDark,
  561. warningDisabled: themeColors.warningDisabled,
  562. warningLight: themeColors.warningLight,
  563. error: themeColors.error,
  564. errorDark: themeColors.errorDark,
  565. errorDisabled: themeColors.errorDisabled,
  566. errorLight: themeColors.errorLight,
  567. success: themeColors.success,
  568. successDark: themeColors.successDark,
  569. successDisabled: themeColors.successDisabled,
  570. successLight: themeColors.successLight,
  571. mainColor: themeColors.mainColor,
  572. contentColor: themeColors.contentColor,
  573. tipsColor: themeColors.tipsColor,
  574. lightColor: themeColors.lightColor,
  575. borderColor: themeColors.borderColor,
  576. bgColor: themeColors.bgColor,
  577. disabledColor: themeColors.disabledColor
  578. })
  579. index.shallowMerge(config.color, buildConfigColorMap(themeColors))
  580. config.themeMode = themeMode
  581. themeState.preference = currentThemePreference
  582. themeState.mode = themeMode
  583. themeState.vars = { ...themeVars }
  584. themeState.version = Number(themeState.version || 0) + 1
  585. syncThemeToH5(themeMode)
  586. applyNativeThemeUI(themeMode, themeColors, themeVars)
  587. if (typeof uni !== 'undefined' && uni.$u && uni.$u.theme) {
  588. uni.$u.theme.mode = themeState.mode
  589. if (Object.prototype.hasOwnProperty.call(uni.$u.theme, 'colors')) {
  590. delete uni.$u.theme.colors
  591. }
  592. uni.$u.theme.vars = { ...themeState.vars }
  593. uni.$u.theme.version = themeState.version
  594. }
  595. if (typeof uni !== 'undefined' && typeof uni.$emit === 'function') {
  596. uni.$emit('uThemeChange', {
  597. mode: themeState.mode,
  598. colors: { ...themeColors },
  599. version: themeState.version,
  600. vars: { ...themeState.vars }
  601. })
  602. }
  603. return themeState
  604. }
  605. export function setTheme(mode = 'light') {
  606. currentThemePreference = normalizeThemeMode(mode)
  607. writeThemePreferenceToStorage(currentThemePreference)
  608. return applyTheme(currentThemePreference)
  609. }
  610. export function setThemePreference(mode = THEME_MODE_SYSTEM) {
  611. currentThemePreference = normalizeThemePreference(mode)
  612. writeThemePreferenceToStorage(currentThemePreference)
  613. if (currentThemePreference === THEME_MODE_SYSTEM) {
  614. return applyTheme(getSystemTheme())
  615. }
  616. return applyTheme(currentThemePreference)
  617. }
  618. export function getThemePreference() {
  619. return currentThemePreference
  620. }
  621. export function refreshThemeFromConfig() {
  622. cachedLightThemeColors = getCurrentLightThemeColors()
  623. if (themeState.version > 0) {
  624. applyTheme(themeState.mode)
  625. }
  626. }
  627. export function initThemeSystem() {
  628. if (typeof uni === 'undefined') return
  629. if (!cachedLightThemeColors) {
  630. cachedLightThemeColors = getCurrentLightThemeColors()
  631. }
  632. currentThemePreference = readThemePreferenceFromStorage()
  633. if (currentThemePreference === THEME_MODE_SYSTEM) {
  634. applyTheme(getSystemTheme())
  635. } else {
  636. applyTheme(currentThemePreference)
  637. }
  638. if (!hasRegisterThemeListener && typeof uni.onThemeChange === 'function') {
  639. uni.onThemeChange((res = {}) => {
  640. if (currentThemePreference === THEME_MODE_SYSTEM) {
  641. applyTheme(res.theme)
  642. }
  643. })
  644. hasRegisterThemeListener = true
  645. }
  646. }