1
This commit is contained in:
@@ -19,7 +19,7 @@ const props = withDefaults(
|
||||
:data-size="size"
|
||||
:class="
|
||||
cn(
|
||||
'ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col transition-all duration-200',
|
||||
'border-border/70 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl border py-4 text-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col transition-all duration-200',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectRootEmits, SelectRootProps } from "reka-ui";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { SelectRoot } from "reka-ui";
|
||||
import { fromSelectUpdateValue, toSelectValue } from "./utils";
|
||||
|
||||
const props = withDefaults(defineProps<SelectRootProps>(), {
|
||||
modelValue: undefined,
|
||||
defaultValue: undefined,
|
||||
open: undefined,
|
||||
});
|
||||
|
||||
const emits = defineEmits<SelectRootEmits>();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "modelValue", "defaultValue");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectRoot
|
||||
v-bind="delegatedProps"
|
||||
:model-value="toSelectValue(modelValue)"
|
||||
:default-value="toSelectValue(defaultValue)"
|
||||
@update:model-value="(value) => emits('update:modelValue', fromSelectUpdateValue(value))"
|
||||
@update:open="(value) => emits('update:open', value)"
|
||||
>
|
||||
<slot />
|
||||
</SelectRoot>
|
||||
</template>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectContentEmits, SelectContentProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { SelectContent, SelectPortal, SelectViewport, useForwardPropsEmits } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<SelectContentProps & { class?: HTMLAttributes["class"] }>(),
|
||||
{
|
||||
position: "popper",
|
||||
sideOffset: 4,
|
||||
},
|
||||
);
|
||||
const emits = defineEmits<SelectContentEmits>();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectPortal>
|
||||
<SelectContent
|
||||
data-slot="select-content"
|
||||
v-bind="{ ...$attrs, ...forwarded }"
|
||||
:class="
|
||||
cn(
|
||||
'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground relative z-50 max-h-(--reka-select-content-available-height) min-w-32 origin-(--reka-select-content-transform-origin) overflow-hidden rounded-md border shadow-md ring-1',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<SelectViewport
|
||||
:class="
|
||||
cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-(--reka-select-trigger-height) w-full min-w-(--reka-select-trigger-width)',
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</SelectViewport>
|
||||
</SelectContent>
|
||||
</SelectPortal>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItemEmits, SelectItemProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { Check } from "lucide-vue-next";
|
||||
import { SelectItem, SelectItemIndicator, SelectItemText, useForwardPropsEmits } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { toSelectItemValue } from "./utils";
|
||||
|
||||
const props = defineProps<SelectItemProps & { class?: HTMLAttributes["class"] }>();
|
||||
const emits = defineEmits<SelectItemEmits>();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "value");
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem
|
||||
data-slot="select-item"
|
||||
v-bind="forwarded"
|
||||
:value="toSelectItemValue(value)"
|
||||
:class="
|
||||
cn(
|
||||
'focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*=size-])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectItemIndicator>
|
||||
<Check class="size-4" />
|
||||
</SelectItemIndicator>
|
||||
</span>
|
||||
<SelectItemText>
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectLabelProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { SelectLabel, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<SelectLabelProps & { class?: HTMLAttributes["class"] }>();
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectLabel
|
||||
data-slot="select-label"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('px-2 py-1.5 text-sm font-medium', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</SelectLabel>
|
||||
</template>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectSeparatorProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { SelectSeparator, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes["class"] }>();
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectSeparator
|
||||
data-slot="select-separator"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('bg-border pointer-events-none -mx-1 my-1 h-px', props.class)"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectTriggerProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ChevronDown } from "lucide-vue-next";
|
||||
import { SelectIcon, SelectTrigger, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<SelectTriggerProps & { class?: HTMLAttributes["class"] }>();
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectTrigger
|
||||
data-slot="select-trigger"
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'border-input bg-background text-foreground ring-offset-background placeholder:text-muted-foreground focus:ring-ring/50 flex h-9 w-full min-w-0 items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus:ring-3 disabled:cursor-not-allowed disabled:opacity-50 data-placeholder:text-muted-foreground [&>span]:truncate [&_svg:not([class*=size-])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
<SelectIcon as-child>
|
||||
<ChevronDown class="size-4 opacity-50" />
|
||||
</SelectIcon>
|
||||
</SelectTrigger>
|
||||
</template>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectValueProps } from "reka-ui";
|
||||
import { SelectValue } from "reka-ui";
|
||||
|
||||
const props = defineProps<SelectValueProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectValue v-bind="props">
|
||||
<slot />
|
||||
</SelectValue>
|
||||
</template>
|
||||
@@ -0,0 +1,8 @@
|
||||
export { default as Select } from "./Select.vue";
|
||||
export { default as SelectContent } from "./SelectContent.vue";
|
||||
export { default as SelectItem } from "./SelectItem.vue";
|
||||
export { default as SelectLabel } from "./SelectLabel.vue";
|
||||
export { default as SelectSeparator } from "./SelectSeparator.vue";
|
||||
export { default as SelectTrigger } from "./SelectTrigger.vue";
|
||||
export { default as SelectValue } from "./SelectValue.vue";
|
||||
export { SelectGroup } from "reka-ui";
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { AcceptableValue } from "reka-ui";
|
||||
|
||||
const EMPTY_VALUE = "__dcalc_select_empty__";
|
||||
|
||||
export type SelectModelValue = AcceptableValue | AcceptableValue[] | undefined;
|
||||
|
||||
export function toSelectValue(value: SelectModelValue): SelectModelValue {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => toSelectValue(item));
|
||||
}
|
||||
|
||||
return value === "" ? EMPTY_VALUE : value;
|
||||
}
|
||||
|
||||
export function toSelectItemValue(value: AcceptableValue): AcceptableValue {
|
||||
return value === "" ? EMPTY_VALUE : value;
|
||||
}
|
||||
|
||||
export function fromSelectValue(value: SelectModelValue): SelectModelValue {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => fromSelectValue(item));
|
||||
}
|
||||
|
||||
return value === EMPTY_VALUE ? "" : value;
|
||||
}
|
||||
|
||||
export function fromSelectUpdateValue(value: AcceptableValue): AcceptableValue {
|
||||
return value === EMPTY_VALUE ? "" : value;
|
||||
}
|
||||
Reference in New Issue
Block a user