@etsoo/materialui 1.6.12 → 1.6.13

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.
@@ -1,6 +1,8 @@
1
1
  import { ButtonProps } from "@mui/material/Button";
2
2
  import { IdType } from "@etsoo/shared";
3
+ import { GridSize } from "@mui/material/Grid";
3
4
  import { DnDSortableListProps } from "./DnDSortableList";
5
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
4
6
  type DnDItemType = {
5
7
  id: IdType;
6
8
  };
@@ -64,6 +66,10 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<ButtonProps,
64
66
  * The field is required or not
65
67
  */
66
68
  required?: boolean;
69
+ /**
70
+ * Item size
71
+ */
72
+ itemSize?: ResponsiveStyleValue<GridSize>;
67
73
  /**
68
74
  * Value
69
75
  */
@@ -22,7 +22,7 @@ const FormLabel_1 = __importDefault(require("@mui/material/FormLabel"));
22
22
  const DnDSortableList_1 = require("./DnDSortableList");
23
23
  function ButtonPopupList(props) {
24
24
  // Destruct
25
- const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
25
+ const { addSplitter = /\s*[,;]\s*/, value = [], items, itemSize = { xs: 12, md: 6, lx: 4 }, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
26
26
  // Methods
27
27
  const dndRef = react_1.default.createRef();
28
28
  // Refs
@@ -45,7 +45,7 @@ function ButtonPopupList(props) {
45
45
  .filter((item) => tempSelectedIds.current.includes(item.id))
46
46
  .map((item) => item.id);
47
47
  onValueChange(ids);
48
- }, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, children: (0, jsx_runtime_1.jsx)(DragIndicator_1.default, {}) }), (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
48
+ }, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, children: (0, jsx_runtime_1.jsx)(DragIndicator_1.default, {}) }), (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
49
49
  const checked = e.target.checked;
50
50
  const newIds = [
51
51
  ...selectedIds.toggleItem(item.id, checked)
@@ -81,7 +81,7 @@ function ButtonPopupCheckbox(props) {
81
81
  // App
82
82
  const app = (0, ReactApp_1.useRequiredAppContext)();
83
83
  // Destruct
84
- const { addSplitter, value = [], inputName, label, labelEnd, labelFormatter = (data) => {
84
+ const { addSplitter, value = [], inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
85
85
  if (typeof labelField === "function")
86
86
  return labelField(data);
87
87
  if (labelField in data) {
@@ -130,7 +130,7 @@ function ButtonPopupCheckbox(props) {
130
130
  setSelectedIds(ids);
131
131
  onValueChange?.(ids);
132
132
  },
133
- inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
133
+ inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, itemSize: itemSize, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
134
134
  tempSelectedIds.current = ids;
135
135
  } })),
136
136
  fullScreen: app.smDown
@@ -1,5 +1,7 @@
1
1
  import { ButtonProps } from "@mui/material/Button";
2
2
  import { DataTypes, IdType } from "@etsoo/shared";
3
+ import { GridSize } from "@mui/material/Grid";
4
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
3
5
  type DnDItemType = {
4
6
  id: IdType;
5
7
  };
@@ -63,6 +65,10 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<ButtonProps, "ch
63
65
  * The field is required or not
64
66
  */
65
67
  required?: boolean;
68
+ /**
69
+ * Item size
70
+ */
71
+ itemSize?: ResponsiveStyleValue<GridSize>;
66
72
  /**
67
73
  * Value
68
74
  */
@@ -21,7 +21,7 @@ const FlexBox_1 = require("./FlexBox");
21
21
  const ReactApp_1 = require("./app/ReactApp");
22
22
  function ButtonPopupList(props) {
23
23
  // Destruct
24
- const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, onAdd, onValueChange } = props;
24
+ const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, itemSize = { xs: 12, md: 6, lx: 4 }, onAdd, onValueChange } = props;
25
25
  // Ref
26
26
  const inputRef = react_1.default.useRef(null);
27
27
  // State
@@ -38,7 +38,7 @@ function ButtonPopupList(props) {
38
38
  : undefined;
39
39
  setCurrentValue(value);
40
40
  onValueChange(value);
41
- }, children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: items.map((item) => ((0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Radio_1.default, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), onAdd && ((0, jsx_runtime_1.jsxs)(FlexBox_1.HBox, { gap: 1, children: [(0, jsx_runtime_1.jsx)(TextField_1.default, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), (0, jsx_runtime_1.jsx)(Button_1.default, { sx: { width: "120px" }, variant: "contained", startIcon: (0, jsx_runtime_1.jsx)(Add_1.default, {}), size: "small", onClick: async () => {
41
+ }, children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: items.map((item) => ((0, jsx_runtime_1.jsx)(Grid_1.default, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Radio_1.default, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), onAdd && ((0, jsx_runtime_1.jsxs)(FlexBox_1.HBox, { gap: 1, children: [(0, jsx_runtime_1.jsx)(TextField_1.default, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), (0, jsx_runtime_1.jsx)(Button_1.default, { sx: { width: "120px" }, variant: "contained", startIcon: (0, jsx_runtime_1.jsx)(Add_1.default, {}), size: "small", onClick: async () => {
42
42
  if (inputRef.current == null)
43
43
  return;
44
44
  const input = inputRef.current.value.trim();
@@ -66,7 +66,7 @@ function ButtonPopupRadio(props) {
66
66
  // App
67
67
  const app = (0, ReactApp_1.useRequiredAppContext)();
68
68
  // Destruct
69
- const { addSplitter, inputName, label, labelEnd, labelFormatter = (data) => {
69
+ const { addSplitter, inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
70
70
  if (labelField in data) {
71
71
  return data[labelField];
72
72
  }
@@ -115,7 +115,7 @@ function ButtonPopupRadio(props) {
115
115
  setCurrentValue(id);
116
116
  onValueChange?.(id);
117
117
  },
118
- inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
118
+ inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, itemSize: itemSize, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
119
119
  tempSelectedId.current = id;
120
120
  } })),
121
121
  fullScreen: app.smDown
@@ -91,7 +91,7 @@ export type DnDSortableListProps<D extends object, E extends React.ElementType =
91
91
  /**
92
92
  * Drag start handler
93
93
  */
94
- onDragStart?: (items: D[], source: Parameters<DragDropEvents["dragstart"]>[0]) => void;
94
+ onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
95
95
  /**
96
96
  * Drag end handler
97
97
  */
@@ -123,7 +123,28 @@ function DnDSortableList(props) {
123
123
  }
124
124
  };
125
125
  }, [items, labelFn, changeItems]);
126
- return ((0, jsx_runtime_1.jsx)(react_2.DragDropProvider, { onDragStart: (source) => onDragStart?.(items, source), onDragEnd: (source, target) => onDragEnd?.(items, source, target), children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
126
+ function handleDragEnd(...args) {
127
+ // Event
128
+ const event = args[0];
129
+ // Cancelled
130
+ if (event.canceled)
131
+ return;
132
+ if ((0, sortable_1.isSortableOperation)(event.operation) && event.operation.source) {
133
+ const { initialIndex, index } = event.operation.source;
134
+ if (initialIndex === index)
135
+ return;
136
+ // Clone
137
+ const newItems = [...items];
138
+ // Removed item
139
+ const [removed] = newItems.splice(initialIndex, 1);
140
+ // Insert to the destination index
141
+ newItems.splice(index, 0, removed);
142
+ changeItems(newItems);
143
+ // Drag end handler
144
+ onDragEnd?.(newItems, ...args);
145
+ }
146
+ }
147
+ return ((0, jsx_runtime_1.jsx)(react_2.DragDropProvider, { onDragStart: (event) => onDragStart?.(items, event), onDragEnd: (event, manager) => handleDragEnd(event, manager), children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
127
148
  const id = idFn(item);
128
149
  return ((0, jsx_runtime_1.jsx)(SortableItem, { id: id, index: index, data: item, itemRenderer: itemRenderer, itemStyle: itemStyle }, id));
129
150
  }) }) }));
@@ -1,6 +1,8 @@
1
1
  import { ButtonProps } from "@mui/material/Button";
2
2
  import { IdType } from "@etsoo/shared";
3
+ import { GridSize } from "@mui/material/Grid";
3
4
  import { DnDSortableListProps } from "./DnDSortableList";
5
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
4
6
  type DnDItemType = {
5
7
  id: IdType;
6
8
  };
@@ -64,6 +66,10 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<ButtonProps,
64
66
  * The field is required or not
65
67
  */
66
68
  required?: boolean;
69
+ /**
70
+ * Item size
71
+ */
72
+ itemSize?: ResponsiveStyleValue<GridSize>;
67
73
  /**
68
74
  * Value
69
75
  */
@@ -16,7 +16,7 @@ import FormLabel from "@mui/material/FormLabel";
16
16
  import { DnDSortableList } from "./DnDSortableList";
17
17
  function ButtonPopupList(props) {
18
18
  // Destruct
19
- const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
19
+ const { addSplitter = /\s*[,;]\s*/, value = [], items, itemSize = { xs: 12, md: 6, lx: 4 }, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
20
20
  // Methods
21
21
  const dndRef = React.createRef();
22
22
  // Refs
@@ -39,7 +39,7 @@ function ButtonPopupList(props) {
39
39
  .filter((item) => tempSelectedIds.current.includes(item.id))
40
40
  .map((item) => item.id);
41
41
  onValueChange(ids);
42
- }, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => (_jsxs(Grid, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [_jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, children: _jsx(DragIndicatorIcon, {}) }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
42
+ }, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => (_jsxs(Grid, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [_jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, children: _jsx(DragIndicatorIcon, {}) }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
43
43
  const checked = e.target.checked;
44
44
  const newIds = [
45
45
  ...selectedIds.toggleItem(item.id, checked)
@@ -75,7 +75,7 @@ export function ButtonPopupCheckbox(props) {
75
75
  // App
76
76
  const app = useRequiredAppContext();
77
77
  // Destruct
78
- const { addSplitter, value = [], inputName, label, labelEnd, labelFormatter = (data) => {
78
+ const { addSplitter, value = [], inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
79
79
  if (typeof labelField === "function")
80
80
  return labelField(data);
81
81
  if (labelField in data) {
@@ -124,7 +124,7 @@ export function ButtonPopupCheckbox(props) {
124
124
  setSelectedIds(ids);
125
125
  onValueChange?.(ids);
126
126
  },
127
- inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
127
+ inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, itemSize: itemSize, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
128
128
  tempSelectedIds.current = ids;
129
129
  } })),
130
130
  fullScreen: app.smDown
@@ -1,5 +1,7 @@
1
1
  import { ButtonProps } from "@mui/material/Button";
2
2
  import { DataTypes, IdType } from "@etsoo/shared";
3
+ import { GridSize } from "@mui/material/Grid";
4
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
3
5
  type DnDItemType = {
4
6
  id: IdType;
5
7
  };
@@ -63,6 +65,10 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<ButtonProps, "ch
63
65
  * The field is required or not
64
66
  */
65
67
  required?: boolean;
68
+ /**
69
+ * Item size
70
+ */
71
+ itemSize?: ResponsiveStyleValue<GridSize>;
66
72
  /**
67
73
  * Value
68
74
  */
@@ -15,7 +15,7 @@ import { HBox, VBox } from "./FlexBox";
15
15
  import { useRequiredAppContext } from "./app/ReactApp";
16
16
  function ButtonPopupList(props) {
17
17
  // Destruct
18
- const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, onAdd, onValueChange } = props;
18
+ const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, itemSize = { xs: 12, md: 6, lx: 4 }, onAdd, onValueChange } = props;
19
19
  // Ref
20
20
  const inputRef = React.useRef(null);
21
21
  // State
@@ -32,7 +32,7 @@ function ButtonPopupList(props) {
32
32
  : undefined;
33
33
  setCurrentValue(value);
34
34
  onValueChange(value);
35
- }, children: _jsx(Grid, { container: true, spacing: 0, children: items.map((item) => (_jsx(Grid, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: _jsx(FormControlLabel, { control: _jsx(Radio, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), onAdd && (_jsxs(HBox, { gap: 1, children: [_jsx(TextField, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), _jsx(Button, { sx: { width: "120px" }, variant: "contained", startIcon: _jsx(AddIcon, {}), size: "small", onClick: async () => {
35
+ }, children: _jsx(Grid, { container: true, spacing: 0, children: items.map((item) => (_jsx(Grid, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: _jsx(FormControlLabel, { control: _jsx(Radio, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), onAdd && (_jsxs(HBox, { gap: 1, children: [_jsx(TextField, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), _jsx(Button, { sx: { width: "120px" }, variant: "contained", startIcon: _jsx(AddIcon, {}), size: "small", onClick: async () => {
36
36
  if (inputRef.current == null)
37
37
  return;
38
38
  const input = inputRef.current.value.trim();
@@ -60,7 +60,7 @@ export function ButtonPopupRadio(props) {
60
60
  // App
61
61
  const app = useRequiredAppContext();
62
62
  // Destruct
63
- const { addSplitter, inputName, label, labelEnd, labelFormatter = (data) => {
63
+ const { addSplitter, inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
64
64
  if (labelField in data) {
65
65
  return data[labelField];
66
66
  }
@@ -109,7 +109,7 @@ export function ButtonPopupRadio(props) {
109
109
  setCurrentValue(id);
110
110
  onValueChange?.(id);
111
111
  },
112
- inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
112
+ inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, itemSize: itemSize, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
113
113
  tempSelectedId.current = id;
114
114
  } })),
115
115
  fullScreen: app.smDown
@@ -91,7 +91,7 @@ export type DnDSortableListProps<D extends object, E extends React.ElementType =
91
91
  /**
92
92
  * Drag start handler
93
93
  */
94
- onDragStart?: (items: D[], source: Parameters<DragDropEvents["dragstart"]>[0]) => void;
94
+ onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
95
95
  /**
96
96
  * Drag end handler
97
97
  */
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { useTheme } from "@mui/material/styles";
4
- import { useSortable } from "@dnd-kit/react/sortable";
4
+ import { isSortableOperation, useSortable } from "@dnd-kit/react/sortable";
5
5
  import { DragDropProvider } from "@dnd-kit/react";
6
6
  /**
7
7
  * DnD sortable item default style
@@ -115,7 +115,28 @@ export function DnDSortableList(props) {
115
115
  }
116
116
  };
117
117
  }, [items, labelFn, changeItems]);
118
- return (_jsx(DragDropProvider, { onDragStart: (source) => onDragStart?.(items, source), onDragEnd: (source, target) => onDragEnd?.(items, source, target), children: _jsx(Component, { ...componentProps, children: items.map((item, index) => {
118
+ function handleDragEnd(...args) {
119
+ // Event
120
+ const event = args[0];
121
+ // Cancelled
122
+ if (event.canceled)
123
+ return;
124
+ if (isSortableOperation(event.operation) && event.operation.source) {
125
+ const { initialIndex, index } = event.operation.source;
126
+ if (initialIndex === index)
127
+ return;
128
+ // Clone
129
+ const newItems = [...items];
130
+ // Removed item
131
+ const [removed] = newItems.splice(initialIndex, 1);
132
+ // Insert to the destination index
133
+ newItems.splice(index, 0, removed);
134
+ changeItems(newItems);
135
+ // Drag end handler
136
+ onDragEnd?.(newItems, ...args);
137
+ }
138
+ }
139
+ return (_jsx(DragDropProvider, { onDragStart: (event) => onDragStart?.(items, event), onDragEnd: (event, manager) => handleDragEnd(event, manager), children: _jsx(Component, { ...componentProps, children: items.map((item, index) => {
119
140
  const id = idFn(item);
120
141
  return (_jsx(SortableItem, { id: id, index: index, data: item, itemRenderer: itemRenderer, itemStyle: itemStyle }, id));
121
142
  }) }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.6.12",
3
+ "version": "1.6.13",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -2,7 +2,7 @@ import Button, { ButtonProps } from "@mui/material/Button";
2
2
  import Chip from "@mui/material/Chip";
3
3
  import React from "react";
4
4
  import { DataTypes, IdType } from "@etsoo/shared";
5
- import Grid from "@mui/material/Grid";
5
+ import Grid, { GridSize } from "@mui/material/Grid";
6
6
  import Typography from "@mui/material/Typography";
7
7
  import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
8
8
  import AddIcon from "@mui/icons-material/Add";
@@ -18,6 +18,7 @@ import {
18
18
  DnDSortableListProps,
19
19
  DnDSortableListRef
20
20
  } from "./DnDSortableList";
21
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
21
22
 
22
23
  type DnDItemType = {
23
24
  id: IdType;
@@ -99,6 +100,11 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
99
100
  */
100
101
  required?: boolean;
101
102
 
103
+ /**
104
+ * Item size
105
+ */
106
+ itemSize?: ResponsiveStyleValue<GridSize>;
107
+
102
108
  /**
103
109
  * Value
104
110
  */
@@ -107,7 +113,7 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
107
113
 
108
114
  type ButtonPopupListProps<D extends DnDItemType> = Pick<
109
115
  ButtonPopupCheckboxProps<D>,
110
- "addSplitter" | "labelField" | "labels" | "onAdd" | "value"
116
+ "addSplitter" | "labelField" | "labels" | "onAdd" | "value" | "itemSize"
111
117
  > &
112
118
  Required<Pick<ButtonPopupCheckboxProps<D>, "labelFormatter">> & {
113
119
  /**
@@ -130,6 +136,7 @@ function ButtonPopupList<D extends DnDItemType>(
130
136
  addSplitter = /\s*[,;]\s*/,
131
137
  value = [],
132
138
  items,
139
+ itemSize = { xs: 12, md: 6, lx: 4 },
133
140
  labelField,
134
141
  labelFormatter,
135
142
  labels,
@@ -180,7 +187,7 @@ function ButtonPopupList<D extends DnDItemType>(
180
187
  { sortable: { index }, ref, handleRef }
181
188
  ) => (
182
189
  <Grid
183
- size={{ xs: 12, md: 6, lg: 4 }}
190
+ size={itemSize}
184
191
  display="flex"
185
192
  justifyContent="flex-start"
186
193
  alignItems="center"
@@ -281,6 +288,7 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
281
288
  addSplitter,
282
289
  value = [],
283
290
  inputName,
291
+ itemSize,
284
292
  label,
285
293
  labelEnd,
286
294
  labelFormatter = (data) => {
@@ -353,6 +361,7 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
353
361
  items={items}
354
362
  labelFormatter={labelFormatter}
355
363
  labelField={labelField}
364
+ itemSize={itemSize}
356
365
  labels={labels}
357
366
  onAdd={onAdd}
358
367
  onValueChange={(ids) => {
@@ -2,7 +2,7 @@ import Button, { ButtonProps } from "@mui/material/Button";
2
2
  import Chip from "@mui/material/Chip";
3
3
  import React from "react";
4
4
  import { DataTypes, IdType, NumberUtils } from "@etsoo/shared";
5
- import Grid from "@mui/material/Grid";
5
+ import Grid, { GridSize } from "@mui/material/Grid";
6
6
  import Typography from "@mui/material/Typography";
7
7
  import AddIcon from "@mui/icons-material/Add";
8
8
  import FormControlLabel from "@mui/material/FormControlLabel";
@@ -12,6 +12,7 @@ import FormLabel from "@mui/material/FormLabel";
12
12
  import RadioGroup from "@mui/material/RadioGroup";
13
13
  import { HBox, VBox } from "./FlexBox";
14
14
  import { useRequiredAppContext } from "./app/ReactApp";
15
+ import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
15
16
 
16
17
  type DnDItemType = {
17
18
  id: IdType;
@@ -93,6 +94,11 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<
93
94
  */
94
95
  required?: boolean;
95
96
 
97
+ /**
98
+ * Item size
99
+ */
100
+ itemSize?: ResponsiveStyleValue<GridSize>;
101
+
96
102
  /**
97
103
  * Value
98
104
  */
@@ -101,7 +107,7 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<
101
107
 
102
108
  type ButtonPopupListProps<D extends DnDItemType> = Pick<
103
109
  ButtonPopupRadioProps<D>,
104
- "addSplitter" | "labels" | "onAdd" | "value"
110
+ "addSplitter" | "labels" | "onAdd" | "value" | "itemSize"
105
111
  > &
106
112
  Required<Pick<ButtonPopupRadioProps<D>, "labelFormatter">> & {
107
113
  /**
@@ -126,6 +132,7 @@ function ButtonPopupList<D extends DnDItemType>(
126
132
  items,
127
133
  labelFormatter,
128
134
  labels,
135
+ itemSize = { xs: 12, md: 6, lx: 4 },
129
136
  onAdd,
130
137
  onValueChange
131
138
  } = props;
@@ -159,7 +166,7 @@ function ButtonPopupList<D extends DnDItemType>(
159
166
  <Grid container spacing={0}>
160
167
  {items.map((item) => (
161
168
  <Grid
162
- size={{ xs: 12, md: 6, lg: 4 }}
169
+ size={itemSize}
163
170
  display="flex"
164
171
  justifyContent="flex-start"
165
172
  alignItems="center"
@@ -233,6 +240,7 @@ export function ButtonPopupRadio<D extends DnDItemType>(
233
240
  const {
234
241
  addSplitter,
235
242
  inputName,
243
+ itemSize,
236
244
  label,
237
245
  labelEnd,
238
246
  labelFormatter = (data) => {
@@ -305,6 +313,7 @@ export function ButtonPopupRadio<D extends DnDItemType>(
305
313
  addSplitter={addSplitter}
306
314
  value={currentValue}
307
315
  items={items}
316
+ itemSize={itemSize}
308
317
  labelFormatter={labelFormatter}
309
318
  labels={labels}
310
319
  onAdd={onAdd}
@@ -1,7 +1,7 @@
1
1
  import { DataTypes, IdType } from "@etsoo/shared";
2
2
  import React from "react";
3
3
  import { CSSProperties, Theme, useTheme } from "@mui/material/styles";
4
- import { useSortable } from "@dnd-kit/react/sortable";
4
+ import { isSortableOperation, useSortable } from "@dnd-kit/react/sortable";
5
5
  import { DragDropEvents, DragDropProvider } from "@dnd-kit/react";
6
6
 
7
7
  /**
@@ -126,7 +126,7 @@ export type DnDSortableListProps<
126
126
  */
127
127
  onDragStart?: (
128
128
  items: D[],
129
- source: Parameters<DragDropEvents["dragstart"]>[0]
129
+ event: Parameters<DragDropEvents["dragstart"]>[0]
130
130
  ) => void;
131
131
 
132
132
  /**
@@ -293,10 +293,37 @@ export function DnDSortableList<
293
293
  };
294
294
  }, [items, labelFn, changeItems]);
295
295
 
296
+ function handleDragEnd(...args: Parameters<DragDropEvents["dragend"]>) {
297
+ // Event
298
+ const event = args[0];
299
+
300
+ // Cancelled
301
+ if (event.canceled) return;
302
+
303
+ if (isSortableOperation(event.operation) && event.operation.source) {
304
+ const { initialIndex, index } = event.operation.source;
305
+ if (initialIndex === index) return;
306
+
307
+ // Clone
308
+ const newItems = [...items];
309
+
310
+ // Removed item
311
+ const [removed] = newItems.splice(initialIndex, 1);
312
+
313
+ // Insert to the destination index
314
+ newItems.splice(index, 0, removed);
315
+
316
+ changeItems(newItems);
317
+
318
+ // Drag end handler
319
+ onDragEnd?.(newItems, ...args);
320
+ }
321
+ }
322
+
296
323
  return (
297
324
  <DragDropProvider
298
- onDragStart={(source) => onDragStart?.(items, source)}
299
- onDragEnd={(source, target) => onDragEnd?.(items, source, target)}
325
+ onDragStart={(event) => onDragStart?.(items, event)}
326
+ onDragEnd={(event, manager) => handleDragEnd(event, manager)}
300
327
  >
301
328
  <Component {...componentProps}>
302
329
  {items.map((item, index) => {