@linagora/twake-mui 14.0.0 → 14.2.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 +14 -0
- package/dist/components/Filename/Filename.stories.d.ts +7 -0
- package/dist/components/Filename/Filename.stories.js +95 -0
- package/dist/components/Filename/index.d.ts +16 -0
- package/dist/components/Filename/index.js +20 -0
- package/dist/components/LoadMore/LoadMore.stories.d.ts +7 -0
- package/dist/components/LoadMore/LoadMore.stories.js +43 -0
- package/dist/components/LoadMore/index.d.ts +9 -0
- package/dist/components/LoadMore/index.js +30 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# @linagora/twake-mui [14.2.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@14.1.0...@linagora/twake-mui@14.2.0) (2026-10-15)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **twake-mui:** Migrate Filename from cozy-ui ([84f4803](https://github.com/linagora/twake-ui/commit/84f4803fedbfc211dabef69717570d75500369b4))
|
|
7
|
+
|
|
8
|
+
# @linagora/twake-mui [14.1.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@14.0.0...@linagora/twake-mui@14.1.0) (2026-10-15)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **twake-mui:** Migrate LoadMore from cozy-ui ([3b505ec](https://github.com/linagora/twake-ui/commit/3b505ec16434d371d7a730cc314df06e6013b724))
|
|
14
|
+
|
|
1
15
|
# @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
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import Filename from './index';
|
|
3
|
+
declare const meta: Meta<typeof Filename>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { File, Icon } from '@linagora/twake-icons';
|
|
2
|
+
import { Box, Stack } from '@mui/material';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import Filename from './index';
|
|
5
|
+
const longName = 'Lacinia condimentum potenti id est tortor dictumst lectus tincidunt hac ultricies, curae mattis nisi neque sodales sagittis dui nulla aliquam turpis eros, finibus ac iaculis dictum et orci elit posuere ex and this is the end';
|
|
6
|
+
const shortName = 'Lacinia condimentum this is the end';
|
|
7
|
+
const path = '/some/folder/and/subfolder';
|
|
8
|
+
const icon = React.createElement(Icon, { icon: File, size: 32 });
|
|
9
|
+
const examples = [
|
|
10
|
+
{
|
|
11
|
+
title: 'Icon, extension and path',
|
|
12
|
+
props: { icon, filename: longName, extension: '.pdf', path }
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
title: 'Middle ellipsis',
|
|
16
|
+
props: {
|
|
17
|
+
icon,
|
|
18
|
+
filename: longName,
|
|
19
|
+
extension: '.pdf',
|
|
20
|
+
path,
|
|
21
|
+
midEllipsis: true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
title: 'Without path',
|
|
26
|
+
props: { icon, filename: longName, extension: '.pdf' }
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: 'Without path, middle ellipsis',
|
|
30
|
+
props: { icon, filename: longName, extension: '.pdf', midEllipsis: true }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: 'Body1 variant',
|
|
34
|
+
props: {
|
|
35
|
+
icon,
|
|
36
|
+
filename: longName,
|
|
37
|
+
extension: '.pdf',
|
|
38
|
+
path,
|
|
39
|
+
variant: 'body1'
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: 'Without icon',
|
|
44
|
+
props: { filename: longName, extension: '.pdf', path }
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: 'Without extension',
|
|
48
|
+
props: { icon, filename: longName, path }
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
title: 'Short name',
|
|
52
|
+
props: { icon, filename: shortName, extension: '.pdf', path }
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: 'Short name without path',
|
|
56
|
+
props: { icon, filename: shortName, extension: '.pdf' }
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
const Section = ({ title, children }) => (React.createElement("section", null,
|
|
60
|
+
React.createElement("h3", null, title),
|
|
61
|
+
children));
|
|
62
|
+
const meta = {
|
|
63
|
+
title: 'Filename',
|
|
64
|
+
component: Filename,
|
|
65
|
+
tags: ['autodocs'],
|
|
66
|
+
argTypes: {
|
|
67
|
+
icon: { control: false },
|
|
68
|
+
filename: { control: 'text' },
|
|
69
|
+
extension: { control: 'text' },
|
|
70
|
+
midEllipsis: { control: 'boolean' },
|
|
71
|
+
variant: {
|
|
72
|
+
control: 'select',
|
|
73
|
+
options: ['h6', 'body1', 'body2', 'subtitle1', 'caption']
|
|
74
|
+
},
|
|
75
|
+
path: { control: 'text' }
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
export default meta;
|
|
79
|
+
export const Default = {
|
|
80
|
+
args: {
|
|
81
|
+
icon,
|
|
82
|
+
filename: longName,
|
|
83
|
+
extension: '.pdf',
|
|
84
|
+
midEllipsis: false,
|
|
85
|
+
variant: 'h6',
|
|
86
|
+
path
|
|
87
|
+
},
|
|
88
|
+
render: args => (React.createElement(Box, { sx: { maxWidth: 400 } },
|
|
89
|
+
React.createElement(Filename, { ...args })))
|
|
90
|
+
};
|
|
91
|
+
export const Screenshot = {
|
|
92
|
+
tags: ['argos'],
|
|
93
|
+
render: () => (React.createElement(Stack, { spacing: 4, sx: { maxWidth: 400 } }, examples.map(({ title, props }) => (React.createElement(Section, { key: title, title: title },
|
|
94
|
+
React.createElement(Filename, { ...props }))))))
|
|
95
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TypographyProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export interface FilenameProps {
|
|
4
|
+
icon?: React.ReactNode;
|
|
5
|
+
/** Folder or file name */
|
|
6
|
+
filename?: string;
|
|
7
|
+
/** File extension, rendered in the secondary text color */
|
|
8
|
+
extension?: string;
|
|
9
|
+
/** Truncates the filename in its middle instead of its end */
|
|
10
|
+
midEllipsis?: boolean;
|
|
11
|
+
variant?: TypographyProps['variant'];
|
|
12
|
+
/** Shown on a second line, under the name */
|
|
13
|
+
path?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
declare const Filename: React.FC<FilenameProps>;
|
|
16
|
+
export default Filename;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Box, Typography } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import MidEllipsis from '../MidEllipsis';
|
|
4
|
+
const NameAndExtension = ({ filename, extension, midEllipsis, variant }) => (React.createElement(React.Fragment, null,
|
|
5
|
+
filename && (React.createElement(Typography, { variant: variant, component: "span", noWrap: true }, midEllipsis ? React.createElement(MidEllipsis, { text: filename }) : filename)),
|
|
6
|
+
extension && (React.createElement(Typography, { variant: variant, component: "span", color: "textSecondary" }, extension))));
|
|
7
|
+
const Filename = ({ icon, filename, extension, midEllipsis = false, variant = 'h6', path }) => {
|
|
8
|
+
const nameAndExtension = (React.createElement(NameAndExtension, { filename: filename, extension: extension, midEllipsis: midEllipsis, variant: variant }));
|
|
9
|
+
const iconBox = icon && (React.createElement(Box, { className: "u-flex u-pos-relative u-mr-1", sx: path ? { float: 'left', top: 4 } : undefined }, icon));
|
|
10
|
+
if (!path) {
|
|
11
|
+
return (React.createElement("div", { className: "u-flex u-flex-items-center" },
|
|
12
|
+
iconBox,
|
|
13
|
+
nameAndExtension));
|
|
14
|
+
}
|
|
15
|
+
return (React.createElement(React.Fragment, null,
|
|
16
|
+
iconBox,
|
|
17
|
+
React.createElement("div", { className: "u-flex" }, nameAndExtension),
|
|
18
|
+
React.createElement(Typography, { variant: "body2", component: "div", noWrap: true }, path)));
|
|
19
|
+
};
|
|
20
|
+
export default Filename;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import LoadMore from './index';
|
|
3
|
+
declare const meta: Meta<typeof LoadMore>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Box, Stack, Typography } from '@mui/material';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import LoadMore from './index';
|
|
4
|
+
const sleep = (timeout) => new Promise(resolve => setTimeout(resolve, timeout));
|
|
5
|
+
const InfiniteList = ({ fetchMore: onFetchMore, ...props }) => {
|
|
6
|
+
const [elements, setElements] = useState([1, 2]);
|
|
7
|
+
const fetchMore = async () => {
|
|
8
|
+
onFetchMore?.();
|
|
9
|
+
await sleep(1000);
|
|
10
|
+
setElements(previous => [...previous, previous.length + 1]);
|
|
11
|
+
};
|
|
12
|
+
return (React.createElement(Box, { sx: { height: 300, overflow: 'auto' } },
|
|
13
|
+
React.createElement(Typography, null, "Scroll down to load more."),
|
|
14
|
+
elements.map(element => (React.createElement(Box, { key: element, sx: { bgcolor: 'grey.500', height: 160, mb: 1 } }))),
|
|
15
|
+
elements.length < 25 && React.createElement(LoadMore, { fetchMore: fetchMore, ...props })));
|
|
16
|
+
};
|
|
17
|
+
const Section = ({ title, children }) => (React.createElement("section", null,
|
|
18
|
+
React.createElement("h3", null, title),
|
|
19
|
+
children));
|
|
20
|
+
const meta = {
|
|
21
|
+
title: 'LoadMore',
|
|
22
|
+
component: LoadMore,
|
|
23
|
+
tags: ['autodocs'],
|
|
24
|
+
argTypes: {
|
|
25
|
+
label: { control: 'text' },
|
|
26
|
+
fetchMore: { action: 'fetchMore' }
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export default meta;
|
|
30
|
+
export const Default = {
|
|
31
|
+
args: {
|
|
32
|
+
label: 'Load more'
|
|
33
|
+
},
|
|
34
|
+
render: args => React.createElement(InfiniteList, { ...args })
|
|
35
|
+
};
|
|
36
|
+
export const Screenshot = {
|
|
37
|
+
tags: ['argos'],
|
|
38
|
+
render: () => (React.createElement(Stack, { spacing: 4 },
|
|
39
|
+
React.createElement(Section, { title: "Button" },
|
|
40
|
+
React.createElement(LoadMore, { label: "Load more", fetchMore: () => { } })),
|
|
41
|
+
React.createElement(Section, { title: "Loading" },
|
|
42
|
+
React.createElement(LoadMore, { label: "Load more", fetchMore: () => new Promise(() => { }) }))))
|
|
43
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ButtonProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export interface LoadMoreProps extends Omit<ButtonProps, 'children' | 'onClick' | 'loading'> {
|
|
4
|
+
/** Called when the next batch of data needs to be loaded. Can return a promise. */
|
|
5
|
+
fetchMore: () => unknown;
|
|
6
|
+
label: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
declare const LoadMore: React.FC<LoadMoreProps>;
|
|
9
|
+
export default LoadMore;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Button, CircularProgress } from '@mui/material';
|
|
2
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
const LoadMore = ({ fetchMore, label, ...props }) => {
|
|
4
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5
|
+
const elementRef = useRef(null);
|
|
6
|
+
const startFetchMore = useCallback(async () => {
|
|
7
|
+
if (isLoading)
|
|
8
|
+
return;
|
|
9
|
+
setIsLoading(true);
|
|
10
|
+
await fetchMore();
|
|
11
|
+
setIsLoading(false);
|
|
12
|
+
}, [isLoading, fetchMore]);
|
|
13
|
+
const handleClick = () => {
|
|
14
|
+
void startFetchMore();
|
|
15
|
+
};
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const element = elementRef.current;
|
|
18
|
+
if (!element)
|
|
19
|
+
return;
|
|
20
|
+
const observer = new IntersectionObserver((entries) => {
|
|
21
|
+
if (entries.some(entry => entry.isIntersecting))
|
|
22
|
+
void startFetchMore();
|
|
23
|
+
});
|
|
24
|
+
observer.observe(element);
|
|
25
|
+
return () => observer.disconnect();
|
|
26
|
+
}, [startFetchMore]);
|
|
27
|
+
return (React.createElement("span", { ref: elementRef },
|
|
28
|
+
React.createElement(Button, { variant: "text", onClick: handleClick, ...props }, isLoading ? React.createElement(CircularProgress, { size: 16 }) : label)));
|
|
29
|
+
};
|
|
30
|
+
export default LoadMore;
|
package/dist/index.d.ts
CHANGED
|
@@ -46,12 +46,14 @@ export { default as DropdownButton } from './components/DropdownButton';
|
|
|
46
46
|
export { default as DropdownText } from './components/DropdownText';
|
|
47
47
|
export { Empty, EmptySubTitle } from './components/Empty';
|
|
48
48
|
export { ExtendableFab } from './components/ExtendableFab';
|
|
49
|
+
export { default as Filename } from './components/Filename';
|
|
49
50
|
export { default as ListItem } from './components/ListItem';
|
|
50
51
|
export { default as ListItemButton } from './components/ListItemButton';
|
|
51
52
|
export { default as ListItemText } from './components/ListItemText';
|
|
52
53
|
export { default as ListItemSkeleton } from './components/ListItemSkeleton';
|
|
53
54
|
export { default as ListSkeleton } from './components/ListSkeleton';
|
|
54
55
|
export { default as ListSubheader } from './components/ListSubheader';
|
|
56
|
+
export { default as LoadMore } from './components/LoadMore';
|
|
55
57
|
export { default as Markdown } from './components/Markdown';
|
|
56
58
|
export { default as MidEllipsis } from './components/MidEllipsis';
|
|
57
59
|
export { default as PointerAlert } from './components/PointerAlert';
|
|
@@ -124,12 +126,14 @@ export type { EmptyProps, EmptyIconSize, EmptyComponentsProps } from './componen
|
|
|
124
126
|
export type { DropdownButtonProps } from './components/DropdownButton';
|
|
125
127
|
export type { DropdownTextProps, DropdownTextVariant } from './components/DropdownText';
|
|
126
128
|
export type { ExtendableFabProps, ScrollOptions } from './components/ExtendableFab';
|
|
129
|
+
export type { FilenameProps } from './components/Filename';
|
|
127
130
|
export type { ListItemProps, ListItemGutters, ListItemSize } from './components/ListItem';
|
|
128
131
|
export type { ListItemButtonProps } from './components/ListItemButton';
|
|
129
132
|
export type { ListItemTextProps } from './components/ListItemText';
|
|
130
133
|
export type { ListItemSkeletonProps } from './components/ListItemSkeleton';
|
|
131
134
|
export type { ListSkeletonProps } from './components/ListSkeleton';
|
|
132
135
|
export type { ListSubheaderProps } from './components/ListSubheader';
|
|
136
|
+
export type { LoadMoreProps } from './components/LoadMore';
|
|
133
137
|
export type { MarkdownProps } from './components/Markdown';
|
|
134
138
|
export type { MidEllipsisProps } from './components/MidEllipsis';
|
|
135
139
|
export type { TabsProps } from './components/Tabs';
|
package/dist/index.js
CHANGED
|
@@ -49,12 +49,14 @@ export { default as DropdownButton } from './components/DropdownButton';
|
|
|
49
49
|
export { default as DropdownText } from './components/DropdownText';
|
|
50
50
|
export { Empty, EmptySubTitle } from './components/Empty';
|
|
51
51
|
export { ExtendableFab } from './components/ExtendableFab';
|
|
52
|
+
export { default as Filename } from './components/Filename';
|
|
52
53
|
export { default as ListItem } from './components/ListItem';
|
|
53
54
|
export { default as ListItemButton } from './components/ListItemButton';
|
|
54
55
|
export { default as ListItemText } from './components/ListItemText';
|
|
55
56
|
export { default as ListItemSkeleton } from './components/ListItemSkeleton';
|
|
56
57
|
export { default as ListSkeleton } from './components/ListSkeleton';
|
|
57
58
|
export { default as ListSubheader } from './components/ListSubheader';
|
|
59
|
+
export { default as LoadMore } from './components/LoadMore';
|
|
58
60
|
export { default as Markdown } from './components/Markdown';
|
|
59
61
|
export { default as MidEllipsis } from './components/MidEllipsis';
|
|
60
62
|
export { default as PointerAlert } from './components/PointerAlert';
|