@seniorsistemas/angular-components 19.3.3 → 19.3.4
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/accordion/README.md +166 -0
- package/alert/README.md +92 -0
- package/autocomplete/README.md +162 -0
- package/badge/README.md +126 -0
- package/bignumber-input/README.md +122 -0
- package/breadcrumb/README.md +144 -0
- package/button/README.md +159 -0
- package/calendar-mask/README.md +89 -0
- package/card/README.md +133 -0
- package/chat/README.md +130 -0
- package/checkbox/README.md +108 -0
- package/checkbox-list/README.md +149 -0
- package/chips/README.md +152 -0
- package/code-editor/README.md +149 -0
- package/collapse-link/README.md +128 -0
- package/confirm-dialog/README.md +105 -0
- package/content-generator/README.md +111 -0
- package/control-errors/README.md +92 -0
- package/country-phone-picker/README.md +121 -0
- package/currency/README.md +90 -0
- package/custom-fields/README.md +142 -0
- package/dialog/README.md +152 -0
- package/dynamic-form/README.md +176 -0
- package/editable-overlay/README.md +98 -0
- package/empty-state/README.md +134 -0
- package/esm2022/loading-state/lib/loading-state/loading-state.component.mjs +4 -4
- package/fesm2022/seniorsistemas-angular-components-loading-state.mjs +3 -3
- package/fesm2022/seniorsistemas-angular-components-loading-state.mjs.map +1 -1
- package/fieldset/README.md +135 -0
- package/file-picker/README.md +162 -0
- package/file-upload/README.md +23 -7
- package/gantt/README.md +173 -0
- package/global-search/README.md +151 -0
- package/grid-menu/README.md +123 -0
- package/help-popover/README.md +134 -0
- package/ia-insight/README.md +24 -6
- package/image-cropper/README.md +140 -0
- package/infinite-scroll/README.md +130 -0
- package/info-sign/README.md +111 -0
- package/inline-edit/README.md +139 -0
- package/insights/README.md +159 -0
- package/interactive-content/README.md +120 -0
- package/kanban/README.md +184 -0
- package/label-value/README.md +154 -0
- package/loading-state/README.md +141 -0
- package/localized-number-input/README.md +128 -0
- package/mouse-events/README.md +157 -0
- package/navigation-button/README.md +160 -0
- package/numeric/README.md +147 -0
- package/numeric-mask/README.md +170 -0
- package/object-card/README.md +158 -0
- package/package.json +1 -1
- package/paginator/README.md +121 -0
- package/panel/README.md +147 -0
- package/password-strength/README.md +144 -0
- package/picklist/README.md +170 -0
- package/pin-code-field/README.md +137 -0
- package/product-header/README.md +33 -6
- package/profile-picture-picker/README.md +159 -0
- package/progressbar/README.md +136 -0
- package/radio-button/README.md +117 -0
- package/rating-scale/README.md +154 -0
- package/select/README.md +147 -0
- package/select-button/README.md +137 -0
- package/sidebar/README.md +117 -0
- package/slide-in-bar/README.md +122 -0
- package/slider/README.md +127 -0
- package/speech-recognition/README.md +104 -0
- package/split-button/README.md +126 -0
- package/spotlight/README.md +200 -0
- package/star-rating/README.md +127 -0
- package/stats-card/README.md +135 -0
- package/stepper/README.md +164 -0
- package/switch/README.md +125 -0
- package/table/README.md +185 -0
- package/text-area-ia/README.md +17 -6
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# InfiniteScroll
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Diretiva de scroll infinito que detecta quando o usuário chega ao final de um elemento com scroll e emite o evento `scrolled`. Deve ser aplicada a um elemento com `overflow` e scroll habilitado.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Listas longas carregadas de API com paginação progressiva
|
|
10
|
+
- Feeds de conteúdo (notícias, posts, cards) com carregamento sob demanda
|
|
11
|
+
- Tabelas ou listas com muitos registros onde a paginação clássica é indesejada
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Listas curtas onde todos os itens podem ser carregados de uma vez — carregue tudo e exiba diretamente
|
|
16
|
+
- Quando o usuário precisa navegar para uma página específica — prefira paginação com controles numéricos
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { InfiniteScrollModule } from '@seniorsistemas/angular-components/infinite-scroll';
|
|
22
|
+
|
|
23
|
+
@NgModule({
|
|
24
|
+
imports: [InfiniteScrollModule],
|
|
25
|
+
})
|
|
26
|
+
export class MeuModule {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Uso básico
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<div
|
|
33
|
+
sInfiniteScroll
|
|
34
|
+
(scrolled)="carregarMais()"
|
|
35
|
+
style="overflow-y: auto; height: 400px;"
|
|
36
|
+
>
|
|
37
|
+
<!-- itens da lista -->
|
|
38
|
+
</div>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### Inputs
|
|
44
|
+
|
|
45
|
+
Esta diretiva não possui inputs configuráveis.
|
|
46
|
+
|
|
47
|
+
### Outputs
|
|
48
|
+
|
|
49
|
+
| Evento | Tipo | Descrição |
|
|
50
|
+
|--------|------|-----------|
|
|
51
|
+
| `scrolled` | `EventEmitter<number>` | Emitido quando o usuário atinge o final do elemento com scroll. |
|
|
52
|
+
|
|
53
|
+
## Exemplos
|
|
54
|
+
|
|
55
|
+
### Lista com carregamento progressivo
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<div
|
|
59
|
+
sInfiniteScroll
|
|
60
|
+
(scrolled)="carregarMais()"
|
|
61
|
+
style="height: 300px; overflow-y: auto;"
|
|
62
|
+
>
|
|
63
|
+
@for (item of itens; track item.id) {
|
|
64
|
+
<div>{{ item.nome }}</div>
|
|
65
|
+
}
|
|
66
|
+
@if (carregando) {
|
|
67
|
+
<div>Carregando...</div>
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
carregarMais() {
|
|
74
|
+
if (this.carregando) return;
|
|
75
|
+
this.carregando = true;
|
|
76
|
+
this.api.buscarItens(this.pagina++).subscribe(novosItens => {
|
|
77
|
+
this.itens = [...this.itens, ...novosItens];
|
|
78
|
+
this.carregando = false;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Com indicador de fim de lista
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<div sInfiniteScroll (scrolled)="carregarMais()" style="height: 300px; overflow-y: auto;">
|
|
87
|
+
@for (item of itens; track item) {
|
|
88
|
+
<div>{{ item }}</div>
|
|
89
|
+
}
|
|
90
|
+
@if (carregando) {
|
|
91
|
+
<div>Carregando...</div>
|
|
92
|
+
}
|
|
93
|
+
@if (!temMais && !carregando) {
|
|
94
|
+
<div>Todos os registros foram carregados.</div>
|
|
95
|
+
}
|
|
96
|
+
</div>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Com debounce no callback (recomendado para APIs)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { Subject } from 'rxjs';
|
|
103
|
+
import { debounceTime } from 'rxjs/operators';
|
|
104
|
+
|
|
105
|
+
private scroll$ = new Subject<void>();
|
|
106
|
+
|
|
107
|
+
ngOnInit() {
|
|
108
|
+
this.scroll$.pipe(debounceTime(300)).subscribe(() => this.buscarProximaPagina());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onScrolled() {
|
|
112
|
+
this.scroll$.next();
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<div sInfiniteScroll (scrolled)="onScrolled()" style="height: 400px; overflow-y: auto;">
|
|
118
|
+
<!-- itens -->
|
|
119
|
+
</div>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Acessibilidade
|
|
123
|
+
|
|
124
|
+
- O elemento hospedeiro deve ter altura definida e `overflow-y: auto` ou `overflow-y: scroll`
|
|
125
|
+
- A diretiva não possui debounce interno — implemente no callback quando necessário para evitar múltiplas requisições simultâneas
|
|
126
|
+
- O evento `scrolled` é disparado quando `scrollTop > scrollHeight - offsetHeight - 2`
|
|
127
|
+
|
|
128
|
+
## Componentes relacionados
|
|
129
|
+
|
|
130
|
+
- [`LoadingState`](../loading-state/README.md) — indicador de carregamento a ser exibido durante o scroll
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# InfoSign
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Diretiva estrutural que exibe um ícone de informação (ⓘ) ao lado do elemento hospedeiro. Ao interagir com o ícone, um tooltip com a mensagem configurada é exibido temporariamente.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Labels de campos de formulário com regras de preenchimento específicas (CPF, CNPJ, CEP)
|
|
10
|
+
- Títulos de seções que necessitam de explicação adicional
|
|
11
|
+
- Qualquer elemento de texto que precise de contexto sem sobrecarregar o layout
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Quando a informação é crítica e deve estar sempre visível — use texto de ajuda abaixo do campo
|
|
16
|
+
- Para ações com conteúdo rico (listas, links, templates) — prefira [`HelpPopover`](../help-popover/README.md)
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { InfoSignModule } from '@seniorsistemas/angular-components/info-sign';
|
|
22
|
+
|
|
23
|
+
@NgModule({
|
|
24
|
+
imports: [InfoSignModule],
|
|
25
|
+
})
|
|
26
|
+
export class MeuModule {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Uso básico
|
|
30
|
+
|
|
31
|
+
Como diretiva estrutural, substitui o elemento hospedeiro por um wrapper com o ícone ⓘ ao lado:
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<label *sInfoSign="'Informe o CPF sem pontos e traços'">CPF</label>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
### Inputs
|
|
40
|
+
|
|
41
|
+
| Propriedade | Sintaxe no template | Tipo | Padrão | Obrigatório | Descrição |
|
|
42
|
+
|-------------|---------------------|------|--------|:-----------:|-----------|
|
|
43
|
+
| `sInfoSign` | `*sInfoSign="'mensagem'"` | `string` | — | Sim | Mensagem exibida no tooltip. Aceita HTML. |
|
|
44
|
+
| `sInfoSignDisplayTime` | `; displayTime: 5000` | `number` | `5000` | Não | Tempo em ms que o tooltip permanece visível após ser exibido. |
|
|
45
|
+
| `sInfoSignFocusedInputRef` | `; focusedInputRef: ref` | `HTMLInputElement \| HTMLElement \| null` | `null` | Não | Referência a um input. O tooltip aparece ao focar o input quando fornecido. |
|
|
46
|
+
| `sInfoSignUseFocusedInputRef` | `; useFocusedInputRef: true` | `boolean` | `true` | Não | Quando `true`, usa `focusedInputRef` para calcular o posicionamento do ícone. |
|
|
47
|
+
|
|
48
|
+
## Exemplos
|
|
49
|
+
|
|
50
|
+
### Em um label de formulário
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
54
|
+
<label *sInfoSign="'Informe o CPF sem pontos e traços. Apenas números.'">
|
|
55
|
+
CPF
|
|
56
|
+
</label>
|
|
57
|
+
<input type="text" placeholder="000.000.000-00" />
|
|
58
|
+
</div>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Com tempo de exibição customizado
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<span *sInfoSign="'Esta mensagem some em 2 segundos.'; displayTime: 2000">
|
|
65
|
+
Passe o mouse no ícone
|
|
66
|
+
</span>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Com conteúdo HTML no tooltip
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<label *sInfoSign="'<strong>Requisitos:</strong><br>• Mínimo 8 caracteres<br>• Letras e números'; displayTime: 8000">
|
|
73
|
+
Senha
|
|
74
|
+
</label>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Com referência a um input (tooltip ao focar)
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<label *sInfoSign="'Dica do campo'; focusedInputRef: meuInput">
|
|
81
|
+
Campo com foco
|
|
82
|
+
</label>
|
|
83
|
+
<input #meuInput type="text" />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Em múltiplos campos de formulário
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
90
|
+
<div>
|
|
91
|
+
<label *sInfoSign="'Informe o CPF sem pontos e traços.'">CPF</label>
|
|
92
|
+
<input type="text" placeholder="000.000.000-00" />
|
|
93
|
+
</div>
|
|
94
|
+
<div>
|
|
95
|
+
<label *sInfoSign="'Data no formato DD/MM/AAAA.'">Data de nascimento</label>
|
|
96
|
+
<input type="text" placeholder="DD/MM/AAAA" />
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Acessibilidade
|
|
102
|
+
|
|
103
|
+
- É uma diretiva estrutural — o elemento hospedeiro é envolvido por um wrapper com o ícone ao lado
|
|
104
|
+
- O tooltip aparece ao passar o mouse sobre o ícone (padrão) ou ao focar o input referenciado
|
|
105
|
+
- O tooltip some automaticamente após `displayTime` ms (padrão: 5000ms)
|
|
106
|
+
- Aceita HTML no conteúdo do tooltip para mensagens formatadas
|
|
107
|
+
|
|
108
|
+
## Componentes relacionados
|
|
109
|
+
|
|
110
|
+
- [`HelpPopover`](../help-popover/README.md) — popover de ajuda com mais opções de conteúdo (header, footer, template)
|
|
111
|
+
- [`Tooltip`](../tooltip/README.md) — tooltip standalone aplicado diretamente em qualquer elemento
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# InlineEdit
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Componente de edição inline que renderiza dinamicamente campos editáveis a partir de uma lista de configurações. Cada campo exibe o valor atual e permite edição ao clicar, integrando-se a um `FormGroup` Angular.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Formulários de detalhes onde o usuário edita campos individualmente sem abrir modal
|
|
10
|
+
- Telas de visualização com edição rápida diretamente no contexto
|
|
11
|
+
- Painéis de configuração onde poucos campos precisam ser editados por vez
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Formulários completos com muitos campos obrigatórios simultâneos — use um formulário convencional
|
|
16
|
+
- Quando todos os campos são editados ao mesmo tempo — prefira Dynamic Form
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { InlineEditModule } from '@seniorsistemas/angular-components/inline-edit';
|
|
22
|
+
|
|
23
|
+
@NgModule({
|
|
24
|
+
imports: [InlineEditModule],
|
|
25
|
+
})
|
|
26
|
+
export class MeuModule {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Uso básico
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<s-inline-edit [formGroup]="form" [fields]="campos" />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### Inputs
|
|
38
|
+
|
|
39
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
40
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
41
|
+
| `formGroup` | `FormGroup` | `new FormGroup({})` | Não | `FormGroup` Angular ao qual os campos inline serão vinculados. |
|
|
42
|
+
| `fields` | `InlineEditField[]` | `[]` | Não | Lista de configurações dos campos inline a serem renderizados. |
|
|
43
|
+
| `errorMessages` | `any` | `undefined` | Não | Mapa de mensagens de erro customizadas por nome de validador. |
|
|
44
|
+
|
|
45
|
+
### Tipos
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Classe base — todos os tipos de campo estendem InlineEditField
|
|
49
|
+
abstract class InlineEditField {
|
|
50
|
+
label: string; // Rótulo exibido ao lado do campo
|
|
51
|
+
name: string; // Nome do FormControl correspondente no FormGroup
|
|
52
|
+
tooltip?: string; // Tooltip exibido ao passar o mouse sobre o rótulo
|
|
53
|
+
errorMessages?: {} | (() => {}); // Mensagens de erro customizadas
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Tipos de campo disponíveis:
|
|
57
|
+
class InlineEditTextField extends InlineEditField {} // Campo de texto simples
|
|
58
|
+
class InlineEditNumberField extends InlineEditField {} // Campo numérico
|
|
59
|
+
class InlineEditTextAreaField extends InlineEditField {} // Área de texto multilinha
|
|
60
|
+
class InlineEditCalendarField extends InlineEditField {} // Seletor de data
|
|
61
|
+
class InlineEditLookupField extends InlineEditField {} // Campo de busca avançada
|
|
62
|
+
class InlineEditContentGeneratorField extends InlineEditField {} // Gerador de conteúdo com IA
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Exemplos
|
|
66
|
+
|
|
67
|
+
### Campos de texto, número e área de texto
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import {
|
|
71
|
+
InlineEditTextField,
|
|
72
|
+
InlineEditNumberField,
|
|
73
|
+
InlineEditTextAreaField,
|
|
74
|
+
} from '@seniorsistemas/angular-components/inline-edit';
|
|
75
|
+
|
|
76
|
+
@Component({ ... })
|
|
77
|
+
export class MeuComponent implements OnInit {
|
|
78
|
+
form!: FormGroup;
|
|
79
|
+
campos: InlineEditField[] = [];
|
|
80
|
+
|
|
81
|
+
ngOnInit() {
|
|
82
|
+
this.form = new FormGroup({
|
|
83
|
+
nome: new FormControl('João Silva'),
|
|
84
|
+
valor: new FormControl(1500),
|
|
85
|
+
observacao: new FormControl('Observação inicial'),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.campos = [
|
|
89
|
+
new InlineEditTextField({ name: 'nome', label: 'Nome', tooltip: 'Nome completo' }),
|
|
90
|
+
new InlineEditNumberField({ name: 'valor', label: 'Valor', tooltip: 'Valor numérico' }),
|
|
91
|
+
new InlineEditTextAreaField({ name: 'observacao', label: 'Observação' }),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<s-inline-edit [formGroup]="form" [fields]="campos"></s-inline-edit>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Com campo de data
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { InlineEditCalendarField } from '@seniorsistemas/angular-components/inline-edit';
|
|
105
|
+
|
|
106
|
+
this.form = new FormGroup({
|
|
107
|
+
dataAdmissao: new FormControl(new Date()),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.campos = [
|
|
111
|
+
new InlineEditCalendarField({ name: 'dataAdmissao', label: 'Data de admissão' }),
|
|
112
|
+
];
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Com mensagens de erro customizadas
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
this.campos = [
|
|
119
|
+
new InlineEditTextField({
|
|
120
|
+
name: 'email',
|
|
121
|
+
label: 'E-mail',
|
|
122
|
+
errorMessages: {
|
|
123
|
+
required: 'O e-mail é obrigatório.',
|
|
124
|
+
email: 'Informe um e-mail válido.',
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
];
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Acessibilidade
|
|
131
|
+
|
|
132
|
+
- Cada campo exibe seu rótulo (`label`) visualmente associado ao controle
|
|
133
|
+
- O `tooltip` opcional fornece contexto adicional ao passar o mouse
|
|
134
|
+
- A edição é ativada por clique e confirmada com Enter ou clique fora do campo
|
|
135
|
+
- Mensagens de validação são exibidas abaixo do campo em caso de erro
|
|
136
|
+
|
|
137
|
+
## Componentes relacionados
|
|
138
|
+
|
|
139
|
+
- [`DynamicForm`](../dynamic-form/README.md) — formulário dinâmico completo com todos os campos habilitados simultaneamente
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Insights
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Componente de painel lateral de insights com IA. Exibe uma lista de itens de insights em um sidebar, com carregamento assíncrono via `Observable`, feedback de like/dislike, cópia e recarregamento. Suporta templates customizáveis para introdução, estado vazio e sem permissão.
|
|
6
|
+
|
|
7
|
+
> **Nota:** O seletor `s-ia-insight` e o pacote `ia-insight` estão descontinuados. Use `s-insights` e o pacote `insights` em novos projetos. O seletor antigo será removido na versão 20.0.0.
|
|
8
|
+
|
|
9
|
+
## Quando usar
|
|
10
|
+
|
|
11
|
+
- Dashboards que precisam exibir análises geradas por IA em um painel lateral
|
|
12
|
+
- Telas de gestão de RH, financeiro ou operacional com insights contextuais
|
|
13
|
+
- Qualquer interface que precise de análises assíncronas com feedback do usuário
|
|
14
|
+
|
|
15
|
+
## Quando não usar
|
|
16
|
+
|
|
17
|
+
- Quando os insights são estáticos e não precisam de carregamento assíncrono — use cards simples
|
|
18
|
+
- Quando não há controle de permissão ou o conteúdo é sempre acessível — simplifique o componente
|
|
19
|
+
|
|
20
|
+
## Instalação
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { InsightsModule } from '@seniorsistemas/angular-components/insights';
|
|
24
|
+
|
|
25
|
+
@NgModule({
|
|
26
|
+
imports: [InsightsModule],
|
|
27
|
+
})
|
|
28
|
+
export class MeuModule {}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Uso básico
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<s-insights
|
|
35
|
+
#insightsRef
|
|
36
|
+
[insights]="listaInsights"
|
|
37
|
+
(openInsights)="onAbrir()"
|
|
38
|
+
(closedInsights)="onFechar()"
|
|
39
|
+
></s-insights>
|
|
40
|
+
|
|
41
|
+
<s-button label="Abrir Insights" (clicked)="insightsRef.open()"></s-button>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### Inputs
|
|
47
|
+
|
|
48
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
49
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
50
|
+
| `insights` | `InsightsItem[]` | `[]` | Não | Lista de itens de insight a serem exibidos no painel. |
|
|
51
|
+
| `hasPermission` | `boolean` | `true` | Não | Indica se o usuário tem permissão para ver os insights. Quando `false`, exibe o template de sem permissão. |
|
|
52
|
+
| `introText` | `string` | `undefined` | Não | Texto de introdução exibido no topo do painel (quando sem template customizado). |
|
|
53
|
+
| `emptyText` | `string` | `undefined` | Não | Texto exibido quando não há insights disponíveis. |
|
|
54
|
+
| `noPermissionText` | `string` | `undefined` | Não | Texto exibido quando o usuário não tem permissão. |
|
|
55
|
+
|
|
56
|
+
### Outputs
|
|
57
|
+
|
|
58
|
+
| Evento | Tipo | Descrição |
|
|
59
|
+
|--------|------|-----------|
|
|
60
|
+
| `openInsights` | `OutputEmitterRef<void>` | Emitido quando o painel de insights é aberto. |
|
|
61
|
+
| `closedInsights` | `OutputEmitterRef<void>` | Emitido quando o painel de insights é fechado. |
|
|
62
|
+
|
|
63
|
+
### Tipos
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
interface InsightsItem {
|
|
67
|
+
title: string; // Título do insight (obrigatório)
|
|
68
|
+
request$: Observable<string>; // Observable que retorna o texto do insight (obrigatório)
|
|
69
|
+
onLikeSelected?: VoidFunction; // Callback ao clicar em "gostei"
|
|
70
|
+
onDislikeSelected?: VoidFunction; // Callback ao clicar em "não gostei"
|
|
71
|
+
onCopy?: (content: string) => void; // Callback ao copiar o conteúdo
|
|
72
|
+
onReload?: VoidFunction; // Callback ao recarregar o insight
|
|
73
|
+
onOpenSidebar?: VoidFunction; // Callback ao abrir o sidebar
|
|
74
|
+
onCloseSideBar?: VoidFunction; // Callback ao fechar o sidebar
|
|
75
|
+
onContentLoaded?: VoidFunction; // Callback quando o conteúdo é carregado
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Exemplos
|
|
80
|
+
|
|
81
|
+
### Insights com múltiplos itens
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { timer } from 'rxjs';
|
|
85
|
+
import { first, switchMap } from 'rxjs/operators';
|
|
86
|
+
import { of } from 'rxjs';
|
|
87
|
+
import { InsightsItem } from '@seniorsistemas/angular-components/insights';
|
|
88
|
+
|
|
89
|
+
insights: InsightsItem[] = [
|
|
90
|
+
{
|
|
91
|
+
title: 'Turnover mensal',
|
|
92
|
+
request$: timer(1000).pipe(first(), switchMap(() =>
|
|
93
|
+
of('O índice de turnover foi de 4,2%, acima da média do setor (2,8%).')
|
|
94
|
+
)),
|
|
95
|
+
onLikeSelected: () => console.log('Gostei'),
|
|
96
|
+
onCopy: (content) => navigator.clipboard.writeText(content),
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<s-insights
|
|
103
|
+
#insightsRef
|
|
104
|
+
[insights]="insights"
|
|
105
|
+
[hasPermission]="true"
|
|
106
|
+
introText="Análises geradas por IA com base nos dados do sistema."
|
|
107
|
+
(openInsights)="onAbrir()"
|
|
108
|
+
(closedInsights)="onFechar()"
|
|
109
|
+
></s-insights>
|
|
110
|
+
|
|
111
|
+
<s-button label="Abrir Insights" priority="primary" (clicked)="insightsRef.open()"></s-button>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Sem permissão
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<s-insights
|
|
118
|
+
#insightsRef
|
|
119
|
+
[insights]="insights"
|
|
120
|
+
[hasPermission]="false"
|
|
121
|
+
noPermissionText="Você não tem permissão para visualizar os insights."
|
|
122
|
+
></s-insights>
|
|
123
|
+
<s-button label="Abrir" (clicked)="insightsRef.open()"></s-button>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Com template customizado de introdução
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<s-insights #insightsRef [insights]="insights" [hasPermission]="true">
|
|
130
|
+
<ng-template sTemplate="intro">
|
|
131
|
+
<div style="padding: 12px; background: #eff6ff; border-radius: 6px;">
|
|
132
|
+
<p style="font-weight: 600; color: #1d4ed8;">Análises com IA</p>
|
|
133
|
+
<p>Insights gerados automaticamente com base nos dados do último trimestre.</p>
|
|
134
|
+
</div>
|
|
135
|
+
</ng-template>
|
|
136
|
+
</s-insights>
|
|
137
|
+
<s-button label="Abrir" (clicked)="insightsRef.open()"></s-button>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Templates customizáveis
|
|
141
|
+
|
|
142
|
+
Use `sTemplate` para customizar seções do painel:
|
|
143
|
+
|
|
144
|
+
| Valor | Descrição |
|
|
145
|
+
|-------|-----------|
|
|
146
|
+
| `sTemplate="intro"` | Conteúdo de introdução no topo do painel |
|
|
147
|
+
| `sTemplate="empty"` | Exibido quando não há insights disponíveis |
|
|
148
|
+
| `sTemplate="no-permission"` | Exibido quando `hasPermission` é `false` |
|
|
149
|
+
|
|
150
|
+
## Acessibilidade
|
|
151
|
+
|
|
152
|
+
- O painel é aberto/fechado via métodos `open()` e `close()` na referência do componente (`#ref`)
|
|
153
|
+
- Enquanto o `Observable` não emite, exibe um estado de carregamento animado
|
|
154
|
+
- As ações de like, dislike, cópia e recarregamento estão acessíveis por clique
|
|
155
|
+
|
|
156
|
+
## Componentes relacionados
|
|
157
|
+
|
|
158
|
+
- [`LoadingState`](../loading-state/README.md) — indicador de carregamento utilizado internamente
|
|
159
|
+
- [`SlideInBar`](../slide-in-bar/README.md) — painel lateral genérico para outros casos de uso
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# InteractiveContent
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Diretiva de acessibilidade que torna qualquer elemento HTML interativo, adicionando suporte a eventos de foco e navegação por teclado (Enter e Espaço) além do clique padrão. Gerencia automaticamente o atributo `tabindex`.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Tornar elementos não-interativos (`div`, `span`, imagens) acessíveis via teclado
|
|
10
|
+
- Implementar cards clicáveis, itens de lista selecionáveis ou grids interativos
|
|
11
|
+
- Garantir conformidade com WCAG para elementos customizados que simulam botões ou links
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Elementos que já são naturalmente interativos (`button`, `a`, `input`) — use os elementos nativos
|
|
16
|
+
- Quando apenas o clique de mouse é necessário e a acessibilidade via teclado não é requisito
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';
|
|
22
|
+
|
|
23
|
+
@Component({
|
|
24
|
+
standalone: true,
|
|
25
|
+
imports: [InteractiveContentDirective],
|
|
26
|
+
})
|
|
27
|
+
export class MeuComponent {}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Uso básico
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<div (sInteractiveContent)="onAtivar($event)" [focusable]="true">
|
|
34
|
+
Conteúdo clicável e acessível via teclado
|
|
35
|
+
</div>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
### Inputs
|
|
41
|
+
|
|
42
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
43
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
44
|
+
| `focusable` | `boolean` | `true` | Não | Habilita o foco via teclado adicionando `tabindex="0"` ao elemento. |
|
|
45
|
+
| `disabled` | `boolean` | `false` | Não | Desabilita todos os eventos de interação do elemento. |
|
|
46
|
+
| `stopPropagation` | `boolean` | `false` | Não | Quando `true`, chama `event.stopPropagation()` ao ativar o elemento. |
|
|
47
|
+
|
|
48
|
+
### Outputs
|
|
49
|
+
|
|
50
|
+
| Evento | Tipo | Descrição |
|
|
51
|
+
|--------|------|-----------|
|
|
52
|
+
| `sInteractiveContent` | `OutputEmitterRef<PointerEvent \| KeyboardEvent>` | Emitido quando o usuário clica ou pressiona Enter/Espaço no elemento. |
|
|
53
|
+
|
|
54
|
+
## Exemplos
|
|
55
|
+
|
|
56
|
+
### Card clicável e acessível
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<div
|
|
60
|
+
(sInteractiveContent)="onAtivar($event)"
|
|
61
|
+
[focusable]="true"
|
|
62
|
+
style="padding: 16px; border: 2px solid #e5e7eb; border-radius: 8px; cursor: pointer;"
|
|
63
|
+
>
|
|
64
|
+
<p>Elemento interativo</p>
|
|
65
|
+
<p>Clique ou pressione Enter/Espaço</p>
|
|
66
|
+
</div>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Cards selecionáveis com destaque visual
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
@for (card of cards; track card.id) {
|
|
73
|
+
<div
|
|
74
|
+
(sInteractiveContent)="cardSelecionado = card"
|
|
75
|
+
[focusable]="true"
|
|
76
|
+
[style.border-color]="cardSelecionado === card ? '#3b82f6' : '#e5e7eb'"
|
|
77
|
+
style="padding: 16px; border: 2px solid; border-radius: 8px; cursor: pointer;"
|
|
78
|
+
>
|
|
79
|
+
{{ card.titulo }}
|
|
80
|
+
</div>
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Elemento desabilitado
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<div
|
|
88
|
+
(sInteractiveContent)="onAtivar($event)"
|
|
89
|
+
[disabled]="!temPermissao"
|
|
90
|
+
style="padding: 16px; cursor: not-allowed; opacity: 0.6;"
|
|
91
|
+
>
|
|
92
|
+
Elemento desabilitado
|
|
93
|
+
</div>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Impedindo propagação de eventos
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<div (click)="onClickExterno()">
|
|
100
|
+
<div
|
|
101
|
+
(sInteractiveContent)="onAtivarInterno($event)"
|
|
102
|
+
[stopPropagation]="true"
|
|
103
|
+
style="padding: 8px; border: 1px solid #ccc;"
|
|
104
|
+
>
|
|
105
|
+
Clique interno (não propaga)
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Acessibilidade
|
|
111
|
+
|
|
112
|
+
- Adiciona `tabindex="0"` automaticamente ao elemento quando `focusable` é `true`
|
|
113
|
+
- Emite o evento `sInteractiveContent` ao clicar ou pressionar Enter/Espaço
|
|
114
|
+
- Com `disabled: true`, o `tabindex` é definido como `-1` e eventos são ignorados
|
|
115
|
+
- Remove os event listeners automaticamente ao destruir o componente
|
|
116
|
+
|
|
117
|
+
## Componentes relacionados
|
|
118
|
+
|
|
119
|
+
- [`Button`](../button/README.md) — botão nativo com acessibilidade integrada; prefira quando a semântica de botão é adequada
|
|
120
|
+
- [`MouseEvents`](../mouse-events/README.md) — diretivas para eventos de mouse avançados (long press, double click)
|