refactor(ui): add color and `reset` props to Input component

environments/review-docs-feat-z0hkbz/deployments/20479
upsiflu 2025-02-02 20:14:29 +01:00
rodzic ee22e02617
commit 6e69a74b75
3 zmienionych plików z 76 dodań i 50 usunięć

Wyświetl plik

@ -2,13 +2,12 @@
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'; import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
import { color } from "~/composables/color.ts"; import { type ColorProps, type VariantProps, type DefaultProps, type RaisedProps, type PastelProps, color } from "~/composables/color.ts";
import Button from "~/components/ui/Button.vue" import Button from "~/components/ui/Button.vue"
import Layout from "~/components/ui/Layout.vue" import Layout from "~/components/ui/Layout.vue"
const props = withDefaults( const { icon, placeholder, ...props } = defineProps<{
defineProps<{
icon?: string; icon?: string;
placeholder?: string; placeholder?: string;
password?: true; password?: true;
@ -16,14 +15,10 @@ const props = withDefaults(
numeric?: true; numeric?: true;
label?: string; label?: string;
autofocus?: boolean; autofocus?: boolean;
raised?: boolean; reset?: () => void;
}>(), } & (ColorProps | DefaultProps | PastelProps )
{ & VariantProps
raised: false, // Default value & RaisedProps>()
}
);
const { icon, placeholder, ...restProps } = props;
// 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/
@ -37,9 +32,9 @@ onKeyboardShortcut('escape', () => showPassword.value = false)
// TODO: Implement `copy password` button? // TODO: Implement `copy password` button?
const attributes = computed(() => ({ const attributes = computed(() => ({
...(restProps.password && !showPassword.value? {type: 'password'} : {}), ...(props.password && !showPassword.value? {type: 'password'} : {}),
...(restProps.search? {type: 'search'} : {}), ...(props.search? {type: 'search'} : {}),
...(restProps.numeric? {type: 'numeric'} : {}), ...(props.numeric? {type: 'numeric'} : {}),
})) }))
const { t } = useI18n() const { t } = useI18n()
@ -47,10 +42,10 @@ const { t } = useI18n()
const input = ref() const input = ref()
onMounted(() => { onMounted(() => {
if (restProps.autofocus) input.value.focus(); if (props.autofocus) input.value.focus();
}) })
const model = defineModel<string|number>() const model = defineModel<string|number>({ required: true })
</script> </script>
<template> <template>
@ -62,8 +57,8 @@ const model = defineModel<string|number>()
<slot name="label" /> <slot name="label" />
</span> </span>
<span v-if="restProps.label" class="label"> <span v-if="props.label" class="label">
{{ restProps.label }} {{ props.label }}
</span> </span>
<input <input
@ -82,7 +77,7 @@ const model = defineModel<string|number>()
</div> </div>
<!-- Search --> <!-- Search -->
<div v-if="restProps.search" class="prefix"> <div v-if="props.search" class="prefix">
<i class="bi bi-search" /> <i class="bi bi-search" />
</div> </div>
@ -96,7 +91,7 @@ const model = defineModel<string|number>()
<!-- Password --> <!-- Password -->
<button <button
v-if="restProps.password" v-if="props.password"
style="background:transparent; border:none; appearance:none;" style="background:transparent; border:none; appearance:none;"
role="switch" role="switch"
type="button" type="button"
@ -110,12 +105,22 @@ const model = defineModel<string|number>()
<!-- Search --> <!-- Search -->
<Button <Button
solid primary solid primary
type="submit" v-if="props.search"
v-if="restProps.search"
class="input-right search" class="input-right search"
> >
{{ t('components.Sidebar.link.search') }} {{ t('components.Sidebar.link.search') }}
</Button> </Button>
<!-- Reset -->
<Button
ghost primary square-small
v-if="props.reset"
icon="bi-arrow-counterclockwise"
class="input-right reset"
:onClick="reset"
:title="t('components.library.EditForm.button.reset')"
/>
</Layout> </Layout>
</template> </template>

Wyświetl plik

@ -102,17 +102,23 @@
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
} }
&:has(>.search)>input {
padding-right: 140px;
}
> .show-password { > .show-password {
justify-content:center; justify-content:center;
} }
&:has(>.show-password)>input { &:has(>.show-password)>input {
padding-right: 40px; padding-right: 40px;
} }
&:has(>.search)>input { >.reset {
padding-right: 140px; min-width: auto;
margin: 4px;
// Make button fit snuggly into rounded border
border-radius: 4px;
} }
} }

Wyświetl plik

@ -7,16 +7,21 @@ import Layout from "~/components/ui/Layout.vue"
import Spacer from "~/components/ui/Spacer.vue" import Spacer from "~/components/ui/Spacer.vue"
import Alert from "~/components/ui/Alert.vue" import Alert from "~/components/ui/Alert.vue"
const value = ref("Value") const value = ref("Preset Value")
const search = ref("")
const user = ref("")
const password = ref("")
const reset = () => { console.log("Hello"); value.value = 'Original value' }
</script> </script>
```ts ```ts
import Input from "~/components/ui/Input.vue" import Input from "~/components/ui/Input.vue";
``` ```
# Input # Input
Inputs are areas in which users can enter information. In Funkwhale, these mostly take the form of search fields. Inputs are areas in which users can enter a single-line text or a number. Several [presets](#presets) are available.
| Prop | Data type | Required? | Description | | Prop | Data type | Required? | Description |
| ------------- | --------- | --------- | --------------------------------------------------------------------------- | | ------------- | --------- | --------- | --------------------------------------------------------------------------- |
@ -24,13 +29,17 @@ Inputs are areas in which users can enter information. In Funkwhale, these mostl
| `icon` | String | No | The [Bootstrap icon](https://icons.getbootstrap.com/) to show on the input. | | `icon` | String | No | The [Bootstrap icon](https://icons.getbootstrap.com/) to show on the input. |
| `v-model` | String | Yes | The text entered in the input. | | `v-model` | String | Yes | The text entered in the input. |
You can link a user's input to form data by referencing the data in a `v-model` directive. Link a user's input to form data by referencing the data in a `v-model` of type `string`.
```ts
const value = ref("Preset Value");
```
```vue-html{2} ```vue-html{2}
<Input v-model="value" placeholder="Your favorite animal" /> <Input v-model="value" placeholder="Your favorite animal" />
``` ```
<Input placeholder="Your favorite animal" /> <Input v-model="value" placeholder="Your favorite animal" />
## Input icons ## Input icons
@ -45,14 +54,14 @@ Add a [Bootstrap icon](https://icons.getbootstrap.com/) to an input to make its
## Label slot ## Label slot
```vue-html{2-4} ```vue-html{2-4}
<Input> <Input v-model="user">
<template #label> <template #label>
User name User name
</template> </template>
</Input> </Input>
``` ```
<Input> <Input v-model="user">
<template #label> <template #label>
User name User name
</template> </template>
@ -61,10 +70,10 @@ Add a [Bootstrap icon](https://icons.getbootstrap.com/) to an input to make its
If you just have a string, we have a convenience prop, so instead you can write: If you just have a string, we have a convenience prop, so instead you can write:
```vue-html ```vue-html
<Input label="User name" /> <Input v-model="user" label="User name" />
``` ```
<Input label="User name" /> <Input v-model="user" label="User name" />
## Input-right slot ## Input-right slot
@ -78,29 +87,33 @@ You can add a template on the right-hand side of the input to guide the user's i
</Input> </Input>
``` ```
<Input placeholder="Search"> <Input v-model="search" placeholder="Search">
<template #input-right> <template #input-right>
suffix suffix
</template> </template>
</Input> </Input>
## Color
See [Button](./button.md#button-colors) for a detailed overview of available props.
## Presets ## Presets
### Search ### Search
```vue-html ```vue-html
<Input search /> <Input search v-model="search" />
``` ```
<Input search /> <Input search v-model="search" />
### Password ### Password
```vue-html ```vue-html
<Spacer :size="64" /> <Spacer :size="64" />
<Layout form stack> <Layout form stack>
<Input label="User name" /> <Input v-model="user" label="User name" />
<Input password label="Password" /> <Input password v-model="password" label="Password" />
<Layout flex> <Layout flex>
<Button primary> Submit </Button> <Button primary> Submit </Button>
<Button> Cancel </Button> <Button> Cancel </Button>
@ -110,8 +123,8 @@ You can add a template on the right-hand side of the input to guide the user's i
<Spacer :size="64" /> <Spacer :size="64" />
<Layout form stack> <Layout form stack>
<Input label="User name" /> <Input v-model="user" label="User name" />
<Input password label="Password" /> <Input password v-model="password" label="Password" />
<Layout flex> <Layout flex>
<Button primary> Submit </Button> <Button primary> Submit </Button>
<Button> Cancel </Button> <Button> Cancel </Button>
@ -124,28 +137,30 @@ We use the spacer to simulate the baseline alignment on page layouts (64px betwe
::: :::
## Value ### Add a reset option
```vue-html ```vue-html
<Input v-model="value"/> <Input
<Input v-model="value"/> v-model="value"
:reset="() => { value = 'Original value' }">
</Input>
``` ```
<Layout flex> <Input
<Input auto v-model="value"/> v-model="value"
<Input auto v-model="value"/> :reset="() => { value = 'Original value' }">
</Layout> </Input>
## Fallthrough attributes ## Fallthrough attributes
If you add attributes that are no props, they will be added to the component: If you add attributes that are no props, they will be added to the component:
```vue-html ```vue-html
<Input required <Input v-model="password" required
field-id="password-field" field-id="password-field"
/> />
``` ```
<Input required <Input v-model="password" required
field-id="password-field" field-id="password-field"
/> />