23 changed files with 422 additions and 19 deletions
@ -0,0 +1,34 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { HTMLAttributes } from 'vue'; |
||||
|
|
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
import { useVModel } from '@vueuse/core'; |
||||
|
|
||||
|
const props = defineProps<{ |
||||
|
class?: HTMLAttributes['class']; |
||||
|
defaultValue?: number | string; |
||||
|
modelValue?: number | string; |
||||
|
}>(); |
||||
|
|
||||
|
const emits = defineEmits<{ |
||||
|
(e: 'update:modelValue', payload: number | string): void; |
||||
|
}>(); |
||||
|
|
||||
|
const modelValue = useVModel(props, 'modelValue', emits, { |
||||
|
defaultValue: props.defaultValue, |
||||
|
passive: true, |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<input |
||||
|
v-model="modelValue" |
||||
|
:class=" |
||||
|
cn( |
||||
|
'border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50', |
||||
|
props.class, |
||||
|
) |
||||
|
" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1 @@ |
|||||
|
export { default as Input } from './Input.vue'; |
||||
@ -0,0 +1,28 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { NumberFieldRootEmits, NumberFieldRootProps } from 'radix-vue'; |
||||
|
|
||||
|
import { type HTMLAttributes, computed } from 'vue'; |
||||
|
|
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
import { NumberFieldRoot, useForwardPropsEmits } from 'radix-vue'; |
||||
|
|
||||
|
const props = defineProps< |
||||
|
{ class?: HTMLAttributes['class'] } & NumberFieldRootProps |
||||
|
>(); |
||||
|
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></slot> |
||||
|
</NumberFieldRoot> |
||||
|
</template> |
||||
@ -0,0 +1,22 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { HTMLAttributes } from 'vue'; |
||||
|
|
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
const props = defineProps<{ |
||||
|
class?: HTMLAttributes['class']; |
||||
|
}>(); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div |
||||
|
:class=" |
||||
|
cn( |
||||
|
'relative [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5 [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5', |
||||
|
props.class, |
||||
|
) |
||||
|
" |
||||
|
> |
||||
|
<slot></slot> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,39 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { NumberFieldDecrementProps } from 'radix-vue'; |
||||
|
|
||||
|
import { type HTMLAttributes, computed } from 'vue'; |
||||
|
|
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
import { Minus } from 'lucide-vue-next'; |
||||
|
import { NumberFieldDecrement, useForwardProps } from 'radix-vue'; |
||||
|
|
||||
|
const props = defineProps< |
||||
|
{ class?: HTMLAttributes['class'] } & NumberFieldDecrementProps |
||||
|
>(); |
||||
|
|
||||
|
const delegatedProps = computed(() => { |
||||
|
const { class: _, ...delegated } = props; |
||||
|
|
||||
|
return delegated; |
||||
|
}); |
||||
|
|
||||
|
const forwarded = useForwardProps(delegatedProps); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<NumberFieldDecrement |
||||
|
data-slot="decrement" |
||||
|
v-bind="forwarded" |
||||
|
:class=" |
||||
|
cn( |
||||
|
'absolute left-0 top-1/2 -translate-y-1/2 p-3 disabled:cursor-not-allowed disabled:opacity-20', |
||||
|
props.class, |
||||
|
) |
||||
|
" |
||||
|
> |
||||
|
<slot> |
||||
|
<Minus class="h-4 w-4" /> |
||||
|
</slot> |
||||
|
</NumberFieldDecrement> |
||||
|
</template> |
||||
@ -0,0 +1,39 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { NumberFieldIncrementProps } from 'radix-vue'; |
||||
|
|
||||
|
import { type HTMLAttributes, computed } from 'vue'; |
||||
|
|
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
import { Plus } from 'lucide-vue-next'; |
||||
|
import { NumberFieldIncrement, useForwardProps } from 'radix-vue'; |
||||
|
|
||||
|
const props = defineProps< |
||||
|
{ class?: HTMLAttributes['class'] } & NumberFieldIncrementProps |
||||
|
>(); |
||||
|
|
||||
|
const delegatedProps = computed(() => { |
||||
|
const { class: _, ...delegated } = props; |
||||
|
|
||||
|
return delegated; |
||||
|
}); |
||||
|
|
||||
|
const forwarded = useForwardProps(delegatedProps); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<NumberFieldIncrement |
||||
|
data-slot="increment" |
||||
|
v-bind="forwarded" |
||||
|
:class=" |
||||
|
cn( |
||||
|
'absolute right-0 top-1/2 -translate-y-1/2 p-3 disabled:cursor-not-allowed disabled:opacity-20', |
||||
|
props.class, |
||||
|
) |
||||
|
" |
||||
|
> |
||||
|
<slot> |
||||
|
<Plus class="h-4 w-4" /> |
||||
|
</slot> |
||||
|
</NumberFieldIncrement> |
||||
|
</template> |
||||
@ -0,0 +1,16 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { cn } from '@vben-core/toolkit'; |
||||
|
|
||||
|
import { NumberFieldInput } from 'radix-vue'; |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<NumberFieldInput |
||||
|
:class=" |
||||
|
cn( |
||||
|
'border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent py-1 text-center text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50', |
||||
|
) |
||||
|
" |
||||
|
data-slot="input" |
||||
|
/> |
||||
|
</template> |
||||
@ -0,0 +1,5 @@ |
|||||
|
export { default as NumberField } from './NumberField.vue'; |
||||
|
export { default as NumberFieldContent } from './NumberFieldContent.vue'; |
||||
|
export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'; |
||||
|
export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'; |
||||
|
export { default as NumberFieldInput } from './NumberFieldInput.vue'; |
||||
@ -0,0 +1,51 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { SelectListItem } from '@vben/types'; |
||||
|
|
||||
|
import { useSlots } from 'vue'; |
||||
|
|
||||
|
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify'; |
||||
|
import { Input, VbenTooltip } from '@vben-core/shadcn-ui'; |
||||
|
|
||||
|
defineOptions({ |
||||
|
name: 'PreferenceSelectItem', |
||||
|
}); |
||||
|
|
||||
|
withDefaults( |
||||
|
defineProps<{ |
||||
|
disabled?: boolean; |
||||
|
items?: SelectListItem[]; |
||||
|
placeholder?: string; |
||||
|
}>(), |
||||
|
{ |
||||
|
disabled: false, |
||||
|
placeholder: '', |
||||
|
items: () => [], |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
const inputValue = defineModel<string>(); |
||||
|
|
||||
|
const slots = useSlots(); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div |
||||
|
:class="{ |
||||
|
'hover:bg-accent': !slots.tip, |
||||
|
'pointer-events-none opacity-50': disabled, |
||||
|
}" |
||||
|
class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1" |
||||
|
> |
||||
|
<span class="flex items-center text-sm"> |
||||
|
<slot></slot> |
||||
|
|
||||
|
<VbenTooltip v-if="slots.tip" side="bottom"> |
||||
|
<template #trigger> |
||||
|
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" /> |
||||
|
</template> |
||||
|
<slot name="tip"></slot> |
||||
|
</VbenTooltip> |
||||
|
</span> |
||||
|
<Input v-model="inputValue" class="h-8 w-[160px]" /> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { SelectListItem } from '@vben/types'; |
||||
|
|
||||
|
import { useSlots } from 'vue'; |
||||
|
|
||||
|
import { MdiQuestionMarkCircleOutline } from '@vben-core/iconify'; |
||||
|
import { |
||||
|
NumberField, |
||||
|
NumberFieldContent, |
||||
|
NumberFieldDecrement, |
||||
|
NumberFieldIncrement, |
||||
|
NumberFieldInput, |
||||
|
VbenTooltip, |
||||
|
} from '@vben-core/shadcn-ui'; |
||||
|
|
||||
|
defineOptions({ |
||||
|
name: 'PreferenceSelectItem', |
||||
|
}); |
||||
|
|
||||
|
withDefaults( |
||||
|
defineProps<{ |
||||
|
disabled?: boolean; |
||||
|
items?: SelectListItem[]; |
||||
|
placeholder?: string; |
||||
|
}>(), |
||||
|
{ |
||||
|
disabled: false, |
||||
|
placeholder: '', |
||||
|
items: () => [], |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
const inputValue = defineModel<number>(); |
||||
|
|
||||
|
const slots = useSlots(); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div |
||||
|
:class="{ |
||||
|
'hover:bg-accent': !slots.tip, |
||||
|
'pointer-events-none opacity-50': disabled, |
||||
|
}" |
||||
|
class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1" |
||||
|
> |
||||
|
<span class="flex items-center text-sm"> |
||||
|
<slot></slot> |
||||
|
|
||||
|
<VbenTooltip v-if="slots.tip" side="bottom"> |
||||
|
<template #trigger> |
||||
|
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" /> |
||||
|
</template> |
||||
|
<slot name="tip"></slot> |
||||
|
</VbenTooltip> |
||||
|
</span> |
||||
|
|
||||
|
<NumberField v-model="inputValue" v-bind="$attrs" class="w-[160px]"> |
||||
|
<NumberFieldContent> |
||||
|
<NumberFieldDecrement /> |
||||
|
<NumberFieldInput /> |
||||
|
<NumberFieldIncrement /> |
||||
|
</NumberFieldContent> |
||||
|
</NumberField> |
||||
|
</div> |
||||
|
</template> |
||||
Loading…
Reference in new issue