trithuc-mvc-react 3.3.6 → 3.3.7

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.
@@ -27,25 +27,47 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
27
27
  onClick={() => {
28
28
  handleExportExcel(tableName, data);
29
29
  }}
30
- sx={{ ml: 0.5, mr: 0.5, minWidth: isSmallScreen ? "73px" : "100px", textTransform: "none" }}
30
+ sx={{
31
+ ml: 0.5,
32
+ mr: 0.5
33
+ // fontSize: {
34
+ // xs: "0.75rem",
35
+ // sm: "0.85rem",
36
+ // md: "0.95rem"
37
+ // },
38
+ // textAlign: "center",
39
+ // textTransform: "none"
40
+ }}
31
41
  >
32
42
  <Typography
33
43
  noWrap
34
44
  sx={{
35
45
  width: "100%",
36
46
  fontSize: {
37
- xs: "0.75rem",
38
- sm: "0.875rem",
39
- md: "1rem"
47
+ xs: "0.65rem",
48
+ sm: "0.75rem",
49
+ md: "0.95rem"
40
50
  },
41
51
  textAlign: "center",
42
- textTransform: "none" // ✅ Đảm bảo chữ gốc giữ nguyên
52
+ textTransform: "none"
43
53
  }}
44
54
  >
45
55
  Excel
46
56
  </Typography>
47
57
  </Button>
48
58
  ) : (
59
+ // <Tooltip title="Excel">
60
+ // <IconButton
61
+ // variant="outlined"
62
+ // color="primary"
63
+ // size={size}
64
+ // onClick={() => {
65
+ // handleExportExcel(tableName, data);
66
+ // }}
67
+ // >
68
+ // <Download fontSize="inherit" />
69
+ // </IconButton>
70
+ // </Tooltip>
49
71
  <Button
50
72
  size={size}
51
73
  variant="outlined"
@@ -53,7 +75,17 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
53
75
  onClick={() => {
54
76
  handleExportExcel(tableName, data);
55
77
  }}
56
- sx={{ ml: 0.5, mr: 0.5, minWidth: isSmallScreen ? "73px" : "100px", textTransform: "none" }}
78
+ sx={{
79
+ ml: 0.5,
80
+ mr: 0.5,
81
+ fontSize: {
82
+ xs: "0.75rem",
83
+ sm: "0.875rem",
84
+ md: "1rem"
85
+ },
86
+ textAlign: "center",
87
+ textTransform: "none"
88
+ }}
57
89
  >
58
90
  <Typography
59
91
  noWrap
@@ -1,6 +1,6 @@
1
1
  import { Box, Button, Card, Grid, IconButton, Stack, Tooltip, Typography, useMediaQuery, useTheme } from "@mui/material";
2
2
 
3
- import { useEffect, useMemo, useState } from "react";
3
+ import { useEffect, useLayoutEffect, useMemo, useState } from "react";
4
4
 
5
5
  import { Add, Refresh } from "@mui/icons-material";
6
6
  import BarChartIcon from "@mui/icons-material/BarChart";
@@ -20,6 +20,7 @@ import ExportExcelButton from "./ExportExcelButton";
20
20
  import { FilterGod } from "./FilterGod";
21
21
  import HuongDanButton from "./HuongDanButton";
22
22
  import { useRef } from "react";
23
+ import { debounce } from "lodash";
23
24
 
24
25
  DataManagement.propTypes = {
25
26
  columns: PropTypes.array,
@@ -134,6 +135,7 @@ function DataManagement({
134
135
 
135
136
  const theme = useTheme();
136
137
  const upXL = useMediaQuery(theme.breakpoints.up("xl"));
138
+ const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
137
139
  const elementSize = useMemo(() => {
138
140
  const elementSize = upXL ? "medium" : "small";
139
141
  return elementSize;
@@ -211,239 +213,215 @@ function DataManagement({
211
213
  bieuDo,
212
214
  disableHead
213
215
  ]);
214
- const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); // Xác định màn hình nhỏ
215
216
  const methods = useForm({ defaultValues: getDefaultValues(filters) });
216
217
  const { reset, setValue } = methods;
217
218
 
219
+ const titleRef = useRef(null);
218
220
  const titleText = title || "";
219
221
  const [titleWidth, setTitleWidth] = useState(0);
220
- const [minWidthValue, setMinWidthValue] = useState(0); // Thêm state cho minWidthValue
222
+ const [minWidthValue, setMinWidthValue] = useState(0);
221
223
 
222
- // Đo chiều rộng của titleText khi render lần đầu
223
- useEffect(() => {
224
- const titleElement = document.createElement("span");
225
- titleElement.style.font = "normal 14px Arial"; // Đảm bảo phông chữ và kích thước giống như bạn dùng trong UI
226
- titleElement.style.visibility = "hidden"; // Ẩn element tạm thời
227
- titleElement.textContent = titleText;
224
+ const leftBoxRef = useRef(null);
225
+ const rightBoxRef = useRef(null);
226
+ const [isFullRow, setIsFullRow] = useState(false);
228
227
 
229
- document.body.appendChild(titleElement);
230
- setTitleWidth(titleElement.offsetWidth); // Lấy chiều rộng của title
231
- document.body.removeChild(titleElement); // Xóa element sau khi đo xong
232
- }, [titleText]);
228
+ const checkLayout = () => {
229
+ if (titleRef.current && rightBoxRef.current) {
230
+ const titleTextWidth = titleRef.current.scrollWidth;
233
231
 
234
- // Khi titleWidth thay đổi, tính toán minWidthValue
235
- useEffect(() => {
236
- if (titleWidth > 0) {
237
- const calculatedMinWidth = Math.max(titleWidth, 150); // Đảm bảo minWidthValue không nhỏ hơn 150px
238
- setMinWidthValue(calculatedMinWidth);
239
- }
240
- }, [titleWidth]); // Phụ thuộc vào titleWidth để đảm bảo minWidthValue được cập nhật khi chiều rộng title thay đổi
232
+ // Tính tổng độ rộng thật của tất cả các phần tử bên trong rightBox
233
+ const rightChildren = Array.from(rightBoxRef.current.children);
234
+ const totalRightWidth = rightChildren.reduce((acc, child) => {
235
+ const childWidth = child.scrollWidth || 0;
236
+ const style = window.getComputedStyle(child);
237
+ const marginLeft = parseFloat(style.marginLeft) || 0;
238
+ const marginRight = parseFloat(style.marginRight) || 0;
239
+ return acc + childWidth + marginLeft + marginRight;
240
+ }, 0);
241
241
 
242
- const rightBoxRef = useRef(null); // Tham chiếu đến khối phải
243
- const [rightBoxMinWidth, setRightBoxMinWidth] = useState(0); // Lưu chiều rộng của khối phải
242
+ const containerWidth = window.innerWidth - 32; // padding 16px trái/phải
243
+ const spacing = 16; // khoảng cách giữa 2 Box
244
244
 
245
- // Đo chiều rộng của khối phải khi component render và khi thay đổi kích thước cửa sổ
246
- useEffect(() => {
247
- // Hàm xử lý resize
248
- const handleResize = () => {
249
- const currentWindowWidth = window.innerWidth;
250
-
251
- // Đo chiều rộng của khối phải trực tiếp từ ref
252
- const rightBoxWidth = rightBoxRef.current ? rightBoxRef.current.offsetWidth : 0;
253
-
254
- // Cập nhật chiều rộng khối phải (rightBoxMinWidth) tùy theo kích thước của cửa sổ và khối
255
- if (currentWindowWidth - (minWidthValue + 30) < rightBoxWidth) {
256
- // console.log(
257
- // `currentWindowWidth - minWidthValue 1: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
258
- // );
259
- setRightBoxMinWidth(currentWindowWidth - 30); // Điều chỉnh chiều rộng khối phải nếu không đủ không gian
260
- } else {
261
- // console.log(
262
- // `currentWindowWidth - minWidthValue 2: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
263
- // );
264
- setRightBoxMinWidth(rightBoxWidth); // Nếu đủ không gian, dùng chiều rộng khối phải
265
- }
266
- };
245
+ const totalContentWidth = titleTextWidth + totalRightWidth + spacing;
267
246
 
268
- // Lần render đầu tiên, tính toán chiều rộng khối phải
269
- const initialResize = () => {
270
- const rightBoxWidth = rightBoxRef.current ? rightBoxRef.current.offsetWidth : 0;
271
- const currentWindowWidth = window.innerWidth;
272
-
273
- // Cập nhật giá trị minWidth ngay khi render lần đầu
274
- if (currentWindowWidth - (minWidthValue + 30) < rightBoxWidth) {
275
- // console.log(
276
- // `currentWindowWidth - minWidthValue 3: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
277
- // );
278
- setRightBoxMinWidth(currentWindowWidth - 30); // Điều chỉnh chiều rộng khối phải nếu không đủ không gian
279
- } else {
280
- // console.log(
281
- // `currentWindowWidth - minWidthValue 4: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
282
- // );
283
- setRightBoxMinWidth(rightBoxWidth); // Nếu đủ không gian, dùng chiều rộng của khối phải
284
- }
285
- };
286
-
287
- // Gọi hàm tính toán ngay khi render lần đầu
288
- if (minWidthValue > 0) {
289
- initialResize();
247
+ setIsFullRow(totalContentWidth > containerWidth);
290
248
  }
249
+ };
250
+
251
+ useEffect(() => {
252
+ const observer = new ResizeObserver(() => {
253
+ checkLayout();
254
+ });
291
255
 
292
- // Lắng nghe sự kiện thay đổi kích thước cửa sổ
293
- window.addEventListener("resize", handleResize);
256
+ if (titleRef.current) observer.observe(titleRef.current);
257
+ if (rightBoxRef.current) observer.observe(rightBoxRef.current);
258
+
259
+ window.addEventListener("resize", checkLayout);
294
260
 
295
- // Dọn dẹp sự kiện khi component bị hủy
296
261
  return () => {
297
- window.removeEventListener("resize", handleResize);
262
+ observer.disconnect();
263
+ window.removeEventListener("resize", checkLayout);
298
264
  };
299
- }, [minWidthValue]); // Phụ thuộc vào minWidthValue để tính toán khi nó được cập nhật
300
-
301
- // In các giá trị trong console để kiểm tra
302
- // useEffect(() => {
303
- // console.log("minWidthValue:", minWidthValue);
304
- // console.log("rightBoxMinWidth:", rightBoxMinWidth);
305
- // }, [rightBoxMinWidth]);
265
+ }, []);
306
266
 
307
267
  return (
308
268
  <>
309
269
  <DataTableContext.Provider value={values}>
310
270
  <FormProvider {...methods}>
311
271
  {!disableHead && (
312
- <Stack
313
- direction="row"
314
- sx={{
315
- mb: upXL ? 1 : 1,
316
- flexWrap: "wrap",
317
- alignItems: "center",
318
- gap: 1,
319
- ...slotProps?.header?.sx
320
- }}
321
- >
322
- {/* Khối trái */}
323
- <Box
324
- sx={{
325
- flexGrow: 1,
326
- Width: `${minWidthValue}px`,
327
- textAlign: "left",
328
- whiteSpace: "normal",
329
- wordBreak: "break-word"
330
- }}
331
- >
332
- <Typography variant={isSmallScreen ? "body2" : "subtitle1"}>{titleText}</Typography>
333
- </Box>
334
-
335
- {/* Khối phải */}
336
- <Box
337
- ref={rightBoxRef} // Tham chiếu đến phần tử này
272
+ <Card sx={{ p: 0, mb: 0 }}>
273
+ <Stack
274
+ direction={isFullRow ? "column" : "row"}
338
275
  sx={{
339
- display: "flex",
340
- flexWrap: "nowrap",
341
- justifyContent: "flex-end",
342
- alignItems: "center",
343
- flexShrink: 0,
344
- // minWidth: "300px", // Đảm bảo chiều rộng tối thiểu
345
- width: rightBoxMinWidth ? `${rightBoxMinWidth}px` : "auto" // Sử dụng chiều rộng đo được
276
+ // mb: upXL ? 1 : 1,
277
+ flexWrap: isFullRow ? "nowrap" : "wrap",
278
+ alignItems: isFullRow ? "stretch" : "center",
279
+ gap: 1,
280
+ ...slotProps?.header?.sx
346
281
  }}
347
282
  >
348
- {(viewMode === "thongke" || viewMode === "bieudo") && (
349
- <Tooltip title="Danh sách dữ liệu">
350
- <IconButton size={elementSize} color="primary" onClick={() => setViewMode("table")} sx={{ ml: 0.5, mr: 0.5 }}>
351
- <TableChartIcon fontSize="inherit" />
352
- </IconButton>
353
- </Tooltip>
354
- )}
355
-
356
- {canThongKe && (
357
- <Tooltip title="Thống kê">
283
+ {/* Left side */}
284
+ <Box
285
+ ref={leftBoxRef}
286
+ sx={{
287
+ flexGrow: isFullRow ? 0 : 1,
288
+ width: isFullRow ? "100%" : `${minWidthValue}px`,
289
+ textAlign: "left",
290
+ whiteSpace: "normal",
291
+ wordBreak: "break-word"
292
+ }}
293
+ >
294
+ <Typography
295
+ ref={titleRef}
296
+ noWrap
297
+ sx={{
298
+ width: "100%",
299
+ fontSize: {
300
+ xs: "0.85rem",
301
+ sm: "0.95rem",
302
+ md: "1rem"
303
+ },
304
+ textAlign: "left",
305
+ textTransform: "none"
306
+ }}
307
+ >
308
+ {titleText}
309
+ </Typography>
310
+ </Box>
311
+
312
+ {/* Right side */}
313
+ <Box
314
+ ref={rightBoxRef}
315
+ sx={{
316
+ display: "flex",
317
+ flexWrap: "wrap",
318
+ justifyContent: isFullRow ? "flex-end" : "flex-end",
319
+ alignItems: "center",
320
+ width: isFullRow ? "100%" : "auto",
321
+ flexShrink: 0
322
+ }}
323
+ >
324
+ {(viewMode === "thongke" || viewMode === "bieudo") && (
325
+ <Tooltip title="Danh sách dữ liệu">
326
+ <IconButton
327
+ size={elementSize}
328
+ color="primary"
329
+ onClick={() => setViewMode("table")}
330
+ sx={{ ml: 0.5, mr: 0.5 }}
331
+ >
332
+ <TableChartIcon fontSize="inherit" />
333
+ </IconButton>
334
+ </Tooltip>
335
+ )}
336
+
337
+ {canThongKe && (
338
+ <Tooltip title="Thống kê">
339
+ <IconButton
340
+ size={elementSize}
341
+ color={viewMode === "thongke" ? "secondary" : "primary"}
342
+ onClick={() => setViewMode("thongke")}
343
+ sx={{ ml: 0.5, mr: 0.5 }}
344
+ >
345
+ <BarChartIcon fontSize="inherit" />
346
+ </IconButton>
347
+ </Tooltip>
348
+ )}
349
+
350
+ {canBieuDo && (
351
+ <Tooltip title="Biểu đồ">
352
+ <IconButton
353
+ size={elementSize}
354
+ color={viewMode === "bieudo" ? "secondary" : "primary"}
355
+ onClick={() => setViewMode("bieudo")}
356
+ sx={{ ml: 0.5, mr: 0.5 }}
357
+ >
358
+ <PieChartIcon fontSize="inherit" />
359
+ </IconButton>
360
+ </Tooltip>
361
+ )}
362
+
363
+ <HuongDanButton tableName={tableName} size={elementSize} />
364
+ <Tooltip title="Làm mới">
358
365
  <IconButton
366
+ variant="outlined"
367
+ color="primary"
359
368
  size={elementSize}
360
- color={viewMode === "thongke" ? "secondary" : "primary"}
361
- onClick={() => setViewMode("thongke")}
362
- sx={{ ml: 0.5, mr: 0.5 }}
369
+ onClick={() => {
370
+ setDataSearch({ ...defaults });
371
+ [...filters].forEach((filter) => {
372
+ filter?.onChange?.();
373
+ });
374
+ reset();
375
+ setValue("Search");
376
+ }}
363
377
  >
364
- <BarChartIcon fontSize="inherit" />
378
+ <Refresh fontSize="inherit" />
365
379
  </IconButton>
366
380
  </Tooltip>
367
- )}
368
381
 
369
- {canBieuDo && (
370
- <Tooltip title="Biểu đồ">
371
- <IconButton
372
- size={elementSize}
373
- color={viewMode === "bieudo" ? "secondary" : "primary"}
374
- onClick={() => setViewMode("bieudo")}
375
- sx={{ ml: 0.5, mr: 0.5 }}
376
- >
377
- <PieChartIcon fontSize="inherit" />
378
- </IconButton>
379
- </Tooltip>
380
- )}
382
+ {titleButtons?.map((button, index) => (
383
+ <div key={index}>{button}</div>
384
+ ))}
381
385
 
382
- <HuongDanButton tableName={tableName} size={elementSize} />
383
- <Tooltip title="Làm mới">
384
- <IconButton
385
- variant="outlined"
386
- color="primary"
387
- size={elementSize}
388
- onClick={() => {
389
- setDataSearch({ ...defaults });
390
- [...filters].forEach((filter) => {
391
- filter?.onChange?.();
392
- });
393
- reset();
394
- setValue("Search");
395
- }}
396
- >
397
- <Refresh fontSize="inherit" />
398
- </IconButton>
399
- </Tooltip>
400
-
401
- {titleButtons?.map((button, index) => (
402
- <div key={index}>{button}</div>
403
- ))}
404
-
405
- <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
406
-
407
- {canCreate && !disableAdd && (
408
- <Button
409
- size={elementSize}
410
- variant="contained"
411
- startIcon={<Add fontSize="inherit" />}
412
- onClick={(e) => {
413
- if (!disableEditor) {
414
- setOpenEditorDialog(true);
415
- setSelectedEditItem(null);
416
- }
417
- onAddClick(e);
418
- }}
419
- sx={{
420
- ml: 0.5,
421
- mr: 0.5,
422
- minWidth: isSmallScreen ? "100px" : "130px",
423
- textTransform: "none" // ✅ Không viết hoa tự động
424
- }}
425
- >
426
- <Typography
427
- noWrap
386
+ <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
387
+
388
+ {canCreate && !disableAdd && (
389
+ <Button
390
+ size={elementSize}
391
+ variant="contained"
392
+ startIcon={<Add fontSize="inherit" />}
393
+ onClick={(e) => {
394
+ if (!disableEditor) {
395
+ setOpenEditorDialog(true);
396
+ setSelectedEditItem(null);
397
+ }
398
+ onAddClick(e);
399
+ }}
428
400
  sx={{
429
- width: "100%",
430
- fontSize: {
431
- xs: "0.75rem",
432
- sm: "0.875rem",
433
- md: "1rem"
434
- },
435
- textAlign: "center",
436
- textTransform: "none" // ✅ Đảm bảo chữ gốc giữ nguyên
401
+ ml: 0.5
437
402
  }}
438
403
  >
439
- {titleAddButton}
440
- </Typography>
441
- </Button>
442
- )}
443
- </Box>
444
- </Stack>
404
+ <Typography
405
+ noWrap
406
+ sx={{
407
+ width: "100%",
408
+ fontSize: {
409
+ xs: "0.65rem",
410
+ sm: "0.75rem",
411
+ md: "0.85rem"
412
+ },
413
+ textAlign: "center",
414
+ textTransform: "none"
415
+ }}
416
+ >
417
+ {titleAddButton}
418
+ </Typography>
419
+ </Button>
420
+ )}
421
+ </Box>
422
+ </Stack>
423
+ </Card>
445
424
  )}
446
-
447
425
  <Card className="custom-card-DataManagement">
448
426
  {tabPanel}
449
427
  {viewMode === "table" && (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "3.3.6",
3
+ "version": "3.3.7",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"