ngx-opalbytes-components 1.20.0 → 1.22.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/README.md +75 -0
- package/cli/cao-comp.cjs +272 -2
- package/fesm2022/ngx-opalbytes-components.mjs +291 -19
- package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/shared/components/autocomplete/autocomplete.stories.ts +133 -0
- package/src/lib/shared/components/base-button/base-button.stories.ts +102 -0
- package/src/lib/shared/components/base-icon/base-icon.stories.ts +56 -0
- package/src/lib/shared/components/base-input/base-input.stories.ts +137 -0
- package/src/lib/shared/components/base-time-range/base-time-range.stories.ts +54 -0
- package/src/lib/shared/components/base-toggle/base-toggle.stories.ts +63 -0
- package/src/lib/shared/components/card/card.stories.ts +114 -0
- package/src/lib/shared/components/date-picker/date-picker.stories.ts +91 -0
- package/src/lib/shared/components/drop-down/drop-down.stories.ts +90 -0
- package/src/lib/shared/components/footer/footer.stories.ts +51 -0
- package/src/lib/shared/components/index.ts +6 -1
- package/src/lib/shared/components/kpi/kpi.stories.ts +65 -0
- package/src/lib/shared/components/links-button/links-button.stories.ts +75 -0
- package/src/lib/shared/components/paginator/paginator.stories.ts +56 -0
- package/src/lib/shared/components/skeleton/skeleton.css +92 -0
- package/src/lib/shared/components/skeleton/skeleton.html +3 -0
- package/src/lib/shared/components/skeleton/skeleton.spec.ts +22 -0
- package/src/lib/shared/components/skeleton/skeleton.stories.ts +131 -0
- package/src/lib/shared/components/skeleton/skeleton.ts +46 -0
- package/src/lib/shared/components/stepper/stepper.stories.ts +77 -0
- package/src/lib/shared/components/tab-group/tab-group.stories.ts +102 -0
- package/src/lib/shared/components/tab-panel/tab-panel.stories.ts +54 -0
- package/src/lib/shared/components/time-picker/time-picker.stories.ts +66 -0
- package/types/ngx-opalbytes-components.d.ts +110 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { CaoDatePicker } from './date-picker';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<CaoDatePicker> = {
|
|
7
|
+
title: 'Componentes/Date Picker',
|
|
8
|
+
component: CaoDatePicker,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
placeholder: { control: 'text' },
|
|
12
|
+
inputClass: { control: 'text' },
|
|
13
|
+
errorMessage: { control: 'text' },
|
|
14
|
+
isDisabled: { control: 'boolean' },
|
|
15
|
+
searchMode: { control: 'boolean' },
|
|
16
|
+
showBorder: { control: 'boolean' },
|
|
17
|
+
isLucideIcon: { control: 'boolean' },
|
|
18
|
+
iconColor: { control: 'color' },
|
|
19
|
+
tooltip: { control: 'text' },
|
|
20
|
+
dataCy: { control: 'text' },
|
|
21
|
+
dateHint: { control: 'text' },
|
|
22
|
+
controlName: { control: 'text' },
|
|
23
|
+
openOnFocus: { control: 'boolean' },
|
|
24
|
+
minDate: { control: 'date' },
|
|
25
|
+
maxDate: { control: 'date' },
|
|
26
|
+
},
|
|
27
|
+
args: {
|
|
28
|
+
valueChange: fn(),
|
|
29
|
+
searchClick: fn(),
|
|
30
|
+
inputChange: fn(),
|
|
31
|
+
leadingIconClick: fn(),
|
|
32
|
+
trailingIconClick: fn(),
|
|
33
|
+
placeholder: 'Data de nascimento',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|
|
38
|
+
type Story = StoryObj<CaoDatePicker>;
|
|
39
|
+
|
|
40
|
+
export const Padrao: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
placeholder: 'Selecione uma data',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const ComLeadingIcon: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
placeholder: 'Selecione uma data',
|
|
49
|
+
leadingIcon: 'Calendar',
|
|
50
|
+
isLucideIcon: true,
|
|
51
|
+
iconColor: '#3b82f6',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const ComTrailingIcon: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
placeholder: 'Selecione uma data',
|
|
58
|
+
trailingIcon: 'X',
|
|
59
|
+
isLucideIcon: true,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const ComDataMinimaEMaxima: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
placeholder: 'Selecione uma data',
|
|
66
|
+
minDate: new Date(2026, 0, 1),
|
|
67
|
+
maxDate: new Date(2026, 11, 31),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const ComDica: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
placeholder: 'Selecione uma data',
|
|
74
|
+
dateHint: 'DD/MM/AAAA',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const Desabilitado: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
placeholder: 'Selecione uma data',
|
|
81
|
+
isDisabled: true,
|
|
82
|
+
value: new Date(2026, 0, 15),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const ComErro: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
placeholder: 'Selecione uma data',
|
|
89
|
+
errorMessage: 'Data obrigatória',
|
|
90
|
+
},
|
|
91
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { CaoDropDownComponent, ICaoDropDownOption } from './drop-down';
|
|
5
|
+
|
|
6
|
+
const defaultOptions: ICaoDropDownOption[] = [
|
|
7
|
+
{ id: 1, nome: 'Ativo' },
|
|
8
|
+
{ id: 2, nome: 'Inativo' },
|
|
9
|
+
{ id: 3, nome: 'Pendente' },
|
|
10
|
+
{ id: 4, nome: 'Suspenso' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const optionsWithIcons: ICaoDropDownOption[] = [
|
|
14
|
+
{ id: 1, nome: 'Ativo', icon: 'check_circle' },
|
|
15
|
+
{ id: 2, nome: 'Inativo', icon: 'cancel' },
|
|
16
|
+
{ id: 3, nome: 'Pendente', icon: 'schedule' },
|
|
17
|
+
{ id: 4, nome: 'Suspenso', icon: 'block' },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const meta: Meta<CaoDropDownComponent> = {
|
|
21
|
+
title: 'Componentes/Drop Down',
|
|
22
|
+
component: CaoDropDownComponent,
|
|
23
|
+
tags: ['autodocs'],
|
|
24
|
+
argTypes: {
|
|
25
|
+
textHeader: { control: 'text' },
|
|
26
|
+
options: { control: 'object' },
|
|
27
|
+
dropDownClass: { control: 'text' },
|
|
28
|
+
isDisabled: { control: 'boolean' },
|
|
29
|
+
tooltip: { control: 'text' },
|
|
30
|
+
dropDirectionInput: { control: 'select', options: ['up', 'down'] },
|
|
31
|
+
dataCy: { control: 'text' },
|
|
32
|
+
ariaLabel: { control: 'text' },
|
|
33
|
+
ariaLabelledBy: { control: 'text' },
|
|
34
|
+
listAriaLabel: { control: 'text' },
|
|
35
|
+
},
|
|
36
|
+
args: {
|
|
37
|
+
itemSelected: fn(),
|
|
38
|
+
textHeader: 'SELECIONE UM ITEM',
|
|
39
|
+
options: defaultOptions,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default meta;
|
|
44
|
+
type Story = StoryObj<CaoDropDownComponent>;
|
|
45
|
+
|
|
46
|
+
export const Padrao: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
textHeader: 'Selecione um status',
|
|
49
|
+
options: defaultOptions,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const ComIcones: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
textHeader: 'Selecione um status',
|
|
56
|
+
options: optionsWithIcons,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Desabilitado: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
textHeader: 'Selecione um status',
|
|
63
|
+
options: defaultOptions,
|
|
64
|
+
isDisabled: true,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const AberturaParaCima: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
textHeader: 'Selecione um status',
|
|
71
|
+
options: defaultOptions,
|
|
72
|
+
dropDirectionInput: 'up',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const ComTooltip: Story = {
|
|
77
|
+
args: {
|
|
78
|
+
textHeader: 'Selecione um status',
|
|
79
|
+
options: defaultOptions,
|
|
80
|
+
tooltip: 'Escolha um dos status disponíveis',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const ClassePersonalizada: Story = {
|
|
85
|
+
args: {
|
|
86
|
+
textHeader: 'Selecione um status',
|
|
87
|
+
options: defaultOptions,
|
|
88
|
+
dropDownClass: 'dropdown-secundary',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
|
|
3
|
+
import { Footer } from './footer';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<Footer> = {
|
|
6
|
+
title: 'Componentes/Footer',
|
|
7
|
+
component: Footer,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
currentRoute: { control: 'text' },
|
|
11
|
+
footerText: { control: 'text' },
|
|
12
|
+
isFixed: { control: 'boolean' },
|
|
13
|
+
},
|
|
14
|
+
args: {
|
|
15
|
+
footerText: 'Opalbytes',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
type Story = StoryObj<Footer>;
|
|
21
|
+
|
|
22
|
+
export const Padrao: Story = {
|
|
23
|
+
args: {
|
|
24
|
+
footerText: 'Opalbytes',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const Fixo: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
footerText: 'Opalbytes',
|
|
31
|
+
isFixed: true,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const ComRota: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
footerText: 'Opalbytes',
|
|
38
|
+
currentRoute: '/dashboard',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const ComConteudo: Story = {
|
|
43
|
+
render: (args) => ({
|
|
44
|
+
template: `
|
|
45
|
+
<cao-footer [currentRoute]="currentRoute" [footerText]="footerText" [isFixed]="isFixed">
|
|
46
|
+
<div>Links úteis: <a href="#">Termos</a> | <a href="#">Privacidade</a></div>
|
|
47
|
+
</cao-footer>
|
|
48
|
+
`,
|
|
49
|
+
props: args,
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
@@ -16,4 +16,9 @@ export * from "./tab-group/tab-group"
|
|
|
16
16
|
export * from "./tab-panel/tab-panel"
|
|
17
17
|
|
|
18
18
|
export * from "./base-icon/base-icon"
|
|
19
|
-
export * from "./kpi/kpi"
|
|
19
|
+
export * from "./kpi/kpi"
|
|
20
|
+
export * from "./skeleton/skeleton"
|
|
21
|
+
|
|
22
|
+
export * from "./card/card.component"
|
|
23
|
+
export * from "./base-toggle/base-toggle.component"
|
|
24
|
+
export * from "./footer/footer"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
|
|
3
|
+
import { CaoKpi, ICaoKPI } from './kpi';
|
|
4
|
+
|
|
5
|
+
const defaultKpi: ICaoKPI = {
|
|
6
|
+
title: 'Faturamento',
|
|
7
|
+
value: 'R$ 128.450,00',
|
|
8
|
+
textColor: '#111827',
|
|
9
|
+
borderColor: '#e5e7eb',
|
|
10
|
+
icon: {
|
|
11
|
+
src: 'https://lucide.dev/icons/trending-up.svg',
|
|
12
|
+
alt: 'ícone de tendência',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const meta: Meta<CaoKpi> = {
|
|
17
|
+
title: 'Componentes/KPI',
|
|
18
|
+
component: CaoKpi,
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
argTypes: {
|
|
21
|
+
kpiData: { control: 'object' },
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
kpiData: defaultKpi,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default meta;
|
|
29
|
+
type Story = StoryObj<CaoKpi>;
|
|
30
|
+
|
|
31
|
+
export const Padrao: Story = {
|
|
32
|
+
args: {
|
|
33
|
+
kpiData: defaultKpi,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Sucesso: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
kpiData: {
|
|
40
|
+
title: 'Meta de vendas',
|
|
41
|
+
value: '87%',
|
|
42
|
+
textColor: '#16a34a',
|
|
43
|
+
borderColor: '#16a34a',
|
|
44
|
+
icon: {
|
|
45
|
+
src: 'https://lucide.dev/icons/check-circle.svg',
|
|
46
|
+
alt: 'ícone de sucesso',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Alerta: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
kpiData: {
|
|
55
|
+
title: 'Pedidos pendentes',
|
|
56
|
+
value: '32',
|
|
57
|
+
textColor: '#dc2626',
|
|
58
|
+
borderColor: '#dc2626',
|
|
59
|
+
icon: {
|
|
60
|
+
src: 'https://lucide.dev/icons/alert-triangle.svg',
|
|
61
|
+
alt: 'ícone de alerta',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { signal } from '@angular/core';
|
|
2
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
import { type Meta, type StoryObj, moduleMetadata } from '@storybook/angular-vite';
|
|
5
|
+
|
|
6
|
+
import { LinksButton, INavContent } from './links-button';
|
|
7
|
+
|
|
8
|
+
const defaultNav: INavContent = {
|
|
9
|
+
title: 'Navegação',
|
|
10
|
+
links: [
|
|
11
|
+
{ text: 'Dashboard', url: '/dashboard' },
|
|
12
|
+
{ text: 'Relatórios', url: '/relatorios' },
|
|
13
|
+
{ text: 'Configurações', url: '/configuracoes' },
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const meta: Meta<LinksButton> = {
|
|
18
|
+
title: 'Componentes/Links Button',
|
|
19
|
+
component: LinksButton,
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
decorators: [
|
|
22
|
+
moduleMetadata({
|
|
23
|
+
providers: [
|
|
24
|
+
{ provide: Router, useValue: { navigate: () => Promise.resolve(true) } },
|
|
25
|
+
{ provide: ActivatedRoute, useValue: {} },
|
|
26
|
+
],
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
type Story = StoryObj<LinksButton>;
|
|
33
|
+
|
|
34
|
+
export const Padrao: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
navContent: signal(defaultNav),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const ComLinkDesabilitado: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
navContent: signal({
|
|
43
|
+
title: 'Navegação',
|
|
44
|
+
links: [
|
|
45
|
+
{ text: 'Dashboard', url: '/dashboard' },
|
|
46
|
+
{ text: 'Indisponível', url: '/x', disabled: true },
|
|
47
|
+
{ text: 'Configurações', url: '/configuracoes' },
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const ComRotaDireta: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
navContent: signal({
|
|
56
|
+
title: 'Navegação externa',
|
|
57
|
+
links: [
|
|
58
|
+
{ text: 'Site oficial', url: 'https://example.com', directRoute: true },
|
|
59
|
+
{ text: 'Dashboard', url: '/dashboard' },
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const ComDataCy: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
navContent: signal({
|
|
68
|
+
title: 'Navegação instrumentada',
|
|
69
|
+
links: [
|
|
70
|
+
{ text: 'Dashboard', url: '/dashboard', dataCy: 'link-dashboard' },
|
|
71
|
+
{ text: 'Relatórios', url: '/relatorios', dataCy: 'link-relatorios' },
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { Paginator } from './paginator';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<Paginator> = {
|
|
7
|
+
title: 'Componentes/Paginator',
|
|
8
|
+
component: Paginator,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
totalItems: { control: { type: 'number', min: 0 } },
|
|
12
|
+
pageSize: { control: { type: 'number', min: 1 } },
|
|
13
|
+
currentPage: { control: { type: 'number', min: 1 } },
|
|
14
|
+
pageSizeOptions: { control: 'object' },
|
|
15
|
+
customClass: { control: 'text' },
|
|
16
|
+
showFirstLastButtons: { control: 'boolean' },
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
pageEvent: fn(),
|
|
20
|
+
totalItems: 250,
|
|
21
|
+
pageSize: 10,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<Paginator>;
|
|
27
|
+
|
|
28
|
+
export const Padrao: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
totalItems: 250,
|
|
31
|
+
pageSize: 10,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const OpcoesPersonalizadas: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
totalItems: 1000,
|
|
38
|
+
pageSize: 25,
|
|
39
|
+
pageSizeOptions: [10, 25, 50, 100],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const SemPrimeiroEUltimo: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
totalItems: 250,
|
|
46
|
+
pageSize: 10,
|
|
47
|
+
showFirstLastButtons: false,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const VariasPaginas: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
totalItems: 3,
|
|
54
|
+
pageSize: 1,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* CSS Custom Properties — all prefixed with --skeleton-*
|
|
3
|
+
* These cascade naturally, so a parent component can override any token:
|
|
4
|
+
*
|
|
5
|
+
* .my-card {
|
|
6
|
+
* --skeleton-bg: #d1d5db;
|
|
7
|
+
* --skeleton-shine: #f3f4f6;
|
|
8
|
+
* --skeleton-radius: 8px;
|
|
9
|
+
* --skeleton-speed: 2s;
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
:host {
|
|
14
|
+
/* ─── Layout defaults (overridable via [width]/[height] inputs) ─── */
|
|
15
|
+
--skeleton-width: 100%;
|
|
16
|
+
--skeleton-height: 100%;
|
|
17
|
+
|
|
18
|
+
/* ─── Visual tokens — inherit from parent if set there ─── */
|
|
19
|
+
--skeleton-bg: #e2e8f0;
|
|
20
|
+
--skeleton-shine: #f8fafc;
|
|
21
|
+
--skeleton-radius: 14px;
|
|
22
|
+
--skeleton-speed: 2s;
|
|
23
|
+
--skeleton-animation-deg: 90deg;
|
|
24
|
+
|
|
25
|
+
display: contents; /* host element is transparent to layout */
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ─────────────────────────── Base block ─────────────────────────── */
|
|
29
|
+
|
|
30
|
+
.cao-skeleton {
|
|
31
|
+
position: relative;
|
|
32
|
+
display: block;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
width: var(--skeleton-width);
|
|
35
|
+
height: var(--skeleton-height);
|
|
36
|
+
background-color: var(--skeleton-bg);
|
|
37
|
+
border-radius: var(--skeleton-radius);
|
|
38
|
+
|
|
39
|
+
/* Prevent content from leaking during SSR */
|
|
40
|
+
color: transparent;
|
|
41
|
+
user-select: none;
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
|
|
44
|
+
/* Respect reduced-motion preference */
|
|
45
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
46
|
+
will-change: transform; /* promote shimmer layer */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* ─────────────────────────── Variants ───────────────────────────── */
|
|
51
|
+
|
|
52
|
+
.cao-skeleton--circle {
|
|
53
|
+
--skeleton-radius: 50%;
|
|
54
|
+
/* For circles the consumer usually sets equal width/height;
|
|
55
|
+
but we guard with aspect-ratio so it stays circular even
|
|
56
|
+
when only width is specified. */
|
|
57
|
+
aspect-ratio: 1 / 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.cao-skeleton--text {
|
|
61
|
+
--skeleton-radius: 4px;
|
|
62
|
+
height: 1em; /* matches current font-size of parent context */
|
|
63
|
+
width: var(--skeleton-width);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* ─────────────────────────── Shimmer ────────────────────────────── */
|
|
67
|
+
|
|
68
|
+
.cao-skeleton__shimmer {
|
|
69
|
+
position: absolute;
|
|
70
|
+
inset: 0;
|
|
71
|
+
|
|
72
|
+
background: linear-gradient(
|
|
73
|
+
var(--skeleton-animation-deg),
|
|
74
|
+
transparent 30%,
|
|
75
|
+
var(--skeleton-shine) 50%,
|
|
76
|
+
transparent 70%
|
|
77
|
+
);
|
|
78
|
+
background-size: 200% 100%;
|
|
79
|
+
|
|
80
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
81
|
+
animation: skeleton-shimmer var(--skeleton-speed) ease-in-out infinite;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@keyframes skeleton-shimmer {
|
|
86
|
+
0% {
|
|
87
|
+
background-position: 200% center;
|
|
88
|
+
}
|
|
89
|
+
100% {
|
|
90
|
+
background-position: -200% center;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CaoSkeletonComponent } from './skeleton';
|
|
4
|
+
|
|
5
|
+
describe('CaoSkeletonComponent', () => {
|
|
6
|
+
let component: CaoSkeletonComponent;
|
|
7
|
+
let fixture: ComponentFixture<CaoSkeletonComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [CaoSkeletonComponent],
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(CaoSkeletonComponent);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
await fixture.whenStable();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should create', () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
|
|
3
|
+
import { CaoSkeletonComponent } from './skeleton';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<CaoSkeletonComponent> = {
|
|
6
|
+
title: 'Componentes/Skeleton',
|
|
7
|
+
component: CaoSkeletonComponent,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
variant: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: ['rect', 'circle', 'text'],
|
|
13
|
+
},
|
|
14
|
+
width: { control: 'text' },
|
|
15
|
+
height: { control: 'text' },
|
|
16
|
+
size: { control: 'text' },
|
|
17
|
+
bgColor: { control: 'color' },
|
|
18
|
+
shine: { control: 'color' },
|
|
19
|
+
borderRadius: { control: 'text' },
|
|
20
|
+
speed: { control: 'text' },
|
|
21
|
+
animationDeg: { control: 'text' },
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
variant: 'rect',
|
|
25
|
+
width: '200px',
|
|
26
|
+
height: '40px',
|
|
27
|
+
bgColor: '#e2e8f0',
|
|
28
|
+
shine: '#f8fafc',
|
|
29
|
+
speed: '2s',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<CaoSkeletonComponent>;
|
|
35
|
+
|
|
36
|
+
export const Retangulo: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
variant: 'rect',
|
|
39
|
+
width: '200px',
|
|
40
|
+
height: '40px',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const RetanguloComBordaArredondada: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
variant: 'rect',
|
|
47
|
+
width: '200px',
|
|
48
|
+
height: '60px',
|
|
49
|
+
borderRadius: '8px',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const Circulos: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
bgColor: '#8100ff',
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
render: (args) => ({
|
|
59
|
+
props: args,
|
|
60
|
+
template: `
|
|
61
|
+
<div style="display: flex; align-items: center; gap: 16px;">
|
|
62
|
+
<cao-skeleton [size]="'24px'" [variant]="'circle'"></cao-skeleton>
|
|
63
|
+
<cao-skeleton [size]="'48px'" [variant]="'circle'"></cao-skeleton>
|
|
64
|
+
<cao-skeleton [size]="'80px'" [variant]="'circle'"></cao-skeleton>
|
|
65
|
+
</div>
|
|
66
|
+
`,
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const RetangulosComTamanhosVariados: Story = {
|
|
71
|
+
render: () => ({
|
|
72
|
+
template: `
|
|
73
|
+
<div style="display: flex; flex-direction: column; gap: 12px; width: 320px;">
|
|
74
|
+
<cao-skeleton [width]="'100%'" [height]="'12px'"></cao-skeleton>
|
|
75
|
+
<cao-skeleton [width]="'80%'" [height]="'12px'"></cao-skeleton>
|
|
76
|
+
<cao-skeleton [width]="'60%'" [height]="'12px'"></cao-skeleton>
|
|
77
|
+
</div>
|
|
78
|
+
`,
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const LinhaDeTexto: Story = {
|
|
83
|
+
render: (args) => ({
|
|
84
|
+
props: args,
|
|
85
|
+
template: `
|
|
86
|
+
<div style="display: flex; flex-direction: column; gap: 12px; width: 260px;">
|
|
87
|
+
<cao-skeleton [variant]="'text'" [width]="'100%'"></cao-skeleton>
|
|
88
|
+
<cao-skeleton [variant]="'text'" [width]="'90%'"></cao-skeleton>
|
|
89
|
+
<cao-skeleton [variant]="'text'" [width]="'70%'"></cao-skeleton>
|
|
90
|
+
</div>
|
|
91
|
+
`,
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const CorCustomizada: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
variant: 'rect',
|
|
98
|
+
width: '220px',
|
|
99
|
+
height: '48px',
|
|
100
|
+
bgColor: '#e0e7ff',
|
|
101
|
+
shine: '#ffffff',
|
|
102
|
+
borderRadius: '12px',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const AnimacaoRapida: Story = {
|
|
107
|
+
args: {
|
|
108
|
+
variant: 'rect',
|
|
109
|
+
width: '200px',
|
|
110
|
+
height: '40px',
|
|
111
|
+
speed: '0.8s',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const AnimacaoLenta: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
variant: 'rect',
|
|
118
|
+
width: '200px',
|
|
119
|
+
height: '40px',
|
|
120
|
+
speed: '4s',
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const GradienteVertical: Story = {
|
|
125
|
+
args: {
|
|
126
|
+
variant: 'rect',
|
|
127
|
+
width: '200px',
|
|
128
|
+
height: '40px',
|
|
129
|
+
animationDeg: '0deg',
|
|
130
|
+
},
|
|
131
|
+
};
|