pixel-react 1.10.5 → 1.10.7

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 (34) hide show
  1. package/lib/components/MenuOption/types.d.ts +1 -1
  2. package/lib/components/Select/components/types.d.ts +6 -1
  3. package/lib/components/Select/types.d.ts +17 -0
  4. package/lib/components/Table/Table.d.ts +1 -1
  5. package/lib/components/Table/Types.d.ts +1 -0
  6. package/lib/index.d.ts +20 -3
  7. package/lib/index.esm.js +245 -93
  8. package/lib/index.esm.js.map +1 -1
  9. package/lib/index.js +245 -93
  10. package/lib/index.js.map +1 -1
  11. package/lib/tsconfig.tsbuildinfo +1 -1
  12. package/lib/utils/getAnchorElement/getAnchorElement.d.ts +1 -0
  13. package/lib/utils/getTreeDetails/getTreeDetails.d.ts +1 -1
  14. package/package.json +1 -1
  15. package/src/components/MenuOption/MenuOption.scss +4 -0
  16. package/src/components/MenuOption/MenuOption.stories.tsx +2 -2
  17. package/src/components/MenuOption/MenuOption.tsx +72 -60
  18. package/src/components/MenuOption/types.ts +1 -1
  19. package/src/components/Select/Select.stories.tsx +43 -1
  20. package/src/components/Select/Select.tsx +16 -1
  21. package/src/components/Select/components/Dropdown.scss +41 -1
  22. package/src/components/Select/components/Dropdown.tsx +52 -6
  23. package/src/components/Select/components/types.ts +7 -2
  24. package/src/components/Select/types.ts +22 -0
  25. package/src/components/SequentialConnectingBranch/SequentialConnectingBranch.stories.tsx +8 -2
  26. package/src/components/SequentialConnectingBranch/components/Branches/Branches.tsx +1 -1
  27. package/src/components/SequentialConnectingBranch/components/DatasetTooltip/DataSetTooltip.scss +14 -0
  28. package/src/components/SequentialConnectingBranch/components/DatasetTooltip/DataSetTooltip.tsx +6 -11
  29. package/src/components/Table/Table.tsx +47 -0
  30. package/src/components/Table/Types.ts +4 -1
  31. package/src/components/TableTree/Components/TableBody.tsx +0 -1
  32. package/src/utils/getAnchorElement/getAnchorElement.ts +7 -0
  33. package/src/utils/getTreeDetails/getTreeDetails.stories.tsx +59 -9
  34. package/src/utils/getTreeDetails/getTreeDetails.ts +66 -13
@@ -6,12 +6,11 @@ import Typography from '../../Typography';
6
6
  import { ffid } from '../../../utils/ffID/ffid';
7
7
  import { ThemeContext } from '../../ThemeProvider/ThemeProvider';
8
8
  import classNames from 'classnames';
9
- import {
10
- getLabel,
11
- } from '../../../utils/getSelectOptionValue/getSelectOptionValue';
9
+ import { getLabel } from '../../../utils/getSelectOptionValue/getSelectOptionValue';
12
10
  import useClickOutside from '../../../hooks/useClickOutside';
13
11
  import Icon from '../../Icon';
14
12
  import Tooltip from '../../Tooltip';
13
+ import Button from '../../Button';
15
14
 
16
15
  const Dropdown: FC<DropdownProps> = ({
17
16
  options = [],
@@ -27,12 +26,22 @@ const Dropdown: FC<DropdownProps> = ({
27
26
  valueAccessor,
28
27
  showIcon = false,
29
28
  showToolTip = false,
29
+ customReccurenece = true,
30
+ onCancelModal = () => {},
31
+ onSaveModal = () => {},
32
+ recurrence = false,
33
+ modalJSXProps = <></>,
30
34
  }) => {
31
35
  const themeContext = useContext(ThemeContext);
32
36
  const currentTheme = themeContext?.currentTheme;
33
37
 
38
+ const customRecurrenceOnBlur = customReccurenece ? () => {} : onSelectBlur;
39
+
34
40
  const optionsWrapperRef = useRef<HTMLDivElement>(null);
35
- useClickOutside(optionsWrapperRef, onSelectBlur, [inputRef, selectArrowRef]);
41
+ useClickOutside(optionsWrapperRef, customRecurrenceOnBlur, [
42
+ inputRef,
43
+ selectArrowRef,
44
+ ]);
36
45
 
37
46
  const { positionX, positionY, fromBottom, width } = dropdownPosition;
38
47
  const { margin, optionHeight, selectInputHeight, dropDownWrapperPadding } =
@@ -98,13 +107,30 @@ const Dropdown: FC<DropdownProps> = ({
98
107
  );
99
108
  };
100
109
 
110
+ const onHandleCancelModal = () => {
111
+ onSelectBlur();
112
+ onCancelModal();
113
+ };
114
+
115
+ const onHandleSaveModal = () => {
116
+ onSelectBlur();
117
+ onSaveModal();
118
+ };
119
+
101
120
  return (
102
121
  <div
103
- className={classNames('ff-select-dropdown-wrapper', currentTheme)}
122
+ className={classNames('ff-select-dropdown-wrapper', currentTheme, {
123
+ 'ff-select-dropdown-modal-wrapper': recurrence,
124
+ 'ff-select-dropdown-mini-modal-wrapper': customReccurenece,
125
+ })}
104
126
  ref={optionsWrapperRef}
105
127
  style={updateDropdownPosition()}
106
128
  >
107
- <div>
129
+ <div
130
+ className={classNames({
131
+ 'ff-select-label-minimodal-wrapper': customReccurenece,
132
+ })}
133
+ >
108
134
  {!checkEmpty(options) ? (
109
135
  options.map((option) => (
110
136
  <div
@@ -140,6 +166,26 @@ const Dropdown: FC<DropdownProps> = ({
140
166
  </Typography>
141
167
  )}
142
168
  </div>
169
+
170
+ {customReccurenece && (
171
+ <div className="ff-select-mini-modal-wrapper" id="ff-select-mini-id">
172
+ <div className="ff-select-modal-wrapper">
173
+ {<>{modalJSXProps}</>}
174
+ <div className="ff-select-mini-modal-footer">
175
+ <Button
176
+ label="Cancel"
177
+ variant="tertiary"
178
+ onClick={onHandleCancelModal}
179
+ />
180
+ <Button
181
+ label="Save"
182
+ variant="secondary"
183
+ onClick={onHandleSaveModal}
184
+ />
185
+ </div>
186
+ </div>
187
+ </div>
188
+ )}
143
189
  </div>
144
190
  );
145
191
  };
@@ -1,5 +1,5 @@
1
1
  import { DropdownPosition, Option } from '../types';
2
- import { RefObject } from 'react';
2
+ import { ReactNode, RefObject } from 'react';
3
3
 
4
4
  export interface DropdownProps {
5
5
  options: Option[];
@@ -14,7 +14,12 @@ export interface DropdownProps {
14
14
  heightFromTop: number;
15
15
  selectedOption?: Option;
16
16
  showIcon?: boolean;
17
- showToolTip?: boolean
17
+ showToolTip?: boolean;
18
+ customReccurenece?: boolean;
19
+ onCancelModal?: () => void;
20
+ onSaveModal?: () => void;
21
+ modalJSXProps?: ReactNode;
22
+ recurrence?: boolean;
18
23
  }
19
24
 
20
25
  export const dropdownDefaultCSSData = {
@@ -1,3 +1,5 @@
1
+ import { ReactNode } from 'react';
2
+
1
3
  export interface SelectProps {
2
4
  /*
3
5
  * Label for the select dropdown
@@ -123,6 +125,26 @@ export interface SelectProps {
123
125
  * Provide the background color for the select label on the hover state
124
126
  */
125
127
  labelBackgroundColor?: string;
128
+
129
+ /**
130
+ * To close the modal in the select dropdown
131
+ */
132
+ onCancelModal?: () => void;
133
+
134
+ /**
135
+ * To close the modal in the select dropdown
136
+ */
137
+ onSaveModal?: () => void;
138
+
139
+ /**
140
+ * Pass the custom Jsx for the Modal
141
+ */
142
+ modalJSXProps?: ReactNode;
143
+
144
+ /**
145
+ * Pass the recurrence boolean for the exeception cases
146
+ */
147
+ recurrence?: boolean;
126
148
  }
127
149
 
128
150
  export interface DropdownPosition {
@@ -21,6 +21,7 @@ export const Primary: Story = {
21
21
  label: 'ffe-Mahesh',
22
22
  value: {
23
23
  status: 'running',
24
+ clientId: 'test',
24
25
  },
25
26
  name: 'ffe-Mahesh',
26
27
  });
@@ -33,6 +34,7 @@ export const Primary: Story = {
33
34
  status: 'running',
34
35
  },
35
36
  name: 'ffe-Ganesh',
37
+ clientId: 'test',
36
38
  },
37
39
  {
38
40
  label: 'ffe-Mahesh',
@@ -40,6 +42,7 @@ export const Primary: Story = {
40
42
  status: 'running',
41
43
  },
42
44
  name: 'ffe-Mahesh',
45
+ clientId: 'test',
43
46
  },
44
47
  ];
45
48
 
@@ -52,8 +55,10 @@ export const Primary: Story = {
52
55
  return (
53
56
  <>
54
57
  <SequentialConnectingBranch
55
- projectType="web"
56
58
  dataSetValues={{
59
+ globalVariableSetName: 'test dev',
60
+ peVariableSetName: 'test dev',
61
+ testDataSetName: 'test dev',
57
62
  peVariableSetId: 'Test dev',
58
63
  globalVariableSetId: 'Test dev',
59
64
  testDataSetId: 'Test dev',
@@ -64,7 +69,8 @@ export const Primary: Story = {
64
69
  machineInstances={[]}
65
70
  selectedMachine={machineSelect}
66
71
  onHandleSelect={onMachineHandleSelect}
67
- scriptType="Manual"
72
+ // scriptType="Manual"
73
+ readOnly={true}
68
74
  />
69
75
  </>
70
76
  );
@@ -208,7 +208,7 @@ const Branches = ({
208
208
  }
209
209
  />
210
210
  </div>
211
- {!readOnly && (
211
+ {readOnly && (
212
212
  <div
213
213
  className={classNames({
214
214
  'ff-connecting-branch-datalist': evenRow,
@@ -0,0 +1,14 @@
1
+ @mixin flex-column {
2
+ display: flex;
3
+ flex-direction: column;
4
+ }
5
+
6
+ .dataSetList_container {
7
+ @include flex-column;
8
+ gap: 4px;
9
+ padding: 6px 12px;
10
+
11
+ .dataSetList_items {
12
+ @include flex-column;
13
+ }
14
+ }
@@ -1,5 +1,4 @@
1
1
  import { FC } from 'react';
2
- import Tooltip from '../../../Tooltip';
3
2
  import Typography from '../../../Typography';
4
3
  import { DataSetTooltipProps } from './types';
5
4
 
@@ -18,22 +17,18 @@ const DataSetTooltip: FC<DataSetTooltipProps> = ({
18
17
 
19
18
  return (
20
19
  <>
21
- <Tooltip placement="bottom">
22
- {Object.entries(toolTipTitleValue).map(([key, value], index) => (
23
- <div key={index}>
24
- <Typography
25
- as="div"
26
- fontSize={10}
27
- lineHeight="15px"
28
- >
20
+ {Object.entries(toolTipTitleValue).map(([key, value], index) => (
21
+ <div key={index} className='' >
22
+ <div className='' >
23
+ <Typography as="div" fontSize={10} lineHeight="15px">
29
24
  {key}
30
25
  </Typography>
31
26
  <Typography as="div" lineHeight="18px">
32
27
  {value}
33
28
  </Typography>
34
29
  </div>
35
- ))}
36
- </Tooltip>
30
+ </div>
31
+ ))}
37
32
  </>
38
33
  );
39
34
  };
@@ -18,6 +18,7 @@ import {
18
18
  verticalListSortingStrategy,
19
19
  } from '@dnd-kit/sortable';
20
20
  import { CSS } from '@dnd-kit/utilities';
21
+ import { useEffect, useRef } from 'react';
21
22
 
22
23
  const SortableRow = ({
23
24
  row,
@@ -115,7 +116,49 @@ const Table = ({
115
116
  headerIconOnClick = () => {},
116
117
  draggable = false,
117
118
  onDragEnd,
119
+ loadMore = () => {},
118
120
  }: TableProps) => {
121
+
122
+ const observerRef = useRef<IntersectionObserver | null>(null);
123
+
124
+ useEffect(() => {
125
+ const scrollContainer = document.getElementById(
126
+ 'ff-table-scroll-container'
127
+ );
128
+ const firstNode = document.getElementById('ff-table-first-node');
129
+ const lastNode = document.getElementById('ff-table-last-node');
130
+
131
+ // Exit early if data is empty or elements are missing
132
+ if (!scrollContainer || !firstNode || !lastNode || !data?.length) {
133
+ return;
134
+ }
135
+
136
+ observerRef.current = new IntersectionObserver(
137
+ (entries) => {
138
+ entries.forEach((entry) => {
139
+ if (entry.isIntersecting) {
140
+ const direction =
141
+ entry.target.id === 'ff-table-last-node' ? 'below' : 'above';
142
+ loadMore(direction);
143
+ }
144
+ });
145
+ },
146
+ {
147
+ root: scrollContainer,
148
+ rootMargin: '8px',
149
+ threshold: 0.1,
150
+ }
151
+ );
152
+
153
+ observerRef.current.observe(firstNode);
154
+ observerRef.current.observe(lastNode);
155
+
156
+ return () => {
157
+ // Cleanup observer
158
+ observerRef.current?.disconnect();
159
+ };
160
+ }, [data, loadMore]);
161
+
119
162
  const handleOnclick = (column: ColumnsProps, row: DataProps) => {
120
163
  let { onClick, accessor } = column;
121
164
  if (onClick && isFunction(onClick)) {
@@ -153,6 +196,7 @@ const Table = ({
153
196
  >
154
197
  <div
155
198
  style={{ height: height, position: 'relative' }}
199
+ id="ff-table-scroll-container"
156
200
  className={classNames(className, {
157
201
  'ff-fixed-header-table': withFixedHeader,
158
202
  'border-borderRadius': borderWithRadius,
@@ -216,6 +260,7 @@ const Table = ({
216
260
  </tr>
217
261
  </thead>
218
262
  <tbody className="ff-fixed-header-table">
263
+ <tr id="ff-table-first-node" />
219
264
  {data?.length > 0 &&
220
265
  data?.map((row: any) => (
221
266
  <SortableRow
@@ -230,6 +275,8 @@ const Table = ({
230
275
  draggable={draggable}
231
276
  />
232
277
  ))}
278
+ <tr id="ff-table-last-node" />
279
+
233
280
  </tbody>
234
281
  </table>
235
282
  {data?.length <= 0 && (
@@ -128,5 +128,8 @@ export interface TableProps {
128
128
  /**
129
129
  * Drag and Drop indexes
130
130
  */
131
- onDragEnd?: (startIndex: number, endIndex: number) => void | undefined
131
+ onDragEnd?: (startIndex: number, endIndex: number) => void | undefined,
132
+
133
+ loadMore?: (_direction?: string) => void;
134
+
132
135
  }
@@ -33,7 +33,6 @@ const TableBody = ({
33
33
  onRowClick={onRowClick}
34
34
  onToggleExpand={(node) => onToggleExpand(node, index)}
35
35
  onCheckBoxChange={onCheckBoxChange}
36
- index={index}
37
36
  onAddConfirm={onAddConfirm}
38
37
  onAddCancel={onAddCancel}
39
38
  />
@@ -0,0 +1,7 @@
1
+ // Helper function to get the anchor element
2
+ export const getAnchorElement = (anchorRef: React.RefObject<HTMLElement> | string) => {
3
+ if (typeof anchorRef === 'string') {
4
+ return document.getElementById(anchorRef);
5
+ }
6
+ return anchorRef?.current || null;
7
+ };
@@ -1,13 +1,14 @@
1
1
  import { useState } from 'react';
2
- import { getTreeDetails, TreeNode, TreeDetailsResult } from './getTreeDetails';
2
+ import { getTreeDetails, TreeDetailsResult } from './getTreeDetails';
3
3
  import React from 'react';
4
+ import { TreeNodeProps } from '../../ComponentProps/TreeNodeProps';
4
5
 
5
6
  export default {
6
7
  title: 'Utils/getTreeDetails',
7
8
  component: getTreeDetails,
8
9
  };
9
10
 
10
- const initialData: TreeNode[] = [
11
+ const initialData: TreeNodeProps[] = [
11
12
  {
12
13
  createdBy: 'USR4705',
13
14
  modifiedBy: '--',
@@ -65,15 +66,31 @@ const initialData: TreeNode[] = [
65
66
  ];
66
67
 
67
68
  export const InteractivePlayground = () => {
68
- const [data, setData] = useState<TreeNode[]>(initialData);
69
- const [newData, setNewData] = useState<TreeNode[]>([]);
70
- const [action, setAction] = useState<'above' | 'below' | 'expand' | 'collapse' | 'start'>('below');
69
+ const [data, setData] = useState<TreeNodeProps[]>(initialData);
70
+ const [newData, setNewData] = useState<TreeNodeProps[]>([]);
71
+ const [action, setAction] = useState<
72
+ | 'above'
73
+ | 'below'
74
+ | 'expand'
75
+ | 'collapse'
76
+ | 'start'
77
+ | 'addAbove'
78
+ | 'addBelow'
79
+ >('below');
71
80
  const [index, setIndex] = useState<number | undefined>(undefined);
81
+ const [sourceId, setSourceId] = useState<string | undefined>(undefined); // State for sourceId
72
82
  const [result, setResult] = useState<TreeDetailsResult | null>(null);
73
83
 
74
84
  const handleGetTreeDetails = () => {
75
85
  try {
76
- const treeDetails = getTreeDetails(action, data, newData, index);
86
+ // If sourceId exists, we pass it, otherwise we pass the index
87
+ const treeDetails = getTreeDetails(
88
+ action,
89
+ data,
90
+ newData,
91
+ index,
92
+ sourceId
93
+ );
77
94
  setData(treeDetails.treeDataList);
78
95
  setResult(treeDetails);
79
96
  } catch (error) {
@@ -91,7 +108,16 @@ export const InteractivePlayground = () => {
91
108
  id="action"
92
109
  value={action}
93
110
  onChange={(e) =>
94
- setAction(e.target.value as 'above' | 'below' | 'expand' | 'collapse' | 'start')
111
+ setAction(
112
+ e.target.value as
113
+ | 'above'
114
+ | 'below'
115
+ | 'expand'
116
+ | 'collapse'
117
+ | 'start'
118
+ | 'addAbove'
119
+ | 'addBelow'
120
+ )
95
121
  }
96
122
  >
97
123
  <option value="above">Above</option>
@@ -99,10 +125,32 @@ export const InteractivePlayground = () => {
99
125
  <option value="expand">Expand</option>
100
126
  <option value="collapse">Collapse</option>
101
127
  <option value="start">Start</option>
128
+ <option value="addAbove">Add Above</option>
129
+ <option value="addBelow">Add Below</option>
102
130
  </select>
103
131
  </div>
104
132
 
105
- {(action === 'expand' || action === 'collapse') && (
133
+ {/* Input for sourceId, if action requires it */}
134
+ {(action === 'expand' ||
135
+ action === 'collapse' ||
136
+ action === 'addAbove' ||
137
+ action === 'addBelow') && (
138
+ <div>
139
+ <label htmlFor="sourceId">
140
+ Source ID (for expand/collapse/addAbove/addBelow):
141
+ </label>
142
+ <input
143
+ type="text"
144
+ id="sourceId"
145
+ value={sourceId || ''}
146
+ onChange={(e) => setSourceId(e.target.value)}
147
+ placeholder="Enter Source ID"
148
+ />
149
+ </div>
150
+ )}
151
+
152
+ {/* Input for index, for actions requiring index */}
153
+ {(action === 'expand' || action === 'collapse') && !sourceId && (
106
154
  <div>
107
155
  <label htmlFor="index">Index (for expand/collapse):</label>
108
156
  <input
@@ -110,7 +158,9 @@ export const InteractivePlayground = () => {
110
158
  id="index"
111
159
  value={index !== undefined ? index : ''}
112
160
  onChange={(e) =>
113
- setIndex(e.target.value ? parseInt(e.target.value, 10) : undefined)
161
+ setIndex(
162
+ e.target.value ? parseInt(e.target.value, 10) : undefined
163
+ )
114
164
  }
115
165
  disabled={action !== 'expand' && action !== 'collapse'}
116
166
  />
@@ -1,4 +1,5 @@
1
1
  import { TreeNodeProps as TreeNode } from '../../ComponentProps/TreeNodeProps';
2
+ import { checkEmpty } from '../checkEmpty/checkEmpty';
2
3
 
3
4
  export interface TreeDetailsResult {
4
5
  treeDataList: TreeNode[];
@@ -10,14 +11,37 @@ export interface TreeDetailsResult {
10
11
  }
11
12
 
12
13
  export const getTreeDetails = (
13
- action: 'above' | 'below' | 'expand' | 'collapse' | 'start',
14
+ action:
15
+ | 'above'
16
+ | 'below'
17
+ | 'expand'
18
+ | 'collapse'
19
+ | 'start'
20
+ | 'addAbove'
21
+ | 'addBelow',
14
22
  oldData: TreeNode[],
15
23
  newData: TreeNode[],
16
- index?: number
24
+ index?: number,
25
+ sourceId?: string
17
26
  ): TreeDetailsResult => {
18
27
  let treeDataList: TreeNode[];
19
28
  let root: TreeNode | undefined = undefined;
20
29
 
30
+ const findIndexByKey = (data: TreeNode[], key: string): number => {
31
+ return data.findIndex((node) => node.key === key);
32
+ };
33
+
34
+ const getIndex = (): number | undefined => {
35
+ if (sourceId) {
36
+ const indexByKey = findIndexByKey(oldData, sourceId);
37
+ if (indexByKey === -1) {
38
+ throw new Error(`Key "${sourceId}" not found in oldData.`);
39
+ }
40
+ return indexByKey;
41
+ }
42
+ return index; // Use index if sourceId is not provided
43
+ };
44
+
21
45
  switch (action) {
22
46
  case 'above':
23
47
  treeDataList = [...newData, ...oldData].slice(0, 40);
@@ -27,16 +51,42 @@ export const getTreeDetails = (
27
51
  break;
28
52
  case 'expand':
29
53
  case 'collapse':
30
- if (typeof index === 'number') {
31
- treeDataList = [...oldData.slice(0, index), ...newData];
32
- } else {
54
+ const actionIndex = getIndex();
55
+ if (actionIndex === undefined) {
33
56
  throw new Error(
34
- "Index is required for 'expand' or 'collapse' actions."
57
+ "Both sourceId and index are required for 'expand' or 'collapse' actions."
35
58
  );
36
59
  }
60
+ treeDataList = [...oldData.slice(0, actionIndex), ...newData];
61
+ break;
62
+ case 'addAbove':
63
+ const addAboveIndex = getIndex();
64
+ if (addAboveIndex === undefined) {
65
+ throw new Error(
66
+ "Both sourceId and index are required for 'addAbove' action."
67
+ );
68
+ }
69
+ treeDataList = [
70
+ ...oldData.slice(0, addAboveIndex),
71
+ ...newData,
72
+ ...oldData.slice(addAboveIndex),
73
+ ];
74
+ break;
75
+ case 'addBelow':
76
+ const addBelowIndex = getIndex();
77
+ if (addBelowIndex === undefined) {
78
+ throw new Error(
79
+ "Both sourceId and index are required for 'addBelow' action."
80
+ );
81
+ }
82
+ treeDataList = [
83
+ ...oldData.slice(0, addBelowIndex + 1),
84
+ ...newData,
85
+ ...oldData.slice(addBelowIndex + 1),
86
+ ];
37
87
  break;
38
88
  case 'start':
39
- if (newData.length > 0) {
89
+ if (!checkEmpty(newData)) {
40
90
  root = newData[0];
41
91
  treeDataList = newData.slice(1);
42
92
  } else {
@@ -47,19 +97,22 @@ export const getTreeDetails = (
47
97
  throw new Error(`Invalid action: ${action}`);
48
98
  }
49
99
 
50
- if (treeDataList.length === 0 && action !== 'start') {
100
+ if (checkEmpty(treeDataList) && action !== 'start') {
51
101
  throw new Error('Tree data list is empty.');
52
102
  }
53
103
 
54
104
  const firstNode = treeDataList[0] || root!;
55
- const lastNode = treeDataList[treeDataList.length - 1]!;
105
+ const lastNode = treeDataList[treeDataList.length - 1] || {
106
+ lastResource: true,
107
+ key: '',
108
+ };
56
109
 
57
110
  return {
58
111
  treeDataList,
59
- next: !lastNode.lastResource,
60
- previous: !firstNode.lastResource,
61
- startId: firstNode.key,
62
- endId: lastNode.key,
112
+ next: !lastNode?.lastResource,
113
+ previous: !firstNode?.lastResource,
114
+ startId: firstNode?.key,
115
+ endId: lastNode?.key,
63
116
  root,
64
117
  };
65
118
  };