feat: add input component

This commit is contained in:
Alexandr
2022-08-03 14:55:16 +03:00
parent 96036c27e8
commit d3921ad9df
18 changed files with 590 additions and 380 deletions

View File

@@ -15,6 +15,13 @@ function buildSidebar() {
...getComponents(), ...getComponents(),
], ],
}, },
{
text: 'Form',
collapsible: true,
items: [
...getFormComponents(),
],
},
{ {
text: 'Utils', text: 'Utils',
collapsible: true, collapsible: true,
@@ -55,6 +62,12 @@ function getComponents() {
] ]
} }
function getFormComponents() {
return [
{ text: 'Input', link: 'components/input/input.md' },
]
}
function getUtils() { function getUtils() {
return [ return [
{ text: 'Flowbite Themable', link: '/components/flowbiteThemable/flowbiteThemable.md' }, { text: 'Flowbite Themable', link: '/components/flowbiteThemable/flowbiteThemable.md' },

View File

@@ -0,0 +1,6 @@
<template>
<Input :disabled="true" placeholder="enter your first name" label="First name" />
</template>
<script lang="ts" setup>
import { Input } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,6 @@
<template>
<Input placeholder="enter your first name" label="First name" />
</template>
<script lang="ts" setup>
import { Input } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,12 @@
<template>
<div class="vp-raw">
<Input placeholder="enter your first name" label="First name">
<template #helper>
Well never share your details. Read our <a href="#" class="font-medium text-blue-600 hover:underline dark:text-blue-500">Privacy Policy</a>.
</template>
</Input>
</div>
</template>
<script lang="ts" setup>
import { Input } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,12 @@
<template>
<div class="vp-raw">
<Input placeholder="enter your search query" label="Search">
<template #prefix>
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</template>
</Input>
</div>
</template>
<script lang="ts" setup>
import { Input } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,10 @@
<template>
<div class="vp-raw flex flex-col align-center gap-2 flex-wrap">
<Input size="sm" placeholder="enter your first name" label="Small" />
<Input size="md" placeholder="enter your last name" label="Medium" />
<Input size="lg" placeholder="enter your second name" label="Large" />
</div>
</template>
<script lang="ts" setup>
import { Input } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,15 @@
<template>
<div class="vp-raw">
<Input size="lg" placeholder="enter your search query" label="Search">
<template #prefix>
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</template>
<template #suffix>
<Button>Search</Button>
</template>
</Input>
</div>
</template>
<script lang="ts" setup>
import { Input, Button } from '../../../../src/index'
</script>

View File

@@ -0,0 +1,112 @@
<script setup>
import InputExample from './examples/InputExample.vue';
import InputSizeExample from './examples/InputSizeExample.vue';
import InputDisabledExample from './examples/InputDisabledExample.vue';
import InputHelperExample from './examples/InputHelperExample.vue';
import InputPrefixExample from './examples/InputPrefixExample.vue';
import InputSuffixExample from './examples/InputSuffixExample.vue'
</script>
# Input
#### Get started with a collection of input fields built with Tailwind CSS to start accepting data from the user based on multiple sizes, variants, and input types
---
:::tip
Original reference: [https://flowbite.com/docs/forms/input-field/](https://flowbite.com/docs/forms/input-field/)
:::
The input field is an important part of the form element that can be used to create interactive controls to accept data from the user based on multiple input types, such as text, email, number, password, URL, phone number, and more.
On this page you will find all of the input types based on multiple variants, styles, colors, and sizes built with the utility classes from Tailwind CSS and components from Flowbite.
## Default
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input placeholder="enter your first name" label="First name" />
</template>
```
<InputExample />
## Size
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input size="sm" placeholder="enter your first name" label="Small" />
<Input size="md" placeholder="enter your last name" label="Medium" />
<Input size="lg" placeholder="enter your second name" label="Large" />
</template>
```
<InputSizeExample />
## Disabled
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input :disabled="true" placeholder="enter your first name" label="First name" />
</template>
```
<InputDisabledExample />
## Slot - Helper
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input placeholder="enter your first name" label="First name">
<template #helper>
Well never share your details. Read our <a href="#" class="font-medium text-blue-600 hover:underline dark:text-blue-500">Privacy Policy</a>.
</template>
</Input>
</template>
```
<InputHelperExample />
## Slot - Prefix
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input placeholder="enter your first name" label="First name">
<template #prefix>
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</template>
</Input>
</template>
```
<InputPrefixExample />
## Slot - Suffix
```vue
<script setup>
import { Input } from 'flowbite-vue'
</script>
<template>
<Input size="lg" placeholder="enter your search query" label="Search">
<template #prefix>
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</template>
<template #suffix>
<Button>Search</Button>
</template>
</Input>
</template>
```
<InputSuffixExample />

650
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "flowbite-vue", "name": "flowbite-vue",
"version": "0.0.4", "version": "0.0.5",
"repository": "https://github.com/themesberg/flowbite-vue.git", "repository": "https://github.com/themesberg/flowbite-vue.git",
"author": "themesberg", "author": "themesberg",
"license": "MIT", "license": "MIT",
@@ -22,9 +22,10 @@
"start": "vitepress serve docs --port $PORT", "start": "vitepress serve docs --port $PORT",
"build": "vitepress build docs && cp -r ./docs/assets/* ./docs/.vitepress/dist/assets/", "build": "vitepress build docs && cp -r ./docs/assets/* ./docs/.vitepress/dist/assets/",
"build:package": "vite build", "build:package": "vite build",
"build:types": "vue-tsc --declaration --emitDeclarationOnly", "build:types": "vue-tsc --declaration --emitDeclarationOnly && tsc-alias -p tsconfig.json",
"build:production": "npm run build:types && npm run build:package", "build:production": "npm run build:types && npm run build:package",
"lint": "eslint --ext .ts,.vue --ignore-path .gitignore --fix src", "lint": "eslint --ext .ts,.vue --ignore-path .gitignore --fix src",
"clear": "rm -fr ./dist && rm -fr ./dist_types",
"format": "prettier . --write", "format": "prettier . --write",
"test": "vitest", "test": "vitest",
"coverage": "vitest run --coverage", "coverage": "vitest run --coverage",
@@ -53,6 +54,7 @@
"postcss-prefix-selector": "^1.16.0", "postcss-prefix-selector": "^1.16.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"tailwindcss": "^3", "tailwindcss": "^3",
"tsc-alias": "^1.7.0",
"typescript": "^4.7.3", "typescript": "^4.7.3",
"vite": "^2.4.3", "vite": "^2.4.3",
"vitest": "^0.16.0", "vitest": "^0.16.0",
@@ -61,7 +63,6 @@
}, },
"dependencies": { "dependencies": {
"@vueuse/core": "^8.9.1", "@vueuse/core": "^8.9.1",
"http-server": "^14.1.1",
"vitepress": "^1.0.0-alpha.4" "vitepress": "^1.0.0-alpha.4"
} }
} }

View File

@@ -34,6 +34,7 @@ import { useButtonSpinner } from './composables/useButtonSpinner'
import FlowbiteThemableChild from '@/components/utils/FlowbiteThemable/components/FlowbiteThemableChild/FlowbiteThemableChild.vue' import FlowbiteThemableChild from '@/components/utils/FlowbiteThemable/components/FlowbiteThemableChild/FlowbiteThemableChild.vue'
import type { ButtonGradient, ButtonMonochromeGradient, ButtonSize, ButtonVariant } from './types' import type { ButtonGradient, ButtonMonochromeGradient, ButtonSize, ButtonVariant } from './types'
import type { ThemableChildrenApply } from '@/components/utils/FlowbiteThemable/components/FlowbiteThemableChild/types'
const props = defineProps({ const props = defineProps({
color: { color: {
type: String as PropType<ButtonVariant>, type: String as PropType<ButtonVariant>,
@@ -85,7 +86,7 @@ const loadingSuffix = computed(() => props.loading && props.loadingPosition ===
const { wrapperClasses, spanClasses } = useButtonClasses(toRefs(props)) const { wrapperClasses, spanClasses } = useButtonClasses(toRefs(props))
const { color: spinnerColor, size: spinnerSize } = useButtonSpinner(toRefs(props)) const { color: spinnerColor, size: spinnerSize } = useButtonSpinner(toRefs(props))
const appliableTheme = computed(() => { const appliableTheme = computed<ThemableChildrenApply[]>(() => {
if(['alternative', 'light'].includes(props.color)) return [] if(['alternative', 'light'].includes(props.color)) return []
return ['background', 'hover', 'focus'] return ['background', 'hover', 'focus']
}) })

View File

@@ -0,0 +1,58 @@
<template>
<div>
<label v-if="label" :class="labelClasses">{{ label }}</label>
<div class="flex relative">
<div v-if="$slots.prefix" class="w-10 flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none overflow-hidden">
<slot name="prefix" />
</div>
<input
v-bind="$attrs"
v-model="model"
:disabled="disabled"
:type="type"
:class="[inputClasses, $slots.prefix ? 'pl-10' : '']"
/>
<div v-if="$slots.suffix" class="absolute right-2.5 bottom-2.5">
<slot name="suffix" />
</div>
</div>
<p v-if="$slots.helper" class="mt-2 text-sm text-gray-500 dark:text-gray-400">
<slot name="helper" />
</p>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type { InputSize } from '@/components/Input/types'
import { useInputClasses } from '@/components/Input/composables/useInputClasses'
import { toRefs } from 'vue'
import { useVModel } from '@vueuse/core'
const props = defineProps({
label: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
type: {
type: String as PropType<'text' | 'password'>,
default: 'text',
},
size: {
type: String as PropType<InputSize>,
default: 'md',
},
modelValue: {
type: String,
default: '',
},
})
const model = useVModel(props, 'modelValue')
const {inputClasses, labelClasses} = useInputClasses(toRefs(props))
</script>

View File

@@ -0,0 +1,39 @@
import type { Ref } from 'vue'
import { computed } from 'vue'
import type { InputSize } from '@/components/Input/types'
import { simplifyTailwindClasses } from '@/utils/simplifyTailwindClasses'
// LABEL
const defaultLabelClasses = 'block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300'
// INPUT
const defaultInputClasses = 'bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500'
const disabledInputClasses = 'cursor-not-allowed bg-gray-100'
const inputSizeClasses: Record<InputSize, string> = {
lg: 'p-4',
md: 'p-2.5 text-sm',
sm: 'p-2 text-sm',
}
export type UseInputClassesProps = {
size: Ref<InputSize>
disabled: Ref<boolean>
}
export function useInputClasses(props: UseInputClassesProps): {
inputClasses: Ref<string>
labelClasses: Ref<string>
} {
const inputClasses = computed(() => {
return simplifyTailwindClasses(defaultInputClasses, inputSizeClasses[props.size.value], props.disabled.value ? disabledInputClasses : '')
})
const labelClasses = computed(() => {
return defaultLabelClasses
})
return {
inputClasses,
labelClasses,
}
}

View File

@@ -0,0 +1 @@
export type InputSize = 'sm' | 'md' | 'lg'

View File

@@ -1,7 +1,14 @@
<template> <template>
<div id="tooltip-default" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-700"> <div>
Tooltip content <button data-tooltip-target="tooltip-default" type="button"
<div class="tooltip-arrow" data-popper-arrow></div> class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Default tooltip
</button>
<div id="tooltip-default" role="tooltip"
class="inline-block absolute z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm transition-opacity duration-300 tooltip dark:bg-gray-700">
Tooltip content
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@@ -1,5 +1,5 @@
<template> <template>
<component :is="tag" :class="simplifyTailwindClasses($attrs.class || [], classes)"> <component :is="tag" :class="simplifyTailwindClasses(classAttr, classes)">
<slot /> <slot />
</component> </component>
</template> </template>
@@ -11,10 +11,12 @@ import {
import type { import type {
ThemableChildrenApply, ThemableChildrenApply,
} from '@/components/utils/FlowbiteThemable/components/FlowbiteThemableChild/types' } from '@/components/utils/FlowbiteThemable/components/FlowbiteThemableChild/types'
import { toRefs } from 'vue' import { computed, toRefs, useAttrs } from 'vue'
import { simplifyTailwindClasses } from '@/utils/simplifyTailwindClasses' import { simplifyTailwindClasses } from '@/utils/simplifyTailwindClasses'
import type { FlowbiteTheme } from '@/components/utils/FlowbiteThemable/types' import type { FlowbiteTheme } from '@/components/utils/FlowbiteThemable/types'
const attrs = useAttrs()
const props = defineProps({ const props = defineProps({
apply: { apply: {
type: Array as PropType<ThemableChildrenApply[]>, type: Array as PropType<ThemableChildrenApply[]>,
@@ -31,4 +33,8 @@ const props = defineProps({
}) })
const { classes } = useFlowbiteThemableChildClasses(toRefs(props)) const { classes } = useFlowbiteThemableChildClasses(toRefs(props))
const classAttr = computed<string>(() => {
return attrs.class as string || '' // TODO:
})
</script> </script>

View File

@@ -29,4 +29,6 @@ export { default as Toast } from './components/Toast/Toast.vue'
export { default as ToastProvider } from './components/Toast/components/ToastProvider/ToastProvider.vue' export { default as ToastProvider } from './components/Toast/components/ToastProvider/ToastProvider.vue'
export { default as Tooltip } from './components/Tooltip/Tooltip.vue' export { default as Tooltip } from './components/Tooltip/Tooltip.vue'
export { default as Input } from './components/Input/Input.vue'
export * from './composables' export * from './composables'

View File

@@ -1,6 +1,7 @@
{ {
"extends": "@vue/tsconfig/tsconfig.web.json", "extends": "@vue/tsconfig/tsconfig.web.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"outDir": "dist_types", "outDir": "dist_types",
"paths": { "paths": {
"@/*": [ "@/*": [