feat: add number-field component

This commit is contained in:
Roman Slonov 2024-05-25 14:25:39 +08:00
parent 63bb21808f
commit 4b62d273b5
No known key found for this signature in database
31 changed files with 869 additions and 0 deletions

View File

@ -256,6 +256,11 @@ export const docsConfig: DocsConfig = {
title: 'Navigation Menu', title: 'Navigation Menu',
href: '/docs/components/navigation-menu', href: '/docs/components/navigation-menu',
}, },
{
title: 'Number Field',
href: '/docs/components/number-field',
label: 'Alpha',
},
{ {
title: 'Pagination', title: 'Pagination',
href: '/docs/components/pagination', href: '/docs/components/pagination',

View File

@ -759,6 +759,48 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/NavigationMenuDemoItem.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/NavigationMenuDemoItem.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NavigationMenuDemoItem.vue"], 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": { "PaginationDemo": {
name: "PaginationDemo", name: "PaginationDemo",
type: "components:example", type: "components:example",
@ -2167,6 +2209,48 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue"], 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": { "PaginationDemo": {
name: "PaginationDemo", name: "PaginationDemo",
type: "components:example", type: "components:example",

View 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" />

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -553,6 +553,23 @@
], ],
"type": "components:ui" "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", "name": "pagination",
"dependencies": [], "dependencies": [],

View File

@ -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"
}