Skip to content

Commit

Permalink
新增useTheme
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongjiaojiao committed Feb 19, 2025
1 parent 8a06136 commit 8f3f4fc
Show file tree
Hide file tree
Showing 23 changed files with 670 additions and 61 deletions.
3 changes: 2 additions & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
"@turf/helpers": "^6.1.4",
"@turf/transform-scale": "^7.2.0",
"colorcolor": "^1.1.1",
"css-vars-ponyfill": "^2.2.1",
"echarts": "^4.8.0",
"lodash.clonedeep": "^4.5.0",
"lodash.difference": "^4.5.0",
"lodash.max": "^4.0.1",
"lodash.merge": "^4.6.1",
"lodash.mergewith": "^4.6.2",
"lodash.tonumber": "^4.0.3",
"lodash.orderby": "^4.6.0",
"lodash.tonumber": "^4.0.3",
"omit.js": "^2.0.2",
"proj4": "^2.15.0",
"simple-statistics": "^7.8.7",
Expand Down
2 changes: 2 additions & 0 deletions core/utils/global-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Events } from 'vue-iclient-core/types/event/Events';
export class GlobalEvent extends Events {
_theme: any = themeFactory[1];
eventTypes: string[];
on: (data: Record<string, (...rest: any) => void>) => void;
un: (data: Record<string, (...rest: any) => void>) => void;

constructor() {
super();
Expand Down
13 changes: 13 additions & 0 deletions core/utils/style/color/__tests__/colorPalette.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import colorPalette from '../colorPalette';

describe('colorPalette test', () => {
beforeAll(() => {});

it('colorPalette', done => {
const res = colorPalette('#fff', 2);
expect(res).toBe('#fff2f0');
done();
});

afterAll(() => {});
});
63 changes: 63 additions & 0 deletions core/utils/style/color/colorPalette.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import tinycolor from 'tinycolor2';

const hueStep = 2;
const saturationStep = 16;
const saturationStep2 = 5;
const brightnessStep1 = 5;
const brightnessStep2 = 15;
const lightColorCount = 5;
const darkColorCount = 4;

function getHue(hsv: any, i: number, isLight: boolean): number {
let hue;
if (hsv.h >= 60 && hsv.h <= 240) {
hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
} else {
hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
}
if (hue < 0) {
hue += 360;
} else if (hue >= 360) {
hue -= 360;
}
return Math.round(hue);
}

function getSaturation(hsv: any, i: number, isLight: boolean): number {
let saturation: number;
if (isLight) {
saturation = Math.round(hsv.s * 100) - saturationStep * i;
} else if (i === darkColorCount) {
saturation = Math.round(hsv.s * 100) + saturationStep;
} else {
saturation = Math.round(hsv.s * 100) + saturationStep2 * i;
}
if (saturation > 100) {
saturation = 100;
}
if (isLight && i === lightColorCount && saturation > 10) {
saturation = 10;
}
if (saturation < 6) {
saturation = 6;
}
return Math.round(saturation);
}

function getValue(hsv: any, i: number, isLight: boolean): number {
if (isLight) {
return Math.round(hsv.v * 100) + brightnessStep1 * i;
}
return Math.round(hsv.v * 100) - brightnessStep2 * i;
}

export default function colorPalette(color: string, index: number): string {
const isLight = index <= 6;
const hsv = tinycolor(color).toHsv();
const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
return tinycolor({
h: getHue(hsv, i, isLight),
s: getSaturation(hsv, i, isLight),
v: getValue(hsv, i, isLight)
}).toHexString(false);
}
207 changes: 207 additions & 0 deletions core/utils/style/color/serialColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import colorPalette from 'vue-iclient-core/utils/style/color/colorPalette';
import themeFactory from 'vue-iclient-core/utils/style/theme/theme.json';
import { getColorWithOpacity, getDarkenColor, getDataType } from 'vue-iclient-core/utils/util';
import cssVars from 'css-vars-ponyfill';

export interface styleConfigParams {
styleConfig: {
id?: string;
className?: string;
include?: string;
};
}

const lightTheme = themeFactory[1];
export type ThemeStyleParams = typeof lightTheme & styleConfigParams;

export interface FunctionColorParams {
successColor?: string | string[];
infoColor?: string | string[];
warningColor?: string | string[];
dangerColor?: string | string[];
}

export interface ExtraColorParams {
[prop: string]: string;
}
export interface ColorGroupExtraColorParams {
colorGroup: string[];
[prop: string]: any;
}

export interface StyleReplacerParams {
themeStyle: ThemeStyleParams;
primarySerialColors?: string[];
functionSerialColors?: FunctionColorParams;
extraSerialColors: ExtraColorParams;
}

const $blue6 = '#1890ff';
const $green6 = '#52c41a';
const $glod6 = '#faad14';
const $red6 = '#f5222d';

const antdPrimaryColor = $blue6;
const antdFunctionColors: FunctionColorParams = {
infoColor: $blue6,
successColor: $green6,
warningColor: $glod6,
dangerColor: $red6
};

const isBrowser = typeof window !== 'undefined';
const isNativeSupport = isBrowser && window.CSS && window.CSS.supports && window.CSS.supports('(--a: 0)');

export function getPrimarySerialColors(nextThemeInfo?: ThemeStyleParams | ColorGroupExtraColorParams): string[] {
const series = [];
const nextThemeStyle = nextThemeInfo;
let prevPrimaryColor: string;
if (nextThemeStyle && nextThemeStyle.colorGroup && nextThemeStyle.colorGroup[0]) {
prevPrimaryColor = nextThemeStyle.colorGroup[0];
}
const acceptColor: string = prevPrimaryColor || antdPrimaryColor;
for (let index = 1; index <= 10; index++) {
let nextColor: string;
switch (index) {
case 2:
nextColor = nextThemeStyle.selectedColor || getColorWithOpacity(acceptColor, 0.15);
break;
case 5:
nextColor = nextThemeStyle.hoverColor || colorPalette(acceptColor, index);
break;
case 6:
nextColor = acceptColor;
break;
case 7:
nextColor = nextThemeStyle.clickColor || colorPalette(acceptColor, index);
break;
default:
nextColor = colorPalette(acceptColor, index);
break;
}
series.push(nextColor);
}
return series;
}

export function getFunctionSerialColors(functionColors?: ThemeStyleParams): FunctionColorParams {
const seriesIndex = [1, 2, 3, 4, 5, 6, 7];
const acceptFunctionColors = functionColors || antdFunctionColors;
const nextFunctionSerialColors: FunctionColorParams = {};
for (const key in acceptFunctionColors) {
if (Object.prototype.hasOwnProperty.call(antdFunctionColors, key)) {
const color = acceptFunctionColors[key] || antdFunctionColors[key];
nextFunctionSerialColors[key] = [];
seriesIndex.forEach(item => {
const nextColor = item === 6 ? color : colorPalette(color, item);
nextFunctionSerialColors[key].push(nextColor);
});
}
}
return nextFunctionSerialColors;
}

export function getExtralColors(
themeStyleData: ThemeStyleParams,
primarySerialColors: string[],
functionColors: FunctionColorParams
): ExtraColorParams {
const tableHeaderSortActiveBg = getDarkenColor(themeStyleData.backgroundLight, 3);
const extraSerialColors: ExtraColorParams = {
textColorWithoutOpacity: getColorWithOpacity(themeStyleData.textColor, 1, false),
backgroundWithoutOpacity: getColorWithOpacity(themeStyleData.background, 1, false),
componentBackgroundWithoutOpacity: getColorWithOpacity(themeStyleData.componentBackground, 1, false),
primaryShadowColor: getColorWithOpacity(primarySerialColors[4], 0.25),
dangerShadowColor: getColorWithOpacity(functionColors.dangerColor[4], 0.25),
disabledDarkenBgColor10: getDarkenColor(themeStyleData.containerDisabled, 10),
tableHeaderSortActiveBg,
tableHeaderFilterActiveBg: getDarkenColor(tableHeaderSortActiveBg, 5)
};
return extraSerialColors;
}

export function dealWithTheme(nextThemeStyle: ThemeStyleParams): StyleReplacerParams {
const defaultThemeStyle = nextThemeStyle.style || 'light';
const defaultTheme = themeFactory.find((item: ThemeStyleParams) => item.label === defaultThemeStyle);
// 合并 lightTheme 是因为可能其他 theme 没有完整的参数,如 disableColor
const serialColorsReplacer = getPrimarySerialColors(
Object.assign({ colorGroup: defaultTheme && defaultTheme.colorGroup }, nextThemeStyle)
);
const themeStyleData = Object.assign({}, lightTheme, defaultTheme, nextThemeStyle);
const functionSerialColorsReplacer = getFunctionSerialColors(themeStyleData);
const nextThemeStyleData = Object.assign({}, themeStyleData, {
selectedColor: serialColorsReplacer[1],
hoverColor: serialColorsReplacer[4],
clickColor: serialColorsReplacer[6]
});
const nextThemeData = {
themeStyle: nextThemeStyleData,
primarySerialColors: serialColorsReplacer,
functionSerialColors: functionSerialColorsReplacer,
extraSerialColors: getExtralColors(themeStyleData, serialColorsReplacer, functionSerialColorsReplacer)
};
setRootStyle(nextThemeData);
return nextThemeData;
}

export function toStyleVariable(variable) {
return `--${variable.replace(/[A-Z]/g, '-$&').toLowerCase()}`;
}

export function getRootStyleSelector(themeStyle: ThemeStyleParams) {
return themeStyle.styleConfig?.className && isNativeSupport ? `.${themeStyle.styleConfig.className}` : ':root';
}

function setRootStyle(themeData: StyleReplacerParams): void {
const { themeStyle, primarySerialColors, functionSerialColors, extraSerialColors } = themeData;
const primaryColor = themeStyle.colorGroup[0];
const variables = {
'--antd-wave-shadow-color': primaryColor,
'--primary-color': primaryColor
};
const themeInfo = Object.assign({}, themeStyle, extraSerialColors);
const themeKeys = Object.keys(themeInfo);
primarySerialColors.forEach((color: string, index: number) => {
const varKey = `--primary-${index + 1}`;
variables[varKey] = color;
});
for (const key in functionSerialColors) {
functionSerialColors[key].forEach((color: string, index: number) => {
const varKey = `--${key.replace('Color', '')}-${index + 1}`;
variables[varKey] = color;
});
}
themeKeys.forEach((key: string) => {
if (!['[object Object]', '[object Array]'].includes(getDataType(themeInfo[key]))) {
const varKey = toStyleVariable(key);
variables[varKey] = themeInfo[key] || 'rgba(0, 0, 0, 0)';
}
});
const rootStyleSelector = getRootStyleSelector(themeStyle);
const antdStyleId = themeStyle.styleConfig?.id || 'sm-component-style';
const rootStyle = `${rootStyleSelector} ${JSON.stringify(variables, null, 2)
.replace(/(:.+),/g, '$1;')
.replace(/"/g, '')}`;
let antStyleTag = document.getElementById(antdStyleId);
if (!antStyleTag) {
antStyleTag = document.createElement('style');
antStyleTag.setAttribute('id', antdStyleId);
antStyleTag.setAttribute('type', 'text/css');
document.head.insertBefore(antStyleTag, document.head.firstChild);
}
const defaultInclude = 'style#sm-component-style, link[href*=vue-iclient-mapboxgl]';
const options = {
include: themeStyle.styleConfig?.include || defaultInclude,
silent: true,
onlyLegacy: true,
variables: {},
watch: false
};
if (!isNativeSupport) {
options.onlyLegacy = false;
options.watch = true;
options.variables = variables;
}
cssVars(options);
antStyleTag.innerHTML = rootStyle;
}
34 changes: 34 additions & 0 deletions core/utils/style/theme/set-theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import globalEvent from 'vue-iclient-core/utils/global-event';
import themeFactory from 'vue-iclient-core/utils/style/theme/theme.json';
import { dealWithTheme, ThemeStyleParams } from 'vue-iclient-core/utils/style/color/serialColors';
import { objectWithoutProperties } from 'vue-iclient-core/utils/util';

interface triggerParams {
triggerEvent: boolean;
ignoreElements: string[];
}

export const setTheme = (themeStyle: any = {}, triggerInfo?: triggerParams) => {
let acceptedThemeStyle = themeStyle;
if (typeof themeStyle === 'string') {
acceptedThemeStyle = themeFactory.find((item: ThemeStyleParams) => item.label === themeStyle) || themeFactory[1];
} else if (acceptedThemeStyle.componentBackground) {
if (!acceptedThemeStyle.collapseCardHeaderBg) {
acceptedThemeStyle.collapseCardHeaderBg = acceptedThemeStyle.componentBackground;
}
if (!acceptedThemeStyle.collapseCardBackground) {
acceptedThemeStyle.collapseCardBackground = acceptedThemeStyle.componentBackground;
}
}
const nextThemeData = dealWithTheme(acceptedThemeStyle);
const nextTheme = {
...nextThemeData.themeStyle
};
if (themeStyle && (typeof themeStyle === 'string' || 'componentBackground' in themeStyle)) {
nextTheme.background = nextTheme.componentBackground;
}
globalEvent.theme = nextTheme;
if (!triggerInfo || triggerInfo.triggerEvent === true) {
globalEvent.changeTheme(objectWithoutProperties(nextTheme, (triggerInfo || {}).ignoreElements || []));
}
};
1 change: 1 addition & 0 deletions vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@types/mapbox-gl": "^3.4.1",
"ant-design-vue": "^4.2.6",
"colorcolor": "^1.1.1",
"css-vars-ponyfill": "^2.2.1",
"echarts": "^4.8.0",
"lodash-es": "^4.17.21",
"lodash.clonedeep": "^4.5.0",
Expand Down
13 changes: 6 additions & 7 deletions vue3/packages/common/components/collapse-card/collapseCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
:style="[collapseCardHeaderBgStyle, headingTextColorStyle]"
@click="iconClicked"
>
<!-- <i :style="iconStyle" :class="{ [iconClass]: true, ['is-auto-rotate']: autoRotate }" /> -->
<SwitcherOutlined />
<i :style="iconStyle" :class="{ [iconClass]: true, ['is-auto-rotate']: autoRotate }" />
</div>
<transition name="sm-component-zoom-in" @after-leave="toggleTransition('leave')" @enter="toggleTransition('enter')">
<div
Expand Down Expand Up @@ -42,9 +41,8 @@
</template>

<script setup>
// import Theme from 'vue-iclient/src/common/_mixin/Theme';
import { ref, computed, watch, onMounted, onBeforeMount, nextTick } from 'vue';
import { SwitcherOutlined } from '@ant-design/icons-vue';
import { ref, computed, watch, onMounted, nextTick } from 'vue';
import { useTheme } from '@supermapgis/common/hooks'
const props = defineProps({
iconPosition: {
Expand All @@ -71,14 +69,15 @@ const props = defineProps({
}
});
const { getTextColorStyle, headingTextColorStyle, collapseCardHeaderBgStyle, collapseCardBackgroundStyle } = useTheme()
const isShow = ref(true);
const transform = ref(null);
// 计算属性
const getCardStyle = computed(() => {
const style = { background: 'transparent' };
// return !props.iconClass && !props.headerName ? style : collapseCardBackgroundStyle;
return !props.iconClass && !props.headerName ? style : {};
return !props.iconClass && !props.headerName ? style : collapseCardBackgroundStyle;
});
const iconStyle = computed(() => {
Expand Down
Loading

0 comments on commit 8f3f4fc

Please sign in to comment.