@ramathibodi/nuxt-commons 0.1.64 → 0.1.66

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
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.64",
7
+ "version": "0.1.66",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -45,6 +45,7 @@ watch(() => alert?.hasAlert(), (hasAlert) => {
45
45
  elevation="2"
46
46
  theme="dark"
47
47
  variant="flat"
48
+ v-bind="currentItem.alertIcon ? { icon: currentItem.alertIcon } : {}"
48
49
  @click:close="renewAlert()"
49
50
  >
50
51
  {{ currentItem.statusCode ? currentItem.statusCode + ' ' : '' }} {{ currentItem.message }} {{ !isEmpty(currentItem.data) ? currentItem.data : '' }}
@@ -34,6 +34,7 @@ interface Props {
34
34
  parentTemplates?: string|string[]
35
35
  dirtyClass?: string
36
36
  dirtyOnCreate?: boolean
37
+ sanitizeDelay?: number
37
38
  }
38
39
 
39
40
  const props = withDefaults(defineProps<Props>(), {
@@ -43,7 +44,8 @@ const props = withDefaults(defineProps<Props>(), {
43
44
  decoration: () => { return {} },
44
45
  parentTemplates: (): string[] => [],
45
46
  dirtyClass: "form-data-dirty",
46
- dirtyOnCreate: false
47
+ dirtyOnCreate: false,
48
+ sanitizeDelay: 2000,
47
49
  })
48
50
 
49
51
  const emit = defineEmits(['update:modelValue'])
@@ -87,7 +89,7 @@ function isBlankString(v: unknown): v is string {
87
89
  return isString(v) && v.trim().length === 0
88
90
  }
89
91
 
90
- const sanitizeBlankStrings = debounce(sanitizeBlankStringsRaw, 500)
92
+ const sanitizeBlankStrings = debounce(sanitizeBlankStringsRaw, props.sanitizeDelay)
91
93
 
92
94
  function sanitizeBlankStringsRaw(val: any, original?: any): void {
93
95
  if (!original && props.originalData) {
@@ -2,39 +2,142 @@
2
2
  import { DateTime } from "luxon";
3
3
  import { computed } from "vue";
4
4
 
5
- type Locale = "th" | "en" | "en-US" | "th-TH";
5
+ type Unit = 'years' | 'months' | 'days' | 'hours' | 'minutes' | 'seconds';
6
+ type Locale = 'th' | 'en' | 'en-US' | 'th-TH';
6
7
 
7
8
  interface Props {
8
9
  modelValue: DateTime;
9
10
  endDate?: DateTime;
10
11
  locale?: Locale;
11
- zeroBase?: boolean; // true = start at 0, false = start at 1
12
+ showSuffix?: boolean;
13
+ units?: Unit[];
14
+ zeroBase?: boolean; // true = start at 0, false = start at 1 (inclusive)
12
15
  }
13
16
 
14
17
  const props = withDefaults(defineProps<Props>(), {
15
- locale: "th",
18
+ locale: 'th',
19
+ showSuffix: true,
16
20
  zeroBase: true,
17
21
  });
18
22
 
19
- const normalizeLocale = (locale: Locale): "en" | "th" =>
20
- locale.startsWith("en") ? "en" : "th";
23
+ // Fallback map: map complex locale (e.g., en-US) to base (e.g., en)
24
+ const normalizeLocale = (locale: Locale): 'en' | 'th' => {
25
+ if (locale.startsWith('en')) return 'en';
26
+ if (locale.startsWith('th')) return 'th';
27
+ return 'th'; // default fallback
28
+ };
21
29
 
22
- const countDate = computed(() => {
23
- const base = props.endDate ?? DateTime.now();
30
+ const labelsEnPlural: Record<Unit, string> = {
31
+ years: 'years',
32
+ months: 'months',
33
+ days: 'days',
34
+ hours: 'hours',
35
+ minutes: 'minutes',
36
+ seconds: 'seconds',
37
+ };
38
+ const labelsEnSingular: Record<Unit, string> = {
39
+ years: 'year',
40
+ months: 'month',
41
+ days: 'day',
42
+ hours: 'hour',
43
+ minutes: 'minute',
44
+ seconds: 'second',
45
+ };
46
+
47
+ const localizedLabels: Record<'en' | 'th', Record<Unit, string>> = {
48
+ en: labelsEnPlural, // จะสลับเป็น singular ตามค่าจริงตอน render
49
+ th: {
50
+ years: 'ปี',
51
+ months: 'เดือน',
52
+ days: 'วัน',
53
+ hours: 'ชั่วโมง',
54
+ minutes: 'นาที',
55
+ seconds: 'วินาที',
56
+ },
57
+ };
58
+
59
+ const localizedSuffix: Record<'en' | 'th', string> = {
60
+ en: 'ago',
61
+ th: 'ที่ผ่านมา',
62
+ };
63
+
64
+ const outputText = computed(() => {
65
+ const base = props.endDate ?? DateTime.now(); // ใช้ endDate ถ้ามี ไม่งั้น now
24
66
  const baseLocale = normalizeLocale(props.locale);
25
67
 
26
- let days = Math.floor(base.diff(props.modelValue, "days").days ?? 0);
68
+ const units: Unit[] = props.units?.length
69
+ ? props.units
70
+ : ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];
71
+
72
+ const diffObj = base.diff(props.modelValue, units).toObject();
73
+
74
+ // helper: คืน label ตาม singular/plural (เฉพาะ en)
75
+ const labelFor = (unit: Unit, value: number) => {
76
+ if (baseLocale === 'en') {
77
+ return value === 1 ? labelsEnSingular[unit] : labelsEnPlural[unit];
78
+ }
79
+ return localizedLabels[baseLocale][unit];
80
+ };
81
+
82
+ // ---------- โหมด single unit ----------
83
+ if (!props.units) {
84
+ const foundUnit = units.find((unit) => (diffObj[unit] ?? 0) >= 1);
85
+ if (foundUnit) {
86
+ const raw = Math.floor(diffObj[foundUnit] ?? 0); // >= 1
87
+ const value = props.zeroBase ? raw : raw + 1; // inclusive (+1)
88
+ const label = labelFor(foundUnit, value);
89
+ const suffix = props.showSuffix ? localizedSuffix[baseLocale] : '';
90
+ return `${value} ${label}${suffix ? ` ${suffix}` : ''}`;
91
+ }
92
+
93
+ // ถ้าไม่มีหน่วยใด >= 1 ให้ใช้หน่วยเล็กสุด
94
+ const lastUnit = units.at(-1)!;
95
+ const raw = Math.max(0, Math.floor(diffObj[lastUnit] ?? 0));
96
+ const value = props.zeroBase ? raw : 1; // inclusive: อย่างน้อย 1
97
+ const label = labelFor(lastUnit, value);
98
+ const suffix = props.showSuffix ? localizedSuffix[baseLocale] : '';
99
+ return `${value} ${label}${suffix ? ` ${suffix}` : ''}`;
100
+ }
101
+
102
+ // ---------- โหมด multi-unit ----------
103
+ // เก็บค่าแบบตัวเลขก่อน แล้วค่อยเรนเดอร์
104
+ const values = units.map((unit) => ({
105
+ unit,
106
+ raw: Math.max(0, Math.floor(diffObj[unit] ?? 0)),
107
+ }));
108
+
109
+ // เลือกหน่วยที่เล็กที่สุด
110
+ const smallest = values[values.length - 1];
111
+
112
+ // ถ้ามีค่าอย่างน้อยหนึ่งหน่วย > 0 และเป็น inclusive (zeroBase=false) => +1 ที่หน่วยเล็กสุด
113
+ const anyPositive = values.some((v) => v.raw > 0);
114
+ const adjusted = values.map((v, idx) => {
115
+ if (!props.zeroBase && anyPositive && idx === values.length - 1) {
116
+ return { ...v, raw: v.raw + 1 };
117
+ }
118
+ return v;
119
+ });
27
120
 
28
- if (!props.zeroBase) days += 1;
121
+ // สร้างข้อความจากหน่วยที่มีค่า > 0
122
+ const parts = adjusted
123
+ .filter((v) => v.raw > 0)
124
+ .map((v) => `${v.raw} ${labelFor(v.unit, v.raw)}`);
29
125
 
30
- if (baseLocale === "en") {
31
- return days === 1 ? "1 day" : `${days} days`;
126
+ // หากทั้งหมดเป็น 0:
127
+ if (parts.length === 0) {
128
+ const minValue = props.zeroBase ? 0 : 1;
129
+ const label = labelFor(smallest.unit, minValue);
130
+ const suffix = props.showSuffix ? localizedSuffix[baseLocale] : '';
131
+ return `${minValue} ${label}${suffix ? ` ${suffix}` : ''}`;
32
132
  }
33
133
 
34
- return `${days} วัน`;
134
+ const suffix = props.showSuffix ? localizedSuffix[baseLocale] : '';
135
+ return `${parts.join(' ')}${suffix ? ` ${suffix}` : ''}`;
35
136
  });
36
137
  </script>
37
138
 
38
139
  <template>
39
- <span>{{ countDate }}</span>
140
+ <span>
141
+ <i v-if="props.zeroBase" class="mdi mdi-clock-time-twelve-outline" aria-hidden="true" ></i>
142
+ {{ outputText }}</span>
40
143
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramathibodi/nuxt-commons",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "Ramathibodi Nuxt modules for common components",
5
5
  "repository": {
6
6
  "type": "git",