@oneuptime/common 7.0.3162 → 7.0.3172

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 (26) hide show
  1. package/UI/Components/Filters/FilterViewer.tsx +3 -8
  2. package/UI/Components/ModelTable/BaseModelTable.tsx +38 -21
  3. package/UI/Components/ModelTable/TableView.tsx +144 -60
  4. package/UI/Components/MoreMenu/MoreMenu.tsx +79 -39
  5. package/UI/Components/MoreMenu/MoreMenuItem.tsx +5 -3
  6. package/UI/Components/MoreMenu/MoreMenuSection.tsx +2 -2
  7. package/UI/Components/Table/Table.tsx +7 -2
  8. package/UI/Components/Table/TableHeader.tsx +13 -20
  9. package/UI/Types/FunctionTypes.ts +2 -0
  10. package/build/dist/UI/Components/Filters/FilterViewer.js +2 -5
  11. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  12. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +20 -21
  13. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  14. package/build/dist/UI/Components/ModelTable/TableView.js +64 -25
  15. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  16. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +29 -8
  17. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  18. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +3 -3
  19. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  20. package/build/dist/UI/Components/MoreMenu/MoreMenuSection.js +1 -1
  21. package/build/dist/UI/Components/MoreMenu/MoreMenuSection.js.map +1 -1
  22. package/build/dist/UI/Components/Table/Table.js +2 -2
  23. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  24. package/build/dist/UI/Components/Table/TableHeader.js +9 -16
  25. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  26. package/package.json +2 -2
@@ -28,7 +28,7 @@ export interface ComponentProps<T extends GenericObject> {
28
28
  onFilterModalOpen: () => void;
29
29
  isModalLoading?: boolean;
30
30
  onFilterRefreshClick?: undefined | (() => void);
31
- initialFilterData?: FilterData<T> | undefined;
31
+ filterData?: FilterData<T> | undefined;
32
32
  }
33
33
 
34
34
  type FilterComponentFunction = <T extends GenericObject>(
@@ -38,9 +38,6 @@ type FilterComponentFunction = <T extends GenericObject>(
38
38
  const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
39
39
  props: ComponentProps<T>,
40
40
  ): ReactElement => {
41
- const [filterData, setFilterData] = useState<FilterData<T>>(
42
- props.initialFilterData || {},
43
- );
44
41
  const [tempFilterDataForModal, setTempFilterDataForModal] = useState<
45
42
  FilterData<T>
46
43
  >({});
@@ -50,14 +47,13 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
50
47
  const changeFilterData: ChangeFilterDataFunction = (
51
48
  filterData: FilterData<T>,
52
49
  ) => {
53
- setFilterData(filterData);
54
50
  setTempFilterDataForModal(filterData);
55
51
  props.onFilterChanged?.(filterData);
56
52
  };
57
53
 
58
54
  useEffect(() => {
59
55
  if (props.showFilterModal) {
60
- setTempFilterDataForModal({ ...filterData });
56
+ setTempFilterDataForModal({ ...props.filterData });
61
57
  }
62
58
  }, [props.showFilterModal]);
63
59
 
@@ -337,7 +333,7 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
337
333
 
338
334
  const filterTexts: Array<ReactElement> = translateFilterToText({
339
335
  filters: props.filters,
340
- filterData: filterData,
336
+ filterData: props.filterData || {},
341
337
  });
342
338
 
343
339
  if (props.filterError) {
@@ -419,7 +415,6 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
419
415
  props.onFilterModalClose();
420
416
  }}
421
417
  onSubmit={() => {
422
- setFilterData({ ...tempFilterDataForModal });
423
418
  setTempFilterDataForModal({});
424
419
  if (props.onFilterChanged) {
425
420
  props.onFilterChanged({
@@ -233,6 +233,8 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
233
233
  ) => ReactElement = <TBaseModel extends BaseModel | AnalyticsBaseModel>(
234
234
  props: ComponentProps<TBaseModel>,
235
235
  ): ReactElement => {
236
+ const [tableView, setTableView] = useState<TableView | null>(null);
237
+
236
238
  const matchBulkSelectedItemByField: keyof TBaseModel =
237
239
  props.bulkActions?.matchBulkSelectedItemByField || "_id";
238
240
 
@@ -800,23 +802,17 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
800
802
  }
801
803
 
802
804
  if (props.saveFilterProps && props.saveFilterProps.tableId) {
803
- const currentTableView: TableView = new TableView();
804
- currentTableView.query = (query || {}) as any;
805
- currentTableView.itemsOnPage = itemsOnPage || 10;
806
-
807
- if (sortBy && sortOrder) {
808
- currentTableView.sort = {
809
- [sortBy as string]: sortOrder,
810
- };
811
- } else {
812
- currentTableView.sort = {};
813
- }
814
-
815
805
  return (
816
806
  <TableViewElement
817
807
  tableId={props.saveFilterProps.tableId}
818
- currentTableView={currentTableView}
819
- onViewChange={(tableView: TableView | null) => {
808
+ tableView={tableView}
809
+ currentQuery={query}
810
+ currentSortBy={sortBy}
811
+ currentItemsOnPage={itemsOnPage}
812
+ currentSortOrder={sortOrder}
813
+ onViewChange={async (tableView: TableView | null) => {
814
+ setTableView(tableView);
815
+
820
816
  if (tableView) {
821
817
  const sortBy: string | undefined = Object.keys(
822
818
  tableView.sort || {},
@@ -831,9 +827,14 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
831
827
 
832
828
  // then set query, sort and items on the page
833
829
  setQuery(tableView.query || {});
830
+ setFilterData(tableView.query || {});
834
831
  setItemsOnPage(tableView.itemsOnPage || 10);
835
- setSortBy(sortBy as keyof TBaseModel);
832
+ setSortBy((sortBy as keyof TBaseModel) || null);
836
833
  setSortOrder(sortOrder);
834
+
835
+ if (classicTableFilters.length === 0) {
836
+ await getFilterDropdownItems();
837
+ }
837
838
  } else {
838
839
  setQuery({});
839
840
  setSortBy(null);
@@ -906,9 +907,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
906
907
  headerbuttons.push({
907
908
  title: "",
908
909
  buttonStyle: ButtonStyleType.ICON,
909
- className: props.showRefreshButton
910
- ? "p-1 px-1 pr-0 pl-0 py-0 mt-1"
911
- : "py-0 pr-0 pl-1 mt-1",
910
+ className: "py-0 pr-0 pl-1 mt-1",
912
911
  onClick: () => {
913
912
  setQuery({});
914
913
  setShowFilterModal(true);
@@ -1238,6 +1237,10 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1238
1237
  setActionButtonSchema(actionsSchema);
1239
1238
  };
1240
1239
 
1240
+ const [filterData, setFilterData] = useState<FilterData<TBaseModel>>(
1241
+ props.initialFilterData || {},
1242
+ );
1243
+
1241
1244
  type OnFilterChangedFunction = (filterData: FilterData<TBaseModel>) => void;
1242
1245
 
1243
1246
  const onFilterChanged: OnFilterChangedFunction = (
@@ -1245,6 +1248,8 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1245
1248
  ): void => {
1246
1249
  const newQuery: Query<TBaseModel> = {};
1247
1250
 
1251
+ setFilterData(filterData);
1252
+
1248
1253
  for (const key in filterData) {
1249
1254
  if (filterData[key] && typeof filterData[key] === Typeof.String) {
1250
1255
  newQuery[key as keyof TBaseModel] = (filterData[key] || "").toString();
@@ -1349,8 +1354,9 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1349
1354
  <Table
1350
1355
  onFilterChanged={(filterData: FilterData<TBaseModel>) => {
1351
1356
  onFilterChanged(filterData);
1357
+ setTableView(null);
1352
1358
  }}
1353
- initialFilterData={props.initialFilterData}
1359
+ filterData={filterData}
1354
1360
  className={
1355
1361
  props.cardProps
1356
1362
  ? ""
@@ -1400,6 +1406,8 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1400
1406
  }),
1401
1407
  );
1402
1408
  }}
1409
+ sortBy={sortBy}
1410
+ sortOrder={sortOrder}
1403
1411
  onBulkSelectItemsOnCurrentPage={() => {
1404
1412
  const items: TBaseModel[] = [...bulkSelectedItems, ...data];
1405
1413
 
@@ -1452,6 +1460,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1452
1460
  ) => {
1453
1461
  setSortBy(sortBy);
1454
1462
  setSortOrder(sortOrder);
1463
+ setTableView(null);
1455
1464
  }}
1456
1465
  singularLabel={props.singularName || model.singularName || "Item"}
1457
1466
  pluralLabel={props.pluralName || model.pluralName || "Items"}
@@ -1483,9 +1492,17 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1483
1492
  await fetchItems();
1484
1493
  }}
1485
1494
  disablePagination={props.disablePagination || false}
1486
- onNavigateToPage={async (pageNumber: number, itemsOnPage: number) => {
1495
+ onNavigateToPage={async (
1496
+ pageNumber: number,
1497
+ newItemsOnPage: number,
1498
+ ) => {
1487
1499
  setCurrentPageNumber(pageNumber);
1488
- setItemsOnPage(itemsOnPage);
1500
+
1501
+ if (newItemsOnPage !== itemsOnPage) {
1502
+ setTableView(null);
1503
+ }
1504
+
1505
+ setItemsOnPage(newItemsOnPage);
1489
1506
  }}
1490
1507
  noItemsMessage={props.noItemsMessage || ""}
1491
1508
  onRefreshClick={async () => {
@@ -1,9 +1,4 @@
1
- import React, {
2
- FunctionComponent,
3
- ReactElement,
4
- useEffect,
5
- useState,
6
- } from "react";
1
+ import React, { ReactElement, useEffect, useRef, useState } from "react";
7
2
  import TableView from "../../../Models/DatabaseModels/TableView";
8
3
  import ObjectID from "../../../Types/ObjectID";
9
4
  import MoreMenu from "../MoreMenu/MoreMenu";
@@ -14,7 +9,7 @@ import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
14
9
  import SortOrder from "../../../Types/BaseDatabase/SortOrder";
15
10
  import API from "../../../Utils/API";
16
11
  import MoreMenuSection from "../MoreMenu/MoreMenuSection";
17
- import Button, { ButtonStyleType } from "../Button/Button";
12
+ import { ButtonStyleType } from "../Button/Button";
18
13
  import IconProp from "../../../Types/Icon/IconProp";
19
14
  import { BarLoader } from "react-spinners";
20
15
  import ConfirmModal from "../Modal/ConfirmModal";
@@ -22,18 +17,30 @@ import ModelFormModal from "../ModelFormModal/ModelFormModal";
22
17
  import { FormType } from "../Forms/ModelForm";
23
18
  import FormFieldSchemaType from "../Forms/Types/FormFieldSchemaType";
24
19
  import { PromiseVoidFunction } from "../../../Types/FunctionTypes";
25
- import { GetReactElementFunction } from "../../Types/FunctionTypes";
20
+ import { GetReactElementArrayFunction } from "../../Types/FunctionTypes";
26
21
  import ProjectUtil from "../../Utils/Project";
27
22
  import User from "../../Utils/User";
23
+ import Icon, { SizeProp, ThickProp } from "../Icon/Icon";
24
+ import GenericObject from "../../../Types/GenericObject";
25
+ import Query from "../../../Types/BaseDatabase/Query";
26
+ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
27
+ import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
28
+ import Sort from "../../../Types/BaseDatabase/Sort";
28
29
 
29
- export interface ComponentProps {
30
+ export interface ComponentProps<T extends GenericObject> {
30
31
  tableId: string;
31
32
  onViewChange: (tableView: TableView | null) => void;
32
- currentTableView: TableView | null;
33
+ currentQuery: Query<T>;
34
+ currentSortOrder: SortOrder | null;
35
+ currentSortBy: keyof T | null;
36
+ currentItemsOnPage: number;
37
+ tableView: TableView | null;
33
38
  }
34
39
 
35
- const TableViewElement: FunctionComponent<ComponentProps> = (
36
- props: ComponentProps,
40
+ const TableViewElement: <T extends DatabaseBaseModel | AnalyticsBaseModel>(
41
+ props: ComponentProps<T>,
42
+ ) => ReactElement = <T extends DatabaseBaseModel | AnalyticsBaseModel>(
43
+ props: ComponentProps<T>,
37
44
  ): ReactElement => {
38
45
  const [error, setError] = useState<string>("");
39
46
  const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -47,9 +54,15 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
47
54
  const [showCreateNewViewModal, setShowCreateNewViewModel] =
48
55
  useState<boolean>(false);
49
56
 
57
+ const moreMenuRef: React.MutableRefObject<undefined> = useRef();
58
+
50
59
  const [currentlySelectedView, setCurrentlySelectedView] =
51
60
  useState<TableView | null>(null);
52
61
 
62
+ useEffect(() => {
63
+ setCurrentlySelectedView(props.tableView);
64
+ }, [props.tableView]);
65
+
53
66
  // load all the filters for this user and for this project.
54
67
  const fetchTableViews: PromiseVoidFunction = async (): Promise<void> => {
55
68
  try {
@@ -122,21 +135,28 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
122
135
  (item: TableView): ReactElement => {
123
136
  return (
124
137
  <div className="flex">
125
- <Button
126
- icon={IconProp.Edit}
127
- buttonStyle={ButtonStyleType.ICON_LIGHT}
128
- onClick={() => {
129
- setTableViewToEdit(item);
130
- }}
131
- />
132
-
133
- <Button
134
- icon={IconProp.Trash}
135
- buttonStyle={ButtonStyleType.ICON_LIGHT}
136
- onClick={() => {
137
- setTableViewToDelete(item);
138
- }}
139
- />
138
+ <div className="h-4 w-4 mr-2">
139
+ <Icon
140
+ icon={IconProp.Edit}
141
+ className="text-gray-400 hover:text-gray-600"
142
+ size={SizeProp.Regular}
143
+ thick={ThickProp.Thick}
144
+ onClick={() => {
145
+ setTableViewToEdit(item);
146
+ }}
147
+ />
148
+ </div>
149
+ <div className="h-4 w-4">
150
+ <Icon
151
+ className="text-gray-400 hover:text-gray-600"
152
+ icon={IconProp.Trash}
153
+ size={SizeProp.Regular}
154
+ thick={ThickProp.Thick}
155
+ onClick={() => {
156
+ setTableViewToDelete(item);
157
+ }}
158
+ />
159
+ </div>
140
160
  </div>
141
161
  );
142
162
  };
@@ -150,6 +170,8 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
150
170
  key={index}
151
171
  rightElement={getRightElementForTableViewMenuItem(item)}
152
172
  text={item.name || ""}
173
+ className="text-gray-600 hover:text-gray-800"
174
+ icon={IconProp.Window}
153
175
  onClick={() => {
154
176
  props.onViewChange && props.onViewChange(item);
155
177
  setCurrentlySelectedView(item);
@@ -159,42 +181,36 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
159
181
  });
160
182
  };
161
183
 
162
- const getMenuContents: GetReactElementFunction = (): ReactElement => {
163
- if (isLoading) {
164
- return <BarLoader />;
165
- }
184
+ const getMenuContents: GetReactElementArrayFunction =
185
+ (): Array<ReactElement> => {
186
+ if (isLoading) {
187
+ return [<BarLoader />];
188
+ }
166
189
 
167
- return (
168
- <>
169
- {allTableViews.length > 0 ? (
190
+ const elements: Array<ReactElement> = [];
191
+
192
+ if (allTableViews.length > 0) {
193
+ elements.push(
170
194
  <MoreMenuSection title="Saved Views">
171
195
  {getViewItems()}
172
- </MoreMenuSection>
173
- ) : (
174
- <></>
175
- )}
176
-
177
- {currentlySelectedView ? (
178
- <MoreMenuItem
179
- text="Deselect View"
180
- onClick={() => {
181
- setCurrentlySelectedView(null);
182
- props.onViewChange && props.onViewChange(null);
183
- }}
184
- ></MoreMenuItem>
185
- ) : (
186
- <></>
187
- )}
196
+ </MoreMenuSection>,
197
+ );
198
+ }
188
199
 
200
+ elements.push(
189
201
  <MoreMenuItem
190
- text="Save View"
202
+ text="Save as New View"
203
+ className="bg-gray-50 hover:bg-gray-100 text-gray-700 hover:text-gray-900 font-medium -mt-2"
204
+ icon={IconProp.Add}
205
+ iconClassName=""
191
206
  onClick={() => {
192
207
  setShowCreateNewViewModel(true);
193
208
  }}
194
- ></MoreMenuItem>
195
- </>
196
- );
197
- };
209
+ ></MoreMenuItem>,
210
+ );
211
+
212
+ return elements;
213
+ };
198
214
 
199
215
  if (error) {
200
216
  return (
@@ -224,6 +240,7 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
224
240
  submitButtonText="Save Changes"
225
241
  onSuccess={async () => {
226
242
  setTableViewToEdit(undefined);
243
+
227
244
  await fetchTableViews();
228
245
  }}
229
246
  formProps={{
@@ -279,15 +296,28 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
279
296
  }}
280
297
  submitButtonText="Save Changes"
281
298
  onBeforeCreate={(tableView: TableView) => {
299
+ let sort: Sort<T> = {};
300
+
301
+ if (props.currentSortOrder && props.currentSortBy) {
302
+ sort = {
303
+ [props.currentSortBy]: props.currentSortOrder,
304
+ } as Sort<T>;
305
+ }
306
+
282
307
  tableView.tableId = props.tableId;
283
- tableView.query = props.currentTableView?.query || {};
284
- tableView.itemsOnPage = props.currentTableView?.itemsOnPage || 10;
285
- tableView.sort = props.currentTableView?.sort || {};
308
+ tableView.query =
309
+ (props.currentQuery as Query<
310
+ DatabaseBaseModel | AnalyticsBaseModel
311
+ >) || {};
312
+ tableView.itemsOnPage = props?.currentItemsOnPage || 10;
313
+ tableView.sort = sort || {};
286
314
  return Promise.resolve(tableView);
287
315
  }}
288
- onSuccess={async () => {
316
+ onSuccess={async (tableView: TableView) => {
289
317
  setShowCreateNewViewModel(false);
290
318
  await fetchTableViews();
319
+ // set as current view
320
+ props.onViewChange && props.onViewChange(tableView);
291
321
  }}
292
322
  formProps={{
293
323
  name: "Save New View",
@@ -311,7 +341,61 @@ const TableViewElement: FunctionComponent<ComponentProps> = (
311
341
  );
312
342
  }
313
343
 
314
- return <MoreMenu>{getMenuContents()}</MoreMenu>;
344
+ type GetElementToBeShownInsteadOfButtonFunction = () =>
345
+ | ReactElement
346
+ | undefined;
347
+
348
+ const getElementToBeShownInsteadOfButton: GetElementToBeShownInsteadOfButtonFunction =
349
+ (): ReactElement | undefined => {
350
+ if (!currentlySelectedView) {
351
+ return undefined;
352
+ }
353
+
354
+ return (
355
+ <div className="ml-2 mt-1 cursor-pointer font-semibold flex rounded-full border-2 border-gray-600 text-gray-600 text-xs p-1 pl-2 pr-2">
356
+ <div
357
+ onClick={() => {
358
+ flipDropdown();
359
+ }}
360
+ >
361
+ {currentlySelectedView.name}
362
+ </div>
363
+ <div className="h-4 w-4 rounded-full bg-gray-500 text-white hover:bg-gray-800 ml-3 -mr-1 p-1">
364
+ <Icon
365
+ icon={IconProp.Close}
366
+ size={SizeProp.Regular}
367
+ thick={ThickProp.Thick}
368
+ onClick={() => {
369
+ setCurrentlySelectedView(null);
370
+ props.onViewChange && props.onViewChange(null);
371
+ closeDropdownMenu();
372
+ }}
373
+ />
374
+ </div>
375
+ </div>
376
+ );
377
+ };
378
+
379
+ const closeDropdownMenu: VoidFunction = (): void => {
380
+ if (moreMenuRef.current) {
381
+ (moreMenuRef.current as any).closeDropdown();
382
+ }
383
+ };
384
+
385
+ const flipDropdown: VoidFunction = (): void => {
386
+ if (moreMenuRef.current) {
387
+ (moreMenuRef.current as any).flipDropdown();
388
+ }
389
+ };
390
+
391
+ return (
392
+ <MoreMenu
393
+ elementToBeShownInsteadOfButton={getElementToBeShownInsteadOfButton()}
394
+ ref={moreMenuRef}
395
+ >
396
+ {getMenuContents()}
397
+ </MoreMenu>
398
+ );
315
399
  };
316
400
 
317
401
  export default TableViewElement;
@@ -1,54 +1,94 @@
1
1
  import React, {
2
- FunctionComponent,
2
+ forwardRef,
3
3
  ReactElement,
4
4
  useEffect,
5
+ useImperativeHandle,
5
6
  useState,
6
7
  } from "react";
7
- import Button, { ButtonStyleType } from "../Button/Button";
8
8
  import IconProp from "../../../Types/Icon/IconProp";
9
9
  import useComponentOutsideClick from "../../Types/UseComponentOutsideClick";
10
+ import Icon from "../Icon/Icon";
10
11
 
11
12
  export interface ComponentProps {
12
- children: Array<ReactElement> | ReactElement;
13
+ children: Array<ReactElement>;
14
+ elementToBeShownInsteadOfButton?: ReactElement | undefined;
13
15
  }
14
16
 
15
- const MoreMenu: FunctionComponent<ComponentProps> = (
16
- props: ComponentProps,
17
- ): ReactElement => {
18
- const { ref, isComponentVisible, setIsComponentVisible } =
19
- useComponentOutsideClick(false);
20
-
21
- const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
22
-
23
- useEffect(() => {
24
- setDropdownVisible(isComponentVisible);
25
- }, [isComponentVisible]);
26
-
27
- return (
28
- <div className="relative inline-block text-left">
29
- <div>
30
- <Button
31
- icon={IconProp.More}
32
- buttonStyle={ButtonStyleType.ICON}
33
- onClick={() => {
34
- setIsComponentVisible(!isDropdownVisible);
35
- }}
36
- />
17
+ const MoreMenu: React.ForwardRefExoticComponent<
18
+ ComponentProps & React.RefAttributes<unknown>
19
+ > = forwardRef(
20
+ (props: ComponentProps, componentRef: React.ForwardedRef<unknown>) => {
21
+ const { ref, isComponentVisible, setIsComponentVisible } =
22
+ useComponentOutsideClick(false);
23
+
24
+ useImperativeHandle(componentRef, () => {
25
+ return {
26
+ closeDropdown() {
27
+ setIsComponentVisible(false);
28
+ },
29
+ openDropdown() {
30
+ setIsComponentVisible(true);
31
+ },
32
+ flipDropdown() {
33
+ setIsComponentVisible(!isDropdownVisible);
34
+ },
35
+ };
36
+ });
37
+
38
+ const [isDropdownVisible, setDropdownVisible] = useState<boolean>(false);
39
+
40
+ useEffect(() => {
41
+ setDropdownVisible(isComponentVisible);
42
+ }, [isComponentVisible]);
43
+
44
+ return (
45
+ <div className="relative inline-block text-left">
46
+ {!props.elementToBeShownInsteadOfButton && (
47
+ <div className="h-7 w-7 text-gray-600 mt-1 cursor-pointer">
48
+ <Icon
49
+ icon={IconProp.More}
50
+ className="p-1"
51
+ onClick={() => {
52
+ setIsComponentVisible(!isDropdownVisible);
53
+ }}
54
+ />
55
+ </div>
56
+ )}
57
+
58
+ {props.elementToBeShownInsteadOfButton && (
59
+ <div>{props.elementToBeShownInsteadOfButton}</div>
60
+ )}
61
+
62
+ {isComponentVisible && (
63
+ <div
64
+ ref={ref}
65
+ className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
66
+ role="menu"
67
+ aria-orientation="vertical"
68
+ aria-labelledby="menu-button"
69
+ >
70
+ {props.children.map((child: ReactElement, index: number) => {
71
+ return (
72
+ <div
73
+ key={index}
74
+ role="menuitem"
75
+ onClick={() => {
76
+ if (isComponentVisible) {
77
+ setIsComponentVisible(false);
78
+ }
79
+ }}
80
+ >
81
+ {child}
82
+ </div>
83
+ );
84
+ })}
85
+ </div>
86
+ )}
37
87
  </div>
88
+ );
89
+ },
90
+ );
38
91
 
39
- {isComponentVisible && (
40
- <div
41
- ref={ref}
42
- className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
43
- role="menu"
44
- aria-orientation="vertical"
45
- aria-labelledby="menu-button"
46
- >
47
- {props.children}
48
- </div>
49
- )}
50
- </div>
51
- );
52
- };
92
+ MoreMenu.displayName = "MoreMenu";
53
93
 
54
94
  export default MoreMenu;
@@ -7,6 +7,8 @@ export interface ComponentProps {
7
7
  text: string;
8
8
  onClick: () => void;
9
9
  rightElement?: Array<ReactElement> | ReactElement | undefined;
10
+ className?: string | undefined;
11
+ iconClassName?: string | undefined;
10
12
  }
11
13
 
12
14
  const MoreMenuItem: FunctionComponent<ComponentProps> = (
@@ -14,7 +16,7 @@ const MoreMenuItem: FunctionComponent<ComponentProps> = (
14
16
  ): ReactElement => {
15
17
  return (
16
18
  <a
17
- className="cursor-pointer group flex items-center px-4 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-50"
19
+ className={`cursor-pointer group flex items-center px-4 py-2 text-sm text-gray-600 hover:text-gray-700 hover:bg-gray-50 ${props.className}`}
18
20
  role="menuitem"
19
21
  onClick={() => {
20
22
  props.onClick();
@@ -23,10 +25,10 @@ const MoreMenuItem: FunctionComponent<ComponentProps> = (
23
25
  {props.icon && (
24
26
  <Icon
25
27
  icon={props.icon}
26
- className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
28
+ className={`mr-3 h-5 w-5 text-gray-400 text-gray-700 hover:text-gray-900 ${props.iconClassName}`}
27
29
  />
28
30
  )}
29
- <div className="flex justify-between">
31
+ <div className="flex w-full justify-between">
30
32
  <div>{props.text}</div>
31
33
  <div>{props.rightElement}</div>
32
34
  </div>
@@ -10,8 +10,8 @@ const MoreMenuSection: FunctionComponent<ComponentProps> = (
10
10
  ): ReactElement => {
11
11
  return (
12
12
  <div>
13
- <div className="text-gray-400 text-sm font-medium">
14
- {props.title.toLocaleUpperCase()}
13
+ <div className="text-gray-400 text-xs font-medium pt-2 pl-3 pr-3 pb-2">
14
+ {props.title.toUpperCase()}
15
15
  </div>
16
16
  {props.children}
17
17
  <MoreMenuDivider />