feat: add number-field component
This commit is contained in:
parent
63bb21808f
commit
4b62d273b5
|
|
@ -256,6 +256,11 @@ export const docsConfig: DocsConfig = {
|
|||
title: 'Navigation Menu',
|
||||
href: '/docs/components/navigation-menu',
|
||||
},
|
||||
{
|
||||
title: 'Number Field',
|
||||
href: '/docs/components/number-field',
|
||||
label: 'Alpha',
|
||||
},
|
||||
{
|
||||
title: 'Pagination',
|
||||
href: '/docs/components/pagination',
|
||||
|
|
|
|||
|
|
@ -759,6 +759,48 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/default/example/NavigationMenuDemoItem.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NavigationMenuDemoItem.vue"],
|
||||
},
|
||||
"NumberFieldCurrency": {
|
||||
name: "NumberFieldCurrency",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldCurrency.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldCurrency.vue"],
|
||||
},
|
||||
"NumberFieldDecimal": {
|
||||
name: "NumberFieldDecimal",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldDecimal.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldDecimal.vue"],
|
||||
},
|
||||
"NumberFieldDemo": {
|
||||
name: "NumberFieldDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldDemo.vue"],
|
||||
},
|
||||
"NumberFieldDisabled": {
|
||||
name: "NumberFieldDisabled",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldDisabled.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldDisabled.vue"],
|
||||
},
|
||||
"NumberFieldForm": {
|
||||
name: "NumberFieldForm",
|
||||
type: "components:example",
|
||||
registryDependencies: ["button","form","number-field","toast"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldForm.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldForm.vue"],
|
||||
},
|
||||
"NumberFieldPercentage": {
|
||||
name: "NumberFieldPercentage",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/default/example/NumberFieldPercentage.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/default/example/NumberFieldPercentage.vue"],
|
||||
},
|
||||
"PaginationDemo": {
|
||||
name: "PaginationDemo",
|
||||
type: "components:example",
|
||||
|
|
@ -2167,6 +2209,48 @@ export const Index = {
|
|||
component: () => import("../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue"],
|
||||
},
|
||||
"NumberFieldCurrency": {
|
||||
name: "NumberFieldCurrency",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldCurrency.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldCurrency.vue"],
|
||||
},
|
||||
"NumberFieldDecimal": {
|
||||
name: "NumberFieldDecimal",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldDecimal.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldDecimal.vue"],
|
||||
},
|
||||
"NumberFieldDemo": {
|
||||
name: "NumberFieldDemo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldDemo.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldDemo.vue"],
|
||||
},
|
||||
"NumberFieldDisabled": {
|
||||
name: "NumberFieldDisabled",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldDisabled.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldDisabled.vue"],
|
||||
},
|
||||
"NumberFieldForm": {
|
||||
name: "NumberFieldForm",
|
||||
type: "components:example",
|
||||
registryDependencies: ["button","form","number-field","toast"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldForm.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldForm.vue"],
|
||||
},
|
||||
"NumberFieldPercentage": {
|
||||
name: "NumberFieldPercentage",
|
||||
type: "components:example",
|
||||
registryDependencies: ["number-field"],
|
||||
component: () => import("../src/lib/registry/new-york/example/NumberFieldPercentage.vue").then((m) => m.default),
|
||||
files: ["../src/lib/registry/new-york/example/NumberFieldPercentage.vue"],
|
||||
},
|
||||
"PaginationDemo": {
|
||||
name: "PaginationDemo",
|
||||
type: "components:example",
|
||||
|
|
|
|||
71
apps/www/src/content/docs/components/number-field.md
Normal file
71
apps/www/src/content/docs/components/number-field.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: Number Field
|
||||
description: A number field allows a user to enter a number and increment or decrement the value using stepper buttons.
|
||||
source: apps/www/src/lib/registry/default/ui/number-field
|
||||
primitive: https://www.radix-vue.com/components/number-field.html
|
||||
---
|
||||
|
||||
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
|
||||
|
||||
## Installation
|
||||
|
||||
<TabPreview name="CLI">
|
||||
<template #CLI>
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add number-field
|
||||
```
|
||||
</template>
|
||||
</TabPreview>
|
||||
|
||||
## Usage
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField>
|
||||
<NumberFieldLabel>Age</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="NumberFieldDisabled" class="max-w-[180px]" />
|
||||
|
||||
### Decimal
|
||||
|
||||
<ComponentPreview name="NumberFieldDecimal" class="max-w-[180px]" />
|
||||
|
||||
### Percentage
|
||||
|
||||
<ComponentPreview name="NumberFieldPercentage" class="max-w-[180px]" />
|
||||
|
||||
### Currency
|
||||
|
||||
<ComponentPreview name="NumberFieldCurrency" class="max-w-[220px]" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="NumberFieldForm" class="max-w-xs" />
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="1500"
|
||||
:format-options="{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
currencyDisplay: 'code',
|
||||
currencySign: 'accounting',
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Balance</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="5"
|
||||
:format-options="{
|
||||
signDisplay: 'exceptZero',
|
||||
minimumFractionDigits: 1,
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Number</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField :default-value="18" :min="0">
|
||||
<NumberFieldLabel>Age</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField :default-value="18" :min="0" disabled>
|
||||
<NumberFieldLabel>Age</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/lib/registry/default/ui/form'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
} from '@/lib/registry/default/ui/number-field'
|
||||
import { toast } from '@/lib/registry/default/ui/toast'
|
||||
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
payment: z.number().min(10, 'Min 10 euros to send payment').max(5000, 'Max 5000 euros to send payment'),
|
||||
}))
|
||||
|
||||
const { handleSubmit } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: {
|
||||
payment: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
toast({
|
||||
title: 'You submitted the following values:',
|
||||
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form class="w-2/3 space-y-6" @submit="onSubmit">
|
||||
<FormField v-slot="{ value, handleChange }" name="payment">
|
||||
<FormItem>
|
||||
<FormLabel>Payment</FormLabel>
|
||||
<NumberField
|
||||
class="gap-2"
|
||||
:model-value="value"
|
||||
:min="0"
|
||||
:format-options="{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
currencyDisplay: 'code',
|
||||
currencySign: 'accounting',
|
||||
}"
|
||||
@update:model-value="handleChange"
|
||||
>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<FormControl>
|
||||
<NumberFieldInput />
|
||||
</FormControl>
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
<FormDescription>
|
||||
Enter value between 10 and 5000.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<Button type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="0.05"
|
||||
:step="0.01"
|
||||
:format-options="{
|
||||
style: 'percent',
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Percent</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldRootEmits, NumberFieldRootProps } from 'radix-vue'
|
||||
import { NumberFieldRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<NumberFieldRootEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldRoot v-bind="forwarded" :class="cn('grid gap-1.5', props.class)">
|
||||
<slot />
|
||||
</NumberFieldRoot>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('relative', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldDecrementProps } from 'radix-vue'
|
||||
import { NumberFieldDecrement, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { Minus } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldDecrementProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldDecrement v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 left-0 p-3 disabled:cursor-not-allowed disabled:opacity-20', props.class)">
|
||||
<slot>
|
||||
<Minus class="h-4 w-4" />
|
||||
</slot>
|
||||
</NumberFieldDecrement>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldIncrementProps } from 'radix-vue'
|
||||
import { NumberFieldIncrement, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldIncrementProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldIncrement v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 right-0 disabled:cursor-not-allowed disabled:opacity-20 p-3', props.class)">
|
||||
<slot>
|
||||
<Plus class="h-4 w-4" />
|
||||
</slot>
|
||||
</NumberFieldIncrement>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { NumberFieldInput } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldInput :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-10 py-2 text-sm text-center ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50')" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldLabelProps } from 'radix-vue'
|
||||
import { NumberFieldLabel, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldLabel v-bind="forwarded" :class="cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', props.class)">
|
||||
<slot />
|
||||
</NumberFieldLabel>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export { default as NumberField } from './NumberField.vue'
|
||||
export { default as NumberFieldInput } from './NumberFieldInput.vue'
|
||||
export { default as NumberFieldLabel } from './NumberFieldLabel.vue'
|
||||
export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'
|
||||
export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'
|
||||
export { default as NumberFieldContent } from './NumberFieldContent.vue'
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="1500"
|
||||
:format-options="{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
currencyDisplay: 'code',
|
||||
currencySign: 'accounting',
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Balance</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="5"
|
||||
:format-options="{
|
||||
signDisplay: 'exceptZero',
|
||||
minimumFractionDigits: 1,
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Number</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField :default-value="18" :min="0">
|
||||
<NumberFieldLabel>Age</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField :default-value="18" disabled>
|
||||
<NumberFieldLabel>Age</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/lib/registry/new-york/ui/form'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
payment: z.number().min(10, 'Min 10 euros to send payment').max(5000, 'Max 5000 euros to send payment'),
|
||||
}))
|
||||
|
||||
const { handleSubmit } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: {
|
||||
payment: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
toast({
|
||||
title: 'You submitted the following values:',
|
||||
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form class="w-2/3 space-y-6" @submit="onSubmit">
|
||||
<FormField v-slot="{ value, handleChange }" name="payment">
|
||||
<FormItem>
|
||||
<FormLabel>Payment</FormLabel>
|
||||
<NumberField
|
||||
class="gap-2"
|
||||
:model-value="value"
|
||||
:min="0"
|
||||
:format-options="{
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
currencyDisplay: 'code',
|
||||
currencySign: 'accounting',
|
||||
}"
|
||||
@update:model-value="handleChange"
|
||||
>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<FormControl>
|
||||
<NumberFieldInput />
|
||||
</FormControl>
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
<FormDescription>
|
||||
Enter value between 10 and 5000.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<Button type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldContent,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldLabel,
|
||||
} from '@/lib/registry/new-york/ui/number-field'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberField
|
||||
:default-value="0.05"
|
||||
:step="0.01"
|
||||
:format-options="{
|
||||
style: 'percent',
|
||||
}"
|
||||
>
|
||||
<NumberFieldLabel>Percent</NumberFieldLabel>
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldRootEmits, NumberFieldRootProps } from 'radix-vue'
|
||||
import { NumberFieldRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<NumberFieldRootEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldRoot v-bind="forwarded" :class="cn('grid gap-1.5', props.class)">
|
||||
<slot />
|
||||
</NumberFieldRoot>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('relative', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldDecrementProps } from 'radix-vue'
|
||||
import { NumberFieldDecrement, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { Minus } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldDecrementProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldDecrement v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 left-0 p-3 disabled:cursor-not-allowed disabled:opacity-20', props.class)">
|
||||
<slot>
|
||||
<Minus class="h-4 w-4" />
|
||||
</slot>
|
||||
</NumberFieldDecrement>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldIncrementProps } from 'radix-vue'
|
||||
import { NumberFieldIncrement, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldIncrementProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldIncrement v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 right-0 disabled:cursor-not-allowed disabled:opacity-20 p-3', props.class)">
|
||||
<slot>
|
||||
<Plus class="h-4 w-4" />
|
||||
</slot>
|
||||
</NumberFieldIncrement>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { NumberFieldInput } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldInput :class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-10 py-1 text-sm text-center shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50')" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import type { NumberFieldLabelProps } from 'radix-vue'
|
||||
import { NumberFieldLabel, useForwardProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<NumberFieldLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NumberFieldLabel v-bind="forwarded" :class="cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', props.class)">
|
||||
<slot />
|
||||
</NumberFieldLabel>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export { default as NumberField } from './NumberField.vue'
|
||||
export { default as NumberFieldInput } from './NumberFieldInput.vue'
|
||||
export { default as NumberFieldLabel } from './NumberFieldLabel.vue'
|
||||
export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'
|
||||
export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'
|
||||
export { default as NumberFieldContent } from './NumberFieldContent.vue'
|
||||
|
|
@ -553,6 +553,23 @@
|
|||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "number-field",
|
||||
"dependencies": [],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
"ui/number-field/NumberField.vue",
|
||||
"ui/number-field/NumberFieldContent.vue",
|
||||
"ui/number-field/NumberFieldDecrement.vue",
|
||||
"ui/number-field/NumberFieldIncrement.vue",
|
||||
"ui/number-field/NumberFieldInput.vue",
|
||||
"ui/number-field/NumberFieldLabel.vue",
|
||||
"ui/number-field/index.ts"
|
||||
],
|
||||
"type": "components:ui"
|
||||
},
|
||||
{
|
||||
"name": "pagination",
|
||||
"dependencies": [],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "number-field",
|
||||
"dependencies": [],
|
||||
"registryDependencies": [
|
||||
"utils"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"name": "NumberField.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport type { NumberFieldRootEmits, NumberFieldRootProps } from 'radix-vue'\nimport { NumberFieldRoot, useForwardPropsEmits } from 'radix-vue'\nimport { type HTMLAttributes, computed } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<NumberFieldRootProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<NumberFieldRootEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <NumberFieldRoot v-bind=\"forwarded\" :class=\"cn('grid gap-1.5', props.class)\">\n <slot />\n </NumberFieldRoot>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "NumberFieldContent.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n <div :class=\"cn('relative', props.class)\">\n <slot />\n </div>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "NumberFieldDecrement.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport type { NumberFieldDecrementProps } from 'radix-vue'\nimport { NumberFieldDecrement, useForwardProps } from 'radix-vue'\nimport { type HTMLAttributes, computed } from 'vue'\nimport { Minus } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<NumberFieldDecrementProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <NumberFieldDecrement v-bind=\"forwarded\" :class=\"cn('absolute top-1/2 -translate-y-1/2 left-0 p-3 disabled:cursor-not-allowed disabled:opacity-20', props.class)\">\n <slot>\n <Minus class=\"h-4 w-4\" />\n </slot>\n </NumberFieldDecrement>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "NumberFieldIncrement.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport type { NumberFieldIncrementProps } from 'radix-vue'\nimport { NumberFieldIncrement, useForwardProps } from 'radix-vue'\nimport { type HTMLAttributes, computed } from 'vue'\nimport { Plus } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<NumberFieldIncrementProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <NumberFieldIncrement v-bind=\"forwarded\" :class=\"cn('absolute top-1/2 -translate-y-1/2 right-0 disabled:cursor-not-allowed disabled:opacity-20 p-3', props.class)\">\n <slot>\n <Plus class=\"h-4 w-4\" />\n </slot>\n </NumberFieldIncrement>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "NumberFieldInput.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport { NumberFieldInput } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n</script>\n\n<template>\n <NumberFieldInput :class=\"cn('flex h-10 w-full rounded-md border border-input bg-background px-10 py-2 text-sm text-center ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50')\" />\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "NumberFieldLabel.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport type { NumberFieldLabelProps } from 'radix-vue'\nimport { NumberFieldLabel, useForwardProps } from 'radix-vue'\nimport { type HTMLAttributes, computed } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<NumberFieldLabelProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <NumberFieldLabel v-bind=\"forwarded\" :class=\"cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', props.class)\">\n <slot />\n </NumberFieldLabel>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "index.ts",
|
||||
"content": "export { default as NumberField } from './NumberField.vue'\nexport { default as NumberFieldInput } from './NumberFieldInput.vue'\nexport { default as NumberFieldLabel } from './NumberFieldLabel.vue'\nexport { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'\nexport { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'\nexport { default as NumberFieldContent } from './NumberFieldContent.vue'\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user