@linagora/twake-mui 13.2.0 → 14.0.0

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,3 +1,31 @@
1
+ # @linagora/twake-mui [14.0.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@13.2.1...@linagora/twake-mui@14.0.0) (2026-10-15)
2
+
3
+
4
+ ### Code Refactoring
5
+
6
+ * **twake-mui:** Remove secondarySort from VirtualizedTable ([f0d5632](https://github.com/linagora/twake-ui/commit/f0d5632c57346428f5075f7ea9e338128a597c48))
7
+
8
+
9
+ ### Features
10
+
11
+ * **twake-mui:** Add sortingMode to VirtualizedTable ([e747721](https://github.com/linagora/twake-ui/commit/e74772185ae3447d0fce935393119e785b3a3c3a))
12
+
13
+
14
+ ### BREAKING CHANGES
15
+
16
+ * **twake-mui:** VirtualizedTable no longer takes secondarySort. Sort
17
+ the rows, secondary order included, before passing them, and set
18
+ sortingMode="server" so that the table keeps their order.
19
+
20
+ Assisted-by: LLM
21
+
22
+ ## @linagora/twake-mui [13.2.1](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@13.2.0...@linagora/twake-mui@13.2.1) (2026-10-13)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **twake-mui:** Draw badge counts as a pill ([2cc8518](https://github.com/linagora/twake-ui/commit/2cc8518f927ddd2b571c455a6a0ddddf475151c9))
28
+
1
29
  # @linagora/twake-mui [13.2.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@13.1.0...@linagora/twake-mui@13.2.0) (2026-10-13)
2
30
 
3
31
 
@@ -3,6 +3,8 @@ import { styled } from '@mui/material/styles';
3
3
  export const NavBadge = styled(Badge)(({ theme }) => ({
4
4
  [`& .${badgeClasses.badge}`]: {
5
5
  border: 'none',
6
+ height: 14,
7
+ minWidth: 14,
6
8
  position: 'static',
7
9
  transform: 'none',
8
10
  backgroundColor: theme.vars.palette.action.selected,
@@ -90,6 +90,14 @@ export const Screenshot = {
90
90
  React.createElement(NavDropdown, null,
91
91
  React.createElement(NavIcon, { icon: Heart }),
92
92
  React.createElement(NavText, { secondaryText: "Secondary", tertiaryText: "Tertiary" }, "Primary Item")))),
93
+ React.createElement("section", null,
94
+ React.createElement("h3", null, "Counts"),
95
+ [12, 99, '99+'].map(badge => (React.createElement(NavItem, { key: badge, disablePadding: true, badge: badge },
96
+ React.createElement(NavLink, null,
97
+ React.createElement(NavIcon, { icon: Folder }),
98
+ React.createElement(NavText, null,
99
+ "Item with ",
100
+ badge)))))),
93
101
  React.createElement("section", null,
94
102
  React.createElement("h3", null, "Secondary"),
95
103
  React.createElement(NavItem, { disablePadding: true, variant: "secondary", selected: true },
@@ -4,4 +4,5 @@ declare const meta: Meta<typeof VirtualizedTable>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof meta>;
6
6
  export declare const Default: Story;
7
+ export declare const ServerSortingMode: Story;
7
8
  export declare const Screenshot: Story;
@@ -1,6 +1,7 @@
1
1
  import { Button, Skeleton, Typography } from '@mui/material';
2
2
  import React, { useState } from 'react';
3
3
  import { fn } from 'storybook/test';
4
+ import { getComparator, stableSort } from './helpers';
4
5
  import { VirtualizedTable } from './index';
5
6
  const createData = (id, name, calories, fat, carbs, protein) => ({ id, name, calories, fat, carbs, protein });
6
7
  const rows = [
@@ -76,10 +77,10 @@ const meta = {
76
77
  rows: { control: 'object' },
77
78
  columns: { control: 'object' },
78
79
  defaultOrder: { control: 'object' },
80
+ sortingMode: { control: 'inline-radio', options: ['client', 'server'] },
79
81
  selectedItems: { control: 'object' },
80
82
  isLoading: { control: 'boolean' },
81
83
  groups: { control: false },
82
- secondarySort: { control: false },
83
84
  isSelectedItem: { control: false },
84
85
  isNewItem: { control: false },
85
86
  context: { control: false },
@@ -96,6 +97,14 @@ export const Default = {
96
97
  },
97
98
  render: args => React.createElement(ExampleTable, { title: "Table", ...args })
98
99
  };
100
+ // The caller sorts the rows, as a server would, and the table keeps their order
101
+ const SortedByCallerTable = () => {
102
+ const [sortedRows, setSortedRows] = useState(rows);
103
+ return (React.createElement(ExampleTable, { title: "Rows sorted by the caller", rows: sortedRows, sortingMode: "server", onSortChange: ({ order, orderBy }) => setSortedRows(stableSort(rows, getComparator(order, orderBy))) }));
104
+ };
105
+ export const ServerSortingMode = {
106
+ render: () => React.createElement(SortedByCallerTable, null)
107
+ };
99
108
  // Visual Regression - Combined view for Argos testing
100
109
  export const Screenshot = {
101
110
  tags: ['argos'],
@@ -12,13 +12,18 @@ export interface VirtualizedTableProps extends Omit<TableVirtuosoProps<Row, Tabl
12
12
  groupLabels: React.ReactNode[];
13
13
  groupCounts: number[];
14
14
  };
15
- /** Default sorting configuration */
15
+ /** Column sorted at first: without it, no column is sorted until one is clicked */
16
16
  defaultOrder?: {
17
17
  direction?: OrderDirection;
18
18
  by?: string;
19
19
  };
20
- /** Applied after the column sort, e.g. to keep directories before files */
21
- secondarySort?: (rows: Row[]) => Row[];
20
+ /**
21
+ * Who sorts the rows. 'server': the table keeps the given order, its
22
+ * headers changing the sort and calling onSortChange for the caller to sort
23
+ * them, e.g. on its server. 'client': the table sorts them by the sorted
24
+ * column
25
+ */
26
+ sortingMode?: 'client' | 'server';
22
27
  /** Array of selected items */
23
28
  selectedItems?: unknown[];
24
29
  /** Function to determine if a row is selected */
@@ -6,7 +6,7 @@ import { RowContent } from './RowContent';
6
6
  import { getComparator, stableSort } from './helpers';
7
7
  import { _isLoading } from './types';
8
8
  import { virtuosoComponents } from './virtuosoComponents';
9
- export const VirtualizedTable = forwardRef(({ rows, columns, groups, defaultOrder, secondarySort, selectedItems = [], isSelectedItem = () => false, isNewItem, context, componentsProps, components = virtuosoComponents, onSortChange, isLoading, skeletonCount = 10, ...props }, ref) => {
9
+ export const VirtualizedTable = forwardRef(({ rows, columns, groups, defaultOrder, sortingMode = 'client', selectedItems = [], isSelectedItem = () => false, isNewItem, context, componentsProps, components = virtuosoComponents, onSortChange, isLoading, skeletonCount = 10, ...props }, ref) => {
10
10
  const [orderDirection, setOrderDirection] = useState(defaultOrder?.direction ?? 'asc');
11
11
  const [orderBy, setOrderBy] = useState(defaultOrder?.by);
12
12
  const data = useMemo(() => {
@@ -16,11 +16,10 @@ export const VirtualizedTable = forwardRef(({ rows, columns, groups, defaultOrde
16
16
  id: `skeleton-${i}`
17
17
  }));
18
18
  }
19
- const sortedRows = orderBy
19
+ return orderBy && sortingMode === 'client'
20
20
  ? stableSort(rows, getComparator(orderDirection, orderBy))
21
21
  : rows;
22
- return secondarySort ? secondarySort(sortedRows) : sortedRows;
23
- }, [rows, orderBy, orderDirection, secondarySort, isLoading, skeletonCount]);
22
+ }, [rows, orderBy, orderDirection, sortingMode, isLoading, skeletonCount]);
24
23
  const { groupLabels, groupCounts } = (isLoading ? undefined : groups?.(data)) ?? {};
25
24
  const tableContext = {
26
25
  ...context,
@@ -537,17 +537,20 @@ export const overrides = {
537
537
  },
538
538
  styleOverrides: {
539
539
  badge: ({ theme }) => ({
540
- boxSizing: 'content-box',
541
- height: 14,
542
- minWidth: 14,
540
+ // Sizes include the border, so an app setting minWidth gets that width
541
+ boxSizing: 'border-box',
542
+ height: 18,
543
+ minWidth: 18,
543
544
  padding: 0,
544
545
  border: `2px solid ${theme.vars.palette.background.paper}`,
545
- borderRadius: '100%',
546
- fontSize: theme.typography.pxToRem(10)
546
+ borderRadius: radius.pill,
547
+ fontSize: theme.typography.pxToRem(10),
548
+ // Every two-digit count fits the circle, longer counts grow into a pill
549
+ fontVariantNumeric: 'tabular-nums'
547
550
  }),
548
551
  dot: {
549
- height: 8,
550
- minWidth: 8
552
+ height: 12,
553
+ minWidth: 12
551
554
  },
552
555
  anchorOriginTopRightRectangular: {
553
556
  transform: 'scale(1) translate(37%, -37%)'
@@ -62,6 +62,8 @@ export const Screenshot = {
62
62
  React.createElement(Target, null)),
63
63
  React.createElement(Badge, { color: "error", badgeContent: 0, showZero: false },
64
64
  React.createElement(Target, null)),
65
+ React.createElement(Badge, { color: "error", badgeContent: 99, max: 99 },
66
+ React.createElement(Target, null)),
65
67
  React.createElement(Badge, { color: "error", badgeContent: 120, max: 99 },
66
68
  React.createElement(Target, null)),
67
69
  React.createElement(Badge, { color: "error", badgeContent: 4, invisible: true },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linagora/twake-mui",
3
3
  "type": "module",
4
- "version": "13.2.0",
4
+ "version": "14.0.0",
5
5
  "description": "MUI v9 theme system for Twake applications",
6
6
  "author": "Twake",
7
7
  "license": "MIT",