2 changed files with 169 additions and 0 deletions
@ -0,0 +1,105 @@ |
|||||
|
<script setup> |
||||
|
defineProps({ |
||||
|
type: { type: String, default: 'primary' }, |
||||
|
size: { type: String, default: 'medium' }, |
||||
|
disabled: { type: Boolean, default: false }, |
||||
|
block: { type: Boolean, default: false }, |
||||
|
loading: { type: Boolean, default: false } |
||||
|
}) |
||||
|
defineOptions({ name: 'SongButton' }) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<button |
||||
|
class="song-btn" |
||||
|
:class="[`song-btn--${type}`, `song-btn--${size}`, { 'song-btn--block': block }]" |
||||
|
:disabled="disabled || loading" |
||||
|
:aria-busy="loading" |
||||
|
> |
||||
|
<i v-if="loading" class="song-btn__ink" aria-hidden="true" /> |
||||
|
<slot /> |
||||
|
</button> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.song-btn { |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
gap: 6px; |
||||
|
font-family: var(--song-font-sans); |
||||
|
border: 1px solid transparent; |
||||
|
border-radius: var(--song-radius-m); |
||||
|
cursor: pointer; |
||||
|
transition: background-color var(--song-dur-1) var(--song-ease), |
||||
|
color var(--song-dur-1) var(--song-ease), |
||||
|
border-color var(--song-dur-1) var(--song-ease); |
||||
|
user-select: none; |
||||
|
} |
||||
|
.song-btn--primary { |
||||
|
background: var(--song-btn-bg); |
||||
|
color: var(--song-btn-text); |
||||
|
} |
||||
|
.song-btn--primary:hover:not(:disabled) { |
||||
|
filter: brightness(1.06); |
||||
|
} |
||||
|
.song-btn--secondary { |
||||
|
background: transparent; |
||||
|
color: var(--song-primary); |
||||
|
border-color: var(--song-border-strong); |
||||
|
} |
||||
|
.song-btn--secondary:hover:not(:disabled) { |
||||
|
background: var(--song-primary-bg); |
||||
|
} |
||||
|
.song-btn--text { |
||||
|
background: transparent; |
||||
|
color: var(--song-primary); |
||||
|
} |
||||
|
.song-btn--text:hover:not(:disabled) { |
||||
|
background: var(--song-primary-bg); |
||||
|
} |
||||
|
.song-btn--danger { |
||||
|
background: transparent; |
||||
|
color: var(--song-danger); |
||||
|
border-color: var(--song-border-strong); |
||||
|
} |
||||
|
.song-btn--danger:hover:not(:disabled) { |
||||
|
background: var(--song-danger-bg); |
||||
|
} |
||||
|
.song-btn--small { |
||||
|
height: 28px; |
||||
|
padding: 0 12px; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.song-btn--medium { |
||||
|
height: 34px; |
||||
|
padding: 0 18px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
.song-btn--large { |
||||
|
height: 42px; |
||||
|
padding: 0 26px; |
||||
|
font-size: 15px; |
||||
|
font-family: var(--song-font-serif); |
||||
|
letter-spacing: 0.06em; |
||||
|
} |
||||
|
.song-btn--block { |
||||
|
width: 100%; |
||||
|
} |
||||
|
.song-btn:disabled { |
||||
|
opacity: 0.45; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
.song-btn__ink { |
||||
|
width: 9px; |
||||
|
height: 9px; |
||||
|
border-radius: 50%; |
||||
|
background: currentColor; |
||||
|
opacity: 0.75; |
||||
|
animation: song-btn-ink-pulse 1.6s var(--song-ease) infinite; |
||||
|
} |
||||
|
@keyframes song-btn-ink-pulse { |
||||
|
0%, 100% { transform: scale(0.55); opacity: 0.35; } |
||||
|
50% { transform: scale(0.95); opacity: 0.85; } |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,64 @@ |
|||||
|
<script setup> |
||||
|
defineProps({ |
||||
|
label: { type: String, default: '' }, |
||||
|
modelValue: { type: [String, Number], default: '' }, |
||||
|
placeholder: { type: String, default: '' }, |
||||
|
type: { type: String, default: 'text' }, |
||||
|
required: { type: Boolean, default: false } |
||||
|
}) |
||||
|
defineEmits(['update:modelValue']) |
||||
|
defineOptions({ name: 'SongInput' }) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<label class="song-field"> |
||||
|
<span v-if="label" class="song-field__label"> |
||||
|
{{ label }}<span v-if="required" class="song-field__req">*</span> |
||||
|
</span> |
||||
|
<input |
||||
|
class="song-field__input" |
||||
|
:type="type" |
||||
|
:value="modelValue" |
||||
|
:placeholder="placeholder" |
||||
|
@input="$emit('update:modelValue', $event.target.value)" |
||||
|
/> |
||||
|
</label> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.song-field { |
||||
|
display: block; |
||||
|
} |
||||
|
.song-field__label { |
||||
|
display: block; |
||||
|
margin-bottom: 6px; |
||||
|
font-size: 13px; |
||||
|
color: var(--song-text-secondary); |
||||
|
} |
||||
|
.song-field__req { |
||||
|
color: var(--song-danger); |
||||
|
margin-left: 2px; |
||||
|
} |
||||
|
.song-field__input { |
||||
|
width: 100%; |
||||
|
height: 34px; |
||||
|
padding: 0 12px; |
||||
|
font-family: var(--song-font-sans); |
||||
|
font-size: 14px; |
||||
|
color: var(--song-text); |
||||
|
background: var(--song-bg-elevated); |
||||
|
border: 1px solid var(--song-border); |
||||
|
border-radius: var(--song-radius-s); |
||||
|
transition: border-color var(--song-dur-1) var(--song-ease); |
||||
|
} |
||||
|
.song-field__input::placeholder { |
||||
|
color: var(--song-text-disabled); |
||||
|
} |
||||
|
.song-field__input:hover { |
||||
|
border-color: var(--song-border-strong); |
||||
|
} |
||||
|
.song-field__input:focus { |
||||
|
outline: none; |
||||
|
border-color: var(--song-primary); |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue