tp-react-elements 3.0.6 → 3.0.9

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,3 +1,3 @@
1
- var Month = {};
1
+ var DateInput = {};
2
2
 
3
- export { Month as __exports };
3
+ export { DateInput as __exports };
@@ -1,3 +1,3 @@
1
- var DateInput = {};
1
+ var InputRangeField = {};
2
2
 
3
- export { DateInput as __exports };
3
+ export { InputRangeField as __exports };
@@ -1,3 +1,3 @@
1
- var InputRangeField = {};
1
+ var Month = {};
2
2
 
3
- export { InputRangeField as __exports };
3
+ export { Month as __exports };
@@ -1,3 +1,3 @@
1
- var DefinedRange = {};
1
+ var DateRange = {};
2
2
 
3
- export { DefinedRange as __exports };
3
+ export { DateRange as __exports };
@@ -1,3 +1,3 @@
1
- var Calendar = {};
1
+ var DateRangePicker = {};
2
2
 
3
- export { Calendar as __exports };
3
+ export { DateRangePicker as __exports };
@@ -1,3 +1,3 @@
1
- var DateRangePicker = {};
1
+ var DefinedRange = {};
2
2
 
3
- export { DateRangePicker as __exports };
3
+ export { DefinedRange as __exports };
@@ -1,3 +1,3 @@
1
- var DateRange = {};
1
+ var Calendar = {};
2
2
 
3
- export { DateRange as __exports };
3
+ export { Calendar as __exports };
@@ -72,7 +72,10 @@ const MultiSelectAutocomplete = ({ props, variant, maxChips = 2, }) => {
72
72
  // ── Stable callbacks ───────────────────────────────────────────────────────
73
73
  const getOptionLabelCb = useCallback((option) => String(option.label), []);
74
74
  const isOptionEqualCb = useCallback((option, value) => String(option.value) === String(value.value), []);
75
- const renderOptionCb = useCallback((renderProps, option, state) => (jsxs("li", { ...renderProps, style: { fontSize: '11px' }, children: [jsx(Checkbox, { checked: state.selected, size: "small", sx: { mr: 1, p: '2px' } }), option.label] })), []);
75
+ const renderOptionCb = useCallback((renderProps, option, state) => {
76
+ const { key, ...otherProps } = renderProps;
77
+ return (jsxs("li", { ...otherProps, style: { fontSize: '11px' }, children: [jsx(Checkbox, { checked: state.selected, size: "small", sx: { mr: 1, p: '2px' } }), option.label] }, key));
78
+ }, []);
76
79
  const chipSx = useMemo(() => ({
77
80
  mr: 0.5,
78
81
  height: 20,
@@ -109,7 +112,7 @@ const MultiSelectAutocomplete = ({ props, variant, maxChips = 2, }) => {
109
112
  clearErrors?.(name);
110
113
  }, [setValue, clearErrors, name, onChangeFn]);
111
114
  // ── Render ─────────────────────────────────────────────────────────────────
112
- return (jsx(Controller, { control: control, name: name, render: ({ field: { ref, onBlur: fieldOnBlur } }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Autocomplete, { ref: ref, open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), multiple: true, disableCloseOnSelect: true, id: `${name}-autocomplete-multi`, options: options, value: selectedOptions, getOptionLabel: getOptionLabelCb, isOptionEqualToValue: isOptionEqualCb, getOptionDisabled: (option) => option.disable, onChange: handleChange, onBlur: (e) => {
115
+ return (jsx(Controller, { control: control, name: name, render: ({ field: { ref, onBlur: fieldOnBlur } }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Autocomplete, { ref: ref, fullWidth: true, open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), multiple: true, disableCloseOnSelect: true, id: `${name}-autocomplete-multi`, options: options, value: selectedOptions, getOptionLabel: getOptionLabelCb, isOptionEqualToValue: isOptionEqualCb, getOptionDisabled: (option) => option.disable, onChange: handleChange, onBlur: (e) => {
113
116
  fieldOnBlur();
114
117
  handleBlur(e);
115
118
  }, size: "small", disablePortal: false, autoHighlight: true, selectOnFocus: true, clearOnBlur: false, renderTags: (tagValue, getTagProps) => (jsx(Box, { ref: tagsContainerRef, sx: {
@@ -14,7 +14,7 @@ const SingleSelect = ({ props, variant }) => {
14
14
  const value = currentValue
15
15
  ? props.item.options?.find((item) => item.value === currentValue)
16
16
  : null;
17
- return (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Tooltip, { title: props.item.isTooltipEnable ? (value?.label || '') : '', placement: "bottom-start", children: jsxs(Box, { sx: { width: '100%', position: 'relative' }, children: [jsx(Autocomplete, { ...field, id: `${props.item.name}-autocomplete`, value: value, onChange: (_, newValue) => {
17
+ return (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Tooltip, { title: props.item.isTooltipEnable ? (value?.label || '') : '', placement: "bottom-start", children: jsxs(Box, { sx: { width: '100%', position: 'relative' }, children: [jsx(Autocomplete, { ...field, fullWidth: true, id: `${props.item.name}-autocomplete`, value: value, onChange: (_, newValue) => {
18
18
  props.setValue(props?.item?.name, newValue?.value);
19
19
  props?.item?.onChangeFn && props?.item?.onChangeFn(newValue?.value, newValue);
20
20
  props?.clearErrors && props?.clearErrors(props?.item?.name);
@@ -49,10 +49,14 @@ const SingleSelect = ({ props, variant }) => {
49
49
  fontSize: '11px',
50
50
  },
51
51
  },
52
- }, renderOption: (props, option) => (createElement("li", { ...props, value: option.value, style: {
53
- opacity: option.disable ? 0.5 : 1,
54
- pointerEvents: option.disable ? 'none' : 'auto',
55
- }, key: option.value }, option.label)), renderInput: (params) => (jsx(TextField, { ...params, placeholder: props.item.placeholder, error: isShowBorderError, label: variant !== 'standard'
52
+ }, renderOption: (props, option) => {
53
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
+ const { key, ...optionProps } = props;
55
+ return (createElement("li", { ...optionProps, value: option.value, style: {
56
+ opacity: option.disable ? 0.5 : 1,
57
+ pointerEvents: option.disable ? 'none' : 'auto',
58
+ }, key: option.value }, option.label));
59
+ }, renderInput: (params) => (jsx(TextField, { ...params, placeholder: props.item.placeholder, error: isShowBorderError, label: variant !== 'standard'
56
60
  ? `${props.item.label}${props.item.required ? ' *' : ''}`
57
61
  : '', inputProps: {
58
62
  ...params.inputProps,
@@ -197,7 +197,7 @@ const SingleSelectSearchApi = ({ props, variant, minSearchLength = 3, debounceMs
197
197
  return fetchError ?? 'No results found';
198
198
  }, [searchQuery, minSearchLength, fetchError]);
199
199
  // ── Render ────────────────────────────────────────────────────────────────
200
- return (jsx(Controller, { control: control, name: name, render: ({ field: { ref, onBlur } }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Autocomplete, { ref: ref, value: resolvedValue, inputValue: displayText, onInputChange: handleInputChange, onChange: handleChange, onBlur: onBlur, options: options, getOptionLabel: getOptionLabel, isOptionEqualToValue: isOptionEqualToValue,
200
+ return (jsx(Controller, { control: control, name: name, render: ({ field: { ref, onBlur } }) => (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(Autocomplete, { ref: ref, fullWidth: true, value: resolvedValue, inputValue: displayText, onInputChange: handleInputChange, onChange: handleChange, onBlur: onBlur, options: options, getOptionLabel: getOptionLabel, isOptionEqualToValue: isOptionEqualToValue,
201
201
  // Options come pre-filtered from the server — disable client-side filtering
202
202
  filterOptions: (x) => x, loading: isLoading, disabled: disable, size: "small", noOptionsText: noOptionsText, sx: {
203
203
  '& .MuiAutocomplete-input': {
@@ -269,7 +269,7 @@ const useResponsiveTier = (tabletBreakpoint, mobileBreakpoint, enabled) => {
269
269
  }, [tabletBreakpoint, mobileBreakpoint, enabled]);
270
270
  return enabled ? tier : 'desktop';
271
271
  };
272
- const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoading, canPrev, canNext, onFirst, onPrev, onNext, onLast, onPageSizeChange, pageSizeOptions, density, gridId, isStacked, }) => {
272
+ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoading, canPrev, canNext, onFirst, onPrev, onNext, onLast, onPageSizeChange, pageSizeOptions, density, gridId, isStacked, isClientFiltered, filteredRowCount, pageRowCount, }) => {
273
273
  const fs = density.footerFontSize;
274
274
  const selectH = density.footerMinHeight - 8;
275
275
  const startRow = totalRecords === 0 ? 0 : pageIndex * pageSize + 1;
@@ -292,7 +292,9 @@ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoa
292
292
  ? 'Loading…'
293
293
  : totalRecords === 0
294
294
  ? 'No records'
295
- : `${startRow.toLocaleString()}–${endRow.toLocaleString()} of ${totalRecords.toLocaleString()}` }), jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: isStacked ? 'center' : 'flex-start', gap: 0.25 }, children: [jsx(Tooltip, { title: "First page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_first_page_icon`, size: "small", onClick: onFirst, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(FirstPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Tooltip, { title: "Previous page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_prev_page_icon`, size: "small", onClick: onPrev, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(ChevronLeftIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Typography, { fontWeight: 600, sx: {
295
+ : isClientFiltered
296
+ ? `${filteredRowCount.toLocaleString()} of ${pageRowCount.toLocaleString()} rows match (${totalRecords.toLocaleString()} total)`
297
+ : `${startRow.toLocaleString()}–${endRow.toLocaleString()} of ${totalRecords.toLocaleString()}` }), jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: isStacked ? 'center' : 'flex-start', gap: 0.25 }, children: [jsx(Tooltip, { title: "First page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_first_page_icon`, size: "small", onClick: onFirst, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(FirstPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Tooltip, { title: "Previous page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_prev_page_icon`, size: "small", onClick: onPrev, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(ChevronLeftIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Typography, { fontWeight: 600, sx: {
296
298
  mx: 0.5,
297
299
  minWidth: density.footerMinHeight - 12,
298
300
  height: density.footerMinHeight - 12,
@@ -458,12 +460,17 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
458
460
  const allowGrow = layoutMode === 'grid' || layoutMode === 'semantic';
459
461
  return columns.map((col) => {
460
462
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
461
- const { filterType, options, getSearchText, ...mrtCol } = col;
463
+ const { filterType, options, getSearchText, exportExcel, ...mrtCol } = col;
462
464
  if (allowGrow && mrtCol.grow === undefined) {
463
465
  mrtCol.grow = true;
464
466
  }
465
467
  // Auto-inject filterFn only when the user hasn't already supplied one.
466
- if (!mrtCol.filterFn && filterType) {
468
+ // Note: `filterType` omitted defaults to the same plain text-input chip
469
+ // as `filterType: 'text'` (see `ColumnFilterType` doc comment), so the
470
+ // text-matching branch below must handle both, not just the explicit
471
+ // `'text'` value — only `enableColumnFilter: false` / `'number'` should
472
+ // fall through without an injected filterFn.
473
+ if (!mrtCol.filterFn) {
467
474
  if (filterType === 'date') {
468
475
  // ISO date strings: "2026-05-05T00:00:00" → startsWith "2026-05-05"
469
476
  mrtCol.filterFn = (row, columnId, filterValue) => filterValue
@@ -483,7 +490,16 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
483
490
  ? true
484
491
  : filterValue.includes(row.getValue(columnId) ?? '');
485
492
  }
486
- // 'text' and 'number' use MRT's built-in client-side filtering
493
+ else if (!filterType || filterType === 'text') {
494
+ // MRT's own default filterFn ('fuzzy', via match-sorter) is a
495
+ // non-contiguous subsequence match — e.g. "PH-1" would match like
496
+ // "%P%H%-%1%" instead of a plain substring. Use MRT's built-in
497
+ // 'contains' filterFn instead, matching Global Search's substring
498
+ // behavior (case-insensitive `.includes()`). Applies whether
499
+ // `filterType` is explicitly `'text'` or simply omitted.
500
+ mrtCol.filterFn = 'contains';
501
+ }
502
+ // 'number' uses MRT's built-in client-side filtering
487
503
  }
488
504
  return mrtCol;
489
505
  });
@@ -519,8 +535,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
519
535
  }, [searchTextGetters]);
520
536
  // Stable references for the two table options below — recreating either
521
537
  // per render would defeat the memoization on every other MRT callback
522
- // (see docs/arch-performance.mdx: all function/object props passed into
523
- // useMaterialReactTable must be memoized).
538
+ // (all function/object props passed into useMaterialReactTable must be memoized).
524
539
  const tpFilterFns = useMemo(() => ({ tpGlobalSearch: tpGlobalSearchFn }), [tpGlobalSearchFn]);
525
540
  const getColumnCanGlobalFilter = useCallback(() => true, []);
526
541
  // ── Derive column list for filter chip picker ──────────────────────────────
@@ -587,7 +602,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
587
602
  const exportRows = scope === 'filtered' && tableRef.current
588
603
  ? tableRef.current.getFilteredRowModel().rows.map((r) => r.original)
589
604
  : rows;
590
- const dataCols = columns.filter((c) => 'accessorKey' in c && c.accessorKey);
605
+ const dataCols = columns.filter((c) => 'accessorKey' in c && c.accessorKey && c.exportExcel !== false);
591
606
  const headerArgb = hexToArgb(exportHeaderBgColor ?? headerBgColor) ?? 'FF1565C0';
592
607
  const textArgb = hexToArgb(exportHeaderTextColor) ?? 'FFFFFFFF';
593
608
  const headerRow = dataCols.map((c) => c.header ?? '');
@@ -751,7 +766,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
751
766
  availableColumns, effectiveMaxVisibleFilters, densityTokens, handleToggleFilter,
752
767
  ]);
753
768
  const renderToolbarInternalActions = useCallback(({ table: t }) => (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [jsx(MRT_ToggleGlobalFilterButton, { table: t, id: `${resolvedGridId}_global_search_icon` }), jsx(MRT_ShowHideColumnsButton, { table: t, id: `${resolvedGridId}_columns_icon` }), showDensityToggle && (jsx(MRT_ToggleDensePaddingButton, { table: t, id: `${resolvedGridId}_density_icon` })), jsx(MRT_ToggleFullScreenButton, { table: t, id: `${resolvedGridId}_fullscreen_icon` })] })), [resolvedGridId, showDensityToggle]);
754
- const renderBottomToolbar = useCallback(({ table: t }) => (jsx(PaginationFooter, { pageIndex: t.getState().pagination.pageIndex, pageSize: t.getState().pagination.pageSize, totalRecords: totalRecords, isFetching: isFetching, isLoading: isLoading, canPrev: t.getCanPreviousPage() && !isFetching, canNext: t.getCanNextPage() && !isFetching, onFirst: () => t.firstPage(), onPrev: () => t.previousPage(), onNext: () => t.nextPage(), onLast: () => t.lastPage(), onPageSizeChange: (s) => t.setPageSize(s), pageSizeOptions: pageSizeOptions, density: densityTokens, gridId: resolvedGridId, isStacked: isMobileTier })), [totalRecords, isFetching, isLoading, pageSizeOptions, densityTokens, resolvedGridId, isMobileTier]);
769
+ const renderBottomToolbar = useCallback(({ table: t }) => (jsx(PaginationFooter, { pageIndex: t.getState().pagination.pageIndex, pageSize: t.getState().pagination.pageSize, totalRecords: totalRecords, isFetching: isFetching, isLoading: isLoading, canPrev: t.getCanPreviousPage() && !isFetching, canNext: t.getCanNextPage() && !isFetching, onFirst: () => t.firstPage(), onPrev: () => t.previousPage(), onNext: () => t.nextPage(), onLast: () => t.lastPage(), onPageSizeChange: (s) => t.setPageSize(s), pageSizeOptions: pageSizeOptions, density: densityTokens, gridId: resolvedGridId, isStacked: isMobileTier, isClientFiltered: isFiltered, filteredRowCount: t.getFilteredRowModel().rows.length, pageRowCount: rows.length })), [totalRecords, isFetching, isLoading, pageSizeOptions, densityTokens, resolvedGridId, isMobileTier, isFiltered, rows]);
755
770
  // ── Table instance ─────────────────────────────────────────────────────────
756
771
  const table = useMaterialReactTable({
757
772
  columns: processedColumns,
@@ -100,6 +100,25 @@ export type TPColumnDef<TData extends object = object, TValue = any> = MRT_Colum
100
100
  * the raw accessor value is matched by default in that case.
101
101
  */
102
102
  getSearchText?: (row: TData) => string | number | null | undefined;
103
+ /**
104
+ * Whether this column is included in the Excel (`.xlsx`) export.
105
+ *
106
+ * Set to `false` on columns that only render UI (e.g. edit/delete action
107
+ * icons) and have no meaningful exported value:
108
+ *
109
+ * ```ts
110
+ * {
111
+ * id: 'actions',
112
+ * header: 'Actions',
113
+ * Cell: ({ row }) => <IconButton onClick={() => onEdit(row.original)}><EditIcon /></IconButton>,
114
+ * exportExcel: false,
115
+ * }
116
+ * ```
117
+ *
118
+ * Default: `true` — every column with an `accessorKey` is exported unless
119
+ * explicitly opted out.
120
+ */
121
+ exportExcel?: boolean;
103
122
  };
104
123
  export interface InvoiceDetail {
105
124
  Data_from: string;
@@ -1,4 +1,4 @@
1
- import { __exports as Calendar } from '../../../../../_virtual/index.esm5.js';
1
+ import { __exports as Calendar } from '../../../../../_virtual/index.esm7.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireDayCell } from '../DayCell/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DateInput } from '../../../../../_virtual/index.esm13.js';
1
+ import { __exports as DateInput } from '../../../../../_virtual/index.esm12.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireClassnames } from '../../../../classnames/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DateRange } from '../../../../../_virtual/index.esm7.js';
1
+ import { __exports as DateRange } from '../../../../../_virtual/index.esm4.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireCalendar } from '../Calendar/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DateRangePicker } from '../../../../../_virtual/index.esm6.js';
1
+ import { __exports as DateRangePicker } from '../../../../../_virtual/index.esm5.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireDateRange } from '../DateRange/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DefinedRange } from '../../../../../_virtual/index.esm4.js';
1
+ import { __exports as DefinedRange } from '../../../../../_virtual/index.esm6.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireStyles } from '../../styles.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as InputRangeField } from '../../../../../_virtual/index.esm14.js';
1
+ import { __exports as InputRangeField } from '../../../../../_virtual/index.esm13.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
 
@@ -1,4 +1,4 @@
1
- import { __exports as Month } from '../../../../../_virtual/index.esm12.js';
1
+ import { __exports as Month } from '../../../../../_virtual/index.esm14.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireDayCell } from '../DayCell/index.esm.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tp-react-elements",
3
- "version": "3.0.6",
3
+ "version": "3.0.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "React form components library built with React Hook Form and Yup",