react-weekly-planning 1.0.44 → 1.0.45

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/README.md CHANGED
@@ -54,27 +54,7 @@ It is possible to use either Weekoffset or Date, or even both simultaneously.
54
54
  ```
55
55
 
56
56
 
57
- #### `scope`
58
57
 
59
- - **Description**: This prop sets the view scope of the calendar.
60
- - **Type**: `"day" | "week"`
61
- - **Use Case**: Use this prop to define whether the calendar should display a full week or only a single day. If omitted, it defaults to a weekly view.
62
-
63
- **Example**:
64
- ```jsx
65
- <Calendar scope="day" dayOffset={0} ... />
66
- ```
67
-
68
- #### `dayOffset`
69
-
70
- - **Description**: This prop specifies which day of the week to display when the scope is set to "day".
71
- - **Type**: `0 | 1 | 2 | 3 | 4 | 5 | 6`
72
- - **Use Case**: If `scope="day"`, use this prop to target a specific day of the week (`0` being the first day of the week, up to `6`).
73
-
74
- **Example**:
75
- ```jsx
76
- <Calendar scope="day" dayOffset={2} ... />
77
- ```
78
58
 
79
59
 
80
60
  ---
@@ -106,7 +86,6 @@ const App = () => {
106
86
  date={date}
107
87
  weekOffset={0}
108
88
  groups={groups}
109
- scope="week"
110
89
  />
111
90
  </div>
112
91
  </CalendarTaskContextProvider>
@@ -126,8 +105,6 @@ Props for the Calendar component.
126
105
  | Prop Name | Type | Description |
127
106
  |------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
128
107
  | `weekOffset` | number | Offset for the week (e.g., -7 for last week, 0 for current week, 7 for next week). |
129
- | `scope` | "day" \| "week" | Sets the calendar view to either a full week or a single day. |
130
- | `dayOffset` | 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 | Offset index for the day column (0 = first day of week, …, 6 = last day) when scope is "day". |
131
108
  | `groups` | GroupFeildsType[] | Array of group data to be displayed in the calendar. |
132
109
  | `className` | string | Additional class names for the calendar component. |
133
110
  | `style` | React.CSSProperties \| undefined | Additional styles for the calendar component. |
@@ -300,7 +277,7 @@ const App = () => (
300
277
 
301
278
  ### 2. Default Behavior of `<Calendar />`
302
279
 
303
- It's important to note that the main `<Calendar />` component uses the `"week"` hash scope by default. If you use the standard component, you are implicitly using this scope. Explicitly defining a `hashScope` on the `CalendarTaskContextProvider` is only necessary when you want to change how tasks are indexed or when building a fully custom UI as shown above.
280
+ It's important to note that the main `<Calendar />` component uses the `"day"` hash scope by default. If you use the standard component, you are implicitly using this scope. Explicitly defining a `hashScope` on the `CalendarTaskContextProvider` is only necessary when you want to change how tasks are indexed or when building a fully custom UI as shown above.
304
281
 
305
282
  ### 3. Understanding Hashes and Scopes
306
283
 
@@ -491,6 +468,8 @@ to create an organization that truly reflects you.
491
468
  ### `useIntersectionObserver`
492
469
 
493
470
  - **Description**: Utility hook designed to help virtualize scrolling components. It leverages the Intersection Observer API to detect when an element enters or leaves the viewport, allowing you to mount or unmount heavy DOM elements dynamically for better performance in long lists or large grids.
471
+ 👉 **Watch the performance demo (rendering 7,000 tasks in a single week):**
472
+ [![Performance Demo](https://img.youtube.com/vi/st4QmsaHoDM/0.jpg)](https://youtu.be/st4QmsaHoDM)
494
473
  - **Parameters**:
495
474
  - `ref` (React.RefObject): The ref assigned to the DOM element you want to observe.
496
475
  - `options` (IntersectionObserverInit, optional): Configuration object (e.g., `rootMargin`, `threshold`).
@@ -13,12 +13,7 @@ const CalendarForWeek = (props) => {
13
13
  const { dailyHours, weekDays } = useCalendarDateState(props.date, props.weekOffset, props.timeZone);
14
14
  const memoizedHeader = useMemo(() => (_jsx("div", { className: "planningCalendarHeader", children: _jsxs("div", { className: `planningCalendarRow ${props.rowsClassName}`, style: Object.assign(Object.assign({}, theadTrStyle), props.rowsStyle), children: [_jsx("div", { className: `dayTh ${props.groupsColsClassName}`, style: Object.assign({}, props.groupsColsStyle), children: _jsx(GroupsHeadContainer, { className: `${props.groupHeadContainerClassName}`, style: props.groupHeadContainerStyle, groupsHeadRender: props.groupsHeadRender }) }), weekDays.map((day, i) => (_jsx("div", { className: `dayCol ${props.daysColsClassName}`, style: Object.assign({}, props.daysColsStyle), children: _jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: i, dayRender: props.dayRender, day: day.day, dayOfTheMonth: day.dayOfTheMonth, dayMonth: day.dayMonth, dayYear: day.dayYear }) }, i)))] }, "header") })), [weekDays, props.rowsClassName, props.rowsStyle, props.groupsColsClassName, props.groupsColsStyle, props.groupHeadContainerClassName, props.groupHeadContainerStyle, props.groupsHeadRender, props.daysColsClassName, props.daysColsStyle, props.dayStyle, props.dayClassName, props.dayRender]);
15
15
  const offset = useMemo(() => updateOffsetWithDateCalendar(props.date), [props.date]);
16
- return (_jsx("div", { className: "calendarForWeek", style: { position: "relative" }, children: _jsxs("div", { className: `planningCalendar ${props.className}`, style: Object.assign({}, props.style), children: [memoizedHeader, _jsx("div", { className: "planningCalendarBody", style: {
17
- // position: "absolute",
18
- // width: "100%",
19
- // // top: `${36 + 5}px`,
20
- // top: `${36 + 5 + ((65 + 4) * Math.max(rowSliceIndexStart, 0))}px`,
21
- }, children: (_a = props.groups) === null || _a === void 0 ? void 0 : _a.map((group, i) => {
16
+ return (_jsx("div", { className: "calendarForWeek", style: { position: "relative" }, children: _jsxs("div", { className: `planningCalendar ${props.className}`, style: Object.assign({}, props.style), children: [memoizedHeader, _jsx("div", { className: "planningCalendarBody", style: {}, children: (_a = props.groups) === null || _a === void 0 ? void 0 : _a.map((group, i) => {
22
17
  var _a;
23
18
  const scope = hashScope || "week";
24
19
  const groupHash = getHash(offset, group.id);
@@ -1,16 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import "./style.css";
3
3
  import { memo, useMemo } from "react";
4
- import useCalendarDateState from "../hooks/useCalendarDateState";
5
4
  import VirtualGroupRowDay from "./VirtualGroupRowDay";
6
5
  import DayContainer from "./DayContainer";
7
6
  import { useCalendarTaskContext } from "../contexts/CalendarTaskContext";
7
+ import useCalendarDateStateForDay from "../hooks/useCalendarDateStateForDay";
8
8
  function CalendarForDay(props) {
9
- const { dailyHours, weekDays } = useCalendarDateState(props.date, props.weekOffset, props.timeZone);
9
+ const { dailyHours, weekDays, weekOffset: currentWeekOffset } = useCalendarDateStateForDay(props.date, props.weekOffset, props.timeZone, props.dayOffset);
10
10
  const { getTasks, isValidTask, addTask, deleteTask, updateTask, getTask, hashScope, tasks } = useCalendarTaskContext();
11
- const currentDay = weekDays[props.dayOffset || 0];
12
- const memoizedHeader = useMemo(() => (currentDay ? (_jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: props.dayOffset || 0, dayRender: props.dayRender, day: currentDay.day, dayOfTheMonth: currentDay.dayOfTheMonth, dayMonth: currentDay.dayMonth, dayYear: currentDay.dayYear })) : null), [weekDays, props.rowsClassName, props.rowsStyle, props.groupsColsClassName, props.groupsColsStyle, props.groupHeadContainerClassName, props.groupHeadContainerStyle, props.groupsHeadRender, props.daysColsClassName, props.daysColsStyle, props.dayStyle, props.dayClassName, props.dayRender]);
13
- return (_jsxs("div", { className: `CalendarTableForDay ${props.className}`, style: Object.assign({ position: "relative" }, props.style), children: [memoizedHeader, _jsx("div", { className: `CalendarTableForDayTasksContainer`, children: props.groups.map((group, i) => (_jsx(VirtualGroupRowDay, { group: group, i: i, props: props, getTasks: getTasks, isValidTask: isValidTask, addTask: addTask, deleteTask: deleteTask, updateTask: updateTask, getTask: getTask, dailyHours: dailyHours, dayOffset: props.dayOffset || 0, hashScope: hashScope || "day", tasks: tasks }, `${group.id}-${i}`))) })] }));
11
+ const dayIndex = props.dayOffset !== undefined ? props.dayOffset : props.date.getDay();
12
+ const currentDay = weekDays[dayIndex];
13
+ const memoizedHeader = useMemo(() => (currentDay ? (_jsx(DayContainer, { style: props.dayStyle, className: props.dayClassName, dayIndex: dayIndex, dayRender: props.dayRender, day: currentDay.day, dayOfTheMonth: currentDay.dayOfTheMonth, dayMonth: currentDay.dayMonth, dayYear: currentDay.dayYear })) : null), [weekDays, dayIndex, props.dayStyle, props.dayClassName, props.dayRender]);
14
+ return (_jsxs("div", { className: `CalendarTableForDay ${props.className}`, style: Object.assign({ position: "relative" }, props.style), children: [memoizedHeader, _jsx("div", { className: `CalendarTableForDayTasksContainer`, children: props.groups.map((group, i) => (_jsx(VirtualGroupRowDay, { group: group, i: i, props: props, getTasks: getTasks, isValidTask: isValidTask, addTask: addTask, deleteTask: deleteTask, updateTask: updateTask, getTask: getTask, dailyHours: dailyHours, dayOffset: dayIndex, weekOffset: currentWeekOffset, hashScope: hashScope || "day", tasks: tasks }, `${group.id}-${i}`))) })] }));
14
15
  }
15
16
  export default memo(CalendarForDay, (prevProps, nextProps) => {
16
17
  var _a, _b;
@@ -4,17 +4,16 @@ import { useIntersectionObserver } from "../hooks/useIntersectionObserver";
4
4
  import GroupContainer from "./GroupContainer";
5
5
  import TaskVirtual from "./TaskContainer/TaskVirtual";
6
6
  import AddTask from "./AddTask";
7
- import { getHash, getNewTaskForDropOrPaste, getUniqueId, updateOffsetWithDateCalendar, } from "../lib/utils";
8
- const VirtualGroupRowDay = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, updateTask, getTask, dailyHours, dayOffset, hashScope, tasks, }) => {
7
+ import { getHash, getNewTaskForDropOrPaste, getUniqueId, } from "../lib/utils";
8
+ const VirtualGroupRowDay = ({ group, i, props, getTasks, isValidTask, addTask, deleteTask, updateTask, getTask, dailyHours, dayOffset, weekOffset, hashScope, tasks, }) => {
9
9
  const ref = useRef(null);
10
10
  const { entry, height } = useIntersectionObserver(ref, {
11
11
  rootMargin: "600px",
12
12
  threshold: 0,
13
13
  });
14
14
  const isVisible = !!(entry === null || entry === void 0 ? void 0 : entry.isIntersecting);
15
- const offset = useMemo(() => updateOffsetWithDateCalendar(props.date), [props.date]);
16
15
  const currentDailyHours = dailyHours[dayOffset];
17
- const hash = useMemo(() => getHash(offset, group.id, dayOffset), [offset, group.id, dayOffset]);
16
+ const hash = useMemo(() => getHash(weekOffset, group.id, dayOffset), [weekOffset, group.id, dayOffset]);
18
17
  const cellTasks = useMemo(() => getTasks(hash[hashScope]), [getTasks, hash, hashScope, tasks]);
19
18
  const handleDragOver = (event) => {
20
19
  event.preventDefault();
@@ -45,5 +44,6 @@ export default memo(VirtualGroupRowDay, (prev, next) => {
45
44
  prev.i === next.i &&
46
45
  prev.tasks === next.tasks &&
47
46
  prev.props.date.getTime() === next.props.date.getTime() &&
48
- prev.dayOffset === next.dayOffset);
47
+ prev.dayOffset === next.dayOffset &&
48
+ prev.weekOffset === next.weekOffset);
49
49
  });
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import "./style.css";
3
3
  import CalendarForWeek from "./CalendarForWeek";
4
- import CalendarForDay from "./CalendarForday";
5
4
  import CalendarTaskContextProvider from "../contexts/CalendarTaskContext";
6
5
  /**
7
6
  * Calendar component to display tasks and groups in a weekly view.
@@ -58,13 +57,12 @@ import CalendarTaskContextProvider from "../contexts/CalendarTaskContext";
58
57
  * @param {() => React.ReactNode} [props.sumHoursHeadRender] - Custom render function for the sum-of-hours header.
59
58
  * @param {(currentTask: TaskFeildsType) => void} [props.handleClickTask] - Handler function for clicking a task.
60
59
  * @param {(currentGroup: GroupFeildsType) => void} [props.handleClickGroup] - Handler function for clicking a group.
61
- * @param {0|1|2|3|4|5|6} [props.dayOffset] - Offset index for the day column (0 = first day of week, …, 6 = last day).
62
60
  * @param {React.CSSProperties} [props.dayColsStyle] - Additional styles for the day columns.
63
61
  * @param {string} [props.dayColsClassName] - Additional class names for the day columns.
64
62
  * @param {React.CSSProperties} [props.hoursColsStyle] - Additional styles for the hours columns.
65
63
  * @param {string} [props.hoursColsClassName] - Additional class names for the hours columns.
66
64
  */
67
65
  const Calendar = (props) => {
68
- return (_jsx(CalendarTaskContextProvider, { hashScope: "day", children: props.scope === "day" ? _jsx(CalendarForDay, Object.assign({}, props)) : _jsx(CalendarForWeek, Object.assign({}, props)) }));
66
+ return (_jsx(CalendarTaskContextProvider, { hashScope: "day", children: _jsx(CalendarForWeek, Object.assign({}, props)) }));
69
67
  };
70
68
  export default Calendar;
@@ -87,24 +87,66 @@
87
87
  .planningCalendar .dayTh,
88
88
  .planningCalendar .groupCol {
89
89
  flex: 0 0 150px;
90
+ min-width: 150px;
90
91
  color: #0f5173;
91
92
  padding-left: 5px;
92
93
  display: flex;
93
94
  align-items: center;
95
+ background-color: inherit;
94
96
  }
95
97
 
96
98
  .planningCalendar .dayCol {
97
99
  flex: 1;
100
+ min-width: 120px;
98
101
  border-left: 0.74px solid rgba(198, 219, 225, 0.68);
99
- min-width: 0;
100
102
  display: flex;
101
103
  flex-direction: column;
102
104
  justify-content: center;
103
105
  }
104
106
 
107
+ @media (max-width: 768px) {
108
+
109
+ .planningCalendar .dayTh,
110
+ .planningCalendar .groupCol {
111
+ flex: 0 0 100px;
112
+ min-width: 100px;
113
+ font-size: 13px;
114
+ position: sticky;
115
+ left: 0;
116
+ z-index: 50;
117
+ box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
118
+ background-color: #fff;
119
+ overflow: hidden;
120
+ white-space: nowrap;
121
+ text-overflow: ellipsis;
122
+ padding-left: 10px;
123
+ }
124
+
125
+ .planningCalendar .dayCol {
126
+ min-width: 120px;
127
+ }
128
+
129
+ .planningCalendarHeader {
130
+ z-index: 201;
131
+ /* Layer over the sticky row column */
132
+ }
133
+
134
+ /* Make sure the group container labels also truncate */
135
+ .groupCol label {
136
+ overflow: hidden;
137
+ text-overflow: ellipsis;
138
+ max-width: 100%;
139
+ }
140
+
141
+ .planningCalendar {
142
+ width: auto;
143
+ }
144
+ }
145
+
105
146
  .planningCalendar .totalTh,
106
147
  .planningCalendar .totalCol {
107
148
  flex: 0 0 50px;
149
+ min-width: 50px;
108
150
  color: #0f5173;
109
151
  text-align: right;
110
152
  padding-right: 5px;
@@ -152,18 +194,15 @@
152
194
  min-height: 80px;
153
195
  }
154
196
 
155
- .calendarForWeek,
156
- .calendarForDay {
197
+ .calendarForWeek {
157
198
  display: flex;
158
199
  width: 100%;
159
200
  flex: 1;
160
201
  overflow: auto;
161
202
  position: relative;
162
-
163
203
  }
164
204
 
165
- .calendarForWeek-container,
166
- .calendarForDay-container {
205
+ .calendarForWeek-container {
167
206
  display: flex;
168
207
  flex-direction: column;
169
208
  width: 100%;
@@ -0,0 +1,19 @@
1
+ import { useMemo } from "react";
2
+ import { calculateWeekDifference, getDateObjectInTimeZone, getDayHourly, getWeekDays, } from "../lib/utils";
3
+ function useCalendarDateStateForDay(date, weekOffset, timeZone, dayOffset) {
4
+ let calendarDateState = useMemo(() => {
5
+ const currentWeekOffset = (weekOffset !== undefined) ? weekOffset : (timeZone
6
+ ? calculateWeekDifference(getDateObjectInTimeZone(timeZone), timeZone)
7
+ : calculateWeekDifference(date, timeZone));
8
+ const weekDays = getWeekDays(currentWeekOffset, timeZone);
9
+ const dailyHours = getDayHourly(currentWeekOffset, timeZone);
10
+ const calData = {
11
+ dailyHours: dailyHours,
12
+ weekDays,
13
+ weekOffset: currentWeekOffset,
14
+ };
15
+ return calData;
16
+ }, [date, weekOffset, dayOffset, timeZone]);
17
+ return Object.assign({}, calendarDateState);
18
+ }
19
+ export default useCalendarDateStateForDay;
@@ -12,6 +12,7 @@ interface VirtualGroupRowDayProps {
12
12
  getTask: (hash: string, taskId: string) => TaskType | undefined;
13
13
  dailyHours: dayInfoType[];
14
14
  dayOffset: number;
15
+ weekOffset: number;
15
16
  hashScope: "week" | "group" | "day";
16
17
  tasks: TasksStore;
17
18
  }
@@ -55,7 +55,6 @@ import { CalendarPropsType } from "../definitions";
55
55
  * @param {() => React.ReactNode} [props.sumHoursHeadRender] - Custom render function for the sum-of-hours header.
56
56
  * @param {(currentTask: TaskFeildsType) => void} [props.handleClickTask] - Handler function for clicking a task.
57
57
  * @param {(currentGroup: GroupFeildsType) => void} [props.handleClickGroup] - Handler function for clicking a group.
58
- * @param {0|1|2|3|4|5|6} [props.dayOffset] - Offset index for the day column (0 = first day of week, …, 6 = last day).
59
58
  * @param {React.CSSProperties} [props.dayColsStyle] - Additional styles for the day columns.
60
59
  * @param {string} [props.dayColsClassName] - Additional class names for the day columns.
61
60
  * @param {React.CSSProperties} [props.hoursColsStyle] - Additional styles for the hours columns.
@@ -96,7 +96,6 @@ export type DayPropsType = {
96
96
  */
97
97
  export type CalendarPropsType = {
98
98
  drop?: "copy" | "move";
99
- scope?: "day" | "week";
100
99
  /** Offset for the week (e.g., -7 for last week, 0 for current week, 7 for next week). */
101
100
  weekOffset?: number;
102
101
  /** Array of group data to be displayed in the calendar. */
@@ -194,8 +193,6 @@ export type CalendarPropsType = {
194
193
  handleClickGroup?: (currentGroup: GroupFeildsType) => void;
195
194
  /** your timezones */
196
195
  timeZone?: TimeZone;
197
- /** day id */
198
- dayOffset?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
199
196
  /**day columns styles */
200
197
  dayColsStyle?: React.CSSProperties | undefined;
201
198
  /**day columns className*/
@@ -0,0 +1,17 @@
1
+ import { TimeZone } from "../definitions";
2
+ declare function useCalendarDateStateForDay(date: Date, weekOffset: number | undefined, timeZone: TimeZone | undefined, dayOffset: number | undefined): {
3
+ dailyHours: {
4
+ positionDay: number;
5
+ day: Date;
6
+ start: number;
7
+ end: number;
8
+ }[];
9
+ weekDays: {
10
+ day: string;
11
+ dayMonth: string;
12
+ dayYear: number;
13
+ dayOfTheMonth: number;
14
+ }[];
15
+ weekOffset: number;
16
+ };
17
+ export default useCalendarDateStateForDay;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-weekly-planning",
3
- "version": "1.0.44",
3
+ "version": "1.0.45",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/types/index.d.ts",
6
6
  "exports": {