@map-colonies/react-components 3.8.1 → 3.10.2

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 (231) hide show
  1. package/.env +1 -0
  2. package/.storybook/main.js +2 -5
  3. package/CHANGELOG.md +54 -0
  4. package/dist/assets/img/dragIcon.png +0 -0
  5. package/dist/assets/img/dragIconLight.png +0 -0
  6. package/dist/assets/img/glyphicons_067_cleaning.png +0 -0
  7. package/dist/assets/img/glyphicons_094_vector_path_square.png +0 -0
  8. package/dist/assets/img/glyphicons_095_vector_path_circle.png +0 -0
  9. package/dist/assets/img/glyphicons_096_vector_path_polygon.png +0 -0
  10. package/dist/assets/img/glyphicons_097_vector_path_line.png +0 -0
  11. package/dist/assets/img/glyphicons_242_google_maps.png +0 -0
  12. package/dist/assets/img/map-marker.gif +0 -0
  13. package/dist/autocomplete/autocomplete.css +25 -0
  14. package/dist/autocomplete/autocomplete.d.ts +33 -0
  15. package/dist/autocomplete/autocomplete.js +480 -0
  16. package/dist/autocomplete/index.d.ts +1 -0
  17. package/dist/autocomplete/index.js +5 -0
  18. package/dist/box/box.d.ts +3 -0
  19. package/dist/box/box.js +35 -0
  20. package/dist/box/index.d.ts +1 -0
  21. package/dist/box/index.js +5 -0
  22. package/dist/cesium-map/data-sources/custom.data-source.d.ts +5 -0
  23. package/dist/cesium-map/data-sources/custom.data-source.js +23 -0
  24. package/dist/cesium-map/data-sources/drawings.data-source.d.ts +34 -0
  25. package/dist/cesium-map/data-sources/drawings.data-source.js +187 -0
  26. package/dist/cesium-map/data-sources/index.d.ts +2 -0
  27. package/dist/cesium-map/data-sources/index.js +14 -0
  28. package/dist/cesium-map/entities/entity.d.ts +5 -0
  29. package/dist/cesium-map/entities/entity.description.d.ts +6 -0
  30. package/dist/cesium-map/entities/entity.description.js +27 -0
  31. package/dist/cesium-map/entities/entity.js +23 -0
  32. package/dist/cesium-map/entities/graphics/polygon.graphics.d.ts +5 -0
  33. package/dist/cesium-map/entities/graphics/polygon.graphics.js +23 -0
  34. package/dist/cesium-map/entities/graphics/polyline.graphics.d.ts +5 -0
  35. package/dist/cesium-map/entities/graphics/polyline.graphics.js +23 -0
  36. package/dist/cesium-map/entities/graphics/rectangle.graphics.d.ts +5 -0
  37. package/dist/cesium-map/entities/graphics/rectangle.graphics.js +23 -0
  38. package/dist/cesium-map/entities/index.d.ts +4 -0
  39. package/dist/cesium-map/entities/index.js +16 -0
  40. package/dist/cesium-map/index.d.ts +7 -0
  41. package/dist/cesium-map/index.js +19 -0
  42. package/dist/cesium-map/layers/3d.tileset.d.ts +7 -0
  43. package/dist/cesium-map/layers/3d.tileset.js +39 -0
  44. package/dist/cesium-map/layers/3d.tileset.update.d.ts +1 -0
  45. package/dist/cesium-map/layers/3d.tileset.update.js +5 -0
  46. package/dist/cesium-map/layers/geojson.layer.d.ts +5 -0
  47. package/dist/cesium-map/layers/geojson.layer.js +23 -0
  48. package/dist/cesium-map/layers/imagery.layer.d.ts +6 -0
  49. package/dist/cesium-map/layers/imagery.layer.js +64 -0
  50. package/dist/cesium-map/layers/index.d.ts +7 -0
  51. package/dist/cesium-map/layers/index.js +19 -0
  52. package/dist/cesium-map/layers/osm.layer.d.ts +9 -0
  53. package/dist/cesium-map/layers/osm.layer.js +36 -0
  54. package/dist/cesium-map/layers/wms.layer.d.ts +9 -0
  55. package/dist/cesium-map/layers/wms.layer.js +36 -0
  56. package/dist/cesium-map/layers/wmts.layer.d.ts +9 -0
  57. package/dist/cesium-map/layers/wmts.layer.js +36 -0
  58. package/dist/cesium-map/layers/xyz.layer.d.ts +9 -0
  59. package/dist/cesium-map/layers/xyz.layer.js +36 -0
  60. package/dist/cesium-map/layers-manager.d.ts +47 -0
  61. package/dist/cesium-map/layers-manager.js +228 -0
  62. package/dist/cesium-map/map.css +54 -0
  63. package/dist/cesium-map/map.d.ts +46 -0
  64. package/dist/cesium-map/map.js +273 -0
  65. package/dist/cesium-map/map.types.d.ts +8 -0
  66. package/dist/cesium-map/map.types.js +12 -0
  67. package/dist/cesium-map/proxied.types.d.ts +19 -0
  68. package/dist/cesium-map/proxied.types.js +89 -0
  69. package/dist/cesium-map/settings/base-maps.css +37 -0
  70. package/dist/cesium-map/settings/base-maps.d.ts +7 -0
  71. package/dist/cesium-map/settings/base-maps.js +78 -0
  72. package/dist/cesium-map/settings/scene-modes.css +19 -0
  73. package/dist/cesium-map/settings/scene-modes.d.ts +7 -0
  74. package/dist/cesium-map/settings/scene-modes.js +65 -0
  75. package/dist/cesium-map/settings/settings.css +49 -0
  76. package/dist/cesium-map/settings/settings.d.ts +23 -0
  77. package/dist/cesium-map/settings/settings.js +79 -0
  78. package/dist/cesium-map/terrain-providers/custom/dummy-quantized-mesh-tile.d.ts +3 -0
  79. package/dist/cesium-map/terrain-providers/custom/dummy-quantized-mesh-tile.js +245 -0
  80. package/dist/cesium-map/terrain-providers/custom/quantized-mesh-decoder.d.ts +9 -0
  81. package/dist/cesium-map/terrain-providers/custom/quantized-mesh-decoder.js +202 -0
  82. package/dist/cesium-map/terrain-providers/custom/quantized-mesh-terrain-provider.d.ts +50 -0
  83. package/dist/cesium-map/terrain-providers/custom/quantized-mesh-terrain-provider.js +136 -0
  84. package/dist/cesium-map/tools/cesium/primitives-conversions.cesium.d.ts +2 -0
  85. package/dist/cesium-map/tools/cesium/primitives-conversions.cesium.js +38 -0
  86. package/dist/cesium-map/tools/coordinates-tracker.tool.css +11 -0
  87. package/dist/cesium-map/tools/coordinates-tracker.tool.d.ts +7 -0
  88. package/dist/cesium-map/tools/coordinates-tracker.tool.js +78 -0
  89. package/dist/cesium-map/tools/draw/drawHelper.css +101 -0
  90. package/dist/cesium-map/tools/draw/drawHelper.d.ts +28 -0
  91. package/dist/cesium-map/tools/draw/drawHelper.js +1694 -0
  92. package/dist/cesium-map/tools/geojson/geojson-to-primitive.d.ts +4 -0
  93. package/dist/cesium-map/tools/geojson/geojson-to-primitive.js +41 -0
  94. package/dist/cesium-map/tools/geojson/index.d.ts +2 -0
  95. package/dist/cesium-map/tools/geojson/index.js +14 -0
  96. package/dist/cesium-map/tools/geojson/point.geojson.d.ts +3 -0
  97. package/dist/cesium-map/tools/geojson/point.geojson.js +21 -0
  98. package/dist/cesium-map/tools/geojson/polygon.geojson.d.ts +3 -0
  99. package/dist/cesium-map/tools/geojson/polygon.geojson.js +24 -0
  100. package/dist/cesium-map/tools/geojson/rectangle.geojson.d.ts +3 -0
  101. package/dist/cesium-map/tools/geojson/rectangle.geojson.js +44 -0
  102. package/dist/cesium-map/tools/inspector.tool.d.ts +4 -0
  103. package/dist/cesium-map/tools/inspector.tool.js +33 -0
  104. package/dist/cesium-map/tools/scale-tracker.tool.css +16 -0
  105. package/dist/cesium-map/tools/scale-tracker.tool.d.ts +8 -0
  106. package/dist/cesium-map/tools/scale-tracker.tool.js +158 -0
  107. package/dist/cesium-map/tools/terranian-height.tool.d.ts +4 -0
  108. package/dist/cesium-map/tools/terranian-height.tool.js +113 -0
  109. package/dist/cssbaseline/cssbaseline.d.ts +5 -0
  110. package/dist/cssbaseline/cssbaseline.js +41 -0
  111. package/dist/cssbaseline/index.d.ts +1 -0
  112. package/dist/cssbaseline/index.js +6 -0
  113. package/dist/date-picker/date-picker.css +9 -0
  114. package/dist/date-picker/date-picker.d.ts +14 -0
  115. package/dist/date-picker/date-picker.js +78 -0
  116. package/dist/date-picker/index.d.ts +1 -0
  117. package/dist/date-picker/index.js +13 -0
  118. package/dist/date-range-picker/date-range-picker.css +9 -0
  119. package/dist/date-range-picker/date-range-picker.d.ts +26 -0
  120. package/dist/date-range-picker/date-range-picker.form-control.css +3 -0
  121. package/dist/date-range-picker/date-range-picker.form-control.d.ts +28 -0
  122. package/dist/date-range-picker/date-range-picker.form-control.js +95 -0
  123. package/dist/date-range-picker/date-range-picker.js +104 -0
  124. package/dist/date-range-picker/index.d.ts +2 -0
  125. package/dist/date-range-picker/index.js +14 -0
  126. package/dist/file-picker/file-picker.css +62 -0
  127. package/dist/file-picker/file-picker.d.ts +276 -0
  128. package/dist/file-picker/file-picker.js +151 -0
  129. package/dist/file-picker/fs-map.json +1557 -0
  130. package/dist/file-picker/index.d.ts +2 -0
  131. package/dist/file-picker/index.js +14 -0
  132. package/dist/file-picker/localization.d.ts +11 -0
  133. package/dist/file-picker/localization.js +124 -0
  134. package/dist/index.d.ts +13 -0
  135. package/dist/index.js +23 -16
  136. package/dist/map-filter-container/container-map.css +5 -0
  137. package/dist/map-filter-container/container-map.d.ts +11 -0
  138. package/dist/map-filter-container/container-map.js +31 -0
  139. package/dist/map-filter-container/index.d.ts +1 -0
  140. package/dist/map-filter-container/index.js +13 -0
  141. package/dist/map-filter-container/map-filter-container.d.ts +9 -0
  142. package/dist/map-filter-container/map-filter-container.js +78 -0
  143. package/dist/map-filter-container/polygon-selection-ui.d.ts +12 -0
  144. package/dist/map-filter-container/polygon-selection-ui.js +62 -0
  145. package/dist/models/defaults.d.ts +28 -0
  146. package/dist/models/defaults.js +32 -0
  147. package/dist/models/enums.d.ts +14 -0
  148. package/dist/models/enums.js +20 -0
  149. package/dist/models/index.d.ts +1 -0
  150. package/dist/models/index.js +13 -0
  151. package/dist/ol-map/feature.d.ts +6 -0
  152. package/dist/ol-map/feature.js +20 -0
  153. package/dist/ol-map/index.d.ts +6 -0
  154. package/dist/ol-map/index.js +18 -0
  155. package/dist/ol-map/interactions/draw.d.ts +8 -0
  156. package/dist/ol-map/interactions/draw.js +44 -0
  157. package/dist/ol-map/interactions/index.d.ts +1 -0
  158. package/dist/ol-map/interactions/index.js +13 -0
  159. package/dist/ol-map/layers/index.d.ts +3 -0
  160. package/dist/ol-map/layers/index.js +15 -0
  161. package/dist/ol-map/layers/tile-layer.d.ts +9 -0
  162. package/dist/ol-map/layers/tile-layer.js +48 -0
  163. package/dist/ol-map/layers/vector-layer.d.ts +4 -0
  164. package/dist/ol-map/layers/vector-layer.js +48 -0
  165. package/dist/ol-map/layers/vector-tile-layer.d.ts +10 -0
  166. package/dist/ol-map/layers/vector-tile-layer.js +66 -0
  167. package/dist/ol-map/map.css +17 -0
  168. package/dist/ol-map/map.d.ts +14 -0
  169. package/dist/ol-map/map.js +117 -0
  170. package/dist/ol-map/source/index.d.ts +6 -0
  171. package/dist/ol-map/source/index.js +18 -0
  172. package/dist/ol-map/source/mvt.d.ts +11 -0
  173. package/dist/ol-map/source/mvt.js +37 -0
  174. package/dist/ol-map/source/osm.d.ts +2 -0
  175. package/dist/ol-map/source/osm.js +14 -0
  176. package/dist/ol-map/source/vector-source.d.ts +4 -0
  177. package/dist/ol-map/source/vector-source.js +45 -0
  178. package/dist/ol-map/source/wms.d.ts +17 -0
  179. package/dist/ol-map/source/wms.js +30 -0
  180. package/dist/ol-map/source/wmts.d.ts +21 -0
  181. package/dist/ol-map/source/wmts.js +59 -0
  182. package/dist/ol-map/source/xyz.d.ts +12 -0
  183. package/dist/ol-map/source/xyz.js +27 -0
  184. package/dist/ol-map/style.d.ts +4 -0
  185. package/dist/ol-map/style.js +22 -0
  186. package/dist/popover/index.d.ts +1 -0
  187. package/dist/popover/index.js +5 -0
  188. package/dist/popover/popover.d.ts +3 -0
  189. package/dist/popover/popover.js +35 -0
  190. package/dist/smart-table/__mock-data__/smartTableMocks.d.ts +7 -0
  191. package/dist/smart-table/__mock-data__/smartTableMocks.js +17 -0
  192. package/dist/smart-table/index.d.ts +2 -0
  193. package/dist/smart-table/index.js +14 -0
  194. package/dist/smart-table/smart-table-head.d.ts +11 -0
  195. package/dist/smart-table/smart-table-head.js +22 -0
  196. package/dist/smart-table/smart-table-row.d.ts +12 -0
  197. package/dist/smart-table/smart-table-row.js +46 -0
  198. package/dist/smart-table/smart-table-types.d.ts +9 -0
  199. package/dist/smart-table/smart-table-types.js +2 -0
  200. package/dist/smart-table/smart-table.d.ts +17 -0
  201. package/dist/smart-table/smart-table.js +51 -0
  202. package/dist/theme/index.d.ts +1 -0
  203. package/dist/theme/index.js +13 -0
  204. package/dist/theme/theme.d.ts +8 -0
  205. package/dist/theme/theme.js +124 -0
  206. package/dist/utils/map.d.ts +3 -0
  207. package/dist/utils/map.js +21 -0
  208. package/dist/utils/projections.d.ts +6 -0
  209. package/dist/utils/projections.js +10 -0
  210. package/dist/utils/story.d.ts +12 -0
  211. package/dist/utils/story.js +2 -0
  212. package/package.json +103 -100
  213. package/public/assets/img/map-marker.gif +0 -0
  214. package/src/lib/cesium-map/map.tsx +22 -12
  215. package/src/lib/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx +155 -0
  216. package/src/lib/cesium-map/terrain-providers/terrain-provider.stories.tsx +5 -3
  217. package/src/lib/cesium-map/tools/coordinates-tracker.tool.tsx +1 -1
  218. package/src/lib/cesium-map/tools/inspector.tool.tsx +15 -0
  219. package/src/lib/cesium-map/tools/terranian-height.tool.tsx +167 -0
  220. package/src/lib/date-range-picker/{stories/DateRangePicker.stories.tsx → date-range-picker.stories.tsx} +5 -5
  221. package/src/lib/file-picker/file-picker.css +62 -0
  222. package/src/lib/file-picker/file-picker.stories.tsx +447 -0
  223. package/src/lib/file-picker/file-picker.tsx +180 -0
  224. package/src/lib/file-picker/fs-map.json +1557 -0
  225. package/src/lib/file-picker/index.ts +2 -0
  226. package/src/lib/file-picker/localization.ts +164 -0
  227. package/src/lib/index.ts +1 -0
  228. package/src/lib/models/enums.ts +1 -0
  229. package/src/lib/smart-table/smart-table-row.spec.tsx +1 -1
  230. package/tsbuildconfig.json +2 -2
  231. package/tsconfig.json +2 -1
@@ -0,0 +1,62 @@
1
+ body[dir='rtl'] .chonky-fileListWrapper [class^='listContainer-'] {
2
+ direction: rtl !important;
3
+ }
4
+
5
+ body[dir='rtl']
6
+ .chonky-fileEntryClickableWrapper
7
+ [class^='listFileEntryProperty-'] {
8
+ direction: ltr !important;
9
+ }
10
+
11
+ body[dir='rtl'] .chonky-chonkyRoot {
12
+ text-align: right;
13
+ }
14
+
15
+ body[dir='rtl'] .chonky-infoText {
16
+ margin-left: unset;
17
+ margin-right: 12px;
18
+ }
19
+
20
+ body[dir='rtl'] .chonky-contextMenuList .MuiListItemText-root,
21
+ body[dir='rtl'] .chonky-dropdownList .MuiListItemText-root {
22
+ text-align: right;
23
+ }
24
+
25
+ body[dir='rtl'] .chonky-icon {
26
+ margin-right: unset;
27
+ margin-left: 8px;
28
+ }
29
+
30
+ .chonky-chonkyRoot div[class^='folderBackSide'] {
31
+ box-shadow: unset;
32
+ }
33
+
34
+ .chonky-chonkyRoot div[class^='folderBackSide']::after {
35
+ border-color: var(--fp-theme-background, #fff)
36
+ var(--fp-theme-background, #fff) transparent transparent;
37
+ }
38
+
39
+ .chonky-chonkyRoot div[class^='listFileEntryIcon-'] {
40
+ color: #d5d5d5;
41
+ }
42
+
43
+ .chonky-chonkyRoot div[class*='previewFile-d'] {
44
+ background-color: #d5d5d5;
45
+ }
46
+
47
+ .chonky-chonkyRoot div[class^='selectionIndicator-'] {
48
+ background: var(--fp-theme-selection-background, #455570);
49
+ }
50
+
51
+ .chonky-chonkyRoot {
52
+ background-color: var(--fp-theme-background, #fff) !important;
53
+ }
54
+
55
+ .chonky-contextMenuList {
56
+ background-color: var(--fp-theme-surface, #fff) !important;
57
+ }
58
+
59
+ button[class*='chonky-activeButton'],
60
+ .chonky-selectionSizeText {
61
+ color: var(--fp-theme-primary, #24aee9) !important;
62
+ }
@@ -0,0 +1,447 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
+ import React, {
6
+ useCallback,
7
+ useState,
8
+ useMemo,
9
+ useRef,
10
+ useEffect,
11
+ } from 'react';
12
+ import { Story } from '@storybook/react/types-6-0';
13
+ import {
14
+ FormControl,
15
+ FormControlLabel,
16
+ FormLabel,
17
+ Radio,
18
+ RadioGroup,
19
+ } from '@material-ui/core';
20
+ import { Box } from '../box';
21
+ import { SupportedLocales } from '../models';
22
+ import {
23
+ FileActionData,
24
+ FilePicker,
25
+ FileArray,
26
+ FileData,
27
+ FileHelper,
28
+ FilePickerActions,
29
+ FilePickerHandle,
30
+ } from './file-picker';
31
+ import FsMap from './fs-map.json';
32
+
33
+ export default {
34
+ title: 'File Picker',
35
+ component: FilePicker,
36
+ };
37
+
38
+ interface CustomFileData extends FileData {
39
+ parentId?: string;
40
+ childrenIds?: string[];
41
+ }
42
+ interface CustomFileMap {
43
+ [fileId: string]: CustomFileData;
44
+ }
45
+
46
+ const prepareCustomFileMap = (): Record<string, unknown> => {
47
+ const baseFileMap = (FsMap.fileMap as unknown) as CustomFileMap;
48
+ const rootFolderId = FsMap.rootFolderId;
49
+ return { baseFileMap, rootFolderId };
50
+ };
51
+
52
+ // Sets up files map and actions
53
+ // eslint-disable-next-line
54
+ const useCustomFileMap = () => {
55
+ const { baseFileMap, rootFolderId } = useMemo(prepareCustomFileMap, []);
56
+
57
+ const [fileMap, setFileMap] = useState<CustomFileMap>(
58
+ baseFileMap as CustomFileMap
59
+ );
60
+ const [currentFolderId, setCurrentFolderId] = useState(rootFolderId);
61
+
62
+ const resetFileMap = useCallback(() => {
63
+ setFileMap(baseFileMap as CustomFileMap);
64
+ setCurrentFolderId(rootFolderId);
65
+ }, [baseFileMap, rootFolderId]);
66
+
67
+ const currentFolderIdRef = useRef(currentFolderId);
68
+
69
+ useEffect(() => {
70
+ currentFolderIdRef.current = currentFolderId;
71
+ }, [currentFolderId]);
72
+
73
+ const deleteFiles = useCallback((files: CustomFileData[]) => {
74
+ setFileMap((currentFileMap) => {
75
+ const newFileMap = { ...currentFileMap };
76
+
77
+ files.forEach((file) => {
78
+ delete newFileMap[file.id];
79
+
80
+ if (file.parentId) {
81
+ const parent = newFileMap[file.parentId];
82
+ const newChildrenIds = parent.childrenIds?.filter(
83
+ (id) => id !== file.id
84
+ );
85
+ newFileMap[file.parentId] = {
86
+ ...parent,
87
+ childrenIds: newChildrenIds,
88
+ childrenCount: newChildrenIds?.length,
89
+ };
90
+ }
91
+ });
92
+
93
+ return newFileMap;
94
+ });
95
+ }, []);
96
+
97
+ const moveFiles = useCallback(
98
+ (
99
+ files: CustomFileData[],
100
+ source: CustomFileData,
101
+ destination: CustomFileData
102
+ ) => {
103
+ setFileMap((currentFileMap) => {
104
+ const newFileMap = { ...currentFileMap };
105
+ const moveFileIds = new Set(files.map((f) => f.id));
106
+
107
+ // Delete files from their source folder.
108
+ const newSourceChildrenIds = source.childrenIds?.filter(
109
+ (id) => !moveFileIds.has(id)
110
+ );
111
+ newFileMap[source.id] = {
112
+ ...source,
113
+ childrenIds: newSourceChildrenIds,
114
+ childrenCount: newSourceChildrenIds?.length,
115
+ };
116
+
117
+ // Add the files to their destination folder.
118
+ const newDestinationChildrenIds = [
119
+ ...(destination.childrenIds as string[]),
120
+ ...files.map((f) => f.id),
121
+ ];
122
+ newFileMap[destination.id] = {
123
+ ...destination,
124
+ childrenIds: newDestinationChildrenIds,
125
+ childrenCount: newDestinationChildrenIds.length,
126
+ };
127
+
128
+ // Finally, update the parent folder ID on the files - from source folder
129
+ // ID to the destination folder ID.
130
+ files.forEach((file) => {
131
+ newFileMap[file.id] = {
132
+ ...file,
133
+ parentId: destination.id,
134
+ };
135
+ });
136
+
137
+ return newFileMap;
138
+ });
139
+ },
140
+ []
141
+ );
142
+
143
+ // TODO: in production we should use UUIDs or MD5 hashes for file paths
144
+ const idCounter = useRef(0);
145
+ const createFolder = useCallback((folderName: string) => {
146
+ setFileMap((currentFileMap) => {
147
+ const newFileMap = { ...currentFileMap };
148
+
149
+ const parentId = currentFolderIdRef.current as string;
150
+ // Create the new folder.
151
+ const newFolderId = `new-folder-${idCounter.current++}`;
152
+ newFileMap[newFolderId] = {
153
+ id: newFolderId,
154
+ name: folderName,
155
+ isDir: true,
156
+ modDate: new Date(),
157
+ parentId: parentId,
158
+ childrenIds: [],
159
+ childrenCount: 0,
160
+ };
161
+
162
+ // Update parent folder to reference the new folder.
163
+ const parent = newFileMap[parentId];
164
+ newFileMap[parentId] = {
165
+ ...parent,
166
+ childrenIds: [...(parent.childrenIds as string[]), newFolderId],
167
+ };
168
+
169
+ return newFileMap;
170
+ });
171
+ }, []);
172
+
173
+ return {
174
+ fileMap,
175
+ currentFolderId,
176
+ setCurrentFolderId,
177
+ resetFileMap,
178
+ deleteFiles,
179
+ moveFiles,
180
+ createFolder,
181
+ };
182
+ };
183
+
184
+ const useFiles = (
185
+ fileMap: CustomFileMap,
186
+ currentFolderId: string
187
+ ): FileArray => {
188
+ return useMemo(() => {
189
+ const currentFolder = fileMap[currentFolderId];
190
+ const files = currentFolder.childrenIds
191
+ ? currentFolder.childrenIds.map((fileId: string) => fileMap[fileId])
192
+ : [];
193
+ return files;
194
+ }, [currentFolderId, fileMap]);
195
+ };
196
+
197
+ const useFolderChain = (
198
+ fileMap: CustomFileMap,
199
+ currentFolderId: string
200
+ ): FileArray => {
201
+ return useMemo(() => {
202
+ const currentFolder = fileMap[currentFolderId];
203
+ const folderChain = [currentFolder];
204
+ let parentId = currentFolder.parentId;
205
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
206
+ while (parentId) {
207
+ const parentFile = fileMap[parentId];
208
+ // eslint-disable-next-line
209
+ if (parentFile) {
210
+ folderChain.unshift(parentFile);
211
+ parentId = parentFile.parentId;
212
+ } else {
213
+ break;
214
+ }
215
+ }
216
+ return folderChain;
217
+ }, [currentFolderId, fileMap]);
218
+ };
219
+
220
+ const useFileActionHandler = (
221
+ setCurrentFolderId: (folderId: string) => void,
222
+ deleteFiles: (files: CustomFileData[]) => void,
223
+ moveFiles: (
224
+ files: FileData[],
225
+ source: FileData,
226
+ destination: FileData
227
+ ) => void,
228
+ createFolder: (folderName: string) => void
229
+ ): ((data: FileActionData) => void) => {
230
+ return useCallback(
231
+ (data: FileActionData) => {
232
+ if (data.id === FilePickerActions.OpenFiles.id) {
233
+ const { targetFile, files } = data.payload;
234
+ const fileToOpen = targetFile ?? files[0];
235
+ // eslint-disable-next-line
236
+ if (fileToOpen && FileHelper.isDirectory(fileToOpen)) {
237
+ setCurrentFolderId(fileToOpen.id);
238
+ return;
239
+ }
240
+ } else if (data.id === FilePickerActions.DeleteFiles.id) {
241
+ deleteFiles(data.state.selectedFilesForAction);
242
+ } else if (data.id === FilePickerActions.MoveFiles.id) {
243
+ moveFiles(
244
+ data.payload.files,
245
+ data.payload.source as FileData,
246
+ data.payload.destination
247
+ );
248
+ } else if (data.id === FilePickerActions.CreateFolder.id) {
249
+ const folderName = prompt('Provide the name for your new folder:');
250
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
251
+ if (folderName) createFolder(folderName);
252
+ }
253
+ },
254
+ [createFolder, deleteFiles, moveFiles, setCurrentFolderId]
255
+ );
256
+ };
257
+
258
+ export const ReadOnlyMode: Story = () => {
259
+ const {
260
+ fileMap,
261
+ currentFolderId,
262
+ setCurrentFolderId,
263
+ // resetFileMap,
264
+ deleteFiles,
265
+ moveFiles,
266
+ createFolder,
267
+ } = useCustomFileMap();
268
+ const files = useFiles(fileMap, currentFolderId as string);
269
+ const folderChain = useFolderChain(fileMap, currentFolderId as string);
270
+ const handleFileAction = useFileActionHandler(
271
+ setCurrentFolderId,
272
+ deleteFiles,
273
+ moveFiles,
274
+ createFolder
275
+ );
276
+ return (
277
+ <Box style={{ height: '600px' }}>
278
+ <FilePicker
279
+ files={files}
280
+ folderChain={folderChain}
281
+ onFileAction={handleFileAction}
282
+ readOnlyMode={true}
283
+ />
284
+ </Box>
285
+ );
286
+ };
287
+
288
+ export const DarkTheme: Story = () => {
289
+ const {
290
+ fileMap,
291
+ currentFolderId,
292
+ setCurrentFolderId,
293
+ // resetFileMap,
294
+ deleteFiles,
295
+ moveFiles,
296
+ createFolder,
297
+ } = useCustomFileMap();
298
+ const files = useFiles(fileMap, currentFolderId as string);
299
+ const folderChain = useFolderChain(fileMap, currentFolderId as string);
300
+ const handleFileAction = useFileActionHandler(
301
+ setCurrentFolderId,
302
+ deleteFiles,
303
+ moveFiles,
304
+ createFolder
305
+ );
306
+ return (
307
+ <Box style={{ height: '600px' }}>
308
+ <FilePicker
309
+ files={files}
310
+ folderChain={folderChain}
311
+ onFileAction={handleFileAction}
312
+ theme={{
313
+ primary: 'blue',
314
+ background: 'black',
315
+ surface: 'gray',
316
+ textOnBackground: 'white',
317
+ selectionBackground: '#455570',
318
+ }}
319
+ />
320
+ </Box>
321
+ );
322
+ };
323
+
324
+ export const Localized: Story = () => {
325
+ const {
326
+ fileMap,
327
+ currentFolderId,
328
+ setCurrentFolderId,
329
+ // resetFileMap,
330
+ deleteFiles,
331
+ moveFiles,
332
+ createFolder,
333
+ } = useCustomFileMap();
334
+ const files = useFiles(fileMap, currentFolderId as string);
335
+ const folderChain = useFolderChain(fileMap, currentFolderId as string);
336
+ const handleFileAction = useFileActionHandler(
337
+ setCurrentFolderId,
338
+ deleteFiles,
339
+ moveFiles,
340
+ createFolder
341
+ );
342
+ const [locale, setLocale] = useState<SupportedLocales>(SupportedLocales.HE);
343
+ const handleLocaleChange = useCallback(
344
+ (event) => setLocale(event.target.value),
345
+ []
346
+ );
347
+ return (
348
+ <>
349
+ <FormControl component="fieldset" style={{ marginBottom: 15 }}>
350
+ <FormLabel component="legend">Pick a language:</FormLabel>
351
+ <RadioGroup
352
+ aria-label="locale"
353
+ name="locale"
354
+ value={locale}
355
+ onChange={handleLocaleChange}
356
+ >
357
+ <FormControlLabel
358
+ value={SupportedLocales.HE}
359
+ control={<Radio />}
360
+ label="עברית"
361
+ />
362
+ <FormControlLabel
363
+ value={SupportedLocales.RU}
364
+ control={<Radio />}
365
+ label="Русский"
366
+ />
367
+ <FormControlLabel
368
+ value={SupportedLocales.EN}
369
+ control={<Radio />}
370
+ label="English"
371
+ />
372
+ </RadioGroup>
373
+ </FormControl>
374
+ <br />
375
+ <Box style={{ height: '600px' }}>
376
+ <FilePicker
377
+ files={files}
378
+ folderChain={folderChain}
379
+ onFileAction={handleFileAction}
380
+ locale={locale}
381
+ />
382
+ </Box>
383
+ </>
384
+ );
385
+ };
386
+
387
+ export const FilesSelection: Story = () => {
388
+ const {
389
+ fileMap,
390
+ currentFolderId,
391
+ setCurrentFolderId,
392
+ // resetFileMap,
393
+ deleteFiles,
394
+ moveFiles,
395
+ createFolder,
396
+ } = useCustomFileMap();
397
+ const files = useFiles(fileMap, currentFolderId as string);
398
+ const folderChain = useFolderChain(fileMap, currentFolderId as string);
399
+ const handleFileAction = useFileActionHandler(
400
+ setCurrentFolderId,
401
+ deleteFiles,
402
+ moveFiles,
403
+ createFolder
404
+ );
405
+ const fileBrowserRef = useRef<FilePickerHandle>(null);
406
+
407
+ const logSelection = useCallback(
408
+ (event: React.MouseEvent) => {
409
+ event.preventDefault();
410
+ event.stopPropagation();
411
+ if (!fileBrowserRef.current) return;
412
+ console.log(fileBrowserRef.current.getFileSelection());
413
+ },
414
+ [fileBrowserRef]
415
+ );
416
+
417
+ const randomizeSelection = useCallback(
418
+ (event: React.MouseEvent) => {
419
+ event.preventDefault();
420
+ event.stopPropagation();
421
+ if (!fileBrowserRef.current) return;
422
+ const randomFileIds = new Set<string>();
423
+ for (const file of files) {
424
+ if (file && Math.random() > 0.5) randomFileIds.add(file.id);
425
+ }
426
+ fileBrowserRef.current.setFileSelection(randomFileIds);
427
+ },
428
+ [files, fileBrowserRef]
429
+ );
430
+
431
+ return (
432
+ <Box style={{ height: '600px' }}>
433
+ <button type="button" onClick={randomizeSelection}>
434
+ Select files
435
+ </button>
436
+ <button type="button" onClick={logSelection}>
437
+ Log selection
438
+ </button>
439
+ <FilePicker
440
+ ref={fileBrowserRef}
441
+ files={files}
442
+ folderChain={folderChain}
443
+ onFileAction={handleFileAction}
444
+ />
445
+ </Box>
446
+ );
447
+ };
@@ -0,0 +1,180 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import {
3
+ ChonkyActions,
4
+ ChonkyFileActionData,
5
+ FileAction,
6
+ FileArray as ChonkyFileArray,
7
+ FileBrowserHandle,
8
+ FileBrowserProps,
9
+ FileData as ChonkyFileData,
10
+ FileHelper as ChonkyFileHelper,
11
+ FullFileBrowser,
12
+ I18nConfig,
13
+ setChonkyDefaults,
14
+ } from 'chonky';
15
+ import { ChonkyIconFA } from 'chonky-icon-fontawesome';
16
+ import { makeStyles } from '@material-ui/core/styles';
17
+ import { Box } from '../box';
18
+ import { SupportedLocales } from '../models';
19
+ import localization from './localization';
20
+
21
+ import './file-picker.css';
22
+
23
+ export type FilePickerHandle = FileBrowserHandle;
24
+
25
+ export type FileActionData = ChonkyFileActionData;
26
+
27
+ export type FileArray = ChonkyFileArray;
28
+
29
+ export type FileData = ChonkyFileData;
30
+
31
+ export type FilePickerAction = FileAction;
32
+
33
+ export class FileHelper extends ChonkyFileHelper {}
34
+
35
+ // eslint-disable-next-line @typescript-eslint/naming-convention
36
+ export const FilePickerActions = ChonkyActions;
37
+
38
+ export type FilePickerView = typeof FilePickerView[keyof typeof FilePickerView];
39
+ // eslint-disable-next-line @typescript-eslint/naming-convention
40
+ export const FilePickerView = {
41
+ listView: ChonkyActions.EnableListView.id,
42
+ gridView: ChonkyActions.EnableGridView.id,
43
+ } as const;
44
+
45
+ export interface FilePickerTheme {
46
+ primary: string;
47
+ background: string;
48
+ surface: string;
49
+ textOnBackground: string;
50
+ selectionBackground: string;
51
+ }
52
+
53
+ // IMPLEMENTATION NOTES: Currently FilePicker component works with his own icon set.
54
+ // In future might be tweaked.
55
+ setChonkyDefaults({ iconComponent: ChonkyIconFA });
56
+
57
+ export interface FilePickerProps extends Partial<FileBrowserProps> {
58
+ theme?: FilePickerTheme;
59
+ styles?: Record<string, string>;
60
+ defaultView?: FilePickerView;
61
+ readOnlyMode?: boolean;
62
+ locale?: SupportedLocales;
63
+ }
64
+
65
+ export const FilePicker: React.FC<FilePickerProps> = React.memo(
66
+ React.forwardRef<FileBrowserHandle, FilePickerProps>(
67
+ (
68
+ {
69
+ theme,
70
+ styles = { height: '100%', minWidth: '600px' },
71
+ defaultView = FilePickerView.listView,
72
+ readOnlyMode = false,
73
+ locale,
74
+ files,
75
+ folderChain,
76
+ onFileAction,
77
+ ...props
78
+ },
79
+ ref
80
+ ) => {
81
+ // IMPLEMENTATION NOTES: Currently FilePicker component discards the ability to show file thumbnail.
82
+ // In future might be tweaked.
83
+ const thumbnailGenerator = useCallback(
84
+ (file: FileData) => null,
85
+ // file.thumbnailUrl ? `https://chonky.io${file.thumbnailUrl}` : null,
86
+ []
87
+ );
88
+
89
+ makeStyles({
90
+ '@global': {
91
+ '.chonky-dropdownList': {
92
+ backgroundColor: `${theme?.surface as string} !important`,
93
+ },
94
+ 'li[class*="chonky-activeButton"]': {
95
+ color: `${theme?.primary as string} !important`,
96
+ },
97
+ },
98
+ })();
99
+
100
+ const toDashCase = (str: string): string => {
101
+ return str.replace(/([A-Z])/g, ($1) => '-' + $1.toLowerCase());
102
+ };
103
+
104
+ const themeObject = useMemo((): Record<string, string> => {
105
+ if (theme !== undefined) {
106
+ const processedColors = Object.keys(theme).reduce(
107
+ (acc: Record<string, string>, key) => {
108
+ const val = ((theme as unknown) as Record<string, string>)[key];
109
+ key = key.startsWith('--')
110
+ ? key
111
+ : `--fp-theme-${toDashCase(key)}`;
112
+ acc[key] = val;
113
+ return acc;
114
+ },
115
+ {}
116
+ );
117
+ return processedColors;
118
+ }
119
+ return {};
120
+ }, [theme]);
121
+
122
+ const [darkMode, setDarkMode] = useState<boolean>(false);
123
+ const [defaultFileViewActionId, setDefaultFileViewActionId] = useState<
124
+ FilePickerView
125
+ >();
126
+ const [disableDragAndDrop, setDisableDragAndDrop] = useState<boolean>(
127
+ false
128
+ );
129
+ const [fileActions, setFileActions] = useState<FilePickerAction[]>();
130
+ const [i18n, setI18n] = useState<I18nConfig>();
131
+ useEffect(() => {
132
+ if (theme) {
133
+ setDarkMode(true);
134
+ }
135
+
136
+ setDefaultFileViewActionId(defaultView);
137
+
138
+ if (readOnlyMode) {
139
+ setDisableDragAndDrop(true);
140
+ } else {
141
+ setFileActions([
142
+ ChonkyActions.CreateFolder,
143
+ ChonkyActions.DeleteFiles,
144
+ ]);
145
+ }
146
+
147
+ if (locale !== undefined) {
148
+ setI18n(localization[locale]);
149
+ }
150
+ }, [theme, defaultView, readOnlyMode, locale]);
151
+
152
+ return (
153
+ <Box
154
+ style={{
155
+ ...styles,
156
+ ...themeObject,
157
+ }}
158
+ >
159
+ <FullFileBrowser
160
+ ref={ref}
161
+ files={files ?? []}
162
+ folderChain={folderChain}
163
+ onFileAction={(data: FileActionData): void => {
164
+ if (typeof onFileAction === 'function') {
165
+ void onFileAction(data);
166
+ }
167
+ }}
168
+ thumbnailGenerator={thumbnailGenerator}
169
+ defaultFileViewActionId={defaultFileViewActionId}
170
+ disableDragAndDrop={disableDragAndDrop}
171
+ fileActions={fileActions}
172
+ darkMode={darkMode}
173
+ i18n={i18n}
174
+ {...props}
175
+ />
176
+ </Box>
177
+ );
178
+ }
179
+ )
180
+ );