@consumidor-positivo/aurora 1.2.0 → 1.3.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 +59 -3
- package/astro/AdBox/index.astro +28 -0
- package/astro/Alert/index.astro +257 -0
- package/astro/BadgeInfo/index.astro +55 -0
- package/astro/BadgeState/index.astro +79 -0
- package/astro/Button/index.astro +11 -3
- package/astro/Card/Container/index.astro +44 -0
- package/astro/Card/Emphasis/index.astro +39 -0
- package/astro/Card/Image/index.astro +26 -0
- package/astro/Card/Root/index.astro +53 -0
- package/astro/Card/Tag/index.astro +26 -0
- package/astro/Chip/index.astro +48 -0
- package/astro/ChipBanner/index.astro +160 -0
- package/astro/Container/index.astro +16 -0
- package/astro/Divider/index.astro +27 -0
- package/astro/Image/index.astro +26 -0
- package/astro/LazyImage/index.astro +55 -0
- package/astro/LinkButton/index.astro +54 -0
- package/astro/Logo/ac/BadgeFullWhite.astro +20 -0
- package/astro/Logo/ac/BadgeWhite.astro +23 -0
- package/astro/Logo/ac/BadgeWhiteStroke.astro +27 -0
- package/astro/Logo/ac/BadgeWhiteStrokeDefault.astro +28 -0
- package/astro/Logo/ac/Badget.astro +24 -0
- package/astro/Logo/ac/Primary.astro +28 -0
- package/astro/Logo/ac/PrimaryFillWhite.astro +28 -0
- package/astro/Logo/ac/PrimaryFullWhite.astro +20 -0
- package/astro/Logo/ac/PrimaryNegative.astro +20 -0
- package/astro/Logo/ac/PrimaryWhite.astro +20 -0
- package/astro/Logo/ac/TertiaryWhite.astro +72 -0
- package/astro/Logo/cp/BadgeWhiteStroke.astro +10 -0
- package/astro/Logo/cp/Badget.astro +14 -0
- package/astro/Logo/cp/PrimaryFillWhite.astro +63 -0
- package/astro/Logo/cp/PrimaryFullWhite.astro +27 -0
- package/astro/Logo/cp/PrimaryLogoWhite.astro +67 -0
- package/astro/Logo/cp/PrimaryNegative.astro +27 -0
- package/astro/Logo/cp/PrimaryWhite.astro +27 -0
- package/astro/Modal/index.astro +213 -0
- package/astro/NotificationsBar/Link/index.astro +70 -0
- package/astro/NotificationsBar/List/index.astro +38 -0
- package/astro/NotificationsBar/index.astro +24 -0
- package/astro/PartnerBanner/index.astro +52 -0
- package/astro/ProfileNav/index.astro +34 -0
- package/astro/ProgressBar/index.astro +70 -0
- package/astro/Skeleton/index.astro +50 -0
- package/astro/SpecialButton/index.astro +331 -0
- package/astro/Spinner/index.astro +75 -0
- package/astro/SubHeader/index.astro +57 -0
- package/astro/Switch/Card/index.astro +69 -0
- package/astro/Switch/Pure/index.astro +47 -0
- package/astro/Tooltip/index.astro +38 -0
- package/astro/form/Checkbox/Field/index.astro +68 -0
- package/astro/form/Checkbox/Group/index.astro +79 -0
- package/astro/form/Datepicker/Calendar/index.astro +53 -0
- package/astro/form/Datepicker/CalendarHeader/index.astro +57 -0
- package/astro/form/Datepicker/Segment/index.astro +53 -0
- package/astro/form/Datepicker/index.astro +756 -0
- package/astro/form/EmailField/index.astro +237 -0
- package/astro/form/Field/ErrorMessage/index.astro +20 -0
- package/astro/form/Field/Input/index.astro +15 -0
- package/astro/form/Field/InputHolder/index.astro +28 -0
- package/astro/form/Field/Label/index.astro +80 -0
- package/astro/form/Field/Message/index.astro +29 -0
- package/astro/form/Field/Root/index.astro +37 -0
- package/astro/form/Field/TextArea/index.astro +69 -0
- package/astro/form/InputField/index.astro +96 -0
- package/astro/form/PasswordField/index.astro +123 -0
- package/astro/form/Radio/Field/index.astro +52 -0
- package/astro/form/Radio/Group/index.astro +78 -0
- package/astro/form/SelectField/index.astro +561 -0
- package/astro/form/TextareaField/index.astro +81 -0
- package/astro/form/TokenField/index.astro +211 -0
- package/astro/tokens/package.json +6 -0
- package/dist/{tokens-C4ypB4bY.js → astro/tokens/index.es.js} +101 -101
- package/dist/astro/tokens/index.es.js.map +1 -0
- package/dist/components/Alert/index.es.js +1 -1
- package/dist/components/BadgeInfo/index.es.js +1 -1
- package/dist/components/BadgeState/index.es.js +1 -1
- package/dist/components/Image/index.es.js +1 -1
- package/dist/components/Spinner/index.es.js +1 -1
- package/dist/components/form/Checkbox/Field/index.es.js +1 -1
- package/dist/components/form/Datepicker/Calendar/index.es.js +2 -2
- package/dist/components/form/Datepicker/CalendarHeader/index.es.js +2 -2
- package/dist/components/form/Datepicker/PortalHolder/index.es.js +1 -1
- package/dist/components/form/Datepicker/Segment/index.es.js +1 -1
- package/dist/components/form/Datepicker/index.es.js +3 -3
- package/dist/components/form/Field/Label/index.es.js +1 -1
- package/dist/components/form/SelectField/index.es.js +1 -1
- package/dist/{index-BcC9liUl.js → index-Bkl5OBIY.js} +3 -3
- package/dist/{index-BcC9liUl.js.map → index-Bkl5OBIY.js.map} +1 -1
- package/dist/{index-CFYmtkje.js → index-CZfMkAoF.js} +2 -2
- package/dist/{index-CFYmtkje.js.map → index-CZfMkAoF.js.map} +1 -1
- package/dist/{index-CeClo_WS.js → index-Cw1KtRQr.js} +2 -2
- package/dist/{index-CeClo_WS.js.map → index-Cw1KtRQr.js.map} +1 -1
- package/dist/main.es.js +101 -101
- package/package.json +7 -1
- package/dist/tokens-C4ypB4bY.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,18 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
O Projeto Aurora é uma abordagem estruturada e abrangente para o Design System dos projetos Consumidor Positivo e Acordo Certo, criado com o objetivo de padronizar e otimizar a experiência do usuário em ambas as plataformas. Este sistema de design consiste em uma biblioteca de componentes reutilizáveis, diretrizes de estilo, padrões de interação e documentação detalhada para garantir consistência e eficiência no desenvolvimento e design de interfaces.
|
|
8
8
|
|
|
9
|
+
Cada componente visual é publicado em **dois formatos**: **React**, para as aplicações (área logada, formulários com estado), e **Astro**, para as páginas públicas estáticas, que assim não carregam React. Os dois usam a mesma marcação e o mesmo CSS. Veja [Dois formatos: React e Astro](#dois-formatos-react-e-astro).
|
|
10
|
+
|
|
9
11
|
<p align="center">
|
|
10
12
|
<a href="#tech-stack">Tech Stack</a> •
|
|
11
|
-
<a href="#sites">Sites</a>
|
|
13
|
+
<a href="#sites">Sites</a> •
|
|
14
|
+
<a href="#dois-formatos-react-e-astro">React e Astro</a> •
|
|
15
|
+
<a href="#desenvolvimento">Desenvolvimento</a>
|
|
12
16
|
</p>
|
|
13
17
|
|
|
14
18
|
### Tech Stack
|
|
15
19
|
|
|
16
20
|
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/2edb6c43-89a3-4c08-b126-96771d488e9b" /> [Reactjs](https://reactjs.org/) ( JS lib )
|
|
21
|
+
- <img width="20" height="20" src="https://astro.build/favicon.svg" /> [Astro](https://astro.build/) ( versão estática dos componentes )
|
|
17
22
|
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/116d0ad2-55a8-4bd6-a8b8-4fbdb3aab82b" /> [Typescript](https://www.typescriptlang.org/) ( Types )
|
|
18
23
|
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/862d66f3-703d-42f1-8e70-3e6ab5c6d318" /> [Vitejs](https://reactjs.org/) ( Tooling )
|
|
19
24
|
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/4a977365-7a05-4131-8b11-6589acbf0831" /> [Sass](<https://emotion.sh/docs/introduction](https://sass-lang.com/)>) ( CSS )
|
|
20
|
-
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/96405885-c5c0-4e26-a645-0e84b7d12ef3" /> [Storybook](https://storybook.js.org/) ( UI )
|
|
25
|
+
- <img width="20" height="20" src="https://github.com/AcordoCertoBR/mono-debtor-hub-ui/assets/397832/96405885-c5c0-4e26-a645-0e84b7d12ef3" /> [Storybook](https://storybook.js.org/) ( UI dos componentes React; os Astro têm um playground próprio )
|
|
21
26
|
|
|
22
27
|
### Sites
|
|
23
28
|
|
|
@@ -58,9 +63,60 @@ import { Carousel } from '@consumidor-positivo/aurora'
|
|
|
58
63
|
|
|
59
64
|
> **Nota:** Se você tentar usar o Carousel sem instalar o `react-snap-carousel`, receberá um erro de dependência em tempo de execução.
|
|
60
65
|
|
|
66
|
+
## Dois formatos: React e Astro
|
|
67
|
+
|
|
68
|
+
O caminho do import decide qual versão você usa.
|
|
69
|
+
|
|
70
|
+
**React**, import nomeado do pacote:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { Button, Text } from '@consumidor-positivo/aurora'
|
|
74
|
+
|
|
75
|
+
<Button type="primary">Simular agora</Button>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Astro**, import default pelo caminho completo do arquivo, com a extensão `.astro`:
|
|
79
|
+
|
|
80
|
+
```astro
|
|
81
|
+
---
|
|
82
|
+
import '@consumidor-positivo/aurora/global.css'
|
|
83
|
+
import Button from '@consumidor-positivo/aurora/astro/Button/index.astro'
|
|
84
|
+
import Text from '@consumidor-positivo/aurora/astro/Text/index.astro'
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
<Button type="primary">Simular agora</Button>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A versão Astro renderiza em build time, sem ilha e sem React no bundle, e tem as mesmas props visuais da React. Como o Astro não tem prop de função, os callbacks viram atributos e eventos: `data-au-modal-toggle` e `data-au-drawer-toggle` abrem Modal e Drawer, e os componentes emitem eventos `au:*` (`au:tabchange`, `au:modalopen`...). Uma página feita só com componentes Astro importa o reset uma vez, pelo `global.css`.
|
|
91
|
+
|
|
92
|
+
Todo componente visual tem as duas versões. Ficam só em React os utilitários (`Transition`, `IsMobile`, `misc/*`) e o `Prototype/Carousel`. A referência completa está em [docs/astro.md](docs/astro.md) e na página "Componentes Astro" do Storybook.
|
|
93
|
+
|
|
94
|
+
## Desenvolvimento
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm install
|
|
98
|
+
npm run prebuild # gera tokens e ícones (obrigatório antes de rodar qualquer coisa)
|
|
99
|
+
npm run storybook # componentes React, porta 6006
|
|
100
|
+
npm run playground:astro # componentes Astro, porta 4321
|
|
101
|
+
npm run check:astro # type check dos .astro
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Playground Astro
|
|
105
|
+
|
|
106
|
+
O Storybook não renderiza `.astro`. Para ver os componentes Astro no navegador, o repo tem um playground em `playground/`: um site Astro mínimo, com uma página por componente, que renderiza os arquivos de `lib/` direto.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run playground:astro # http://localhost:4321
|
|
110
|
+
npm run playground:astro:build # build estático das mesmas páginas
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
O menu lateral lista as páginas e, na página aberta, os exemplos dela. O botão "Mobile" mostra a mesma página num frame de 375px, para ver o comportamento mobile (drawer, select em tela cheia, calendário) sem redimensionar a janela.
|
|
114
|
+
|
|
115
|
+
Para adicionar um exemplo, crie um arquivo em `playground/src/pages/components/`, importe o componente por `@components/<Nome>/index.astro` e envolva cada caso em `Example`. A página nova entra no menu sozinha. O CI faz o build do playground, então cada exemplo também é um teste de compilação.
|
|
116
|
+
|
|
61
117
|
## Testes
|
|
62
118
|
|
|
63
|
-
Este projeto usa Vitest para testes
|
|
119
|
+
Este projeto usa Vitest para testes unitários e @testing-library/react para testes de componentes React.
|
|
64
120
|
|
|
65
121
|
execute os testes com:
|
|
66
122
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import '../../dist/components/AdBox/styles.css'
|
|
4
|
+
|
|
5
|
+
const AD_LABEL = 'Publicidade'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Same props as React (`className` is the plain `class`). The ad content is
|
|
9
|
+
* the default slot; every HTML attribute is forwarded to the `<aside>`.
|
|
10
|
+
*/
|
|
11
|
+
export type Props = HTMLAttributes<'aside'> & {
|
|
12
|
+
type?: 'content' | 'heading'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { type = 'content', class: className, ...rest } = Astro.props
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<aside
|
|
19
|
+
class:list={['au-ad-box', `au-ad-box--type-${type}`, className]}
|
|
20
|
+
aria-label={AD_LABEL}
|
|
21
|
+
{...rest}>
|
|
22
|
+
<div class="au-ad-box__heading">
|
|
23
|
+
<span class="au-ad-box__label">{AD_LABEL}</span>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="au-ad-box__box">
|
|
26
|
+
<div class="au-ad-box__container"><slot /></div>
|
|
27
|
+
</div>
|
|
28
|
+
</aside>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import {
|
|
4
|
+
COLOR_BRAND_CYAN_50,
|
|
5
|
+
COLOR_ERROR_50,
|
|
6
|
+
COLOR_INFO_50,
|
|
7
|
+
COLOR_NEUTRAL_60,
|
|
8
|
+
COLOR_NEUTRAL_70,
|
|
9
|
+
COLOR_SUCCESS_50,
|
|
10
|
+
COLOR_WARNING_50,
|
|
11
|
+
} from '@consumidor-positivo/aurora/astro/tokens'
|
|
12
|
+
import Text from '../Text/index.astro'
|
|
13
|
+
import IconAlertCircle from '../icons/default/IconAlertCircle.astro'
|
|
14
|
+
import IconAlertOctagon from '../icons/default/IconAlertOctagon.astro'
|
|
15
|
+
import IconAlertTriangle from '../icons/default/IconAlertTriangle.astro'
|
|
16
|
+
import IconCheck from '../icons/default/IconCheck.astro'
|
|
17
|
+
import IconClock from '../icons/default/IconClock.astro'
|
|
18
|
+
import IconInfo from '../icons/default/IconInfo.astro'
|
|
19
|
+
import IconX from '../icons/default/IconX.astro'
|
|
20
|
+
import '../../dist/components/Alert/styles.css'
|
|
21
|
+
|
|
22
|
+
type AlertStatus =
|
|
23
|
+
| 'success'
|
|
24
|
+
| 'error'
|
|
25
|
+
| 'warning'
|
|
26
|
+
| 'info'
|
|
27
|
+
| 'timer'
|
|
28
|
+
| 'progress'
|
|
29
|
+
| 'neutral'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Differences from React:
|
|
33
|
+
* - `title.content` and `text` are strings; for markup, use the `title` and
|
|
34
|
+
* `text` slots instead. `customIcon` is the `icon` slot.
|
|
35
|
+
* - No callbacks. The controller emits, from the root, `au:alertaction` (the
|
|
36
|
+
* action button was clicked), `au:countdownend` (the timer reached zero) and
|
|
37
|
+
* `au:alertclose` (the close button was clicked, right before the alert is
|
|
38
|
+
* removed from the page).
|
|
39
|
+
* - With `status="timer"`, the countdown settings and the action button (which
|
|
40
|
+
* only appears once the countdown ends) travel in a `<template>` that the
|
|
41
|
+
* controller reads and removes.
|
|
42
|
+
*/
|
|
43
|
+
export type Props = Omit<HTMLAttributes<'div'>, 'title'> & {
|
|
44
|
+
showIcon?: boolean
|
|
45
|
+
/**
|
|
46
|
+
* Visual state (Figma: Alerts, States). 'timer' covers the Figma
|
|
47
|
+
* Running/Resend pair: Running while the countdown runs, Resend when it
|
|
48
|
+
* ends and the action button appears.
|
|
49
|
+
*/
|
|
50
|
+
status?: AlertStatus
|
|
51
|
+
type?: 1 | 2
|
|
52
|
+
orientation?: 'horizontal' | 'vertical'
|
|
53
|
+
title?: { content?: string; weight?: 'bold' | 'normal' }
|
|
54
|
+
text?: string
|
|
55
|
+
/** Label of the action button. With `status="timer"` it shows when the countdown ends. */
|
|
56
|
+
actionButton?: { content?: string }
|
|
57
|
+
closeButton?: boolean
|
|
58
|
+
/** Seconds of the 'timer' countdown. */
|
|
59
|
+
countdown?: number
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const {
|
|
63
|
+
showIcon = true,
|
|
64
|
+
status = 'info',
|
|
65
|
+
type = 1,
|
|
66
|
+
orientation = 'horizontal',
|
|
67
|
+
title,
|
|
68
|
+
text,
|
|
69
|
+
actionButton,
|
|
70
|
+
closeButton = false,
|
|
71
|
+
countdown = 59,
|
|
72
|
+
class: className,
|
|
73
|
+
...rest
|
|
74
|
+
} = Astro.props
|
|
75
|
+
|
|
76
|
+
const isTimer = status === 'timer'
|
|
77
|
+
const hasTitleSlot = Astro.slots.has('title')
|
|
78
|
+
const hasTextSlot = Astro.slots.has('text')
|
|
79
|
+
// Same conditions as React: the wrapper shows for any `title` object, the <h4>
|
|
80
|
+
// only with content, and the <p> always goes with the wrapper.
|
|
81
|
+
const hasTitle = !!title?.content || hasTitleSlot
|
|
82
|
+
const hasText = !!text || hasTextSlot
|
|
83
|
+
const hasCustomIcon = Astro.slots.has('icon')
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
<div
|
|
87
|
+
class:list={['au-alert', `au-alert--${status}--type-${type}`, className]}
|
|
88
|
+
data-element="au-alert"
|
|
89
|
+
{...rest}>
|
|
90
|
+
<div class="au-alert__content">
|
|
91
|
+
{
|
|
92
|
+
showIcon && (
|
|
93
|
+
<span aria-hidden="true">
|
|
94
|
+
{hasCustomIcon ? (
|
|
95
|
+
<slot name="icon" />
|
|
96
|
+
) : status === 'success' ? (
|
|
97
|
+
<IconCheck rawColor={COLOR_SUCCESS_50} />
|
|
98
|
+
) : status === 'error' ? (
|
|
99
|
+
<IconAlertOctagon rawColor={COLOR_ERROR_50} />
|
|
100
|
+
) : status === 'warning' ? (
|
|
101
|
+
<IconAlertTriangle rawColor={COLOR_WARNING_50} />
|
|
102
|
+
) : status === 'progress' ? (
|
|
103
|
+
<IconInfo rawColor={COLOR_BRAND_CYAN_50} />
|
|
104
|
+
) : status === 'neutral' ? (
|
|
105
|
+
<IconAlertCircle rawColor={COLOR_NEUTRAL_60} />
|
|
106
|
+
) : isTimer ? (
|
|
107
|
+
<div class="au-alert__timer">
|
|
108
|
+
<IconClock rawColor={COLOR_WARNING_50} />
|
|
109
|
+
<Text
|
|
110
|
+
class="au-alert__countdown"
|
|
111
|
+
variant="body-small"
|
|
112
|
+
weight="bold"
|
|
113
|
+
role="status"
|
|
114
|
+
aria-live="polite"
|
|
115
|
+
aria-atomic="true">
|
|
116
|
+
{`${countdown}s`}
|
|
117
|
+
</Text>
|
|
118
|
+
</div>
|
|
119
|
+
) : (
|
|
120
|
+
<IconInfo rawColor={COLOR_INFO_50} />
|
|
121
|
+
)}
|
|
122
|
+
</span>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
<div class={`au-alert__container--${orientation}`}>
|
|
126
|
+
{
|
|
127
|
+
(!!title || hasTitleSlot || hasText) && (
|
|
128
|
+
<div>
|
|
129
|
+
{hasTitle && (
|
|
130
|
+
<h4
|
|
131
|
+
class:list={[
|
|
132
|
+
'au-alert__title',
|
|
133
|
+
`au-alert__title--${title?.weight}`,
|
|
134
|
+
hasText && 'au-alert__title--with-text',
|
|
135
|
+
]}>
|
|
136
|
+
{hasTitleSlot ? <slot name="title" /> : title?.content}
|
|
137
|
+
</h4>
|
|
138
|
+
)}
|
|
139
|
+
<p class="au-alert__support-text">
|
|
140
|
+
{hasTextSlot ? <slot name="text" /> : text}
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
<slot />
|
|
147
|
+
|
|
148
|
+
{
|
|
149
|
+
isTimer ? (
|
|
150
|
+
<template data-au-alert-timer data-countdown={String(countdown)}>
|
|
151
|
+
{actionButton && (
|
|
152
|
+
<button class="au-alert__action-btn" type="button">
|
|
153
|
+
{actionButton.content}
|
|
154
|
+
</button>
|
|
155
|
+
)}
|
|
156
|
+
</template>
|
|
157
|
+
) : (
|
|
158
|
+
actionButton && (
|
|
159
|
+
<button class="au-alert__action-btn" type="button">
|
|
160
|
+
{actionButton.content}
|
|
161
|
+
</button>
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
closeButton && (
|
|
170
|
+
<button class="au-alert__close-btn" type="button" aria-label="Fechar">
|
|
171
|
+
<IconX rawColor={COLOR_NEUTRAL_70} />
|
|
172
|
+
</button>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<script>
|
|
178
|
+
import { elementController } from '@consumidor-positivo/aurora/astro/runtime'
|
|
179
|
+
|
|
180
|
+
const TICK_MS = 1000
|
|
181
|
+
|
|
182
|
+
elementController('au-alert', ({ root, on, emit }) => {
|
|
183
|
+
const template = root.querySelector<HTMLTemplateElement>(
|
|
184
|
+
':scope > .au-alert__content > [class^="au-alert__container--"] > template[data-au-alert-timer]',
|
|
185
|
+
)
|
|
186
|
+
const container = template?.parentElement ?? null
|
|
187
|
+
const countdown = Number(template?.dataset.countdown ?? 0)
|
|
188
|
+
// Detached while the countdown runs, like React, which does not render
|
|
189
|
+
// the button until it ends.
|
|
190
|
+
const actionButton =
|
|
191
|
+
(template?.content.firstElementChild as HTMLButtonElement | null) ?? null
|
|
192
|
+
const counter = root.querySelector<HTMLElement>(
|
|
193
|
+
':scope > .au-alert__content > span > .au-alert__timer > .au-alert__countdown',
|
|
194
|
+
)
|
|
195
|
+
const timerBox = counter?.parentElement ?? null
|
|
196
|
+
template?.remove()
|
|
197
|
+
|
|
198
|
+
let timeLeft = countdown
|
|
199
|
+
let timer: ReturnType<typeof setInterval> | undefined
|
|
200
|
+
|
|
201
|
+
function setFinished(isFinished: boolean) {
|
|
202
|
+
if (counter && timerBox) {
|
|
203
|
+
if (isFinished) counter.remove()
|
|
204
|
+
else if (!counter.isConnected) timerBox.appendChild(counter)
|
|
205
|
+
}
|
|
206
|
+
if (actionButton && container) {
|
|
207
|
+
if (!isFinished) actionButton.remove()
|
|
208
|
+
else if (!actionButton.isConnected) container.appendChild(actionButton)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function startCountdown() {
|
|
213
|
+
clearInterval(timer)
|
|
214
|
+
if (counter) counter.textContent = `${timeLeft}s`
|
|
215
|
+
// React never counts down from zero (or less): it stays on "0s" with
|
|
216
|
+
// no action button.
|
|
217
|
+
if (timeLeft <= 0) return
|
|
218
|
+
|
|
219
|
+
timer = setInterval(() => {
|
|
220
|
+
timeLeft -= 1
|
|
221
|
+
if (timeLeft > 0) {
|
|
222
|
+
if (counter) counter.textContent = `${timeLeft}s`
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
clearInterval(timer)
|
|
227
|
+
setFinished(true)
|
|
228
|
+
emit('au:countdownend')
|
|
229
|
+
}, TICK_MS)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
on('click', (event) => {
|
|
233
|
+
const target = event.target as Element | null
|
|
234
|
+
// A nested Alert in the default slot handles its own buttons.
|
|
235
|
+
if (target?.closest('.au-alert') !== root) return
|
|
236
|
+
|
|
237
|
+
if (target.closest('.au-alert__action-btn')) {
|
|
238
|
+
event.preventDefault()
|
|
239
|
+
if (template) {
|
|
240
|
+
timeLeft = countdown
|
|
241
|
+
setFinished(false)
|
|
242
|
+
startCountdown()
|
|
243
|
+
}
|
|
244
|
+
emit('au:alertaction')
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (target.closest('.au-alert__close-btn')) {
|
|
249
|
+
clearInterval(timer)
|
|
250
|
+
emit('au:alertclose')
|
|
251
|
+
root.remove()
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
if (template) startCountdown()
|
|
256
|
+
})
|
|
257
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import IconInfo from '../icons/default/IconInfo.astro'
|
|
4
|
+
import {
|
|
5
|
+
COLOR_BRAND_CYAN_50,
|
|
6
|
+
COLOR_ERROR_50,
|
|
7
|
+
COLOR_INFO_50,
|
|
8
|
+
COLOR_NEUTRAL_70,
|
|
9
|
+
COLOR_SUCCESS_50,
|
|
10
|
+
COLOR_WARNING_50,
|
|
11
|
+
} from '@consumidor-positivo/aurora/astro/tokens'
|
|
12
|
+
import '../../dist/components/BadgeInfo/styles.css'
|
|
13
|
+
|
|
14
|
+
// The same tokens React passes as `rawColor`.
|
|
15
|
+
const STATUS_COLOR = {
|
|
16
|
+
success: COLOR_SUCCESS_50,
|
|
17
|
+
error: COLOR_ERROR_50,
|
|
18
|
+
warning: COLOR_WARNING_50,
|
|
19
|
+
info: COLOR_INFO_50,
|
|
20
|
+
progress: COLOR_BRAND_CYAN_50,
|
|
21
|
+
neutral: COLOR_NEUTRAL_70,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The React `customIcon` prop is the `icon` slot here: when filled, it replaces
|
|
26
|
+
* the default `IconInfo`. React `children` is the default slot, rendered after
|
|
27
|
+
* the content. Every HTML attribute is forwarded to the root `div`.
|
|
28
|
+
*/
|
|
29
|
+
export type Props = Omit<HTMLAttributes<'div'>, 'color'> & {
|
|
30
|
+
status: 'info' | 'success' | 'error' | 'warning' | 'progress' | 'neutral'
|
|
31
|
+
color?: 'primary' | 'secondary'
|
|
32
|
+
text: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { status, color = 'primary', text, class: className, ...rest } = Astro.props
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<div
|
|
39
|
+
class:list={[
|
|
40
|
+
'au-badgeInfo',
|
|
41
|
+
`au-badgeInfo--${status}`,
|
|
42
|
+
`au-badgeInfo--color-${color}`,
|
|
43
|
+
className,
|
|
44
|
+
]}
|
|
45
|
+
{...rest}>
|
|
46
|
+
<div class="au-badgeInfo__content">
|
|
47
|
+
<div class="au-badgeInfo__content-icon" aria-hidden="true">
|
|
48
|
+
<slot name="icon">
|
|
49
|
+
<IconInfo rawColor={STATUS_COLOR[status]} />
|
|
50
|
+
</slot>
|
|
51
|
+
</div>
|
|
52
|
+
<p class="au-badgeInfo__content-support-text">{text}</p>
|
|
53
|
+
</div>
|
|
54
|
+
<slot />
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import IconAlertOctagon from '../icons/default/IconAlertOctagon.astro'
|
|
4
|
+
import IconAlertTriangle from '../icons/default/IconAlertTriangle.astro'
|
|
5
|
+
import IconCheck from '../icons/default/IconCheck.astro'
|
|
6
|
+
import IconInfo from '../icons/default/IconInfo.astro'
|
|
7
|
+
import IconMoreHorizontal from '../icons/default/IconMoreHorizontal.astro'
|
|
8
|
+
import {
|
|
9
|
+
COLOR_BRAND_CYAN_50,
|
|
10
|
+
COLOR_ERROR_50,
|
|
11
|
+
COLOR_INFO_50,
|
|
12
|
+
COLOR_NEUTRAL_60,
|
|
13
|
+
COLOR_SUCCESS_50,
|
|
14
|
+
COLOR_WARNING_50,
|
|
15
|
+
} from '@consumidor-positivo/aurora/astro/tokens'
|
|
16
|
+
import '../../dist/components/BadgeState/styles.css'
|
|
17
|
+
|
|
18
|
+
// The same tokens React passes as `rawColor`.
|
|
19
|
+
const STATUS_MAP = {
|
|
20
|
+
success: { label: 'Sucesso', icon: IconCheck, color: COLOR_SUCCESS_50 },
|
|
21
|
+
error: { label: 'Erro', icon: IconAlertOctagon, color: COLOR_ERROR_50 },
|
|
22
|
+
warning: { label: 'Atenção', icon: IconAlertTriangle, color: COLOR_WARNING_50 },
|
|
23
|
+
info: { label: 'Informação', icon: IconInfo, color: COLOR_INFO_50 },
|
|
24
|
+
support: { label: 'Mais informações', icon: IconMoreHorizontal, color: COLOR_BRAND_CYAN_50 },
|
|
25
|
+
neutral: { label: 'Neutro', icon: IconMoreHorizontal, color: COLOR_NEUTRAL_60 },
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Same props as React, minus `icon` (declared but never rendered there). React
|
|
30
|
+
* `children` is the default slot, rendered after the content. Every HTML
|
|
31
|
+
* attribute is forwarded to the root `div`.
|
|
32
|
+
*/
|
|
33
|
+
export type Props = HTMLAttributes<'div'> & {
|
|
34
|
+
status: 'info' | 'success' | 'error' | 'warning' | 'support' | 'neutral'
|
|
35
|
+
/** Round border, for a number badge. */
|
|
36
|
+
isNumberBadge?: boolean
|
|
37
|
+
variant?: 'regular' | 'strong'
|
|
38
|
+
text?: string
|
|
39
|
+
/** Renders only the status icon, labelled for assistive tech. */
|
|
40
|
+
iconOnly?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
status,
|
|
45
|
+
isNumberBadge = false,
|
|
46
|
+
variant = 'regular',
|
|
47
|
+
text,
|
|
48
|
+
iconOnly = false,
|
|
49
|
+
class: className,
|
|
50
|
+
...rest
|
|
51
|
+
} = Astro.props
|
|
52
|
+
|
|
53
|
+
const { label, icon: StatusIcon, color } = STATUS_MAP[status]
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
<div
|
|
57
|
+
class:list={[
|
|
58
|
+
'au-badgeState',
|
|
59
|
+
`au-badgeState--${status}`,
|
|
60
|
+
isNumberBadge && 'au-badgeState--border-circle',
|
|
61
|
+
iconOnly && 'au-badgeState--icon-only',
|
|
62
|
+
`au-badgeState--variant-${variant}`,
|
|
63
|
+
className,
|
|
64
|
+
]}
|
|
65
|
+
aria-label={iconOnly ? label : undefined}
|
|
66
|
+
{...rest}>
|
|
67
|
+
<div class="au-badgeState__content">
|
|
68
|
+
{
|
|
69
|
+
iconOnly ? (
|
|
70
|
+
<span aria-hidden="true">
|
|
71
|
+
<StatusIcon rawColor={color} />
|
|
72
|
+
</span>
|
|
73
|
+
) : (
|
|
74
|
+
<p class="au-badgeState__content-support-text">{text}</p>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
</div>
|
|
78
|
+
<slot />
|
|
79
|
+
</div>
|
package/astro/Button/index.astro
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import IconLoader from '../icons/default/IconLoader.astro'
|
|
3
4
|
import '../../dist/components/Button/styles.css'
|
|
4
5
|
|
|
5
6
|
type AuroraButtonProps = {
|
|
@@ -9,6 +10,12 @@ type AuroraButtonProps = {
|
|
|
9
10
|
/** Stretches the button to fill the container width. */
|
|
10
11
|
expand?: 'x'
|
|
11
12
|
htmlType?: 'button' | 'submit' | 'reset'
|
|
13
|
+
/**
|
|
14
|
+
* Shows the spinner in place of the content and disables the button.
|
|
15
|
+
* As in React, with `as="a"` it only adds the class: the link keeps its
|
|
16
|
+
* content and stays clickable.
|
|
17
|
+
*/
|
|
18
|
+
loading?: boolean
|
|
12
19
|
size?: 'large' | 'medium'
|
|
13
20
|
/**
|
|
14
21
|
* Visual type. Avoid `'link'`: use the `LinkButton` component instead
|
|
@@ -42,6 +49,7 @@ const {
|
|
|
42
49
|
disabled = false,
|
|
43
50
|
expand,
|
|
44
51
|
htmlType = 'button',
|
|
52
|
+
loading = false,
|
|
45
53
|
size = 'medium',
|
|
46
54
|
type = 'primary',
|
|
47
55
|
negative = false,
|
|
@@ -70,6 +78,7 @@ const Tag = isAnchor ? 'a' : 'button'
|
|
|
70
78
|
expand && `au-btn--expand-${expand}`,
|
|
71
79
|
negative && 'au-btn--negative',
|
|
72
80
|
disabled && 'au-btn--disabled',
|
|
81
|
+
loading && 'au-btn--loading',
|
|
73
82
|
round && `au-btn--round-${size}`,
|
|
74
83
|
btnText && 'btn-text',
|
|
75
84
|
className,
|
|
@@ -78,8 +87,7 @@ const Tag = isAnchor ? 'a' : 'button'
|
|
|
78
87
|
href={isAnchor ? href : undefined}
|
|
79
88
|
target={isAnchor ? target : undefined}
|
|
80
89
|
type={isAnchor ? undefined : htmlType}
|
|
81
|
-
disabled={!isAnchor && disabled ? true : undefined}
|
|
82
|
-
aria-disabled={isAnchor && disabled ? 'true' : undefined}
|
|
90
|
+
disabled={!isAnchor && (disabled || loading) ? true : undefined}
|
|
83
91
|
{...rest}>
|
|
84
|
-
<slot
|
|
92
|
+
{!isAnchor && loading ? <IconLoader /> : <slot>Button component</slot>}
|
|
85
93
|
</Tag>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import '../../../dist/components/Card/styles.css'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Same props as React. `gap` and `width` are inline `style`, written like
|
|
7
|
+
* React writes them (an unset one comes out as `undefinedpx`, which the
|
|
8
|
+
* browser drops), so a `style` passed here is ignored. Every HTML attribute is forwarded.
|
|
9
|
+
*/
|
|
10
|
+
export type Props = Omit<HTMLAttributes<'div'>, 'style'> & {
|
|
11
|
+
direction?: 'column' | 'row'
|
|
12
|
+
alignItems?: 'center' | 'start'
|
|
13
|
+
justifyContent?: 'center' | 'space-between'
|
|
14
|
+
/** Gap in px. */
|
|
15
|
+
gap?: number
|
|
16
|
+
/** Width in px. */
|
|
17
|
+
width?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
direction,
|
|
22
|
+
alignItems,
|
|
23
|
+
justifyContent,
|
|
24
|
+
gap,
|
|
25
|
+
width,
|
|
26
|
+
class: className,
|
|
27
|
+
...rest
|
|
28
|
+
} = Astro.props
|
|
29
|
+
|
|
30
|
+
const style = `gap:${gap}px;width:${width}px`
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<div
|
|
34
|
+
class:list={[
|
|
35
|
+
'au-card__container',
|
|
36
|
+
direction && `au-card__container--direction-${direction}`,
|
|
37
|
+
alignItems && `au-card__container--align-items-${alignItems}`,
|
|
38
|
+
justifyContent && `au-card__container--justify-content-${justifyContent}`,
|
|
39
|
+
className,
|
|
40
|
+
]}
|
|
41
|
+
{...rest}
|
|
42
|
+
style={style}>
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import Text from '../../Text/index.astro'
|
|
4
|
+
import '../../../dist/components/Card/styles.css'
|
|
5
|
+
|
|
6
|
+
export type CardEmphasisItem = { title: string; description?: string }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Same props as React. `height` / `maxHeight` are inline `style`, written
|
|
10
|
+
* like React writes them (an unset one comes out as `undefinedpx`, which the
|
|
11
|
+
* browser drops), so a `style` passed here is ignored. Every HTML attribute is forwarded.
|
|
12
|
+
*/
|
|
13
|
+
export type Props = Omit<HTMLAttributes<'div'>, 'style'> & {
|
|
14
|
+
content: CardEmphasisItem[]
|
|
15
|
+
/** Height in px. */
|
|
16
|
+
height?: number
|
|
17
|
+
/** Max height in px. */
|
|
18
|
+
maxHeight?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { content, height, maxHeight, class: className, ...rest } = Astro.props
|
|
22
|
+
|
|
23
|
+
const style = `height:${height}px;max-height:${maxHeight}px`
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<div class:list={['au-card__emphasis', className]} {...rest} style={style}>
|
|
27
|
+
{
|
|
28
|
+
content?.map((item) => (
|
|
29
|
+
<div class="au-card__emphasis-container">
|
|
30
|
+
<Text variant="body-small" color="secondary">
|
|
31
|
+
{item.title}
|
|
32
|
+
</Text>
|
|
33
|
+
<Text variant="body-small" weight="semibold">
|
|
34
|
+
{item.description}
|
|
35
|
+
</Text>
|
|
36
|
+
</div>
|
|
37
|
+
))
|
|
38
|
+
}
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
import '../../../dist/components/Card/styles.css'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Same props as React. The attributes go to the wrapper `div` (where React
|
|
7
|
+
* puts `data-testid`); `src`, `alt` and the size go to the `<img>`, written
|
|
8
|
+
* like React writes it (an unset size comes out as `undefinedpx`).
|
|
9
|
+
*/
|
|
10
|
+
export type Props = HTMLAttributes<'div'> & {
|
|
11
|
+
src: string
|
|
12
|
+
alt?: string
|
|
13
|
+
/** Width in px. */
|
|
14
|
+
width?: number
|
|
15
|
+
/** Height in px. */
|
|
16
|
+
height?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { src, alt, width, height, ...rest } = Astro.props
|
|
20
|
+
|
|
21
|
+
const style = `width:${width}px;height:${height}px`
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<div {...rest}>
|
|
25
|
+
<img style={style} src={src} alt={alt} />
|
|
26
|
+
</div>
|