20260814173126
This commit is contained in:
1 parent
68de86aee9
commit
b6f9a4c912
17 files changed
+1210
-544
No files matched your search
@@ -1,26 +0,0 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
},
|
||||
})
|
||||
|
||||
const classes = computed(() => ['button', `button-${props.variant}`, `button-size-${props.size}`])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button :type="type" :class="classes">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
@@ -1,24 +1,133 @@
|
||||
<script setup>
|
||||
import { useTheme } from '../../composables/useTheme'
|
||||
import { useTheme } from '@/theme/useTheme'
|
||||
import { PRESETS } from '@/theme/color'
|
||||
|
||||
const { colorTheme, colorThemes, setColorTheme } = useTheme()
|
||||
const { themeColor, setThemeColor } = useTheme()
|
||||
|
||||
function handleColorInput(event) {
|
||||
setThemeColor(event.target.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="color-theme-picker" role="group" aria-label="主题色">
|
||||
<button
|
||||
v-for="item in colorThemes"
|
||||
:key="item.value"
|
||||
class="color-swatch"
|
||||
type="button"
|
||||
:class="{ 'color-swatch-active': colorTheme === item.value }"
|
||||
:style="{ '--swatch-color': item.color }"
|
||||
:aria-label="`${item.label}主题色`"
|
||||
:aria-pressed="colorTheme === item.value"
|
||||
:title="item.label"
|
||||
@click="setColorTheme(item.value)"
|
||||
>
|
||||
<span class="color-swatch-dot" aria-hidden="true" />
|
||||
</button>
|
||||
<div class="color-theme-picker">
|
||||
<span class="color-theme-label">主题色</span>
|
||||
<div class="theme-presets" aria-label="常用主题色">
|
||||
<button
|
||||
v-for="preset in PRESETS"
|
||||
:key="preset.value"
|
||||
class="theme-swatch"
|
||||
:class="{ 'is-selected': themeColor === preset.value }"
|
||||
type="button"
|
||||
:style="{ '--swatch-color': preset.value }"
|
||||
:aria-label="preset.name"
|
||||
:aria-pressed="themeColor === preset.value"
|
||||
:title="`${preset.name} ${preset.value}`"
|
||||
@click="setThemeColor(preset.value)"
|
||||
/>
|
||||
</div>
|
||||
<label class="custom-color" title="自定义主题色">
|
||||
<input
|
||||
class="color-theme-input"
|
||||
type="color"
|
||||
:value="themeColor"
|
||||
aria-label="自定义主题色"
|
||||
@input="handleColorInput"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-theme-picker {
|
||||
display: inline-flex;
|
||||
height: 2.5rem;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.25rem 0.375rem 0.25rem 0.75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--card);
|
||||
color: var(--card-foreground);
|
||||
}
|
||||
|
||||
.theme-presets {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.theme-swatch {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
border: 1px solid color-mix(in srgb, var(--foreground) 18%, transparent);
|
||||
border-radius: 50%;
|
||||
background: var(--swatch-color);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
transition:
|
||||
transform 120ms ease,
|
||||
box-shadow 120ms ease;
|
||||
}
|
||||
|
||||
.theme-swatch:hover {
|
||||
transform: scale(1.12);
|
||||
}
|
||||
|
||||
.theme-swatch.is-selected {
|
||||
box-shadow:
|
||||
0 0 0 2px var(--card),
|
||||
0 0 0 4px var(--ring);
|
||||
}
|
||||
|
||||
.theme-swatch:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 2px var(--card),
|
||||
0 0 0 4px var(--ring);
|
||||
}
|
||||
|
||||
.custom-color {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.color-theme-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.color-theme-input {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: calc(var(--radius) - 0.25rem);
|
||||
background: var(--background);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.color-theme-input::-webkit-color-swatch-wrapper {
|
||||
padding: 0.1875rem;
|
||||
}
|
||||
|
||||
.color-theme-input::-webkit-color-swatch {
|
||||
border: 0;
|
||||
border-radius: calc(var(--radius) - 0.375rem);
|
||||
}
|
||||
|
||||
.color-theme-input::-moz-color-swatch {
|
||||
border: 0;
|
||||
border-radius: calc(var(--radius) - 0.375rem);
|
||||
}
|
||||
|
||||
.color-theme-input:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 2px var(--background),
|
||||
0 0 0 4px color-mix(in srgb, var(--ring) 50%, transparent);
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import Button from './Button.vue'
|
||||
import { useTheme } from '../../composables/useTheme'
|
||||
import Button from './button/button.vue'
|
||||
import { useTheme } from '@/theme/useTheme'
|
||||
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
})
|
||||
|
||||
const classes = computed(() => ['button', `button-${props.type}`])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button :class="classes">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,102 @@
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
height: 2.5rem;
|
||||
padding: 0 1rem;
|
||||
font-size: 0.875rem;
|
||||
outline: none;
|
||||
transition:
|
||||
color 120ms ease,
|
||||
background-color 120ms ease,
|
||||
border-color 120ms ease,
|
||||
transform 80ms ease;
|
||||
}
|
||||
|
||||
.button:active:not(:disabled) {
|
||||
transform: scale(0.985);
|
||||
}
|
||||
|
||||
.button-default {
|
||||
color: var(--primary-foreground);
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.button-default:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.button-default:active {
|
||||
background: var(--primary-active);
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.button-secondary:hover,
|
||||
.button-ghost:hover {
|
||||
background: var(--accent);
|
||||
color: var(--accent-foreground);
|
||||
}
|
||||
|
||||
.button-secondary:active,
|
||||
.button-ghost:active {
|
||||
background: color-mix(in srgb, var(--accent) 88%, var(--foreground));
|
||||
color: var(--accent-foreground);
|
||||
}
|
||||
|
||||
.button-outline {
|
||||
color: var(--foreground);
|
||||
border-color: var(--input);
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.button-outline:hover {
|
||||
color: var(--accent-foreground);
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.button-outline:active {
|
||||
color: var(--accent-foreground);
|
||||
background: color-mix(in srgb, var(--accent) 88%, var(--foreground));
|
||||
}
|
||||
|
||||
.button-ghost {
|
||||
color: var(--foreground);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.button-destructive {
|
||||
color: var(--destructive-foreground);
|
||||
background: var(--destructive);
|
||||
}
|
||||
|
||||
.button-destructive:hover {
|
||||
background: color-mix(in srgb, var(--destructive) 90%, var(--background));
|
||||
}
|
||||
|
||||
.button-destructive:active {
|
||||
background: color-mix(in srgb, var(--destructive) 80%, var(--background));
|
||||
}
|
||||
|
||||
.button-link {
|
||||
color: var(--primary);
|
||||
background: transparent;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
.button-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.button-link:active {
|
||||
color: var(--primary-active);
|
||||
}
|
||||
Reference in new issue
Block a user