Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tw): init listbox component #4421

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
319bba3
feat: added tw classes for listbox
davidruvolo51 Oct 31, 2024
209e2d9
initial listbox component
davidruvolo51 Oct 31, 2024
4bf9393
revised listbox classes
davidruvolo51 Nov 1, 2024
42d7c8e
feat: separated elements into child components
davidruvolo51 Nov 1, 2024
f05673f
temp: reverted to single component
davidruvolo51 Nov 5, 2024
b09d82a
finalised structure and added initial interactive events
davidruvolo51 Nov 8, 2024
be06742
feat: added more events; reorganised binding of events
davidruvolo51 Nov 8, 2024
97be2b7
updated example for testing interactivity
davidruvolo51 Nov 8, 2024
3ddd24a
ran formatter; added missing files
davidruvolo51 Nov 8, 2024
6751bfd
fix: added missing base style
davidruvolo51 Nov 11, 2024
2afd5b5
feat: added/fixed keyboard events
davidruvolo51 Nov 11, 2024
f2839b6
fix: adjusted element identifier pattern
davidruvolo51 Nov 12, 2024
98f3667
feat: added e2e test
davidruvolo51 Nov 12, 2024
a3bbef7
chore: merged master
davidruvolo51 Nov 12, 2024
b3f5505
feat: added additional tw styles; added keyboard prevents
davidruvolo51 Nov 13, 2024
1055d91
fix: corrected input types
davidruvolo51 Nov 18, 2024
21010e9
chore: finialised story
davidruvolo51 Nov 18, 2024
4db42b0
fix: set selected value focus when menu opens
davidruvolo51 Nov 19, 2024
76c9204
docs: added option to switch data types in story
davidruvolo51 Nov 19, 2024
809be83
Merge branch 'master' into feat/tw-listbox
connoratrug Nov 20, 2024
b008137
fix: when setting isOidcEnabled is false override env variable (#4403)
harmbrugge Nov 21, 2024
ffca17f
build: reduce preview resource requirements (#4507)
mswertz Nov 21, 2024
63fdd73
fix(catalogue): add UMCG template loader (#4466)
BrendaHijmans Nov 21, 2024
fbc31c0
fixed example hyperlinks to be valid (#4508)
svandenhoek Nov 21, 2024
97b0e5d
feat(directory): molgenis-emx2-directory-client (#3863)
dtroelofsprins Nov 21, 2024
c3bd9b0
feat(RDF): Every `ColumnType` should now be correctly converted to RD…
svandenhoek Nov 21, 2024
e9a3fcf
Add combined networks to biobanks (#4512)
hslh Nov 22, 2024
ba25da8
feat: added display for input data
davidruvolo51 Nov 26, 2024
f2d3221
fix: consolidated functions; implemented other minor changes from review
davidruvolo51 Nov 26, 2024
eee88e8
Merge branch 'master' into feat/tw-listbox
chinook25 Dec 2, 2024
99ee1da
fix: minor revisions from review
davidruvolo51 Dec 5, 2024
d4f8aee
feat: added events; added auto selection on load; updated story; mino…
davidruvolo51 Dec 5, 2024
623c311
fix: added check for modelValue type
davidruvolo51 Dec 6, 2024
660cf3a
feat: initial form connection
davidruvolo51 Dec 10, 2024
2617d03
feat: added FormField properties and methods; fixed: null value handl…
davidruvolo51 Dec 11, 2024
9a60d03
feat: added listbox as field input; improved links between text eleme…
davidruvolo51 Dec 11, 2024
f669076
minor revisions post review
davidruvolo51 Dec 11, 2024
8d0aedf
fix: adjusted error typing
davidruvolo51 Dec 11, 2024
65f262e
fix: selected form properly updates display; expanded listbox display…
davidruvolo51 Dec 11, 2024
f2a9ee2
fix: adjusted test as the placeholder option is the default
davidruvolo51 Dec 11, 2024
5425258
fix: adjusted component events
davidruvolo51 Dec 12, 2024
2918559
merged master; fixed tw styles
davidruvolo51 Jan 16, 2025
3544ea8
merged master
davidruvolo51 Jan 20, 2025
fa01c1a
feat: isolated elements into components
davidruvolo51 Jan 20, 2025
a5b4742
created child listbox components
davidruvolo51 Jan 22, 2025
18a7678
merged listbox v2 branch; changed typing
davidruvolo51 Jan 22, 2025
ea406b8
removed file
davidruvolo51 Jan 22, 2025
d45100b
feat: improved ref handling; simplified typing;
davidruvolo51 Jan 22, 2025
90ec103
ran formatter
davidruvolo51 Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/tailwind-components/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
--background-color-invalid: var(--color-red-200);
--background-color-input: var(--color-white);
--background-color-input-checked: var(--color-yellow-500);
--background-color-listbox: var(--color-white);
--background-color-listbox-hover: var(--color-gray-100);
--background-color-listbox-selected: var(--color-umcg-light-blue);
davidruvolo51 marked this conversation as resolved.
Show resolved Hide resolved

--text-color-button-primary: var(--color-gray-900);
--text-color-button-primary-hover: var(--color-gray-900);
Expand Down Expand Up @@ -143,6 +146,10 @@
--text-color-pagination-input: var(--color-blue-800);
--text-color-pagination-hover: var(--color-white);
--text-color-footer-link: var(--color-yellow-500);
--text-color-listbox: var(--color-black);
--text-color-listbox-hover: var(--color-black);
--text-color-listbox-selected: var(--color-blue-50);
--text-color-button-input-toggle: var(--color-black);

--text-color-invalid: var(--color-red-500);
--text-color-valid: var(--color-green-800);
Expand All @@ -168,6 +175,7 @@
--border-color-pagination: var(--color-transparent);
--border-color-input: var(--color-transparent);
--border-color-input-inverted: var(--color-gray-600);
--border-color-listbox-option: var(--color-gray-100);

--border-radius-3px: 3px;
--border-radius-50px: 50px;
Expand Down
154 changes: 154 additions & 0 deletions apps/tailwind-components/components/input/Listbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<template>
<ul class="mb-6">
<li>selection: {{ modelValue }}</li>
<li>focus index: {{ focusIndex }}</li>
</ul>

<div class="w-full relative">
<InputListboxToggle
:id="`listbox-${id}-toggle`"
:value="modelValue?.value"
:label="modelValue?.label"
:placeholder="placeholder"
:controls="`listbox-${id}-options`"
:required="required"
:hasError="hasError"
@click="openCloseListbox"
/>

<ul
:id="`listbox-${id}-options`"
role="listbox"
tabindex="0"
ref="listboxList"
:aria-expanded="listboxIsExpanded"
:aria-activedescendant="modelValue?.elemId"
class="absolute b-0 w-full z-10 bg-listbox"
:class="{
hidden: !listboxIsExpanded,
}"
@keydown="onKeyboardEvent"
>
<li
v-for="(option, index) in listboxOptions"
:ref="listboxOptionRefs.set"
role="option"
:id="`listbox-${id}-options-${index}`"
class="flex justify-start items-center gap-3 pl-3 py-1 text-listbox border-t-[1px] border-t-listbox-option cursor-pointer focus:bg-listbox-hover focus:text-listbox hover:bg-listbox-hover hover:text-listbox"
:class="{
'!bg-listbox-selected !text-listbox-selected':
option.value === modelValue?.value,
}"
@click="onListOptionClick(option)"
@focus="console.log(option.value, ' is focused')"
>
<BaseIcon
name="Check"
class="fill-listbox-selected"
:class="option.value === modelValue?.value ? 'visible' : 'invisible'"
:width="18"
/>
<span v-if="option.label">
{{ option.label }}
</span>
<span v-else>
{{ option.value }}
</span>
</li>
<!-- <ListboxListItem
v-for="(option, index) in listboxOptions"
:key="index"
:ref="listboxOptionRefs.set"
:listbox-id="`listbox-${id}-options`"
:elem-id="`listbox-${id}-options-${index}`"
:value="option.value"
:label="option.label"
:selected="option.value === modelValue?.value"
@update:model-value="onSelection"
/> -->
</ul>
</div>
</template>

<script lang="ts" setup>
import { useTemplateRefsList } from "@vueuse/core";
import { ref, useTemplateRef } from "vue";

interface IListboxOption {
elemId?: string;
value: string | number | boolean;
label?: string;
selected?: boolean;
}

const props = withDefaults(
defineProps<{
id: string;
listboxLabelId: string;
listboxOptions: IListboxOption[];
required?: boolean;
hasError?: boolean;
placeholder?: string;
}>(),
{
required: false,
hasError: false,
placeholder: "Select an option",
}
);

const modelValue = defineModel<IListboxOption>();
const listboxIsExpanded = ref<boolean>(false);
davidruvolo51 marked this conversation as resolved.
Show resolved Hide resolved
const focusIndex = ref<number>(0);
const listboxListRef = useTemplateRef<HTMLUListElement>("listboxList");
const listboxOptionRefs = useTemplateRefsList();

function focusFirstElement() {
(listboxOptionRefs.value[0] as HTMLOptionElement).focus();
}

function openCloseListbox() {
listboxIsExpanded.value = !listboxIsExpanded.value;
if (listboxIsExpanded.value) {
console.log(listboxListRef.value);
listboxListRef.value?.focus();
focusFirstElement();
}
}

function onListOptionClick(data: IListboxOption) {
modelValue.value = data;
openCloseListbox();
}

function onKeyboardEvent(event: KeyboardEvent) {
const key = event.key;
if (key === "ArrowDown") {
event.preventDefault();
const newIndexValue: number = focusIndex.value + 1;
if (newIndexValue > props.listboxOptions.length - 1) {
focusIndex.value = props.listboxOptions.length - 1;
} else {
focusIndex.value = newIndexValue;
}
}
if (key === "ArrowUp") {
event.preventDefault();
console.log("up arrow pressed");
const newIndexValue: number = focusIndex.value - 1;
if (newIndexValue < 0) {
focusIndex.value = 0;
} else {
focusIndex.value = newIndexValue;
}
}

if (key === "Enter") {
console.log("new selection");
}

if (key === "Spacebar" || key === "" || key === " ") {
event.preventDefault();
}
}
</script>
27 changes: 27 additions & 0 deletions apps/tailwind-components/components/input/Listbox/List.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<ul
:id="id"
role="listbox"
tabindex="0"
:aria-expanded="expanded"
:aria-activedescendant="selectedElemId"
class="absolute b-0 w-full z-10 bg-listbox"
:class="{
hidden: !expanded,
}"
>
<slot></slot>
</ul>
</template>

<script lang="ts" setup>
interface IListbox {
id: string;
expanded: boolean;
selectedElemId?: string;
}

withDefaults(defineProps<IListbox>(), {
expanded: false,
});
</script>
77 changes: 77 additions & 0 deletions apps/tailwind-components/components/input/Listbox/ListItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<template>
<li
role="option"
tabindex="0"
:aria-selected="selected"
class="flex justify-start items-center gap-3 pl-3 py-1 text-listbox border-t-[1px] border-t-listbox-option cursor-pointer"
:class="{
'bg-listbox-selected text-listbox-selected': selected,
'hover:bg-listbox-hover hover:text-listbox focus:bg-listbox-hover focus:text-listbox':
!selected,
}"
@click="emitSelection()"
>
<BaseIcon
name="Check"
class="fill-listbox-selected"
:class="selected ? 'visible' : 'invisible'"
:width="18"
/>
<span v-if="label">
{{ label }}
</span>
<span v-else>
{{ value }}
</span>
</li>
</template>

<script lang="ts" setup>
import { ref } from "vue";

interface IListBoxItemData {
value: string | number | boolean;
label?: string;
}

interface IListboxItem extends IListBoxItemData {
elemId: string;
selected?: boolean;
å;
}

const props = withDefaults(defineProps<IListboxItem>(), {
selected: false,
});

const modelValue = ref<IListboxItem>({
elemId: props.elemId,
value: props.value,
label: props.label,
});

const emit = defineEmits<{
(e: "update:modelValue", option: IListBoxItemData): void;
}>();

function emitSelection() {
emit("update:modelValue", modelValue.value);
}

// function onKeyboardEvent (event: KeyboardEvent) {
// const key = event.key;
// if (key === 'ArrowDown') {
// event.preventDefault();
// console.log('down arrow pressed');
// }
// if (key === 'ArrowUp') {
// event.preventDefault();
// console.log('up arrow pressed');
// }

// if (key === "Enter") {
// console.log("new selection")
// emitSelection();
// }
// }
</script>
54 changes: 54 additions & 0 deletions apps/tailwind-components/components/input/Listbox/Toggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<button
:id="id"
aria-haspopup="listbox"
:aria-controls="controls"
:aria-expanded="expanded"
:aria-required="required"
class="flex justify-start items-center h-10 w-full text-left pl-10 border-input bg-input text-button-input-toggle"
@click="onClick"
>
<span class="w-full" v-if="label">
{{ label }}
</span>
<span class="w-full" v-else-if="value">
{{ value }}
</span>
<span class="w-full" v-else>
{{ placeholder }}
</span>
<div class="w-[60px] flex flex-col">
<BaseIcon :width="18" name="caret-up" class="mx-auto -my-1" />
<BaseIcon :width="18" name="caret-down" class="mx-auto -my-1" />
</div>
</button>
</template>

<script lang="ts" setup>
import { ref } from "vue";

interface IInputToggle {
id: string;
value?: string | number | boolean;
label?: string;
placeholder?: string;
controls: string;
required: boolean;
hasError: boolean;
}

withDefaults(defineProps<IInputToggle>(), {
placeholder: "Select an option",
});

const expanded = ref<boolean>(false);

const emit = defineEmits<{
(e: "click", value: boolean): void;
}>();

function onClick() {
expanded.value = !expanded.value;
emit("click", expanded.value);
}
</script>
33 changes: 33 additions & 0 deletions apps/tailwind-components/pages/input/Listbox.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<InputLabel id="pizza-toppings-label" class="block mb-3">
<span>Which pizza topping is you favorite?</span>
</InputLabel>
<InputListbox
id="pizza-toppings"
listboxLabelId="pizza-toppings-label"
:listboxOptions="[
{
value: 'tomatoes',
label: 'Roma tomatoes',
},
{
value: 'pepperoni',
label: 'Pepperoni',
},
{
value: 'mozzerella',
label: 'Fresh mozzerella',
},
{
value: 'chillies',
label: 'Chillies',
},
{
value: 'basil',
label: 'Fresh basil',
},
]"
/>
</template>

<script lang="ts" setup></script>
Loading