jamespot-react-components 1.0.2 → 1.0.16
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/.storybook/StorybookContainer/StorybookContainer.css +1 -0
- package/babel.config.js +0 -1
- package/build/jamespot-react-components.js +254 -235
- package/build/jamespot-react-components.js.LICENSE.txt +19 -13
- package/build/jamespot-react-components.js.map +1 -1
- package/build/src/components/Common/JRCConditionalWrapper.d.ts +4 -4
- package/build/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.d.ts +5 -2
- package/build/src/components/Form/Input/JRCInputDate/JRCInputDate.d.ts +11 -0
- package/build/src/components/Form/Input/JRCInputDate/JRCInputDate.stories.d.ts +5 -0
- package/build/src/components/Form/Input/JRCInputText/JRCInputText.d.ts +1 -1
- package/build/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.d.ts +15 -0
- package/build/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.stories.d.ts +5 -0
- package/build/src/components/JRCAppContainer/JRCAppContainer.d.ts +1 -0
- package/build/src/components/JRCButton/JRCButton.d.ts +25 -0
- package/build/src/components/JRCButton/JRCButtonConfig.d.ts +1 -2
- package/build/src/components/JRCEllipsis/JRCEllipsis.d.ts +5 -0
- package/build/src/components/JRCEllipsis/JRCEllipsis.stories.d.ts +5 -0
- package/build/src/components/JRCHref/JRCHref.d.ts +18 -10
- package/build/src/components/JRCImg/url.util.d.ts +3 -3
- package/build/src/components/JRCList/JRCList.d.ts +2 -0
- package/build/src/components/JRCModal/JRCModal.d.ts +5 -1
- package/build/src/components/JRCModal/JRCModal.styles.d.ts +0 -1
- package/build/src/components/JRCTag/JRCTag.d.ts +2 -0
- package/build/src/components/Templates/JRCBase.template.d.ts +9 -0
- package/build/src/components/Templates/JRCTemplate.stories.d.ts +13 -0
- package/build/src/components/Templates/JRCTwoColumns.template.d.ts +7 -0
- package/build/src/hooks/UseDidMountEffect.d.ts +1 -2
- package/build/src/index.d.ts +11 -8
- package/build/src/styles/theme.d.ts +4 -1
- package/build/src/types.d.ts +6 -2
- package/externals.d.ts +0 -1
- package/externals.json +3 -1
- package/package.json +4 -4
- package/src/components/Common/JRCConditionalWrapper.tsx +6 -13
- package/src/components/Form/Input/JRCFormColor/JRCFormColor.tsx +12 -12
- package/src/components/Form/Input/JRCFormEmail/JRCInputEmail.tsx +4 -8
- package/src/components/Form/Input/JRCFormSelect/JRCFormSelect.tsx +5 -1
- package/src/components/Form/Input/JRCFormSelect/JRCFormSelectTag.tsx +3 -1
- package/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.stories.tsx +1 -4
- package/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.tsx +12 -10
- package/src/components/Form/Input/JRCInputDate/JRCInputDate.stories.tsx +50 -0
- package/src/components/Form/Input/JRCInputDate/JRCInputDate.tsx +26 -0
- package/src/components/Form/Input/JRCInputText/JRCInputText.tsx +2 -2
- package/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.stories.tsx +52 -0
- package/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.tsx +36 -0
- package/src/components/Form/Input/JRCSelect/JRCInputSelect.tsx +1 -1
- package/src/components/JRCAppContainer/JRCAppContainer.tsx +6 -2
- package/src/components/JRCAppLeftColumn/JRCAppLeftColumn.styles.tsx +2 -1
- package/src/components/JRCAvatar/JRCAvatar.test.tsx +10 -6
- package/src/components/JRCAvatar/JRCAvatar.tsx +1 -0
- package/src/components/JRCButton/JRCButton.stories.tsx +1 -1
- package/src/components/JRCButton/JRCButton.tsx +9 -3
- package/src/components/JRCButton/JRCButtonConfig.tsx +1 -1
- package/src/components/JRCButton/JRCValidationButton.tsx +10 -4
- package/src/components/JRCButton/__snapshots__/JRCButton.test.tsx.snap +1 -2
- package/src/components/JRCButtonDropdown/JRCButtonDropdown.tsx +2 -2
- package/src/components/JRCEllipsis/JRCEllipsis.stories.tsx +18 -0
- package/src/components/JRCEllipsis/JRCEllipsis.tsx +22 -0
- package/src/components/JRCHref/JRCHref.stories.tsx +2 -0
- package/src/components/JRCHref/JRCHref.tsx +42 -15
- package/src/components/JRCIcon/JRCIcon.tsx +1 -1
- package/src/components/JRCIconButton/JRCIconButton.tsx +1 -4
- package/src/components/JRCImg/JRCImg.tsx +4 -2
- package/src/components/JRCImg/url.util.ts +7 -6
- package/src/components/JRCImg/url.utils.test.ts +7 -1
- package/src/components/JRCList/JRCList.styles.tsx +16 -2
- package/src/components/JRCList/JRCList.tsx +5 -5
- package/src/components/JRCList/JRCListMockData.stories.tsx +1 -1
- package/src/components/JRCModal/JRCModal.styles.tsx +0 -6
- package/src/components/JRCModal/JRCModal.tsx +75 -61
- package/src/components/JRCTag/JRCTag.tsx +29 -9
- package/src/components/JRCThemeProvider/animation.css +19 -0
- package/src/components/JRCThemeProvider/font.css +1 -1
- package/src/components/JRCThemeProvider/gabarit.css +4 -0
- package/src/components/JRCTooltip/JRCTooltip.tsx +5 -2
- package/src/components/JRCTypography/__snapshots__/JRCTypography.test.tsx.snap +1 -1
- package/src/components/Templates/JRCBase.template.tsx +39 -0
- package/src/components/Templates/JRCTemplate.stories.tsx +55 -0
- package/src/components/Templates/JRCTwoColumns.template.tsx +20 -0
- package/src/hooks/UseDidMountEffect.tsx +1 -3
- package/src/index.tsx +17 -10
- package/src/styles/theme.tsx +12 -6
- package/src/translation/lang.json +3 -2
- package/src/types.ts +8 -2
- package/build/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.d.ts +0 -10
- package/build/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.stories.d.ts +0 -5
- package/build/src/components/JRCColumnCenterTitle/JRCColumnCenterTitle.d.ts +0 -12
- package/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.stories.tsx +0 -30
- package/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.tsx +0 -36
- package/src/components/JRCColumnCenterTitle/JRCColumnCenterTitle.tsx +0 -26
- package/src/variables.scss +0 -67
|
@@ -45,7 +45,7 @@ const Icon = styled.i<Required<JRCIconProps>>`
|
|
|
45
45
|
`;
|
|
46
46
|
|
|
47
47
|
const JRCIcon = ({ color = 'primary', variant = 'default', name, size = 20, className }: JRCIconProps) => (
|
|
48
|
-
<Icon className={`react-icon ${name} ${className}`} name={name} size={size} color={color} variant={variant} />
|
|
48
|
+
<Icon className={`react-icon ${name} ${className || ''}`} name={name} size={size} color={color} variant={variant} />
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
export default JRCIcon;
|
|
@@ -81,10 +81,7 @@ const JRCIconButton = (props: JRCIconButtonProps & typeof defaultProps) => {
|
|
|
81
81
|
return (
|
|
82
82
|
<JRCConditionalWrapper
|
|
83
83
|
condition={!!props.tooltip}
|
|
84
|
-
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
86
|
-
// @ts-ignore
|
|
87
|
-
wrapper={(children) => <JRCTooltip {...props.tooltip}>{children}</JRCTooltip>}>
|
|
84
|
+
wrapper={(children) => <JRCTooltip {...(props.tooltip as JRCTooltipProps)}>{children}</JRCTooltip>}>
|
|
88
85
|
<Button {...props} data-cy={props.dataCy} size={props.size}>
|
|
89
86
|
<JRCIcon
|
|
90
87
|
name={props.icon}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import styled from 'styled-components';
|
|
3
|
+
import styled, { ThemeContext } from 'styled-components';
|
|
4
4
|
import { formatImgUrl, ImgUrlProps } from './url.util';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -43,9 +43,11 @@ const Img = styled.img`
|
|
|
43
43
|
`;
|
|
44
44
|
|
|
45
45
|
const JRCImg = (props: JRCImgProps) => {
|
|
46
|
+
const themeContext = React.useContext(ThemeContext);
|
|
47
|
+
|
|
46
48
|
return (
|
|
47
49
|
<Img
|
|
48
|
-
src={formatImgUrl(props)}
|
|
50
|
+
src={formatImgUrl(props, themeContext.dpr)}
|
|
49
51
|
loading={props.loading || 'lazy'}
|
|
50
52
|
alt={props.alt}
|
|
51
53
|
width={props.width}
|
|
@@ -46,7 +46,7 @@ export type ImgUrlProps = {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
export function getFrom(arg: Pick<ImgUrlProps, 'from'>) {
|
|
49
|
-
return removeIfStartsWith(suffixIfDoesNotExist(arg.from, '/'), '/');
|
|
49
|
+
return removeIfStartsWith(suffixIfDoesNotExist(arg.from, '/'), '/') || '';
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function getTimestamp(arg: Timestamp) {
|
|
@@ -58,9 +58,10 @@ export function getUri(arg: UriOrTypeId): string {
|
|
|
58
58
|
return `${arg.type}/${arg.recordId}`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export function getSize(arg: Size & WidthHeight): string {
|
|
61
|
+
export function getSize(arg: Size & WidthHeight, dpr = 2): string {
|
|
62
62
|
if ('size' in arg && arg.size) return arg.size;
|
|
63
|
-
else if ('width' in arg && arg.width && 'height' in arg && arg.height)
|
|
63
|
+
else if ('width' in arg && arg.width && 'height' in arg && arg.height)
|
|
64
|
+
return `${arg.width * dpr}x${arg.height * dpr}`;
|
|
64
65
|
return '';
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -76,7 +77,7 @@ export function getUrl(url: string): URL | undefined {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
try {
|
|
79
|
-
return new URL(window.location.origin + url);
|
|
80
|
+
return new URL(window.location.origin + '/' + removeIfStartsWith(url, '/'));
|
|
80
81
|
} catch (e) {
|
|
81
82
|
/* silent exception */
|
|
82
83
|
}
|
|
@@ -95,7 +96,7 @@ export function getUrl(url: string): URL | undefined {
|
|
|
95
96
|
* The final url is {from}/{size}/{uri}.{format}?_={timestamp}
|
|
96
97
|
* @param arg
|
|
97
98
|
*/
|
|
98
|
-
export function formatImgUrl(arg: ImgUrlProps) {
|
|
99
|
+
export function formatImgUrl(arg: ImgUrlProps, dpr = 2) {
|
|
99
100
|
if ('url' in arg && arg.url) {
|
|
100
101
|
const url = getUrl(arg.url);
|
|
101
102
|
if (url) {
|
|
@@ -104,5 +105,5 @@ export function formatImgUrl(arg: ImgUrlProps) {
|
|
|
104
105
|
return url.toString();
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
|
-
return `/${getFrom(arg)}${getSize(arg)}/${getUri(arg)}.${getFormat(arg)}${getTimestamp(arg)}`;
|
|
108
|
+
return `/${getFrom(arg)}${getSize(arg, dpr)}/${getUri(arg)}.${getFormat(arg)}${getTimestamp(arg)}`;
|
|
108
109
|
}
|
|
@@ -9,6 +9,7 @@ describe('Test utils.url', () => {
|
|
|
9
9
|
// @ts-ignore
|
|
10
10
|
window.location = { origin: 'https://website.com' };
|
|
11
11
|
});
|
|
12
|
+
|
|
12
13
|
test('formatImgUrl url', () => {
|
|
13
14
|
expect(formatImgUrl({ url: '/url/random' })).toStrictEqual('https://website.com/url/random');
|
|
14
15
|
expect(formatImgUrl({ url: '/url/random?_=12345' })).toStrictEqual('https://website.com/url/random?_=12345');
|
|
@@ -24,18 +25,23 @@ describe('Test utils.url', () => {
|
|
|
24
25
|
expect(formatImgUrl({ url: 'https://otherwebsite.com/url/random?_=12345', timestamp: 123 })).toStrictEqual(
|
|
25
26
|
'https://otherwebsite.com/url/random?_=12345',
|
|
26
27
|
);
|
|
28
|
+
expect(formatImgUrl({ url: 'img/react/bookmark-noresult.png', height: 450 })).toStrictEqual(
|
|
29
|
+
'https://website.com/img/react/bookmark-noresult.png',
|
|
30
|
+
);
|
|
27
31
|
});
|
|
28
32
|
|
|
29
33
|
test('getFrom', () => {
|
|
30
|
-
expect(getFrom({})).toStrictEqual(
|
|
34
|
+
expect(getFrom({})).toStrictEqual('');
|
|
31
35
|
expect(getFrom({ from: 'imagestatic' })).toStrictEqual('imagestatic/');
|
|
32
36
|
expect(getFrom({ from: 'imagestatic/' })).toStrictEqual('imagestatic/');
|
|
33
37
|
expect(getFrom({ from: '/imagestatic/' })).toStrictEqual('imagestatic/');
|
|
38
|
+
expect(getFrom({ from: undefined })).toStrictEqual('');
|
|
34
39
|
});
|
|
35
40
|
|
|
36
41
|
test('getSize', () => {
|
|
37
42
|
expect(getSize({ size: '32' })).toStrictEqual('32');
|
|
38
43
|
expect(getSize({ width: 32, height: 32 })).toStrictEqual('64x64');
|
|
44
|
+
expect(getSize({ width: 32, height: 32 }, 3)).toStrictEqual('96x96');
|
|
39
45
|
});
|
|
40
46
|
|
|
41
47
|
test('getUri', () => {
|
|
@@ -4,6 +4,7 @@ import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
|
|
4
4
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
5
5
|
import { JRCIconButton } from '../JRCIconButton/JRCIconButton';
|
|
6
6
|
import { JRCConditionalWrapper } from '../Common/JRCConditionalWrapper';
|
|
7
|
+
import { useIntl } from 'react-intl';
|
|
7
8
|
import { DropTargetMonitor } from 'react-dnd/dist/types/types';
|
|
8
9
|
|
|
9
10
|
const TableCss = css`
|
|
@@ -19,13 +20,20 @@ export const Table = styled.table`
|
|
|
19
20
|
border-collapse: collapse;
|
|
20
21
|
`;
|
|
21
22
|
|
|
22
|
-
export const Thead = styled.thead
|
|
23
|
+
export const Thead = styled.thead`
|
|
24
|
+
position: sticky;
|
|
25
|
+
top: 0;
|
|
26
|
+
background-color: white;
|
|
27
|
+
border-bottom: 2px solid ${(props) => props.theme.color.grey2};
|
|
28
|
+
z-index: 1;
|
|
29
|
+
`;
|
|
23
30
|
|
|
24
31
|
export const TBody = styled.tbody``;
|
|
25
32
|
|
|
26
33
|
export const Th = styled.th<{ width?: number | string }>`
|
|
27
34
|
${TableCss};
|
|
28
35
|
font-size: 12px;
|
|
36
|
+
padding: 0 4px;
|
|
29
37
|
font-weight: normal;
|
|
30
38
|
${(props) => props.width && props.width > 0 && `width: ${props.width}px`};
|
|
31
39
|
`;
|
|
@@ -75,6 +83,7 @@ export const Tr = styled.tr<{ dragging?: boolean }>`
|
|
|
75
83
|
|
|
76
84
|
export const Td = styled.td`
|
|
77
85
|
${TableCss};
|
|
86
|
+
padding: 0 4px;
|
|
78
87
|
${(props) => props.width && `width: ${props.width}`};
|
|
79
88
|
`;
|
|
80
89
|
|
|
@@ -137,6 +146,8 @@ export const DraggableTBody = ({ draggable, children }: DraggableTBodyProps) =>
|
|
|
137
146
|
const DND_ITEM_TYPE = 'row';
|
|
138
147
|
|
|
139
148
|
export const DraggableTr = ({ index, onDrag, onDrop, draggable, children }: DraggableTrProps) => {
|
|
149
|
+
const intl = useIntl();
|
|
150
|
+
|
|
140
151
|
const dropRef = React.useRef<HTMLTableRowElement>(null);
|
|
141
152
|
const dragRef = React.useRef<HTMLTableCellElement>(null);
|
|
142
153
|
let dragging = false;
|
|
@@ -199,7 +210,10 @@ export const DraggableTr = ({ index, onDrag, onDrop, draggable, children }: Drag
|
|
|
199
210
|
<Tr ref={dropRef} dragging={dragging}>
|
|
200
211
|
{draggable && (
|
|
201
212
|
<DraggableTd ref={dragRef}>
|
|
202
|
-
<JRCIconButton
|
|
213
|
+
<JRCIconButton
|
|
214
|
+
icon="icon-fs-dw"
|
|
215
|
+
tooltip={{ description: intl.formatMessage({ id: 'GLOBAL_Move' }), position: 'right' }}
|
|
216
|
+
/>
|
|
203
217
|
|
|
204
218
|
</DraggableTd>
|
|
205
219
|
)}
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { useTable, usePagination, useSortBy, UseSortByColumnProps, HeaderGroup, Column, Row, Cell } from 'react-table';
|
|
3
3
|
import { FormattedMessage } from 'react-intl';
|
|
4
4
|
import JRCPagination from '../JRCPagination/JRCPagination';
|
|
5
|
-
import useDidMountEffect from '../../hooks/UseDidMountEffect';
|
|
5
|
+
import { useDidMountEffect } from '../../hooks/UseDidMountEffect';
|
|
6
6
|
import JRCIcon from '../JRCIcon/JRCIcon';
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -25,6 +25,7 @@ import type { Orders } from 'jamespot-user-api';
|
|
|
25
25
|
* @member data default react-table data props
|
|
26
26
|
* @member eventHandlers event handlers: onDrag, onSort and onPage. You Must rearrange the array of data when these events are dispatched
|
|
27
27
|
* @member config config object. See the Type for use
|
|
28
|
+
* @member className className
|
|
28
29
|
*/
|
|
29
30
|
export type JRCListProps<T extends Record<string, unknown>> = {
|
|
30
31
|
columns: Array<Column<T>>;
|
|
@@ -45,6 +46,7 @@ export type JRCListProps<T extends Record<string, unknown>> = {
|
|
|
45
46
|
nbResults: number;
|
|
46
47
|
};
|
|
47
48
|
};
|
|
49
|
+
className?: string;
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export const JRCList = <T extends Record<string, unknown>>(props: JRCListProps<T>) => {
|
|
@@ -133,7 +135,7 @@ export const JRCList = <T extends Record<string, unknown>>(props: JRCListProps<T
|
|
|
133
135
|
|
|
134
136
|
return (
|
|
135
137
|
<React.Fragment>
|
|
136
|
-
<Table {...getTableProps()}>
|
|
138
|
+
<Table {...getTableProps()} className={props.className}>
|
|
137
139
|
<Thead>
|
|
138
140
|
{headerGroups.map((headerGroup) => (
|
|
139
141
|
<Tr {...headerGroup.getHeaderGroupProps()}>
|
|
@@ -162,9 +164,7 @@ export const JRCList = <T extends Record<string, unknown>>(props: JRCListProps<T
|
|
|
162
164
|
<TBody {...getTableBodyProps()}>
|
|
163
165
|
<DraggableTBody draggable={props.config.draggable}>
|
|
164
166
|
{rows.map((row: Row<T>, index: number): any => {
|
|
165
|
-
|
|
166
|
-
prepareRow(row);
|
|
167
|
-
}
|
|
167
|
+
prepareRow(row);
|
|
168
168
|
return (
|
|
169
169
|
<DraggableTr
|
|
170
170
|
index={index}
|
|
@@ -83,7 +83,7 @@ const Template: Story<never> = (/*args: JRCListProps<MyData>*/) => {
|
|
|
83
83
|
limit: config.pagination.perPage,
|
|
84
84
|
orders,
|
|
85
85
|
};
|
|
86
|
-
jamespot.
|
|
86
|
+
jamespot.bookmark.getList(args).then((res: any) => {
|
|
87
87
|
setData(res.data);
|
|
88
88
|
setConfig({
|
|
89
89
|
...config,
|
|
@@ -63,12 +63,6 @@ export const JRCModalHeader = styled.div`
|
|
|
63
63
|
justify-content: space-between;
|
|
64
64
|
`;
|
|
65
65
|
|
|
66
|
-
export const JRCModalHeaderTitle = styled.div`
|
|
67
|
-
display: flex;
|
|
68
|
-
flex-direction: row;
|
|
69
|
-
flex: 1;
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
66
|
export const JRCModalFooter = styled.div`
|
|
73
67
|
flex-shrink: 0;
|
|
74
68
|
display: flex;
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
JRCModalContainerFull,
|
|
10
10
|
JRCModalContent,
|
|
11
11
|
JRCModalHeader,
|
|
12
|
-
JRCModalHeaderTitle,
|
|
13
12
|
JRCModalFooter,
|
|
14
13
|
JRCModalClose,
|
|
15
14
|
} from './JRCModal.styles';
|
|
@@ -40,6 +39,8 @@ export interface JRCModalButtonType extends ComponentPropsWithoutRef<'button'> {
|
|
|
40
39
|
* @member enableClickAwayCloseModal boolean to enable the close of the Modal by Click Outside of Modal
|
|
41
40
|
* @member children default ReactNode children in the Modal content
|
|
42
41
|
* @member portalId id attribute for createPortal
|
|
42
|
+
* @member inPlace if true, does not use a portal
|
|
43
|
+
* @member className use for overriding the styled-components styles
|
|
43
44
|
*/
|
|
44
45
|
export interface JRCModalProps {
|
|
45
46
|
open: boolean;
|
|
@@ -52,6 +53,8 @@ export interface JRCModalProps {
|
|
|
52
53
|
isFull?: boolean;
|
|
53
54
|
headerButtons?: ReactNode;
|
|
54
55
|
portalId?: string;
|
|
56
|
+
inPlace?: boolean;
|
|
57
|
+
className?: string;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
type MouseEvents = 'click' | 'mousedown' | 'mouseup';
|
|
@@ -90,69 +93,67 @@ const JRCModalContainerClickAway = ({
|
|
|
90
93
|
return React.createElement(props.isFull ? JRCModalContainerFull : JRCModalContainer, { ref: node, ...props });
|
|
91
94
|
};
|
|
92
95
|
|
|
93
|
-
const JRCModal = (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
96
|
+
const JRCModal = React.forwardRef(
|
|
97
|
+
(
|
|
98
|
+
{
|
|
99
|
+
open,
|
|
100
|
+
closeHandler,
|
|
101
|
+
buttons,
|
|
102
|
+
children,
|
|
103
|
+
title,
|
|
104
|
+
showIconClose,
|
|
105
|
+
enableClickAwayCloseModal,
|
|
106
|
+
isFull,
|
|
107
|
+
headerButtons,
|
|
108
|
+
portalId = 'react-modal',
|
|
109
|
+
inPlace = false,
|
|
110
|
+
className,
|
|
111
|
+
}: JRCModalProps,
|
|
112
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
113
|
+
) => {
|
|
114
|
+
const closeModal = () => {
|
|
115
|
+
closeHandler();
|
|
116
|
+
};
|
|
110
117
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const dummyVoid = (): undefined => {
|
|
115
|
-
return undefined;
|
|
116
|
-
};
|
|
118
|
+
const escapeHandler = (e: React.KeyboardEvent) => {
|
|
119
|
+
if (e.key == 'Escape') closeHandler();
|
|
120
|
+
};
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
const handleClickAway = (event: any) => {
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
closeHandler();
|
|
125
|
+
};
|
|
122
126
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
127
|
+
const getClickHandlerList = () => {
|
|
128
|
+
const handlerList: (() => void)[] = [];
|
|
129
|
+
if (buttons != undefined) {
|
|
130
|
+
buttons.map((button: JRCModalButtonType) => {
|
|
131
|
+
if (button.isClosed == undefined || !button.isClosed)
|
|
132
|
+
handlerList.push(() => {
|
|
133
|
+
closeHandler();
|
|
134
|
+
button.clickHandler != undefined && button.clickHandler();
|
|
135
|
+
});
|
|
136
|
+
else
|
|
137
|
+
handlerList.push(() => {
|
|
138
|
+
/* dummy void */
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return handlerList;
|
|
143
|
+
};
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
const displayHeaderButtons = headerButtons ? headerButtons : null;
|
|
146
|
+
const clickHandlerList = getClickHandlerList();
|
|
147
|
+
const portalDiv = document.getElementById(portalId);
|
|
144
148
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
148
|
-
// @ts-ignore
|
|
149
|
-
<JRCThemeProvider>
|
|
150
|
-
<JRCModalBox onKeyDown={(e) => escapeHandler(e)} tabIndex={0} ref={modalRef}>
|
|
149
|
+
const ModalBox = () => (
|
|
150
|
+
<JRCModalBox onKeyDown={(e) => escapeHandler(e)} tabIndex={0} ref={ref} className={className}>
|
|
151
151
|
<JRCModalContainerClickAway
|
|
152
|
+
className="modal-container"
|
|
152
153
|
onClickAway={enableClickAwayCloseModal ? handleClickAway : undefined}
|
|
153
154
|
isFull={isFull}>
|
|
154
155
|
{(title || showIconClose) && (
|
|
155
|
-
<JRCModalHeader>
|
|
156
|
+
<JRCModalHeader className={'modal-header'}>
|
|
156
157
|
{title && <JRCH3>{title}</JRCH3>}
|
|
157
158
|
{displayHeaderButtons}
|
|
158
159
|
{showIconClose && (
|
|
@@ -163,7 +164,7 @@ const JRCModal = ({
|
|
|
163
164
|
</JRCModalHeader>
|
|
164
165
|
)}
|
|
165
166
|
|
|
166
|
-
<JRCModalContent>{children}</JRCModalContent>
|
|
167
|
+
<JRCModalContent className="modal-content">{children}</JRCModalContent>
|
|
167
168
|
|
|
168
169
|
{buttons ? (
|
|
169
170
|
<JRCModalFooter>
|
|
@@ -180,10 +181,23 @@ const JRCModal = ({
|
|
|
180
181
|
) : null}
|
|
181
182
|
</JRCModalContainerClickAway>
|
|
182
183
|
</JRCModalBox>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (open) {
|
|
187
|
+
if (inPlace) {
|
|
188
|
+
return <ModalBox />;
|
|
189
|
+
} else if (portalDiv) {
|
|
190
|
+
// when using a portal, we need to provide the Theme explicitely
|
|
191
|
+
return ReactDOM.createPortal(
|
|
192
|
+
<JRCThemeProvider>
|
|
193
|
+
<ModalBox />
|
|
194
|
+
</JRCThemeProvider>,
|
|
195
|
+
portalDiv,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
},
|
|
201
|
+
);
|
|
188
202
|
|
|
189
203
|
export default JRCModal;
|
|
@@ -19,6 +19,7 @@ import JRCTooltip from '../JRCTooltip/JRCTooltip';
|
|
|
19
19
|
* @member focusable - whether the element is focusable. Independant of onClick property
|
|
20
20
|
* @member isFocused - whether the element is focused. Property used with variant for styling the component
|
|
21
21
|
* @member tooltipDescription - if present, show a tooltip with the description
|
|
22
|
+
* @member clickVariant - whether the hover/focus style is a selectable item or a deletable item
|
|
22
23
|
*/
|
|
23
24
|
export type JRCTagProps<T> = UriOrTypeId & {
|
|
24
25
|
collapsed?: boolean;
|
|
@@ -31,9 +32,10 @@ export type JRCTagProps<T> = UriOrTypeId & {
|
|
|
31
32
|
isFocused?: boolean;
|
|
32
33
|
tooltipDescription?: string;
|
|
33
34
|
alt?: string;
|
|
35
|
+
clickVariant?: 'select' | 'delete';
|
|
34
36
|
};
|
|
35
37
|
|
|
36
|
-
const TagWrapper = styled.div<{ focusable: boolean; isFocused: boolean }>`
|
|
38
|
+
const TagWrapper = styled.div<{ focusable: boolean; isFocused: boolean; clickVariant: 'select' | 'delete' }>`
|
|
37
39
|
position: relative;
|
|
38
40
|
display: inline-flex;
|
|
39
41
|
align-items: center;
|
|
@@ -44,16 +46,16 @@ const TagWrapper = styled.div<{ focusable: boolean; isFocused: boolean }>`
|
|
|
44
46
|
cursor: ${(props) => (props.focusable ? 'pointer' : 'default')};
|
|
45
47
|
background-color: ${(props) => {
|
|
46
48
|
if (props.focusable && props.isFocused) {
|
|
47
|
-
return props.theme.color.
|
|
49
|
+
return props.theme.color[config[props.clickVariant].hoverBGColor];
|
|
48
50
|
} else {
|
|
49
|
-
return props.theme.color.
|
|
51
|
+
return props.theme.color[config[props.clickVariant].backgroundColor];
|
|
50
52
|
}
|
|
51
53
|
}};
|
|
52
54
|
color: ${(props) => {
|
|
53
55
|
if (props.focusable && props.isFocused) {
|
|
54
|
-
return props.theme.color.
|
|
56
|
+
return props.theme.color[config[props.clickVariant].hoverColor];
|
|
55
57
|
} else {
|
|
56
|
-
return props.theme.color.
|
|
58
|
+
return props.theme.color[config[props.clickVariant].color];
|
|
57
59
|
}
|
|
58
60
|
}};
|
|
59
61
|
|
|
@@ -72,14 +74,15 @@ const TagWrapper = styled.div<{ focusable: boolean; isFocused: boolean }>`
|
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
:hover {
|
|
75
|
-
${(props) =>
|
|
76
|
-
|
|
77
|
+
${(props) =>
|
|
78
|
+
props.focusable && `background-color: ${props.theme.color[config[props.clickVariant].hoverBGColor]}`};
|
|
79
|
+
${(props) => props.focusable && `color: ${props.theme.color[config[props.clickVariant].hoverColor]}`};
|
|
77
80
|
}
|
|
78
81
|
:hover i {
|
|
79
|
-
${(props) => props.focusable && `display: block`};
|
|
82
|
+
${(props) => props.focusable && props.clickVariant === 'delete' && `display: block`};
|
|
80
83
|
}
|
|
81
84
|
& i {
|
|
82
|
-
${(props) => props.focusable && props.isFocused && `display: block`};
|
|
85
|
+
${(props) => props.focusable && props.isFocused && props.clickVariant === 'delete' && `display: block`};
|
|
83
86
|
}
|
|
84
87
|
`;
|
|
85
88
|
|
|
@@ -87,8 +90,24 @@ const TagLabel = styled.div`
|
|
|
87
90
|
margin-left: 4px;
|
|
88
91
|
`;
|
|
89
92
|
|
|
93
|
+
const config = {
|
|
94
|
+
select: {
|
|
95
|
+
color: 'grey4',
|
|
96
|
+
backgroundColor: 'white',
|
|
97
|
+
hoverColor: 'grey5',
|
|
98
|
+
hoverBGColor: 'grey0',
|
|
99
|
+
},
|
|
100
|
+
delete: {
|
|
101
|
+
color: 'grey5',
|
|
102
|
+
backgroundColor: 'grey0',
|
|
103
|
+
hoverColor: 'grey4',
|
|
104
|
+
hoverBGColor: 'orangeL80',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
90
108
|
export function JRCTag<T>(props: JRCTagProps<T>) {
|
|
91
109
|
const uri = 'uri' in props ? props.uri : `${props.type}/${props.recordId}`;
|
|
110
|
+
const clickVariant = props.clickVariant || 'delete';
|
|
92
111
|
|
|
93
112
|
const getAvatar = () =>
|
|
94
113
|
React.createElement(
|
|
@@ -104,6 +123,7 @@ export function JRCTag<T>(props: JRCTagProps<T>) {
|
|
|
104
123
|
: undefined,
|
|
105
124
|
focusable: props.focusable,
|
|
106
125
|
isFocused: props.isFocused,
|
|
126
|
+
clickVariant,
|
|
107
127
|
},
|
|
108
128
|
<>
|
|
109
129
|
<JRCAvatar alt={props.alt} uri={uri} variant={props.variant} focusable={props.focusable} />
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
.wiggle {
|
|
2
|
+
animation: 0.25s ease-in-out 0s 4 wiggle;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
@keyframes wiggle {
|
|
6
|
+
0% {
|
|
7
|
+
transform: rotate(0deg);
|
|
8
|
+
}
|
|
9
|
+
25% {
|
|
10
|
+
transform: rotate(10deg) scale(1.1);
|
|
11
|
+
}
|
|
12
|
+
75% {
|
|
13
|
+
transform: rotate(-10deg) scale(1.1);
|
|
14
|
+
}
|
|
15
|
+
100% {
|
|
16
|
+
transform: rotate(0deg);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
1
20
|
.scale-left {
|
|
2
21
|
animation: scale var(--timeout, 1000ms) linear 0s 1;
|
|
3
22
|
}
|
|
@@ -71,6 +71,9 @@ const Text = styled(JRCText)<{ sizeTooltip: keyof typeof sizes.padding }>`
|
|
|
71
71
|
margin: 0;
|
|
72
72
|
text-align: ${(props) => (props.sizeTooltip === 'small' ? 'center' : 'initial')};
|
|
73
73
|
`;
|
|
74
|
+
const InlineBlock = styled.span`
|
|
75
|
+
display: inline-block;
|
|
76
|
+
`;
|
|
74
77
|
|
|
75
78
|
export const JRCTooltip = (props: JRCTooltipProps) => {
|
|
76
79
|
const id = uuidv4();
|
|
@@ -86,14 +89,14 @@ export const JRCTooltip = (props: JRCTooltipProps) => {
|
|
|
86
89
|
};
|
|
87
90
|
return (
|
|
88
91
|
<>
|
|
89
|
-
<
|
|
92
|
+
<InlineBlock
|
|
90
93
|
aria-describedby={id}
|
|
91
94
|
data-event="mouseenter focusin"
|
|
92
95
|
data-event-off={props.closeButton && props.button ? 'click' : 'mouseleave'}
|
|
93
96
|
data-tip
|
|
94
97
|
data-for={id}>
|
|
95
98
|
{props.children}
|
|
96
|
-
</
|
|
99
|
+
</InlineBlock>
|
|
97
100
|
<Tooltip
|
|
98
101
|
effect="solid"
|
|
99
102
|
role="tooltip"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { JRCAppContainer } from 'components/JRCAppContainer/JRCAppContainer';
|
|
2
|
+
import JRCLoader from 'components/JRCLoader/JRCLoader';
|
|
3
|
+
import { JRCTypography } from 'components/JRCTypography/JRCTypography';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
|
|
7
|
+
export type JRCTemplateBaseProps = {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
title?: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const ChildrenContainerStyle = styled.div`
|
|
14
|
+
display: block;
|
|
15
|
+
width: 100%;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
export const ChildrenContainer = (props: JRCTemplateBaseProps) => {
|
|
19
|
+
return (
|
|
20
|
+
<ChildrenContainerStyle>
|
|
21
|
+
{props.title && (
|
|
22
|
+
<JRCTypography variant="h1" size="xl" weight="medium">
|
|
23
|
+
{props.title}
|
|
24
|
+
</JRCTypography>
|
|
25
|
+
)}
|
|
26
|
+
{props.isLoading ? <JRCLoader variant="skeleton" /> : <>{props.children}</>}
|
|
27
|
+
</ChildrenContainerStyle>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const JRCTemplateBase: React.FC<any> = (props: JRCTemplateBaseProps) => {
|
|
32
|
+
return (
|
|
33
|
+
<JRCAppContainer>
|
|
34
|
+
<ChildrenContainer title={props.title} isLoading={props.isLoading}>
|
|
35
|
+
{props.children}
|
|
36
|
+
</ChildrenContainer>
|
|
37
|
+
</JRCAppContainer>
|
|
38
|
+
);
|
|
39
|
+
};
|