pukaad-ui-lib 1.308.0 → 1.310.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
3
  "configKey": "pukaadUI",
4
- "version": "1.308.0",
4
+ "version": "1.310.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -95,6 +95,7 @@
95
95
  v-model="isOpen"
96
96
  :items="props.item.review?.images"
97
97
  :start-index="startIndex"
98
+ :review="props.item"
98
99
  title="รูปภาพรีวิว"
99
100
  />
100
101
 
@@ -21,6 +21,7 @@ export interface InputDatePickerProps {
21
21
  isDateDisabled?: (date: DateValue) => boolean;
22
22
  defaultPlaceholder?: DateValue;
23
23
  reverseYears?: boolean;
24
+ variant?: "default" | "button";
24
25
  }
25
26
  type __VLS_Props = InputDatePickerProps;
26
27
  type __VLS_ModelProps = {
@@ -38,6 +39,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
38
39
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
39
40
  "onUpdate:modelValue"?: ((value: Date | undefined) => any) | undefined;
40
41
  }>, {
42
+ variant: "default" | "button";
41
43
  disabled: boolean;
42
44
  required: boolean;
43
45
  id: string;
@@ -6,7 +6,7 @@
6
6
  v-slot="{ errorMessage }"
7
7
  v-model="modelValue"
8
8
  >
9
- <ShadFormItem>
9
+ <ShadFormItem :class="cn('flex flex-col w-full', props.class)">
10
10
  <ShadFormLabel v-if="props.label || $slots.label" class="w-full">
11
11
  <slot name="label">
12
12
  <div class="flex-1">
@@ -25,26 +25,37 @@
25
25
  :disabled="props.disabled"
26
26
  :class="
27
27
  cn(
28
- 'w-full flex items-center justify-between gap-2 font-body-large',
29
- errorMessage && 'border-destructive hover:border-destructive',
30
- props.class
28
+ 'w-full flex items-center gap-2 font-body-large',
29
+ !modelValue && props.variant === 'button' ? 'justify-center' : 'justify-between',
30
+ errorMessage && 'border-destructive hover:border-destructive'
31
31
  )
32
32
  "
33
33
  >
34
- <span v-if="modelValue">{{ formattedDate }}</span>
35
- <span v-else class="text-cloud">{{ placeholder }}</span>
36
- <Icon
37
- v-if="modelValue"
38
- name="lucide:x"
39
- :size="16"
40
- class="!pointer-events-auto cursor-pointer"
41
- @click.stop.prevent="clearDate"
42
- />
43
- <Icon v-else name="lucide:calendar" :size="16" />
34
+ <!-- Filled State -->
35
+ <template v-if="modelValue">
36
+ <span>{{ formattedDate }}</span>
37
+ <Icon
38
+ name="lucide:x"
39
+ :size="16"
40
+ class="!pointer-events-auto cursor-pointer shrink-0"
41
+ @click.stop.prevent="clearDate"
42
+ />
43
+ </template>
44
+
45
+ <!-- Empty State: button mode -->
46
+ <template v-else-if="props.variant === 'button'">
47
+ <span>{{ placeholder }}</span>
48
+ </template>
49
+
50
+ <!-- Empty State: default mode -->
51
+ <template v-else>
52
+ <span class="text-cloud">{{ placeholder }}</span>
53
+ <Icon name="lucide:calendar" :size="16" class="shrink-0" />
54
+ </template>
44
55
  </Button>
45
56
  </PopoverTrigger>
46
57
  <PopoverContent class="w-auto p-0" align="start">
47
- <div :class="cn('sm:flex', showTime && 'flex-row')">
58
+ <div :class="cn('sm:flex', props.showTime && 'flex-row')">
48
59
  <Calendar
49
60
  v-model="calendarValue"
50
61
  initial-focus
@@ -58,7 +69,7 @@
58
69
  />
59
70
  <!-- Time Picker -->
60
71
  <div
61
- v-if="showTime"
72
+ v-if="props.showTime"
62
73
  class="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x sm:border-t-0"
63
74
  >
64
75
  <!-- Hours -->
@@ -144,7 +155,8 @@ const props = defineProps({
144
155
  minValue: { type: Date, required: false },
145
156
  isDateDisabled: { type: Function, required: false },
146
157
  defaultPlaceholder: { type: null, required: false },
147
- reverseYears: { type: Boolean, required: false }
158
+ reverseYears: { type: Boolean, required: false },
159
+ variant: { type: String, required: false, default: "default" }
148
160
  });
149
161
  const maxCalendarValue = computed(() => {
150
162
  if (!props.maxValue) return void 0;
@@ -21,6 +21,7 @@ export interface InputDatePickerProps {
21
21
  isDateDisabled?: (date: DateValue) => boolean;
22
22
  defaultPlaceholder?: DateValue;
23
23
  reverseYears?: boolean;
24
+ variant?: "default" | "button";
24
25
  }
25
26
  type __VLS_Props = InputDatePickerProps;
26
27
  type __VLS_ModelProps = {
@@ -38,6 +39,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
38
39
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
39
40
  "onUpdate:modelValue"?: ((value: Date | undefined) => any) | undefined;
40
41
  }>, {
42
+ variant: "default" | "button";
41
43
  disabled: boolean;
42
44
  required: boolean;
43
45
  id: string;
@@ -1,53 +1,51 @@
1
1
  <template>
2
- <NuxtRating
3
- :border-color="props.borderColor"
4
- :active-color="props.activeColor"
5
- :inactive-color="props.backgroundColor"
6
- :border-width="props.borderSize"
7
- :read-only="props.readonly"
8
- :rating-step="props.ratingStep"
9
- :rounded-corners="true"
10
- :rating-size="`${props.size}px`"
11
- :rating-content="[
12
- 12,
13
- 17.27,
14
- 16.15,
15
- 19.78,
16
- 17.64,
17
- 18.7,
18
- 16.54,
19
- 13.98,
20
- 20.21,
21
- 10.8,
22
- 19.64,
23
- 9.05,
24
- 14.81,
25
- 8.64,
26
- 12.92,
27
- 4.18,
28
- 11.08,
29
- 4.18,
30
- 9.19,
31
- 8.63,
32
- 4.36,
33
- 9.04,
34
- 3.79,
35
- 10.8,
36
- 7.46,
37
- 13.98,
38
- 6.36,
39
- 18.7,
40
- 7.85,
41
- 19.78,
42
- 12,
43
- 17.27
44
- ]"
45
- @rating-selected="onSelect"
46
- :rating-value="ratingValue"
47
- />
2
+ <div class="inline-flex items-center gap-0">
3
+ <svg
4
+ v-for="n in 5"
5
+ :key="n"
6
+ :width="props.size"
7
+ :height="props.size"
8
+ viewBox="4 2 16.42 20.21"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ :style="{
11
+ cursor: props.readonly ? 'default' : 'pointer',
12
+ marginRight: n < 5 ? '2px' : '0'
13
+ }"
14
+ @click="!props.readonly && onSelect(n)"
15
+ >
16
+ <defs>
17
+ <linearGradient
18
+ :id="`star-grad-${uid}-${n}`"
19
+ x1="0"
20
+ x2="100%"
21
+ y1="0"
22
+ y2="0"
23
+ >
24
+ <stop
25
+ :offset="`${getFill(n)}%`"
26
+ :stop-color="props.activeColor"
27
+ stop-opacity="1"
28
+ />
29
+ <stop
30
+ :offset="`${getFill(n)}%`"
31
+ :stop-color="props.backgroundColor"
32
+ stop-opacity="1"
33
+ />
34
+ </linearGradient>
35
+ </defs>
36
+ <polygon
37
+ points="12,17.27,16.15,19.78,17.64,18.7,16.54,13.98,20.21,10.8,19.64,9.05,14.81,8.64,12.92,4.18,11.08,4.18,9.19,8.63,4.36,9.04,3.79,10.8,7.46,13.98,6.36,18.7,7.85,19.78,12,17.27"
38
+ :fill="`url(#star-grad-${uid}-${n})`"
39
+ :stroke="props.borderSize > 0 ? props.borderColor : 'none'"
40
+ :stroke-width="props.borderSize"
41
+ stroke-linejoin="round"
42
+ />
43
+ </svg>
44
+ </div>
48
45
  </template>
49
46
 
50
47
  <script setup>
48
+ import { computed } from "vue";
51
49
  const emit = defineEmits(["select"]);
52
50
  const props = defineProps({
53
51
  readonly: { type: Boolean, required: false, default: false },
@@ -62,6 +60,13 @@ const ratingValue = defineModel({
62
60
  type: Number,
63
61
  default: 0
64
62
  });
63
+ const uid = Math.random().toString(36).slice(2, 7);
64
+ const getFill = (n) => {
65
+ const val = ratingValue.value;
66
+ if (val >= n) return 100;
67
+ if (val < n - 1) return 0;
68
+ return Math.round((val - (n - 1)) * 100);
69
+ };
65
70
  const onSelect = (value) => {
66
71
  ratingValue.value = value;
67
72
  emit("select", value);
@@ -1,7 +1,10 @@
1
+ import type { CardReviewProps } from "#pukaad-ui/types/components/card/card-review";
1
2
  export interface ModalMediaViewProps {
2
3
  items?: string[];
3
4
  title?: string;
4
5
  startIndex?: number;
6
+ /** ข้อมูล review เต็ม (user, review, replies) สำหรับแสดงที่ overlay ด้านล่าง */
7
+ review?: CardReviewProps;
5
8
  }
6
9
  type __VLS_Props = ModalMediaViewProps;
7
10
  type __VLS_ModelProps = {
@@ -9,15 +9,153 @@
9
9
  :select-index="props.startIndex"
10
10
  class="h-full"
11
11
  />
12
+
13
+ <!-- Review info overlay (แสดงเมื่อมีข้อมูล review) -->
14
+ <div
15
+ v-if="props.review"
16
+ class="absolute bottom-0 w-full z-10"
17
+ style="
18
+ background: linear-gradient(
19
+ to top,
20
+ rgba(0, 0, 0, 0.75) 0%,
21
+ transparent 100%
22
+ );
23
+ "
24
+ >
25
+ <div class="flex flex-colw-full">
26
+ <!-- User row -->
27
+ <div
28
+ v-if="!showMoreDescription"
29
+ class="flex gap-[16px] p-[16px] w-full text-white"
30
+ >
31
+ <Avatar
32
+ :src="props.review.user?.avatar"
33
+ :alt="props.review.user?.name"
34
+ :size="36"
35
+ class="shrink-0"
36
+ />
37
+ <div class="flex flex-col gap-[8px] w-full">
38
+ <div class="flex flex-col gap-[8px]">
39
+ <div class="flex justify-between w-full">
40
+ <span class="font-body-large">
41
+ {{ props.review.user?.name }}
42
+ </span>
43
+ <Icon name="lucide:flag" />
44
+ </div>
45
+ <div class="flex flex-col">
46
+ <span class="font-body-small">
47
+ {{ convertNumber(props.review.user?.review_count ?? 0) }}
48
+ รีวิว •
49
+ {{ convertNumber(props.review.user?.like_count ?? 0) }}
50
+ ชื่นชอบรีวิว
51
+ </span>
52
+
53
+ <!-- Rating + date -->
54
+ <div
55
+ v-if="props.review.review"
56
+ class="flex items-center gap-[8px]"
57
+ >
58
+ <InputRating
59
+ :size="14"
60
+ readonly
61
+ :model-value="props.review.review.rating"
62
+ />
63
+ <span class="font-body-small opacity-75">
64
+ {{
65
+ convertDateTorelativeText(
66
+ props.review.review.created_at ?? ""
67
+ )
68
+ }}
69
+ </span>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <!-- Description -->
74
+ <p
75
+ v-if="props.review.review?.description"
76
+ class="font-body-large line-clamp-1 cursor-pointer"
77
+ @click="showMoreDescription = true"
78
+ >
79
+ {{ props.review.review.description }}
80
+ </p>
81
+ </div>
82
+ </div>
83
+ <div
84
+ v-if="showMoreDescription"
85
+ class="flex gap-[16px] bg-white rounded-t-[16px] w-full p-[16px] max-h-[322px]"
86
+ >
87
+ <Avatar
88
+ :src="props.review.user?.avatar"
89
+ :alt="props.review.user?.name"
90
+ :size="36"
91
+ class="shrink-0"
92
+ />
93
+ <div class="flex flex-col gap-[8px] w-full">
94
+ <div class="flex flex-col gap-[8px]">
95
+ <div class="flex justify-between w-full">
96
+ <span class="font-body-large">
97
+ {{ props.review.user?.name }}
98
+ </span>
99
+ <Icon
100
+ name="lucide:x"
101
+ class="cursor-pointer"
102
+ @click="showMoreDescription = false"
103
+ />
104
+ </div>
105
+ <div class="flex flex-col">
106
+ <span class="font-body-small">
107
+ {{ convertNumber(props.review.user?.review_count ?? 0) }}
108
+ รีวิว •
109
+ {{ convertNumber(props.review.user?.like_count ?? 0) }}
110
+ ชื่นชอบรีวิว
111
+ </span>
112
+
113
+ <!-- Rating + date -->
114
+ <div
115
+ v-if="props.review.review"
116
+ class="flex items-center gap-[8px]"
117
+ >
118
+ <InputRating
119
+ :size="14"
120
+ readonly
121
+ :model-value="props.review.review.rating"
122
+ />
123
+ <span class="font-body-small opacity-75">
124
+ {{
125
+ convertDateTorelativeText(
126
+ props.review.review.created_at ?? ""
127
+ )
128
+ }}
129
+ </span>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ <!-- Description -->
134
+ <p
135
+ v-if="props.review.review?.description"
136
+ class="font-body-large max-h-[280px] overflow-y-auto"
137
+ @click="showMoreDescription = true"
138
+ >
139
+ {{ props.review.review.description }}
140
+ </p>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
12
145
  </div>
13
146
  </Modal>
14
147
  </template>
15
148
 
16
149
  <script setup>
150
+ import { ref } from "vue";
151
+ import { useConvert } from "../../composables/useConvert";
152
+ const { convertNumber, convertDateTorelativeText } = useConvert();
17
153
  const props = defineProps({
18
154
  items: { type: Array, required: false, default: () => [] },
19
155
  title: { type: String, required: false, default: "" },
20
- startIndex: { type: Number, required: false, default: 0 }
156
+ startIndex: { type: Number, required: false, default: 0 },
157
+ review: { type: Object, required: false }
21
158
  });
22
159
  const isOpen = defineModel({ type: Boolean, ...{ default: false } });
160
+ const showMoreDescription = ref(false);
23
161
  </script>
@@ -1,7 +1,10 @@
1
+ import type { CardReviewProps } from "#pukaad-ui/types/components/card/card-review";
1
2
  export interface ModalMediaViewProps {
2
3
  items?: string[];
3
4
  title?: string;
4
5
  startIndex?: number;
6
+ /** ข้อมูล review เต็ม (user, review, replies) สำหรับแสดงที่ overlay ด้านล่าง */
7
+ review?: CardReviewProps;
5
8
  }
6
9
  type __VLS_Props = ModalMediaViewProps;
7
10
  type __VLS_ModelProps = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
- "version": "1.308.0",
3
+ "version": "1.310.0",
4
4
  "description": "pukaad-ui for MeMSG",
5
5
  "repository": {
6
6
  "type": "git",