Skip to content

Commit

Permalink
feat(autocomplete): new autocomplete component
Browse files Browse the repository at this point in the history
  • Loading branch information
gravitano committed May 11, 2022
1 parent 3613471 commit aa26812
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 0 deletions.
43 changes: 43 additions & 0 deletions packages/autocomplete/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# GITS Autocomplete Component

Autocomplete Component.

## Installation

npm

```
npm i @gits-id/autocomplete
```

yarn

```
yarn add @gits-id/autocomplete
```

pnpm

```
pnpm i @gits-id/autocomplete
```

## Usage

```vue
<script setup lang="ts">
import Autocomplete from '@gits-id/autocomplete';
</script>
<template>
<Autocomplete />
</template>
```

## Documentation

View `Autocomplete` documentation [here](https://gits-ui.web.app/?path=/story/components-autocomplete--default).

## License

MIT
38 changes: 38 additions & 0 deletions packages/autocomplete/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@gits-id/autocomplete",
"version": "0.2.4",
"description": "GITS Autocomplete Component",
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly && mv dist/src dist/types",
"prepublishOnly": "npm run build",
"test": "vitest"
},
"keywords": [
"autocomplete",
"gits",
"ui-component"
],
"author": "",
"license": "ISC",
"dependencies": {
"@gits-id/button": "^0.2.4",
"@headlessui/vue": "^1.5.0",
"feather-icons": "^4.28.0",
"tailwindcss": "^3.0.23",
"vue": "^3.2.31",
"vue-feather": "^2.0.0",
"vue-remix-icons": "^1.1.1"
},
"devDependencies": {
"@gits-id/tailwind-config": "^0.2.4",
"@gits-id/utils": "^0.2.3",
"@vue/test-utils": "^2.0.0-rc.17",
"vitest": "^0.7.10"
},
"main": "dist/autocomplete.umd.js",
"unpkg": "dist/autocomplete.iife.js",
"jsdelivr": "dist/autocomplete.iife.js",
"module": "./dist/autocomplete.es.js",
"types": "./dist/types/index.d.ts",
"gitHead": "1c8786cbf03a24b464264d95d0d3a90812476412"
}
6 changes: 6 additions & 0 deletions packages/autocomplete/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
35 changes: 35 additions & 0 deletions packages/autocomplete/src/Autocomplete.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {ref} from 'vue';
import Autocomplete from './Autocomplete.vue';

const items = [
{value: 1, text: 'Wade Cooper'},
{value: 2, text: 'Arlene Mccoy'},
{value: 3, text: 'Devon Webb'},
{value: 4, text: 'Tom Cook'},
{value: 5, text: 'Tanya Fox'},
{value: 6, text: 'Hellen Schmidt'},
];

export default {
title: 'Components/Autocomplete',
component: Autocomplete,
argTypes: {},
args: {
items,
},
};

const Template = (args) => ({
components: {Autocomplete},
setup() {
const selected = ref();
return {args, selected};
},
template: `
<Autocomplete v-bind="args" v-model="selected"/>
Selected: {{ selected }}
`,
});

export const Default = Template.bind({});
Default.args = {};
235 changes: 235 additions & 0 deletions packages/autocomplete/src/Autocomplete.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
<script setup lang="ts">
import {ref, computed, toRefs, watch} from 'vue';
import {
Combobox,
ComboboxInput,
ComboboxButton,
ComboboxOptions,
ComboboxOption,
ComboboxLabel,
TransitionRoot,
} from '@headlessui/vue';
import {useField} from 'vee-validate';
import {
RiCheckLine as CheckIcon,
RiCloseLine as XIcon,
RiArrowDownSLine as ChevronDownIcon,
} from 'vue-remix-icons';
type Item = {
text: string;
value: string | number;
};
type Props = {
modelValue?: Item | string | null;
searchBy?: string;
displayText?: string;
placeholder?: string;
label?: string;
items: Item[];
name?: string;
rules?: string;
clearable?: boolean;
notFoundText?: string;
noDataText?: string;
};
const props = withDefaults(defineProps<Props>(), {
modelValue: undefined,
searchBy: 'text',
displayText: 'text',
placeholder: 'Search...',
label: '',
rules: '',
items: () => [],
noDataText: 'No data.',
notFoundText: 'Nothing found.',
});
const emit = defineEmits(['update:modelValue', 'update:query']);
const {modelValue, searchBy, items, name, rules} = toRefs(props);
const {value: selected, errorMessage} = useField(name, rules, {
initialValue: modelValue.value,
});
const query = ref('');
watch(modelValue, (val) => {
selected.value = val;
});
watch(selected, (val) => {
emit('update:modelValue', val);
});
watch(query, (val) => {
emit('update:query', val);
if (val === '') {
selected.value = '';
}
});
const filteredItems = computed(() =>
query.value === ''
? items.value
: items.value.filter((item) =>
item[searchBy.value]
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.value.toLowerCase().replace(/\s+/g, '')),
),
);
const clear = () => {
selected.value = '';
query.value = '';
};
</script>

<template>
<Combobox v-model="selected" class="mb-4" as="div">
<ComboboxLabel v-if="label" class="mb-2 font-medium">
{{ label }}
</ComboboxLabel>
<div class="relative mt-1">
<div
class="
relative
w-full
text-left
bg-white
border border-gray-400
rounded
cursor-default
focus:outline-none
focus-within:ring
focus-within:ring-primary-500
focus-within:border-primary-500
focus-within:ring-opacity-50
sm:text-sm
overflow-hidden
transition
duration-300
"
>
<ComboboxInput
class="
w-full
border-none
focus:ring-0
py-3
pl-3
pr-20
leading-5
text-gray-600
"
:display-value="(item) => item[displayText] || ''"
:placeholder="placeholder"
@change="query = $event.target.value"
/>
<div class="absolute inset-y-0 right-0 flex items-center pr-4">
<button
v-if="clearable"
type="button"
class="
mr-1
text-gray-400
hover:text-gray-700
hover:bg-gray-200
rounded-full
p-1
transition
duration-300
"
@click="clear"
>
<XIcon class="w-6 h-6" aria-hidden="true" />
</button>
<ComboboxButton>
<ChevronDownIcon
class="w-6 h-6 fill-current text-gray-500"
aria-hidden="true"
/>
</ComboboxButton>
</div>
</div>
<TransitionRoot
leave="transition ease-in duration-100"
leave-from="opacity-100"
leave-to="opacity-0"
@after-leave="query = ''"
>
<ComboboxOptions
class="
absolute
z-10
w-full
py-1
mt-1
overflow-auto
text-base
bg-white
rounded-md
shadow-lg
max-h-60
ring-1 ring-black ring-opacity-5
focus:outline-none
sm:text-sm
"
>
<div
v-if="filteredItems.length === 0 && query === ''"
class="cursor-default select-none relative py-2 px-4 text-gray-700"
>
{{ noDataText }}
</div>

<div
v-if="filteredItems.length === 0 && query !== ''"
class="cursor-default select-none relative py-2 px-4 text-gray-700"
>
{{ notFoundText }}
</div>

<ComboboxOption
v-for="(item, idx) in filteredItems"
:key="idx"
v-slot="{selected, active}"
as="template"
:value="item"
>
<li
class="cursor-default select-none relative py-2 pl-10 pr-4"
:class="{
'bg-gray-100': active,
'text-gray-900': !active,
}"
>
<span
class="block truncate"
:class="{
'font-medium text-primary-500': selected,
'font-normal': !selected,
}"
>
{{ item[displayText] }}
</span>
<span
v-if="selected"
class="absolute inset-y-0 left-0 flex items-center pl-3"
>
<CheckIcon
class="w-5 h-5 fill-current text-primary-500"
aria-hidden="true"
/>
</span>
</li>
</ComboboxOption>
</ComboboxOptions>
</TransitionRoot>
</div>
</Combobox>
<div class="text-error text-sm mt-1">{{ errorMessage }}</div>
</template>
6 changes: 6 additions & 0 deletions packages/autocomplete/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
content: [
'./src/**/*.{vue,js,ts,jsx,tsx}',
],
presets: [require('@gits-id/tailwind-config/preset')],
};
21 changes: 21 additions & 0 deletions packages/autocomplete/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"declaration": true,
"sourceMap": false,
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"allowJs": true,
"strict": true,
"noUnusedLocals": true,
"rootDir": ".",
"skipLibCheck": true,
"types": ["vite/client"],
"emitDeclarationOnly": true,
"allowSyntheticDefaultImports": true
},
"include": ["vue.d.ts", "*.vue", "src"],
"exclude": ["**/*.stories.ts", "**/*.spec.ts", "**/*.test.ts"]
}
Loading

0 comments on commit aa26812

Please sign in to comment.