@dynect/base 0.2.0 → 0.3.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/package.json +1 -1
- package/src/runtime/composables/useDevices.ts +2 -11
- package/src/runtime/composables/useDropzone.ts +6 -30
- package/src/runtime/composables/useGantt.ts +12 -3
- package/src/runtime/composables/useKanban.ts +2 -0
- package/src/runtime/composables/useServerSideEvent.ts +3 -8
- package/src/runtime/plugins/ssr-width.ts +1 -0
- package/src/runtime/plugins/toast.client.ts +1 -0
package/package.json
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { computed, onMounted, onUnmounted, readonly, ref } from 'vue';
|
|
2
|
+
|
|
1
3
|
export type ViewportDeviceType = 'mobile' | 'tablet' | 'laptop' | 'desktop';
|
|
2
4
|
export type OS = 'windows' | 'macos' | 'linux' | 'ios' | 'android' | 'unknown';
|
|
3
5
|
export type Browser = 'chrome' | 'firefox' | 'safari' | 'edge' | 'opera' | 'samsung' | 'unknown';
|
|
@@ -80,40 +82,29 @@ export const useDevices = () => {
|
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
return {
|
|
83
|
-
// State
|
|
84
85
|
viewportWidth: readonly(viewportWidth),
|
|
85
86
|
viewportHeight: readonly(viewportHeight),
|
|
86
87
|
isOnline: readonly(isOnline),
|
|
87
88
|
ua: readonly(ua),
|
|
88
|
-
|
|
89
|
-
// Derived
|
|
90
89
|
os,
|
|
91
90
|
browser,
|
|
92
91
|
deviceType,
|
|
93
92
|
orientation,
|
|
94
|
-
|
|
95
|
-
// Booleans
|
|
96
93
|
isMobile,
|
|
97
94
|
isTablet,
|
|
98
95
|
isLaptop,
|
|
99
96
|
isDesktop,
|
|
100
97
|
isTouch,
|
|
101
|
-
|
|
102
|
-
// OS shorthands
|
|
103
98
|
isWindows: computed(() => os.value === 'windows'),
|
|
104
99
|
isMacOS: computed(() => os.value === 'macos'),
|
|
105
100
|
isLinux: computed(() => os.value === 'linux'),
|
|
106
101
|
isIOS: computed(() => os.value === 'ios'),
|
|
107
102
|
isAndroid: computed(() => os.value === 'android'),
|
|
108
|
-
|
|
109
|
-
// Browser shorthands
|
|
110
103
|
isChrome: computed(() => browser.value === 'chrome'),
|
|
111
104
|
isFirefox: computed(() => browser.value === 'firefox'),
|
|
112
105
|
isSafari: computed(() => browser.value === 'safari'),
|
|
113
106
|
isEdge: computed(() => browser.value === 'edge'),
|
|
114
107
|
isOpera: computed(() => browser.value === 'opera'),
|
|
115
|
-
|
|
116
|
-
// Orientation
|
|
117
108
|
isPortrait: computed(() => orientation.value === 'portrait'),
|
|
118
109
|
isLandscape: computed(() => orientation.value === 'landscape'),
|
|
119
110
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ref, type Ref } from 'vue';
|
|
2
|
+
import { useNuxtApp } from 'nuxt/app';
|
|
3
|
+
|
|
1
4
|
interface DropzoneConfig {
|
|
2
5
|
allowedExt?: string[];
|
|
3
6
|
maxSizeLimit?: number;
|
|
@@ -9,14 +12,10 @@ export const useDropzone = (config: DropzoneConfig = {}) => {
|
|
|
9
12
|
const dragActive = ref(false);
|
|
10
13
|
const droppedFile = ref<File[]>([]);
|
|
11
14
|
|
|
12
|
-
// Use config values with defaults
|
|
13
15
|
const maxSizeLimit = config.maxSizeLimit ?? 8;
|
|
14
16
|
const maxFileLimit = config.maxFileLimit ?? 3;
|
|
15
17
|
const allowedExt = config.allowedExt ?? ['pdf', 'doc', 'docx', 'jpeg', 'jpg', 'png', 'xls', 'xlsx', 'mp4', 'mov', 'avi', 'mpeg', 'mpg', 'zip'];
|
|
16
18
|
|
|
17
|
-
/**
|
|
18
|
-
* Converts dropped files to FormData entries
|
|
19
|
-
*/
|
|
20
19
|
const uploadFiles = () => {
|
|
21
20
|
const formData = new FormData();
|
|
22
21
|
droppedFile.value.forEach((file) => {
|
|
@@ -25,73 +24,53 @@ export const useDropzone = (config: DropzoneConfig = {}) => {
|
|
|
25
24
|
return [...formData.entries()] as [string, File][];
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
/**
|
|
29
|
-
* Toggle drag active state
|
|
30
|
-
*/
|
|
31
27
|
const toggleActive = () => {
|
|
32
28
|
dragActive.value = !dragActive.value;
|
|
33
29
|
};
|
|
34
30
|
|
|
35
|
-
/**
|
|
36
|
-
* Validate file extension
|
|
37
|
-
*/
|
|
38
31
|
const validateFileExtension = (fileName: string): boolean => {
|
|
39
32
|
const extension = fileName.split('.').pop()?.toLowerCase();
|
|
40
33
|
return extension ? allowedExt.includes(extension) : false;
|
|
41
34
|
};
|
|
42
35
|
|
|
43
|
-
/**
|
|
44
|
-
* Validate file size
|
|
45
|
-
*/
|
|
46
36
|
const validateFileSize = (fileSize: number): boolean => {
|
|
47
37
|
const sizeInMB = fileSize / (1024 * 1024);
|
|
48
38
|
return sizeInMB < maxSizeLimit;
|
|
49
39
|
};
|
|
50
40
|
|
|
51
|
-
/**
|
|
52
|
-
* Handle file selection from drop or file input
|
|
53
|
-
*/
|
|
54
41
|
const selectedFile = (event: DragEvent | Event, type: 'drop' | 'select', fileInputRef: Ref<HTMLInputElement | null>) => {
|
|
55
42
|
const files = type === 'drop' ? Array.from((event as DragEvent).dataTransfer?.files ?? []) : Array.from((event.target as HTMLInputElement).files ?? []);
|
|
56
43
|
|
|
57
44
|
if (files.length === 0) return;
|
|
58
45
|
|
|
59
|
-
// Check file count limit
|
|
60
46
|
const totalFiles = droppedFile.value.length + files.length;
|
|
61
47
|
if (totalFiles > maxFileLimit) {
|
|
62
|
-
$toast.error(`Maximum ${maxFileLimit} file${maxFileLimit > 1 ? 's' : ''} allowed. You currently have ${droppedFile.value.length} file(s) and tried to add ${files.length} more.`, { position: 'top-center' });
|
|
48
|
+
($toast as any).error(`Maximum ${maxFileLimit} file${maxFileLimit > 1 ? 's' : ''} allowed. You currently have ${droppedFile.value.length} file(s) and tried to add ${files.length} more.`, { position: 'top-center' });
|
|
63
49
|
return;
|
|
64
50
|
}
|
|
65
51
|
|
|
66
|
-
// Validate file extensions
|
|
67
52
|
const invalidFiles = files.filter((file) => !validateFileExtension(file.name));
|
|
68
53
|
if (invalidFiles.length > 0) {
|
|
69
54
|
const fileNames = invalidFiles.map((f) => f.name).join(', ');
|
|
70
|
-
$toast.error(`Invalid file format: ${fileNames}. Supported formats: ${allowedExt.join(', ')}`, { position: 'top-center' });
|
|
55
|
+
($toast as any).error(`Invalid file format: ${fileNames}. Supported formats: ${allowedExt.join(', ')}`, { position: 'top-center' });
|
|
71
56
|
return;
|
|
72
57
|
}
|
|
73
58
|
|
|
74
|
-
// Validate file sizes
|
|
75
59
|
const oversizedFiles = files.filter((file) => !validateFileSize(file.size));
|
|
76
60
|
if (oversizedFiles.length > 0) {
|
|
77
61
|
const fileNames = oversizedFiles.map((f) => f.name).join(', ');
|
|
78
|
-
$toast.error(`File size exceeds ${maxSizeLimit} MB limit: ${fileNames}`, { position: 'top-center' });
|
|
62
|
+
($toast as any).error(`File size exceeds ${maxSizeLimit} MB limit: ${fileNames}`, { position: 'top-center' });
|
|
79
63
|
return;
|
|
80
64
|
}
|
|
81
65
|
|
|
82
|
-
// Add valid files
|
|
83
66
|
droppedFile.value.push(...files);
|
|
84
67
|
dragActive.value = droppedFile.value.length > 0;
|
|
85
68
|
|
|
86
|
-
// Reset input value to allow re-uploading the same file
|
|
87
69
|
if (fileInputRef.value) {
|
|
88
70
|
fileInputRef.value.value = '';
|
|
89
71
|
}
|
|
90
72
|
};
|
|
91
73
|
|
|
92
|
-
/**
|
|
93
|
-
* Remove file by index
|
|
94
|
-
*/
|
|
95
74
|
const deleteFile = (index: number) => {
|
|
96
75
|
if (index >= 0 && index < droppedFile.value.length) {
|
|
97
76
|
droppedFile.value.splice(index, 1);
|
|
@@ -99,9 +78,6 @@ export const useDropzone = (config: DropzoneConfig = {}) => {
|
|
|
99
78
|
}
|
|
100
79
|
};
|
|
101
80
|
|
|
102
|
-
/**
|
|
103
|
-
* Clear all files
|
|
104
|
-
*/
|
|
105
81
|
const clearFiles = () => {
|
|
106
82
|
droppedFile.value = [];
|
|
107
83
|
dragActive.value = false;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import { inject, provide, type InjectionKey, type Ref } from 'vue';
|
|
2
|
+
import {
|
|
3
|
+
differenceInDays,
|
|
4
|
+
differenceInMonths,
|
|
5
|
+
differenceInWeeks,
|
|
6
|
+
getDaysInMonth,
|
|
7
|
+
startOfDay,
|
|
8
|
+
startOfMonth,
|
|
9
|
+
startOfWeek,
|
|
10
|
+
} from '../utils/date';
|
|
11
|
+
|
|
1
12
|
export type GanttRange = 'daily' | 'weekly' | 'monthly';
|
|
2
13
|
|
|
3
14
|
export interface GanttFeature {
|
|
@@ -59,7 +70,6 @@ export function useGantt() {
|
|
|
59
70
|
return context;
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
// Helper functions for date calculations
|
|
63
73
|
export function getDifferenceIn(range: GanttRange) {
|
|
64
74
|
switch (range) {
|
|
65
75
|
case 'daily':
|
|
@@ -78,7 +88,7 @@ export function getStartOf(range: GanttRange) {
|
|
|
78
88
|
case 'daily':
|
|
79
89
|
return startOfDay;
|
|
80
90
|
case 'weekly':
|
|
81
|
-
return (date: Date) => startOfWeek(date, { weekStartsOn: 1 });
|
|
91
|
+
return (date: Date) => startOfWeek(date, { weekStartsOn: 1 });
|
|
82
92
|
case 'monthly':
|
|
83
93
|
return startOfMonth;
|
|
84
94
|
default:
|
|
@@ -118,7 +128,6 @@ export function getOffset(date: Date, timelineStartDate: Date, context: { zoom:
|
|
|
118
128
|
return offset * context.columnWidth * (context.zoom / 100) + innerOffset;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
// Generate timeline data
|
|
122
131
|
export function generateTimelineData(startYear: number, yearCount: number): TimelineData {
|
|
123
132
|
const data: TimelineData = [];
|
|
124
133
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { useState } from 'nuxt/app';
|
|
3
|
+
|
|
1
4
|
export interface AppNotification {
|
|
2
5
|
id: string;
|
|
3
6
|
type: string;
|
|
@@ -17,14 +20,6 @@ export interface AppAnnouncement {
|
|
|
17
20
|
read: boolean;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
// {
|
|
21
|
-
// id: 'sample-1',
|
|
22
|
-
// type: 'info',
|
|
23
|
-
// title: 'Leave request approved',
|
|
24
|
-
// message: 'Your annual leave request for 25-27 Apr has been approved.',
|
|
25
|
-
// createdAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
|
|
26
|
-
// read: false,
|
|
27
|
-
// },
|
|
28
23
|
export function useServerSideEvent() {
|
|
29
24
|
const connected = useState<boolean>('sse:connected', () => false);
|
|
30
25
|
const notifications = useState<AppNotification[]>('sse:notifications', () => []);
|