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),