Browse Source

feat(component): add base primitives button and input

main
Mohan 1 month ago
parent
commit
e638d30166
  1. 105
      src/components/SongButton.vue
  2. 64
      src/components/SongInput.vue

105
src/components/SongButton.vue

@ -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>

64
src/components/SongInput.vue

@ -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>
Loading…
Cancel
Save