@korsolutions/ui 0.0.71 → 0.0.74

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.
Files changed (172) hide show
  1. package/dist/module/components/calendar/calendar/calendar-root.js +57 -0
  2. package/dist/module/components/calendar/calendar/calendar-root.js.map +1 -0
  3. package/dist/module/components/calendar/{components → calendar}/calendar-weeks.js +12 -5
  4. package/dist/module/components/calendar/calendar/calendar-weeks.js.map +1 -0
  5. package/dist/module/components/calendar/calendar/index.js +19 -0
  6. package/dist/module/components/calendar/calendar/index.js.map +1 -0
  7. package/dist/module/components/calendar/calendar/types.js.map +1 -0
  8. package/dist/module/components/calendar/{variants → calendar/variants}/default.js +29 -13
  9. package/dist/module/components/calendar/calendar/variants/default.js.map +1 -0
  10. package/dist/module/components/calendar/calendar/variants/index.js.map +1 -0
  11. package/dist/module/components/calendar/index.js +3 -16
  12. package/dist/module/components/calendar/index.js.map +1 -1
  13. package/dist/module/components/calendar/{context.js → shared/calendar-context.js} +2 -2
  14. package/dist/module/components/calendar/shared/calendar-context.js.map +1 -0
  15. package/dist/module/components/calendar/{components → shared}/calendar-day.js +12 -11
  16. package/dist/module/components/calendar/shared/calendar-day.js.map +1 -0
  17. package/dist/module/components/calendar/{components → shared}/calendar-header.js +1 -1
  18. package/dist/module/components/calendar/shared/calendar-header.js.map +1 -0
  19. package/dist/module/components/calendar/shared/calendar-nav-buttons.js +63 -0
  20. package/dist/module/components/calendar/shared/calendar-nav-buttons.js.map +1 -0
  21. package/dist/module/components/calendar/{components → shared}/calendar-title.js +2 -2
  22. package/dist/module/components/calendar/shared/calendar-title.js.map +1 -0
  23. package/dist/module/components/calendar/{components → shared}/calendar-week-labels.js +1 -1
  24. package/dist/module/components/calendar/shared/calendar-week-labels.js.map +1 -0
  25. package/dist/module/components/calendar/shared/types.js +4 -0
  26. package/dist/module/components/calendar/shared/types.js.map +1 -0
  27. package/dist/module/components/calendar/timeline/calendar-timeline.js +135 -0
  28. package/dist/module/components/calendar/timeline/calendar-timeline.js.map +1 -0
  29. package/dist/module/components/calendar/timeline/index.js +4 -0
  30. package/dist/module/components/calendar/timeline/index.js.map +1 -0
  31. package/dist/module/components/calendar/timeline/types.js +4 -0
  32. package/dist/module/components/calendar/timeline/types.js.map +1 -0
  33. package/dist/module/components/calendar/timeline/variants/default.js +62 -0
  34. package/dist/module/components/calendar/timeline/variants/default.js.map +1 -0
  35. package/dist/module/components/calendar/timeline/variants/index.js +7 -0
  36. package/dist/module/components/calendar/timeline/variants/index.js.map +1 -0
  37. package/dist/module/components/calendar/week-calendar/index.js +4 -0
  38. package/dist/module/components/calendar/week-calendar/index.js.map +1 -0
  39. package/dist/module/components/calendar/week-calendar/types.js +4 -0
  40. package/dist/module/components/calendar/week-calendar/types.js.map +1 -0
  41. package/dist/module/components/calendar/week-calendar/variants/default.js +145 -0
  42. package/dist/module/components/calendar/week-calendar/variants/default.js.map +1 -0
  43. package/dist/module/components/calendar/week-calendar/variants/index.js +7 -0
  44. package/dist/module/components/calendar/week-calendar/variants/index.js.map +1 -0
  45. package/dist/module/components/calendar/week-calendar/week-calendar.js +158 -0
  46. package/dist/module/components/calendar/week-calendar/week-calendar.js.map +1 -0
  47. package/dist/module/components/combobox/components/combobox-empty.js.map +1 -1
  48. package/dist/module/components/index.js +1 -1
  49. package/dist/module/index.js +2 -1
  50. package/dist/module/index.js.map +1 -1
  51. package/dist/module/themes/provider.js.map +1 -1
  52. package/dist/module/utils/date-utils.js +45 -17
  53. package/dist/module/utils/date-utils.js.map +1 -1
  54. package/dist/typescript/src/components/calendar/{components → calendar}/calendar-root.d.ts +2 -1
  55. package/dist/typescript/src/components/calendar/calendar/calendar-root.d.ts.map +1 -0
  56. package/dist/typescript/src/components/calendar/calendar/calendar-weeks.d.ts.map +1 -0
  57. package/dist/typescript/src/components/calendar/calendar/index.d.ts +25 -0
  58. package/dist/typescript/src/components/calendar/calendar/index.d.ts.map +1 -0
  59. package/dist/typescript/src/components/calendar/calendar/types.d.ts +7 -0
  60. package/dist/typescript/src/components/calendar/calendar/types.d.ts.map +1 -0
  61. package/dist/typescript/src/components/calendar/calendar/variants/default.d.ts +3 -0
  62. package/dist/typescript/src/components/calendar/calendar/variants/default.d.ts.map +1 -0
  63. package/dist/typescript/src/components/calendar/calendar/variants/index.d.ts.map +1 -0
  64. package/dist/typescript/src/components/calendar/index.d.ts +7 -24
  65. package/dist/typescript/src/components/calendar/index.d.ts.map +1 -1
  66. package/dist/typescript/src/components/calendar/{context.d.ts → shared/calendar-context.d.ts} +10 -4
  67. package/dist/typescript/src/components/calendar/shared/calendar-context.d.ts.map +1 -0
  68. package/dist/typescript/src/components/calendar/shared/calendar-day.d.ts.map +1 -0
  69. package/dist/typescript/src/components/calendar/shared/calendar-header.d.ts.map +1 -0
  70. package/dist/typescript/src/components/calendar/shared/calendar-nav-buttons.d.ts +7 -0
  71. package/dist/typescript/src/components/calendar/shared/calendar-nav-buttons.d.ts.map +1 -0
  72. package/dist/typescript/src/components/calendar/shared/calendar-title.d.ts.map +1 -0
  73. package/dist/typescript/src/components/calendar/shared/calendar-week-labels.d.ts.map +1 -0
  74. package/dist/typescript/src/components/calendar/shared/types.d.ts +18 -0
  75. package/dist/typescript/src/components/calendar/shared/types.d.ts.map +1 -0
  76. package/dist/typescript/src/components/calendar/timeline/calendar-timeline.d.ts +23 -0
  77. package/dist/typescript/src/components/calendar/timeline/calendar-timeline.d.ts.map +1 -0
  78. package/dist/typescript/src/components/calendar/timeline/index.d.ts +4 -0
  79. package/dist/typescript/src/components/calendar/timeline/index.d.ts.map +1 -0
  80. package/dist/typescript/src/components/calendar/timeline/types.d.ts +13 -0
  81. package/dist/typescript/src/components/calendar/timeline/types.d.ts.map +1 -0
  82. package/dist/typescript/src/components/calendar/timeline/variants/default.d.ts +3 -0
  83. package/dist/typescript/src/components/calendar/timeline/variants/default.d.ts.map +1 -0
  84. package/dist/typescript/src/components/calendar/timeline/variants/index.d.ts +4 -0
  85. package/dist/typescript/src/components/calendar/timeline/variants/index.d.ts.map +1 -0
  86. package/dist/typescript/src/components/calendar/week-calendar/index.d.ts +4 -0
  87. package/dist/typescript/src/components/calendar/week-calendar/index.d.ts.map +1 -0
  88. package/dist/typescript/src/components/calendar/week-calendar/types.d.ts +7 -0
  89. package/dist/typescript/src/components/calendar/week-calendar/types.d.ts.map +1 -0
  90. package/dist/typescript/src/components/calendar/week-calendar/variants/default.d.ts +3 -0
  91. package/dist/typescript/src/components/calendar/week-calendar/variants/default.d.ts.map +1 -0
  92. package/dist/typescript/src/components/calendar/week-calendar/variants/index.d.ts +4 -0
  93. package/dist/typescript/src/components/calendar/week-calendar/variants/index.d.ts.map +1 -0
  94. package/dist/typescript/src/components/calendar/week-calendar/week-calendar.d.ts +16 -0
  95. package/dist/typescript/src/components/calendar/week-calendar/week-calendar.d.ts.map +1 -0
  96. package/dist/typescript/src/components/combobox/components/combobox-empty.d.ts +2 -1
  97. package/dist/typescript/src/components/combobox/components/combobox-empty.d.ts.map +1 -1
  98. package/dist/typescript/src/components/index.d.ts +1 -1
  99. package/dist/typescript/src/index.d.ts.map +1 -1
  100. package/dist/typescript/src/themes/provider.d.ts +4 -0
  101. package/dist/typescript/src/themes/provider.d.ts.map +1 -1
  102. package/dist/typescript/src/utils/date-utils.d.ts +8 -1
  103. package/dist/typescript/src/utils/date-utils.d.ts.map +1 -1
  104. package/package.json +1 -1
  105. package/src/components/calendar/calendar/calendar-root.tsx +73 -0
  106. package/src/components/calendar/{components → calendar}/calendar-weeks.tsx +22 -8
  107. package/src/components/calendar/calendar/index.ts +26 -0
  108. package/src/components/calendar/calendar/types.ts +7 -0
  109. package/src/components/calendar/{variants → calendar/variants}/default.tsx +30 -14
  110. package/src/components/calendar/index.ts +21 -24
  111. package/src/components/calendar/shared/calendar-context.ts +35 -0
  112. package/src/components/calendar/{components → shared}/calendar-day.tsx +41 -13
  113. package/src/components/calendar/{components → shared}/calendar-header.tsx +7 -2
  114. package/src/components/calendar/shared/calendar-nav-buttons.tsx +81 -0
  115. package/src/components/calendar/{components → shared}/calendar-title.tsx +8 -3
  116. package/src/components/calendar/{components → shared}/calendar-week-labels.tsx +8 -2
  117. package/src/components/calendar/shared/types.ts +24 -0
  118. package/src/components/calendar/timeline/calendar-timeline.tsx +202 -0
  119. package/src/components/calendar/timeline/index.ts +6 -0
  120. package/src/components/calendar/timeline/types.ts +13 -0
  121. package/src/components/calendar/timeline/variants/default.tsx +59 -0
  122. package/src/components/calendar/timeline/variants/index.ts +5 -0
  123. package/src/components/calendar/week-calendar/index.ts +3 -0
  124. package/src/components/calendar/week-calendar/types.ts +7 -0
  125. package/src/components/calendar/week-calendar/variants/default.tsx +141 -0
  126. package/src/components/calendar/week-calendar/variants/index.ts +5 -0
  127. package/src/components/calendar/week-calendar/week-calendar.tsx +243 -0
  128. package/src/components/combobox/components/combobox-empty.tsx +2 -1
  129. package/src/components/index.ts +1 -1
  130. package/src/index.tsx +2 -0
  131. package/src/themes/provider.tsx +4 -0
  132. package/src/utils/date-utils.ts +53 -21
  133. package/dist/module/components/calendar/components/calendar-day.js.map +0 -1
  134. package/dist/module/components/calendar/components/calendar-header.js.map +0 -1
  135. package/dist/module/components/calendar/components/calendar-nav-button.js +0 -61
  136. package/dist/module/components/calendar/components/calendar-nav-button.js.map +0 -1
  137. package/dist/module/components/calendar/components/calendar-root.js +0 -39
  138. package/dist/module/components/calendar/components/calendar-root.js.map +0 -1
  139. package/dist/module/components/calendar/components/calendar-title.js.map +0 -1
  140. package/dist/module/components/calendar/components/calendar-week-labels.js.map +0 -1
  141. package/dist/module/components/calendar/components/calendar-weeks.js.map +0 -1
  142. package/dist/module/components/calendar/context.js.map +0 -1
  143. package/dist/module/components/calendar/types.js.map +0 -1
  144. package/dist/module/components/calendar/variants/default.js.map +0 -1
  145. package/dist/module/components/calendar/variants/index.js.map +0 -1
  146. package/dist/typescript/src/components/calendar/components/calendar-day.d.ts.map +0 -1
  147. package/dist/typescript/src/components/calendar/components/calendar-header.d.ts.map +0 -1
  148. package/dist/typescript/src/components/calendar/components/calendar-nav-button.d.ts +0 -10
  149. package/dist/typescript/src/components/calendar/components/calendar-nav-button.d.ts.map +0 -1
  150. package/dist/typescript/src/components/calendar/components/calendar-root.d.ts.map +0 -1
  151. package/dist/typescript/src/components/calendar/components/calendar-title.d.ts.map +0 -1
  152. package/dist/typescript/src/components/calendar/components/calendar-week-labels.d.ts.map +0 -1
  153. package/dist/typescript/src/components/calendar/components/calendar-weeks.d.ts.map +0 -1
  154. package/dist/typescript/src/components/calendar/context.d.ts.map +0 -1
  155. package/dist/typescript/src/components/calendar/types.d.ts +0 -37
  156. package/dist/typescript/src/components/calendar/types.d.ts.map +0 -1
  157. package/dist/typescript/src/components/calendar/variants/default.d.ts +0 -3
  158. package/dist/typescript/src/components/calendar/variants/default.d.ts.map +0 -1
  159. package/dist/typescript/src/components/calendar/variants/index.d.ts.map +0 -1
  160. package/src/components/calendar/components/calendar-nav-button.tsx +0 -60
  161. package/src/components/calendar/components/calendar-root.tsx +0 -42
  162. package/src/components/calendar/context.ts +0 -23
  163. package/src/components/calendar/types.ts +0 -39
  164. /package/dist/module/components/calendar/{types.js → calendar/types.js} +0 -0
  165. /package/dist/module/components/calendar/{variants → calendar/variants}/index.js +0 -0
  166. /package/dist/typescript/src/components/calendar/{components → calendar}/calendar-weeks.d.ts +0 -0
  167. /package/dist/typescript/src/components/calendar/{variants → calendar/variants}/index.d.ts +0 -0
  168. /package/dist/typescript/src/components/calendar/{components → shared}/calendar-day.d.ts +0 -0
  169. /package/dist/typescript/src/components/calendar/{components → shared}/calendar-header.d.ts +0 -0
  170. /package/dist/typescript/src/components/calendar/{components → shared}/calendar-title.d.ts +0 -0
  171. /package/dist/typescript/src/components/calendar/{components → shared}/calendar-week-labels.d.ts +0 -0
  172. /package/src/components/calendar/{variants → calendar/variants}/index.ts +0 -0
@@ -0,0 +1,202 @@
1
+ import React, { useMemo } from "react";
2
+ import { ScrollView, View, type StyleProp, type ViewStyle } from "react-native";
3
+ import { isDateSameDay } from "../../../utils/date-utils";
4
+ import { TimelineVariants } from "./variants";
5
+
6
+ const HOUR_HEIGHT = 60;
7
+
8
+ function formatHour(hour: number): string {
9
+ if (hour === 0) return "12 AM";
10
+ if (hour < 12) return `${hour} AM`;
11
+ if (hour === 12) return "12 PM";
12
+ return `${hour - 12} PM`;
13
+ }
14
+
15
+ function getMinutesFromStartOfDay(date: Date): number {
16
+ return date.getHours() * 60 + date.getMinutes();
17
+ }
18
+
19
+ export interface TimelineEventLayout {
20
+ top: number;
21
+ height: number;
22
+ left: string;
23
+ width: string;
24
+ }
25
+
26
+ export interface CalendarTimelineProps<T> {
27
+ events?: T[];
28
+ date: Date;
29
+ startHour?: number;
30
+ endHour?: number;
31
+ getStart: (event: T) => Date;
32
+ getEnd: (event: T) => Date;
33
+ keyExtractor: (event: T) => string;
34
+ renderEvent: (event: T, layout: TimelineEventLayout) => React.ReactNode;
35
+ variant?: keyof typeof TimelineVariants;
36
+ style?: StyleProp<ViewStyle>;
37
+ }
38
+
39
+ function groupOverlappingEvents<T>(
40
+ events: T[],
41
+ getStart: (e: T) => Date,
42
+ getEnd: (e: T) => Date,
43
+ ): T[][] {
44
+ const sorted = [...events].sort(
45
+ (a, b) => getStart(a).getTime() - getStart(b).getTime(),
46
+ );
47
+ const groups: T[][] = [];
48
+
49
+ for (const event of sorted) {
50
+ let placed = false;
51
+ const eStart = getStart(event);
52
+ const eEnd = getEnd(event);
53
+
54
+ for (const group of groups) {
55
+ const overlaps = group.some((g) => {
56
+ const gStart = getStart(g);
57
+ const gEnd = getEnd(g);
58
+ return (
59
+ (eStart >= gStart && eStart < gEnd) ||
60
+ (eEnd > gStart && eEnd <= gEnd) ||
61
+ (eStart <= gStart && eEnd >= gEnd)
62
+ );
63
+ });
64
+
65
+ if (overlaps) {
66
+ group.push(event);
67
+ placed = true;
68
+ break;
69
+ }
70
+ }
71
+
72
+ if (!placed) {
73
+ groups.push([event]);
74
+ }
75
+ }
76
+
77
+ return groups;
78
+ }
79
+
80
+ import { Text } from "react-native";
81
+
82
+ export function CalendarTimeline<T>(props: CalendarTimelineProps<T>) {
83
+ const {
84
+ date,
85
+ startHour = 0,
86
+ endHour = 24,
87
+ getStart,
88
+ getEnd,
89
+ keyExtractor,
90
+ renderEvent,
91
+ style,
92
+ } = props;
93
+ const variantStyles = TimelineVariants[props.variant || "default"]();
94
+
95
+ const hours = useMemo(() => {
96
+ const arr: number[] = [];
97
+ for (let i = startHour; i < endHour; i++) {
98
+ arr.push(i);
99
+ }
100
+ return arr;
101
+ }, [startHour, endHour]);
102
+
103
+ const allEvents = props.events ?? [];
104
+
105
+ const dayEvents = useMemo(() => {
106
+ return allEvents.filter((event) => isDateSameDay(getStart(event), date));
107
+ }, [allEvents, date, getStart]);
108
+
109
+ const eventGroups = useMemo(
110
+ () => groupOverlappingEvents(dayEvents, getStart, getEnd),
111
+ [dayEvents, getStart, getEnd],
112
+ );
113
+
114
+ const now = new Date();
115
+ const isToday = isDateSameDay(now, date);
116
+ const currentTimePosition = isToday
117
+ ? ((getMinutesFromStartOfDay(now) - startHour * 60) / 60) * HOUR_HEIGHT
118
+ : null;
119
+
120
+ return (
121
+ <ScrollView
122
+ style={[variantStyles.container, style]}
123
+ showsVerticalScrollIndicator
124
+ >
125
+ <View style={variantStyles.timeline}>
126
+ {/* Time column */}
127
+ <View style={variantStyles.timeColumn}>
128
+ {hours.map((hour) => (
129
+ <View key={hour} style={variantStyles.timeSlot}>
130
+ <Text style={variantStyles.timeText}>{formatHour(hour)}</Text>
131
+ </View>
132
+ ))}
133
+ </View>
134
+
135
+ {/* Events column */}
136
+ <View style={variantStyles.eventsColumn}>
137
+ {hours.map((hour) => (
138
+ <View key={hour} style={variantStyles.hourLine} />
139
+ ))}
140
+
141
+ {/* Current time indicator */}
142
+ {currentTimePosition != null && currentTimePosition >= 0 && (
143
+ <View
144
+ style={{
145
+ position: "absolute",
146
+ left: 0,
147
+ right: 0,
148
+ top: currentTimePosition,
149
+ flexDirection: "row",
150
+ alignItems: "center",
151
+ zIndex: 1000,
152
+ }}
153
+ >
154
+ <View style={variantStyles.currentTimeDot} />
155
+ <View style={variantStyles.currentTimeLineBar} />
156
+ </View>
157
+ )}
158
+
159
+ {/* Events */}
160
+ {eventGroups.map((group) =>
161
+ group.map((event) => {
162
+ const startMinutes = getMinutesFromStartOfDay(getStart(event));
163
+ const endMinutes = getMinutesFromStartOfDay(getEnd(event));
164
+ const duration = endMinutes - startMinutes;
165
+
166
+ const top =
167
+ ((startMinutes - startHour * 60) / 60) * HOUR_HEIGHT + 1;
168
+ const height = (duration / 60) * HOUR_HEIGHT - 3;
169
+
170
+ const indexInGroup = group.indexOf(event);
171
+ const totalInGroup = group.length;
172
+ const eventWidth = `${100 / totalInGroup - 1}%`;
173
+ const left = `${(100 / totalInGroup) * indexInGroup}%`;
174
+
175
+ const layout: TimelineEventLayout = {
176
+ top,
177
+ height: Math.max(height, 20),
178
+ left,
179
+ width: eventWidth,
180
+ };
181
+
182
+ return (
183
+ <View
184
+ key={keyExtractor(event)}
185
+ style={{
186
+ position: "absolute",
187
+ top: layout.top,
188
+ height: layout.height,
189
+ left: layout.left as unknown as number,
190
+ width: layout.width as unknown as number,
191
+ }}
192
+ >
193
+ {renderEvent(event, layout)}
194
+ </View>
195
+ );
196
+ }),
197
+ )}
198
+ </View>
199
+ </View>
200
+ </ScrollView>
201
+ );
202
+ }
@@ -0,0 +1,6 @@
1
+ export { CalendarTimeline } from "./calendar-timeline";
2
+ export type {
3
+ CalendarTimelineProps,
4
+ TimelineEventLayout,
5
+ } from "./calendar-timeline";
6
+ export type { TimelineStyles } from "./types";
@@ -0,0 +1,13 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+
3
+ export interface TimelineStyles {
4
+ container?: StyleProp<ViewStyle>;
5
+ timeline?: StyleProp<ViewStyle>;
6
+ timeColumn?: StyleProp<ViewStyle>;
7
+ timeSlot?: StyleProp<ViewStyle>;
8
+ timeText?: StyleProp<TextStyle>;
9
+ eventsColumn?: StyleProp<ViewStyle>;
10
+ hourLine?: StyleProp<ViewStyle>;
11
+ currentTimeDot?: StyleProp<ViewStyle>;
12
+ currentTimeLineBar?: StyleProp<ViewStyle>;
13
+ }
@@ -0,0 +1,59 @@
1
+ import { useThemedStyles } from "../../../../utils/use-themed-styles";
2
+ import type { TimelineStyles } from "../types";
3
+
4
+ const HOUR_HEIGHT = 60;
5
+ const TIME_COLUMN_WIDTH = 60;
6
+
7
+ export const useTimelineVariantDefault = (): TimelineStyles => {
8
+ return useThemedStyles(
9
+ ({ colors, radius, fontFamily, fontSize }): TimelineStyles => ({
10
+ container: {
11
+ backgroundColor: colors.background,
12
+ borderRadius: radius,
13
+ borderWidth: 1,
14
+ borderColor: colors.border,
15
+ },
16
+ timeline: {
17
+ flexDirection: "row",
18
+ minHeight: HOUR_HEIGHT * 24,
19
+ },
20
+ timeColumn: {
21
+ width: TIME_COLUMN_WIDTH,
22
+ borderRightWidth: 1,
23
+ borderRightColor: colors.border,
24
+ },
25
+ timeSlot: {
26
+ height: HOUR_HEIGHT,
27
+ paddingHorizontal: 8,
28
+ alignItems: "flex-end",
29
+ },
30
+ timeText: {
31
+ fontSize: fontSize * 0.75,
32
+ fontWeight: "500",
33
+ fontFamily,
34
+ color: colors.mutedForeground,
35
+ },
36
+ eventsColumn: {
37
+ flex: 1,
38
+ position: "relative",
39
+ },
40
+ hourLine: {
41
+ height: HOUR_HEIGHT,
42
+ borderBottomWidth: 1,
43
+ borderBottomColor: colors.border,
44
+ },
45
+ currentTimeDot: {
46
+ width: 10,
47
+ height: 10,
48
+ borderRadius: 5,
49
+ marginLeft: -5,
50
+ backgroundColor: colors.danger,
51
+ },
52
+ currentTimeLineBar: {
53
+ flex: 1,
54
+ height: 2,
55
+ backgroundColor: colors.danger,
56
+ },
57
+ }),
58
+ );
59
+ };
@@ -0,0 +1,5 @@
1
+ import { useTimelineVariantDefault } from "./default";
2
+
3
+ export const TimelineVariants = {
4
+ default: useTimelineVariantDefault,
5
+ };
@@ -0,0 +1,3 @@
1
+ export type { WeekCalendarStyles } from "./types";
2
+ export { WeekCalendar } from "./week-calendar";
3
+ export type { WeekCalendarProps } from "./week-calendar";
@@ -0,0 +1,7 @@
1
+ import type { StyleProp, ViewStyle } from "react-native";
2
+ import type { BaseCalendarStyles } from "../shared/types";
3
+
4
+ export interface WeekCalendarStyles extends BaseCalendarStyles {
5
+ weekStrip?: StyleProp<ViewStyle>;
6
+ swipeContainer?: StyleProp<ViewStyle>;
7
+ }
@@ -0,0 +1,141 @@
1
+ import { hslaSetRelativeLightness } from "../../../../utils/hsla-utils";
2
+ import { useThemedStyles } from "../../../../utils/use-themed-styles";
3
+ import type { WeekCalendarStyles } from "../types";
4
+
5
+ export const useWeekCalendarVariantDefault = (): WeekCalendarStyles => {
6
+ return useThemedStyles(
7
+ ({ colors, radius, fontFamily, fontSize }): WeekCalendarStyles => ({
8
+ root: {
9
+ padding: 16,
10
+ backgroundColor: colors.background,
11
+ borderRadius: radius,
12
+ borderWidth: 1,
13
+ borderColor: colors.border,
14
+ },
15
+ header: {
16
+ flexDirection: "row",
17
+ justifyContent: "space-between",
18
+ alignItems: "center",
19
+ marginBottom: 16,
20
+ paddingHorizontal: 8,
21
+ },
22
+ headerTitle: {
23
+ fontSize: fontSize * 1.125,
24
+ fontWeight: "600",
25
+ fontFamily,
26
+ color: colors.foreground,
27
+ },
28
+ navButtons: {
29
+ flexDirection: "row",
30
+ gap: 4,
31
+ },
32
+ navButton: {
33
+ default: {
34
+ width: 32,
35
+ height: 32,
36
+ borderRadius: radius,
37
+ alignItems: "center",
38
+ justifyContent: "center",
39
+ backgroundColor: "transparent",
40
+ },
41
+ disabled: {
42
+ opacity: 0.5,
43
+ },
44
+ hovered: {
45
+ backgroundColor: hslaSetRelativeLightness(colors.secondary, -1),
46
+ },
47
+ },
48
+ navButtonIcon: {
49
+ default: {
50
+ color: colors.foreground,
51
+ size: fontSize * 1.25,
52
+ style: {
53
+ color: colors.foreground,
54
+ fontSize: fontSize * 1.25,
55
+ lineHeight: fontSize * 1.25,
56
+ textAlign: "center",
57
+ fontWeight: "500",
58
+ pointerEvents: "none",
59
+ },
60
+ },
61
+ disabled: {
62
+ color: colors.mutedForeground,
63
+ },
64
+ },
65
+ weekLabels: {
66
+ flexDirection: "row",
67
+ justifyContent: "space-between",
68
+ marginBottom: 8,
69
+ paddingVertical: 8,
70
+ gap: 2,
71
+ },
72
+ weekLabel: {
73
+ fontSize: fontSize * 0.875,
74
+ fontWeight: "500",
75
+ fontFamily,
76
+ color: colors.mutedForeground,
77
+ width: 40,
78
+ textAlign: "center",
79
+ },
80
+ swipeContainer: {
81
+ overflow: "hidden",
82
+ },
83
+ weekStrip: {
84
+ flexDirection: "row",
85
+ justifyContent: "space-between",
86
+ gap: 2,
87
+ },
88
+ dayButton: {
89
+ default: {
90
+ width: 40,
91
+ height: 40,
92
+ borderRadius: radius * 0.5,
93
+ alignItems: "center",
94
+ justifyContent: "center",
95
+ backgroundColor: "transparent",
96
+ },
97
+ selected: {
98
+ backgroundColor: colors.primary,
99
+ },
100
+ today: {
101
+ borderWidth: 1,
102
+ borderColor: colors.primary,
103
+ },
104
+ disabled: {
105
+ opacity: 0.3,
106
+ },
107
+ hovered: {
108
+ backgroundColor: colors.muted,
109
+ },
110
+ },
111
+ dayText: {
112
+ default: {
113
+ fontSize,
114
+ fontFamily,
115
+ color: colors.foreground,
116
+ fontWeight: "400",
117
+ pointerEvents: "none",
118
+ },
119
+ selected: {
120
+ color: colors.primaryForeground,
121
+ fontWeight: "600",
122
+ },
123
+ today: {
124
+ color: colors.primary,
125
+ fontWeight: "600",
126
+ },
127
+ disabled: {
128
+ color: colors.mutedForeground,
129
+ },
130
+ },
131
+ dayMarker: {
132
+ width: 5,
133
+ height: 5,
134
+ borderRadius: 2.5,
135
+ backgroundColor: colors.primary,
136
+ marginTop: 2,
137
+ alignSelf: "center",
138
+ },
139
+ }),
140
+ );
141
+ };
@@ -0,0 +1,5 @@
1
+ import { useWeekCalendarVariantDefault } from "./default";
2
+
3
+ export const WeekCalendarVariants = {
4
+ default: useWeekCalendarVariantDefault,
5
+ };
@@ -0,0 +1,243 @@
1
+ import React, { useCallback, useMemo, useState } from "react";
2
+ import {
3
+ Pressable,
4
+ ScrollView,
5
+ StyleSheet,
6
+ Text,
7
+ View,
8
+ type NativeScrollEvent,
9
+ type NativeSyntheticEvent,
10
+ type StyleProp,
11
+ type ViewStyle,
12
+ } from "react-native";
13
+ import { useComponentsConfig } from "../../../themes";
14
+ import {
15
+ addWeeks,
16
+ endOfWeek,
17
+ formatDate,
18
+ getWeekDates,
19
+ isDateAfter,
20
+ isDateBefore,
21
+ startOfWeek,
22
+ subWeeks,
23
+ } from "../../../utils/date-utils";
24
+ import {
25
+ CalendarContext,
26
+ type CalendarContextValue,
27
+ } from "../shared/calendar-context";
28
+ import { CalendarDay } from "../shared/calendar-day";
29
+ import type { CalendarNavButtonState } from "../shared/types";
30
+ import { WeekCalendarVariants } from "./variants";
31
+
32
+ const DEFAULT_WEEK_DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
33
+
34
+ export interface WeekCalendarProps {
35
+ value?: Date | null;
36
+ onChange?: (date: Date | null) => void;
37
+ defaultWeek?: Date;
38
+ minDate?: Date;
39
+ maxDate?: Date;
40
+ markedDates?: Date[];
41
+ weekDays?: string[];
42
+ variant?: keyof typeof WeekCalendarVariants;
43
+ style?: StyleProp<ViewStyle>;
44
+ }
45
+
46
+ const calculateNavState = (
47
+ isDisabled: boolean,
48
+ isHovered: boolean,
49
+ ): CalendarNavButtonState => {
50
+ if (isDisabled) return "disabled";
51
+ if (isHovered) return "hovered";
52
+ return "default";
53
+ };
54
+
55
+ export function WeekCalendar(props: WeekCalendarProps) {
56
+ const {
57
+ value,
58
+ onChange,
59
+ defaultWeek = new Date(),
60
+ minDate,
61
+ maxDate,
62
+ markedDates,
63
+ weekDays = DEFAULT_WEEK_DAYS,
64
+ style,
65
+ } = props;
66
+ const styles = WeekCalendarVariants[props.variant || "default"]();
67
+ const config = useComponentsConfig();
68
+ const PrevIcon = config?.calendar?.prevIcon;
69
+ const NextIcon = config?.calendar?.nextIcon;
70
+
71
+ const [currentWeekStart, setCurrentWeekStart] = useState<Date>(
72
+ startOfWeek(defaultWeek),
73
+ );
74
+ const [containerWidth, setContainerWidth] = useState(0);
75
+ const [prevHovered, setPrevHovered] = useState(false);
76
+ const [nextHovered, setNextHovered] = useState(false);
77
+
78
+ const currentMonth = currentWeekStart;
79
+
80
+ const goToPrev = useCallback(() => {
81
+ setCurrentWeekStart((prev) => subWeeks(prev, 1));
82
+ }, []);
83
+
84
+ const goToNext = useCallback(() => {
85
+ setCurrentWeekStart((prev) => addWeeks(prev, 1));
86
+ }, []);
87
+
88
+ const isPrevDisabled = useMemo(() => {
89
+ if (!minDate) return false;
90
+ const prevWeekEnd = endOfWeek(subWeeks(currentWeekStart, 1));
91
+ return isDateBefore(prevWeekEnd, minDate);
92
+ }, [currentWeekStart, minDate]);
93
+
94
+ const isNextDisabled = useMemo(() => {
95
+ if (!maxDate) return false;
96
+ const nextWeekStart = addWeeks(currentWeekStart, 1);
97
+ return isDateAfter(nextWeekStart, maxDate);
98
+ }, [currentWeekStart, maxDate]);
99
+
100
+ const prevState = calculateNavState(isPrevDisabled, prevHovered);
101
+ const nextState = calculateNavState(isNextDisabled, nextHovered);
102
+
103
+ const prevIconProps = StyleSheet.flatten([
104
+ styles.navButtonIcon?.default,
105
+ styles.navButtonIcon?.[prevState],
106
+ ]);
107
+ const nextIconProps = StyleSheet.flatten([
108
+ styles.navButtonIcon?.default,
109
+ styles.navButtonIcon?.[nextState],
110
+ ]);
111
+
112
+ // Strip logic
113
+ const prevWeekDates = getWeekDates(subWeeks(currentWeekStart, 1));
114
+ const currentWeekDates = getWeekDates(currentWeekStart);
115
+ const nextWeekDates = getWeekDates(addWeeks(currentWeekStart, 1));
116
+ const weeks = [prevWeekDates, currentWeekDates, nextWeekDates];
117
+ const scrollKey = currentWeekStart.getTime();
118
+
119
+ const handleScrollEnd = useCallback(
120
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
121
+ const offsetX = event.nativeEvent.contentOffset.x;
122
+ const pageIndex = Math.round(offsetX / containerWidth);
123
+
124
+ if (pageIndex === 0) {
125
+ setCurrentWeekStart(subWeeks(currentWeekStart, 1));
126
+ } else if (pageIndex === 2) {
127
+ setCurrentWeekStart(addWeeks(currentWeekStart, 1));
128
+ }
129
+ },
130
+ [containerWidth, currentWeekStart],
131
+ );
132
+
133
+ const contextValue = useMemo<CalendarContextValue>(
134
+ () => ({
135
+ value,
136
+ onChange,
137
+ currentMonth,
138
+ goToPrev,
139
+ goToNext,
140
+ isPrevDisabled,
141
+ isNextDisabled,
142
+ minDate,
143
+ maxDate,
144
+ markedDates,
145
+ styles,
146
+ currentWeekStart,
147
+ setCurrentWeekStart,
148
+ }),
149
+ [
150
+ value,
151
+ onChange,
152
+ currentMonth,
153
+ goToPrev,
154
+ goToNext,
155
+ isPrevDisabled,
156
+ isNextDisabled,
157
+ minDate,
158
+ maxDate,
159
+ markedDates,
160
+ styles,
161
+ currentWeekStart,
162
+ ],
163
+ );
164
+
165
+ return (
166
+ <CalendarContext.Provider value={contextValue}>
167
+ <View style={[styles.root, style]}>
168
+ {/* Header */}
169
+ <View style={styles.header}>
170
+ <Text style={styles.headerTitle}>
171
+ {formatDate(currentMonth, "MMMM yyyy")}
172
+ </Text>
173
+ <View style={styles.navButtons}>
174
+ <Pressable
175
+ onPress={goToPrev}
176
+ onHoverIn={() => setPrevHovered(true)}
177
+ onHoverOut={() => setPrevHovered(false)}
178
+ disabled={isPrevDisabled}
179
+ style={[styles.navButton?.default, styles.navButton?.[prevState]]}
180
+ >
181
+ {PrevIcon ? (
182
+ <PrevIcon {...prevIconProps} />
183
+ ) : (
184
+ <Text style={prevIconProps?.style}>‹</Text>
185
+ )}
186
+ </Pressable>
187
+ <Pressable
188
+ onPress={goToNext}
189
+ onHoverIn={() => setNextHovered(true)}
190
+ onHoverOut={() => setNextHovered(false)}
191
+ disabled={isNextDisabled}
192
+ style={[styles.navButton?.default, styles.navButton?.[nextState]]}
193
+ >
194
+ {NextIcon ? (
195
+ <NextIcon {...nextIconProps} />
196
+ ) : (
197
+ <Text style={nextIconProps?.style}>›</Text>
198
+ )}
199
+ </Pressable>
200
+ </View>
201
+ </View>
202
+
203
+ {/* Week day labels */}
204
+ <View style={styles.weekLabels}>
205
+ {weekDays.map((day, index) => (
206
+ <Text key={index} numberOfLines={1} style={styles.weekLabel}>
207
+ {day}
208
+ </Text>
209
+ ))}
210
+ </View>
211
+
212
+ {/* Swipeable strip */}
213
+ <View
214
+ style={styles.swipeContainer}
215
+ onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
216
+ >
217
+ {containerWidth > 0 && (
218
+ <ScrollView
219
+ key={scrollKey}
220
+ horizontal
221
+ pagingEnabled
222
+ showsHorizontalScrollIndicator={false}
223
+ scrollEventThrottle={16}
224
+ onMomentumScrollEnd={handleScrollEnd}
225
+ contentOffset={{ x: containerWidth, y: 0 }}
226
+ >
227
+ {weeks.map((weekDates, weekIndex) => (
228
+ <View
229
+ key={weekIndex}
230
+ style={[styles.weekStrip, { width: containerWidth }]}
231
+ >
232
+ {weekDates.map((date, dayIndex) => (
233
+ <CalendarDay key={dayIndex} date={date} />
234
+ ))}
235
+ </View>
236
+ ))}
237
+ </ScrollView>
238
+ )}
239
+ </View>
240
+ </View>
241
+ </CalendarContext.Provider>
242
+ );
243
+ }
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import { StyleSheet, Text } from "react-native";
3
+ import type { TextChildren } from "../../../types/element.types";
3
4
  import { useCombobox } from "../context";
4
5
 
5
6
  export interface ComboboxEmptyProps {
6
- children?: string;
7
+ children?: TextChildren;
7
8
  }
8
9
 
9
10
  export function ComboboxEmpty(props: ComboboxEmptyProps) {