@linzjs/step-ag-grid 2.4.11 → 3.0.0

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 (38) hide show
  1. package/dist/index.js +301 -249
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/GridCell.d.ts +4 -7
  4. package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
  5. package/dist/src/components/gridForm/GridFormEditBearing.d.ts +1 -1
  6. package/dist/src/components/gridForm/GridFormMessage.d.ts +3 -3
  7. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +2 -2
  8. package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +7 -3
  9. package/dist/src/components/gridForm/GridFormSubComponentTextInput.d.ts +1 -1
  10. package/dist/src/components/gridForm/GridFormTextArea.d.ts +1 -1
  11. package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -1
  12. package/dist/src/contexts/GridPopoverContext.d.ts +5 -3
  13. package/dist/src/contexts/GridPopoverContextProvider.d.ts +3 -1
  14. package/dist/src/lui/TextInputFormatted.d.ts +3 -2
  15. package/dist/step-ag-grid.esm.js +302 -250
  16. package/dist/step-ag-grid.esm.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/components/GridCell.tsx +9 -33
  19. package/src/components/GridPopoverHook.tsx +3 -7
  20. package/src/components/GridSubComponentTextArea.tsx +0 -6
  21. package/src/components/gridForm/GridFormDropDown.tsx +23 -30
  22. package/src/components/gridForm/GridFormEditBearing.tsx +10 -8
  23. package/src/components/gridForm/GridFormMessage.tsx +9 -7
  24. package/src/components/gridForm/GridFormMultiSelect.tsx +56 -48
  25. package/src/components/gridForm/GridFormPopoverMenu.tsx +94 -29
  26. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +2 -1
  27. package/src/components/gridForm/GridFormTextArea.tsx +8 -8
  28. package/src/components/gridForm/GridFormTextInput.tsx +12 -16
  29. package/src/contexts/GridPopoverContext.tsx +10 -5
  30. package/src/contexts/GridPopoverContextProvider.tsx +24 -27
  31. package/src/lui/TextAreaInput.tsx +13 -1
  32. package/src/lui/TextInputFormatted.tsx +14 -3
  33. package/src/react-menu3/components/MenuList.tsx +42 -10
  34. package/src/react-menu3/contexts/SettingsContext.ts +1 -1
  35. package/src/stories/grid/FormTest.tsx +18 -16
  36. package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +0 -1
  37. package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +3 -3
  38. package/src/stories/grid/GridReadOnly.stories.tsx +15 -5
@@ -1,10 +1,12 @@
1
1
  import { GridBaseRow } from "../Grid";
2
2
  import { useCallback, useContext, useEffect, useRef, useState } from "react";
3
- import { GridContext } from "../../contexts/GridContext";
4
3
  import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
5
- import { MenuDivider, MenuItem } from "../../react-menu3";
4
+ import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
6
5
  import { useGridPopoverHook } from "../GridPopoverHook";
7
- import { CellEditorCommon, CellParams } from "../GridCell";
6
+ import { CellEditorCommon } from "../GridCell";
7
+ import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
8
+ import { ClickEvent } from "../../react-menu3/types";
9
+ import { GridPopoverContext } from "../../contexts/GridPopoverContext";
8
10
 
9
11
  export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends CellEditorCommon {
10
12
  options: (selectedRows: RowType[]) => Promise<MenuOption<RowType>[]>;
@@ -17,24 +19,35 @@ interface MenuSeparatorType {
17
19
  __isMenuSeparator__: boolean;
18
20
  }
19
21
 
20
- export interface MenuOption<RowType> {
22
+ export interface SelectedMenuOptionResult<RowType extends GridBaseRow> extends MenuOption<RowType> {
23
+ subValue: any;
24
+ }
25
+
26
+ export interface MenuOption<RowType extends GridBaseRow> {
21
27
  label: JSX.Element | string | MenuSeparatorType;
22
- action?: (selectedRows: RowType[]) => Promise<boolean>;
28
+ action?: (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => Promise<void>;
23
29
  disabled?: string | boolean;
24
30
  supportsMultiEdit: boolean;
25
31
  hidden?: boolean;
32
+ subComponent?: (props: any) => JSX.Element;
26
33
  }
27
34
 
28
35
  /**
29
36
  * NOTE: If the popout menu doesn't appear on single click when also selecting row it's because
30
37
  * you need a useMemo around your columnDefs
31
38
  */
32
- export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFormPopoutMenuProps<RowType>) => {
33
- const props = _props as GridFormPopoutMenuProps<RowType> & CellParams<RowType>;
34
- const { updatingCells } = useContext(GridContext);
39
+ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridFormPopoutMenuProps<RowType>) => {
40
+ const { selectedRows, updateValue } = useContext(GridPopoverContext);
41
+
35
42
  const optionsInitialising = useRef(false);
36
43
  const [options, setOptions] = useState<MenuOption<RowType>[]>();
37
44
 
45
+ // Save triggers during async action processing which triggers another action(), this ref blocks that
46
+ const actionProcessing = useRef(false);
47
+ const [subComponentSelected, setSubComponentSelected] = useState<MenuOption<RowType> | null>(null);
48
+ const subComponentIsValid = useRef(false);
49
+ const [subSelectedValue, setSubSelectedValue] = useState<any>();
50
+
38
51
  // Load up options list if it's async function
39
52
  useEffect(() => {
40
53
  if (options || optionsInitialising.current) return;
@@ -42,33 +55,61 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
42
55
  const optionsConf = props.options ?? [];
43
56
 
44
57
  (async () => {
45
- if (typeof optionsConf == "function") {
46
- setOptions(await optionsConf(props.selectedRows));
47
- } else {
48
- setOptions(optionsConf);
49
- }
50
-
58
+ setOptions(typeof optionsConf == "function" ? await optionsConf(selectedRows) : optionsConf);
51
59
  optionsInitialising.current = false;
52
60
  })();
53
- }, [options, props.options, props.selectedRows]);
61
+ }, [options, props.options, selectedRows]);
54
62
 
55
63
  const actionClick = useCallback(
56
- async (menuOption: MenuOption<any>) => {
57
- return await updatingCells({ selectedRows: props.selectedRows, field: props.field }, async (selectedRows) => {
58
- menuOption.action && (await menuOption.action(selectedRows));
64
+ async (menuOption: MenuOption<RowType>) => {
65
+ actionProcessing.current = true;
66
+ return updateValue(async () => {
67
+ const result = { ...menuOption, subValue: subSelectedValue };
68
+ menuOption.action && (await menuOption.action(selectedRows, result));
69
+ actionProcessing.current = false;
59
70
  return true;
60
71
  });
61
72
  },
62
- [props.field, props.selectedRows, updatingCells],
73
+ [selectedRows, subSelectedValue, updateValue],
63
74
  );
64
75
 
65
- const selectedRowCount = props.selectedRows.length;
76
+ const onMenuItemClick = useCallback(
77
+ (e: ClickEvent, item: MenuOption<RowType>) => {
78
+ if (item.subComponent) {
79
+ subComponentIsValid.current = false;
80
+ setSubSelectedValue(null);
81
+ setSubComponentSelected(subComponentSelected === item ? null : item);
82
+ e.keepOpen = true;
83
+ } else {
84
+ setSubComponentSelected(null);
85
+ actionClick(item).then();
86
+ }
87
+ },
88
+ [actionClick, subComponentSelected],
89
+ );
90
+
91
+ const selectedRowCount = selectedRows.length;
66
92
 
67
93
  const filteredOptions = options?.filter((menuOption) => {
68
94
  return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
69
95
  });
70
96
 
71
- const { popoverWrapper } = useGridPopoverHook({ className: props.className });
97
+ const save = useCallback(async () => {
98
+ // if a subcomponent is open we assume that it's meant to be saved.
99
+ if (!actionProcessing.current && subComponentSelected) {
100
+ if (!subComponentIsValid.current) return false;
101
+ await actionClick(subComponentSelected);
102
+ }
103
+ return true;
104
+ }, [actionClick, subComponentSelected]);
105
+
106
+ const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
107
+
108
+ const localTriggerSave = async (reason?: string) => {
109
+ if (!subComponentIsValid.current) return;
110
+ return triggerSave(reason);
111
+ };
112
+
72
113
  return popoverWrapper(
73
114
  <ComponentLoadingWrapper loading={!filteredOptions} className={"GridFormPopupMenu"}>
74
115
  <>
@@ -77,14 +118,38 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
77
118
  <MenuDivider key={`$$divider_${index}`} />
78
119
  ) : (
79
120
  !item.hidden && (
80
- <MenuItem
81
- key={`${item.label}`}
82
- onClick={() => actionClick(item)}
83
- disabled={!!item.disabled || !filteredOptions?.includes(item)}
84
- title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
85
- >
86
- {item.label as JSX.Element | string}
87
- </MenuItem>
121
+ <>
122
+ <MenuItem
123
+ key={`${item.label}`}
124
+ onClick={(e: ClickEvent) => onMenuItemClick(e, item)}
125
+ disabled={!!item.disabled || !filteredOptions?.includes(item)}
126
+ title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
127
+ >
128
+ {item.label as JSX.Element | string}
129
+ </MenuItem>
130
+ {item.subComponent && subComponentSelected === item && (
131
+ <FocusableItem className={"LuiDeprecatedForms"} key={`${item.label}_subcomponent`}>
132
+ {(_: any) =>
133
+ item.subComponent && (
134
+ <GridSubComponentContext.Provider
135
+ value={{
136
+ value: subSelectedValue,
137
+ setValue: (value: any) => {
138
+ setSubSelectedValue(value);
139
+ },
140
+ setValid: (valid: boolean) => {
141
+ subComponentIsValid.current = valid;
142
+ },
143
+ triggerSave: localTriggerSave,
144
+ }}
145
+ >
146
+ <item.subComponent />
147
+ </GridSubComponentContext.Provider>
148
+ )
149
+ }
150
+ </FocusableItem>
151
+ )}
152
+ </>
88
153
  )
89
154
  ),
90
155
  )}
@@ -1,6 +1,7 @@
1
+ import "../../styles/GridFormSubComponentTextInput.scss";
2
+
1
3
  import { useState, KeyboardEvent } from "react";
2
4
  import { TextInputFormatted } from "../../lui/TextInputFormatted";
3
- import "../../styles/GridFormSubComponentTextInput.scss";
4
5
 
5
6
  export interface GridFormSubComponentTextInput {
6
7
  setValue: (value: string) => void;
@@ -1,8 +1,9 @@
1
- import { useCallback, useState } from "react";
1
+ import { useCallback, useContext, useState } from "react";
2
2
  import { TextAreaInput } from "../../lui/TextAreaInput";
3
3
  import { useGridPopoverHook } from "../GridPopoverHook";
4
4
  import { GridBaseRow } from "../Grid";
5
- import { CellEditorCommon, CellParams } from "../GridCell";
5
+ import { CellEditorCommon } from "../GridCell";
6
+ import { GridPopoverContext } from "../../contexts/GridPopoverContext";
6
7
 
7
8
  export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends CellEditorCommon {
8
9
  placeholder?: string;
@@ -13,9 +14,9 @@ export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends Cell
13
14
  onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
14
15
  }
15
16
 
16
- export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTextAreaProps<RowType>) => {
17
- const props = _props as GridFormTextAreaProps<RowType> & CellParams<RowType>;
18
- const [value, setValue] = useState(props.value ?? "");
17
+ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTextAreaProps<RowType>) => {
18
+ const { field, value: initialVale } = useContext(GridPopoverContext);
19
+ const [value, setValue] = useState(initialVale ?? "");
19
20
 
20
21
  const invalid = useCallback(() => {
21
22
  if (props.required && value.length == 0) {
@@ -34,13 +35,12 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTe
34
35
  async (selectedRows: any[]): Promise<boolean> => {
35
36
  if (invalid()) return false;
36
37
 
37
- if (props.value === (value ?? "")) return true;
38
+ if (initialVale === (value ?? "")) return true;
38
39
 
39
40
  if (props.onSave) {
40
41
  return await props.onSave(selectedRows, value);
41
42
  }
42
43
 
43
- const field = props.field;
44
44
  if (field == null) {
45
45
  console.error("ColDef has no field set");
46
46
  return false;
@@ -48,7 +48,7 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTe
48
48
  selectedRows.forEach((row) => (row[field] = value));
49
49
  return true;
50
50
  },
51
- [props, invalid, value],
51
+ [invalid, initialVale, value, props, field],
52
52
  );
53
53
  const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
54
54
  return popoverWrapper(
@@ -1,8 +1,9 @@
1
- import { useCallback, useState } from "react";
1
+ import { useCallback, useContext, useState } from "react";
2
2
  import { TextInputFormatted } from "../../lui/TextInputFormatted";
3
3
  import { useGridPopoverHook } from "../GridPopoverHook";
4
4
  import { GridBaseRow } from "../Grid";
5
- import { CellEditorCommon, CellParams } from "../GridCell";
5
+ import { CellEditorCommon } from "../GridCell";
6
+ import { GridPopoverContext } from "../../contexts/GridPopoverContext";
6
7
 
7
8
  export interface GridFormTextInputProps<RowType extends GridBaseRow> extends CellEditorCommon {
8
9
  placeholder?: string;
@@ -15,9 +16,10 @@ export interface GridFormTextInputProps<RowType extends GridBaseRow> extends Cel
15
16
  onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
16
17
  }
17
18
 
18
- export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormTextInputProps<RowType>) => {
19
- const props = _props as GridFormTextInputProps<RowType> & CellParams<RowType>;
20
- const initValue = props.value == null ? "" : `${props.value}`;
19
+ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTextInputProps<RowType>) => {
20
+ const { field, data, value: initialVale } = useContext(GridPopoverContext);
21
+
22
+ const initValue = initialVale == null ? "" : `${initialVale}`;
21
23
  const [value, setValue] = useState(initValue);
22
24
 
23
25
  const invalid = useCallback(() => {
@@ -29,13 +31,13 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
29
31
  return `Text must be no longer than ${props.maxLength} characters`;
30
32
  }
31
33
  if (props.validate) {
32
- return props.validate(trimmedValue, props.data);
34
+ return props.validate(trimmedValue, data);
33
35
  }
34
36
  return null;
35
- }, [props, value]);
37
+ }, [data, props, value]);
36
38
 
37
39
  const save = useCallback(
38
- async (selectedRows: any[]): Promise<boolean> => {
40
+ async (selectedRows: RowType[]): Promise<boolean> => {
39
41
  if (invalid()) return false;
40
42
  const trimmedValue = value.trim();
41
43
  if (initValue === trimmedValue) return true;
@@ -44,15 +46,15 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
44
46
  return await props.onSave(selectedRows, trimmedValue);
45
47
  }
46
48
 
47
- const field = props.field;
48
49
  if (field == null) {
49
50
  console.error("ColDef has no field set");
50
51
  return false;
51
52
  }
53
+ // @ts-ignore row[field] row is of type any
52
54
  selectedRows.forEach((row) => (row[field] = trimmedValue));
53
55
  return true;
54
56
  },
55
- [invalid, value, initValue, props],
57
+ [invalid, value, initValue, props, field],
56
58
  );
57
59
  const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
58
60
 
@@ -63,12 +65,6 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
63
65
  onChange={(e) => setValue(e.target.value)}
64
66
  error={invalid()}
65
67
  formatted={props.units}
66
- onMouseEnter={(e) => {
67
- if (document.activeElement != e.currentTarget) {
68
- e.currentTarget.focus();
69
- e.currentTarget.selectionStart = e.currentTarget.value.length;
70
- }
71
- }}
72
68
  inputProps={{
73
69
  style: { width: "100%" },
74
70
  placeholder: props.placeholder,
@@ -1,5 +1,4 @@
1
1
  import { createContext, RefObject } from "react";
2
- import { ICellEditorParams } from "ag-grid-community";
3
2
 
4
3
  export interface PropsType {
5
4
  value: any;
@@ -13,14 +12,20 @@ export type GridPopoverContextType = {
13
12
  anchorRef: RefObject<Element>;
14
13
  saving: boolean;
15
14
  setSaving: (saving: boolean) => void;
16
- setProps: (props: ICellEditorParams, multiEdit: boolean) => void;
17
- propsRef: RefObject<PropsType>;
15
+ field: string;
16
+ value: any;
17
+ data: any;
18
+ selectedRows: any[];
19
+ updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
18
20
  };
19
21
 
20
22
  export const GridPopoverContext = createContext<GridPopoverContextType>({
21
23
  anchorRef: { current: null },
22
24
  saving: false,
23
25
  setSaving: () => {},
24
- setProps: () => {},
25
- propsRef: { current: null },
26
+ field: "",
27
+ value: null,
28
+ data: null,
29
+ selectedRows: [],
30
+ updateValue: async () => false,
26
31
  });
@@ -1,41 +1,35 @@
1
- import { ReactNode, RefObject, useCallback, useContext, useRef, useState } from "react";
2
- import { GridPopoverContext, PropsType } from "./GridPopoverContext";
1
+ import { ReactNode, RefObject, useCallback, useContext, useMemo, useRef, useState } from "react";
2
+ import { GridPopoverContext } from "./GridPopoverContext";
3
3
  import { ICellEditorParams } from "ag-grid-community";
4
4
  import { GridContext } from "./GridContext";
5
5
  import { sortBy } from "lodash-es";
6
6
  import { GridBaseRow } from "../components/Grid";
7
7
 
8
8
  interface GridPopoverContextProps {
9
+ props: ICellEditorParams;
9
10
  children: ReactNode;
10
11
  }
11
12
 
12
- export const GridPopoverContextProvider = (props: GridPopoverContextProps) => {
13
+ export const GridPopoverContextProvider = ({ props, children }: GridPopoverContextProps) => {
13
14
  const { getSelectedRows, updatingCells } = useContext(GridContext);
14
- const anchorRef = useRef<Element>();
15
- const propsRef = useRef<PropsType>({} as PropsType);
15
+ const anchorRef = useRef<Element>(props.eGridCell);
16
16
 
17
17
  const [saving, setSaving] = useState(false);
18
18
 
19
- const setProps = useCallback(
20
- (props: ICellEditorParams, multiEdit: boolean) => {
21
- // Then item that is clicked on will always be first in the list
22
- const selectedRows = multiEdit
23
- ? sortBy(getSelectedRows(), (row) => row.id !== props.data.id)
24
- : [props.data as GridBaseRow];
25
- const field = props.colDef?.field ?? "";
19
+ const { colDef } = props;
20
+ const { cellEditorParams } = colDef;
21
+ const multiEdit = cellEditorParams?.multiEdit ?? false;
22
+ // Then item that is clicked on will always be first in the list
23
+ const selectedRows = useMemo(
24
+ () => (multiEdit ? sortBy(getSelectedRows(), (row) => row.id !== props.data.id) : [props.data as GridBaseRow]),
25
+ [getSelectedRows, multiEdit, props.data],
26
+ );
27
+ const field = props.colDef?.field ?? "";
26
28
 
27
- anchorRef.current = props.eGridCell;
28
- propsRef.current = {
29
- value: props.value,
30
- data: props.data,
31
- field,
32
- selectedRows,
33
- updateValue: async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> => {
34
- return !saving && (await updatingCells({ selectedRows, field }, saveFn, setSaving));
35
- },
36
- };
37
- },
38
- [getSelectedRows, saving, updatingCells],
29
+ const updateValue = useCallback(
30
+ async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> =>
31
+ !saving && (await updatingCells({ selectedRows, field }, saveFn, setSaving)),
32
+ [field, saving, selectedRows, updatingCells],
39
33
  );
40
34
 
41
35
  return (
@@ -44,11 +38,14 @@ export const GridPopoverContextProvider = (props: GridPopoverContextProps) => {
44
38
  anchorRef: anchorRef as any as RefObject<Element>,
45
39
  saving,
46
40
  setSaving,
47
- propsRef,
48
- setProps,
41
+ selectedRows,
42
+ field,
43
+ data: props.data,
44
+ value: props.value,
45
+ updateValue,
49
46
  }}
50
47
  >
51
- {props.children}
48
+ {children}
52
49
  </GridPopoverContext.Provider>
53
50
  );
54
51
  };
@@ -34,7 +34,19 @@ export const TextAreaInput = (props: LuiTextAreaInputProps) => {
34
34
  <div className="LuiTextAreaInput-wrapper">
35
35
  {" "}
36
36
  {/* wrapper div used for error styling */}
37
- <textarea id={id} value={props.value} onChange={props.onChange} rows={5} {...props.inputProps} />
37
+ <textarea
38
+ id={id}
39
+ value={props.value}
40
+ onChange={props.onChange}
41
+ rows={5}
42
+ {...props.inputProps}
43
+ onMouseEnter={(e) => {
44
+ if (document.activeElement != e.currentTarget) {
45
+ e.currentTarget.focus();
46
+ e.currentTarget.selectionStart = e.currentTarget.value.length;
47
+ }
48
+ }}
49
+ />
38
50
  </div>
39
51
  </label>
40
52
 
@@ -1,12 +1,20 @@
1
1
  import "./TextInputFormatted.scss";
2
2
 
3
- import { ChangeEventHandler, DetailedHTMLProps, InputHTMLAttributes } from "react";
3
+ import {
4
+ ChangeEventHandler,
5
+ DetailedHTMLProps,
6
+ FocusEventHandler,
7
+ InputHTMLAttributes,
8
+ MouseEventHandler,
9
+ } from "react";
4
10
 
5
11
  import clsx from "clsx";
6
12
  import { LuiIcon } from "@linzjs/lui";
7
13
 
8
14
  export interface LuiTextInputProps {
9
15
  onChange?: ChangeEventHandler<HTMLInputElement>;
16
+ onFocus?: FocusEventHandler<HTMLInputElement>;
17
+ onClick?: MouseEventHandler<HTMLInputElement>;
10
18
  inputProps?: DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
11
19
  error?: string | boolean | null;
12
20
  warning?: string | boolean | null;
@@ -16,8 +24,6 @@ export interface LuiTextInputProps {
16
24
 
17
25
  placeholder?: string;
18
26
  formatted?: string;
19
-
20
- onMouseEnter?: (e: React.MouseEvent<HTMLTextAreaElement>) => void;
21
27
  }
22
28
 
23
29
  export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
@@ -38,6 +44,11 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
38
44
  spellCheck={true}
39
45
  defaultValue={props.value}
40
46
  onChange={props.onChange}
47
+ onFocus={props.onFocus}
48
+ onClick={props.onClick}
49
+ onMouseEnter={(e) => {
50
+ e.currentTarget.focus();
51
+ }}
41
52
  {...props.inputProps}
42
53
  />
43
54
  <span className={"LuiTextInput-formatted"}>{props.formatted}</span>
@@ -1,28 +1,29 @@
1
- import { useState, useReducer, useEffect, useRef, useMemo, useCallback, useContext } from "react";
1
+ import { useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from "react";
2
2
  import { flushSync } from "react-dom";
3
- import { useBEM, useCombinedRef, useLayoutEffect, useItems } from "../hooks";
4
- import { getPositionHelpers, positionMenu, positionContextMenu } from "../positionUtils";
3
+ import { useBEM, useCombinedRef, useItems, useLayoutEffect } from "../hooks";
4
+ import { getPositionHelpers, positionContextMenu, positionMenu } from "../positionUtils";
5
5
  import {
6
- mergeProps,
7
6
  batchedUpdates,
7
+ CloseReason,
8
8
  commonProps,
9
9
  floatEqual,
10
+ FocusPositions,
10
11
  getScrollAncestor,
11
12
  getTransition,
12
- safeCall,
13
+ HoverActionTypes,
13
14
  isMenuOpen,
14
- menuClass,
15
- menuArrowClass,
16
- CloseReason,
17
15
  Keys,
18
- FocusPositions,
19
- HoverActionTypes,
16
+ menuArrowClass,
17
+ menuClass,
18
+ mergeProps,
19
+ safeCall,
20
20
  } from "../utils";
21
21
  import { ControlledMenuProps, MenuDirection } from "../types";
22
22
  import { MenuListItemContext } from "../contexts/MenuListItemContext";
23
23
  import { HoverItemContext } from "../contexts/HoverItemContext";
24
24
  import { MenuListContext } from "../contexts/MenuListContext";
25
25
  import { SettingsContext } from "../contexts/SettingsContext";
26
+ import { debounce } from "lodash-es";
26
27
 
27
28
  export const MenuList = ({
28
29
  ariaLabel,
@@ -75,6 +76,7 @@ export const MenuList = ({
75
76
  const focusRef = useRef<HTMLDivElement>(null);
76
77
  const arrowRef = useRef<HTMLDivElement>(null);
77
78
  const prevOpen = useRef(false);
79
+ const latestWindowSize = useRef({ width: 0, height: 0 });
78
80
  const latestMenuSize = useRef({ width: 0, height: 0 });
79
81
  const latestHandlePosition = useRef(() => {});
80
82
  const { hoverItem, dispatch, updateItems } = useItems(menuRef, focusRef);
@@ -320,6 +322,36 @@ export const MenuList = ({
320
322
  return () => resizeObserver.unobserve(observeTarget);
321
323
  }, [reposition]);
322
324
 
325
+ // Matt added window resize observer
326
+ useEffect(() => {
327
+ if (typeof ResizeObserver !== "function" || reposition === "initial") return;
328
+
329
+ const callback = debounce(() => {
330
+ const { width, height } = menuRef.current.ownerDocument.body.getBoundingClientRect();
331
+ if (width === 0 || height === 0) return;
332
+ if (
333
+ floatEqual(width, latestWindowSize.current.width, 1) &&
334
+ floatEqual(height, latestWindowSize.current.height, 1)
335
+ ) {
336
+ return;
337
+ }
338
+ latestWindowSize.current = { width, height };
339
+ flushSync(() => {
340
+ latestHandlePosition.current();
341
+ forceReposSubmenu();
342
+ });
343
+ }, 250);
344
+
345
+ const resizeObserver = new ResizeObserver(callback);
346
+
347
+ const observeTarget = menuRef.current.ownerDocument.body;
348
+ resizeObserver.observe(observeTarget, { box: "border-box" });
349
+ return () => {
350
+ callback.cancel();
351
+ resizeObserver.unobserve(observeTarget);
352
+ };
353
+ }, [reposition]);
354
+
323
355
  useEffect(() => {
324
356
  if (!isOpen) {
325
357
  dispatch(HoverActionTypes.RESET, undefined, 0);
@@ -1,4 +1,3 @@
1
- // FIXME hacking a default context in here is probably bad
2
1
  import { createContext, MutableRefObject, RefObject } from "react";
3
2
  import { ControlledMenuProps, MenuReposition, MenuViewScroll, RectElement, TransitionFieldType } from "../types";
4
3
 
@@ -17,4 +16,5 @@ interface SettingsContextType extends ControlledMenuProps {
17
16
  viewScroll?: MenuViewScroll;
18
17
  }
19
18
 
19
+ // FIXME hacking a default context in here is probably bad, but the context is mess
20
20
  export const SettingsContext = createContext<SettingsContextType>({} as SettingsContextType);
@@ -1,11 +1,11 @@
1
1
  import "./FormTest.scss";
2
2
 
3
- import { useCallback, useState } from "react";
3
+ import { useCallback, useContext, useState } from "react";
4
4
  import { LuiAlertModal, LuiAlertModalButtons, LuiButton, LuiTextInput } from "@linzjs/lui";
5
5
  import { wait } from "../../utils/util";
6
- import { CellEditorCommon, CellParams } from "../../components/GridCell";
6
+ import { CellEditorCommon } from "../../components/GridCell";
7
7
  import { useGridPopoverHook } from "../../components/GridPopoverHook";
8
- import { GridBaseRow } from "../../components/Grid";
8
+ import { GridPopoverContext } from "../../contexts/GridPopoverContext";
9
9
 
10
10
  export interface IFormTestRow {
11
11
  id: number;
@@ -16,28 +16,30 @@ export interface IFormTestRow {
16
16
  distance: number | null;
17
17
  }
18
18
 
19
- export const FormTest = <RowType extends GridBaseRow>(_props: CellEditorCommon): JSX.Element => {
20
- const props = _props as CellParams<RowType>;
21
- const [v1, ...v2] = props.value.split(" ");
19
+ export const FormTest = (props: CellEditorCommon): JSX.Element => {
20
+ const { value } = useContext(GridPopoverContext);
21
+ const [v1, ...v2] = value.split(" ");
22
22
 
23
23
  const [nameType, setNameType] = useState(v1);
24
24
  const [numba, setNumba] = useState(v2.join(" "));
25
25
 
26
- const save = useCallback(async (): Promise<boolean> => {
27
- // eslint-disable-next-line no-console
28
- console.log("onSave", props.selectedRows, nameType, numba);
26
+ const save = useCallback(
27
+ async (selectedRows: IFormTestRow[]): Promise<boolean> => {
28
+ // eslint-disable-next-line no-console
29
+ console.log("onSave", selectedRows, nameType, numba);
29
30
 
30
- // @ts-ignore
31
- props.selectedRows.forEach((row) => (row["name"] = [nameType, numba].join(" ")));
32
- await wait(1000);
31
+ selectedRows.forEach((row) => (row.name = [nameType, numba].join(" ")));
32
+ await wait(1000);
33
33
 
34
- // Close form
35
- return true;
36
- }, [nameType, numba, props.selectedRows]);
34
+ // Close form
35
+ return true;
36
+ },
37
+ [nameType, numba],
38
+ );
37
39
 
38
40
  const [showModal, setShowModal] = useState<boolean>(false);
39
41
 
40
- const { popoverWrapper } = useGridPopoverHook({ className: _props.className, save });
42
+ const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
41
43
 
42
44
  return popoverWrapper(
43
45
  <>
@@ -143,7 +143,6 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
143
143
  action: async (selectedRows) => {
144
144
  await wait(1500);
145
145
  setRowData(rowData.filter((data) => !selectedRows.some((row) => row.id == data.id)));
146
- return true;
147
146
  },
148
147
  supportsMultiEdit: true,
149
148
  },