v-uixy 1.3.1 → 1.5.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/templates/assets/css/main.css +4 -0
- package/templates/components/Alert/Alert.types.ts +4 -0
- package/templates/components/Alert/AlertItem.component.vue +2 -4
- package/templates/components/Alert/index.ts +6 -1
- package/templates/components/Avatar/Avatar.component.vue +7 -3
- package/templates/components/Avatar/Avatar.styles.ts +3 -3
- package/templates/components/Breadcrumb/Breadcrumb.component.vue +116 -0
- package/templates/components/Breadcrumb/Breadcrumb.styles.ts +40 -0
- package/templates/components/Breadcrumb/Breadcrumb.types.ts +30 -0
- package/templates/components/Breadcrumb/index.ts +17 -0
- package/templates/components/Button/Button.styles.ts +3 -3
- package/templates/components/Calendar/Calendar.component.vue +76 -17
- package/templates/components/Calendar/Calendar.styles.ts +15 -3
- package/templates/components/Calendar/Calendar.types.ts +9 -0
- package/templates/components/Chart/Chart.component.vue +308 -0
- package/templates/components/Chart/Chart.types.ts +18 -0
- package/templates/components/Chart/index.ts +13 -0
- package/templates/components/Checkbox/Checkbox.styles.ts +1 -1
- package/templates/components/ColorPicker/ColorPicker.component.vue +4 -4
- package/templates/components/Command/Command.component.vue +6 -6
- package/templates/components/Command/Command.types.ts +1 -0
- package/templates/components/DatePicker/DatePicker.component.vue +219 -0
- package/templates/components/DatePicker/DatePicker.styles.ts +74 -0
- package/templates/components/DatePicker/DatePicker.types.ts +18 -0
- package/templates/components/DatePicker/DatePicker.utils.ts +46 -0
- package/templates/components/DatePicker/TimeWheel.vue +145 -0
- package/templates/components/DatePicker/TimeWheelColumn.vue +147 -0
- package/templates/components/DatePicker/index.ts +11 -0
- package/templates/components/DateRangePicker/DateRangePicker.component.vue +304 -0
- package/templates/components/DateRangePicker/DateRangePicker.types.ts +23 -0
- package/templates/components/DateRangePicker/index.ts +11 -0
- package/templates/components/FileUpload/FileUpload.component.vue +306 -0
- package/templates/components/FileUpload/FileUpload.styles.ts +74 -0
- package/templates/components/FileUpload/FileUpload.types.ts +31 -0
- package/templates/components/FileUpload/index.ts +15 -0
- package/templates/components/Input/Input.component.vue +27 -27
- package/templates/components/Input/Input.styles.ts +5 -5
- package/templates/components/Input/Input.types.ts +1 -0
- package/templates/components/InputOTP/InputOTP.component.vue +4 -2
- package/templates/components/InputOTP/InputOTP.types.ts +1 -0
- package/templates/components/Modal/Modal.component.vue +28 -4
- package/templates/components/Modal/Modal.types.ts +3 -1
- package/templates/components/Popover/Popover.component.vue +33 -2
- package/templates/components/Popover/Popover.types.ts +1 -0
- package/templates/components/SegmentedControl/SegmentedControl.component.vue +85 -0
- package/templates/components/SegmentedControl/SegmentedControl.styles.ts +55 -0
- package/templates/components/SegmentedControl/SegmentedControl.types.ts +15 -0
- package/templates/components/SegmentedControl/index.ts +11 -0
- package/templates/components/Select/Select.component.vue +253 -108
- package/templates/components/Select/Select.styles.ts +6 -6
- package/templates/components/Select/Select.types.ts +12 -0
- package/templates/components/Select/SelectTreeItem.vue +154 -0
- package/templates/components/Select/composables/useSelect.composable.ts +260 -42
- package/templates/components/Tabs/Tabs.component.vue +11 -2
- package/templates/components/Tabs/Tabs.types.ts +1 -0
- package/templates/components/Textarea/Textarea.component.vue +23 -32
- package/templates/components/Textarea/Textarea.styles.ts +4 -4
- package/templates/components/Textarea/Textarea.types.ts +1 -0
- package/templates/components/TimePicker/TimePicker.component.vue +21 -18
- package/templates/components/TimePicker/TimePicker.styles.ts +1 -1
- package/templates/components/TimePicker/TimePicker.types.ts +1 -0
- package/templates/components.json +37 -1
- package/templates/composables/usePosition.ts +17 -2
package/package.json
CHANGED
|
@@ -156,6 +156,10 @@
|
|
|
156
156
|
@apply bg-transparent;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
159
163
|
.slider::-webkit-slider-thumb {
|
|
160
164
|
@apply relative z-10 h-5 w-5 cursor-pointer appearance-none rounded-full border-2 border-solid border-black bg-gray-100 dark:border-gray-100 dark:bg-black;
|
|
161
165
|
}
|
|
@@ -36,13 +36,11 @@
|
|
|
36
36
|
<script setup lang="ts">
|
|
37
37
|
import { motion, useMotionValue, animate } from "motion-v";
|
|
38
38
|
import { UixyButton, UixyIconButton, UixyIcon, UixyCard } from "..";
|
|
39
|
-
import type { UixyAlertItemProps } from "./Alert.types";
|
|
39
|
+
import type { UixyAlertItemProps, UixyAlertItemEmits } from "./Alert.types";
|
|
40
40
|
|
|
41
41
|
const props = defineProps<UixyAlertItemProps>();
|
|
42
42
|
|
|
43
|
-
const emit = defineEmits<
|
|
44
|
-
(event: "close-alert"): void;
|
|
45
|
-
}>();
|
|
43
|
+
const emit = defineEmits<UixyAlertItemEmits>();
|
|
46
44
|
|
|
47
45
|
const width = useMotionValue("100%");
|
|
48
46
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import UixyAlert from "./Alert.component.vue";
|
|
2
2
|
import UixyAlertItem from "./AlertItem.component.vue";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type UixyAlertProps,
|
|
5
|
+
type UixyAlertItemProps,
|
|
6
|
+
type UixyAlertItemEmits,
|
|
7
|
+
} from "./Alert.types";
|
|
4
8
|
|
|
5
9
|
export * from "./composables";
|
|
6
10
|
export {
|
|
@@ -8,4 +12,5 @@ export {
|
|
|
8
12
|
UixyAlertItem,
|
|
9
13
|
type UixyAlertProps,
|
|
10
14
|
type UixyAlertItemProps,
|
|
15
|
+
type UixyAlertItemEmits,
|
|
11
16
|
};
|
|
@@ -8,11 +8,11 @@ export const avatarWrapperStyles = styles(
|
|
|
8
8
|
squared: "rounded-2",
|
|
9
9
|
default: "",
|
|
10
10
|
},
|
|
11
|
-
}
|
|
11
|
+
},
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
export const avatarImageStyles = styles(
|
|
15
|
-
"flex aspect-square items-center overflow-hidden text-center text-[0]",
|
|
15
|
+
"flex aspect-square object-cover items-center overflow-hidden text-center text-[0]",
|
|
16
16
|
{
|
|
17
17
|
show: {
|
|
18
18
|
true: "",
|
|
@@ -23,5 +23,5 @@ export const avatarImageStyles = styles(
|
|
|
23
23
|
squared: "rounded-2",
|
|
24
24
|
default: "",
|
|
25
25
|
},
|
|
26
|
-
}
|
|
26
|
+
},
|
|
27
27
|
);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav aria-label="Breadcrumb">
|
|
3
|
+
<ol :class="breadcrumbListStyles({ size: props.size })">
|
|
4
|
+
<template v-for="(node, index) in nodes" :key="index">
|
|
5
|
+
<li :class="breadcrumbItemStyles()">
|
|
6
|
+
<button
|
|
7
|
+
v-if="node.kind === 'ellipsis'"
|
|
8
|
+
type="button"
|
|
9
|
+
:class="breadcrumbEllipsisStyles()"
|
|
10
|
+
aria-label="Show hidden breadcrumbs"
|
|
11
|
+
@click="expanded = true"
|
|
12
|
+
>
|
|
13
|
+
<uixy-icon
|
|
14
|
+
name="more-horizontal"
|
|
15
|
+
:class="breadcrumbIconStyles({ size: props.size })"
|
|
16
|
+
/>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<uixy-link
|
|
20
|
+
v-else-if="node.item?.link && !node.current"
|
|
21
|
+
v-bind="node.item.link"
|
|
22
|
+
:class="breadcrumbLinkStyles()"
|
|
23
|
+
>
|
|
24
|
+
<uixy-icon
|
|
25
|
+
v-if="node.item.icon"
|
|
26
|
+
:name="node.item.icon"
|
|
27
|
+
:class="breadcrumbIconStyles({ size: props.size })"
|
|
28
|
+
/>
|
|
29
|
+
{{ node.item.label }}
|
|
30
|
+
</uixy-link>
|
|
31
|
+
|
|
32
|
+
<span
|
|
33
|
+
v-else
|
|
34
|
+
:class="breadcrumbCurrentStyles()"
|
|
35
|
+
:aria-current="node.current ? 'page' : undefined"
|
|
36
|
+
>
|
|
37
|
+
<uixy-icon
|
|
38
|
+
v-if="node.item?.icon"
|
|
39
|
+
:name="node.item.icon"
|
|
40
|
+
:class="breadcrumbIconStyles({ size: props.size })"
|
|
41
|
+
/>
|
|
42
|
+
{{ node.item?.label }}
|
|
43
|
+
</span>
|
|
44
|
+
</li>
|
|
45
|
+
|
|
46
|
+
<li
|
|
47
|
+
v-if="index < nodes.length - 1"
|
|
48
|
+
:class="breadcrumbSeparatorStyles()"
|
|
49
|
+
aria-hidden="true"
|
|
50
|
+
>
|
|
51
|
+
<uixy-icon
|
|
52
|
+
:name="props.separator"
|
|
53
|
+
:class="breadcrumbSeparatorIconStyles({ size: props.size })"
|
|
54
|
+
/>
|
|
55
|
+
</li>
|
|
56
|
+
</template>
|
|
57
|
+
</ol>
|
|
58
|
+
</nav>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup lang="ts">
|
|
62
|
+
import { UixyLink } from "../Link";
|
|
63
|
+
import { UixyIcon } from "../Icon";
|
|
64
|
+
import type {
|
|
65
|
+
BreadcrumbNode,
|
|
66
|
+
ItemNode,
|
|
67
|
+
UixyBreadcrumbItem,
|
|
68
|
+
UixyBreadcrumbProps,
|
|
69
|
+
} from "./Breadcrumb.types";
|
|
70
|
+
import {
|
|
71
|
+
breadcrumbListStyles,
|
|
72
|
+
breadcrumbItemStyles,
|
|
73
|
+
breadcrumbLinkStyles,
|
|
74
|
+
breadcrumbCurrentStyles,
|
|
75
|
+
breadcrumbSeparatorStyles,
|
|
76
|
+
breadcrumbEllipsisStyles,
|
|
77
|
+
breadcrumbIconStyles,
|
|
78
|
+
breadcrumbSeparatorIconStyles,
|
|
79
|
+
} from "./Breadcrumb.styles";
|
|
80
|
+
|
|
81
|
+
const props = withDefaults(defineProps<UixyBreadcrumbProps>(), {
|
|
82
|
+
separator: "chevron-right",
|
|
83
|
+
size: "md",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const expanded = ref(false);
|
|
87
|
+
|
|
88
|
+
const nodes = computed<BreadcrumbNode[]>(() => {
|
|
89
|
+
const items = props.items ?? [];
|
|
90
|
+
const total = items.length;
|
|
91
|
+
|
|
92
|
+
const toItemNode = (item: UixyBreadcrumbItem, index: number): ItemNode => ({
|
|
93
|
+
kind: "item",
|
|
94
|
+
item,
|
|
95
|
+
current: item.current ?? index === total - 1,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const shouldCollapse =
|
|
99
|
+
!expanded.value &&
|
|
100
|
+
props.maxItems &&
|
|
101
|
+
props.maxItems >= 1 &&
|
|
102
|
+
total > props.maxItems;
|
|
103
|
+
|
|
104
|
+
if (!shouldCollapse) return items.map(toItemNode);
|
|
105
|
+
|
|
106
|
+
const tail = Math.min(Math.max(props.maxItems! - 1, 1), total - 1);
|
|
107
|
+
|
|
108
|
+
const head = toItemNode(items[0]!, 0);
|
|
109
|
+
|
|
110
|
+
const tailNodes = items
|
|
111
|
+
.slice(total - tail)
|
|
112
|
+
.map((item, i) => toItemNode(item, total - tail + i));
|
|
113
|
+
|
|
114
|
+
return [head, { kind: "ellipsis" }, ...tailNodes];
|
|
115
|
+
});
|
|
116
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import styles from "~/utils/styles";
|
|
2
|
+
|
|
3
|
+
export const breadcrumbListStyles = styles("flex flex-wrap items-center", {
|
|
4
|
+
size: {
|
|
5
|
+
sm: "gap-1 text-xs",
|
|
6
|
+
md: "gap-1.5 text-sm",
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const breadcrumbItemStyles = styles("inline-flex items-center");
|
|
11
|
+
|
|
12
|
+
export const breadcrumbLinkStyles = styles(
|
|
13
|
+
"inline-flex items-center gap-1.5 rounded-1 text-gray-600 hover:text-black dark:text-gray-500 dark:hover:text-white transition-colors duration-150 ease-in-out outline-offset-2 outline-gray-300",
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const breadcrumbCurrentStyles = styles(
|
|
17
|
+
"inline-flex items-center gap-1.5 font-500 text-gray-900 dark:text-gray-100 select-none",
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const breadcrumbSeparatorStyles = styles(
|
|
21
|
+
"inline-flex items-center text-gray-400 dark:text-gray-700 select-none",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const breadcrumbEllipsisStyles = styles(
|
|
25
|
+
"inline-flex items-center justify-center rounded-1 p-0.5 text-gray-500 hover:text-black dark:text-gray-600 dark:hover:text-white transition-colors duration-150 ease-in-out cursor-pointer outline-offset-2 outline-gray-300",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const breadcrumbIconStyles = styles("shrink-0", {
|
|
29
|
+
size: {
|
|
30
|
+
sm: "h-3.5 w-3.5",
|
|
31
|
+
md: "h-4 w-4",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const breadcrumbSeparatorIconStyles = styles("shrink-0", {
|
|
36
|
+
size: {
|
|
37
|
+
sm: "h-3 w-3",
|
|
38
|
+
md: "h-3.5 w-3.5",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type UixyLinkProps } from "../Link";
|
|
2
|
+
import type { IconName } from "~/types/icons";
|
|
3
|
+
|
|
4
|
+
export interface UixyBreadcrumbItem {
|
|
5
|
+
label: string;
|
|
6
|
+
link?: UixyLinkProps;
|
|
7
|
+
icon?: IconName;
|
|
8
|
+
current?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UixyBreadcrumbProps {
|
|
12
|
+
items: UixyBreadcrumbItem[];
|
|
13
|
+
separator?: IconName;
|
|
14
|
+
size?: "sm" | "md";
|
|
15
|
+
maxItems?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ItemNode {
|
|
19
|
+
kind: "item";
|
|
20
|
+
item: UixyBreadcrumbItem;
|
|
21
|
+
current: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EllipsisNode {
|
|
25
|
+
kind: "ellipsis";
|
|
26
|
+
item?: undefined;
|
|
27
|
+
current?: undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type BreadcrumbNode = ItemNode | EllipsisNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import UixyBreadcrumb from "./Breadcrumb.component.vue";
|
|
2
|
+
import {
|
|
3
|
+
type UixyBreadcrumbProps,
|
|
4
|
+
type UixyBreadcrumbItem,
|
|
5
|
+
type BreadcrumbNode,
|
|
6
|
+
type ItemNode,
|
|
7
|
+
type EllipsisNode,
|
|
8
|
+
} from "./Breadcrumb.types";
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
UixyBreadcrumb,
|
|
12
|
+
type UixyBreadcrumbProps,
|
|
13
|
+
type UixyBreadcrumbItem,
|
|
14
|
+
type BreadcrumbNode,
|
|
15
|
+
type ItemNode,
|
|
16
|
+
type EllipsisNode,
|
|
17
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styles from "~/utils/styles";
|
|
2
2
|
|
|
3
3
|
export const buttonStyles = styles(
|
|
4
|
-
"relative cursor-pointer h-8 font-500 transition-all duration-200 ease-in-out hover:shadow-xs flex items-center justify-center gap-2 disabled:pointer-events-none",
|
|
4
|
+
"relative cursor-pointer h-8 font-500 transition-all duration-200 ease-in-out hover:shadow-xs flex items-center justify-center gap-2 disabled:pointer-events-none border border-transparent",
|
|
5
5
|
{
|
|
6
6
|
variant: {
|
|
7
7
|
primary:
|
|
@@ -9,7 +9,7 @@ export const buttonStyles = styles(
|
|
|
9
9
|
secondary:
|
|
10
10
|
"bg-gray-300 hover:bg-gray-200 dark:bg-gray-900 dark:hover:bg-gray-700 dark:text-gray-100 text-black dark:disabled:bg-gray-600 dark:disabled:text-gray-700 disabled:text-gray-500",
|
|
11
11
|
tertiary:
|
|
12
|
-
"bg-transparent border
|
|
12
|
+
"bg-transparent border-solid border-gray-300 dark:border-gray-800 hover:bg-gray-200 dark:hover:bg-gray-800 text-black dark:text-gray-100 dark:disabled:border-gray-900 dark:disabled:text-gray-500 dark:disabled:bg-gray-800 disabled:bg-gray-300 disabled:text-gray-500 disabled:border-gray-400",
|
|
13
13
|
quaternary:
|
|
14
14
|
"dark:hover:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 disabled:text-gray-700",
|
|
15
15
|
},
|
|
@@ -22,7 +22,7 @@ export const buttonStyles = styles(
|
|
|
22
22
|
true: "rounded-full",
|
|
23
23
|
false: "rounded-1",
|
|
24
24
|
},
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
export const iconStyles = styles("h-5 w-5 shrink-0", {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
size="sm"
|
|
8
8
|
variant="tertiary"
|
|
9
9
|
/>
|
|
10
|
-
<p class="w-
|
|
10
|
+
<p class="w-40 text-center text-sm font-500">
|
|
11
11
|
{{ MONTHS[selectedMonth] }} {{ selectedYear }}
|
|
12
12
|
</p>
|
|
13
13
|
<uixy-icon-button
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
class="grid w-full grid-cols-7 text-xs text-gray-400 dark:text-gray-700"
|
|
23
23
|
>
|
|
24
24
|
<div v-for="day in adjustedDays" :key="day" class="py-2 text-center">
|
|
25
|
-
{{ day
|
|
25
|
+
{{ day?.slice(0, 2) }}
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
|
|
@@ -33,13 +33,27 @@
|
|
|
33
33
|
:class="
|
|
34
34
|
calendarDayStyles({
|
|
35
35
|
currentMonth: !!day.fromCurrentMonth,
|
|
36
|
-
selected:
|
|
36
|
+
selected: isRange
|
|
37
|
+
? isEndpoint(day.date)
|
|
38
|
+
: model?.toDateString?.() === day.date.toDateString(),
|
|
39
|
+
inRange: isRange && isInRange(day.date),
|
|
37
40
|
disabled:
|
|
38
41
|
!!props.disabled || day.fromPreviousMonth || day.fromNextMonth,
|
|
42
|
+
greyedOut:
|
|
43
|
+
!!day.fromCurrentMonth &&
|
|
44
|
+
!!props.isDateDisabled &&
|
|
45
|
+
props.isDateDisabled(day.date),
|
|
46
|
+
today: today.toDateString() === day.date.toDateString(),
|
|
39
47
|
})
|
|
40
48
|
"
|
|
41
|
-
:disabled="
|
|
49
|
+
:disabled="
|
|
50
|
+
day.fromPreviousMonth ||
|
|
51
|
+
day.fromNextMonth ||
|
|
52
|
+
(!!props.isDateDisabled && props.isDateDisabled(day.date))
|
|
53
|
+
"
|
|
42
54
|
@click="handleDayClick(day.date)"
|
|
55
|
+
@mouseenter="emit('dayHover', day.date)"
|
|
56
|
+
@mouseleave="emit('dayLeave')"
|
|
43
57
|
>
|
|
44
58
|
{{ day.day }}
|
|
45
59
|
</button>
|
|
@@ -51,12 +65,55 @@
|
|
|
51
65
|
import { ref, computed } from "vue";
|
|
52
66
|
import { UixyIconButton } from "../IconButton";
|
|
53
67
|
import { calendarDayStyles, calendarStyles } from "./Calendar.styles";
|
|
54
|
-
import type { UixyCalendarProps } from "./Calendar.types";
|
|
68
|
+
import type { UixyCalendarProps, UixyCalendarEmits } from "./Calendar.types";
|
|
55
69
|
|
|
56
70
|
const props = defineProps<UixyCalendarProps>();
|
|
57
71
|
|
|
72
|
+
const emit = defineEmits<UixyCalendarEmits>();
|
|
73
|
+
|
|
58
74
|
const model = defineModel<Date | null>();
|
|
59
75
|
|
|
76
|
+
const isRange = computed(
|
|
77
|
+
() => props.rangeStart !== undefined || props.rangeEnd !== undefined,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const sameDay = (a?: Date | null, b?: Date | null) =>
|
|
81
|
+
!!a && !!b && a.toDateString() === b.toDateString();
|
|
82
|
+
|
|
83
|
+
const effectiveEnd = computed(
|
|
84
|
+
() => props.rangeEnd ?? props.rangeHover ?? null,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const isEndpoint = (date: Date) =>
|
|
88
|
+
sameDay(date, props.rangeStart) || sameDay(date, effectiveEnd.value);
|
|
89
|
+
|
|
90
|
+
const isInRange = (date: Date) => {
|
|
91
|
+
const start = props.rangeStart;
|
|
92
|
+
const end = effectiveEnd.value;
|
|
93
|
+
|
|
94
|
+
if (!start || !end) return false;
|
|
95
|
+
|
|
96
|
+
const d = new Date(
|
|
97
|
+
date.getFullYear(),
|
|
98
|
+
date.getMonth(),
|
|
99
|
+
date.getDate(),
|
|
100
|
+
).getTime();
|
|
101
|
+
|
|
102
|
+
const a = new Date(
|
|
103
|
+
start.getFullYear(),
|
|
104
|
+
start.getMonth(),
|
|
105
|
+
start.getDate(),
|
|
106
|
+
).getTime();
|
|
107
|
+
|
|
108
|
+
const b = new Date(
|
|
109
|
+
end.getFullYear(),
|
|
110
|
+
end.getMonth(),
|
|
111
|
+
end.getDate(),
|
|
112
|
+
).getTime();
|
|
113
|
+
|
|
114
|
+
return d > Math.min(a, b) && d < Math.max(a, b);
|
|
115
|
+
};
|
|
116
|
+
|
|
60
117
|
const MONTHS = [
|
|
61
118
|
"January",
|
|
62
119
|
"February",
|
|
@@ -82,11 +139,13 @@
|
|
|
82
139
|
"Saturday",
|
|
83
140
|
];
|
|
84
141
|
|
|
142
|
+
const today = new Date();
|
|
143
|
+
|
|
85
144
|
const selectedYear = ref(props.year ?? new Date().getFullYear());
|
|
86
145
|
const selectedMonth = ref(props.month ?? new Date().getMonth());
|
|
87
146
|
|
|
88
147
|
const adjustedDays = computed(() =>
|
|
89
|
-
props.startOfWeek === "Monday" ? [...DAYS.slice(1), DAYS[0]] : DAYS
|
|
148
|
+
props.startOfWeek === "Monday" ? [...DAYS.slice(1), DAYS[0]] : DAYS,
|
|
90
149
|
);
|
|
91
150
|
|
|
92
151
|
const getDaysInMonth = (year: number, month: number) =>
|
|
@@ -101,11 +160,11 @@
|
|
|
101
160
|
});
|
|
102
161
|
|
|
103
162
|
const daysInCurrentMonth = computed(() =>
|
|
104
|
-
getDaysInMonth(selectedYear.value, selectedMonth.value)
|
|
163
|
+
getDaysInMonth(selectedYear.value, selectedMonth.value),
|
|
105
164
|
);
|
|
106
165
|
|
|
107
166
|
const daysInPreviousMonth = computed(() =>
|
|
108
|
-
getDaysInMonth(selectedYear.value, selectedMonth.value - 1)
|
|
167
|
+
getDaysInMonth(selectedYear.value, selectedMonth.value - 1),
|
|
109
168
|
);
|
|
110
169
|
|
|
111
170
|
const prevMonthDaysToShow = computed(() =>
|
|
@@ -114,11 +173,11 @@
|
|
|
114
173
|
date: new Date(
|
|
115
174
|
selectedYear.value,
|
|
116
175
|
selectedMonth.value - 1,
|
|
117
|
-
daysInPreviousMonth.value - i
|
|
176
|
+
daysInPreviousMonth.value - i,
|
|
118
177
|
),
|
|
119
178
|
dayOfWeek: (firstDayOfMonth.value - 1 - i + 7) % 7,
|
|
120
179
|
fromPreviousMonth: true,
|
|
121
|
-
})).reverse()
|
|
180
|
+
})).reverse(),
|
|
122
181
|
);
|
|
123
182
|
|
|
124
183
|
const currentMonthDaysToShow = computed(() =>
|
|
@@ -128,18 +187,18 @@
|
|
|
128
187
|
dayOfWeek: new Date(
|
|
129
188
|
selectedYear.value,
|
|
130
189
|
selectedMonth.value,
|
|
131
|
-
i + 1
|
|
190
|
+
i + 1,
|
|
132
191
|
).getDay(),
|
|
133
192
|
fromCurrentMonth: true,
|
|
134
|
-
}))
|
|
193
|
+
})),
|
|
135
194
|
);
|
|
136
195
|
|
|
137
196
|
const lastDayOfMonth = computed(() =>
|
|
138
197
|
new Date(
|
|
139
198
|
selectedYear.value,
|
|
140
199
|
selectedMonth.value,
|
|
141
|
-
daysInCurrentMonth.value
|
|
142
|
-
).getDay()
|
|
200
|
+
daysInCurrentMonth.value,
|
|
201
|
+
).getDay(),
|
|
143
202
|
);
|
|
144
203
|
|
|
145
204
|
const nextMonthDaysToShow = computed(() => {
|
|
@@ -190,8 +249,8 @@
|
|
|
190
249
|
}
|
|
191
250
|
|
|
192
251
|
function handleDayClick(date: Date) {
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
252
|
+
if (props.disabled) return;
|
|
253
|
+
if (props.isDateDisabled?.(date)) return;
|
|
254
|
+
model.value = date;
|
|
196
255
|
}
|
|
197
256
|
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styles from "~/utils/styles";
|
|
2
2
|
|
|
3
3
|
export const calendarStyles = styles(
|
|
4
|
-
"flex w-fit flex-col items-center rounded-2 border border-gray-300 bg-gray-100 p-3 shadow-xs dark:border-gray-900 dark:bg-black"
|
|
4
|
+
"flex w-fit flex-col items-center rounded-2 border border-gray-300 bg-gray-100 p-3 shadow-xs dark:border-gray-900 dark:bg-black",
|
|
5
5
|
);
|
|
6
6
|
|
|
7
7
|
export const calendarDayStyles = styles(
|
|
@@ -9,15 +9,27 @@ export const calendarDayStyles = styles(
|
|
|
9
9
|
{
|
|
10
10
|
currentMonth: {
|
|
11
11
|
true: "dark:text-gray-300",
|
|
12
|
-
false: "text-gray-
|
|
12
|
+
false: "text-gray-400 dark:text-gray-600",
|
|
13
13
|
},
|
|
14
14
|
selected: {
|
|
15
15
|
true: "bg-black text-white hover:bg-black dark:bg-white dark:!text-black dark:hover:bg-white",
|
|
16
16
|
false: "hover:bg-gray-300 dark:hover:bg-gray-700",
|
|
17
17
|
},
|
|
18
|
+
inRange: {
|
|
19
|
+
true: "bg-gray-300 hover:bg-gray-300 dark:bg-gray-800 dark:hover:bg-gray-800",
|
|
20
|
+
false: "",
|
|
21
|
+
},
|
|
18
22
|
disabled: {
|
|
19
23
|
true: "cursor-default pointer-events-none",
|
|
20
24
|
false: "cursor-pointer",
|
|
21
25
|
},
|
|
22
|
-
|
|
26
|
+
greyedOut: {
|
|
27
|
+
true: "opacity-35 cursor-not-allowed pointer-events-none",
|
|
28
|
+
false: "",
|
|
29
|
+
},
|
|
30
|
+
today: {
|
|
31
|
+
true: "ring-1 ring-gray-400 dark:ring-gray-600",
|
|
32
|
+
false: "",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
23
35
|
);
|
|
@@ -3,4 +3,13 @@ export interface UixyCalendarProps {
|
|
|
3
3
|
month?: number;
|
|
4
4
|
startOfWeek?: "Sunday" | "Monday";
|
|
5
5
|
disabled?: boolean;
|
|
6
|
+
isDateDisabled?: (date: Date) => boolean;
|
|
7
|
+
rangeStart?: Date | null;
|
|
8
|
+
rangeEnd?: Date | null;
|
|
9
|
+
rangeHover?: Date | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface UixyCalendarEmits {
|
|
13
|
+
(event: "dayHover", date: Date): void;
|
|
14
|
+
(event: "dayLeave"): void;
|
|
6
15
|
}
|