Skip to content

Commit

Permalink
chore: update playground radio components
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Sep 14, 2024
1 parent 2017403 commit df23646
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 49 deletions.
43 changes: 18 additions & 25 deletions packages/playground/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
<template>
<div class="flex gap-4 relative p-8">
<form class="w-full" v-bind="formProps">
<InputSelect
name="country"
v-model="model"
label="Select Input"
:get-option-value="t => t.code"
:groups="continents"
/>

<Slider name="amount" />

<CheckboxItem name="terms" />

<input type="text" name="test" value="test" />

<button>Submit lets gooo</button>
</form>

<div class="w-1/3 relative">
<pre class="max-h-[95vh] overflow-y-auto bg-gray-200 rounded-lg p-4 sticky top-4">{{ values }}</pre>
</div>
</div>
<RadioGroup label="Radio Group" description="There is only one right answer" :schema="schema">
<InputRadioItem label="Tea 🍵" value="🍵" />
<InputRadioItem label="Coffee ☕️" value="☕️" />
<InputRadioItem label="Milk 🥛" value="🥛" />
</RadioGroup>

<RadioGroup label="Radio Group" description="There is only one right answer" :schema="schema">
<RadioItem label="Tea 🍵" value="🍵" />
<RadioItem label="Coffee ☕️" value="☕️" />
<RadioItem label="Milk 🥛" value="🥛" />
</RadioGroup>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { z } from 'zod';
import InputText from '@/components/InputText.vue';
import InputNumber from '@/components/InputNumber.vue';
import InputTextArea from '@/components/InputTextArea.vue';
Expand All @@ -44,10 +33,14 @@ import { useForm } from '@formwerk/core';
import InputSelect from '@/components/InputSelect.vue';
import OptionItem from './components/OptionItem.vue';
const { values, formProps } = useForm({});
const model = ref('');
import { defineSchema } from '@formwerk/schema-zod';
const schema = defineSchema(
z.string().min(1, { message: 'Please select a drink' }).endsWith('☕️', { message: 'WRONG ANSWER!' }),
);
const continents = [
{
label: 'Africa',
Expand Down
58 changes: 54 additions & 4 deletions packages/playground/src/components/InputRadioItem.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,65 @@
<script setup lang="ts">
import { RadioProps, useRadio } from '@formwerk/core';
import { type RadioProps, useRadio } from '@formwerk/core';
const props = defineProps<RadioProps>();
const { labelProps, inputProps } = useRadio(props);
</script>

<template>
<div class="flex items-center">
<input v-bind="inputProps" />
<div class="radio-item">
<label v-bind="labelProps">
<input v-bind="inputProps" class="sr-only" />

<label v-bind="labelProps" class="ml-1">{{ label }}</label>
{{ label }}
</label>
</div>
</template>

<style>
:root {
--color-text: #333;
--color-hint: #666;
--color-border: #ccc;
--color-focus: #0056b3;
--color-error: #f00;
--color-valid: #059669;
--color-hover: #eee;
}
</style>

<style scoped>
.radio-item {
display: inline-flex;
align-items: center;
border-radius: 99999px;
padding: 0.5rem 1rem;
border: 1px solid var(--color-border);
font-size: 13px;
font-weight: 500;
transition:
background-color 0.15s,
color 0.15s;
&:has(:focus) {
border: 1px solid var(--color-focus);
}
&:has(:checked) {
background-color: var(--color-focus);
color: #fff;
}
}
/** This is a common utility CSS class, you can find it in your CSS framework of choice */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
</style>
89 changes: 78 additions & 11 deletions packages/playground/src/components/RadioGroup.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,90 @@
<script setup lang="ts">
import { RadioGroupProps, useRadioGroup } from '@formwerk/core';
import { type RadioGroupProps, useRadioGroup } from '@formwerk/core';
const props = defineProps<RadioGroupProps>();
const { radioGroupProps, labelProps, descriptionProps, errorMessageProps, errorMessage } = useRadioGroup(props);
const { groupProps, labelProps, descriptionProps, errorMessageProps, errorMessage, fieldValue } = useRadioGroup(props);
</script>

<template>
<div
v-bind="radioGroupProps"
class="flex border border-gray-200 rounded-md p-2 gap-1"
:class="{ 'flex-col': orientation === 'vertical' }"
>
<span v-bind="labelProps" class="font-medium">{{ label }}</span>
<div v-bind="groupProps" class="radio-group">
<div v-bind="labelProps" class="group-label">{{ label }} {{ fieldValue }}</div>

<slot />
<div class="radios-container">
<slot />
</div>

<div v-if="errorMessageProps" v-bind="errorMessageProps" class="text-red-500 text-xs">{{ errorMessage }}</div>
<div v-if="errorMessage" v-bind="errorMessageProps" class="error">
{{ errorMessage }}
</div>

<div v-else-if="description" v-bind="descriptionProps" class="text-gray-500 text-xs">{{ description }}</div>
<div v-else-if="description" v-bind="descriptionProps" class="hint">
{{ description }}
</div>
</div>
</template>

<style>
:root {
--color-text: #333;
--color-hint: #666;
--color-border: #ccc;
--color-focus: #007bff;
--color-error: #f00;
--color-valid: #059669;
--color-hover: #eee;
}
</style>

<style scoped>
.radio-group {
display: flex;
flex-direction: column;
.hint,
.error {
margin-top: 0.25rem;
}
.error {
color: var(--color-error);
display: none;
font-size: 13px;
}
.hint {
color: var(--color-hint);
font-size: 13px;
opacity: 0;
transition: opacity 0.3s ease;
}
.radios-container {
margin-top: 0.25rem;
}
.group-label {
color: var(--color-text);
display: block;
margin-bottom: 0.25rem;
font-size: 14px;
font-weight: 500;
}
&:has(:focus) {
.hint {
opacity: 1;
}
}
&:has(:invalid) {
.error {
display: block;
}
.hint {
display: none;
}
}
}
</style>
62 changes: 53 additions & 9 deletions packages/playground/src/components/RadioItem.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,63 @@
<script setup lang="ts">
import { RadioProps, useRadio } from '@formwerk/core';
import { type RadioProps, useRadio } from '@formwerk/core';
const props = defineProps<RadioProps>();
const { labelProps, radioProps, isChecked } = useRadio(props);
const { labelProps, inputProps } = useRadio(props);
</script>

<template>
<div v-bind="radioProps" class="flex items-center">
<div
class="w-5 h-5 rounded-full flex-shrink-0 border border-gray-600 flex items-center justify-center focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<div class="w-3 h-3 rounded-full flex-shrink-0" :class="{ 'bg-blue-500': isChecked }" />
<div class="radio-item" v-bind="inputProps">
<div v-bind="labelProps">
{{ label }}
</div>

<span v-bind="labelProps" class="ml-1">{{ label }}</span>
</div>
</template>

<style>
:root {
--color-text: #333;
--color-hint: #666;
--color-border: #ccc;
--color-focus: #0056b3;
--color-error: #f00;
--color-valid: #059669;
--color-hover: #eee;
}
</style>

<style scoped>
.radio-item {
display: inline-flex;
align-items: center;
border-radius: 99999px;
padding: 0.5rem 1rem;
border: 1px solid var(--color-border);
font-size: 13px;
font-weight: 500;
transition:
background-color 0.15s,
color 0.15s;
&:focus {
border: 1px solid var(--color-focus);
}
&[aria-checked='true'] {
background-color: var(--color-focus);
color: #fff;
}
}
/** This is a common utility CSS class, you can find it in your CSS framework of choice */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
</style>

0 comments on commit df23646

Please sign in to comment.