@granto-umbrella/umbrella-components 3.0.6 → 3.0.8
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/dist/umbrella-components.es.js +18540 -18420
- package/dist/umbrella-components.umd.js +293 -246
- package/package.json +1 -1
- package/src/components/atoms/Input/Input.tsx +1 -1
- package/src/components/atoms/Input/Input.types.ts +1 -1
- package/src/components/atoms/Select/Select.styles.ts +0 -1
- package/src/components/atoms/Skeleton/Skeleton.styles.ts +32 -0
- package/src/components/atoms/Skeleton/Skeleton.tsx +43 -0
- package/src/components/atoms/Skeleton/Skeleton.types.ts +13 -0
- package/src/components/molecules/TimeLine/TimeLine.mapper.ts +2 -2
- package/src/components/molecules/TimeLine/TimeLine.styles.ts +36 -0
- package/src/components/molecules/TimeLine/TimeLine.tsx +58 -3
- package/src/components/molecules/TimeLine/TimeLine.types.ts +7 -1
- package/src/components/organisms/TimelineModal/TimelineModal.tsx +13 -1
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ export interface InputProps {
|
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
value?: string;
|
|
6
6
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
-
onBlur?: (event: React.
|
|
7
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
8
8
|
className?: string;
|
|
9
9
|
error?: boolean;
|
|
10
10
|
disabled?: boolean;
|
|
@@ -48,7 +48,6 @@ export const CustomSelect = styled(Select).attrs({
|
|
|
48
48
|
? semanticColors.global.border.danger.enabled
|
|
49
49
|
: semanticColors.global.border.medium};
|
|
50
50
|
border-radius: ${semanticRadius.global.radius.md};
|
|
51
|
-
box-shadow: ${semanticShadows.shadow.default};
|
|
52
51
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
53
52
|
min-height: ${primitiveSizes.size.x12};
|
|
54
53
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/components/atoms/Skeleton/styles.ts
|
|
2
|
+
import styled, { keyframes, css } from 'styled-components';
|
|
3
|
+
import { primitiveColors } from '../../../styles/tokens';
|
|
4
|
+
|
|
5
|
+
const shimmer = keyframes`
|
|
6
|
+
0% { background-position: -200% 0; }
|
|
7
|
+
100% { background-position: 200% 0; }
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
export const SkeletonWrap = styled.div`
|
|
11
|
+
display: grid;
|
|
12
|
+
gap: 8px;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const SkeletonBlock = styled.div<{
|
|
16
|
+
$variant: 'text' | 'rect' | 'circle';
|
|
17
|
+
$animated: boolean;
|
|
18
|
+
}>`
|
|
19
|
+
background: linear-gradient(
|
|
20
|
+
90deg,
|
|
21
|
+
${primitiveColors.gray[100]} 0%,
|
|
22
|
+
${primitiveColors.gray[200]} 50%,
|
|
23
|
+
${primitiveColors.gray[100]} 100%
|
|
24
|
+
);
|
|
25
|
+
background-size: 200% 100%;
|
|
26
|
+
|
|
27
|
+
${({ $animated }) =>
|
|
28
|
+
$animated &&
|
|
29
|
+
css`
|
|
30
|
+
animation: ${shimmer} 1.4s ease-in-out infinite;
|
|
31
|
+
`}
|
|
32
|
+
`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SkeletonBlock, SkeletonWrap } from './Skeleton.styles';
|
|
3
|
+
import { SkeletonProps } from './Skeleton.types';
|
|
4
|
+
|
|
5
|
+
export const Skeleton: React.FC<SkeletonProps> = ({
|
|
6
|
+
width,
|
|
7
|
+
height,
|
|
8
|
+
radius,
|
|
9
|
+
count = 1,
|
|
10
|
+
variant = 'rect',
|
|
11
|
+
animated = true,
|
|
12
|
+
className,
|
|
13
|
+
style,
|
|
14
|
+
ariaLabel = 'Carregando conteúdo',
|
|
15
|
+
}) => {
|
|
16
|
+
const items = Array.from({ length: count });
|
|
17
|
+
return (
|
|
18
|
+
<SkeletonWrap
|
|
19
|
+
role="status"
|
|
20
|
+
aria-live="polite"
|
|
21
|
+
aria-busy="true"
|
|
22
|
+
aria-label={ariaLabel}
|
|
23
|
+
className={className}
|
|
24
|
+
>
|
|
25
|
+
{items.map((_, i) => (
|
|
26
|
+
<SkeletonBlock
|
|
27
|
+
key={i}
|
|
28
|
+
$variant={variant}
|
|
29
|
+
$animated={animated}
|
|
30
|
+
style={{
|
|
31
|
+
width: width ?? (variant === 'text' ? '100%' : undefined),
|
|
32
|
+
height:
|
|
33
|
+
height ??
|
|
34
|
+
(variant === 'text' ? '1em' : variant === 'circle' ? 40 : 16),
|
|
35
|
+
borderRadius:
|
|
36
|
+
variant === 'circle' ? '9999px' : radius != null ? radius : 8,
|
|
37
|
+
...style,
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
))}
|
|
41
|
+
</SkeletonWrap>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type SkeletonVariant = 'text' | 'rect' | 'circle';
|
|
2
|
+
|
|
3
|
+
export type SkeletonProps = {
|
|
4
|
+
width?: string | number;
|
|
5
|
+
height?: string | number;
|
|
6
|
+
radius?: number;
|
|
7
|
+
count?: number;
|
|
8
|
+
variant?: SkeletonVariant;
|
|
9
|
+
animated?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
style?: React.CSSProperties;
|
|
12
|
+
ariaLabel?: string;
|
|
13
|
+
};
|
|
@@ -5,6 +5,7 @@ function toVariant(eventType: string): TimelineVariant {
|
|
|
5
5
|
if (eventType === 'subscription:partner_subscribe') return 'accepted';
|
|
6
6
|
if (eventType === 'subscription:partner_quote') return 'continued';
|
|
7
7
|
if (eventType === 'order:proposal_refused') return 'rejected';
|
|
8
|
+
if (eventType === 'issue:partner_issue') return 'issue';
|
|
8
9
|
return 'continued';
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -15,8 +16,7 @@ function buildTitle(ev: RemoteEvent): string {
|
|
|
15
16
|
return 'Subscrição realizada';
|
|
16
17
|
if (eventType === 'subscription:partner_quote') return 'Cotação processada';
|
|
17
18
|
if (eventType === 'order:proposal_refused') return 'Proposta recusada';
|
|
18
|
-
|
|
19
|
-
// Fallback genérico
|
|
19
|
+
if (eventType === 'issue:partner_issue') return 'Emissão realizada';
|
|
20
20
|
return data?.title ? String(data.title) : eventType;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -55,6 +55,9 @@ export const Dot = styled.span<{
|
|
|
55
55
|
download: css`
|
|
56
56
|
background: ${semanticColors.branding.surface.disabled};
|
|
57
57
|
`,
|
|
58
|
+
issue: css`
|
|
59
|
+
background: ${semanticColors.global.surface.status.online};
|
|
60
|
+
`,
|
|
58
61
|
};
|
|
59
62
|
return map[$variant as TimelineVariant] ?? map['continued'];
|
|
60
63
|
}}
|
|
@@ -73,6 +76,9 @@ export const Dot = styled.span<{
|
|
|
73
76
|
download: css`
|
|
74
77
|
border-color: ${semanticColors.branding.surface.enabled};
|
|
75
78
|
`,
|
|
79
|
+
issue: css`
|
|
80
|
+
border-color: ${semanticColors.global.border.feedback.success};
|
|
81
|
+
`,
|
|
76
82
|
};
|
|
77
83
|
return map[$variant as TimelineVariant] ?? map['continued'];
|
|
78
84
|
}}
|
|
@@ -91,6 +97,7 @@ export const Title = styled.h4`
|
|
|
91
97
|
`;
|
|
92
98
|
|
|
93
99
|
export const Sub = styled.div`
|
|
100
|
+
margin-top: 16px;
|
|
94
101
|
color: ${semanticColors.global.text.subtitle.enabled};
|
|
95
102
|
font-size: ${typographyTokens.fontSizes.headingM};
|
|
96
103
|
`;
|
|
@@ -110,3 +117,32 @@ export const List = styled.ol`
|
|
|
110
117
|
display: flex;
|
|
111
118
|
flex-direction: column;
|
|
112
119
|
`;
|
|
120
|
+
|
|
121
|
+
export const Empty = styled.div`
|
|
122
|
+
display: grid;
|
|
123
|
+
gap: 12px;
|
|
124
|
+
justify-items: start;
|
|
125
|
+
background: #fff;
|
|
126
|
+
border: 1px dashed ${primitiveColors.gray[300]};
|
|
127
|
+
border-radius: 12px;
|
|
128
|
+
padding: 16px;
|
|
129
|
+
color: ${semanticColors.global.text.subtitle.enabled};
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
export const RetryButton = styled.button`
|
|
133
|
+
appearance: none;
|
|
134
|
+
border: 1px solid ${semanticColors.branding.surface.enabled};
|
|
135
|
+
background: ${semanticColors.branding.surface.enabled};
|
|
136
|
+
color: ${primitiveColors.base.white};
|
|
137
|
+
border-radius: 10px;
|
|
138
|
+
padding: 8px 12px;
|
|
139
|
+
font-size: ${typographyTokens.fontSizes.bodyM};
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
|
|
142
|
+
&:hover {
|
|
143
|
+
filter: brightness(0.95);
|
|
144
|
+
}
|
|
145
|
+
&:active {
|
|
146
|
+
filter: brightness(0.9);
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
@@ -1,8 +1,63 @@
|
|
|
1
|
+
// src/components/molecules/TimeLine/TimeLine.tsx
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ItemWrap,
|
|
5
|
+
Card,
|
|
6
|
+
Sub,
|
|
7
|
+
Row,
|
|
8
|
+
Dot,
|
|
9
|
+
List,
|
|
10
|
+
Title,
|
|
11
|
+
Empty,
|
|
12
|
+
RetryButton,
|
|
13
|
+
} from './TimeLine.styles';
|
|
3
14
|
import { TimelineProps } from './TimeLine.types';
|
|
15
|
+
import { Skeleton } from '../../atoms/Skeleton/Skeleton';
|
|
16
|
+
|
|
17
|
+
export const Timeline: React.FC<TimelineProps> = ({
|
|
18
|
+
items,
|
|
19
|
+
loading = false,
|
|
20
|
+
onRetry,
|
|
21
|
+
emptyLabel = 'Nenhum evento encontrado.',
|
|
22
|
+
skeletonItems = 3,
|
|
23
|
+
}) => {
|
|
24
|
+
if (loading) {
|
|
25
|
+
return (
|
|
26
|
+
<List aria-label="Linha do tempo (carregando)">
|
|
27
|
+
{Array.from({ length: skeletonItems }).map((_, i) => (
|
|
28
|
+
<ItemWrap key={`sk-${i}`}>
|
|
29
|
+
<Skeleton
|
|
30
|
+
variant="circle"
|
|
31
|
+
width={24}
|
|
32
|
+
height={24}
|
|
33
|
+
ariaLabel="Carregando ponto da linha do tempo"
|
|
34
|
+
/>
|
|
35
|
+
<Card>
|
|
36
|
+
<Skeleton variant="text" width="60%" height={32} />
|
|
37
|
+
|
|
38
|
+
<Sub>
|
|
39
|
+
<Skeleton variant="text" width="40%" height={18} />
|
|
40
|
+
</Sub>
|
|
41
|
+
</Card>
|
|
42
|
+
</ItemWrap>
|
|
43
|
+
))}
|
|
44
|
+
</List>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!items || items.length === 0) {
|
|
49
|
+
return (
|
|
50
|
+
<Empty role="status" aria-live="polite">
|
|
51
|
+
<div>{emptyLabel}</div>
|
|
52
|
+
{onRetry && (
|
|
53
|
+
<RetryButton type="button" onClick={onRetry}>
|
|
54
|
+
Tentar novamente
|
|
55
|
+
</RetryButton>
|
|
56
|
+
)}
|
|
57
|
+
</Empty>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
4
60
|
|
|
5
|
-
export const Timeline: React.FC<TimelineProps> = ({ items }) => {
|
|
6
61
|
return (
|
|
7
62
|
<List aria-label="Linha do tempo">
|
|
8
63
|
{items.map((it) => {
|
|
@@ -17,7 +72,7 @@ export const Timeline: React.FC<TimelineProps> = ({ items }) => {
|
|
|
17
72
|
|
|
18
73
|
return (
|
|
19
74
|
<ItemWrap key={it.id}>
|
|
20
|
-
{it?.type && <Dot aria-hidden $variant={it.type || '
|
|
75
|
+
{it?.type && <Dot aria-hidden $variant={it.type || 'continued'} />}
|
|
21
76
|
<Card>
|
|
22
77
|
<Row style={{ justifyContent: 'space-between' }}>
|
|
23
78
|
<Title>{it.title}</Title>
|
|
@@ -3,7 +3,8 @@ export type TimelineVariant =
|
|
|
3
3
|
| 'accepted'
|
|
4
4
|
| 'continued'
|
|
5
5
|
| 'rejected'
|
|
6
|
-
| 'download'
|
|
6
|
+
| 'download'
|
|
7
|
+
| 'issue';
|
|
7
8
|
|
|
8
9
|
export interface TimelineItem {
|
|
9
10
|
id: string;
|
|
@@ -19,6 +20,10 @@ export interface TimelineItem {
|
|
|
19
20
|
|
|
20
21
|
export type TimelineProps = {
|
|
21
22
|
items: TimelineItem[];
|
|
23
|
+
loading?: boolean;
|
|
24
|
+
onRetry?: () => void;
|
|
25
|
+
emptyLabel?: string;
|
|
26
|
+
skeletonItems?: number;
|
|
22
27
|
};
|
|
23
28
|
|
|
24
29
|
export interface TimelineActor {
|
|
@@ -37,6 +42,7 @@ export type RemoteEventType =
|
|
|
37
42
|
| 'subscription:partner_quote'
|
|
38
43
|
| 'subscription:partner_subscribe'
|
|
39
44
|
| 'order:proposal_refused'
|
|
45
|
+
| 'issue:partner_issue'
|
|
40
46
|
| string;
|
|
41
47
|
|
|
42
48
|
export interface RemoteEventActor {
|
|
@@ -9,11 +9,17 @@ export const TimelineModal = ({
|
|
|
9
9
|
onOpenChange,
|
|
10
10
|
onClose,
|
|
11
11
|
data,
|
|
12
|
+
onRetry,
|
|
13
|
+
emptyLabel,
|
|
14
|
+
loading,
|
|
12
15
|
}: {
|
|
13
16
|
open: boolean;
|
|
14
17
|
onOpenChange: (open: boolean) => void;
|
|
15
18
|
onClose: () => void;
|
|
19
|
+
onRetry?: () => void;
|
|
20
|
+
emptyLabel?: string;
|
|
16
21
|
data: any;
|
|
22
|
+
loading?: boolean;
|
|
17
23
|
}) => {
|
|
18
24
|
return (
|
|
19
25
|
<Dialog.Root open={open} onOpenChange={onOpenChange}>
|
|
@@ -30,7 +36,13 @@ export const TimelineModal = ({
|
|
|
30
36
|
</Title>
|
|
31
37
|
</Dialog.Title>
|
|
32
38
|
|
|
33
|
-
<Timeline
|
|
39
|
+
<Timeline
|
|
40
|
+
items={data}
|
|
41
|
+
onRetry={onRetry}
|
|
42
|
+
emptyLabel={emptyLabel}
|
|
43
|
+
skeletonItems={4}
|
|
44
|
+
loading={loading}
|
|
45
|
+
/>
|
|
34
46
|
</Content>
|
|
35
47
|
</Dialog.Root>
|
|
36
48
|
);
|