Skip to content

feat(packages): ui: add number-field #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export * from './label';
export * from './menu';
export * from './menubar';
export * from './navigation-menu';
export * from './number-field';
export * from './pagination';
export * from './pin-input';
export * from './popover';
Expand Down
9 changes: 9 additions & 0 deletions packages/ui/src/components/number-field/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import SNumberFieldDecrement from './number-field-decrement.vue';
import SNumberFieldIncrement from './number-field-increment.vue';
import SNumberFieldInput from './number-field-input.vue';
import SNumberFieldRoot from './number-field-root.vue';
import SNumberField from './number-field.vue';

export { SNumberFieldDecrement, SNumberFieldIncrement, SNumberFieldInput, SNumberFieldRoot, SNumberField };

export * from './types';
31 changes: 31 additions & 0 deletions packages/ui/src/components/number-field/number-field-decrement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script setup lang="ts">
import { computed } from 'vue';
import { NumberFieldDecrement, Slot } from '@soybean-ui/primitives';
import { cn, numberFieldVariants } from '@soybean-ui/variants';
import { Minus } from 'lucide-vue-next';
import type { NumberFieldDecrementProps } from './types';

defineOptions({
name: 'SNumberFieldDecrement'
});

const { class: cls, size, center, iconClass, disabled } = defineProps<NumberFieldDecrementProps>();

const mergedCls = computed(() => {
const { decrement, decrementIcon } = numberFieldVariants({ size, center });
return {
cls: cn(decrement(), cls),
iconCls: cn(decrementIcon(), iconClass)
};
});
</script>

<template>
<NumberFieldDecrement :class="mergedCls.cls" data-slot="decrement" :disabled="disabled">
<Slot :class="mergedCls.iconCls">
<slot>
<Minus />
</slot>
</Slot>
</NumberFieldDecrement>
</template>
32 changes: 32 additions & 0 deletions packages/ui/src/components/number-field/number-field-increment.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script setup lang="ts">
import { computed } from 'vue';
import { NumberFieldIncrement, Slot } from '@soybean-ui/primitives';
import { cn, numberFieldVariants } from '@soybean-ui/variants';
import { Plus } from 'lucide-vue-next';
import type { NumberFieldIncrementProps } from './types';

defineOptions({
name: 'SNumberFieldIncrement'
});

const { class: cls, size, center, iconClass, disabled } = defineProps<NumberFieldIncrementProps>();

const mergedCls = computed(() => {
const { increment, incrementIcon } = numberFieldVariants({ size, center });

return {
cls: cn(increment(), cls),
iconCls: cn(incrementIcon(), iconClass)
};
});
</script>

<template>
<NumberFieldIncrement :class="mergedCls.cls" data-slot="increment" :disabled="disabled">
<Slot :class="mergedCls.iconCls">
<slot>
<Plus />
</slot>
</Slot>
</NumberFieldIncrement>
</template>
22 changes: 22 additions & 0 deletions packages/ui/src/components/number-field/number-field-input.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { computed } from 'vue';
import { NumberFieldInput } from '@soybean-ui/primitives';
import { cn, numberFieldVariants } from '@soybean-ui/variants';
import type { NumberFieldInputProps } from './types';

defineOptions({
name: 'SNumberFieldInput'
});

const { class: cls, size, center } = defineProps<NumberFieldInputProps>();

const mergedCls = computed(() => {
const { input } = numberFieldVariants({ size, center });

return cn(input(), cls);
});
</script>

<template>
<NumberFieldInput :class="mergedCls" data-slot="input" />
</template>
28 changes: 28 additions & 0 deletions packages/ui/src/components/number-field/number-field-root.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup lang="ts">
import { computed } from 'vue';
import { NumberFieldRoot, useForwardPropsEmits } from '@soybean-ui/primitives';
import { cn, numberFieldVariants } from '@soybean-ui/variants';
import type { NumberFieldRootEmits, NumberFieldRootProps } from './types';

defineOptions({
name: 'SNumberFieldRoot'
});

const { class: cls, size, ...delegatedProps } = defineProps<NumberFieldRootProps>();

const emit = defineEmits<NumberFieldRootEmits>();

const forwarded = useForwardPropsEmits(delegatedProps, emit);

const mergedCls = computed(() => {
const { root } = numberFieldVariants({ size });

return cn(root(), cls);
});
</script>

<template>
<NumberFieldRoot v-bind="forwarded" :class="mergedCls">
<slot />
</NumberFieldRoot>
</template>
50 changes: 50 additions & 0 deletions packages/ui/src/components/number-field/number-field.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { useForwardPropsEmits } from '@soybean-ui/primitives';
import NumberFieldDecrement from './number-field-decrement.vue';
import NumberFieldIncrement from './number-field-increment.vue';
import NumberFieldInput from './number-field-input.vue';
import NumberFieldRoot from './number-field-root.vue';
import type { NumberFieldEmits, NumberFieldProps } from './types';

defineOptions({
name: 'SNumberField'
});

const {
class: cls,
size,
center,
disabledDecrement,
disabledIncrement,
ui,
...rootProps
} = defineProps<NumberFieldProps>();

const emit = defineEmits<NumberFieldEmits>();

const forwarded = useForwardPropsEmits(rootProps, emit);
</script>

<template>
<NumberFieldRoot v-bind="forwarded" :class="cls || ui?.root" :size="size">
<NumberFieldInput :class="ui?.input" :size="size" :center="center" />
<NumberFieldDecrement
:class="ui?.decrement"
:size="size"
:center="center"
:icon-class="ui?.decrementIcon"
:disabled="disabled || disabledDecrement"
>
<slot name="decrement-icon" />
</NumberFieldDecrement>
<NumberFieldIncrement
:class="ui?.increment"
:size="size"
:center="center"
:icon-class="ui?.incrementIcon"
:disabled="disabled || disabledIncrement"
>
<slot name="increment-icon" />
</NumberFieldIncrement>
</NumberFieldRoot>
</template>
43 changes: 43 additions & 0 deletions packages/ui/src/components/number-field/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type {
ClassValue,
NumberFieldRootEmits,
NumberFieldDecrementProps as _NumberFieldDecrementProps,
NumberFieldIncrementProps as _NumberFieldIncrementProps,
NumberFieldInputProps as _NumberFieldInputProps,
NumberFieldRootProps as _NumberFieldRootProps
} from '@soybean-ui/primitives';
import type { NumberFieldSlots, ThemeSize } from '@soybean-ui/variants';

export interface NumberFieldRootProps extends _NumberFieldRootProps {
size?: ThemeSize;
}

export interface NumberFieldInputProps extends _NumberFieldInputProps {
size?: ThemeSize;
center?: boolean;
}

export interface NumberFieldDecrementProps extends _NumberFieldDecrementProps {
size?: ThemeSize;
center?: boolean;
iconClass?: ClassValue;
}

export interface NumberFieldIncrementProps extends _NumberFieldIncrementProps {
size?: ThemeSize;
center?: boolean;
iconClass?: ClassValue;
}

export type NumberFieldUi = Partial<Record<NumberFieldSlots, ClassValue>>;

export interface NumberFieldProps extends NumberFieldRootProps {
center?: boolean;
disabledDecrement?: boolean;
disabledIncrement?: boolean;
ui?: NumberFieldUi;
}

export type NumberFieldEmits = NumberFieldRootEmits;

export type { NumberFieldRootEmits };
1 change: 1 addition & 0 deletions packages/variants/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './variants/label';
export * from './variants/menu';
export * from './variants/menubar';
export * from './variants/navigation-menu';
export * from './variants/number-field';
export * from './variants/pagination';
export * from './variants/pin-input';
export * from './variants/popover';
Expand Down
119 changes: 119 additions & 0 deletions packages/variants/src/variants/number-field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// @unocss-include
import { tv } from 'tailwind-variants';

export const numberFieldVariants = tv({
slots: {
root: 'flex items-center w-full rounded-md border border-input bg-background focus-within:(border-input ring-2 ring-offset-2 ring-primary) disabled:(cursor-not-allowed opacity-50)',
decrement: `flex h-full shrink-0 items-center justify-center bg-transparent outline-none disabled:(cursor-not-allowed opacity-20)`,
decrementIcon: '',
increment: `flex h-full shrink-0 items-center justify-center bg-transparent outline-none disabled:(cursor-not-allowed opacity-20)`,
incrementIcon: '',
input: [
`h-full w-full grow bg-transparent`,
`placeholder:text-muted-foreground outline-none disabled:(cursor-not-allowed opacity-50)`
]
},
variants: {
size: {
xs: {
decrement: 'p-1',
decrementIcon: 'text-xs',
increment: 'p-1',
incrementIcon: 'text-xs',
input: 'h-6 text-xs'
},
sm: {
decrement: 'p-1.25',
decrementIcon: 'text-sm',
increment: 'p-1.25',
incrementIcon: 'text-sm',
input: 'h-7 text-sm'
},
md: {
decrement: 'p-1.25',
decrementIcon: 'text-sm',
increment: 'p-1.25',
incrementIcon: 'text-sm',
input: 'h-8 text-sm'
},
lg: {
decrement: 'p-1.5',
decrementIcon: 'text-base',
increment: 'p-1.5',
incrementIcon: 'text-base',
input: 'h-9 text-base'
},
xl: {
decrement: 'p-1.5',
decrementIcon: 'text-lg',
increment: 'p-1.5',
incrementIcon: 'text-lg',
input: 'h-10 text-base'
},
xxl: {
decrement: 'p-2',
decrementIcon: 'text-xl',
increment: 'p-2',
incrementIcon: 'text-xl',
input: 'h-12 text-lg'
}
},
center: {
true: {
decrement: 'order-1',
input: 'text-center order-2',
increment: 'order-3'
}
}
},
compoundVariants: [
{
size: 'xs',
center: false,
class: {
input: 'pl-1.5'
}
},
{
size: 'sm',
center: false,
class: {
input: 'pl-2'
}
},
{
size: 'md',
center: false,
class: {
input: 'pl-2.5'
}
},
{
size: 'lg',
center: false,
class: {
input: 'pl-3'
}
},
{
size: 'xl',
center: false,
class: {
input: 'pl-3.5'
}
},
{
size: 'xxl',
center: false,
class: {
input: 'pl-4'
}
}
],
defaultVariants: {
size: 'md',
center: false
}
});

export type NumberFieldSlots = keyof typeof numberFieldVariants.slots;
6 changes: 6 additions & 0 deletions src/views/ui/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import UiInput from './modules/input.vue';
import UiKeyboardKey from './modules/keyboard-key.vue';
import UiMenubar from './modules/menubar.vue';
import UiNavigationMenu from './modules/navigation-menu.vue';
import UiNumberField from './modules/number-field.vue';
import UiPagination from './modules/pagination.vue';
import UiPinInput from './modules/pin-input.vue';
import UiPopover from './modules/popover.vue';
Expand Down Expand Up @@ -189,6 +190,11 @@ const tabs: TabConfig[] = [
label: 'NavigationMenu',
component: UiNavigationMenu
},
{
value: 'number-field',
label: 'NumberField',
component: UiNumberField
},
{
value: 'popover',
label: 'Popover',
Expand Down
Loading