Skip to content

Instantly share code, notes, and snippets.

@ricvillagrana
Last active March 22, 2023 07:02
Show Gist options
  • Save ricvillagrana/6c3224ad22e6c78274ffaf5eab78bfac to your computer and use it in GitHub Desktop.
Save ricvillagrana/6c3224ad22e6c78274ffaf5eab78bfac to your computer and use it in GitHub Desktop.
<div x-data="{ open: false, filter: '', selected: <%= @options.select { |option| @form.object.send(@field).any?(option[1]) } %>, options: <%= @options %> }" class="flex flex-col">
<div class="flex flex-row justify-between">
<label for="<%= @field %>">
<%= @label %>
</label>
<button type="button" class="link text-sm" @click="selected = []">
Clear <%= @label.downcase %>
</button>
</div>
<div class="relative">
<template x-if="selected.length > 0">
<div class="flex flex-wrap gap-1 my-2">
<template x-for="option in selected">
<div class="flex flex-row items-center gap-1 badge bg-yellow-300 text-md cursor-pointer" @click="selected = selected.filter(current => current[1] !== option[1])">
<input type="hidden" name="<%= "#{@form.object_name}[#{@field}][]" %>" :value="option[1]" />
<span x-text="option[0]"></span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.72 6.97a.75.75 0 10-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 101.06 1.06L12 13.06l1.72 1.72a.75.75 0 101.06-1.06L13.06 12l1.72-1.72a.75.75 0 10-1.06-1.06L12 10.94l-1.72-1.72z" clip-rule="evenodd" />
</svg>
</div>
</template>
</div>
</template>
<div @click.away="open = false">
<div class="relative">
<input
x-ref="search"
x-model="filter"
@click="open = true"
class="w-full form-input pr-6"
:class="{ '!app-shadow': open }"
placeholder="Search and select options"
@keydown.arrow-down.prevent="$refs.option_0.focus()"
@keydown.escape="open = false; filter = ''"
@keydown.enter.prevent="if (filter !== '') selected.push(options.filter(option => option[1].includes(filter.toLowerCase()))[0]); filter = ''"
/>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer" @click="open = false">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M5.47 5.47a.75.75 0 011.06 0L12 10.94l5.47-5.47a.75.75 0 111.06 1.06L13.06 12l5.47 5.47a.75.75 0 11-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 01-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 010-1.06z" clip-rule="evenodd" />
</svg>
</div>
</div>
<div
x-show="open"
class="absolute z-10 w-full mt-2 bg-white app-shadow max-h-96 overflow-y-auto"
>
<div class="flex flex-wrap <%= @row ? 'flex-row' : 'flex-col' %> p-2">
<template x-for="(option, index) in options.filter(option => option[1].includes(filter.toLowerCase()))" :key="index">
<div
:x-ref="'option_' + index"
tabindex="0"
@keydown="/^[a-zA-Z]+$/.test(event.key) ? $refs.search.focus() : null"
@keydown.enter.prevent="e => e.target.click()"
@keydown.space.prevent="e => e.target.click()"
@keydown.arrow-up.prevent="e => e.target.previousElementSibling.focus()"
@keydown.arrow-down.prevent="e => e.target.nextElementSibling.focus()"
@keydown.arrow-left.prevent="e => e.target.previousElementSibling.focus()"
@keydown.arrow-right.prevent="e => e.target.nextElementSibling.focus()"
@keydown.tab.prevent="e => e.target.nextElementSibling.focus()"
@keydown.shift.tab.prevent="e => e.target.previousElementSibling.focus()"
:class="{ 'bg-yellow-300': selected.some(item => item[1] === option[1]) }"
@click="if (selected.some(item => item[1] === option[1])) { selected = selected.filter(item => item[1] !== option[1]) } else { selected.push(option) }"
class="p-2 mr-2 mb-2 cursor-pointer hover:bg-yellow-300"
>
<span x-text="option[0]"></span>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment