feat(ui): form layout

This commit is contained in:
upsiflu 2024-12-30 11:50:57 +01:00
parent 2ebda850c7
commit 8ff2c05e05
7 changed files with 78 additions and 124 deletions

View File

@ -80,7 +80,7 @@ const submit = async () => {
</script>
<template>
<form
<Layout form stack
style="max-width: 600px"
@submit.prevent="submit()"
>
@ -107,7 +107,6 @@ const submit = async () => {
</Alert>
<Spacer v-if="errors.length > 0" />
<template v-if="domain === store.getters['instance/domain']">
<div style="margin-bottom: 16px">
<Input
id="username-field"
ref="username"
@ -128,8 +127,6 @@ const submit = async () => {
</template>
</template>
</Input>
</div>
<div style="margin-bottom: 16px">
<Input password
v-model="credentials.password"
field-id="password-field"
@ -146,7 +143,6 @@ const submit = async () => {
</router-link>
</template>
</Input>
</div>
</template>
<template v-else>
<p>
@ -161,5 +157,5 @@ const submit = async () => {
>
{{ t('components.auth.LoginForm.button.login') }}
</Button>
</form>
</Layout>
</template>

View File

@ -106,7 +106,7 @@ fetchInstanceSettings()
:show-signup="false"
/>
</div>
<form
<Layout form stack
v-else
style="max-width: 600px"
@submit.prevent="submit()"
@ -116,20 +116,17 @@ fetchInstanceSettings()
>
{{ t('components.auth.SignupForm.message.registrationClosed') }}
</Alert>
<Spacer v-if="!store.state.instance.settings.users.registration_enabled.value"/>
<Alert yellow
v-else-if="signupRequiresApproval"
>
{{ t('components.auth.SignupForm.message.requiresReview') }}
</Alert>
<Spacer v-else-if="signupRequiresApproval" />
<template v-if="formCustomization?.help_text">
<rendered-description
:content="formCustomization.help_text"
:fetch-html="fetchDescriptionHtml"
:permissive="true"
/>
<div class="ui hidden divider" />
</template>
<Alert red
v-if="errors.length > 0"
@ -146,7 +143,6 @@ fetchInstanceSettings()
</li>
</ul>
</Alert>
<div class="required field">
<Input
id="username-field"
:label = "t('components.auth.SignupForm.label.username')"
@ -158,8 +154,6 @@ fetchInstanceSettings()
autofocus
:placeholder="labels.usernamePlaceholder"
/>
</div>
<div class="required field">
<Input
:label="t('components.auth.SignupForm.label.email')"
id="email-field"
@ -170,19 +164,12 @@ fetchInstanceSettings()
type="email"
:placeholder="labels.emailPlaceholder"
/>
</div>
<div class="required field">
<Input password
:label="t('components.auth.SignupForm.label.password')"
v-model="payload.password1"
field-id="password-field"
/>
</div>
<div
v-if="!store.state.instance.settings.users.registration_enabled.value"
class="required field"
>
<Input
<Input v-if="!store.state.instance.settings.users.registration_enabled.value"
:label="t('components.auth.SignupForm.label.invitation')"
id="invitation-code"
v-model="payload.invitation"
@ -191,14 +178,11 @@ fetchInstanceSettings()
name="invitation"
:placeholder="labels.placeholder"
/>
</div>
<template v-if="signupRequiresApproval && (formCustomization?.fields.length ?? 0) > 0">
<div
<div v-if="signupRequiresApproval && (formCustomization?.fields.length ?? 0) > 0"
v-for="(field, idx) in formCustomization?.fields"
:key="idx"
:class="[{required: field.required}, 'field']"
>
<!-- TODO: Add `label` prop to `Textarea` component -->
<Textarea
:label="field.label"
v-if="field.input_type === 'long_text'"
@ -216,8 +200,6 @@ fetchInstanceSettings()
:required="field.required"
/>
</div>
</template>
<Spacer />
<Button
primary
auto
@ -225,5 +207,5 @@ fetchInstanceSettings()
>
{{ t('components.auth.SignupForm.button.create') }}
</Button>
</form>
</Layout>
</template>

View File

@ -107,12 +107,6 @@ onMounted(() => {
padding: 10px;
}
// Margin
&+.button {
margin-left: 8px;
}
border-radius: var(--fw-border-radius);
transform: translateX(var(--fw-translate-x)) translateY(var(--fw-translate-y)) scale(var(--fw-scale));

View File

@ -5,24 +5,26 @@ const props = defineProps<{
noRule?:true,
noWrap?:true
}
& { [P in "stack" | "grid" | "flex" | "columns"]?: true | string }
& { [C in "nav" | "aside" | "header" | "footer" | "main" | "label" | "h1" | "h2" | "h3" | "h4" | "h5"]?:true }>()
const columnWidth = props.columnWidth ?? 46
& { [P in "stack" | "grid" | "flex" | "columns" | "row" | "page"]?: true | string }
& { [C in "nav" | "aside" | "header" | "footer" | "main" | "label" | "form" | "h1" | "h2" | "h3" | "h4" | "h5"]?:true }>()
const columnWidth = props.columnWidth ?? 46
</script>
<template>
<component
:is="props.nav ? 'nav' : props.aside ? 'aside' : props.header ? 'header' : props.footer ? 'footer' : props.main ? 'main' : props.label ? 'label' : props.h1? 'h1' : props.h2? 'h2' : props.h3? 'h3' : props.h4? 'h4' : props.h5? 'h5' : 'div'"
:class="[
$style.layout,
noGap || $style.gap,
noWrap || $style.wrap,
props.grid ? $style[props.grid===true ? 'grid' : 'grid-custom']
: props.flex ? $style.flex
: props.columns? $style.columns
: $style.stack
]">
<slot />
<component
:is="props.nav ? 'nav' : props.aside ? 'aside' : props.header ? 'header' : props.footer ? 'footer' : props.main ? 'main' : props.label ? 'label' : props.form ? 'form' : props.h1? 'h1' : props.h2? 'h2' : props.h3? 'h3' : props.h4? 'h4' : props.h5? 'h5' : 'div'"
:class="[
$style.layout,
noGap || $style.gap,
noWrap || $style.wrap,
props.grid ? $style[props.grid===true ? 'grid' : 'grid-custom']
: props.flex ? $style.flex
: props.columns? $style.columns
: $style.stack
]">
<slot />
</component>
</template>

View File

@ -61,6 +61,7 @@
}
> .label {
margin-top: -18px;
padding-bottom: 8px;
font-size:14px;
font-weight:600;

View File

@ -8,6 +8,7 @@ import { useRouter } from 'vue-router'
import Input from '~/components/ui/Input.vue'
import Button from '~/components/ui/Button.vue'
import Spacer from '~/components/ui/layout/Spacer.vue'
import Layout from '~/components/ui/Layout.vue'
import axios from 'axios'
@ -55,7 +56,7 @@ onMounted(() => emailInput.value.focus())
<h2>
{{ t('views.auth.PasswordReset.header.reset') }}
</h2>
<form
<Layout form stack
@submit.prevent="submit()"
style="max-width: 600px"
>
@ -79,35 +80,34 @@ onMounted(() => emailInput.value.focus())
<p>
{{ t('views.auth.PasswordReset.help.form') }}
</p>
<div class="field">
<Input
:label="t('views.auth.PasswordReset.label.email')"
id="account-email"
ref="emailInput"
v-model="email"
required
type="email"
name="email"
autofocus
:placeholder="labels.placeholder"
/>
</div>
<Spacer />
<Button
:class="['ui', {'loading': isLoading}, 'success', 'button']"
type="submit"
primary
auto
>
{{ t('views.auth.PasswordReset.button.requestReset') }}
</Button>
<Button
:to="{path: '/login'}"
ghost
auto
>
{{ t('views.auth.PasswordReset.link.back') }}
</Button>
</form>
<Input
:label="t('views.auth.PasswordReset.label.email')"
id="account-email"
ref="emailInput"
v-model="email"
required
type="email"
name="email"
autofocus
:placeholder="labels.placeholder"
/>
<Layout flex>
<Button
:class="['ui', {'loading': isLoading}, 'success', 'button']"
type="submit"
primary
auto
>
{{ t('views.auth.PasswordReset.button.requestReset') }}
</Button>
<Button
:to="{path: '/login'}"
ghost
auto
>
{{ t('views.auth.PasswordReset.link.back') }}
</Button>
</Layout>
</Layout>
</main>
</template>

View File

@ -3,6 +3,7 @@ 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"
import Alert from "~/components/ui/Alert.vue"
</script>
# Input
@ -90,54 +91,32 @@ You can add a template on the right-hand side of the input to guide the user's i
### Password
```vue-html
<Layout flex no-gap style="align-items:flex-end">
<Spacer v
:size="80"
style="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="align-self: baseline;"
/>
<Input password>
<template #label>
Password
</template>
</Input>
<Spacer :size="64" />
<Layout form stack>
<Input label="User name" />
<Input password label="Password" />
<Layout flex>
<Button primary> Submit </Button>
<Button> Cancel </Button>
</Layout>
</Layout>
```
<!-- Implement a component for baseline alignments like this -->
<Spacer :size="64" />
<Layout form stack>
<Input label="User name" />
<Input password label="Password" />
<Layout flex>
<Button primary> Submit </Button>
<Button> Cancel </Button>
</Layout>
</Layout>
<Layout flex no-gap style="align-items:flex-end">
<Spacer v
:size="80"
style="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="align-self: baseline;"
/>
<Input password>
<template #label>
Password
</template>
</Input>
</Layout>
::: tip
We use the spacer to simulate the baseline alignment on page layouts (64px between sections)
:::
## Fallthrough attributes