Skip to content

Commit

Permalink
feat(NeTextInput): add isSearch prop, prefix & suffix slots (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
andre8244 authored Nov 6, 2024
1 parent 90eaffe commit 30aa720
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 6 deletions.
62 changes: 60 additions & 2 deletions src/components/NeTextInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
-->

<script setup lang="ts">
import { computed, ref, useAttrs } from 'vue'
import { computed, ref, useAttrs, useSlots } from 'vue'
import { v4 as uuidv4 } from 'uuid'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleExclamation as fasCircleExclamation } from '@fortawesome/free-solid-svg-icons'
import { faEye as fasEye } from '@fortawesome/free-solid-svg-icons'
import { faEyeSlash as fasEyeSlash } from '@fortawesome/free-solid-svg-icons'
import { faMagnifyingGlass, faXmark } from '@fortawesome/free-solid-svg-icons'

const props = defineProps({
label: {
Expand Down Expand Up @@ -40,6 +41,14 @@ const props = defineProps({
optional: {
type: Boolean
},
isSearch: {
type: Boolean,
default: false
},
clearSearchLabel: {
type: String,
default: 'Clear'
},
isPassword: {
type: Boolean,
default: false
Expand Down Expand Up @@ -73,6 +82,8 @@ defineOptions({
inheritAttrs: false
})

const slots = useSlots()

// add fontawesome icons
library.add(fasCircleExclamation)
library.add(fasEye)
Expand All @@ -92,7 +103,12 @@ let isPasswordVisible = ref(false)
const componentId = computed(() => (props.id ? props.id : uuidv4()))

const inputStyles = computed(() =>
[inputBaseStyle, props.invalidMessage ? inputInvalidStyle : inputValidStyle].join(' ')
[
inputBaseStyle,
props.invalidMessage ? inputInvalidStyle : inputValidStyle,
(slots.prefix || props.isSearch) && 'pl-10',
(slots.suffix || props.isSearch) && 'pr-10'
].join(' ')
)

const inputRef = ref()
Expand Down Expand Up @@ -130,6 +146,11 @@ function togglePasswordVisible() {
function focus() {
inputRef.value.focus()
}

function clearText() {
emit('update:modelValue', '')
focus()
}
</script>

<template>
Expand All @@ -148,6 +169,25 @@ function focus() {
<span v-if="optional" class="ml-2 font-normal">{{ optionalLabel }}</span>
</label>
<div class="relative rounded-md shadow-sm">
<!-- search icon -->
<div
v-if="isSearch"
class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"
>
<FontAwesomeIcon
:icon="faMagnifyingGlass"
class="h-4 w-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
/>
</div>
<!-- prefix -->
<div
v-else-if="$slots.prefix"
class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"
>
<slot name="prefix"></slot>
</div>
<!-- text input -->
<input
:id="componentId"
ref="inputRef"
Expand Down Expand Up @@ -189,6 +229,24 @@ function focus() {
aria-hidden="true"
/>
</div>
<!-- clear search icon -->
<div
v-else-if="isSearch && modelValue"
class="absolute inset-y-0 right-0 flex items-center pr-3"
>
<button type="button" :disabled="disabled" class="flex items-center" @click="clearText">
<span class="sr-only">{{ clearSearchLabel }}</span>
<FontAwesomeIcon
:icon="faXmark"
class="h-4 w-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
/>
</button>
</div>
<!-- suffix -->
<div v-else-if="$slots.suffix" class="absolute inset-y-0 right-0 flex items-center pr-3">
<slot name="suffix"></slot>
</div>
</div>
<!-- invalid message -->
<p
Expand Down
57 changes: 53 additions & 4 deletions stories/NeTextInput.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import type { Meta, StoryObj } from '@storybook/vue3'

import { NeTextInput, NeTooltip } from '../src/main'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faEnvelope, faDollarSign } from '@fortawesome/free-solid-svg-icons'

const meta = {
title: 'NeTextInput',
Expand All @@ -17,6 +19,8 @@ const meta = {
helperText: '',
invalidMessage: '',
optional: false,
isSearch: false,
clearSearchLabel: 'Clear',
disabled: false,
id: '',
isPassword: false,
Expand All @@ -29,7 +33,7 @@ const meta = {
export default meta
type Story = StoryObj<typeof meta>

const template = '<NeTextInput v-bind="args" class="max-w-md" />'
const template = '<NeTextInput v-bind="args" class="max-w-xs" />'

export const Default: Story = {
render: (args) => ({
Expand Down Expand Up @@ -96,10 +100,10 @@ export const Password: Story = {
},
template: template
}),
args: { isPassword: true, label: 'Enter password', placeholder: '' }
args: { isPassword: true, label: 'Enter password', placeholder: 'Current password' }
}

const typeNumberTemplate = '<NeTextInput v-bind="args" type="number" class="max-w-md" />'
const typeNumberTemplate = '<NeTextInput v-bind="args" type="number" class="max-w-xs" />'

export const TypeNumber: Story = {
render: (args) => ({
Expand All @@ -113,7 +117,7 @@ export const TypeNumber: Story = {
}

const templateWithTooltip =
'<NeTextInput v-bind="args" class="max-w-md">\
'<NeTextInput v-bind="args" class="max-w-xs">\
<template #tooltip>\
<NeTooltip>\
<template #content>Tooltip</template>\
Expand All @@ -131,3 +135,48 @@ export const WithTooltip: Story = {
}),
args: {}
}

const templateWithPrefix = `<NeTextInput v-bind="args" class="max-w-xs">
<template #prefix>
<FontAwesomeIcon :icon="faEnvelope" class="h-4 w-4 text-gray-500 dark:text-gray-400" aria-hidden="true" />
</template>
</NeTextInput>`

export const WithPrefix: Story = {
render: (args) => ({
components: { NeTextInput, FontAwesomeIcon },
setup() {
return { args, faEnvelope }
},
template: templateWithPrefix
}),
args: {}
}

const templateWithSuffix = `<NeTextInput v-bind="args" class="max-w-xs">
<template #suffix>
<FontAwesomeIcon :icon="faDollarSign" class="h-4 w-4 text-gray-500 dark:text-gray-400" aria-hidden="true" />
</template>
</NeTextInput>`

export const WithSuffix: Story = {
render: (args) => ({
components: { NeTextInput, FontAwesomeIcon },
setup() {
return { args, faDollarSign }
},
template: templateWithSuffix
}),
args: {}
}

export const Search: Story = {
render: (args) => ({
components: { NeTextInput },
setup() {
return { args }
},
template: template
}),
args: { isSearch: true }
}

0 comments on commit 30aa720

Please sign in to comment.