@etsoo/react 1.4.75 → 1.4.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app/Utils.d.ts +7 -0
- package/lib/app/Utils.js +26 -0
- package/lib/components/GridColumn.d.ts +1 -1
- package/lib/components/ScrollerGrid.d.ts +1 -1
- package/lib/mu/AutocompleteExtendedProps.d.ts +1 -1
- package/lib/mu/ComboBox.d.ts +1 -1
- package/lib/mu/ComboBox.js +4 -0
- package/lib/mu/DataGridEx.d.ts +9 -0
- package/lib/mu/DataGridEx.js +2 -2
- package/lib/mu/DnDList.d.ts +1 -1
- package/lib/mu/ItemList.d.ts +2 -2
- package/lib/mu/MUGlobal.d.ts +8 -0
- package/lib/mu/MUGlobal.js +3 -0
- package/lib/mu/OptionGroup.d.ts +2 -2
- package/lib/mu/ResponsibleContainer.d.ts +4 -0
- package/lib/mu/ResponsibleContainer.js +6 -3
- package/lib/mu/ScrollerListEx.d.ts +10 -1
- package/lib/mu/ScrollerListEx.js +5 -3
- package/lib/mu/SelectBool.js +2 -1
- package/lib/mu/SelectEx.d.ts +2 -2
- package/lib/mu/SelectEx.js +23 -8
- package/lib/mu/TableEx.d.ts +1 -1
- package/lib/mu/Tiplist.js +3 -2
- package/lib/mu/pages/ResponsivePageProps.d.ts +4 -0
- package/lib/mu/pages/ViewPage.d.ts +1 -1
- package/package.json +4 -4
- package/src/app/Utils.ts +32 -0
- package/src/components/GridColumn.ts +1 -1
- package/src/components/ScrollerGrid.tsx +1 -1
- package/src/mu/AutocompleteExtendedProps.ts +1 -1
- package/src/mu/ComboBox.tsx +5 -1
- package/src/mu/DataGridEx.tsx +19 -0
- package/src/mu/DnDList.tsx +1 -1
- package/src/mu/ItemList.tsx +2 -2
- package/src/mu/MUGlobal.ts +11 -0
- package/src/mu/OptionGroup.tsx +2 -2
- package/src/mu/ResponsibleContainer.tsx +15 -0
- package/src/mu/ScrollerListEx.tsx +32 -2
- package/src/mu/SelectBool.tsx +2 -1
- package/src/mu/SelectEx.tsx +28 -14
- package/src/mu/TableEx.tsx +2 -2
- package/src/mu/Tiplist.tsx +7 -3
- package/src/mu/pages/ResponsivePageProps.ts +5 -0
- package/src/mu/pages/ViewPage.tsx +1 -1
package/lib/app/Utils.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
/**
|
|
2
3
|
* React utils
|
|
3
4
|
*/
|
|
@@ -8,6 +9,12 @@ export declare namespace Utils {
|
|
|
8
9
|
* @returns Formatted value
|
|
9
10
|
*/
|
|
10
11
|
function formatInputValue(value: unknown): string | number | any[] | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Is safe click
|
|
14
|
+
* @param event Mouse event
|
|
15
|
+
* @returns Result
|
|
16
|
+
*/
|
|
17
|
+
function isSafeClick(event: React.MouseEvent<HTMLElement>): boolean;
|
|
11
18
|
/**
|
|
12
19
|
* Trigger input change event
|
|
13
20
|
* @param input Form input
|
package/lib/app/Utils.js
CHANGED
|
@@ -20,6 +20,32 @@ export var Utils;
|
|
|
20
20
|
return String(value);
|
|
21
21
|
}
|
|
22
22
|
Utils.formatInputValue = formatInputValue;
|
|
23
|
+
/**
|
|
24
|
+
* Is safe click
|
|
25
|
+
* @param event Mouse event
|
|
26
|
+
* @returns Result
|
|
27
|
+
*/
|
|
28
|
+
function isSafeClick(event) {
|
|
29
|
+
// No target
|
|
30
|
+
// HTMLElement <= Element, SVGElement <= Element
|
|
31
|
+
if (!(event.target instanceof Element))
|
|
32
|
+
return true;
|
|
33
|
+
// Outside of the currentTarget
|
|
34
|
+
let target = event.target;
|
|
35
|
+
if (!event.currentTarget.contains(target))
|
|
36
|
+
return false;
|
|
37
|
+
while (target != null && target != event.currentTarget) {
|
|
38
|
+
const nodeName = target.nodeName.toUpperCase();
|
|
39
|
+
if (nodeName === 'INPUT' ||
|
|
40
|
+
nodeName === 'BUTTON' ||
|
|
41
|
+
nodeName === 'A' ||
|
|
42
|
+
target.hasAttribute('onClick'))
|
|
43
|
+
return false;
|
|
44
|
+
target = target.parentElement;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
Utils.isSafeClick = isSafeClick;
|
|
23
49
|
/**
|
|
24
50
|
* Trigger input change event
|
|
25
51
|
* @param input Form input
|
package/lib/mu/ComboBox.d.ts
CHANGED
package/lib/mu/ComboBox.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Keyboard } from '@etsoo/shared';
|
|
|
2
2
|
import { Autocomplete } from '@mui/material';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Utils } from '../app/Utils';
|
|
5
|
+
import { Utils as SharedUtils } from '@etsoo/shared';
|
|
5
6
|
import { InputField } from './InputField';
|
|
6
7
|
import { SearchField } from './SearchField';
|
|
7
8
|
/**
|
|
@@ -81,6 +82,9 @@ export function ComboBox(props) {
|
|
|
81
82
|
return;
|
|
82
83
|
if (onLoadData)
|
|
83
84
|
onLoadData(result);
|
|
85
|
+
if (search) {
|
|
86
|
+
SharedUtils.addBlankItem(result, idField, labelField);
|
|
87
|
+
}
|
|
84
88
|
setOptions(result);
|
|
85
89
|
});
|
|
86
90
|
}
|
package/lib/mu/DataGridEx.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { GridColumn } from '../components/GridColumn';
|
|
3
3
|
import { GridLoaderStates } from '../components/GridLoader';
|
|
4
4
|
import { ScrollerGridProps } from '../components/ScrollerGrid';
|
|
5
|
+
import { MouseEventWithDataHandler } from './MUGlobal';
|
|
5
6
|
/**
|
|
6
7
|
* Footer item renderer props
|
|
7
8
|
*/
|
|
@@ -55,6 +56,14 @@ export interface DataGridExProps<T extends Record<string, any>> extends Omit<Scr
|
|
|
55
56
|
* Hover color
|
|
56
57
|
*/
|
|
57
58
|
hoverColor?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Double click handler
|
|
61
|
+
*/
|
|
62
|
+
onDoubleClick?: MouseEventWithDataHandler<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Click handler
|
|
65
|
+
*/
|
|
66
|
+
onClick?: MouseEventWithDataHandler<T>;
|
|
58
67
|
/**
|
|
59
68
|
* Selectable to support hover over and out effect and row clickable
|
|
60
69
|
* @default true
|
package/lib/mu/DataGridEx.js
CHANGED
|
@@ -126,7 +126,7 @@ export function DataGridEx(props) {
|
|
|
126
126
|
})));
|
|
127
127
|
}
|
|
128
128
|
// Destruct
|
|
129
|
-
const { alternatingColors = [theme.palette.grey[100], undefined], borderRowsCount, bottomHeight = 53, checkable = false, className, columns, defaultOrderBy, height, headerHeight = 56, headerRenderer = defaultHeaderRenderer, footerRenderer = defaultFooterRenderer, footerItemRenderer = DataGridRenderers.defaultFooterItemRenderer, hideFooter = false, hoverColor = '#f6f9fb', idField = 'id', mRef = React.createRef(), selectable = true, selectedColor = '#edf4fb', width, ...rest } = props;
|
|
129
|
+
const { alternatingColors = [theme.palette.grey[100], undefined], borderRowsCount, bottomHeight = 53, checkable = false, className, columns, defaultOrderBy, height, headerHeight = 56, headerRenderer = defaultHeaderRenderer, footerRenderer = defaultFooterRenderer, footerItemRenderer = DataGridRenderers.defaultFooterItemRenderer, hideFooter = false, hoverColor = '#f6f9fb', idField = 'id', mRef = React.createRef(), onClick, onDoubleClick, selectable = true, selectedColor = '#edf4fb', width, ...rest } = props;
|
|
130
130
|
if (checkable) {
|
|
131
131
|
const cbColumn = {
|
|
132
132
|
field: 'selected',
|
|
@@ -261,7 +261,7 @@ export function DataGridEx(props) {
|
|
|
261
261
|
cellProps,
|
|
262
262
|
renderProps
|
|
263
263
|
});
|
|
264
|
-
return (React.createElement("div", { className: rowClass, style: style, "data-row": rowIndex, "data-column": columnIndex, onMouseDown: selectable && !checkable ? handleMouseDown : undefined, onMouseOver: selectable ? handleMouseOver : undefined, onMouseOut: selectable ? handleMouseOut : undefined },
|
|
264
|
+
return (React.createElement("div", { className: rowClass, style: style, "data-row": rowIndex, "data-column": columnIndex, onMouseDown: selectable && !checkable ? handleMouseDown : undefined, onMouseOver: selectable ? handleMouseOver : undefined, onMouseOut: selectable ? handleMouseOut : undefined, onClick: (event) => onClick && data != null && onClick(event, data), onDoubleClick: (event) => onDoubleClick && data != null && onDoubleClick(event, data) },
|
|
265
265
|
React.createElement(Box, { ...cellProps, onMouseEnter: handleMouseEnter }, child)));
|
|
266
266
|
};
|
|
267
267
|
// Column width calculator
|
package/lib/mu/DnDList.d.ts
CHANGED
package/lib/mu/ItemList.d.ts
CHANGED
|
@@ -10,11 +10,11 @@ export interface ItemListProps<T extends Record<string, unknown>> {
|
|
|
10
10
|
/**
|
|
11
11
|
* Id field name
|
|
12
12
|
*/
|
|
13
|
-
idField?: keyof T;
|
|
13
|
+
idField?: string & keyof T;
|
|
14
14
|
/**
|
|
15
15
|
* Label field name or callback
|
|
16
16
|
*/
|
|
17
|
-
labelField?: keyof T | ((item: T) => string);
|
|
17
|
+
labelField?: (string & keyof T) | ((item: T) => string);
|
|
18
18
|
/**
|
|
19
19
|
* Button icon
|
|
20
20
|
*/
|
package/lib/mu/MUGlobal.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
1
2
|
import { Theme } from '@mui/material';
|
|
3
|
+
/**
|
|
4
|
+
* Mouse event handler with data
|
|
5
|
+
*/
|
|
6
|
+
export declare type MouseEventWithDataHandler<T> = (event: React.MouseEvent<HTMLDivElement>, data: T) => void;
|
|
7
|
+
/**
|
|
8
|
+
* MUGlobal for global configurations
|
|
9
|
+
*/
|
|
2
10
|
export declare class MUGlobal {
|
|
3
11
|
/**
|
|
4
12
|
* Search field shrink
|
package/lib/mu/MUGlobal.js
CHANGED
package/lib/mu/OptionGroup.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface OptionGroupProps<T extends Record<string, any> = IdLabelDto, D
|
|
|
17
17
|
/**
|
|
18
18
|
* Id field, default is id
|
|
19
19
|
*/
|
|
20
|
-
idField?: keyof T;
|
|
20
|
+
idField?: string & keyof T;
|
|
21
21
|
/**
|
|
22
22
|
* Label
|
|
23
23
|
*/
|
|
@@ -25,7 +25,7 @@ export interface OptionGroupProps<T extends Record<string, any> = IdLabelDto, D
|
|
|
25
25
|
/**
|
|
26
26
|
* Label field, default is label
|
|
27
27
|
*/
|
|
28
|
-
labelField?: keyof T;
|
|
28
|
+
labelField?: string & keyof T;
|
|
29
29
|
/**
|
|
30
30
|
* Multiple choose item
|
|
31
31
|
*/
|
|
@@ -72,6 +72,10 @@ export interface ResponsibleContainerProps<T extends {}, F extends DataTypes.Bas
|
|
|
72
72
|
* Pull to refresh data
|
|
73
73
|
*/
|
|
74
74
|
pullToRefresh?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Quick action for double click or click under mobile
|
|
77
|
+
*/
|
|
78
|
+
quickAction?: (data: T) => void;
|
|
75
79
|
/**
|
|
76
80
|
* Size ready to read miliseconds span
|
|
77
81
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, Stack } from '@mui/material';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { Labels } from '../app/Labels';
|
|
4
|
+
import { Utils } from '../app/Utils';
|
|
4
5
|
import { GridDataGet } from '../components/GridLoader';
|
|
5
6
|
import useCombinedRefs from '../uses/useCombinedRefs';
|
|
6
7
|
import { useDimensions } from '../uses/useDimensions';
|
|
@@ -24,7 +25,7 @@ function defaultContainerBoxSx(paddings, hasField, _dataGrid) {
|
|
|
24
25
|
*/
|
|
25
26
|
export function ResponsibleContainer(props) {
|
|
26
27
|
// Destruct
|
|
27
|
-
const { adjustHeight, columns, containerBoxSx = defaultContainerBoxSx, dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total), elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal.pagePaddings, pullToRefresh = true, sizeReadyMiliseconds = 0, ...rest } = props;
|
|
28
|
+
const { adjustHeight, columns, containerBoxSx = defaultContainerBoxSx, dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total), elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal.pagePaddings, pullToRefresh = true, quickAction, sizeReadyMiliseconds = 0, ...rest } = props;
|
|
28
29
|
// Labels
|
|
29
30
|
const labels = Labels.CommonPage;
|
|
30
31
|
// Refs
|
|
@@ -106,7 +107,7 @@ export function ResponsibleContainer(props) {
|
|
|
106
107
|
delete rest.itemRenderer;
|
|
107
108
|
return [
|
|
108
109
|
React.createElement(Box, { className: "DataGridBox" },
|
|
109
|
-
React.createElement(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, outerRef: (element) => {
|
|
110
|
+
React.createElement(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data), outerRef: (element) => {
|
|
110
111
|
if (element != null && elementReady)
|
|
111
112
|
elementReady(element, true);
|
|
112
113
|
}, columns: columns, ...rest })),
|
|
@@ -124,7 +125,9 @@ export function ResponsibleContainer(props) {
|
|
|
124
125
|
delete rest.selectable;
|
|
125
126
|
return [
|
|
126
127
|
React.createElement(Box, { className: "ListBox", sx: { height: heightLocal } },
|
|
127
|
-
React.createElement(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, mRef: mRefs,
|
|
128
|
+
React.createElement(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, mRef: mRefs, onClick: (event, data) => quickAction &&
|
|
129
|
+
Utils.isSafeClick(event) &&
|
|
130
|
+
quickAction(data), oRef: (element) => {
|
|
128
131
|
if (element != null && elementReady)
|
|
129
132
|
elementReady(element, false);
|
|
130
133
|
}, ...rest })),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ListChildComponentProps } from 'react-window';
|
|
3
3
|
import { ScrollerListProps } from '../components/ScrollerList';
|
|
4
|
+
import { MouseEventWithDataHandler } from './MUGlobal';
|
|
4
5
|
/**
|
|
5
6
|
* Extended ScrollerList inner item renderer props
|
|
6
7
|
*/
|
|
@@ -40,7 +41,7 @@ export interface ScrollerListExProps<T> extends Omit<ScrollerListProps<T>, 'item
|
|
|
40
41
|
/**
|
|
41
42
|
* Id field
|
|
42
43
|
*/
|
|
43
|
-
idField?: keyof T;
|
|
44
|
+
idField?: string & keyof T;
|
|
44
45
|
/**
|
|
45
46
|
* Inner item renderer
|
|
46
47
|
*/
|
|
@@ -53,6 +54,14 @@ export interface ScrollerListExProps<T> extends Omit<ScrollerListProps<T>, 'item
|
|
|
53
54
|
* Item size, a function indicates its a variable size list
|
|
54
55
|
*/
|
|
55
56
|
itemSize: ScrollerListExItemSize;
|
|
57
|
+
/**
|
|
58
|
+
* Double click handler
|
|
59
|
+
*/
|
|
60
|
+
onDoubleClick?: MouseEventWithDataHandler<T>;
|
|
61
|
+
/**
|
|
62
|
+
* Click handler
|
|
63
|
+
*/
|
|
64
|
+
onClick?: MouseEventWithDataHandler<T>;
|
|
56
65
|
/**
|
|
57
66
|
* On items select change
|
|
58
67
|
*/
|
package/lib/mu/ScrollerListEx.js
CHANGED
|
@@ -66,7 +66,7 @@ const defaultMargin = (margin, isNarrow) => {
|
|
|
66
66
|
};
|
|
67
67
|
};
|
|
68
68
|
// Default itemRenderer
|
|
69
|
-
function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, selected, style, itemHeight, space, margins }) {
|
|
69
|
+
function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, selected, style, itemHeight, onClick, onDoubleClick, space, margins }) {
|
|
70
70
|
// Child
|
|
71
71
|
const child = innerItemRenderer({
|
|
72
72
|
index,
|
|
@@ -81,7 +81,7 @@ function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, sele
|
|
|
81
81
|
if (selected)
|
|
82
82
|
rowClass += ` ${selectedClassName}`;
|
|
83
83
|
// Layout
|
|
84
|
-
return (React.createElement("div", { className: rowClass, style: style, onMouseDown: (event) => onMouseDown(event.currentTarget, data) }, child));
|
|
84
|
+
return (React.createElement("div", { className: rowClass, style: style, onMouseDown: (event) => onMouseDown(event.currentTarget, data), onClick: (event) => onClick && onClick(event, data), onDoubleClick: (event) => onDoubleClick && onDoubleClick(event, data) }, child));
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Extended ScrollerList
|
|
@@ -118,12 +118,14 @@ export function ScrollerListEx(props) {
|
|
|
118
118
|
itemHeight,
|
|
119
119
|
innerItemRenderer,
|
|
120
120
|
onMouseDown,
|
|
121
|
+
onClick,
|
|
122
|
+
onDoubleClick,
|
|
121
123
|
space,
|
|
122
124
|
margins,
|
|
123
125
|
selected: isSelected(itemProps.data),
|
|
124
126
|
...itemProps
|
|
125
127
|
});
|
|
126
|
-
}, onSelectChange, selectedColor = '#edf4fb', ...rest } = props;
|
|
128
|
+
}, onClick, onDoubleClick, onSelectChange, selectedColor = '#edf4fb', ...rest } = props;
|
|
127
129
|
// Theme
|
|
128
130
|
const theme = useTheme();
|
|
129
131
|
// Cache calculation
|
package/lib/mu/SelectBool.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Utils } from '@etsoo/shared';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import { globalApp } from '..';
|
|
3
4
|
import { SelectEx } from './SelectEx';
|
|
@@ -15,7 +16,7 @@ export function SelectBool(props) {
|
|
|
15
16
|
{ id: 'true', label: globalApp.get('yes') }
|
|
16
17
|
];
|
|
17
18
|
if (search)
|
|
18
|
-
|
|
19
|
+
Utils.addBlankItem(options);
|
|
19
20
|
// Layout
|
|
20
21
|
return React.createElement(SelectEx, { options: options, search: search, ...rest });
|
|
21
22
|
}
|
package/lib/mu/SelectEx.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface SelectExProps<T extends {}> extends Omit<SelectProps, 'labelId'
|
|
|
8
8
|
/**
|
|
9
9
|
* Id field, default is id
|
|
10
10
|
*/
|
|
11
|
-
idField?: keyof T;
|
|
11
|
+
idField?: string & keyof T;
|
|
12
12
|
/**
|
|
13
13
|
* Item icon renderer
|
|
14
14
|
*/
|
|
@@ -16,7 +16,7 @@ export interface SelectExProps<T extends {}> extends Omit<SelectProps, 'labelId'
|
|
|
16
16
|
/**
|
|
17
17
|
* Label field, default is label
|
|
18
18
|
*/
|
|
19
|
-
labelField?: ((option: T) => string) | keyof T;
|
|
19
|
+
labelField?: ((option: T) => string) | (string & keyof T);
|
|
20
20
|
/**
|
|
21
21
|
* Load data callback
|
|
22
22
|
*/
|
package/lib/mu/SelectEx.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Checkbox, FormControl, InputLabel, ListItemText, MenuItem, OutlinedInpu
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { MUGlobal } from './MUGlobal';
|
|
5
5
|
import { ListItemRightIcon } from './ListItemRightIcon';
|
|
6
|
+
import { Utils } from '@etsoo/shared';
|
|
6
7
|
/**
|
|
7
8
|
* Extended select component
|
|
8
9
|
* @param props Props
|
|
@@ -68,6 +69,16 @@ export function SelectEx(props) {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
};
|
|
72
|
+
// Get option id
|
|
73
|
+
const getId = (option) => {
|
|
74
|
+
return Reflect.get(option, idField);
|
|
75
|
+
};
|
|
76
|
+
// Get option label
|
|
77
|
+
const getLabel = (option) => {
|
|
78
|
+
return typeof labelField === 'function'
|
|
79
|
+
? labelField(option)
|
|
80
|
+
: Reflect.get(option, labelField);
|
|
81
|
+
};
|
|
71
82
|
// Refs
|
|
72
83
|
const divRef = React.useRef();
|
|
73
84
|
// When layout ready
|
|
@@ -86,6 +97,9 @@ export function SelectEx(props) {
|
|
|
86
97
|
return;
|
|
87
98
|
if (onLoadData)
|
|
88
99
|
onLoadData(result);
|
|
100
|
+
if (search) {
|
|
101
|
+
Utils.addBlankItem(result, idField, labelField);
|
|
102
|
+
}
|
|
89
103
|
setOptions(result);
|
|
90
104
|
});
|
|
91
105
|
}
|
|
@@ -107,18 +121,19 @@ export function SelectEx(props) {
|
|
|
107
121
|
}, renderValue: (selected) => {
|
|
108
122
|
// The text shows up
|
|
109
123
|
return localOptions
|
|
110
|
-
.filter((option) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
.filter((option) => {
|
|
125
|
+
const id = getId(option);
|
|
126
|
+
return Array.isArray(selected)
|
|
127
|
+
? selected.indexOf(id) !== -1
|
|
128
|
+
: selected === id;
|
|
129
|
+
})
|
|
130
|
+
.map((option) => getLabel(option))
|
|
114
131
|
.join(', ');
|
|
115
132
|
}, sx: { minWidth: '150px' }, ...rest }, localOptions.map((option) => {
|
|
116
133
|
// Option id
|
|
117
|
-
const id = option
|
|
134
|
+
const id = getId(option);
|
|
118
135
|
// Option label
|
|
119
|
-
const label =
|
|
120
|
-
? labelField(option)
|
|
121
|
-
: option[labelField];
|
|
136
|
+
const label = getLabel(option);
|
|
122
137
|
// Option
|
|
123
138
|
return (React.createElement(MenuItem, { key: id, value: id, onClick: (event) => {
|
|
124
139
|
if (onItemClick) {
|
package/lib/mu/TableEx.d.ts
CHANGED
package/lib/mu/Tiplist.js
CHANGED
|
@@ -10,7 +10,6 @@ import { SearchField } from './SearchField';
|
|
|
10
10
|
* @returns Component
|
|
11
11
|
*/
|
|
12
12
|
export function Tiplist(props) {
|
|
13
|
-
var _a;
|
|
14
13
|
// Destruct
|
|
15
14
|
const { search = false, idField = 'id', idValue, inputAutoComplete = 'off', inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, label, loadData, defaultValue, value, name, readOnly, onChange, openOnFocus = true, sx = { minWidth: '180px' }, ...rest } = props;
|
|
16
15
|
// Value input ref
|
|
@@ -30,6 +29,8 @@ export function Tiplist(props) {
|
|
|
30
29
|
options: [],
|
|
31
30
|
value: null
|
|
32
31
|
});
|
|
32
|
+
// Input value
|
|
33
|
+
const inputValue = React.useMemo(() => states.value && Reflect.get(states.value, idField), [states.value]);
|
|
33
34
|
React.useEffect(() => {
|
|
34
35
|
if (localValue != null)
|
|
35
36
|
stateUpdate({ value: localValue });
|
|
@@ -134,7 +135,7 @@ export function Tiplist(props) {
|
|
|
134
135
|
}, []);
|
|
135
136
|
// Layout
|
|
136
137
|
return (React.createElement("div", null,
|
|
137
|
-
React.createElement("input", { ref: inputRef, "data-reset": "true", type: "text", style: { display: 'none' }, name: name, value:
|
|
138
|
+
React.createElement("input", { ref: inputRef, "data-reset": "true", type: "text", style: { display: 'none' }, name: name, value: inputValue !== null && inputValue !== void 0 ? inputValue : '', readOnly: true, onChange: inputOnChange }),
|
|
138
139
|
React.createElement(Autocomplete, { filterOptions: (options, _state) => options, value: states.value, options: states.options, onChange: (event, value, reason, details) => {
|
|
139
140
|
// Set value
|
|
140
141
|
setInputValue(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.79",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"@emotion/react": "^11.7.1",
|
|
51
51
|
"@emotion/style": "^0.8.0",
|
|
52
52
|
"@emotion/styled": "^11.6.0",
|
|
53
|
-
"@etsoo/appscript": "^1.2.
|
|
53
|
+
"@etsoo/appscript": "^1.2.29",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.0",
|
|
55
|
-
"@etsoo/shared": "^1.1.
|
|
55
|
+
"@etsoo/shared": "^1.1.9",
|
|
56
56
|
"@mui/icons-material": "^5.3.1",
|
|
57
57
|
"@mui/material": "^5.3.1",
|
|
58
58
|
"@reach/router": "^1.3.4",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"@types/react-test-renderer": "^17.0.1",
|
|
86
86
|
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
|
87
87
|
"@typescript-eslint/parser": "^5.10.1",
|
|
88
|
-
"eslint": "^8.
|
|
88
|
+
"eslint": "^8.8.0",
|
|
89
89
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
90
90
|
"eslint-plugin-import": "^2.25.4",
|
|
91
91
|
"eslint-plugin-react": "^7.28.0",
|
package/src/app/Utils.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* React utils
|
|
3
5
|
*/
|
|
@@ -19,6 +21,36 @@ export namespace Utils {
|
|
|
19
21
|
return String(value);
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Is safe click
|
|
26
|
+
* @param event Mouse event
|
|
27
|
+
* @returns Result
|
|
28
|
+
*/
|
|
29
|
+
export function isSafeClick(event: React.MouseEvent<HTMLElement>) {
|
|
30
|
+
// No target
|
|
31
|
+
// HTMLElement <= Element, SVGElement <= Element
|
|
32
|
+
if (!(event.target instanceof Element)) return true;
|
|
33
|
+
|
|
34
|
+
// Outside of the currentTarget
|
|
35
|
+
let target: Element | null = event.target;
|
|
36
|
+
if (!event.currentTarget.contains(target)) return false;
|
|
37
|
+
|
|
38
|
+
while (target != null && target != event.currentTarget) {
|
|
39
|
+
const nodeName = target.nodeName.toUpperCase();
|
|
40
|
+
if (
|
|
41
|
+
nodeName === 'INPUT' ||
|
|
42
|
+
nodeName === 'BUTTON' ||
|
|
43
|
+
nodeName === 'A' ||
|
|
44
|
+
target.hasAttribute('onClick')
|
|
45
|
+
)
|
|
46
|
+
return false;
|
|
47
|
+
|
|
48
|
+
target = target.parentElement;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
22
54
|
/**
|
|
23
55
|
* Trigger input change event
|
|
24
56
|
* @param input Form input
|
package/src/mu/ComboBox.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { Keyboard } from '@etsoo/shared';
|
|
|
3
3
|
import { Autocomplete, AutocompleteRenderInputParams } from '@mui/material';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { Utils } from '../app/Utils';
|
|
6
|
+
import { Utils as SharedUtils } from '@etsoo/shared';
|
|
6
7
|
import { AutocompleteExtendedProps } from './AutocompleteExtendedProps';
|
|
7
8
|
import { InputField } from './InputField';
|
|
8
9
|
import { SearchField } from './SearchField';
|
|
@@ -20,7 +21,7 @@ export interface ComboBoxProps<T extends {}>
|
|
|
20
21
|
/**
|
|
21
22
|
* Label field
|
|
22
23
|
*/
|
|
23
|
-
labelField?: keyof T;
|
|
24
|
+
labelField?: string & keyof T;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Load data callback
|
|
@@ -155,6 +156,9 @@ export function ComboBox<T extends {} = IdLabelDto>(props: ComboBoxProps<T>) {
|
|
|
155
156
|
loadData().then((result) => {
|
|
156
157
|
if (result == null || !isMounted.current) return;
|
|
157
158
|
if (onLoadData) onLoadData(result);
|
|
159
|
+
if (search) {
|
|
160
|
+
SharedUtils.addBlankItem(result, idField, labelField);
|
|
161
|
+
}
|
|
158
162
|
setOptions(result);
|
|
159
163
|
});
|
|
160
164
|
}
|
package/src/mu/DataGridEx.tsx
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from '../components/ScrollerGrid';
|
|
26
26
|
import useCombinedRefs from '../uses/useCombinedRefs';
|
|
27
27
|
import { DataGridRenderers } from './DataGridRenderers';
|
|
28
|
+
import { MouseEventWithDataHandler } from './MUGlobal';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Footer item renderer props
|
|
@@ -98,6 +99,16 @@ export interface DataGridExProps<T extends Record<string, any>>
|
|
|
98
99
|
*/
|
|
99
100
|
hoverColor?: string;
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Double click handler
|
|
104
|
+
*/
|
|
105
|
+
onDoubleClick?: MouseEventWithDataHandler<T>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Click handler
|
|
109
|
+
*/
|
|
110
|
+
onClick?: MouseEventWithDataHandler<T>;
|
|
111
|
+
|
|
101
112
|
/**
|
|
102
113
|
* Selectable to support hover over and out effect and row clickable
|
|
103
114
|
* @default true
|
|
@@ -351,6 +362,8 @@ export function DataGridEx<T extends Record<string, any>>(
|
|
|
351
362
|
hoverColor = '#f6f9fb',
|
|
352
363
|
idField = 'id',
|
|
353
364
|
mRef = React.createRef(),
|
|
365
|
+
onClick,
|
|
366
|
+
onDoubleClick,
|
|
354
367
|
selectable = true,
|
|
355
368
|
selectedColor = '#edf4fb',
|
|
356
369
|
width,
|
|
@@ -567,6 +580,12 @@ export function DataGridEx<T extends Record<string, any>>(
|
|
|
567
580
|
}
|
|
568
581
|
onMouseOver={selectable ? handleMouseOver : undefined}
|
|
569
582
|
onMouseOut={selectable ? handleMouseOut : undefined}
|
|
583
|
+
onClick={(event) =>
|
|
584
|
+
onClick && data != null && onClick(event, data)
|
|
585
|
+
}
|
|
586
|
+
onDoubleClick={(event) =>
|
|
587
|
+
onDoubleClick && data != null && onDoubleClick(event, data)
|
|
588
|
+
}
|
|
570
589
|
>
|
|
571
590
|
<Box {...cellProps} onMouseEnter={handleMouseEnter}>
|
|
572
591
|
{child}
|
package/src/mu/DnDList.tsx
CHANGED
package/src/mu/ItemList.tsx
CHANGED
|
@@ -22,12 +22,12 @@ export interface ItemListProps<T extends Record<string, unknown>> {
|
|
|
22
22
|
/**
|
|
23
23
|
* Id field name
|
|
24
24
|
*/
|
|
25
|
-
idField?: keyof T;
|
|
25
|
+
idField?: string & keyof T;
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Label field name or callback
|
|
29
29
|
*/
|
|
30
|
-
labelField?: keyof T | ((item: T) => string);
|
|
30
|
+
labelField?: (string & keyof T) | ((item: T) => string);
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Button icon
|
package/src/mu/MUGlobal.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { NumberUtils } from '@etsoo/shared';
|
|
2
2
|
import { Breakpoint, Theme } from '@mui/material';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Mouse event handler with data
|
|
6
|
+
*/
|
|
7
|
+
export type MouseEventWithDataHandler<T> = (
|
|
8
|
+
event: React.MouseEvent<HTMLDivElement>,
|
|
9
|
+
data: T
|
|
10
|
+
) => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* MUGlobal for global configurations
|
|
14
|
+
*/
|
|
4
15
|
export class MUGlobal {
|
|
5
16
|
/**
|
|
6
17
|
* Search field shrink
|
package/src/mu/OptionGroup.tsx
CHANGED
|
@@ -32,7 +32,7 @@ export interface OptionGroupProps<
|
|
|
32
32
|
/**
|
|
33
33
|
* Id field, default is id
|
|
34
34
|
*/
|
|
35
|
-
idField?: keyof T;
|
|
35
|
+
idField?: string & keyof T;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Label
|
|
@@ -42,7 +42,7 @@ export interface OptionGroupProps<
|
|
|
42
42
|
/**
|
|
43
43
|
* Label field, default is label
|
|
44
44
|
*/
|
|
45
|
-
labelField?: keyof T;
|
|
45
|
+
labelField?: string & keyof T;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Multiple choose item
|
|
@@ -3,6 +3,7 @@ import { Box, Stack, SxProps, Theme } from '@mui/material';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { ListChildComponentProps } from 'react-window';
|
|
5
5
|
import { Labels } from '../app/Labels';
|
|
6
|
+
import { Utils } from '../app/Utils';
|
|
6
7
|
import { GridColumn } from '../components/GridColumn';
|
|
7
8
|
import {
|
|
8
9
|
GridDataGet,
|
|
@@ -125,6 +126,11 @@ export interface ResponsibleContainerProps<
|
|
|
125
126
|
*/
|
|
126
127
|
pullToRefresh?: boolean;
|
|
127
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Quick action for double click or click under mobile
|
|
131
|
+
*/
|
|
132
|
+
quickAction?: (data: T) => void;
|
|
133
|
+
|
|
128
134
|
/**
|
|
129
135
|
* Size ready to read miliseconds span
|
|
130
136
|
*/
|
|
@@ -173,6 +179,7 @@ export function ResponsibleContainer<
|
|
|
173
179
|
mRef,
|
|
174
180
|
paddings = MUGlobal.pagePaddings,
|
|
175
181
|
pullToRefresh = true,
|
|
182
|
+
quickAction,
|
|
176
183
|
sizeReadyMiliseconds = 0,
|
|
177
184
|
...rest
|
|
178
185
|
} = props;
|
|
@@ -285,6 +292,9 @@ export function ResponsibleContainer<
|
|
|
285
292
|
width={rect.width}
|
|
286
293
|
loadData={localLoadData}
|
|
287
294
|
mRef={mRefs}
|
|
295
|
+
onDoubleClick={(_, data) =>
|
|
296
|
+
quickAction && quickAction(data)
|
|
297
|
+
}
|
|
288
298
|
outerRef={(element?: HTMLDivElement) => {
|
|
289
299
|
if (element != null && elementReady)
|
|
290
300
|
elementReady(element, true);
|
|
@@ -314,6 +324,11 @@ export function ResponsibleContainer<
|
|
|
314
324
|
height={heightLocal}
|
|
315
325
|
loadData={localLoadData}
|
|
316
326
|
mRef={mRefs}
|
|
327
|
+
onClick={(event, data) =>
|
|
328
|
+
quickAction &&
|
|
329
|
+
Utils.isSafeClick(event) &&
|
|
330
|
+
quickAction(data)
|
|
331
|
+
}
|
|
317
332
|
oRef={(element) => {
|
|
318
333
|
if (element != null && elementReady)
|
|
319
334
|
elementReady(element, false);
|
|
@@ -4,7 +4,7 @@ import { useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { ListChildComponentProps } from 'react-window';
|
|
6
6
|
import { ScrollerList, ScrollerListProps } from '../components/ScrollerList';
|
|
7
|
-
import { MUGlobal } from './MUGlobal';
|
|
7
|
+
import { MouseEventWithDataHandler, MUGlobal } from './MUGlobal';
|
|
8
8
|
|
|
9
9
|
// Scroll bar size
|
|
10
10
|
const scrollbarSize = 16;
|
|
@@ -127,7 +127,7 @@ export interface ScrollerListExProps<T>
|
|
|
127
127
|
/**
|
|
128
128
|
* Id field
|
|
129
129
|
*/
|
|
130
|
-
idField?: keyof T;
|
|
130
|
+
idField?: string & keyof T;
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Inner item renderer
|
|
@@ -146,6 +146,16 @@ export interface ScrollerListExProps<T>
|
|
|
146
146
|
*/
|
|
147
147
|
itemSize: ScrollerListExItemSize;
|
|
148
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Double click handler
|
|
151
|
+
*/
|
|
152
|
+
onDoubleClick?: MouseEventWithDataHandler<T>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Click handler
|
|
156
|
+
*/
|
|
157
|
+
onClick?: MouseEventWithDataHandler<T>;
|
|
158
|
+
|
|
149
159
|
/**
|
|
150
160
|
* On items select change
|
|
151
161
|
*/
|
|
@@ -175,6 +185,16 @@ interface defaultItemRendererProps<T> extends ListChildComponentProps<T> {
|
|
|
175
185
|
*/
|
|
176
186
|
itemHeight: number;
|
|
177
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Double click handler
|
|
190
|
+
*/
|
|
191
|
+
onDoubleClick?: MouseEventWithDataHandler<T>;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Click handler
|
|
195
|
+
*/
|
|
196
|
+
onClick?: MouseEventWithDataHandler<T>;
|
|
197
|
+
|
|
178
198
|
/**
|
|
179
199
|
* Item space
|
|
180
200
|
*/
|
|
@@ -200,6 +220,8 @@ function defaultItemRenderer<T>({
|
|
|
200
220
|
selected,
|
|
201
221
|
style,
|
|
202
222
|
itemHeight,
|
|
223
|
+
onClick,
|
|
224
|
+
onDoubleClick,
|
|
203
225
|
space,
|
|
204
226
|
margins
|
|
205
227
|
}: defaultItemRendererProps<T>) {
|
|
@@ -223,6 +245,10 @@ function defaultItemRenderer<T>({
|
|
|
223
245
|
className={rowClass}
|
|
224
246
|
style={style}
|
|
225
247
|
onMouseDown={(event) => onMouseDown(event.currentTarget, data)}
|
|
248
|
+
onClick={(event) => onClick && onClick(event, data)}
|
|
249
|
+
onDoubleClick={(event) =>
|
|
250
|
+
onDoubleClick && onDoubleClick(event, data)
|
|
251
|
+
}
|
|
226
252
|
>
|
|
227
253
|
{child}
|
|
228
254
|
</div>
|
|
@@ -282,12 +308,16 @@ export function ScrollerListEx<T extends Record<string, unknown>>(
|
|
|
282
308
|
itemHeight,
|
|
283
309
|
innerItemRenderer,
|
|
284
310
|
onMouseDown,
|
|
311
|
+
onClick,
|
|
312
|
+
onDoubleClick,
|
|
285
313
|
space,
|
|
286
314
|
margins,
|
|
287
315
|
selected: isSelected(itemProps.data),
|
|
288
316
|
...itemProps
|
|
289
317
|
});
|
|
290
318
|
},
|
|
319
|
+
onClick,
|
|
320
|
+
onDoubleClick,
|
|
291
321
|
onSelectChange,
|
|
292
322
|
selectedColor = '#edf4fb',
|
|
293
323
|
...rest
|
package/src/mu/SelectBool.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IdLabelDto } from '@etsoo/appscript';
|
|
2
|
+
import { Utils } from '@etsoo/shared';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import { globalApp } from '..';
|
|
4
5
|
import { SelectEx, SelectExProps } from './SelectEx';
|
|
@@ -24,7 +25,7 @@ export function SelectBool(props: SelectBoolProps) {
|
|
|
24
25
|
{ id: 'true', label: globalApp.get('yes')! }
|
|
25
26
|
];
|
|
26
27
|
|
|
27
|
-
if (search)
|
|
28
|
+
if (search) Utils.addBlankItem(options);
|
|
28
29
|
|
|
29
30
|
// Layout
|
|
30
31
|
return <SelectEx options={options} search={search} {...rest} />;
|
package/src/mu/SelectEx.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import React from 'react';
|
|
|
14
14
|
import { MUGlobal } from './MUGlobal';
|
|
15
15
|
import { IdLabelDto } from '@etsoo/appscript';
|
|
16
16
|
import { ListItemRightIcon } from './ListItemRightIcon';
|
|
17
|
+
import { Utils } from '@etsoo/shared';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Extended select component props
|
|
@@ -23,7 +24,7 @@ export interface SelectExProps<T extends {}>
|
|
|
23
24
|
/**
|
|
24
25
|
* Id field, default is id
|
|
25
26
|
*/
|
|
26
|
-
idField?: keyof T;
|
|
27
|
+
idField?: string & keyof T;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Item icon renderer
|
|
@@ -33,7 +34,7 @@ export interface SelectExProps<T extends {}>
|
|
|
33
34
|
/**
|
|
34
35
|
* Label field, default is label
|
|
35
36
|
*/
|
|
36
|
-
labelField?: ((option: T) => string) | keyof T;
|
|
37
|
+
labelField?: ((option: T) => string) | (string & keyof T);
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* Load data callback
|
|
@@ -143,6 +144,18 @@ export function SelectEx<T extends {} = IdLabelDto>(props: SelectExProps<T>) {
|
|
|
143
144
|
}
|
|
144
145
|
};
|
|
145
146
|
|
|
147
|
+
// Get option id
|
|
148
|
+
const getId = (option: T) => {
|
|
149
|
+
return Reflect.get(option, idField);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Get option label
|
|
153
|
+
const getLabel = (option: T) => {
|
|
154
|
+
return typeof labelField === 'function'
|
|
155
|
+
? labelField(option)
|
|
156
|
+
: Reflect.get(option, labelField);
|
|
157
|
+
};
|
|
158
|
+
|
|
146
159
|
// Refs
|
|
147
160
|
const divRef = React.useRef<HTMLDivElement>();
|
|
148
161
|
|
|
@@ -159,6 +172,9 @@ export function SelectEx<T extends {} = IdLabelDto>(props: SelectExProps<T>) {
|
|
|
159
172
|
loadData().then((result) => {
|
|
160
173
|
if (result == null || !isMounted.current) return;
|
|
161
174
|
if (onLoadData) onLoadData(result);
|
|
175
|
+
if (search) {
|
|
176
|
+
Utils.addBlankItem(result, idField, labelField);
|
|
177
|
+
}
|
|
162
178
|
setOptions(result);
|
|
163
179
|
});
|
|
164
180
|
}
|
|
@@ -198,26 +214,24 @@ export function SelectEx<T extends {} = IdLabelDto>(props: SelectExProps<T>) {
|
|
|
198
214
|
renderValue={(selected) => {
|
|
199
215
|
// The text shows up
|
|
200
216
|
return localOptions
|
|
201
|
-
.filter((option
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
217
|
+
.filter((option) => {
|
|
218
|
+
const id = getId(option);
|
|
219
|
+
return Array.isArray(selected)
|
|
220
|
+
? selected.indexOf(id) !== -1
|
|
221
|
+
: selected === id;
|
|
222
|
+
})
|
|
223
|
+
.map((option) => getLabel(option))
|
|
207
224
|
.join(', ');
|
|
208
225
|
}}
|
|
209
226
|
sx={{ minWidth: '150px' }}
|
|
210
227
|
{...rest}
|
|
211
228
|
>
|
|
212
|
-
{localOptions.map((option
|
|
229
|
+
{localOptions.map((option) => {
|
|
213
230
|
// Option id
|
|
214
|
-
const id = option
|
|
231
|
+
const id = getId(option);
|
|
215
232
|
|
|
216
233
|
// Option label
|
|
217
|
-
const label =
|
|
218
|
-
typeof labelField === 'function'
|
|
219
|
-
? labelField(option)
|
|
220
|
-
: option[labelField];
|
|
234
|
+
const label = getLabel(option);
|
|
221
235
|
|
|
222
236
|
// Option
|
|
223
237
|
return (
|
package/src/mu/TableEx.tsx
CHANGED
|
@@ -68,7 +68,7 @@ export interface TableExProps<T extends Record<string, any>>
|
|
|
68
68
|
/**
|
|
69
69
|
* Id field
|
|
70
70
|
*/
|
|
71
|
-
idField?: keyof T;
|
|
71
|
+
idField?: string & keyof T;
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Max height
|
|
@@ -107,7 +107,7 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
107
107
|
// Theme
|
|
108
108
|
const theme = useTheme();
|
|
109
109
|
|
|
110
|
-
type keyType = keyof T;
|
|
110
|
+
type keyType = string & keyof T;
|
|
111
111
|
|
|
112
112
|
// Destruct
|
|
113
113
|
const {
|
package/src/mu/Tiplist.tsx
CHANGED
|
@@ -83,6 +83,12 @@ export function Tiplist<T extends {} = IdLabelDto>(props: TiplistProps<T>) {
|
|
|
83
83
|
}
|
|
84
84
|
);
|
|
85
85
|
|
|
86
|
+
// Input value
|
|
87
|
+
const inputValue = React.useMemo(
|
|
88
|
+
() => states.value && Reflect.get(states.value, idField),
|
|
89
|
+
[states.value]
|
|
90
|
+
);
|
|
91
|
+
|
|
86
92
|
React.useEffect(() => {
|
|
87
93
|
if (localValue != null) stateUpdate({ value: localValue });
|
|
88
94
|
}, [localValue]);
|
|
@@ -219,9 +225,7 @@ export function Tiplist<T extends {} = IdLabelDto>(props: TiplistProps<T>) {
|
|
|
219
225
|
type="text"
|
|
220
226
|
style={{ display: 'none' }}
|
|
221
227
|
name={name}
|
|
222
|
-
value={
|
|
223
|
-
(states.value && Reflect.get(states.value, idField)) ?? ''
|
|
224
|
-
}
|
|
228
|
+
value={inputValue ?? ''}
|
|
225
229
|
readOnly
|
|
226
230
|
onChange={inputOnChange}
|
|
227
231
|
/>
|