Skip to content

Commit

Permalink
feat(radio): add radio component
Browse files Browse the repository at this point in the history
  • Loading branch information
stfsy committed Jul 24, 2022
1 parent 8df4429 commit 28d4966
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 1 deletion.
13 changes: 13 additions & 0 deletions docs/.vuepress/examples/FormInputRadio.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<FormInputRadio
id="demoRadio"
name="radio"
label="Label"
description="Showcases the radio element"
:options="[{ value: '1', label: 'First Element' }, { value: '2', label: 'Second Element' }]">
</FormInputRadio>
</template>

<script setup>
import { FormInputRadio } from '@discue/ui-components'
</script>
11 changes: 11 additions & 0 deletions docs/components/form-input-radio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# FormInputRadio <Badge type="tip" text="since v0.17.0" vertical="top" />

A radio button element wrapped inside a labelled form element. The label and input elements will be generated at runtime based on the `options` prop, which is expected to be an array. The expected format of the array items is `{ value: 'uniqueValueString', label: 'uniqueLabelString'}`. While the component was designed for mainly two or three options, it can host any number of options.

The element supports the Vue.js [v-model directive](https://vuejs.org/api/built-in-directives.html#v-model). On input the directive will be updated with the index of selected option.

## Preview
<DynamicComponentDisplay type="FormInputRadio" :attach-v-model="true" initial-value="2" id="demoRadio" name="radio" description="Showcases the radio element" label="Label" :options="[{value: '1', label:'First Element'}, {value: '2', label: 'Second Element'}]"></DynamicComponentDisplay>

## Example
@[code](@examples/FormInputRadio.vue)
116 changes: 116 additions & 0 deletions src/components/form-input-radio.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<template>
<FormElementContainerWithLabel :id="id" :input-invalid="invalid" :label="label" :description="error"
:focussed="isFocussed">
<div :class="wrapperClazz">
<div :class="optionClazz" v-for="option in options" :key="option.value">

<input :id="option.label + '_id'" autocomplete="off" type="radio"
:checked="option.default === true || modelValue == option.value" :required="required"
:name="name" :value="option.value"
class="hidden peer checked:bg-gray-900 rounded text-lg outline-none text-gray-100 placeholder:text-gray-300 py-2 px-3 leading-8"
@input="onInput($event)">

<label :for="option.label + '_id'" @focus="onFocus" @blur="onBlur"
class="flex flex-row items-center w-full space-x-2 px-3 py-1 leading-7 text-xl text-gray-900 peer-checked:text-gray-900 cursor-pointer">
<svg v-if="modelValue == option.value" xmlns="http://www.w3.org/2000/svg"
class="stroke-current h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" class="stroke-2 stroke-current h-6 w-6" fill="none"
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8 01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<Text :highlight="modelValue == option.value" :inherit-color="true">{{ option.label }}</Text>
</label>

</div>
</div>
</FormElementContainerWithLabel>
</template>

<script setup>
import { computed, ref } from 'vue';
import FormElementContainerWithLabel from './form-element-container-with-label.vue';
import Text from './text.vue';
const props = defineProps({
id: {
type: String,
},
name: {
type: String,
},
label: {
type: String,
},
invalidMessage: {
type: String,
},
invalid: {
type: Boolean,
},
description: {
type: String,
},
required: {
type: Boolean,
default: true,
},
modelValue: {
type: String,
},
options: {
type: Array,
},
vertical: {
type: Boolean,
default: false,
},
})
const emits = defineEmits(['update:modelValue'])
const isFocussed = ref(false)
const error = computed(() => {
return props.invalidMessage ? props.invalidMessage : props.description
})
const wrapperClazz = computed(() => {
const clazz = ['flex']
if (props.vertical) {
clazz.push('flex-row')
} else {
clazz.push('flex-col')
}
return clazz.join(' ')
})
const optionClazz = computed(() => {
const clazz = ['text-gray-100 flex flex-row items-center']
if (props.vertical) {
clazz.push('w-1/2')
} else {
clazz.push('w-full')
}
return clazz.join(' ')
})
function onInput(event) {
const value = event.target.value
emits('update:modelValue', value)
onFocus() // labels cannot be focused so we need to augment focussing and bluring for error message handling to work
}
function onFocus() {
isFocussed.value = true
setTimeout(onBlur, 1000) // labels cannot be focused so we need to augment focussing and bluring for error message handling to work
}
function onBlur() {
isFocussed.value = false
}
</script>
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { default as DropDownMenuBannerItem } from './components/drop-down-menu-b
export { default as DropDownMenuItem } from './components/drop-down-menu-item.vue';
export { default as DropDownMenu } from './components/drop-down-menu.vue';
export { default as FormElementErrorMessage } from './components/form-element-error-message.vue';
export { default as FormInputRadio } from './components/form-input-radio.vue';
export { default as FormInputSelect } from './components/form-input-select.vue';
export { default as Headlines } from './components/headlines.vue';
export { default as NavButton } from './components/nav-button.vue';
Expand Down
10 changes: 9 additions & 1 deletion styles/preview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,19 @@
@apply border-none;
}

label[for=demoSelect], select {
label[for=demoSelect],
label[for=demoRadio],
select {
@apply bg-stone-300;
}

option {
@apply bg-gray-50;
}

div {
div.relative {
height: 1rem;
}
}
}

0 comments on commit 28d4966

Please sign in to comment.