edvoyui-component-library-test-flight 0.0.229 → 0.0.230

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.
@@ -6,10 +6,19 @@ interface ITimeLine {
6
6
  date?: string;
7
7
  dateTime?: string;
8
8
  showmore?: any;
9
+ [key: string]: any;
9
10
  }
10
11
  declare function __VLS_template(): {
11
12
  attrs: Partial<{}>;
12
13
  slots: {
14
+ dateIcon?(_: {
15
+ date: Date | null;
16
+ label: string;
17
+ }): any;
18
+ dateHeader?(_: {
19
+ date: Date | null;
20
+ label: string;
21
+ }): any;
13
22
  line?(_: {
14
23
  type: "icon" | "image" | "view";
15
24
  }): any;
@@ -49,7 +58,7 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
49
58
  of<T>(...items: T[]): T[];
50
59
  readonly [Symbol.species]: ArrayConstructor;
51
60
  };
52
- default: never[];
61
+ default: () => never[];
53
62
  };
54
63
  type: {
55
64
  type: PropType<"view" | "icon" | "image">;
@@ -63,6 +72,14 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
63
72
  type: BooleanConstructor;
64
73
  default: boolean;
65
74
  };
75
+ groupByDate: {
76
+ type: BooleanConstructor;
77
+ default: boolean;
78
+ };
79
+ dateKey: {
80
+ type: StringConstructor;
81
+ default: string;
82
+ };
66
83
  }>, {}, {}, {}, {}, import('../../../node_modules/vue').ComponentOptionsMixin, import('../../../node_modules/vue').ComponentOptionsMixin, {}, string, import('../../../node_modules/vue').PublicProps, Readonly<import('../../../node_modules/vue').ExtractPropTypes<{
67
84
  items: {
68
85
  type: {
@@ -79,7 +96,7 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
79
96
  of<T>(...items: T[]): T[];
80
97
  readonly [Symbol.species]: ArrayConstructor;
81
98
  };
82
- default: never[];
99
+ default: () => never[];
83
100
  };
84
101
  type: {
85
102
  type: PropType<"view" | "icon" | "image">;
@@ -93,11 +110,21 @@ declare const __VLS_component: import('../../../node_modules/vue').DefineCompone
93
110
  type: BooleanConstructor;
94
111
  default: boolean;
95
112
  };
113
+ groupByDate: {
114
+ type: BooleanConstructor;
115
+ default: boolean;
116
+ };
117
+ dateKey: {
118
+ type: StringConstructor;
119
+ default: string;
120
+ };
96
121
  }>> & Readonly<{}>, {
97
122
  type: "icon" | "image" | "view";
98
123
  icon: string | Record<string, any>;
99
124
  items: ITimeLine[];
100
125
  showMore: boolean;
126
+ groupByDate: boolean;
127
+ dateKey: string;
101
128
  }, {}, {}, {}, string, import('../../../node_modules/vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
102
129
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
103
130
  export default _default;
@@ -1,119 +1,149 @@
1
1
  <template>
2
2
  <div>
3
3
  <ul role="list" class="p-4 space-y-6">
4
- <li
5
- v-for="(item, itemIdx) in items"
6
- :data="item"
7
- :key="`timeline_${itemIdx}`"
8
- class="relative flex gap-x-4 group"
9
- :type="type"
10
- :timeline-icon="icon"
11
- :show-more="showMore"
12
- >
13
- <div
14
- :class="[
15
- itemIdx === items.length - 1 ? 'h-6' : '-bottom-6',
16
- 'absolute left-0 top-0 flex w-6 justify-center',
17
- ]"
18
- >
19
- <slot name="line" :type="type">
4
+ <template v-for="group in timelineGroups" :key="group.key">
5
+ <li v-if="groupByDate" class="relative flex items-center gap-x-4">
20
6
  <div
21
- :class="[
22
- type === 'icon' ? 'w-0.5 bg-green-500' : 'w-px bg-gray-200',
23
- ]"
24
- />
25
- </slot>
26
-
27
- </div>
28
- <template v-if="type === 'image'">
29
- <slot name="image" :data="item" :dataIndex="itemIdx">
30
- <img
31
- :src="item.person?.imageUrl"
32
- alt=""
33
- class="relative flex-initial flex-shrink-0 rounded-full size-6 bg-gray-50"
34
- />
35
- </slot>
36
- <slot name="details" :data="item" :dataIndex="itemIdx">
7
+ class="absolute top-0 left-0 flex justify-center w-6 -bottom-6"
8
+ aria-hidden="true"
9
+ >
10
+ <div class="w-px bg-gray-200" />
11
+ </div>
12
+ <slot name="dateIcon" :date="group.date" :label="group.label">
37
13
  <div
38
- class="flex-1 min-w-0 p-3 rounded-md ring-1 ring-inset ring-gray-200"
14
+ class="relative flex items-center justify-center flex-none -m-1 rounded-full bg-purple-100 size-8"
39
15
  >
40
- <div class="flex justify-between gap-x-4 mb-0.5">
41
- <div class="text-xs leading-5 text-gray-500">
42
- <span class="font-medium text-gray-900">{{
43
- item.person?.name
44
- }}</span>
45
- </div>
46
- <time
47
- :datetime="item.dateTime"
48
- class="flex-none text-xs leading-5 text-gray-500"
49
- >
50
- {{ item.date + " " + item.dateTime }}
51
- </time>
52
- </div>
53
- <p class="text-sm leading-6 text-gray-500">
54
- {{ item.comment }}
55
- </p>
56
-
57
- <details
58
- v-if="showMore"
59
- :open="itemIdx === 0"
60
- class="h-6 p-2 mt-2 text-xs text-gray-500 transition-colors duration-100 ease-in-out select-none open:border open:border-gray-100 open:bg-gray-50 open:rounded-md group open:h-auto"
61
- >
62
- <summary
63
- class="flex flex-row items-center justify-start text-sm leading-5 text-gray-900 list-none cursor-pointer"
64
- >
65
- <slot name="showMoreTitle" :data="item" :open="itemIdx === 0">
66
- {{ item.showmore?.title || "More Details" }}
67
- <PlusIcon
68
- class="ml-auto -mr-1 transition-all duration-300 opacity-75 fill-current size-4 group-open:hidden"
69
- />
70
- <MinusIcon
71
- class="hidden ml-auto -mr-1 transition-all duration-300 opacity-75 fill-current size-4 group-open:inline-block"
72
- />
73
- </slot>
74
- </summary>
75
- <slot name="showMoreContent" :data="item">
76
- <div>{{ item.showmore?.content }}</div>
77
- </slot>
78
- </details>
16
+ <CalendarDaysIcon class="text-purple-500 size-4" />
79
17
  </div>
80
18
  </slot>
81
- </template>
82
- <template v-else>
19
+ <slot name="dateHeader" :date="group.date" :label="group.label">
20
+ <h3 class="text-sm font-semibold text-gray-900">
21
+ {{ group.label }}
22
+ </h3>
23
+ </slot>
24
+ </li>
25
+
26
+ <li
27
+ v-for="(item, itemIdx) in group.items"
28
+ :key="getItemKey(group.key, item, itemIdx)"
29
+ :data="item"
30
+ class="relative flex gap-x-4 group"
31
+ :type="type"
32
+ :timeline-icon="icon"
33
+ :show-more="showMore"
34
+ >
83
35
  <div
84
- class="relative flex items-center justify-center flex-none bg-white size-6"
36
+ :class="[
37
+ itemIdx === group.items.length - 1 ? 'h-6' : '-bottom-6',
38
+ 'absolute left-0 top-0 flex w-6 justify-center',
39
+ ]"
85
40
  >
86
- <component
87
- v-if="type === 'icon'"
88
- :is="icon || CheckCircleIcon"
89
- class="text-green-500 size-6"
90
- aria-hidden="true"
91
- />
92
- <div
93
- v-else
94
- class="size-1.5 rounded-full bg-gray-100 ring-1 ring-gray-300 group-hover:bg-purple-200 group-hover:ring-purple-500"
95
- />
41
+ <slot name="line" :type="type">
42
+ <div
43
+ :class="[
44
+ type === 'icon' ? 'w-0.5 bg-green-500' : 'w-px bg-gray-200',
45
+ ]"
46
+ />
47
+ </slot>
96
48
  </div>
97
- <p class="flex-auto py-0.5 text-xs leading-5 text-gray-500">
98
- <span class="font-medium text-gray-900">{{
99
- item.person?.name
100
- }}</span>
101
- {{ item?.comment }}
102
- </p>
103
- <time
104
- :datetime="item.dateTime"
105
- class="flex-none py-0.5 text-xs leading-5 text-gray-500"
106
- >{{ item.date + " " + item.dateTime }}</time
107
- >
108
- </template>
109
- </li>
49
+ <template v-if="type === 'image'">
50
+ <slot name="image" :data="item" :dataIndex="itemIdx">
51
+ <img
52
+ :src="item.person?.imageUrl"
53
+ alt=""
54
+ class="relative flex-initial flex-shrink-0 rounded-full size-6 bg-gray-50"
55
+ />
56
+ </slot>
57
+ <slot name="details" :data="item" :dataIndex="itemIdx">
58
+ <div
59
+ class="flex-1 min-w-0 p-3 rounded-md ring-1 ring-inset ring-gray-200"
60
+ >
61
+ <div class="flex justify-between gap-x-4 mb-0.5">
62
+ <div class="text-xs leading-5 text-gray-500">
63
+ <span class="font-medium text-gray-900">{{
64
+ item.person?.name
65
+ }}</span>
66
+ </div>
67
+ <time
68
+ :datetime="item.dateTime"
69
+ class="flex-none text-xs leading-5 text-gray-500"
70
+ >
71
+ {{ item.date + " " + item.dateTime }}
72
+ </time>
73
+ </div>
74
+ <p class="text-sm leading-6 text-gray-500">
75
+ {{ item.comment }}
76
+ </p>
77
+
78
+ <details
79
+ v-if="showMore"
80
+ :open="itemIdx === 0"
81
+ class="h-6 p-2 mt-2 text-xs text-gray-500 transition-colors duration-100 ease-in-out select-none open:border open:border-gray-100 open:bg-gray-50 open:rounded-md group open:h-auto"
82
+ >
83
+ <summary
84
+ class="flex flex-row items-center justify-start text-sm leading-5 text-gray-900 list-none cursor-pointer"
85
+ >
86
+ <slot
87
+ name="showMoreTitle"
88
+ :data="item"
89
+ :open="itemIdx === 0"
90
+ >
91
+ {{ item.showmore?.title || "More Details" }}
92
+ <PlusIcon
93
+ class="ml-auto -mr-1 transition-all duration-300 opacity-75 fill-current size-4 group-open:hidden"
94
+ />
95
+ <MinusIcon
96
+ class="hidden ml-auto -mr-1 transition-all duration-300 opacity-75 fill-current size-4 group-open:inline-block"
97
+ />
98
+ </slot>
99
+ </summary>
100
+ <slot name="showMoreContent" :data="item">
101
+ <div>{{ item.showmore?.content }}</div>
102
+ </slot>
103
+ </details>
104
+ </div>
105
+ </slot>
106
+ </template>
107
+ <template v-else>
108
+ <div
109
+ class="relative flex items-center justify-center flex-none bg-white size-6"
110
+ >
111
+ <component
112
+ v-if="type === 'icon'"
113
+ :is="icon || CheckCircleIcon"
114
+ class="text-green-500 size-6"
115
+ aria-hidden="true"
116
+ />
117
+ <div
118
+ v-else
119
+ class="size-1.5 rounded-full bg-gray-100 ring-1 ring-gray-300 group-hover:bg-purple-200 group-hover:ring-purple-500"
120
+ />
121
+ </div>
122
+ <p class="flex-auto py-0.5 text-xs leading-5 text-gray-500">
123
+ <span class="font-medium text-gray-900">{{
124
+ item.person?.name
125
+ }}</span>
126
+ {{ item?.comment }}
127
+ </p>
128
+ <time
129
+ :datetime="item.dateTime"
130
+ class="flex-none py-0.5 text-xs leading-5 text-gray-500"
131
+ >{{ item.date + " " + item.dateTime }}</time
132
+ >
133
+ </template>
134
+ </li>
135
+ </template>
110
136
  </ul>
111
137
  </div>
112
138
  </template>
113
139
 
114
140
  <script setup lang="ts">
115
- import { PropType, toRefs } from "vue";
116
- import { MinusIcon, PlusIcon } from "@heroicons/vue/24/outline";
141
+ import { computed, PropType, toRefs } from "vue";
142
+ import {
143
+ CalendarDaysIcon,
144
+ MinusIcon,
145
+ PlusIcon,
146
+ } from "@heroicons/vue/24/outline";
117
147
  import { CheckCircleIcon } from "@heroicons/vue/24/solid";
118
148
 
119
149
  interface ITimeLine {
@@ -123,12 +153,20 @@ interface ITimeLine {
123
153
  date?: string;
124
154
  dateTime?: string;
125
155
  showmore?: any;
156
+ [key: string]: any;
157
+ }
158
+
159
+ interface ITimeLineGroup {
160
+ key: string;
161
+ label: string;
162
+ date: Date | null;
163
+ items: ITimeLine[];
126
164
  }
127
165
 
128
166
  const props = defineProps({
129
167
  items: {
130
168
  type: Array<ITimeLine>,
131
- default: [],
169
+ default: () => [],
132
170
  },
133
171
  type: {
134
172
  type: String as PropType<"view" | "icon" | "image">,
@@ -142,7 +180,74 @@ const props = defineProps({
142
180
  type: Boolean,
143
181
  default: false,
144
182
  },
183
+ groupByDate: {
184
+ type: Boolean,
185
+ default: false,
186
+ },
187
+ dateKey: {
188
+ type: String,
189
+ default: "createdAt",
190
+ },
145
191
  });
146
192
 
147
- const { items, type, icon, showMore } = toRefs(props);
193
+ const { items, type, icon, showMore, groupByDate, dateKey } = toRefs(props);
194
+
195
+ const getItemKey = (groupKey: string, item: ITimeLine, itemIdx: number) => {
196
+ if (!groupByDate.value) return `timeline_${itemIdx}`;
197
+ return `timeline_${groupKey}_${item.id ?? itemIdx}`;
198
+ };
199
+
200
+ const getLocalDateKey = (date: Date) => {
201
+ const year = date.getFullYear();
202
+ const month = String(date.getMonth() + 1).padStart(2, "0");
203
+ const day = String(date.getDate()).padStart(2, "0");
204
+ return `${year}-${month}-${day}`;
205
+ };
206
+
207
+ const getDateLabel = (date: Date | null) => {
208
+ if (!date) return "Unknown date";
209
+
210
+ const today = new Date();
211
+ const yesterday = new Date(today);
212
+ yesterday.setDate(today.getDate() - 1);
213
+
214
+ const dateKeyValue = getLocalDateKey(date);
215
+ if (dateKeyValue === getLocalDateKey(today)) return "Today";
216
+ if (dateKeyValue === getLocalDateKey(yesterday)) return "Yesterday";
217
+
218
+ return new Intl.DateTimeFormat("en-GB", {
219
+ day: "numeric",
220
+ month: "short",
221
+ year: "numeric",
222
+ }).format(date);
223
+ };
224
+
225
+ const timelineGroups = computed<ITimeLineGroup[]>(() => {
226
+ if (!groupByDate.value) {
227
+ return [{ key: "all", label: "", date: null, items: items.value }];
228
+ }
229
+
230
+ const groups = new Map<string, ITimeLineGroup>();
231
+
232
+ items.value.forEach((item) => {
233
+ const rawDate = item[dateKey.value];
234
+ const parsedDate = rawDate ? new Date(rawDate) : null;
235
+ const date =
236
+ parsedDate && !Number.isNaN(parsedDate.getTime()) ? parsedDate : null;
237
+ const key = date ? getLocalDateKey(date) : "unknown";
238
+
239
+ if (!groups.has(key)) {
240
+ groups.set(key, {
241
+ key,
242
+ label: getDateLabel(date),
243
+ date,
244
+ items: [],
245
+ });
246
+ }
247
+
248
+ groups.get(key)?.items.push(item);
249
+ });
250
+
251
+ return Array.from(groups.values());
252
+ });
148
253
  </script>