pukaad-ui-lib 1.333.0 → 1.334.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/button.vue +10 -10
- package/dist/runtime/components/card/card-place-detail.vue +5 -4
- package/dist/runtime/components/card/card-place-search-row.d.vue.ts +20 -0
- package/dist/runtime/components/card/card-place-search-row.vue +47 -0
- package/dist/runtime/components/card/card-place-search-row.vue.d.ts +20 -0
- package/dist/runtime/components/carousel.vue +31 -31
- package/dist/runtime/components/display/display-image-place.vue +98 -98
- package/dist/runtime/components/input/input-autocomplete.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-autocomplete.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-localized-name.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-localized-name.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-rating.vue +6 -6
- package/dist/runtime/components/input/input-suggest.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-suggest.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-tag.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-tag.vue.d.ts +1 -1
- package/dist/runtime/components/modal/modal-media-view.vue +136 -136
- package/dist/runtime/components/ui/alert-dialog/AlertDialog.vue +1 -2
- package/dist/runtime/components/ui/calendar/Calendar.d.vue.ts +1 -1
- package/dist/runtime/components/ui/calendar/Calendar.vue.d.ts +1 -1
- package/dist/runtime/components/ui/checkbox/Checkbox.vue +2 -4
- package/dist/runtime/components/ui/command/CommandDialog.vue +0 -1
- package/dist/runtime/components/ui/dialog/Dialog.vue +1 -2
- package/dist/runtime/components/ui/dropdown-menu/DropdownMenuContent.vue +0 -1
- package/dist/runtime/components/ui/dropdown-menu/DropdownMenuSubContent.vue +0 -1
- package/dist/runtime/components/ui/popover/PopoverContent.vue +0 -2
- package/dist/runtime/components/ui/select/Select.vue +0 -1
- package/dist/runtime/components/ui/select/SelectContent.vue +0 -2
- package/dist/runtime/components/ui/separator/Separator.d.vue.ts +1 -1
- package/dist/runtime/components/ui/separator/Separator.vue.d.ts +1 -1
- package/dist/runtime/components/ui/sheet/Sheet.vue +1 -2
- package/dist/runtime/components/ui/tabs/Tabs.d.vue.ts +2 -2
- package/dist/runtime/components/ui/tabs/Tabs.vue.d.ts +2 -2
- package/dist/runtime/components/ui/tooltip/TooltipProvider.vue +1 -2
- package/package.json +1 -1
- /package/dist/runtime/assets/svg/socials/{Whatsapp.svg → WhatsApp.svg} +0 -0
package/dist/module.json
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
props.variant === 'text' || props.variant === 'link' ? '!p-0 !h-auto' : '',
|
|
5
5
|
props.circle && 'rounded-full aspect-square',
|
|
6
6
|
props.class
|
|
7
|
-
]" :variant="props.variant" :color="props.color" :size="resolvedSize" :type="props.type"
|
|
8
|
-
:disabled="props.loading || props.disabled">
|
|
9
|
-
<span class="inline-grid place-items-center">
|
|
10
|
-
<span class="inline-flex items-center justify-center gap-[8px] [grid-area:1/1]"
|
|
11
|
-
:class="{ invisible: props.loading }">
|
|
12
|
-
<slot />
|
|
13
|
-
</span>
|
|
14
|
-
<ShadSpinner v-if="props.loading" class="[grid-area:1/1]" />
|
|
15
|
-
</span>
|
|
16
|
-
</ShadButton>
|
|
7
|
+
]" :variant="props.variant" :color="props.color" :size="resolvedSize" :type="props.type"
|
|
8
|
+
:disabled="props.loading || props.disabled">
|
|
9
|
+
<span class="inline-grid place-items-center">
|
|
10
|
+
<span class="inline-flex items-center justify-center gap-[8px] [grid-area:1/1]"
|
|
11
|
+
:class="{ invisible: props.loading }">
|
|
12
|
+
<slot />
|
|
13
|
+
</span>
|
|
14
|
+
<ShadSpinner v-if="props.loading" class="[grid-area:1/1]" />
|
|
15
|
+
</span>
|
|
16
|
+
</ShadButton>
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<script setup>
|
|
@@ -92,17 +92,18 @@ const DAY_NAMES = [
|
|
|
92
92
|
const props = defineProps({
|
|
93
93
|
item: { type: Object, required: false, default: () => ({}) }
|
|
94
94
|
});
|
|
95
|
+
const toArray = (value) => Array.isArray(value) ? value : [];
|
|
95
96
|
const categoryText = computed(
|
|
96
|
-
() => (props.item.categories
|
|
97
|
+
() => toArray(props.item.categories).filter((c) => c?.category_name).map((c) => c.category_name).join(", ")
|
|
97
98
|
);
|
|
98
99
|
const businessHours = computed(
|
|
99
|
-
() => (props.item.opening_hours
|
|
100
|
+
() => toArray(props.item.opening_hours).filter((h) => !!h).map((h) => ({
|
|
100
101
|
day: DAY_NAMES[h.day] ?? "",
|
|
101
|
-
time_range: !h.isOpen ? "\u0E2B\u0E22\u0E38\u0E14\u0E17\u0E33\u0E01\u0E32\u0E23" : h.isAllDay ? "\u0E40\u0E1B\u0E34\u0E14\u0E15\u0E25\u0E2D\u0E14 24 \u0E0A\u0E21." : (h.timeSlots
|
|
102
|
+
time_range: !h.isOpen ? "\u0E2B\u0E22\u0E38\u0E14\u0E17\u0E33\u0E01\u0E32\u0E23" : h.isAllDay ? "\u0E40\u0E1B\u0E34\u0E14\u0E15\u0E25\u0E2D\u0E14 24 \u0E0A\u0E21." : toArray(h.timeSlots).map((t) => `${t.start} - ${t.end}`).join(", ") || "\u0E40\u0E1B\u0E34\u0E14"
|
|
102
103
|
}))
|
|
103
104
|
);
|
|
104
105
|
const socialLinks = computed(
|
|
105
|
-
() => (props.item.contact_channels
|
|
106
|
+
() => toArray(props.item.contact_channels).filter((ch) => ch?.value).map((ch) => ({
|
|
106
107
|
name: ch.channel_type ?? "link",
|
|
107
108
|
link: ch.value
|
|
108
109
|
}))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface PlaceSearchRowItem {
|
|
2
|
+
id: string;
|
|
3
|
+
business_name: string;
|
|
4
|
+
image_cover?: string;
|
|
5
|
+
rating?: number;
|
|
6
|
+
review_count?: number;
|
|
7
|
+
address?: {
|
|
8
|
+
full_address?: string;
|
|
9
|
+
} | null;
|
|
10
|
+
}
|
|
11
|
+
export interface CardPlaceSearchRowProps {
|
|
12
|
+
item: PlaceSearchRowItem;
|
|
13
|
+
}
|
|
14
|
+
declare const __VLS_export: import("vue").DefineComponent<CardPlaceSearchRowProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
+
select: (item: PlaceSearchRowItem) => any;
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<CardPlaceSearchRowProps> & Readonly<{
|
|
17
|
+
onSelect?: ((item: PlaceSearchRowItem) => any) | undefined;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex items-center gap-[8px] px-[8px] py-[6px] rounded-md hover:bg-bright cursor-pointer"
|
|
4
|
+
@click="emit('select', props.item)"
|
|
5
|
+
>
|
|
6
|
+
<!-- thumbnail -->
|
|
7
|
+
<Image
|
|
8
|
+
v-if="props.item.image_cover"
|
|
9
|
+
:src="props.item.image_cover"
|
|
10
|
+
:width="71"
|
|
11
|
+
:height="40"
|
|
12
|
+
class="shrink-0 rounded-[4px] object-cover"
|
|
13
|
+
/>
|
|
14
|
+
<div
|
|
15
|
+
v-else
|
|
16
|
+
class="w-[71px] h-[40px] bg-cloud shrink-0 rounded-[4px] flex items-center justify-center"
|
|
17
|
+
>
|
|
18
|
+
<Icon name="fa6-solid:map-location-dot" :size="20" class="text-gray" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<!-- content -->
|
|
22
|
+
<div class="flex flex-col gap-[4px] min-w-0 flex-1">
|
|
23
|
+
<div class="font-body-medium text-black truncate">
|
|
24
|
+
{{ props.item.business_name }}
|
|
25
|
+
</div>
|
|
26
|
+
<div class="flex items-center gap-[4px] min-w-0">
|
|
27
|
+
<InputRating :model-value="props.item.rating ?? 0" readonly :size="14" />
|
|
28
|
+
<template v-if="props.item.review_count != null">
|
|
29
|
+
<span class="font-body-small text-gray shrink-0">
|
|
30
|
+
{{ $convert.convertNumber(props.item.review_count) }}
|
|
31
|
+
</span>
|
|
32
|
+
<span class="font-body-small text-gray shrink-0">•</span>
|
|
33
|
+
</template>
|
|
34
|
+
<span class="font-body-small text-gray truncate">
|
|
35
|
+
{{ props.item.address?.full_address }}
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup>
|
|
43
|
+
const props = defineProps({
|
|
44
|
+
item: { type: Object, required: true }
|
|
45
|
+
});
|
|
46
|
+
const emit = defineEmits(["select"]);
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface PlaceSearchRowItem {
|
|
2
|
+
id: string;
|
|
3
|
+
business_name: string;
|
|
4
|
+
image_cover?: string;
|
|
5
|
+
rating?: number;
|
|
6
|
+
review_count?: number;
|
|
7
|
+
address?: {
|
|
8
|
+
full_address?: string;
|
|
9
|
+
} | null;
|
|
10
|
+
}
|
|
11
|
+
export interface CardPlaceSearchRowProps {
|
|
12
|
+
item: PlaceSearchRowItem;
|
|
13
|
+
}
|
|
14
|
+
declare const __VLS_export: import("vue").DefineComponent<CardPlaceSearchRowProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
+
select: (item: PlaceSearchRowItem) => any;
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<CardPlaceSearchRowProps> & Readonly<{
|
|
17
|
+
onSelect?: ((item: PlaceSearchRowItem) => any) | undefined;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative w-full h-full">
|
|
3
|
-
<div class="absolute top-[20px] left-1/2 transform -translate-x-1/2 z-[50]">
|
|
4
|
-
<div class="rounded-full py-[6px] px-[16px] bg-black/60 flex items-center justify-center">
|
|
5
|
-
<span class="text-white text-sm font-medium tracking-widest">
|
|
6
|
-
{{ currentIndex + 1 }} / {{ props.items.length }}
|
|
7
|
-
</span>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<ShadCarousel
|
|
12
|
-
:opts="{ startIndex: props.selectIndex }"
|
|
13
|
-
class="carousel-media w-full h-full"
|
|
14
|
-
@init-api="onInitApi"
|
|
15
|
-
>
|
|
16
|
-
<ShadCarouselContent class="h-full">
|
|
17
|
-
<ShadCarouselItem
|
|
18
|
-
v-for="(item, i) in props.items"
|
|
19
|
-
:key="i"
|
|
20
|
-
class="h-full flex items-center justify-center"
|
|
21
|
-
>
|
|
22
|
-
<img
|
|
23
|
-
:src="item.url"
|
|
24
|
-
class="w-full h-full object-contain select-none"
|
|
25
|
-
draggable="false"
|
|
26
|
-
/>
|
|
27
|
-
</ShadCarouselItem>
|
|
28
|
-
</ShadCarouselContent>
|
|
29
|
-
<ShadCarouselPrevious v-if="props.items.length > 1" class="!left-[12px]" />
|
|
30
|
-
<ShadCarouselNext v-if="props.items.length > 1" class="!right-[12px]" />
|
|
31
|
-
</ShadCarousel>
|
|
32
|
-
</div>
|
|
2
|
+
<div class="relative w-full h-full">
|
|
3
|
+
<div class="absolute top-[20px] left-1/2 transform -translate-x-1/2 z-[50]">
|
|
4
|
+
<div class="rounded-full py-[6px] px-[16px] bg-black/60 flex items-center justify-center">
|
|
5
|
+
<span class="text-white text-sm font-medium tracking-widest">
|
|
6
|
+
{{ currentIndex + 1 }} / {{ props.items.length }}
|
|
7
|
+
</span>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<ShadCarousel
|
|
12
|
+
:opts="{ startIndex: props.selectIndex }"
|
|
13
|
+
class="carousel-media w-full h-full"
|
|
14
|
+
@init-api="onInitApi"
|
|
15
|
+
>
|
|
16
|
+
<ShadCarouselContent class="h-full">
|
|
17
|
+
<ShadCarouselItem
|
|
18
|
+
v-for="(item, i) in props.items"
|
|
19
|
+
:key="i"
|
|
20
|
+
class="h-full flex items-center justify-center"
|
|
21
|
+
>
|
|
22
|
+
<img
|
|
23
|
+
:src="item.url"
|
|
24
|
+
class="w-full h-full object-contain select-none"
|
|
25
|
+
draggable="false"
|
|
26
|
+
/>
|
|
27
|
+
</ShadCarouselItem>
|
|
28
|
+
</ShadCarouselContent>
|
|
29
|
+
<ShadCarouselPrevious v-if="props.items.length > 1" class="!left-[12px]" />
|
|
30
|
+
<ShadCarouselNext v-if="props.items.length > 1" class="!right-[12px]" />
|
|
31
|
+
</ShadCarousel>
|
|
32
|
+
</div>
|
|
33
33
|
</template>
|
|
34
34
|
|
|
35
35
|
<script setup>
|
|
@@ -1,102 +1,102 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<!-- Personal state → Featured layout — 1 รูป: w-full -->
|
|
3
|
-
<div v-if="layoutMode === 'featured' && photos.length === 1" class="h-[360px]">
|
|
4
|
-
<div class="w-full h-full rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
|
|
5
|
-
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
6
|
-
</div>
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
<!-- Personal state → Featured layout — 2 รูป: แบ่งครึ่ง w-full -->
|
|
10
|
-
<div v-else-if="layoutMode === 'featured' && photos.length === 2" class="flex gap-[8px] h-[360px]">
|
|
11
|
-
<div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
|
|
12
|
-
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
13
|
-
</div>
|
|
14
|
-
<div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
|
|
15
|
-
<Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
|
|
19
|
-
<!-- Personal state → Featured layout — 3+ รูป: layout เดิม -->
|
|
20
|
-
<div v-else-if="layoutMode === 'featured'" class="flex gap-[8px] h-[360px]">
|
|
21
|
-
<div v-if="photos[0]" class="w-[620px] shrink-0 rounded-[8px] overflow-hidden cursor-pointer"
|
|
22
|
-
@click="openLightbox('photo', 0)">
|
|
23
|
-
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<div v-if="photos[1]" class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
|
|
27
|
-
<Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
<div class="w-[306px] shrink-0 flex flex-col gap-[8px]">
|
|
31
|
-
<div v-if="photos[2]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
32
|
-
@click="openLightbox('photo', 2)">
|
|
33
|
-
<Image :src="photos[2].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
34
|
-
<div v-if="featuredPhoto2Overflow > 0"
|
|
35
|
-
class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
36
|
-
<Icon name="fa6-solid:image" :size="42" />
|
|
37
|
-
<span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span>
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
<div v-if="videos[0]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
42
|
-
@click="openLightbox('video', 0)">
|
|
43
|
-
<Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
44
|
-
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
45
|
-
<Icon name="fa6-solid:video" :size="42" />
|
|
46
|
-
<span class="font-body-large">วิดีโอ ({{ videos.length }})</span>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
<div v-else-if="photos[3]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
50
|
-
@click="openLightbox('photo', 3)">
|
|
51
|
-
<Image :src="photos[3].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
52
|
-
<div v-if="featuredPhoto3Overflow > 0"
|
|
53
|
-
class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
54
|
-
<Icon name="fa6-solid:image" :size="42" />
|
|
55
|
-
<span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<!-- Office / Business state → Album layout -->
|
|
62
|
-
<div v-else-if="layoutMode === 'album'" class="grid grid-cols-5 gap-[10px]">
|
|
63
|
-
<!-- Row 1: photos[0..3] -->
|
|
64
|
-
<div v-for="(photo, i) in galleryRow1" :key="`g1-${i}`"
|
|
65
|
-
class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', i)">
|
|
66
|
-
<Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
<!-- Row 1 col 5: image count overlay -->
|
|
70
|
-
<div v-show="galleryCountPhoto != null"
|
|
71
|
-
class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
72
|
-
@click="openLightbox('photo', 8)">
|
|
73
|
-
<Image v-if="galleryCountPhoto" :src="galleryCountPhoto.url" class="w-full h-full object-cover" width="auto"
|
|
74
|
-
height="auto" />
|
|
75
|
-
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
76
|
-
<Icon name="fa6-solid:image" :size="35" />
|
|
77
|
-
<span class="font-label-medium">รูปภาพ ({{ convertNumber(photos.length - 8) }})</span>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<!-- Row 2: photos[4..7] -->
|
|
82
|
-
<div v-for="(photo, i) in galleryRow2" :key="`g2-${i}`"
|
|
83
|
-
class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 4 + i)">
|
|
84
|
-
<Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
<!-- Row 2 col 5: video overlay -->
|
|
88
|
-
<div v-if="videos[0]" class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
89
|
-
@click="openLightbox('video', 0)">
|
|
90
|
-
<Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
91
|
-
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
92
|
-
<Icon name="fa6-solid:video" :size="35" />
|
|
93
|
-
<span class="font-label-medium">วิดีโอ ({{ videos.length }})</span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
<!-- Lightbox modal -->
|
|
99
|
-
<ModalMediaView v-model="isMediaOpen" :items="mediaItems" :title="props.title" :start-index="mediaStartIndex" />
|
|
2
|
+
<!-- Personal state → Featured layout — 1 รูป: w-full -->
|
|
3
|
+
<div v-if="layoutMode === 'featured' && photos.length === 1" class="h-[360px]">
|
|
4
|
+
<div class="w-full h-full rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
|
|
5
|
+
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<!-- Personal state → Featured layout — 2 รูป: แบ่งครึ่ง w-full -->
|
|
10
|
+
<div v-else-if="layoutMode === 'featured' && photos.length === 2" class="flex gap-[8px] h-[360px]">
|
|
11
|
+
<div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
|
|
12
|
+
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
13
|
+
</div>
|
|
14
|
+
<div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
|
|
15
|
+
<Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<!-- Personal state → Featured layout — 3+ รูป: layout เดิม -->
|
|
20
|
+
<div v-else-if="layoutMode === 'featured'" class="flex gap-[8px] h-[360px]">
|
|
21
|
+
<div v-if="photos[0]" class="w-[620px] shrink-0 rounded-[8px] overflow-hidden cursor-pointer"
|
|
22
|
+
@click="openLightbox('photo', 0)">
|
|
23
|
+
<Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div v-if="photos[1]" class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
|
|
27
|
+
<Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="w-[306px] shrink-0 flex flex-col gap-[8px]">
|
|
31
|
+
<div v-if="photos[2]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
32
|
+
@click="openLightbox('photo', 2)">
|
|
33
|
+
<Image :src="photos[2].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
34
|
+
<div v-if="featuredPhoto2Overflow > 0"
|
|
35
|
+
class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
36
|
+
<Icon name="fa6-solid:image" :size="42" />
|
|
37
|
+
<span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div v-if="videos[0]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
42
|
+
@click="openLightbox('video', 0)">
|
|
43
|
+
<Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
44
|
+
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
45
|
+
<Icon name="fa6-solid:video" :size="42" />
|
|
46
|
+
<span class="font-body-large">วิดีโอ ({{ videos.length }})</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div v-else-if="photos[3]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
50
|
+
@click="openLightbox('photo', 3)">
|
|
51
|
+
<Image :src="photos[3].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
52
|
+
<div v-if="featuredPhoto3Overflow > 0"
|
|
53
|
+
class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
54
|
+
<Icon name="fa6-solid:image" :size="42" />
|
|
55
|
+
<span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<!-- Office / Business state → Album layout -->
|
|
62
|
+
<div v-else-if="layoutMode === 'album'" class="grid grid-cols-5 gap-[10px]">
|
|
63
|
+
<!-- Row 1: photos[0..3] -->
|
|
64
|
+
<div v-for="(photo, i) in galleryRow1" :key="`g1-${i}`"
|
|
65
|
+
class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', i)">
|
|
66
|
+
<Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Row 1 col 5: image count overlay -->
|
|
70
|
+
<div v-show="galleryCountPhoto != null"
|
|
71
|
+
class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
72
|
+
@click="openLightbox('photo', 8)">
|
|
73
|
+
<Image v-if="galleryCountPhoto" :src="galleryCountPhoto.url" class="w-full h-full object-cover" width="auto"
|
|
74
|
+
height="auto" />
|
|
75
|
+
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
76
|
+
<Icon name="fa6-solid:image" :size="35" />
|
|
77
|
+
<span class="font-label-medium">รูปภาพ ({{ convertNumber(photos.length - 8) }})</span>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Row 2: photos[4..7] -->
|
|
82
|
+
<div v-for="(photo, i) in galleryRow2" :key="`g2-${i}`"
|
|
83
|
+
class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 4 + i)">
|
|
84
|
+
<Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- Row 2 col 5: video overlay -->
|
|
88
|
+
<div v-if="videos[0]" class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
|
|
89
|
+
@click="openLightbox('video', 0)">
|
|
90
|
+
<Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
|
|
91
|
+
<div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
|
|
92
|
+
<Icon name="fa6-solid:video" :size="35" />
|
|
93
|
+
<span class="font-label-medium">วิดีโอ ({{ videos.length }})</span>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Lightbox modal -->
|
|
99
|
+
<ModalMediaView v-model="isMediaOpen" :items="mediaItems" :title="props.title" :start-index="mediaStartIndex" />
|
|
100
100
|
</template>
|
|
101
101
|
|
|
102
102
|
<script setup>
|
|
@@ -51,8 +51,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
51
51
|
options: AutocompleteOption[] | string[] | number[];
|
|
52
52
|
description: string;
|
|
53
53
|
labelKey: string;
|
|
54
|
-
placeholder: string;
|
|
55
54
|
limit: number;
|
|
55
|
+
placeholder: string;
|
|
56
56
|
disabledErrorMessage: boolean;
|
|
57
57
|
disabledBorder: boolean;
|
|
58
58
|
showCounter: boolean;
|
|
@@ -51,8 +51,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
51
51
|
options: AutocompleteOption[] | string[] | number[];
|
|
52
52
|
description: string;
|
|
53
53
|
labelKey: string;
|
|
54
|
-
placeholder: string;
|
|
55
54
|
limit: number;
|
|
55
|
+
placeholder: string;
|
|
56
56
|
disabledErrorMessage: boolean;
|
|
57
57
|
disabledBorder: boolean;
|
|
58
58
|
showCounter: boolean;
|
|
@@ -12,8 +12,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
12
12
|
name: string;
|
|
13
13
|
modelValue: LocalizedNameItem[];
|
|
14
14
|
options: InputSelectItem[];
|
|
15
|
-
placeholder: string;
|
|
16
15
|
limit: number;
|
|
16
|
+
placeholder: string;
|
|
17
17
|
showCounter: boolean;
|
|
18
18
|
addLabel: string;
|
|
19
19
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -12,8 +12,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
12
12
|
name: string;
|
|
13
13
|
modelValue: LocalizedNameItem[];
|
|
14
14
|
options: InputSelectItem[];
|
|
15
|
-
placeholder: string;
|
|
16
15
|
limit: number;
|
|
16
|
+
placeholder: string;
|
|
17
17
|
showCounter: boolean;
|
|
18
18
|
addLabel: string;
|
|
19
19
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<!-- v-html เพื่อหลีกเลี่ยง Vue patch SVG attributes เป็น DOM property (read-only getter bug) -->
|
|
3
|
-
<div
|
|
4
|
-
class="inline-flex items-center gap-0"
|
|
5
|
-
v-html="starsHtml"
|
|
6
|
-
@click="handleClick"
|
|
7
|
-
/>
|
|
2
|
+
<!-- v-html เพื่อหลีกเลี่ยง Vue patch SVG attributes เป็น DOM property (read-only getter bug) -->
|
|
3
|
+
<div
|
|
4
|
+
class="inline-flex items-center gap-0"
|
|
5
|
+
v-html="starsHtml"
|
|
6
|
+
@click="handleClick"
|
|
7
|
+
/>
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script setup>
|
|
@@ -21,8 +21,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
21
21
|
required: boolean;
|
|
22
22
|
description: string;
|
|
23
23
|
labelKey: string;
|
|
24
|
-
placeholder: string;
|
|
25
24
|
limit: number;
|
|
25
|
+
placeholder: string;
|
|
26
26
|
disabledErrorMessage: boolean;
|
|
27
27
|
disabledBorder: boolean;
|
|
28
28
|
showCounter: boolean;
|
|
@@ -21,8 +21,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
21
21
|
required: boolean;
|
|
22
22
|
description: string;
|
|
23
23
|
labelKey: string;
|
|
24
|
-
placeholder: string;
|
|
25
24
|
limit: number;
|
|
25
|
+
placeholder: string;
|
|
26
26
|
disabledErrorMessage: boolean;
|
|
27
27
|
disabledBorder: boolean;
|
|
28
28
|
showCounter: boolean;
|
|
@@ -26,8 +26,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
26
26
|
}>, {
|
|
27
27
|
name: string;
|
|
28
28
|
state: "user" | "admin";
|
|
29
|
-
placeholder: string;
|
|
30
29
|
limit: number;
|
|
30
|
+
placeholder: string;
|
|
31
31
|
ignore: string[];
|
|
32
32
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
33
33
|
declare const _default: typeof __VLS_export;
|
|
@@ -26,8 +26,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
26
26
|
}>, {
|
|
27
27
|
name: string;
|
|
28
28
|
state: "user" | "admin";
|
|
29
|
-
placeholder: string;
|
|
30
29
|
limit: number;
|
|
30
|
+
placeholder: string;
|
|
31
31
|
ignore: string[];
|
|
32
32
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
33
33
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,140 +1,140 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Modal v-model="isOpen" class="sm:max-w-[1280px]" disable-padding>
|
|
3
|
-
<template #header>
|
|
4
|
-
<ShadDialogTitle class="p-4">{{ props.title }}</ShadDialogTitle>
|
|
5
|
-
</template>
|
|
6
|
-
<div class="relative h-[824px] bg-black overflow-hidden">
|
|
7
|
-
<Carousel
|
|
8
|
-
:items="props.items"
|
|
9
|
-
:select-index="props.startIndex"
|
|
10
|
-
class="h-full"
|
|
11
|
-
@update:current-index="currentIndex = $event"
|
|
12
|
-
/>
|
|
13
|
-
|
|
14
|
-
<!-- description overlay (แสดงเฉพาะเมื่อ item ปัจจุบันมี description และไม่มี review) -->
|
|
15
|
-
<div
|
|
16
|
-
v-if="!props.review && currentItem?.description"
|
|
17
|
-
class="absolute bottom-0 w-full z-10"
|
|
18
|
-
style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
|
|
19
|
-
>
|
|
20
|
-
<div class="p-[16px] flex flex-col gap-[4px] text-white font-body-large">
|
|
21
|
-
<div class="font-body-large-prominent">ผู้ดูแลระบบ</div>
|
|
22
|
-
<div>{{ currentItem.description }}</div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<!-- Review info overlay (แสดงเมื่อมีข้อมูล review) -->
|
|
27
|
-
<div
|
|
28
|
-
v-if="props.review"
|
|
29
|
-
class="absolute bottom-0 w-full z-10"
|
|
30
|
-
style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
|
|
31
|
-
>
|
|
32
|
-
<div class="flex flex-col w-full">
|
|
33
|
-
<div
|
|
34
|
-
v-if="!showMoreDescription"
|
|
35
|
-
class="flex gap-[16px] p-[16px] w-full text-white"
|
|
36
|
-
>
|
|
37
|
-
<Avatar
|
|
38
|
-
:src="props.review.user?.avatar"
|
|
39
|
-
:alt="props.review.user?.name"
|
|
40
|
-
:size="36"
|
|
41
|
-
class="shrink-0"
|
|
42
|
-
/>
|
|
43
|
-
<div class="flex flex-col gap-[8px] w-full">
|
|
44
|
-
<div class="flex flex-col gap-[8px]">
|
|
45
|
-
<div class="flex justify-between w-full">
|
|
46
|
-
<span class="font-body-large">
|
|
47
|
-
{{ props.review.user?.name }}
|
|
48
|
-
</span>
|
|
49
|
-
<Icon name="lucide:flag" />
|
|
50
|
-
</div>
|
|
51
|
-
<div class="flex flex-col">
|
|
52
|
-
<span class="font-body-small">
|
|
53
|
-
{{ convertNumber(props.review.user?.review_count ?? 0) }}
|
|
54
|
-
รีวิว •
|
|
55
|
-
{{ convertNumber(props.review.user?.like_count ?? 0) }}
|
|
56
|
-
ชื่นชอบรีวิว
|
|
57
|
-
</span>
|
|
58
|
-
<div
|
|
59
|
-
v-if="props.review.review"
|
|
60
|
-
class="flex items-center gap-[8px]"
|
|
61
|
-
>
|
|
62
|
-
<InputRating
|
|
63
|
-
:size="14"
|
|
64
|
-
readonly
|
|
65
|
-
:model-value="props.review.review.rating"
|
|
66
|
-
/>
|
|
67
|
-
<span class="font-body-small opacity-75">
|
|
68
|
-
{{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
|
|
69
|
-
</span>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
<p
|
|
74
|
-
v-if="props.review.review?.description"
|
|
75
|
-
class="font-body-large line-clamp-1 cursor-pointer"
|
|
76
|
-
@click="showMoreDescription = true"
|
|
77
|
-
>
|
|
78
|
-
{{ props.review.review.description }}
|
|
79
|
-
</p>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
<div
|
|
83
|
-
v-if="showMoreDescription"
|
|
84
|
-
class="flex gap-[16px] bg-white rounded-t-[16px] w-full p-[16px] max-h-[322px]"
|
|
85
|
-
>
|
|
86
|
-
<Avatar
|
|
87
|
-
:src="props.review.user?.avatar"
|
|
88
|
-
:alt="props.review.user?.name"
|
|
89
|
-
:size="36"
|
|
90
|
-
class="shrink-0"
|
|
91
|
-
/>
|
|
92
|
-
<div class="flex flex-col gap-[8px] w-full">
|
|
93
|
-
<div class="flex flex-col gap-[8px]">
|
|
94
|
-
<div class="flex justify-between w-full">
|
|
95
|
-
<span class="font-body-large">
|
|
96
|
-
{{ props.review.user?.name }}
|
|
97
|
-
</span>
|
|
98
|
-
<Icon
|
|
99
|
-
name="lucide:x"
|
|
100
|
-
class="cursor-pointer"
|
|
101
|
-
@click="showMoreDescription = false"
|
|
102
|
-
/>
|
|
103
|
-
</div>
|
|
104
|
-
<div class="flex flex-col">
|
|
105
|
-
<span class="font-body-small">
|
|
106
|
-
{{ convertNumber(props.review.user?.review_count ?? 0) }}
|
|
107
|
-
รีวิว •
|
|
108
|
-
{{ convertNumber(props.review.user?.like_count ?? 0) }}
|
|
109
|
-
ชื่นชอบรีวิว
|
|
110
|
-
</span>
|
|
111
|
-
<div
|
|
112
|
-
v-if="props.review.review"
|
|
113
|
-
class="flex items-center gap-[8px]"
|
|
114
|
-
>
|
|
115
|
-
<InputRating
|
|
116
|
-
:size="14"
|
|
117
|
-
readonly
|
|
118
|
-
:model-value="props.review.review.rating"
|
|
119
|
-
/>
|
|
120
|
-
<span class="font-body-small opacity-75">
|
|
121
|
-
{{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
|
|
122
|
-
</span>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
<p
|
|
127
|
-
v-if="props.review.review?.description"
|
|
128
|
-
class="font-body-large max-h-[280px] overflow-y-auto"
|
|
129
|
-
>
|
|
130
|
-
{{ props.review.review.description }}
|
|
131
|
-
</p>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</Modal>
|
|
2
|
+
<Modal v-model="isOpen" class="sm:max-w-[1280px]" disable-padding>
|
|
3
|
+
<template #header>
|
|
4
|
+
<ShadDialogTitle class="p-4">{{ props.title }}</ShadDialogTitle>
|
|
5
|
+
</template>
|
|
6
|
+
<div class="relative h-[824px] bg-black overflow-hidden">
|
|
7
|
+
<Carousel
|
|
8
|
+
:items="props.items"
|
|
9
|
+
:select-index="props.startIndex"
|
|
10
|
+
class="h-full"
|
|
11
|
+
@update:current-index="currentIndex = $event"
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<!-- description overlay (แสดงเฉพาะเมื่อ item ปัจจุบันมี description และไม่มี review) -->
|
|
15
|
+
<div
|
|
16
|
+
v-if="!props.review && currentItem?.description"
|
|
17
|
+
class="absolute bottom-0 w-full z-10"
|
|
18
|
+
style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
|
|
19
|
+
>
|
|
20
|
+
<div class="p-[16px] flex flex-col gap-[4px] text-white font-body-large">
|
|
21
|
+
<div class="font-body-large-prominent">ผู้ดูแลระบบ</div>
|
|
22
|
+
<div>{{ currentItem.description }}</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- Review info overlay (แสดงเมื่อมีข้อมูล review) -->
|
|
27
|
+
<div
|
|
28
|
+
v-if="props.review"
|
|
29
|
+
class="absolute bottom-0 w-full z-10"
|
|
30
|
+
style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
|
|
31
|
+
>
|
|
32
|
+
<div class="flex flex-col w-full">
|
|
33
|
+
<div
|
|
34
|
+
v-if="!showMoreDescription"
|
|
35
|
+
class="flex gap-[16px] p-[16px] w-full text-white"
|
|
36
|
+
>
|
|
37
|
+
<Avatar
|
|
38
|
+
:src="props.review.user?.avatar"
|
|
39
|
+
:alt="props.review.user?.name"
|
|
40
|
+
:size="36"
|
|
41
|
+
class="shrink-0"
|
|
42
|
+
/>
|
|
43
|
+
<div class="flex flex-col gap-[8px] w-full">
|
|
44
|
+
<div class="flex flex-col gap-[8px]">
|
|
45
|
+
<div class="flex justify-between w-full">
|
|
46
|
+
<span class="font-body-large">
|
|
47
|
+
{{ props.review.user?.name }}
|
|
48
|
+
</span>
|
|
49
|
+
<Icon name="lucide:flag" />
|
|
50
|
+
</div>
|
|
51
|
+
<div class="flex flex-col">
|
|
52
|
+
<span class="font-body-small">
|
|
53
|
+
{{ convertNumber(props.review.user?.review_count ?? 0) }}
|
|
54
|
+
รีวิว •
|
|
55
|
+
{{ convertNumber(props.review.user?.like_count ?? 0) }}
|
|
56
|
+
ชื่นชอบรีวิว
|
|
57
|
+
</span>
|
|
58
|
+
<div
|
|
59
|
+
v-if="props.review.review"
|
|
60
|
+
class="flex items-center gap-[8px]"
|
|
61
|
+
>
|
|
62
|
+
<InputRating
|
|
63
|
+
:size="14"
|
|
64
|
+
readonly
|
|
65
|
+
:model-value="props.review.review.rating"
|
|
66
|
+
/>
|
|
67
|
+
<span class="font-body-small opacity-75">
|
|
68
|
+
{{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<p
|
|
74
|
+
v-if="props.review.review?.description"
|
|
75
|
+
class="font-body-large line-clamp-1 cursor-pointer"
|
|
76
|
+
@click="showMoreDescription = true"
|
|
77
|
+
>
|
|
78
|
+
{{ props.review.review.description }}
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div
|
|
83
|
+
v-if="showMoreDescription"
|
|
84
|
+
class="flex gap-[16px] bg-white rounded-t-[16px] w-full p-[16px] max-h-[322px]"
|
|
85
|
+
>
|
|
86
|
+
<Avatar
|
|
87
|
+
:src="props.review.user?.avatar"
|
|
88
|
+
:alt="props.review.user?.name"
|
|
89
|
+
:size="36"
|
|
90
|
+
class="shrink-0"
|
|
91
|
+
/>
|
|
92
|
+
<div class="flex flex-col gap-[8px] w-full">
|
|
93
|
+
<div class="flex flex-col gap-[8px]">
|
|
94
|
+
<div class="flex justify-between w-full">
|
|
95
|
+
<span class="font-body-large">
|
|
96
|
+
{{ props.review.user?.name }}
|
|
97
|
+
</span>
|
|
98
|
+
<Icon
|
|
99
|
+
name="lucide:x"
|
|
100
|
+
class="cursor-pointer"
|
|
101
|
+
@click="showMoreDescription = false"
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="flex flex-col">
|
|
105
|
+
<span class="font-body-small">
|
|
106
|
+
{{ convertNumber(props.review.user?.review_count ?? 0) }}
|
|
107
|
+
รีวิว •
|
|
108
|
+
{{ convertNumber(props.review.user?.like_count ?? 0) }}
|
|
109
|
+
ชื่นชอบรีวิว
|
|
110
|
+
</span>
|
|
111
|
+
<div
|
|
112
|
+
v-if="props.review.review"
|
|
113
|
+
class="flex items-center gap-[8px]"
|
|
114
|
+
>
|
|
115
|
+
<InputRating
|
|
116
|
+
:size="14"
|
|
117
|
+
readonly
|
|
118
|
+
:model-value="props.review.review.rating"
|
|
119
|
+
/>
|
|
120
|
+
<span class="font-body-small opacity-75">
|
|
121
|
+
{{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
<p
|
|
127
|
+
v-if="props.review.review?.description"
|
|
128
|
+
class="font-body-large max-h-[280px] overflow-y-auto"
|
|
129
|
+
>
|
|
130
|
+
{{ props.review.review.description }}
|
|
131
|
+
</p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</Modal>
|
|
138
138
|
</template>
|
|
139
139
|
|
|
140
140
|
<script setup>
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import { AlertDialogRoot, useForwardPropsEmits } from "reka-ui";
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
open: { type: Boolean, required: false },
|
|
5
|
-
defaultOpen: { type: Boolean, required: false }
|
|
6
|
-
unmountOnHide: { type: Boolean, required: false }
|
|
5
|
+
defaultOpen: { type: Boolean, required: false }
|
|
7
6
|
});
|
|
8
7
|
const emits = defineEmits(["update:open"]);
|
|
9
8
|
const forwarded = useForwardPropsEmits(props, emits);
|
|
@@ -30,7 +30,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
30
30
|
"onUpdate:modelValue"?: ((date: DateValue | undefined) => any) | undefined;
|
|
31
31
|
"onUpdate:placeholder"?: ((date: DateValue) => any) | undefined;
|
|
32
32
|
}>, {
|
|
33
|
-
modelValue: DateValue | DateValue[]
|
|
33
|
+
modelValue: DateValue | DateValue[];
|
|
34
34
|
layout: "month-and-year" | "month-only" | "year-only";
|
|
35
35
|
reverseYears: boolean;
|
|
36
36
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -30,7 +30,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
30
30
|
"onUpdate:modelValue"?: ((date: DateValue | undefined) => any) | undefined;
|
|
31
31
|
"onUpdate:placeholder"?: ((date: DateValue) => any) | undefined;
|
|
32
32
|
}>, {
|
|
33
|
-
modelValue: DateValue | DateValue[]
|
|
33
|
+
modelValue: DateValue | DateValue[];
|
|
34
34
|
layout: "month-and-year" | "month-only" | "year-only";
|
|
35
35
|
reverseYears: boolean;
|
|
36
36
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -3,13 +3,11 @@ import { reactiveOmit } from "@vueuse/core";
|
|
|
3
3
|
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "reka-ui";
|
|
4
4
|
import { cn } from "#pukaad-ui/runtime/plugins/shadcn";
|
|
5
5
|
const props = defineProps({
|
|
6
|
-
defaultValue: { type:
|
|
7
|
-
modelValue: { type: null, required: false },
|
|
6
|
+
defaultValue: { type: [Boolean, String], required: false },
|
|
7
|
+
modelValue: { type: [Boolean, String, null], required: false },
|
|
8
8
|
disabled: { type: Boolean, required: false },
|
|
9
9
|
value: { type: null, required: false },
|
|
10
10
|
id: { type: String, required: false },
|
|
11
|
-
trueValue: { type: null, required: false },
|
|
12
|
-
falseValue: { type: null, required: false },
|
|
13
11
|
asChild: { type: Boolean, required: false },
|
|
14
12
|
as: { type: null, required: false },
|
|
15
13
|
name: { type: String, required: false },
|
|
@@ -6,7 +6,6 @@ const props = defineProps({
|
|
|
6
6
|
open: { type: Boolean, required: false },
|
|
7
7
|
defaultOpen: { type: Boolean, required: false },
|
|
8
8
|
modal: { type: Boolean, required: false },
|
|
9
|
-
unmountOnHide: { type: Boolean, required: false },
|
|
10
9
|
title: { type: String, required: false, default: "Command Palette" },
|
|
11
10
|
description: { type: String, required: false, default: "Search for a command to run..." }
|
|
12
11
|
});
|
|
@@ -3,8 +3,7 @@ import { DialogRoot, useForwardPropsEmits } from "reka-ui";
|
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
open: { type: Boolean, required: false },
|
|
5
5
|
defaultOpen: { type: Boolean, required: false },
|
|
6
|
-
modal: { type: Boolean, required: false }
|
|
7
|
-
unmountOnHide: { type: Boolean, required: false }
|
|
6
|
+
modal: { type: Boolean, required: false }
|
|
8
7
|
});
|
|
9
8
|
const emits = defineEmits(["update:open"]);
|
|
10
9
|
const forwarded = useForwardPropsEmits(props, emits);
|
|
@@ -11,7 +11,6 @@ const props = defineProps({
|
|
|
11
11
|
preventOpenAutoFocus: { type: Boolean, required: false, default: false },
|
|
12
12
|
forceMount: { type: Boolean, required: false },
|
|
13
13
|
loop: { type: Boolean, required: false },
|
|
14
|
-
memoDependencies: { type: Array, required: false },
|
|
15
14
|
side: { type: null, required: false },
|
|
16
15
|
sideOffset: { type: Number, required: false, default: 4 },
|
|
17
16
|
sideFlip: { type: Boolean, required: false },
|
|
@@ -8,7 +8,6 @@ import { cn } from "#pukaad-ui/runtime/plugins/shadcn";
|
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
forceMount: { type: Boolean, required: false },
|
|
10
10
|
loop: { type: Boolean, required: false },
|
|
11
|
-
memoDependencies: { type: Array, required: false },
|
|
12
11
|
sideOffset: { type: Number, required: false },
|
|
13
12
|
sideFlip: { type: Boolean, required: false },
|
|
14
13
|
alignOffset: { type: Number, required: false },
|
|
@@ -7,7 +7,6 @@ defineOptions({
|
|
|
7
7
|
});
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
forceMount: { type: Boolean, required: false },
|
|
10
|
-
memoDependencies: { type: Array, required: false },
|
|
11
10
|
side: { type: null, required: false },
|
|
12
11
|
sideOffset: { type: Number, required: false, default: 4 },
|
|
13
12
|
sideFlip: { type: Boolean, required: false },
|
|
@@ -26,7 +25,6 @@ const props = defineProps({
|
|
|
26
25
|
disableUpdateOnLayoutShift: { type: Boolean, required: false },
|
|
27
26
|
prioritizePosition: { type: Boolean, required: false },
|
|
28
27
|
reference: { type: null, required: false },
|
|
29
|
-
dir: { type: String, required: false },
|
|
30
28
|
asChild: { type: Boolean, required: false },
|
|
31
29
|
as: { type: null, required: false },
|
|
32
30
|
disableOutsidePointerEvents: { type: Boolean, required: false },
|
|
@@ -5,7 +5,6 @@ const props = defineProps({
|
|
|
5
5
|
defaultOpen: { type: Boolean, required: false },
|
|
6
6
|
defaultValue: { type: null, required: false },
|
|
7
7
|
modelValue: { type: null, required: false },
|
|
8
|
-
nullableValue: { type: String, required: false },
|
|
9
8
|
by: { type: [String, Function], required: false },
|
|
10
9
|
dir: { type: String, required: false },
|
|
11
10
|
multiple: { type: Boolean, required: false },
|
|
@@ -15,7 +15,6 @@ const props = defineProps({
|
|
|
15
15
|
forceMount: { type: Boolean, required: false },
|
|
16
16
|
position: { type: String, required: false, default: "popper" },
|
|
17
17
|
bodyLock: { type: Boolean, required: false },
|
|
18
|
-
memoDependencies: { type: Array, required: false },
|
|
19
18
|
side: { type: null, required: false },
|
|
20
19
|
sideOffset: { type: Number, required: false },
|
|
21
20
|
sideFlip: { type: Boolean, required: false },
|
|
@@ -34,7 +33,6 @@ const props = defineProps({
|
|
|
34
33
|
disableUpdateOnLayoutShift: { type: Boolean, required: false },
|
|
35
34
|
prioritizePosition: { type: Boolean, required: false },
|
|
36
35
|
reference: { type: null, required: false },
|
|
37
|
-
dir: { type: String, required: false },
|
|
38
36
|
asChild: { type: Boolean, required: false },
|
|
39
37
|
as: { type: null, required: false },
|
|
40
38
|
disableOutsidePointerEvents: { type: Boolean, required: false },
|
|
@@ -4,7 +4,7 @@ type __VLS_Props = SeparatorProps & {
|
|
|
4
4
|
class?: HTMLAttributes["class"];
|
|
5
5
|
};
|
|
6
6
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
7
|
-
orientation:
|
|
7
|
+
orientation: "vertical" | "horizontal";
|
|
8
8
|
decorative: boolean;
|
|
9
9
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
@@ -4,7 +4,7 @@ type __VLS_Props = SeparatorProps & {
|
|
|
4
4
|
class?: HTMLAttributes["class"];
|
|
5
5
|
};
|
|
6
6
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
7
|
-
orientation:
|
|
7
|
+
orientation: "vertical" | "horizontal";
|
|
8
8
|
decorative: boolean;
|
|
9
9
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
10
|
declare const _default: typeof __VLS_export;
|
|
@@ -3,8 +3,7 @@ import { DialogRoot, useForwardPropsEmits } from "reka-ui";
|
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
open: { type: Boolean, required: false },
|
|
5
5
|
defaultOpen: { type: Boolean, required: false },
|
|
6
|
-
modal: { type: Boolean, required: false }
|
|
7
|
-
unmountOnHide: { type: Boolean, required: false }
|
|
6
|
+
modal: { type: Boolean, required: false }
|
|
8
7
|
});
|
|
9
8
|
const emits = defineEmits(["update:open"]);
|
|
10
9
|
const forwarded = useForwardPropsEmits(props, emits);
|
|
@@ -10,9 +10,9 @@ type __VLS_Slots = {} & {
|
|
|
10
10
|
default?: (props: typeof __VLS_8) => any;
|
|
11
11
|
};
|
|
12
12
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
-
"update:modelValue": (payload:
|
|
13
|
+
"update:modelValue": (payload: string | number) => any;
|
|
14
14
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
-
"onUpdate:modelValue"?: ((payload:
|
|
15
|
+
"onUpdate:modelValue"?: ((payload: string | number) => any) | undefined;
|
|
16
16
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
17
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
18
18
|
declare const _default: typeof __VLS_export;
|
|
@@ -10,9 +10,9 @@ type __VLS_Slots = {} & {
|
|
|
10
10
|
default?: (props: typeof __VLS_8) => any;
|
|
11
11
|
};
|
|
12
12
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
-
"update:modelValue": (payload:
|
|
13
|
+
"update:modelValue": (payload: string | number) => any;
|
|
14
14
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
-
"onUpdate:modelValue"?: ((payload:
|
|
15
|
+
"onUpdate:modelValue"?: ((payload: string | number) => any) | undefined;
|
|
16
16
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
17
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
18
18
|
declare const _default: typeof __VLS_export;
|
|
@@ -6,8 +6,7 @@ const props = defineProps({
|
|
|
6
6
|
disableHoverableContent: { type: Boolean, required: false },
|
|
7
7
|
disableClosingTrigger: { type: Boolean, required: false },
|
|
8
8
|
disabled: { type: Boolean, required: false },
|
|
9
|
-
ignoreNonKeyboardFocus: { type: Boolean, required: false }
|
|
10
|
-
content: { type: Object, required: false }
|
|
9
|
+
ignoreNonKeyboardFocus: { type: Boolean, required: false }
|
|
11
10
|
});
|
|
12
11
|
</script>
|
|
13
12
|
|
package/package.json
CHANGED
|
File without changes
|