Skip to content

Commit

Permalink
Merge pull request #1525 from EnergySage/CED-1754
Browse files Browse the repository at this point in the history
CED-1754 Create v3 EsFormRadioCards
  • Loading branch information
tomleo authored Sep 18, 2024
2 parents 1bfc1f6 + 32c01d8 commit 3089749
Show file tree
Hide file tree
Showing 14 changed files with 759 additions and 105 deletions.
57 changes: 57 additions & 0 deletions es-ds-components/components/es-form-radio-card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script setup lang="ts">
import { useFocus } from '@vueuse/core';
const inputElem = useTemplateRef('input-elem');
const { focused: inputFocused } = useFocus(inputElem);
/* eslint-disable @typescript-eslint/no-explicit-any */
interface IProps {
id: string;
name: string;
value: any;
disabled?: boolean;
inline?: boolean;
displayName?: string;
}
const props = withDefaults(defineProps<IProps>(), {
disabled: false,
inline: false,
displayName: '',
});
// Need to define the implicit emit from v-model, so that it can also get fired
// from the label clicks
const emit = defineEmits(['update:modelValue']);
function handleRadioButtonClick() {
if (!props.disabled) {
emit('update:modelValue', props.value);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const model = defineModel<any>();
const isChecked = computed(() => props.value === model.value);
</script>

<template>
<label
class="es-form-radio-card es-card interactive w-100 btn btn-outline-primary"
:class="{ active: isChecked, disabled: props.disabled, focus: isChecked || inputFocused }"
@click="handleRadioButtonClick">
<input
:id="id"
ref="input-elem"
v-model="model"
:disabled="props.disabled"
type="radio"
name="props.name"
:value="props.value"
:checked="isChecked"
@click="handleRadioButtonClick" />
<slot>
{{ displayName }}
</slot>
</label>
</template>
78 changes: 78 additions & 0 deletions es-ds-components/components/es-form-radio-cards.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script setup lang="ts">
interface Option {
id: string;
text: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
disabled?: boolean;
inline?: boolean;
}
interface Props {
id: string;
label: string;
name?: string;
options?: Option[];
inline?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
modelValue?: any;
hasIcon?: boolean;
labelClass?: string;
labelSrOnly?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
name: '',
options: undefined,
inline: false,
modelValue: undefined,
hasIcon: false,
labelClass: 'font-size-h3',
labelSrOnly: false,
});
const emit = defineEmits(['update:model-value']);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function handleUpdate(value: any) {
emit('update:model-value', value);
}
</script>

<template>
<fieldset
:id="`${props.id}-fieldset`"
class="form-group">
<legend
:id="`${props.id}-legend`"
class="font-size-h1 font-weight-bolder mb-200 pb-0 text-dark"
:class="{ 'sr-only': props.labelSrOnly }"
tabindex="-1">
{{ props.label }}
</legend>
<div>
<div
:id="`${props.id}-radiogroup`"
role="radiogroup"
tabindex="-1"
class="es-form-radio-cards d-flex justify-content-center btn-group-vertical"
:class="{ 'has-icon': props.hasIcon }">
<slot :options="options">
<es-form-radio-card
v-for="option in options"
:id="option.id"
:key="option.value"
:disabled="option?.disabled || false"
:name="props.name"
:value="option.value"
:model-value="props.modelValue"
:inline="props.inline || false"
@update:model-value="handleUpdate">
<span :class="props.labelClass">
{{ option.text }}
</span>
</es-form-radio-card>
</slot>
</div>
</div>
</fieldset>
</template>
42 changes: 17 additions & 25 deletions es-ds-components/components/es-radio-button-group.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
<script setup lang="ts">
/*
Similar API to https://bootstrap-vue.org/docs/components/form-radio#component-reference
Only more restrained, and based on historical usage. Things like configurable
value field aren't supported as they weren't used downstream.
Similarly things like size are not implemented
*/
interface IOptions {
id: string;
text: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
Expand All @@ -21,41 +13,41 @@ interface IProps {
name?: string;
options?: IOptions[];
inline?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
modelValue?: any;
}
withDefaults(defineProps<IProps>(), {
const props = withDefaults(defineProps<IProps>(), {
inline: false,
name: '',
options: undefined,
modelValue: undefined,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const model = defineModel<any>();
defineEmits(['update:model-value']);
</script>

<template>
<fieldset
:id="id"
class="form-group">
<legend
:id="`legend-${id}`"
:id="`${props.id}-legend`"
class="font-size-100"
tabindex="-1">
{{ label }}
{{ props.label }}
</legend>
<template v-if="$slots.default">
<slot />
</template>
<template v-else>
<slot :options="options">
<es-radio-button
v-for="option in options"
:id="option.id"
:key="option.value"
v-model="model"
:disabled="option?.disabled || false"
:display-name="option?.text"
:group-name="name"
:inline="inline"
:value="option.value" />
</template>
:name="props.name"
:value="option.value"
:model-value="props.modelValue"
:inline="props.inline || false"
:display-name="option.text"
@update:model-value="$emit('update:model-value', option.value)" />
</slot>
</fieldset>
</template>
65 changes: 41 additions & 24 deletions es-ds-components/components/es-radio-button.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
<script setup lang="ts">
import RadioButton from 'primevue/radiobutton';
/* eslint-disable @typescript-eslint/no-explicit-any */
interface IProps {
name: string;
value: any;
id: string;
disabled?: boolean;
displayName: string;
groupName?: string;
inline?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
displayName?: string;
}
const props = withDefaults(defineProps<IProps>(), {
disabled: false,
displayName: '',
groupName: '',
inline: false,
value: null,
displayName: '',
});
// Need to define the implicit emit from v-model, so that it can also get fired
// from the label clicks
const emit = defineEmits(['update:modelValue']);
function handleRadioButtonClick() {
if (!props.disabled) {
emit('update:modelValue', props.value);
}
}
// Similar to the API of https://bootstrap-vue.org/docs/components/form-radio#changing-the-option-field-names
// the value can be a string, number, or simple object. Avoid using complex types in values.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const model = defineModel<any>();
const isChecked = computed(() => props.value === model.value);
</script>

<template>
<div :class="`custom-control custom-radio custom-control${inline ? '-inline' : ''}`">
<radio-button
<div
class="custom-control custom-radio"
:class="{ 'custom-control-inline': props.inline }">
<input
:id="id"
v-model="model"
:disabled="props.disabled"
type="radio"
:name="props.name"
class="custom-control-input"
input-class="custom-radio-input"
v-bind="$attrs"
:disabled="disabled"
:input-id="`${props.value}-${props.groupName}`"
:name="props.groupName"
:value="props.value" />
<slot>
<label
:for="`${props.value}-${props.groupName}`"
class="custom-control-label">
{{ props.displayName }}
</label>
</slot>
:value="props.value"
:checked="isChecked"
@click="handleRadioButtonClick" />
<label
class="custom-control-label"
:for="id"
@click="handleRadioButtonClick">
<slot>
{{ displayName }}
</slot>
</label>
</div>
</template>
1 change: 1 addition & 0 deletions es-ds-components/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default defineNuxtConfig({
'@vuelidate/core',
'@vuelidate/validators',
'html-truncate',
'@vueuse/core',
'primevue/accordion',
'primevue/accordiontab',
'primevue/breadcrumb',
Expand Down
Loading

0 comments on commit 3089749

Please sign in to comment.