refactor(ui): implement search and password type input
This commit is contained in:
parent
709a7be25e
commit
f3da51bbe6
|
@ -1,35 +1,102 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { computed, ref, useAttrs } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
|
||||||
|
|
||||||
const { icon, placeholder } = defineProps<{ icon?: string, placeholder?:string }>()
|
import Button from "~/components/ui/Button.vue"
|
||||||
|
import Layout from "~/components/ui/Layout.vue"
|
||||||
|
|
||||||
|
const { icon, placeholder, ...restProps } = defineProps<{
|
||||||
|
icon?: string,
|
||||||
|
placeholder?:string,
|
||||||
|
password?:true,
|
||||||
|
search?:true,
|
||||||
|
numeric?:true,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isNumeric = restProps.numeric
|
||||||
|
|
||||||
|
const showPassword = ref(false)
|
||||||
|
onKeyboardShortcut('escape', () => showPassword.value = false)
|
||||||
|
|
||||||
|
// TODO: Accept fallback $attrs: `const fallthroughAttrs = useAttrs()`
|
||||||
|
|
||||||
|
const attributes = computed(() => ({
|
||||||
|
...(restProps.password && !showPassword.value? {type:'password'} : {}),
|
||||||
|
...(restProps.search? {type:'search'} : {}),
|
||||||
|
...(restProps.numeric? {type:'numeric'} : {}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const model = defineModel<string|number>()
|
const model = defineModel<string|number>()
|
||||||
|
|
||||||
const input = ref()
|
const input = ref()
|
||||||
|
|
||||||
|
|
||||||
// TODO(A11y): Add `inputmode="numeric" pattern="[0-9]*"` to input if model type is number:
|
// TODO(A11y): Add `inputmode="numeric" pattern="[0-9]*"` to input if model type is number:
|
||||||
// https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/
|
// https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<label
|
<Layout stack no-gap label
|
||||||
:class="{ 'has-icon': !!icon }"
|
:class="{ 'has-icon': !!icon }"
|
||||||
class="funkwhale input"
|
class="funkwhale input"
|
||||||
>
|
>
|
||||||
|
<span v-if="$slots['label']" class="label">
|
||||||
|
<slot name="label" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-bind="attributes"
|
||||||
|
v-model="model"
|
||||||
|
ref="input"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@click.stop
|
||||||
|
@blur="showPassword = false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Left side icon -->
|
||||||
|
|
||||||
<div v-if="icon" class="prefix">
|
<div v-if="icon" class="prefix">
|
||||||
<i :class="['bi', icon]" />
|
<i :class="['bi', icon]" />
|
||||||
</div>
|
</div>
|
||||||
<input
|
|
||||||
v-bind="$attrs"
|
<!-- Search -->
|
||||||
v-model="model"
|
<div v-if="restProps.search" class="prefix">
|
||||||
ref="input"
|
<i class="bi bi-search" />
|
||||||
:placeholder="placeholder"
|
|
||||||
@click.stop
|
|
||||||
/>
|
|
||||||
<div v-if="$slots['input-right']" class="input-right">
|
|
||||||
<slot name="input-right" />
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
|
||||||
|
<!-- Right side -->
|
||||||
|
|
||||||
|
<div v-if="$slots['input-right']" class="input-right">
|
||||||
|
<span>
|
||||||
|
<slot name="input-right" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Password -->
|
||||||
|
<button
|
||||||
|
v-if="restProps.password"
|
||||||
|
style="background:transparent; border:none; appearance:none;"
|
||||||
|
role="switch"
|
||||||
|
type="button"
|
||||||
|
@click="showPassword = !showPassword"
|
||||||
|
@blur="showPassword = false"
|
||||||
|
class="input-right show-password" title="toggle visibility"
|
||||||
|
>
|
||||||
|
<i class="bi bi-eye" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Search -->
|
||||||
|
<Button
|
||||||
|
solid primary
|
||||||
|
v-if="restProps.search"
|
||||||
|
class="input-right search"
|
||||||
|
>
|
||||||
|
{{ t('components.Sidebar.link.search') }}
|
||||||
|
</Button>
|
||||||
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
.funkwhale {
|
.funkwhale.input {
|
||||||
|
|
||||||
form label {
|
position: relative;
|
||||||
display: flex;
|
flex-grow: 1;
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--form-label-color);
|
|
||||||
font-size: .92857143em;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> input {
|
> input {
|
||||||
background-color: var(--fw-bg-color);
|
background-color: var(--fw-bg-color);
|
||||||
|
@ -60,37 +54,73 @@ form label {
|
||||||
--fw-bg-color: var(--fw-gray-800);
|
--fw-bg-color: var(--fw-gray-800);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-icon {
|
|
||||||
input {
|
|
||||||
padding-left: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefix,
|
&.has-icon > input {
|
||||||
.input-right {
|
padding-left: 36px;
|
||||||
display: flex;
|
}
|
||||||
|
|
||||||
|
> .label {
|
||||||
|
padding-bottom: 4px;
|
||||||
|
font-size:14px;
|
||||||
|
font-weight:600;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .prefix,
|
||||||
|
> .input-right {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
writing-mode: horizontal-tb;
|
|
||||||
color: var(--fw-placeholder-color);
|
color: var(--fw-placeholder-color);
|
||||||
|
}
|
||||||
|
|
||||||
& .button,
|
> .prefix {
|
||||||
& .button:hover {
|
position: absolute;
|
||||||
background-color: transparent !important;
|
left: 0;
|
||||||
border: none !important;
|
bottom: 0;
|
||||||
|
|
||||||
|
height: 40px;
|
||||||
|
min-width: 44px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> i {
|
||||||
|
font-size:18px;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefix {
|
&:has(>.prefix)>input {
|
||||||
width: 32px;
|
padding-left: 40px;
|
||||||
justify-content: center;
|
}
|
||||||
|
|
||||||
|
> .input-right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
right: 0px;
|
||||||
left: 4px;
|
bottom: 0px;
|
||||||
bottom: 0;
|
|
||||||
width: 32px;
|
height: 40px;
|
||||||
justify-content: center;
|
min-width: 44px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .search {
|
||||||
|
> i {
|
||||||
|
font-size:18px;
|
||||||
|
}
|
||||||
|
&.button {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .show-password {
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(>.show-password)>input {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:has(>.search)>input {
|
||||||
|
padding-right: 140px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import Input from "~/components/ui/Input.vue"
|
import Input from "~/components/ui/Input.vue"
|
||||||
|
import Button from "~/components/ui/Button.vue"
|
||||||
|
import Layout from "~/components/ui/Layout.vue"
|
||||||
|
import Spacer from "~/components/ui/layout/Spacer.vue"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
|
@ -49,3 +52,34 @@ You can add a template on the right-hand side of the input to guide the user's i
|
||||||
suffix
|
suffix
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</Input>
|
||||||
|
|
||||||
|
## Presets
|
||||||
|
|
||||||
|
### Search
|
||||||
|
|
||||||
|
<Input search />
|
||||||
|
|
||||||
|
### Password
|
||||||
|
|
||||||
|
<Layout flex no-gap style="align-items:flex-end">
|
||||||
|
<Spacer v
|
||||||
|
:size="80"
|
||||||
|
style="outline:1px solid red; align-self: baseline;"
|
||||||
|
/>
|
||||||
|
<Input>
|
||||||
|
<template #label>
|
||||||
|
User name
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Layout>
|
||||||
|
<Layout flex no-gap style="align-items:flex-end">
|
||||||
|
<Spacer v
|
||||||
|
:size="80"
|
||||||
|
style="outline:1px solid red; align-self: baseline;"
|
||||||
|
/>
|
||||||
|
<Input password>
|
||||||
|
<template #label>
|
||||||
|
Password
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Layout>
|
||||||
|
|
Loading…
Reference in New Issue