funkwhale/front/src/components/ui/Input.vue

113 lines
2.8 KiB
Vue

<script setup lang="ts">
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n';
import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
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,
label?: string,
}>()
// 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/
// const isNumeric = restProps.numeric
const showPassword = ref(false)
onKeyboardShortcut('escape', () => showPassword.value = false)
// TODO: Accept fallback $attrs: `const fallthroughAttrs = useAttrs()`
// TODO: Implement `copy password` button?
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>()
</script>
<template>
<Layout stack no-gap label
:class="{ 'has-icon': !!icon }"
class="funkwhale input"
>
<span v-if="$slots['label']" class="label">
<slot name="label" />
</span>
<span v-if="restProps.label" class="label">
{{ restProps.label }}
</span>
<input
v-bind="{...$attrs, ...attributes}"
v-model="model"
ref="input"
:placeholder="placeholder"
@click.stop
@blur="showPassword = false"
/>
<!-- Left side icon -->
<div v-if="icon" class="prefix">
<i :class="['bi', icon]" />
</div>
<!-- Search -->
<div v-if="restProps.search" class="prefix">
<i class="bi bi-search" />
</div>
<!-- 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="(e) => { console.log(e.relatedTarget); if (e.relatedTarget && 'value' in e.relatedTarget && e.relatedTarget.value === model) showPassword = showPassword; else showPassword = false; }"
class="input-right show-password" title="toggle visibility"
>
<i class="bi bi-eye" />
</button>
<!-- Search -->
<Button
solid primary
type="submit"
v-if="restProps.search"
class="input-right search"
>
{{ t('components.Sidebar.link.search') }}
</Button>
</Layout>
</template>
<style lang="scss">
@import './input.scss';
input[type=number]::-webkit-inner-spin-button {
opacity: 1;
}
</style>