Skip to content

Commit

Permalink
Support multiple in listbox
Browse files Browse the repository at this point in the history
  • Loading branch information
xinjie-zhang committed Dec 11, 2022
1 parent c5c6f9f commit 66822de
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 29 deletions.
55 changes: 26 additions & 29 deletions components/listbox.html
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
<template x-component:hui="listbox" x-modelable="value" x-data="{
value: null,
get disabled() {return $prop('disabled')}
get disabled() {return $prop('disabled')},
get multiple() {return $prop('multiple')}
}">

<input type="hidden" :name="$prop('name')" :value="$prop('value') || ''">

<slot></slot>
<script>
const _ = $vui._
return {
_elActive: null,
_elSelected: null,
_isOpen: false,
get compButton() {
return this.$findOne('listbox-button')
},
get compLabel() {
return this.$findOne('listbox-label')
},
get compOptions() {
return this.$findOne('listbox-options')
},
open() {
this._isOpen = true
$vui.focus(this.compOptions)
$vui.focus(this.$findOne('listbox-options'))
},
close() { this._isOpen = false },
get isOpen() { return this._isOpen },
isActive(el) {
return this._elActive === el
},
isSelected(el) {
return this._elSelected === el
isSelected(val) {
return this.multiple ? this.value && this.value.indexOf(val) !== -1 : this.value === val
},
activate(el) {
this._elActive = el
if (this._elActive) {
$vui.scrollIntoView(this._elActive)
}
},
get allOptions() {
return this.$find('listbox-option')
},
get enabledOptions() {
let options = []
this.allOptions.forEach(el => !$vui.$data(el).disabled && options.push(el))
return options
return _.filter(this.$find('listbox-option'), el => !$vui.$api(el).disabled)
},
get selectedElements() {
return _.filter(this.$find('listbox-option'), el => !$vui.$api(el).selected)
},
activateFirstOrSelected() {
if (this._elSelected) {
this.activate(this._elSelected)
let els = this.selectedElements
if (els.length > 0) {
this.activate(els[0])
} else {
this.activateFirst()
}
Expand Down Expand Up @@ -75,10 +69,13 @@
let count = elOptions.length
if (count > 0) this.activate(elOptions[count - 1])
},
select(el) {
if (!el) return
this._elSelected = el
this.value = $vui.$data(el).value
select(val) {
if (!this.multiple)
this.value = val
else if (_.isArray(this.value))
this.value.indexOf(val) === -1 && this.value.push(val)
else
this.value = [val]
},
selectActive() {
if (this._elActive) {
Expand All @@ -95,7 +92,7 @@
return {
focusButton() {
let context = this.$api.$of('listbox')
$vui.focus(context.compButton)
$vui.focus(context.$findOne('listbox-button'))
}
}
</script>
Expand Down Expand Up @@ -200,10 +197,10 @@
},
get selected() {
let context = this.context
return context && context.isSelected && context.isSelected(this.$el)
return context && context.isSelected && context.isSelected(this.value)
},
activate() { this.context.activate(this.$el) },
select() { this.context.select(this.$el) },
select() { this.context.select(this.value) },
close() { this.context.close() },
onSelect() {
if (!this.disabled) {
Expand Down
131 changes: 131 additions & 0 deletions examples/listbox/multi-select.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<head>
<!--
<script src="https://unpkg.com/@vimesh/style" defer></script>
<script src="https://unpkg.com/@vimesh/ui"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
-->

<script src="/node_modules/@vimesh/style/dist/vs.dev.js" defer></script>
<script src="/node_modules/@vimesh/ui/dist/vui.dev.js"></script>
<script src="/node_modules/alpinejs/dist/cdn.js" defer></script>

<script>
$vui.config.importMap = {
"*": '/components/${component}.html'
}
</script>
<style>
[x-cloak] {
display: none !important;
}
</style>

<script>

people = [
{ id: 1, name: 'Wade Cooper' },
{ id: 2, name: 'Arlene Mccoy' },
{ id: 3, name: 'Devon Webb' },
{ id: 4, name: 'Tom Cook' },
{ id: 5, name: 'Tanya Fox', disabled: true },
{ id: 6, name: 'Hellen Schmidt' },
{ id: 7, name: 'Caroline Schultz' },
{ id: 8, name: 'Mason Heaney', disabled: true },
{ id: 9, name: 'Claudie Smitham' },
{ id: 10, name: 'Emil Schaefer' },
]
let data = {
people,
activePersons: [people[1], people[9]],
removePerson(toRemove) {
this.activePersons = this.activePersons.filter((p) => p !== toRemove)
},
onSubmit(e) {
e.preventDefault()
console.log(this.activePersons)
console.log([...new FormData(e.currentTarget).entries()])
}
}
</script>
</head>

<body x-cloak x-import="hui/listbox" class="p-2" x-data="data">
<div class="flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12">
<div class="w-full max-w-4xl">
<div class="space-y-1">
<form @submit="onSubmit($event)">
<hui-listbox x-model="activePersons" name="people" multiple>
<hui-listbox-label class="block text-sm font-medium leading-5 text-gray-700">
Assigned to
</hui-listbox-label>

<div class="relative">
<span class="inline-block w-full rounded-md shadow-sm">
<hui-listbox-button
class="focus:shadow-outline-blue relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 focus:outline-none sm:text-sm sm:leading-5">
<span class="block flex flex-wrap gap-2">
<template x-if="activePersons.length === 0">
<span class="p-0.5">Empty</span>
</template>
<template x-for="person in activePersons" :key="person.id">
<span class="flex items-center gap-1 rounded bg-blue-50 px-2 py-0.5">
<span x-text="person.name"></span>
<svg class="h-4 w-4 cursor-pointer" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http:https://www.w3.org/2000/svg"
@click.stop.prevent="removePerson(person)">
<path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</span>
</template>

</span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="none"
stroke="currentColor">
<path d="M7 7l3-3 3 3m0 6l-3 3-3-3" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
</hui-listbox-button>
</span>

<div class="absolute mt-1 w-full rounded-md bg-white shadow-lg">
<hui-listbox-options
class="shadow-xs max-h-60 overflow-auto rounded-md py-1 text-base leading-6 focus:outline-none sm:text-sm sm:leading-5">
<template x-for="person in people" :key="person.id">
<hui-listbox-option :value="person" :disabled="person.disabled">
<li class="list-none relative cursor-default select-none py-2 pl-3 pr-9 focus:outline-none"
:class="[active ? 'bg-indigo-600 text-white' : 'text-gray-900',
disabled ? 'bg-gray-50 text-gray-300' : '']">
<span class="block truncate"
:class="{ 'font-semibold': selected, 'font-normal': !selected }"
x-text="person.name">
</span>
<template x-if="selected">
<span class="absolute inset-y-0 right-0 flex items-center pr-4"
:class="{ 'text-white': active, 'text-indigo-600': !active }">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd" />
</svg>
</span>
</template>
</li>
</hui-listbox-option>
</template>
</hui-listbox-options>
</div>
</div>
</hui-listbox>
<button
class="mt-2 inline-flex items-center rounded border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
Submit
</button>
</form>
</div>
</div>
</div>

</body>

0 comments on commit 66822de

Please sign in to comment.