Skip to content

Commit

Permalink
feat: several improvements to the select API
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Aug 28, 2024
1 parent ec8a996 commit 5c9dfe1
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 18 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/a11y/useLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createRefCapture } from '../utils/common';
interface LabelProps {
for: MaybeRefOrGetter<string>;
label: MaybeRefOrGetter<Maybe<string>>;
targetRef?: MaybeRefOrGetter<HTMLElement | undefined>;
targetRef?: MaybeRefOrGetter<Maybe<HTMLElement>>;
handleClick?: () => void;
}

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/useSelect/useListBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { useKeyPressed } from '../helpers/useKeyPressed';
import { isMac } from '../utils/platform';
import { usePopoverController } from '../helpers/usePopoverController';

export interface ListBoxProps<TOption> {
options: TOption[];
export interface ListBoxProps {
multiple?: boolean;
orientation?: Orientation;

Expand Down Expand Up @@ -42,7 +41,7 @@ export interface ListManagerCtx<TOption = unknown> {
export const ListManagerKey: InjectionKey<ListManagerCtx> = Symbol('ListManagerKey');

export function useListBox<TOption, TValue = TOption>(
_props: Reactivify<ListBoxProps<TOption>>,
_props: Reactivify<ListBoxProps>,
elementRef?: Ref<Maybe<HTMLElement>>,
) {
const props = normalizeProps(_props);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/useSelect/useOptionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function useOptionGroup(_props: Reactivify<OptionGroupProps>, elementRef?
const { labelProps, labelledByProps } = useLabel({
label: props.label,
for: groupId,
targetRef: groupRef,
});

const groupProps = computed(() => {
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/useSelect/useSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export interface SelectProps<TOption, TValue = TOption> {
name?: string;
description?: string;

options: TOption[];
getValue?(option: TOption): TValue;
modelValue?: Arrayable<TValue>;

Expand Down
51 changes: 48 additions & 3 deletions packages/playground/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<template>
<div class="flex gap-4 relative p-8">
<form class="w-full">
<InputSelect name="select" label="Select Input" :options="options" :get-value="option => option.code">
<OptionItem v-for="option in options" :key="option.code" :option="option">{{ option.name }}</OptionItem>
</InputSelect>
<InputSelect name="select" label="Select Input" :get-value="option => option.code" :groups="continents" />

<!-- <div class="flex flex-col gap-4">-->
<!-- <InputText-->
Expand Down Expand Up @@ -152,6 +150,53 @@ const { values } = useForm({
const model = ref('');
const continents = [
{
label: 'Africa',
code: 'AF',
items: [
{ label: 'Egypt', code: 'EG' },
{ label: 'Nigeria', code: 'NG' },
{ label: 'South Africa', code: 'ZA' },
],
},
{
label: 'America',
code: 'AM',
items: [
{ label: 'United States', code: 'US' },
{ label: 'Canada', code: 'CA' },
{ label: 'Brazil', code: 'BR' },
],
},
{
label: 'Asia',
code: 'AS',
items: [
{ label: 'China', code: 'CN' },
{ label: 'Japan', code: 'JP' },
{ label: 'India', code: 'IN' },
],
},
{
label: 'Europe',
code: 'EU',
items: [
{ label: 'Germany', code: 'DE' },
{ label: 'France', code: 'FR' },
{ label: 'United Kingdom', code: 'UK' },
],
},
{
label: 'Oceania',
code: 'OC',
items: [
{ label: 'Australia', code: 'AU' },
{ label: 'New Zealand', code: 'NZ' },
],
},
];
const options = [
{ name: 'Egypt', code: 'EG' },
{ name: 'United States', code: 'US' },
Expand Down
36 changes: 33 additions & 3 deletions packages/playground/src/components/InputSelect.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
<script setup lang="ts" generic="TOption, TValue">
<script setup lang="ts" generic="TOption extends { label: string }, TValue">
import { useSelect, SelectProps } from '@formwerk/core';
import OptionItem from './OptionItem.vue';
import OptionGroup from './OptionGroup.vue';
const props = defineProps<SelectProps<TOption, TValue>>();
export interface TheProps<TOption, TValue> extends SelectProps<TOption, TValue> {
groups?: { items: TOption[]; label: string }[];
options?: TOption[];
}
const props = defineProps<TheProps<TOption, TValue>>();
const { triggerProps, labelProps, errorMessageProps, isTouched, displayError, fieldValue, listBoxProps } =
useSelect(props);
Expand All @@ -18,7 +25,29 @@ const { triggerProps, labelProps, errorMessageProps, isTouched, displayError, fi
</div>

<div v-bind="listBoxProps" popover class="listbox">
<slot />
<slot>
<template v-if="groups">
<OptionGroup v-for="group in groups" :key="group.label" :label="group.label">
<slot name="group" :options="group.items">
<OptionItem
v-for="(option, idx) in group.items"
:key="(getValue?.(option) as any) ?? idx"
:option="option"
>
<slot name="option" :option="option">
{{ option.label }}
</slot>
</OptionItem>
</slot>
</OptionGroup>
</template>

<template v-else-if="options">
<OptionItem v-for="(option, idx) in options" :key="(getValue?.(option) as any) ?? idx" :option="option">
<slot name="option" :option="option" />
</OptionItem>
</template>
</slot>
</div>

<span v-bind="errorMessageProps" class="error-message">
Expand Down Expand Up @@ -48,6 +77,7 @@ const { triggerProps, labelProps, errorMessageProps, isTouched, displayError, fi
.listbox {
margin: 0;
@apply p-0 max-h-[60vh] relative;
position-anchor: --trigger;
position-area: bottom;
inset-area: bottom;
Expand Down
21 changes: 21 additions & 0 deletions packages/playground/src/components/OptionGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div v-bind="groupProps" class="">
<p v-bind="labelProps" class="bg-gray-600 text-white px-2 py-1 select-none sticky top-0">{{ label }}</p>

<div class="px-1 flex flex-col gap-0.5 py-1">
<slot />
</div>
</div>
</template>

<script setup lang="ts">
import { OptionGroupProps, useOptionGroup } from '@formwerk/core';
interface TheProps extends OptionGroupProps {
label: string;
}
const props = defineProps<TheProps>();
const { labelProps, groupProps } = useOptionGroup(props);
</script>
17 changes: 10 additions & 7 deletions packages/playground/src/components/OptionItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div v-bind="optionProps" class="option">
<div v-bind="optionProps" class="option px-2 rounded-md py-0.5 border">
<slot />
</div>
</template>
Expand All @@ -13,12 +13,15 @@ const { optionProps } = useOption(props);
</script>

<style scoped>
.option:focus {
@apply bg-gray-200 outline-none;
}
.option {
@apply border border-transparent select-none;
&:focus {
@apply border-gray-400 outline-none;
}
[aria-selected='true'],
[aria-checked='true'] {
@apply bg-gray-700 text-white;
&[aria-selected='true'],
&[aria-checked='true'] {
@apply bg-blue-700 text-white;
}
}
</style>

0 comments on commit 5c9dfe1

Please sign in to comment.