Skip to content

Commit

Permalink
feat(components): v2 input components
Browse files Browse the repository at this point in the history
build v2 input components to work with form v2

fix #102
  • Loading branch information
joshuagraber committed Oct 8, 2024
1 parent e7df38a commit 7ebd780
Show file tree
Hide file tree
Showing 16 changed files with 349 additions and 6 deletions.
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'footer-max-length': [2, 'never']
'footer-max-length': [0]
}
};
4 changes: 1 addition & 3 deletions src/components/Input/PdapInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ const errorMessageId = computed(() => `pdap-${props.name}-input-error`);
/* Error state */
.pdap-input-error {
@apply flex-wrap;
row-gap: 0;
@apply flex-wrap gap-x-0;
}
.pdap-input-error label {
Expand Down
47 changes: 47 additions & 0 deletions src/components/InputCheckbox/PdapInputCheckbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div
class="pdap-input pdap-input-checkbox"
:class="{ ['pdap-input-error']: error }"
>
<slot v-if="$slots.error" name="error" class="pdap-input-error-message" />
<div v-else-if="error" class="pdap-input-error-message">{{ error }}</div>

<input
:id="id"
:name="name"
:defaultChecked="defaultChecked"
:value="values?.[name] ?? ''"
v-bind="$attrs"
type="checkbox"
@input="onInput"
/>

<slot v-if="$slots.label" name="label" :for="id" />
<label v-else-if="label" :for="id">{{ label }}</label>
</div>
</template>

<script setup lang="ts">
import { computed, inject, useSlots } from 'vue';
import { PdapInputCheckboxProps } from './types';
import { PdapFormProvideV2 } from '../FormV2/types';
import { provideKey } from '../FormV2/util';
const { label, name } = defineProps<PdapInputCheckboxProps>();
const slots = useSlots();
if (!slots.label && !label)
throw new Error(
'All form inputs must have a label, passed as a slot or a prop'
);
const { values, setValues, v$ } = inject<PdapFormProvideV2>(provideKey)!;
const error = computed(() => {
return v$.value[name]?.$error ? v$.value[name]?.$errors?.[0]?.$message : '';
});
function onInput(e: Event) {
setValues({ [name]: (e.target as unknown as HTMLInputElement).checked });
}
</script>
1 change: 1 addition & 0 deletions src/components/InputCheckbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InputCheckbox } from './PdapInputCheckbox.vue';
6 changes: 6 additions & 0 deletions src/components/InputCheckbox/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface PdapInputCheckboxProps {
id: string;
label?: string;
name: string;
defaultChecked?: boolean;
}
83 changes: 83 additions & 0 deletions src/components/InputPassword/PdapInputPassword.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<div
class="pdap-input pdap-input-password"
:class="{ ['pdap-input-error']: error }"
>
<slot v-if="$slots.error" name="error" class="pdap-input-error-message" />
<div v-else-if="error" class="pdap-input-error-message">{{ error }}</div>

<div class="pdap-input-password-wrapper">
<input
:id="id"
:name="name"
:placeholder="placeholder ?? 'Password'"
:value="values?.[name]"
v-bind="$attrs"
:type="isMasked ? 'password' : 'text'"
@input="onInput"
/>
<button
type="button"
class="pdap-input-password-toggle"
:aria-label="isMasked ? 'Show text' : 'Hide text'"
@click="toggleMask"
>
<FontAwesomeIcon :icon="isMasked ? faEye : faEyeSlash" />
</button>
</div>

<slot v-if="$slots.label" name="label" :for="id" />
<label v-else-if="label" :for="id">{{ label }}</label>
</div>
</template>

<script setup lang="ts">
import { computed, inject, ref, useSlots } from 'vue';
import { PdapInputTextProps } from '../InputText/types';
import { PdapFormProvideV2 } from '../FormV2/types';
import { provideKey } from '../FormV2/util';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
const { label, name } = defineProps<PdapInputTextProps>();
const slots = useSlots();
if (!slots.label && !label)
throw new Error(
'All form inputs must have a label, passed as a slot or a prop'
);
const { values, setValues, v$ } = inject<PdapFormProvideV2>(provideKey)!;
const error = computed(() => {
return v$.value[name]?.$error ? v$.value[name]?.$errors?.[0]?.$message : '';
});
const isMasked = ref(true);
function onInput(e: Event) {
setValues({ [name]: (e.target as unknown as HTMLInputElement).value });
}
function toggleMask() {
isMasked.value = !isMasked.value;
}
</script>

<style>
@tailwind components;
@layer components {
.pdap-input-password input {
@apply w-full;
}
.pdap-input-password-wrapper {
@apply relative;
}
.pdap-input-password-toggle {
@apply absolute right-4 cursor-pointer text-[rgba(0_0_0)] top-1/2 translate-y-[-50%];
}
}
</style>
1 change: 1 addition & 0 deletions src/components/InputPassword/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InputPassword } from './PdapInputPassword.vue';
44 changes: 44 additions & 0 deletions src/components/InputText/PdapInputText.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div class="pdap-input" :class="{ ['pdap-input-error']: error }">
<slot v-if="$slots.error" name="error" class="pdap-input-error-message" />
<div v-else-if="error" class="pdap-input-error-message">{{ error }}</div>

<input
:id="id"
:name="name"
:placeholder="placeholder"
:value="values?.[name] ?? ''"
v-bind="$attrs"
type="text"
@input="onInput"
/>

<slot v-if="$slots.label" name="label" :for="id" />
<label v-else-if="label" :for="id">{{ label }}</label>
</div>
</template>

<script setup lang="ts">
import { computed, inject, useSlots } from 'vue';
import { PdapInputTextProps } from './types';
import { PdapFormProvideV2 } from '../FormV2/types';
import { provideKey } from '../FormV2/util';
const { label, name } = defineProps<PdapInputTextProps>();
const slots = useSlots();
if (!slots.label && !label)
throw new Error(
'All form inputs must have a label, passed as a slot or a prop'
);
const { values, setValues, v$ } = inject<PdapFormProvideV2>(provideKey)!;
const error = computed(() => {
return v$.value[name]?.$error ? v$.value[name]?.$errors?.[0]?.$message : '';
});
function onInput(e: Event) {
setValues({ [name]: (e.target as unknown as HTMLInputElement).value });
}
</script>
1 change: 1 addition & 0 deletions src/components/InputText/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InputText } from './PdapInputText.vue';
6 changes: 6 additions & 0 deletions src/components/InputText/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface PdapInputTextProps {
id: string;
label?: string;
name: string;
placeholder?: string;
}
4 changes: 4 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ export { Button } from './Button';
export { ErrorBoundary } from './ErrorBoundary';
export { Footer } from './Footer';
export { Form } from './Form';
export { FormV2 } from './FormV2';
export { Input } from './Input';
export { InputCheckbox } from './InputCheckbox';
export { InputPassword } from './InputPassword';
export { InputText } from './InputText';
export { Header } from './Header';
export { Nav } from './Nav';
export { QuickSearchForm } from './QuickSearchForm';
Expand Down
75 changes: 75 additions & 0 deletions src/demo/pages/FormV2Demo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<template>
<main>
<FormV2
id="form-id"
name="form-name"
:schema="SCHEMA"
@submit="(values) => console.log({ values })"
>
<InputText
:id="INPUT_TEXT_NAME"
:name="INPUT_TEXT_NAME"
:placeholder="PLACEHOLDER"
>
<template #label>
<h4>Your name</h4>
</template>
</InputText>

<InputPassword
:id="INPUT_PASSWORD_NAME"
:name="INPUT_PASSWORD_NAME"
placeholder="Password"
label="Type your password here"
/>

<p>Foo bar baz, extra content here</p>

<InputCheckbox
:id="INPUT_CHECKBOX_NAME"
:name="INPUT_CHECKBOX_NAME"
label="Do you like ice cream?"
/>

<Button type="submit">Submit</Button>
</FormV2>
</main>
</template>

<script setup lang="ts">
import { Button } from '../../components/Button';
import { FormV2 } from '../../components/FormV2';
import { InputText } from '../../components/InputText';
import { InputCheckbox } from '../../components/InputCheckbox';
import { InputPassword } from '../../components/InputPassword';
const INPUT_CHECKBOX_NAME = 'ice-cream';
const INPUT_TEXT_NAME = 'first-name';
const INPUT_PASSWORD_NAME = 'password';
const PLACEHOLDER = 'Paul';
const SCHEMA = [
{
name: INPUT_TEXT_NAME,
validators: {
required: {
value: true,
},
},
},
{
name: INPUT_PASSWORD_NAME,
validators: {
password: {
value: true,
message: 'Your password should be a valid password',
},
},
},
];
</script>

<style>
main {
min-height: 80vh;
}
</style>
6 changes: 6 additions & 0 deletions src/demo/router.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router';
import ComponentDemo from './pages/ComponentDemo.vue';
import #FormDemo from './pages/#FormDemo.vue';
import FormV2Demo from './pages/FormV2Demo.vue';

const routes = [
{
Expand Down Expand Up @@ -46,6 +47,11 @@ const routes = [
component: #FormDemo,
name: 'Login Demo',
},
{
path: '/form-v2-demo',
component: FormV2Demo,
name: 'FormV2 Demo',
},
];

const router = createRouter({
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './components/Dropdown/types';
export * from './components/ErrorBoundary/types';
export * from './components/Footer/types';
export * from './components/Form/types';
export * from './components/FormV2/types';
export * from './components/Header/types';
export * from './components/Input/types';
export * from './components/Nav/types';
Expand Down
Loading

0 comments on commit 7ebd780

Please # to comment.