diff --git a/packages/ui/src/components/button/Button.vue b/packages/ui/src/components/button/Button.vue index 68c304697..dc55b3a0e 100644 --- a/packages/ui/src/components/button/Button.vue +++ b/packages/ui/src/components/button/Button.vue @@ -1,37 +1,37 @@ diff --git a/packages/ui/src/components/button/meta.ts b/packages/ui/src/components/button/meta.ts index 364dc3367..aaff64d43 100644 --- a/packages/ui/src/components/button/meta.ts +++ b/packages/ui/src/components/button/meta.ts @@ -62,6 +62,20 @@ export const buttonProps = { color: { type: String, }, + + /** + * Specifies the href of the button + */ + href: { + type: String, + }, + + /** + * Specifies the target of the button + */ + target: { + type: String, + }, } as const export type ButtonProps = ExtractPublicPropTypes diff --git a/packages/ui/src/components/button/style/index.css b/packages/ui/src/components/button/style/index.css index 6366bb47a..749fe6199 100644 --- a/packages/ui/src/components/button/style/index.css +++ b/packages/ui/src/components/button/style/index.css @@ -2,40 +2,55 @@ .ant-btn { @apply relative; - @apply inline-flex shrink-0 cursor-pointer items-center justify-center gap-1 whitespace-nowrap; + @apply inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap; @apply border-1 text-sm; @apply box-border rounded-md px-4 transition-all duration-200 select-none; - &:where(.ant-btn-disabled) { - @apply cursor-not-allowed; - } + &:where(.ant-btn-loading) { - @apply cursor-default opacity-50; + @apply cursor-default opacity-65; } - &:where(.ant-btn-solid) { - @apply border-none bg-[var(--bg-color)] text-[var(--bg-color-content)]; - @apply not-disabled:hover:bg-[var(--bg-color-hover)] not-disabled:active:bg-[var(--bg-color-active)]; + &:where(.ant-btn-solid:not(:disabled)) { + @apply border-none bg-[var(--accent-color)] text-[var(--accent-color-content)]; + @apply hover:bg-[var(--accent-color-hover)] active:bg-[var(--accent-color-active)]; } - &:where(.ant-btn-outlined), - &:where(.ant-btn-dashed) { - @apply border-[var(--border-color-tint-30)] bg-transparent text-[var(--text-color)]; - @apply not-disabled:hover:border-[var(--border-color-hover)] not-disabled:hover:text-[var(--text-color-hover)] not-disabled:active:border-[var(--border-color-active)] not-disabled:active:text-[var(--text-color-active)]; - @apply disabled:border-[var(--border-color-tint-80)]; + &:where(.ant-btn-outlined:not(:disabled)), + &:where(.ant-btn-dashed:not(:disabled)) { + @apply border-[var(--accent-color)] bg-transparent text-[var(--accent-color)]; + @apply hover:text-[var(--accent-color-hover)] active:border-[var(--accent-color-active)] active:text-[var(--accent-color-active)]; + @apply border-[var(--accent-color-active)] hover:border-[var(--accent-color-hover)]; } - &:where(.ant-btn-text) { - @apply border-none bg-transparent text-[var(--text-color)]; - @apply not-disabled:hover:bg-[var(--bg-color-tint-90)] not-disabled:hover:text-[var(--text-color-hover)]; + &:where(.ant-btn-text:not(.ant-btn-custom-color):not(:disabled)) { + @apply border-none bg-transparent text-[var(--neutral-color)]; + @apply hover:bg-[var(--neutral-disabled-bg)]; + } + &:where(.ant-btn-text.ant-btn-custom-color:not(:disabled)) { + @apply border-none bg-transparent text-[var(--accent-color)]; + @apply hover:bg-[var(--accent-color-1)] hover:text-[var(--accent-color-hover)]; } - &:where(.ant-btn-link) { - @apply border-none bg-transparent text-[var(--text-color)] not-disabled:hover:text-[var(--text-color-hover)]; + &:where(.ant-btn-link:not(:disabled)) { + @apply border-none bg-transparent text-[var(--accent-color)] hover:text-[var(--accent-color-hover)]; } &:where(.ant-btn-dashed) { @apply border-dashed; } - &:where(.ant-btn-filled) { - @apply border-none bg-[var(--bg-color-tint-90)] text-[var(--text-color)] not-disabled:hover:text-[var(--text-color-hover)]; - @apply not-disabled:hover:bg-[var(--bg-color-tint-80)] not-disabled:active:bg-[var(--bg-color-tint-80)]; + &:where(.ant-btn-filled:not(:disabled)) { + @apply border-none bg-[var(--accent-color-1)] text-[var(--accent-color)] hover:text-[var(--accent-color-hover)]; + @apply hover:bg-[var(--accent-color-2)] active:bg-[var(--accent-color-3)] active:text-[var(--accent-color-active)]; + } + + &:where(.ant-btn-disabled) { + @apply cursor-not-allowed; + @apply border-[var(--neutral-border)] bg-[var(--neutral-disabled-bg)] text-[var(--neutral-disabled)]; + } + &:where(.ant-btn-disabled.ant-btn-text), + &:where(.ant-btn-disabled.ant-btn-link) { + @apply border-none bg-transparent; + } + + &:where(.ant-btn-disabled.ant-btn-filled) { + @apply border-none; } &:where(.ant-btn-sm) { diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 3c47b0b4d..9b6ce5f04 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,2 +1,3 @@ export { default as Button } from './button' export { default as Input } from './input' +export { default as Theme } from './theme' diff --git a/packages/ui/src/components/theme/Theme.vue b/packages/ui/src/components/theme/Theme.vue new file mode 100644 index 000000000..16219aeb1 --- /dev/null +++ b/packages/ui/src/components/theme/Theme.vue @@ -0,0 +1,12 @@ + + + diff --git a/packages/ui/src/components/theme/hook.ts b/packages/ui/src/components/theme/hook.ts new file mode 100644 index 000000000..040cdaaae --- /dev/null +++ b/packages/ui/src/components/theme/hook.ts @@ -0,0 +1,21 @@ +import { inject, InjectionKey, provide, Reactive } from 'vue' + +type ThemeType = Reactive<{ + appearance: 'light' | 'dark' + primaryColor: string + dangerColor: string +}> + +const ThemeSymbol: InjectionKey = Symbol('theme') + +export const useThemeInject = () => { + return inject(ThemeSymbol, { + appearance: 'light', + primaryColor: '#1677ff', + dangerColor: '#ff4d4f', + }) +} + +export const useThemeProvide = (theme: ThemeType) => { + provide(ThemeSymbol, theme) +} diff --git a/packages/ui/src/components/theme/index.ts b/packages/ui/src/components/theme/index.ts new file mode 100644 index 000000000..c077563fd --- /dev/null +++ b/packages/ui/src/components/theme/index.ts @@ -0,0 +1,14 @@ +export * from './hook' + +import { App, Plugin } from 'vue' +import Theme from './Theme.vue' + +export { Theme } + +/* istanbul ignore next */ +Theme.install = function (app: App) { + app.component('ATheme', Theme) + return app +} + +export default Theme as typeof Theme & Plugin diff --git a/packages/ui/src/components/theme/meta.ts b/packages/ui/src/components/theme/meta.ts new file mode 100644 index 000000000..8ae12f91f --- /dev/null +++ b/packages/ui/src/components/theme/meta.ts @@ -0,0 +1,31 @@ +import { PropType, ExtractPublicPropTypes } from 'vue' + +// Theme Props +export const themeProps = { + /** + * Specifies the theme of the component + * @default 'light' + */ + appearance: { + type: String as PropType<'light' | 'dark'>, + default: 'light', + }, + /** + * Specifies the primary color of the component + * @default '#1677FF' + */ + primaryColor: { + type: String, + default: '#1677FF', + }, + /** + * Specifies the danger color of the component + * @default '#ff4d4f' + */ + dangerColor: { + type: String, + default: '#ff4d4f', + }, +} as const + +export type ThemeProps = ExtractPublicPropTypes diff --git a/packages/ui/src/style/base.css b/packages/ui/src/style/base.css index 0a2c86ee7..510d18aec 100644 --- a/packages/ui/src/style/base.css +++ b/packages/ui/src/style/base.css @@ -19,4 +19,11 @@ --color-warning-content: #ffffff; --color-error: #ff3333; --color-error-content: #ffffff; + + --neutral-color: #000000e0; + --neutral-secondary: #000000a6; + --neutral-disabled: #00000040; + --neutral-border: #d9d9d9; + --neutral-separator: #0505050f; + --neutral-bg: #f5f5f5; } diff --git a/packages/ui/src/utils/colorAlgorithm.ts b/packages/ui/src/utils/colorAlgorithm.ts index 4c19db8a9..08eb72480 100644 --- a/packages/ui/src/utils/colorAlgorithm.ts +++ b/packages/ui/src/utils/colorAlgorithm.ts @@ -1,4 +1,5 @@ import { TinyColor } from '@ctrl/tinycolor' +import { generate, presetPalettes, presetDarkPalettes } from '@ant-design/colors' export const getAlphaColor = (baseColor: string, alpha: number) => new TinyColor(baseColor).setAlpha(alpha).toRgbString() @@ -16,12 +17,65 @@ export const getShadeColor = (baseColor: string, shadeNumber: number) => { return new TinyColor(baseColor).shade(shadeNumber).toString() } -export const getCssVarColor = (baseColor: string) => { +export const getLightNeutralColor = () => { return { + '--neutral-color': '#000000e0', + '--neutral-secondary': '#000000a6', + '--neutral-disabled': '#00000040', + '--neutral-disabled-bg': '#0000000a', + '--neutral-border': '#d9d9d9', + '--neutral-separator': '#0505050f', + '--neutral-bg': '#f5f5f5', + } +} + +export const getDarkNeutralColor = () => { + return { + '--neutral-color': '#FFFFFFD9', + '--neutral-secondary': '#FFFFFFA6', + '--neutral-disabled': '#FFFFFF40', + '--neutral-disabled-bg': 'rgba(255, 255, 255, 0.08)', + '--neutral-border': '#424242', + '--neutral-separator': '#FDFDFD1F', + '--neutral-bg': '#000000', + } +} + +export const getCssVarColor = ( + baseColor: string, + opts?: { appearance: 'light' | 'dark'; backgroundColor: string }, +) => { + const { appearance = 'light', backgroundColor = '#141414' } = opts || {} + const color = new TinyColor(baseColor) + const preset = appearance === 'dark' ? presetDarkPalettes : presetPalettes + const colors = + preset[baseColor] || + generate( + color.toHexString(), + appearance === 'dark' ? { theme: appearance, backgroundColor } : undefined, + ) + const accentColor = colors[5] + return { + '--accent-color-1': colors[0], + '--accent-color-2': colors[1], + '--accent-color-3': colors[2], + '--accent-color-4': colors[3], + '--accent-color-5': colors[4], + '--accent-color-6': colors[5], + '--accent-color-7': colors[6], + '--accent-color-8': colors[7], + '--accent-color-9': colors[8], + '--accent-color-10': colors[9], + '--accent-color': accentColor, + '--accent-color-hover': colors[4], + '--accent-color-active': colors[5], + '--accent-color-content': '#ffffff', + ...(appearance === 'dark' ? getDarkNeutralColor() : getLightNeutralColor()), '--bg-color': baseColor, '--bg-color-hover': getTintColor(baseColor, 10), '--bg-color-active': getTintColor(baseColor, 20), '--bg-color-content': '#ffffff', + '--border-color': baseColor, '--border-color-hover': getTintColor(baseColor, 10), '--border-color-active': getTintColor(baseColor, 20),