Merge branch 'feat/consultation-82' into fe-prescription-56

This commit is contained in:
2025-10-08 08:00:01 +07:00
252 changed files with 10818 additions and 997 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ defineProps<{
</script>
<template>
<div :class="`m-3 mb-5 flex-wrap md:flex ${classValExt || ''}`">
<div :class="` mb-5 flex-wrap md:flex ${classValExt || ''}`">
<slot />
</div>
</template>
+74 -39
View File
@@ -5,6 +5,7 @@ interface Item {
value: string
label: string
code?: string
priority?: number
}
const props = defineProps<{
@@ -15,7 +16,7 @@ const props = defineProps<{
searchPlaceholder?: string
emptyMessage?: string
class?: string
disabled?: boolean
isDisabled?: boolean
}>()
const emit = defineEmits<{
@@ -24,31 +25,32 @@ const emit = defineEmits<{
const open = ref(false)
const selectedItem = computed(() =>
props.items.find(item => item.value === props.modelValue),
)
const selectedItem = computed(() => props.items.find((item) => item.value === props.modelValue))
const displayText = computed(() =>
selectedItem.value?.label || props.placeholder || '---pilih item',
)
const displayText = computed(() => {
if (selectedItem.value?.label) {
return selectedItem.value.label
}
return props.placeholder || 'Pilih item'
})
// Create searchable items with combined code and label for better search
// Sort by:
// 1. Selected item first (highest priority)
// 2. Then by label alphabetically
const searchableItems = computed(() => {
const itemsWithSearch = props.items.map(item => ({
const itemsWithSearch = props.items.map((item) => ({
...item,
searchValue: `${item.code || ''} ${item.label}`.trim(),
isSelected: item.value === props.modelValue,
}))
return itemsWithSearch.sort((a, b) => {
// Selected item always comes first
const aPriority = a.priority ?? 0
const bPriority = b.priority ?? 0
if (aPriority !== bPriority) {
return bPriority - aPriority
}
if (a.isSelected && !b.isSelected) return -1
if (!a.isSelected && b.isSelected) return 1
// If neither or both are selected, sort by label alphabetically
return a.label.localeCompare(b.label)
})
})
@@ -63,45 +65,78 @@ function onSelect(item: Item) {
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
:id="props.id"
:disabled="props.disabled"
variant="outline"
role="combobox"
:aria-expanded="open"
:aria-controls="`${props.id}-list`"
:aria-describedby="`${props.id}-search`"
:class="cn(
'w-full justify-between border-1 border-gray-400 bg-white hover:bg-gray-50 text-sm font-normal focus:outline-none focus:ring-1 focus:ring-black',
!modelValue && 'text-muted-foreground',
props.class,
)"
:id="props.id"
:disabled="props.isDisabled"
variant="outline"
role="combobox"
:aria-expanded="open"
:aria-controls="`${props.id}-list`"
:aria-describedby="`${props.id}-search`"
:class="
cn(
'w-full justify-between border text-sm font-normal rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
{
'cursor-not-allowed bg-gray-100 opacity-50 border-gray-300 text-gray-500': props.isDisabled,
'bg-white text-black dark:bg-gray-800 dark:text-white dark:border-gray-600 border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700': !props.isDisabled,
'text-gray-400 dark:text-gray-500': !modelValue && !props.isDisabled,
},
props.class,
)
"
>
{{ displayText }}
<Icon name="i-lucide-chevrons-up-down" class="ml-2 h-4 w-4 shrink-0 opacity-50" />
<Icon
name="i-lucide-chevrons-up-down"
:class="cn('ml-2 h-4 w-4 shrink-0', {
'opacity-30': props.isDisabled,
'opacity-50 text-gray-500 dark:text-gray-300': !props.isDisabled
})"
/>
</Button>
</PopoverTrigger>
<PopoverContent class="w-full p-0">
<Command>
<PopoverContent class="w-[var(--radix-popover-trigger-width)] p-0 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
<Command class="bg-white dark:bg-gray-800">
<CommandInput
:id="`${props.id}-search`"
class="h-9"
class="h-9 bg-white dark:bg-gray-800 text-black dark:text-white border-0 border-b border-gray-200 dark:border-gray-700 focus:ring-0"
:placeholder="searchPlaceholder || 'Cari...'"
:aria-label="`Cari ${displayText}`"
/>
<CommandEmpty>{{ emptyMessage || 'Item tidak ditemukan.' }}</CommandEmpty>
<CommandList :id="`${props.id}-list`" role="listbox">
<CommandEmpty class="text-gray-500 dark:text-gray-400 py-6 text-center text-sm">
{{ emptyMessage || 'Item tidak ditemukan.' }}
</CommandEmpty>
<CommandList
:id="`${props.id}-list`"
role="listbox"
class="max-h-60 overflow-auto"
>
<CommandGroup>
<CommandItem v-for="item in searchableItems" :key="item.value" :value="item.searchValue" @select="onSelect(item)">
<div class="flex items-center justify-between w-full">
<CommandItem
v-for="item in searchableItems"
:key="item.value"
:value="item.searchValue"
:class="
cn(
'flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 text-sm',
'focus:outline-none text-black dark:text-white',
'hover:bg-primary hover:text-white focus:bg-primary focus:text-white',
'data-[highlighted]:bg-primary data-[highlighted]:text-white',
)
"
@select="onSelect(item)"
>
<div class="flex w-full items-center justify-between">
<span>{{ item.label }}</span>
<div class="flex items-center gap-2">
<span v-if="item.code" class="text-xs text-muted-foreground">{{ item.code }}</span>
<span
v-if="item.code"
class="text-xs text-muted-foreground"
>
{{ item.code }}
</span>
<Icon
name="i-lucide-check"
:class="cn(
'h-4 w-4',
modelValue === item.value ? 'opacity-100' : 'opacity-0',
)"
:class="cn('h-4 w-4', modelValue === item.value ? 'opacity-100' : 'opacity-0')"
/>
</div>
</div>
@@ -1,9 +1,10 @@
<script setup lang="ts">
// helpers
import { format, parseISO } from 'date-fns'
import { id as localeID } from 'date-fns/locale'
// components
import { Button } from '~/components/pub/ui/button'
import { Calendar } from '~/components/pub/ui/calendar'
import Calendar from '~/components/pub/ui/calendar/Calendar.vue'
import { Popover, PopoverContent, PopoverTrigger } from '~/components/pub/ui/popover'
const props = defineProps<{
@@ -18,7 +19,7 @@ const emit = defineEmits<{
const date = ref<Date | any>(undefined)
watch(date, (value) => {
const newValue = format(value, 'yyyy-MM-dd')
const newValue = format(value, 'yyyy-MM-dd', { locale: localeID })
emit('update:modelValue', newValue)
})
@@ -42,14 +43,14 @@ onMounted(() => {
<PopoverTrigger as-child>
<Button variant="outline" class="bg-white border-gray-400 font-normal text-right h-[40px] w-full">
<div class="flex justify-between items-center w-full">
<p v-if="date">{{ format(date, 'PPP') }}</p>
<p v-if="date">{{ format(date, 'PPP', { locale: localeID }) }}</p>
<p v-else class="text-sm text-black text-opacity-50">{{ props.placeholder || 'Tanggal' }}</p>
<Icon name="i-lucide-calendar" class="h-5 w-5" />
</div>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<Calendar v-model="date" mode="single" />
<Calendar v-model="date" mode="single" locale="id" />
</PopoverContent>
</Popover>
</div>
@@ -13,7 +13,7 @@ const props = withDefaults(
density: 'default',
side: 'default',
position: 'default',
layout: 'default',
layout: 'stacked',
class: '',
},
)
@@ -26,12 +26,13 @@ const widthClass = computed(() => {
})
const wrapperClass = computed(() => [
'w-full flex-shrink-0 mb-3',
'w-full flex-shrink-0',
widthClass.value,
props.layout === 'stacked' ? 'flex flex-col p-2' : 'md:flex',
props.layout === 'stacked' ? 'flex flex-col' : 'md:flex',
props.density !== 'dense' ? 'mb-2 md:mb-2.5 xl:mb-3' : '',
// Only add margin bottom if no custom class overrides it
props.class?.includes('mb-') ? '' : (props.density !== 'dense' ? 'mb-2 md:mb-2.5 xl:mb-3' : 'mb-3'),
props.class,
])
</script>
+3 -2
View File
@@ -10,8 +10,9 @@ defineProps<{
<template>
<div class="grow">
<slot />
<div v-if="id && errors?.[id]" class="field-error-info">
{{ errors[id]?.message }}
<!-- Always reserve space for error message to prevent CLS -->
<div class="field-error-info">
{{ (id && errors?.[id]) ? errors[id]?.message : '' }}
</div>
</div>
</template>
@@ -0,0 +1,82 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
import Field from '~/components/pub/my-ui/form/field.vue'
import Label from '~/components/pub/my-ui/form/label.vue'
import { Input } from '~/components/pub/ui/input'
import { cn } from '~/lib/utils'
const props = defineProps<{
fieldName: string
placeholder: string
label: string
errors?: FormErrors
class?: string
numericOnly?: boolean
maxLength?: number
isRequired?: boolean
isDisabled?: boolean
}>()
function handleInput(event: Event) {
const target = event.target as HTMLInputElement
let value = target.value
// Filter numeric only jika diperlukan
if (props.numericOnly) {
value = value.replace(/\D/g, '')
}
// Batasi panjang maksimal jika diperlukan
if (props.maxLength && value.length > props.maxLength) {
value = value.slice(0, props.maxLength)
}
// Update value jika ada perubahan
if (target.value !== value) {
target.value = value
// Trigger input event untuk update form
target.dispatchEvent(new Event('input', { bubbles: true }))
}
}
</script>
<template>
<FieldGroup>
<Label
v-if="label !== ''"
:label-for="fieldName"
:is-required="isRequired && !isDisabled"
>
{{ label }}
</Label>
<Field
:id="fieldName"
:errors="errors"
>
<FormField
v-slot="{ componentField }"
:name="fieldName"
>
<FormItem>
<FormControl>
<Input
:disabled="isDisabled"
v-bind="componentField"
:placeholder="placeholder"
:maxlength="maxLength"
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0')"
autocomplete="off"
aria-autocomplete="none"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
@input="handleInput"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</Field>
</FieldGroup>
</template>
+17 -4
View File
@@ -2,16 +2,18 @@
const props = withDefaults(
defineProps<{
labelFor?: string
size?: 'default' | 'narrow' | 'wide'
size?: 'default' | 'narrow' | 'wide' | 'fit'
height?: 'default' | 'compact'
position?: 'default' | 'dynamic'
stacked?: boolean
isRequired?: boolean
}>(),
{
size: 'default',
height: 'default',
position: 'default',
stacked: false,
stacked: true,
isRequired: false,
},
)
@@ -19,6 +21,7 @@ const sizeMap = {
default: 'w-28 2xl:w-36',
narrow: 'w-24 2xl:w-28',
wide: 'w-44 2xl:w-48',
fit: 'w-fit',
} as const
const heightMap = {
@@ -43,13 +46,23 @@ const wrapperClass = computed(() => [
props.stacked ? '' : positionWrapMap[props.position],
])
const labelClass = computed(() => [props.stacked ? 'block mb-1 text-sm font-medium' : positionChildMap[props.position]])
const labelClass = computed(() => [props.stacked ? 'block mb-1 text-sm font-normal' : positionChildMap[props.position]])
</script>
<template>
<div :class="wrapperClass">
<label class="block" :class="labelClass" :for="labelFor">
<label
class="block"
:class="labelClass"
:for="labelFor"
>
<slot />
<span
v-if="isRequired"
class="text-red-600"
>
*
</span>
</label>
</div>
</template>
+114 -25
View File
@@ -1,20 +1,20 @@
<script setup lang="ts">
import { SelectRoot } from 'radix-vue'
import {
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from '~/components/pub/ui/select'
import { watch } from 'vue'
import SelectContent from '~/components/pub/ui/select/SelectContent.vue'
import SelectGroup from '~/components/pub/ui/select/SelectGroup.vue'
import SelectItem from '~/components/pub/ui/select/SelectItem.vue'
import SelectLabel from '~/components/pub/ui/select/SelectLabel.vue'
import SelectSeparator from '~/components/pub/ui/select/SelectSeparator.vue'
import SelectTrigger from '~/components/pub/ui/select/SelectTrigger.vue'
import SelectValue from '~/components/pub/ui/select/SelectValue.vue'
import { cn } from '~/lib/utils'
interface Item {
value: string
label: string
code?: string
priority?: number // Priority untuk sorting: negatif = bawah, positif = atas, 0/undefined = normal sorting
}
const props = defineProps<{
@@ -24,40 +24,121 @@ const props = defineProps<{
label?: string
separator?: boolean
class?: string
isSelectedFirst?: boolean
preserveOrder?: boolean
isDisabled?: boolean
autoWidth?: boolean
autoFill?: boolean
// otherPlacement sudah tidak digunakan, diganti dengan priority system di Item interface
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
// Sort items with selected item first, then alphabetically
const sortedItems = computed(() => {
const itemsWithSelection = props.items.map(item => ({
const processedItems = computed(() => {
const itemsWithSelection = props.items.map((item) => ({
...item,
isSelected: item.value === props.modelValue,
}))
return itemsWithSelection.sort((a, b) => {
// Selected item always comes first
if (a.isSelected && !b.isSelected) return -1
if (!a.isSelected && b.isSelected) return 1
// Jika preserveOrder true, kembalikan urutan array asli
if (props.preserveOrder) {
return itemsWithSelection
}
// Sorting dengan priority system
return itemsWithSelection.sort((a, b) => {
const aPriority = a.priority ?? 0
const bPriority = b.priority ?? 0
// Jika ada priority, sort berdasarkan priority (descending: tinggi ke rendah)
if (aPriority !== bPriority) {
return bPriority - aPriority
}
// Jika priority sama (termasuk 0/undefined), lakukan sorting normal
if (props.isSelectedFirst) {
if (a.isSelected && !b.isSelected) return -1
if (!a.isSelected && b.isSelected) return 1
}
// If neither or both are selected, sort by label alphabetically
return a.label.localeCompare(b.label)
})
})
// Computed property untuk menghitung width optimal berdasarkan konten terpanjang
const optimalWidth = computed(() => {
// Jika autoWidth false atau undefined, gunakan full width
if (!props.autoWidth) return '100%'
if (!props.items.length) return 'auto'
// Mencari label terpanjang
const longestLabel = props.items.reduce((longest, item) => {
const itemText = item.code ? `${item.label} ${item.code}` : item.label
return itemText.length > longest.length ? itemText : longest
}, '')
// Menghitung width berdasarkan panjang karakter (estimasi)
// Setiap karakter ~0.6em, ditambah padding dan space untuk icon
const estimatedWidth = Math.max(longestLabel.length * 0.6 + 3, 8) // minimum 8em
return `${Math.min(estimatedWidth, 25)}em` // maksimum 25em
})
function onValueChange(value: string) {
emit('update:modelValue', value)
}
// Auto fill logic - automatically select first item if autoFill is enabled
watch(
() => props.items,
(newItems) => {
if (props.autoFill && newItems.length > 0 && !props.modelValue) {
// Auto select first item only if no value is currently selected
const firstItem = newItems[0]
if (firstItem?.value) {
emit('update:modelValue', firstItem.value)
}
}
},
{ immediate: true },
)
</script>
<template>
<SelectRoot :model-value="modelValue" @update:model-value="onValueChange">
<SelectTrigger :class="cn('w-full focus:outline-none focus:ring-1 focus:ring-black bg-white', props.class)">
<SelectValue :placeholder="placeholder || 'Pilih item'" :class="cn(
props.modelValue ? 'text-black' : 'text-muted-foreground',
)" />
<SelectRoot
:model-value="modelValue"
:disabled="isDisabled"
@update:model-value="onValueChange"
>
<SelectTrigger
:class="
cn(
'rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
{
'cursor-not-allowed bg-gray-100 opacity-50': isDisabled,
'bg-white text-black dark:bg-gray-800 dark:text-white': !isDisabled,
'w-full': !autoWidth,
},
props.class,
)
"
:style="autoWidth ? { width: optimalWidth, minWidth: '8em' } : {}"
icon-name="i-radix-icons-chevron-down"
icon-class="text-gray-500 dark:text-gray-300"
>
<SelectValue
:placeholder="placeholder || 'Pilih item'"
:class="
cn('text-sm', {
'text-gray-400': !props.modelValue,
'text-black dark:text-white': props.modelValue,
'text-gray-500': isDisabled,
})
"
/>
</SelectTrigger>
<SelectContent>
@@ -66,10 +147,18 @@ function onValueChange(value: string) {
{{ label }}
</SelectLabel>
<SelectItem v-for="item in sortedItems" :key="item.value" :value="item.value" class="cursor-pointer">
<div class="flex items-center justify-between w-full">
<SelectItem
v-for="item in processedItems"
:key="item.value"
:value="item.value"
class="cursor-pointer hover:bg-primary hover:text-white focus:bg-primary focus:text-white"
>
<div class="flex w-full items-center justify-between">
<span>{{ item.label }}</span>
<span v-if="item.code" class="text-xs text-muted-foreground ml-2">
<span
v-if="item.code"
class="ml-2 text-xs text-muted-foreground"
>
{{ item.code }}
</span>
</div>
@@ -0,0 +1,20 @@
<script setup lang="ts">
defineProps<{
label: string
}>()
</script>
<template>
<div class="flex flex-col gap-1 lg:grid lg:grid-cols-[180px_minmax(0,1fr)] lg:gap-x-3">
<!-- Label -->
<span class="text-md font-normal text-muted-foreground">
{{ label }}
</span>
<!-- Value -->
<span class="truncate lg:whitespace-normal">
<span class="me-3 hidden lg:inline-block">:</span>
<slot />
</span>
</div>
</template>
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { cn } from '~/lib/utils'
defineProps<{
title: string
headerClass?: string
contentClass?: string
}>()
</script>
<template>
<section class="mb-6">
<h3 :class="cn('mb-3 flex items-center gap-1 pb-1 text-base font-semibold', headerClass)">
<slot name="icon" />
{{ title }}
</h3>
<div :class="cn('space-y-2', contentClass)">
<slot />
</div>
</section>
</template>
+4 -3
View File
@@ -41,11 +41,12 @@ const isOpen = computed({
<template>
<Dialog v-model:open="isOpen">
<DialogContent
:class="sizeClass" @interact-outside="(e: any) => preventOutside && e.preventDefault()"
:class="sizeClass"
@interact-outside="(e: any) => preventOutside && e.preventDefault()"
@pointer-down-outside="(e: any) => preventOutside && e.preventDefault()"
>
>
<DialogHeader>
<DialogTitle>{{ props.title }}</DialogTitle>
<DialogTitle class="text-[20px]">{{ props.title }}</DialogTitle>
<DialogDescription v-if="props.description">{{ props.description }}</DialogDescription>
</DialogHeader>
<slot />
+20 -5
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
type ClickType = 'cancel' | 'draft' | 'submit'
type ClickType = 'cancel' | 'edit'
const emit = defineEmits<{
(e: 'click', type: ClickType): void
@@ -12,12 +12,27 @@ function onClick(type: ClickType) {
<template>
<div class="m-2 flex gap-2 px-2">
<Button class="bg-gray-400" type="button" @click="onClick('cancel')">
<Icon name="i-lucide-arrow-left" class="me-2 align-middle" />
<Button
class="bg-gray-400"
type="button"
@click="onClick('cancel')"
>
<Icon
name="i-lucide-arrow-left"
class="me-2 align-middle"
/>
Back
</Button>
<Button class="bg-orange-500" variant="outline" type="button" @click="onClick('draft')">
<Icon name="i-lucide-file" class="me-2 align-middle" />
<Button
class="bg-orange-500"
variant="outline"
type="button"
@click="onClick('edit')"
>
<Icon
name="i-lucide-file"
class="me-2 align-middle"
/>
Edit
</Button>
</div>
+16 -4
View File
@@ -12,12 +12,24 @@ function onClick(type: ClickType) {
<template>
<div class="m-2 flex gap-2 px-2">
<Button class="bg-gray-400" @click="onClick('cancel')">
<Icon name="i-lucide-arrow-left" class="me-2 align-middle" />
<Button
class="bg-gray-400"
@click="onClick('cancel')"
>
<Icon
name="i-lucide-arrow-left"
class="me-2 align-middle"
/>
Back
</Button>
<Button class="bg-primary" @click="onClick('submit')">
<Icon name="i-lucide-check" class="me-2 align-middle" />
<Button
class="bg-primary"
@click="onClick('submit')"
>
<Icon
name="i-lucide-check"
class="me-2 align-middle"
/>
Submit
</Button>
</div>
+27 -7
View File
@@ -1,9 +1,11 @@
<script setup lang="ts">
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
import { cn } from '~/lib/utils'
const props = defineProps<{
prep: HeaderPrep
refSearchNav?: RefSearchNav
class?: string
}>()
function emitSearchNavClick() {
@@ -21,15 +23,24 @@ function btnClick() {
<template>
<header>
<div class="flex items-center justify-between">
<div
class="flex items-center justify-between"
:class="cn('', props.class)"
>
<div class="flex items-center">
<div class="md:text-base xl:text-lg font-semibold text-gray-900">
<Icon :name="props.prep.icon!" class="mr-2 size-4 md:size-6 align-middle" />
<div class="font-semibold text-gray-900 md:text-base xl:text-lg">
<Icon
:name="props.prep.icon!"
class="mr-2 size-4 align-middle md:size-6"
/>
{{ props.prep.title }}
</div>
</div>
<div class="flex items-center">
<div v-if="props.refSearchNav" class="text-lg text-gray-900">
<div
v-if="props.refSearchNav"
class="text-lg text-gray-900"
>
<Input
type="text"
placeholder="Search"
@@ -38,9 +49,18 @@ function btnClick() {
@input="onInput"
/>
</div>
<div v-if="prep.addNav" class="flex items-center ms-2">
<Button class="rounded-md border border-gray-300 text-white" @click="btnClick">
<Icon name="i-lucide-plus" class="align-middle" />
<div
v-if="prep.addNav"
class="ms-2 flex items-center"
>
<Button
class="rounded-md border border-gray-300 text-white"
@click="btnClick"
>
<Icon
name="i-lucide-plus"
class="mr-2 h-4 w-4 align-middle"
/>
{{ prep.addNav.label }}
</Button>
</div>
+11 -1
View File
@@ -5,7 +5,17 @@ import { CalendarRoot, useForwardPropsEmits } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '~/lib/utils'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNextButton, CalendarPrevButton } from '.'
import CalendarCell from './CalendarCell.vue'
import CalendarCellTrigger from './CalendarCellTrigger.vue'
import CalendarGrid from './CalendarGrid.vue'
import CalendarGridBody from './CalendarGridBody.vue'
import CalendarGridHead from './CalendarGridHead.vue'
import CalendarGridRow from './CalendarGridRow.vue'
import CalendarHeadCell from './CalendarHeadCell.vue'
import CalendarHeader from './CalendarHeader.vue'
import CalendarHeading from './CalendarHeading.vue'
import CalendarNextButton from './CalendarNextButton.vue'
import CalendarPrevButton from './CalendarPrevButton.vue'
const props = defineProps<CalendarRootProps & { class?: HTMLAttributes['class'] }>()
+1 -1
View File
@@ -11,6 +11,6 @@ const { name, formMessageId } = useFormField()
:id="formMessageId"
as="p"
:name="toValue(name)"
class="text-[0.8rem] text-destructive font-medium"
class="text-[0.8rem] text-destructive font-sans"
/>
</template>
+6 -8
View File
@@ -1,14 +1,12 @@
<script setup lang="ts">
import type { SelectRootEmits, SelectRootProps } from 'radix-vue'
import { SelectRoot, useForwardPropsEmits } from 'radix-vue'
import {
SelectContent,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue,
} from '~/components/pub/ui/select'
import SelectContent from './SelectContent.vue'
import SelectGroup from './SelectGroup.vue'
import SelectItem from './SelectItem.vue'
import SelectSeparator from './SelectSeparator.vue'
import SelectTrigger from './SelectTrigger.vue'
import SelectValue from './SelectValue.vue'
interface Item {
value: string
@@ -5,7 +5,8 @@ import { SelectContent, SelectPortal, SelectViewport, useForwardPropsEmits } fro
import { computed } from 'vue'
import { cn } from '~/lib/utils'
import { SelectScrollDownButton, SelectScrollUpButton } from '.'
import SelectScrollDownButton from './SelectScrollDownButton.vue'
import SelectScrollUpButton from './SelectScrollUpButton.vue'
defineOptions({
inheritAttrs: false,