Skip to content

Instantly share code, notes, and snippets.

@gokhantaskan
Last active August 11, 2023 06:49
Show Gist options
  • Save gokhantaskan/b4fb78ee1c5e9795fb524601c9dd5759 to your computer and use it in GitHub Desktop.
Save gokhantaskan/b4fb78ee1c5e9795fb524601c9dd5759 to your computer and use it in GitHub Desktop.
Basic fake radio group with buttons - experimental
...
<template>
...
<RadioGroup
v-model="value"
@option-click="next" // or @change
>
<template
v-for="(item, i) in isOwnedData"
:key="i"
>
<RadioGroupOption
:class="['aria-checked:text-primary']"
:value="item.value"
>
{{ item.label }}
</RadioGroupOption>
</template>
</RadioGroup>
...
</template>
import type { InjectionKey, Ref } from "vue";
export type RadioGroupOptionValue = NonNullable<unknown>;
export type UpdateModelValueFn = (value: RadioGroupOptionValue) => void;
export type OptionClickFn = (value: RadioGroupOptionValue) => void;
export const radioGroupStateInjectionKey = Symbol(
"radioGroupState"
) as InjectionKey<{
modelValue: Readonly<Ref<RadioGroupOptionValue>>;
updateModelValue: UpdateModelValueFn;
onOptionClick: OptionClickFn;
}>;
<script setup lang="ts">
import { type RadioGroupOptionValue, radioGroupStateInjectionKey } from ".";
const modelValue = defineModel<RadioGroupOptionValue>("modelValue", {
required: true,
});
const emit = defineEmits<{
change: [RadioGroupOptionValue];
optionClick: [RadioGroupOptionValue];
}>();
watch(modelValue, modelValue => emit("change", modelValue));
provide(radioGroupStateInjectionKey, {
modelValue: readonly(modelValue),
updateModelValue(value) {
modelValue.value = value;
},
onOptionClick(value) {
emit("optionClick", value);
},
});
</script>
<template>
<div
class="c-radio-group"
role="radiogroup"
>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { type RadioGroupOptionValue, radioGroupStateInjectionKey } from ".";
const { value, as = "button" } = defineProps<{
value: RadioGroupOptionValue;
as?: "button" | "div" | "span";
}>();
defineSlots<{
default?: { checked: boolean; click: (e: Event) => void }; // eslint-disable-line
}>();
const radioGroupState = inject(radioGroupStateInjectionKey);
const isSelected = computed(() => radioGroupState?.modelValue.value === value);
const attributes = ref({
role: "radio",
"aria-checked": isSelected,
...(as === "button" ? { type: "button" } : { tabindex: 0 }),
...useAttrs(),
});
function onClick() {
radioGroupState?.onOptionClick(value);
radioGroupState?.updateModelValue(value);
}
</script>
<template>
<component
:is="as"
class="c-radio-group-option"
v-bind="attributes"
@click="onClick"
>
<slot :checked="isSelected"></slot>
</component>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment