@asteby/metacore-runtime-react 18.10.0 → 18.10.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 18.10.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 530ad31: The totals footer is now pinned to the bottom of the table box even with few
8
+ rows (the table fills its container height and a spacer row absorbs the slack),
9
+ instead of floating right under the last row.
10
+
11
+ ## 18.10.1
12
+
13
+ ### Patch Changes
14
+
15
+ - ea5e587: The dynamic table totals footer is now sticky (pinned to the bottom of the
16
+ scroll area) so the column totals stay visible while scrolling the rows.
17
+
3
18
  ## 18.10.0
4
19
 
5
20
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,GACX,EAAE,iBAAiB,+BAm2BnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,GACX,EAAE,iBAAiB,+BAo3BnB"}
@@ -620,21 +620,22 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
620
620
  if (!metadata) {
621
621
  return _jsx("div", { className: "text-center text-muted-foreground py-8", children: "Error al cargar la configuraci\u00F3n de la tabla." });
622
622
  }
623
- return (_jsxs(OptionsContext.Provider, { value: { optionsMap }, children: [_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsx(DataTableToolbar, { table: table, searchPlaceholder: metadata.searchPlaceholder || 'Buscar...', filters: filters, activeFilters: dynamicFilters, onDynamicFilterChange: handleDynamicFilterChange, dateFilter: { value: dateRange, onChange: setDateRange, placeholder: 'Filtrar por fecha' }, perPageOptions: metadata.perPageOptions, onRefresh: handleRefresh, isLoading: loadingData, selectedCount: Object.keys(rowSelection).length, onBulkDelete: () => setShowBulkDeleteConfirm(true), extraActions: _jsxs(_Fragment, { children: [metadata.canExport && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setExportOpen(true), children: [_jsx(Download, { className: "h-4 w-4 mr-1" }), " Exportar"] })), metadata.canImport && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setImportOpen(true), children: [_jsx(Upload, { className: "h-4 w-4 mr-1" }), " Importar"] }))] }) }) }), _jsx("div", { className: 'hidden sm:block flex-1 min-h-0 overflow-auto border rounded-md bg-card', children: _jsxs(Table, { noWrapper: true, className: "min-w-max w-full", children: [_jsx(TableHeader, { className: 'sticky top-0 z-10', children: table.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: headerGroup.headers.map((header) => {
623
+ return (_jsxs(OptionsContext.Provider, { value: { optionsMap }, children: [_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsx(DataTableToolbar, { table: table, searchPlaceholder: metadata.searchPlaceholder || 'Buscar...', filters: filters, activeFilters: dynamicFilters, onDynamicFilterChange: handleDynamicFilterChange, dateFilter: { value: dateRange, onChange: setDateRange, placeholder: 'Filtrar por fecha' }, perPageOptions: metadata.perPageOptions, onRefresh: handleRefresh, isLoading: loadingData, selectedCount: Object.keys(rowSelection).length, onBulkDelete: () => setShowBulkDeleteConfirm(true), extraActions: _jsxs(_Fragment, { children: [metadata.canExport && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setExportOpen(true), children: [_jsx(Download, { className: "h-4 w-4 mr-1" }), " Exportar"] })), metadata.canImport && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setImportOpen(true), children: [_jsx(Upload, { className: "h-4 w-4 mr-1" }), " Importar"] }))] }) }) }), _jsx("div", { className: 'hidden sm:block flex-1 min-h-0 overflow-auto border rounded-md bg-card', children: _jsxs(Table, { noWrapper: true, className: cn('min-w-max w-full', aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && 'h-full'), children: [_jsx(TableHeader, { className: 'sticky top-0 z-10', children: table.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: headerGroup.headers.map((header) => {
624
624
  const isActionsColumn = header.id === 'actions';
625
625
  return (_jsx(TableHead, { colSpan: header.colSpan, style: header.column.columnDef.size ? { width: header.column.columnDef.size } : undefined, className: cn('bg-card border-b h-10', isActionsColumn && 'sticky right-0 z-20 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id));
626
- }) }, headerGroup.id))) }), _jsx(TableBody, { children: loadingData && data.length === 0 ? (_jsx(TableSkeleton, {})) : table.getRowModel().rows?.length ? (table.getRowModel().rows.map((row) => (_jsx(TableRow, { "data-state": row.getIsSelected() && 'selected', children: row.getVisibleCells().map((cell) => {
627
- const isActionsColumn = cell.column.id === 'actions';
628
- return (_jsx(TableCell, { style: cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined, className: cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
629
- }) }, row.id)))) : (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0', children: _jsxs("div", { className: "flex h-full py-12 flex-col items-center justify-center gap-2 text-muted-foreground", children: [_jsx("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-muted/50", children: _jsx(Inbox, { className: "h-10 w-10" }) }), _jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("h3", { className: "text-lg font-semibold text-foreground", children: "No se encontraron resultados" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No hay datos para mostrar en este momento." })] })] }) }) })) }), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableFooter, { children: _jsx(TableRow, { className: "hover:bg-transparent", children: table.getVisibleLeafColumns().map((leaf, idx) => {
626
+ }) }, headerGroup.id))) }), _jsx(TableBody, { children: loadingData && data.length === 0 ? (_jsx(TableSkeleton, {})) : table.getRowModel().rows?.length ? (_jsxs(_Fragment, { children: [table.getRowModel().rows.map((row) => (_jsx(TableRow, { "data-state": row.getIsSelected() && 'selected', children: row.getVisibleCells().map((cell) => {
627
+ const isActionsColumn = cell.column.id === 'actions';
628
+ return (_jsx(TableCell, { style: cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined, className: cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
629
+ }) }, row.id))), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableRow, { className: 'border-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0' }) }))] })) : (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0', children: _jsxs("div", { className: "flex h-full py-12 flex-col items-center justify-center gap-2 text-muted-foreground", children: [_jsx("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-muted/50", children: _jsx(Inbox, { className: "h-10 w-10" }) }), _jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("h3", { className: "text-lg font-semibold text-foreground", children: "No se encontraron resultados" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No hay datos para mostrar en este momento." })] })] }) }) })) }), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableFooter, { className: "bg-transparent", children: _jsx(TableRow, { className: "hover:bg-transparent", children: table.getVisibleLeafColumns().map((leaf, idx) => {
630
630
  const col = (metadata?.columns ?? []).find((c) => c.key === leaf.id);
631
631
  const isFirst = idx === 0;
632
+ const stickyBase = 'sticky bottom-0 z-10 border-t bg-background py-2 font-semibold';
632
633
  // Aggregate cell: render the SUM formatted like the body cell.
633
634
  if (col && aggregateOf(col)) {
634
- return (_jsx(TableCell, { className: "py-2 text-right font-semibold tabular-nums", children: formatAggregateTotal(col, footerTotals[leaf.id], currency, i18n.language) }, leaf.id));
635
+ return (_jsx(TableCell, { className: `${stickyBase} text-right tabular-nums`, children: formatAggregateTotal(col, footerTotals[leaf.id], currency, i18n.language) }, leaf.id));
635
636
  }
636
637
  // First non-aggregate column carries the "Total" label.
637
- return (_jsx(TableCell, { className: "py-2 font-semibold", children: isFirst ? t('common.total', 'Total') : '' }, leaf.id));
638
+ return (_jsx(TableCell, { className: stickyBase, children: isFirst ? t('common.total', 'Total') : '' }, leaf.id));
638
639
  }) }) }))] }) }), _jsx("div", { className: 'flex flex-1 min-h-0 flex-col gap-2 overflow-y-auto sm:hidden', children: loadingData && data.length === 0 ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("div", { className: 'rounded-lg border bg-card p-3', children: [_jsx(Skeleton, { className: 'h-4 w-24' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-40' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-32' })] }, i)))) : table.getRowModel().rows?.length ? (table.getRowModel().rows.map((row) => {
639
640
  const cells = row.getVisibleCells();
640
641
  const actionsCell = cells.find((c) => c.column.id === 'actions');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "18.10.0",
3
+ "version": "18.10.2",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -755,7 +755,7 @@ export function DynamicTable({
755
755
  a 7-column table forces a wide horizontal scroll there, so we
756
756
  render a card-per-row list instead (see MobileCards below). */}
757
757
  <div className='hidden sm:block flex-1 min-h-0 overflow-auto border rounded-md bg-card'>
758
- <Table noWrapper className="min-w-max w-full">
758
+ <Table noWrapper className={cn('min-w-max w-full', aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && 'h-full')}>
759
759
  <TableHeader className='sticky top-0 z-10'>
760
760
  {table.getHeaderGroups().map((headerGroup: HeaderGroup<any>) => (
761
761
  <TableRow key={headerGroup.id} className='border-b-0 hover:bg-transparent'>
@@ -779,7 +779,8 @@ export function DynamicTable({
779
779
  {loadingData && data.length === 0 ? (
780
780
  <TableSkeleton />
781
781
  ) : table.getRowModel().rows?.length ? (
782
- table.getRowModel().rows.map((row: Row<any>) => (
782
+ <>
783
+ {table.getRowModel().rows.map((row: Row<any>) => (
783
784
  <TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
784
785
  {row.getVisibleCells().map((cell: Cell<any, unknown>) => {
785
786
  const isActionsColumn = cell.column.id === 'actions'
@@ -794,7 +795,16 @@ export function DynamicTable({
794
795
  )
795
796
  })}
796
797
  </TableRow>
797
- ))
798
+ ))}
799
+ {/* Spacer row: absorbs the table's leftover height (table is
800
+ h-full when a footer shows) so the totals footer is pinned to
801
+ the bottom of the box even with only a few rows. */}
802
+ {aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (
803
+ <TableRow className='border-0 hover:bg-transparent'>
804
+ <TableCell colSpan={columns.length} className='h-full p-0' />
805
+ </TableRow>
806
+ )}
807
+ </>
798
808
  ) : (
799
809
  <TableRow className='border-b-0 hover:bg-transparent'>
800
810
  <TableCell colSpan={columns.length} className='h-full p-0'>
@@ -812,19 +822,26 @@ export function DynamicTable({
812
822
  )}
813
823
  </TableBody>
814
824
  {aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (
815
- <TableFooter>
825
+ // Sticky footer: the totals row stays pinned to the bottom of
826
+ // the scroll area instead of scrolling away with the rows. The
827
+ // sticky lives on the cells (a <tr> can't be position:sticky
828
+ // reliably); each carries an opaque bg + top border so the body
829
+ // scrolls underneath cleanly.
830
+ <TableFooter className="bg-transparent">
816
831
  <TableRow className="hover:bg-transparent">
817
832
  {table.getVisibleLeafColumns().map((leaf: any, idx: number) => {
818
833
  const col = (metadata?.columns ?? []).find(
819
834
  (c) => c.key === leaf.id,
820
835
  )
821
836
  const isFirst = idx === 0
837
+ const stickyBase =
838
+ 'sticky bottom-0 z-10 border-t bg-background py-2 font-semibold'
822
839
  // Aggregate cell: render the SUM formatted like the body cell.
823
840
  if (col && aggregateOf(col as any)) {
824
841
  return (
825
842
  <TableCell
826
843
  key={leaf.id}
827
- className="py-2 text-right font-semibold tabular-nums"
844
+ className={`${stickyBase} text-right tabular-nums`}
828
845
  >
829
846
  {formatAggregateTotal(
830
847
  col as any,
@@ -837,7 +854,7 @@ export function DynamicTable({
837
854
  }
838
855
  // First non-aggregate column carries the "Total" label.
839
856
  return (
840
- <TableCell key={leaf.id} className="py-2 font-semibold">
857
+ <TableCell key={leaf.id} className={stickyBase}>
841
858
  {isFirst ? t('common.total', 'Total') : ''}
842
859
  </TableCell>
843
860
  )