@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
package/dialog/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Componente de diálogo modal que exibe conteúdo em uma janela sobreposta. Pode ser utilizado de forma declarativa (via `[(visible)]` no template) ou programática (via `DialogService.open()`).
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Exibir formulários ou conteúdo que requer atenção exclusiva do usuário
|
|
10
|
+
- Fluxos de confirmação, edição ou criação em sobreposição à tela atual
|
|
11
|
+
- Quando precisa capturar o resultado do diálogo (botão confirmar/cancelar)
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Confirmações simples sem formulário — use [`ConfirmDialog`](../confirm-dialog/README.md)
|
|
16
|
+
- Mensagens de feedback — use [`Alert`](../alert/README.md) ou notificações inline
|
|
17
|
+
- Tooltips ou popovers de ajuda — use [`HelpPopover`](../help-popover/README.md)
|
|
18
|
+
|
|
19
|
+
## Instalação
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { DialogComponent, DialogService } from '@seniorsistemas/angular-components/dialog';
|
|
23
|
+
|
|
24
|
+
@Component({
|
|
25
|
+
standalone: true,
|
|
26
|
+
imports: [DialogComponent],
|
|
27
|
+
providers: [DialogService],
|
|
28
|
+
})
|
|
29
|
+
export class MeuComponent {}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Uso básico
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<!-- Modo declarativo -->
|
|
36
|
+
<s-button label="Abrir" (clicked)="visible = true"></s-button>
|
|
37
|
+
|
|
38
|
+
<s-dialog header="Título" [(visible)]="visible">
|
|
39
|
+
<p>Conteúdo do diálogo</p>
|
|
40
|
+
<ng-template sTemplate="footer" let-activeDialog="activeDialog">
|
|
41
|
+
<s-button label="Cancelar" priority="secondary" (clicked)="activeDialog.dismiss()"></s-button>
|
|
42
|
+
<s-button label="Confirmar" priority="primary" (clicked)="activeDialog.close('ok')"></s-button>
|
|
43
|
+
</ng-template>
|
|
44
|
+
</s-dialog>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### Inputs
|
|
50
|
+
|
|
51
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
52
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
53
|
+
| `header` | `string` | — | — | Texto do cabeçalho. Pode ser substituído por `sTemplate="header"` |
|
|
54
|
+
| `visible` | `boolean` | `undefined` | — | Controla a visibilidade. Suporta two-way binding via `[(visible)]`. Não usar com `DialogService` |
|
|
55
|
+
| `size` | `DialogSize` | `'md'` | — | Tamanho do diálogo |
|
|
56
|
+
| `escapeOnEsc` | `boolean` | `true` | — | Fecha o diálogo ao pressionar Escape |
|
|
57
|
+
| `destroyClickOutside` | `boolean` | `true` | — | Fecha o diálogo ao clicar no backdrop |
|
|
58
|
+
| `contentClassName` | `string` | `''` | — | Classes CSS adicionais no container de conteúdo |
|
|
59
|
+
| `closeAriaLabel` | `string` | `'Fechar'` | — | Aria-label do botão de fechar |
|
|
60
|
+
|
|
61
|
+
### Tipos
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
|
|
65
|
+
// sm = 300px | md = 500px | lg = 800px | xl = 1140px | fullscreen
|
|
66
|
+
|
|
67
|
+
type DialogOptions = {
|
|
68
|
+
destroyClickOutside?: boolean;
|
|
69
|
+
escapeOnEsc?: boolean;
|
|
70
|
+
size?: DialogSize;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type DialogRef<T> = {
|
|
74
|
+
componentInstance: T;
|
|
75
|
+
closed: Observable<any>; // Emite ao chamar activeDialog.close(result)
|
|
76
|
+
dismissed: Observable<any>; // Emite ao chamar activeDialog.dismiss() ou fechar pelo X/Esc
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Exemplos
|
|
81
|
+
|
|
82
|
+
### Modo declarativo
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<s-button label="Abrir Dialog" priority="primary" (clicked)="dialogVisible = true"></s-button>
|
|
86
|
+
|
|
87
|
+
<s-dialog
|
|
88
|
+
header="Editar registro"
|
|
89
|
+
[(visible)]="dialogVisible"
|
|
90
|
+
size="lg"
|
|
91
|
+
[escapeOnEsc]="true"
|
|
92
|
+
[destroyClickOutside]="false"
|
|
93
|
+
>
|
|
94
|
+
<ng-template sTemplate="body">
|
|
95
|
+
<p>Conteúdo do formulário aqui.</p>
|
|
96
|
+
</ng-template>
|
|
97
|
+
<ng-template sTemplate="footer" let-activeDialog="activeDialog">
|
|
98
|
+
<div style="display: flex; justify-content: flex-end; gap: 8px; width: 100%;">
|
|
99
|
+
<s-button label="Cancelar" priority="secondary" (clicked)="activeDialog.dismiss()"></s-button>
|
|
100
|
+
<s-button label="Salvar" priority="primary" (clicked)="activeDialog.close('salvo')"></s-button>
|
|
101
|
+
</div>
|
|
102
|
+
</ng-template>
|
|
103
|
+
</s-dialog>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Modo programático via DialogService
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Componente de conteúdo do diálogo
|
|
110
|
+
@Component({
|
|
111
|
+
standalone: true,
|
|
112
|
+
imports: [TemplateModule, ButtonModule],
|
|
113
|
+
template: `
|
|
114
|
+
<ng-template sTemplate="footer" let-activeDialog="activeDialog">
|
|
115
|
+
<s-button label="Cancelar" (clicked)="activeDialog.dismiss()"></s-button>
|
|
116
|
+
<s-button label="Confirmar" priority="primary" (clicked)="activeDialog.close('confirmado')"></s-button>
|
|
117
|
+
</ng-template>
|
|
118
|
+
<p>Conteúdo do diálogo.</p>
|
|
119
|
+
`,
|
|
120
|
+
})
|
|
121
|
+
class MeuDialogComponent {}
|
|
122
|
+
|
|
123
|
+
// No componente pai
|
|
124
|
+
const ref = this.dialogService.open(MeuDialogComponent, { size: 'lg' });
|
|
125
|
+
ref.closed.subscribe(result => console.log('Fechado:', result));
|
|
126
|
+
ref.dismissed.subscribe(() => console.log('Dispensado'));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Cabeçalho customizado
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<s-dialog [(visible)]="visible">
|
|
133
|
+
<ng-template sTemplate="header">
|
|
134
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
135
|
+
<i class="fas fa-exclamation-triangle" style="color: #f59e0b;"></i>
|
|
136
|
+
<span>Atenção</span>
|
|
137
|
+
</div>
|
|
138
|
+
</ng-template>
|
|
139
|
+
<p>Esta ação requer sua atenção.</p>
|
|
140
|
+
</s-dialog>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Acessibilidade
|
|
144
|
+
|
|
145
|
+
- O diálogo é renderizado como uma janela modal com `role="dialog"` e captura o foco enquanto está aberto
|
|
146
|
+
- Pressionar `Escape` fecha o diálogo quando `escapeOnEsc` é `true`
|
|
147
|
+
- O botão de fechar possui `aria-label` configurável via `closeAriaLabel`
|
|
148
|
+
- O backdrop bloqueia interação com o conteúdo de fundo
|
|
149
|
+
|
|
150
|
+
## Componentes relacionados
|
|
151
|
+
|
|
152
|
+
- [`ConfirmDialog`](../confirm-dialog/README.md) — diálogo de confirmação rápido via serviço, sem template customizado
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# DynamicForm
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Componente de formulário dinâmico que renderiza campos configuráveis a partir de uma estrutura de dados (`DynamicStructure[]`). Integra-se a um `FormGroup` Angular existente e suporta uma grande variedade de tipos de campo, layout em grid e organização em seções e fieldsets.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Formulários gerados a partir de metadados ou configuração da API
|
|
10
|
+
- Campos que precisam de layout em grid responsivo configurável
|
|
11
|
+
- Formulários com estrutura variável em runtime
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Formulários simples com poucos campos fixos — crie o template diretamente
|
|
16
|
+
- Quando a flexibilidade de layout do DynamicForm é desnecessária e adiciona complexidade sem ganho
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
|
|
22
|
+
// ou standalone:
|
|
23
|
+
import { DynamicFormComponent } from '@seniorsistemas/angular-components/dynamic-form';
|
|
24
|
+
|
|
25
|
+
@Component({ standalone: true, imports: [DynamicFormComponent] })
|
|
26
|
+
export class MeuComponent {}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Uso básico
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<s-dynamic-form [form]="meuFormGroup" [configs]="estrutura" />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
this.form = new FormGroup({
|
|
37
|
+
nome: new FormControl(''),
|
|
38
|
+
email: new FormControl(''),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
this.configs: DynamicStructure[] = [{
|
|
42
|
+
type: 'row',
|
|
43
|
+
fields: [
|
|
44
|
+
{ name: 'nome', type: 'string', label: 'Nome', size: { md: 6 } },
|
|
45
|
+
{ name: 'email', type: 'string', label: 'E-mail', size: { md: 6 } },
|
|
46
|
+
]
|
|
47
|
+
}];
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API
|
|
51
|
+
|
|
52
|
+
### Inputs
|
|
53
|
+
|
|
54
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
55
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
56
|
+
| `form` | `FormGroup` | — | Sim | `FormGroup` Angular ao qual os campos serão vinculados |
|
|
57
|
+
| `configs` | `DynamicStructure[]` | `[]` | — | Lista de estruturas de campos dinâmicos a renderizar |
|
|
58
|
+
| `fields` | `DynamicType[]` | — | — | **Depreciado.** Use `configs` no lugar |
|
|
59
|
+
| `id` | `string` | — | — | Identificador único do formulário no DOM |
|
|
60
|
+
| `errorMessages` | `any` | `{}` | — | Mapa de mensagens de erro globais por nome de validador |
|
|
61
|
+
|
|
62
|
+
### Tipos de campo disponíveis
|
|
63
|
+
|
|
64
|
+
| Tipo | Descrição |
|
|
65
|
+
|------|-----------|
|
|
66
|
+
| `string` | Campo de texto simples |
|
|
67
|
+
| `password` | Campo de senha com toggle de visibilidade |
|
|
68
|
+
| `textArea` | Área de texto multilinha |
|
|
69
|
+
| `number` | Campo numérico com máscara |
|
|
70
|
+
| `integer` | Campo de número inteiro |
|
|
71
|
+
| `money` | Campo monetário |
|
|
72
|
+
| `slider` | Controle deslizante |
|
|
73
|
+
| `date` | Seletor de data |
|
|
74
|
+
| `dateTime` | Seletor de data e hora |
|
|
75
|
+
| `time` | Seletor de hora |
|
|
76
|
+
| `boolean` | Radio button sim/não/vazio |
|
|
77
|
+
| `booleanSwitch` | Toggle switch |
|
|
78
|
+
| `enum` | Dropdown de opções |
|
|
79
|
+
| `radioButton` | Grupo de radio buttons |
|
|
80
|
+
| `autocomplete` | Campo com sugestões |
|
|
81
|
+
| `chips` | Campo de tags |
|
|
82
|
+
| `checkbox` | Checkbox hierárquico |
|
|
83
|
+
| `lookup` | Campo de busca avançada (modal) |
|
|
84
|
+
| `starRating` | Avaliação por estrelas |
|
|
85
|
+
| `contentGenerator` | Gerador de conteúdo com IA |
|
|
86
|
+
| `profilePicture` | Upload de foto de perfil |
|
|
87
|
+
| `countryPhonePicker` | Seletor de telefone por país |
|
|
88
|
+
| `blob` | Upload de arquivo |
|
|
89
|
+
|
|
90
|
+
### Estruturas de layout
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
type DynamicStructure =
|
|
94
|
+
| { type: 'row'; fields: DynamicType[] }
|
|
95
|
+
| { type: 'section'; title?: string; configs: DynamicStructure[] }
|
|
96
|
+
| { type: 'fieldset'; legend?: string; configs: DynamicStructure[] };
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Exemplos
|
|
100
|
+
|
|
101
|
+
### Formulário básico com grid
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<form [formGroup]="form" style="max-width: 800px;">
|
|
105
|
+
<s-dynamic-form [form]="form" [configs]="configs" />
|
|
106
|
+
</form>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
this.form = new FormGroup({
|
|
111
|
+
nome: new FormControl(''),
|
|
112
|
+
email: new FormControl(''),
|
|
113
|
+
tipo: new FormControl(null),
|
|
114
|
+
ativo: new FormControl(false),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.configs = [{
|
|
118
|
+
type: 'row',
|
|
119
|
+
fields: [
|
|
120
|
+
{ name: 'nome', type: 'string', label: 'Nome', size: { md: 6 } },
|
|
121
|
+
{ name: 'email', type: 'string', label: 'E-mail', size: { md: 6 } },
|
|
122
|
+
{
|
|
123
|
+
name: 'tipo',
|
|
124
|
+
type: 'enum',
|
|
125
|
+
label: 'Tipo',
|
|
126
|
+
size: { md: 6 },
|
|
127
|
+
options: [
|
|
128
|
+
{ label: 'Pessoa Física', value: 'pf' },
|
|
129
|
+
{ label: 'Pessoa Jurídica', value: 'pj' },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{ name: 'ativo', type: 'booleanSwitch', label: 'Ativo', size: { md: 6 },
|
|
133
|
+
optionsLabel: { true: 'Sim', false: 'Não' } },
|
|
134
|
+
],
|
|
135
|
+
}];
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Formulário com data e número
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
this.configs = [{
|
|
142
|
+
type: 'row',
|
|
143
|
+
fields: [
|
|
144
|
+
{ name: 'valor', type: 'number', label: 'Valor', size: { md: 4 }, allowNegative: false },
|
|
145
|
+
{ name: 'data', type: 'date', label: 'Data', size: { md: 4 }, appendTo: 'body' },
|
|
146
|
+
{ name: 'descricao', type: 'string', label: 'Descrição', size: { md: 4 } },
|
|
147
|
+
],
|
|
148
|
+
}];
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Formulário com seções
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
this.configs = [{
|
|
155
|
+
type: 'section',
|
|
156
|
+
title: 'Dados Pessoais',
|
|
157
|
+
configs: [{
|
|
158
|
+
type: 'row',
|
|
159
|
+
fields: [
|
|
160
|
+
{ name: 'nome', type: 'string', label: 'Nome', size: { md: 6 } },
|
|
161
|
+
{ name: 'cpf', type: 'string', label: 'CPF', size: { md: 6 } },
|
|
162
|
+
]
|
|
163
|
+
}]
|
|
164
|
+
}];
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Acessibilidade
|
|
168
|
+
|
|
169
|
+
- Os campos internos utilizam `label` vinculado via `for`/`id`
|
|
170
|
+
- Campos obrigatórios são marcados com `aria-required`
|
|
171
|
+
- Mensagens de erro são associadas aos campos correspondentes
|
|
172
|
+
|
|
173
|
+
## Componentes relacionados
|
|
174
|
+
|
|
175
|
+
- [`CustomFields`](../custom-fields/README.md) — campos customizados via API da plataforma Senior, baseado no DynamicForm
|
|
176
|
+
- [`Fieldset`](../fieldset/README.md) — agrupador de campos com suporte a toggle
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# EditableOverlay
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Diretiva aplicada a um `p-table` do PrimeNG para corrigir o comportamento de clique fora da célula editável. Evita que cliques em overlays internos (dropdowns, modais, lookups) fechem indevidamente o modo de edição da célula da tabela.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Tabelas PrimeNG (`p-table`) com células editáveis que contêm dropdowns, selects ou modais
|
|
10
|
+
- Quando o modo de edição da célula fecha ao interagir com overlays internos, impedindo a seleção de valores
|
|
11
|
+
|
|
12
|
+
## Quando não usar
|
|
13
|
+
|
|
14
|
+
- Tabelas sem modo de edição inline — a diretiva não tem efeito nesse cenário
|
|
15
|
+
- Tabelas que não são do PrimeNG — a diretiva sobrescreve métodos internos do `p-table`
|
|
16
|
+
|
|
17
|
+
## Instalação
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { EditableOverlayModule } from '@seniorsistemas/angular-components/editable-overlay';
|
|
21
|
+
|
|
22
|
+
@Component({ standalone: true, imports: [EditableOverlayModule] })
|
|
23
|
+
export class MeuComponent {}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Uso básico
|
|
27
|
+
|
|
28
|
+
Aplique `sEditableOverlay` no elemento `p-table` e passe a referência da tabela via `[table]`:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<p-table sEditableOverlay [table]="dt" #dt>
|
|
32
|
+
<ng-template pTemplate="body" let-row>
|
|
33
|
+
<tr>
|
|
34
|
+
<td pEditableColumn>
|
|
35
|
+
<p-cellEditor>
|
|
36
|
+
<ng-template pTemplate="input">
|
|
37
|
+
<p-dropdown [options]="opcoes" [(ngModel)]="row.tipo"></p-dropdown>
|
|
38
|
+
</ng-template>
|
|
39
|
+
<ng-template pTemplate="output">
|
|
40
|
+
{{ row.tipo }}
|
|
41
|
+
</ng-template>
|
|
42
|
+
</p-cellEditor>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
</ng-template>
|
|
46
|
+
</p-table>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### Inputs (na diretiva `sEditableOverlay`)
|
|
52
|
+
|
|
53
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
54
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
55
|
+
| `table` | `any` | — | Sim | Referência direta à instância do `p-table` (mesmo que a template reference variable `#dt`) |
|
|
56
|
+
|
|
57
|
+
## Exemplos
|
|
58
|
+
|
|
59
|
+
### Tabela com dropdown em célula editável
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<p-table [value]="dados" sEditableOverlay [table]="dt" #dt>
|
|
63
|
+
<ng-template pTemplate="header">
|
|
64
|
+
<tr>
|
|
65
|
+
<th>Nome</th>
|
|
66
|
+
<th>Categoria</th>
|
|
67
|
+
</tr>
|
|
68
|
+
</ng-template>
|
|
69
|
+
<ng-template pTemplate="body" let-item>
|
|
70
|
+
<tr>
|
|
71
|
+
<td>{{ item.nome }}</td>
|
|
72
|
+
<td pEditableColumn>
|
|
73
|
+
<p-cellEditor>
|
|
74
|
+
<ng-template pTemplate="input">
|
|
75
|
+
<p-dropdown
|
|
76
|
+
[options]="categorias"
|
|
77
|
+
[(ngModel)]="item.categoria"
|
|
78
|
+
appendTo="body"
|
|
79
|
+
></p-dropdown>
|
|
80
|
+
</ng-template>
|
|
81
|
+
<ng-template pTemplate="output">
|
|
82
|
+
{{ item.categoria }}
|
|
83
|
+
</ng-template>
|
|
84
|
+
</p-cellEditor>
|
|
85
|
+
</td>
|
|
86
|
+
</tr>
|
|
87
|
+
</ng-template>
|
|
88
|
+
</p-table>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Acessibilidade
|
|
92
|
+
|
|
93
|
+
- A diretiva não altera atributos ARIA — a acessibilidade da tabela editável depende da configuração do `p-table` do PrimeNG
|
|
94
|
+
- O comportamento de foco e teclado permanece o mesmo do `p-table`
|
|
95
|
+
|
|
96
|
+
## Componentes relacionados
|
|
97
|
+
|
|
98
|
+
- [`Select`](../select/README.md) — dropdown de seleção usado em células editáveis
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# EmptyState
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Componente de estado vazio para exibição padronizada de mensagens quando não há conteúdo a ser exibido. Apresenta ícone, título, descrição opcional e até dois botões de ação (primário e secundário), com suporte a menu de múltiplas opções no botão primário.
|
|
6
|
+
|
|
7
|
+
## Quando usar
|
|
8
|
+
|
|
9
|
+
- Listagens sem registros onde o usuário deve ser orientado sobre a próxima ação
|
|
10
|
+
- Telas de busca sem resultados
|
|
11
|
+
- Páginas de erro ou acesso negado onde o usuário deve ser redirecionado
|
|
12
|
+
|
|
13
|
+
## Quando não usar
|
|
14
|
+
|
|
15
|
+
- Estados de carregamento — use um skeleton ou spinner
|
|
16
|
+
- Mensagens de feedback sobre ações recém executadas — use [`Alert`](../alert/README.md)
|
|
17
|
+
- Erros de formulário — use [`ControlErrors`](../control-errors/README.md)
|
|
18
|
+
|
|
19
|
+
## Instalação
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { EmptyStateModule } from '@seniorsistemas/angular-components/empty-state';
|
|
23
|
+
|
|
24
|
+
@Component({ standalone: true, imports: [EmptyStateModule] })
|
|
25
|
+
export class MeuComponent {}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Uso básico
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<s-empty-state
|
|
32
|
+
title="Nenhum registro encontrado"
|
|
33
|
+
(primaryAction)="onCreate()"
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
### Inputs
|
|
40
|
+
|
|
41
|
+
| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|
|
42
|
+
|-------------|------|--------|:-----------:|-----------|
|
|
43
|
+
| `title` | `string` | — | Sim | Título principal exibido no estado vazio |
|
|
44
|
+
| `iconClass` | `string` | `'fa fa-inbox'` | — | Classe CSS do ícone (ex: `'fa fa-search'`) |
|
|
45
|
+
| `description` | `string` | — | — | Texto descritivo exibido abaixo do título |
|
|
46
|
+
| `showPrimaryAction` | `boolean` | `true` | — | Controla a visibilidade do botão de ação primária |
|
|
47
|
+
| `showSecondaryAction` | `boolean` | `true` | — | Controla a visibilidade do botão de ação secundária |
|
|
48
|
+
| `primaryActionLabel` | `string` | — | — | Texto do botão de ação primária |
|
|
49
|
+
| `secondaryActionLabel` | `string` | — | — | Texto do botão de ação secundária |
|
|
50
|
+
| `primaryModel` | `TieredMenuItemData[]` | `[]` | — | Itens do menu do botão primário. Quando fornecido, transforma o botão em split-button |
|
|
51
|
+
| `id` | `string` | `s-empty-state-{n}` | — | Identificador único do componente |
|
|
52
|
+
|
|
53
|
+
### Outputs
|
|
54
|
+
|
|
55
|
+
| Evento | Tipo | Descrição |
|
|
56
|
+
|--------|------|-----------|
|
|
57
|
+
| `primaryAction` | `EventEmitter<void>` | Emitido ao clicar no botão primário (quando `primaryModel` está vazio) |
|
|
58
|
+
| `secondaryAction` | `EventEmitter<void>` | Emitido ao clicar no botão secundário |
|
|
59
|
+
|
|
60
|
+
## Exemplos
|
|
61
|
+
|
|
62
|
+
### Estado vazio simples
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<s-empty-state
|
|
66
|
+
title="Nenhum registro encontrado"
|
|
67
|
+
[showPrimaryAction]="false"
|
|
68
|
+
[showSecondaryAction]="false"
|
|
69
|
+
></s-empty-state>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Com descrição e ação primária
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<s-empty-state
|
|
76
|
+
title="Nenhum registro encontrado"
|
|
77
|
+
description="Clique no botão abaixo para adicionar um novo registro."
|
|
78
|
+
iconClass="fa fa-inbox"
|
|
79
|
+
[showPrimaryAction]="true"
|
|
80
|
+
primaryActionLabel="Adicionar"
|
|
81
|
+
[showSecondaryAction]="false"
|
|
82
|
+
(primaryAction)="onCreate()"
|
|
83
|
+
></s-empty-state>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Com ambas as ações
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<s-empty-state
|
|
90
|
+
title="Nenhum registro encontrado"
|
|
91
|
+
description="Adicione um novo registro ou cancele para voltar."
|
|
92
|
+
[showPrimaryAction]="true"
|
|
93
|
+
primaryActionLabel="Adicionar"
|
|
94
|
+
[showSecondaryAction]="true"
|
|
95
|
+
secondaryActionLabel="Cancelar"
|
|
96
|
+
(primaryAction)="onCreate()"
|
|
97
|
+
(secondaryAction)="onCancel()"
|
|
98
|
+
></s-empty-state>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Com menu de opções no botão primário
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<s-empty-state
|
|
105
|
+
title="Nenhum registro encontrado"
|
|
106
|
+
[showPrimaryAction]="true"
|
|
107
|
+
primaryActionLabel="Adicionar"
|
|
108
|
+
[primaryModel]="[
|
|
109
|
+
{ label: 'Adicionar manualmente', iconClass: 'fa fa-plus' },
|
|
110
|
+
{ label: 'Importar arquivo', iconClass: 'fa fa-upload' }
|
|
111
|
+
]"
|
|
112
|
+
></s-empty-state>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Componente auxiliar para redirecionamento ao portal
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<s-empty-state-go-back
|
|
119
|
+
title="Acesso não autorizado"
|
|
120
|
+
description="Você não tem permissão para acessar esta página."
|
|
121
|
+
iconClass="fa fa-exclamation-triangle"
|
|
122
|
+
primaryActionLabel="Voltar ao portal"
|
|
123
|
+
></s-empty-state-go-back>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Acessibilidade
|
|
127
|
+
|
|
128
|
+
- O ícone é puramente decorativo e não possui texto alternativo obrigatório
|
|
129
|
+
- Os botões de ação são elementos `<button>` com labels visíveis
|
|
130
|
+
- O título deve descrever claramente o estado vazio para leitores de tela
|
|
131
|
+
|
|
132
|
+
## Componentes relacionados
|
|
133
|
+
|
|
134
|
+
- [`Button`](../button/README.md) — botões de ação primária e secundária usados internamente
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
1
|
+
import { Component, Input, ViewEncapsulation } from '@angular/core';
|
|
2
2
|
import { LoadingStateIndicators } from './components/loading-state-indicators';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import * as i1 from "@angular/common";
|
|
@@ -92,11 +92,11 @@ export class LoadingStateComponent {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoadingStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
95
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoadingStateComponent, selector: "s-loading-state", inputs: { indicator: "indicator", id: "id", blockWindow: "blockWindow", loading: "loading" }, ngImport: i0, template: "<div\n [id]=\"id\"\n class=\"s-loading-state\"\n [ngClass]=\"{\n 's-loading-state--loading': loading,\n 's-loading-state--blocking': blocking,\n 's-loading-state--fullscreen': blockWindow\n }\"\n>\n <div\n [id]=\"id + '-loader'\"\n class=\"loader\"\n >\n <div\n [id]=\"id + '-spinner'\"\n class=\"spinner\"\n >\n @switch (indicator) {\n @case ('logo') {\n <s-logo-indicator></s-logo-indicator>\n }\n @case ('dots') {\n <s-dots-indicator></s-dots-indicator>\n }\n }\n </div>\n <div\n [id]=\"id + '-overlay'\"\n class=\"overlay\"\n ></div>\n </div>\n <div\n [id]=\"id + '-contents'\"\n class=\"state-contents\"\n >\n <ng-container *ngTemplateOutlet=\"contents || originalContent\"></ng-container>\n <ng-template #originalContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", styles: [".s-loading-state{position:relative}.s-loading-state .loader{opacity:0;position:absolute;transition:opacity .2s ease-out,display .2s ease-out;visibility:hidden;inset:1px;display:flex;justify-content:center;align-items:center}.s-loading-state .loader .overlay{
|
|
95
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoadingStateComponent, selector: "s-loading-state", inputs: { indicator: "indicator", id: "id", blockWindow: "blockWindow", loading: "loading" }, ngImport: i0, template: "<div\n [id]=\"id\"\n class=\"s-loading-state\"\n [ngClass]=\"{\n 's-loading-state--loading': loading,\n 's-loading-state--blocking': blocking,\n 's-loading-state--fullscreen': blockWindow\n }\"\n>\n <div\n [id]=\"id + '-loader'\"\n class=\"loader\"\n >\n <div\n [id]=\"id + '-spinner'\"\n class=\"spinner\"\n >\n @switch (indicator) {\n @case ('logo') {\n <s-logo-indicator></s-logo-indicator>\n }\n @case ('dots') {\n <s-dots-indicator></s-dots-indicator>\n }\n }\n </div>\n <div\n [id]=\"id + '-overlay'\"\n class=\"overlay\"\n ></div>\n </div>\n <div\n [id]=\"id + '-contents'\"\n class=\"state-contents\"\n >\n <ng-container *ngTemplateOutlet=\"contents || originalContent\"></ng-container>\n <ng-template #originalContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", styles: ["s-loading-state .s-loading-state{position:relative}s-loading-state .s-loading-state .loader{opacity:0;position:absolute;transition:opacity .2s ease-out,display .2s ease-out;visibility:hidden;inset:1px;display:flex;justify-content:center;align-items:center}s-loading-state .s-loading-state .loader .overlay{height:100%;opacity:.8;transition:opacity .2s ease-out,display .2s ease-out;width:100%}s-loading-state .s-loading-state .loader .spinner{display:flex;position:absolute;z-index:2}s-loading-state .s-loading-state.s-loading-state--loading>.loader,s-loading-state .s-loading-state.s-loading-state--blocking>.loader{cursor:wait;visibility:visible;z-index:2}s-loading-state .s-loading-state.s-loading-state--loading>.loader>.overlay,s-loading-state .s-loading-state.s-loading-state--blocking>.loader>.overlay{cursor:wait;z-index:1}s-loading-state .s-loading-state.s-loading-state--loading>.state-contents,s-loading-state .s-loading-state.s-loading-state--blocking>.state-contents{position:relative;z-index:0!important;opacity:.5}s-loading-state .s-loading-state.s-loading-state--fullscreen>.loader{left:0;position:fixed;top:0;z-index:1005}s-loading-state .s-loading-state.s-loading-state--blocking.s-loading-state--loading>.loader{opacity:1}s-loading-state .s-loading-state-container.s-loading-state--loading.s-loading-state--blocking s-loading-state .s-loading-state-container>.loader{opacity:0;visibility:hidden}s-loading-state .s-loading-state-container.s-loading-state--loading.s-loading-state--blocking s-loading-state .s-loading-state-container>.state-contents{z-index:0!important}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.DotsIndicatorComponent, selector: "s-dots-indicator" }, { kind: "component", type: i3.LoadingIndicatorComponent, selector: "s-logo-indicator" }], encapsulation: i0.ViewEncapsulation.None });
|
|
96
96
|
}
|
|
97
97
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoadingStateComponent, decorators: [{
|
|
98
98
|
type: Component,
|
|
99
|
-
args: [{ selector: 's-loading-state', template: "<div\n [id]=\"id\"\n class=\"s-loading-state\"\n [ngClass]=\"{\n 's-loading-state--loading': loading,\n 's-loading-state--blocking': blocking,\n 's-loading-state--fullscreen': blockWindow\n }\"\n>\n <div\n [id]=\"id + '-loader'\"\n class=\"loader\"\n >\n <div\n [id]=\"id + '-spinner'\"\n class=\"spinner\"\n >\n @switch (indicator) {\n @case ('logo') {\n <s-logo-indicator></s-logo-indicator>\n }\n @case ('dots') {\n <s-dots-indicator></s-dots-indicator>\n }\n }\n </div>\n <div\n [id]=\"id + '-overlay'\"\n class=\"overlay\"\n ></div>\n </div>\n <div\n [id]=\"id + '-contents'\"\n class=\"state-contents\"\n >\n <ng-container *ngTemplateOutlet=\"contents || originalContent\"></ng-container>\n <ng-template #originalContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", styles: [".s-loading-state{position:relative}.s-loading-state .loader{opacity:0;position:absolute;transition:opacity .2s ease-out,display .2s ease-out;visibility:hidden;inset:1px;display:flex;justify-content:center;align-items:center}.s-loading-state .loader .overlay{
|
|
99
|
+
args: [{ selector: 's-loading-state', encapsulation: ViewEncapsulation.None, template: "<div\n [id]=\"id\"\n class=\"s-loading-state\"\n [ngClass]=\"{\n 's-loading-state--loading': loading,\n 's-loading-state--blocking': blocking,\n 's-loading-state--fullscreen': blockWindow\n }\"\n>\n <div\n [id]=\"id + '-loader'\"\n class=\"loader\"\n >\n <div\n [id]=\"id + '-spinner'\"\n class=\"spinner\"\n >\n @switch (indicator) {\n @case ('logo') {\n <s-logo-indicator></s-logo-indicator>\n }\n @case ('dots') {\n <s-dots-indicator></s-dots-indicator>\n }\n }\n </div>\n <div\n [id]=\"id + '-overlay'\"\n class=\"overlay\"\n ></div>\n </div>\n <div\n [id]=\"id + '-contents'\"\n class=\"state-contents\"\n >\n <ng-container *ngTemplateOutlet=\"contents || originalContent\"></ng-container>\n <ng-template #originalContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", styles: ["s-loading-state .s-loading-state{position:relative}s-loading-state .s-loading-state .loader{opacity:0;position:absolute;transition:opacity .2s ease-out,display .2s ease-out;visibility:hidden;inset:1px;display:flex;justify-content:center;align-items:center}s-loading-state .s-loading-state .loader .overlay{height:100%;opacity:.8;transition:opacity .2s ease-out,display .2s ease-out;width:100%}s-loading-state .s-loading-state .loader .spinner{display:flex;position:absolute;z-index:2}s-loading-state .s-loading-state.s-loading-state--loading>.loader,s-loading-state .s-loading-state.s-loading-state--blocking>.loader{cursor:wait;visibility:visible;z-index:2}s-loading-state .s-loading-state.s-loading-state--loading>.loader>.overlay,s-loading-state .s-loading-state.s-loading-state--blocking>.loader>.overlay{cursor:wait;z-index:1}s-loading-state .s-loading-state.s-loading-state--loading>.state-contents,s-loading-state .s-loading-state.s-loading-state--blocking>.state-contents{position:relative;z-index:0!important;opacity:.5}s-loading-state .s-loading-state.s-loading-state--fullscreen>.loader{left:0;position:fixed;top:0;z-index:1005}s-loading-state .s-loading-state.s-loading-state--blocking.s-loading-state--loading>.loader{opacity:1}s-loading-state .s-loading-state-container.s-loading-state--loading.s-loading-state--blocking s-loading-state .s-loading-state-container>.loader{opacity:0;visibility:hidden}s-loading-state .s-loading-state-container.s-loading-state--loading.s-loading-state--blocking s-loading-state .s-loading-state-container>.state-contents{z-index:0!important}\n"] }]
|
|
100
100
|
}], propDecorators: { indicator: [{
|
|
101
101
|
type: Input
|
|
102
102
|
}], id: [{
|
|
@@ -107,4 +107,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
107
107
|
type: Input,
|
|
108
108
|
args: [{ required: true }]
|
|
109
109
|
}] } });
|
|
110
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
110
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGluZy1zdGF0ZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWNvbXBvbmVudHMvbG9hZGluZy1zdGF0ZS9zcmMvbGliL2xvYWRpbmctc3RhdGUvbG9hZGluZy1zdGF0ZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWNvbXBvbmVudHMvbG9hZGluZy1zdGF0ZS9zcmMvbGliL2xvYWRpbmctc3RhdGUvbG9hZGluZy1zdGF0ZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBMEIsaUJBQWlCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFNUYsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7Ozs7O0FBRS9FOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBT0gsTUFBTSxPQUFPLHFCQUFxQjtJQUN2QixNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUV6Qjs7Ozs7O09BTUc7SUFFSSxTQUFTLEdBQTJCLHNCQUFzQixDQUFDLElBQUksQ0FBQztJQUV2RTs7T0FFRztJQUVJLEVBQUUsR0FBRyxtQkFBbUIscUJBQXFCLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztJQUVoRTs7Ozs7T0FLRztJQUVJLFdBQVcsR0FBRyxLQUFLLENBQUM7SUFFM0I7Ozs7T0FJRztJQUNILElBQ1csT0FBTyxDQUFDLE9BQWdCO1FBQy9CLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDNUMsQ0FBQztJQUVELElBQVcsT0FBTztRQUNkLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN6QixDQUFDO0lBRU0sUUFBUSxHQUE0QixJQUFJLENBQUM7SUFDekMsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUVoQixRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ2pCLG1CQUFtQixHQUFHLEdBQUcsQ0FBQztJQUMxQixxQkFBcUIsR0FBRyxHQUFHLENBQUM7SUFDNUIsY0FBYyxDQUFNO0lBQ3BCLGdCQUFnQixDQUFNO0lBRXZCLFdBQVc7UUFDZCxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN0QixZQUFZLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3hCLFlBQVksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUN4QyxDQUFDO0lBQ0wsQ0FBQztJQUVNLEtBQUs7UUFDUixJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3hCLFlBQVksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztZQUNwQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsU0FBUyxDQUFDO1FBQ3RDLENBQUM7UUFFRCxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxjQUFjLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRTtnQkFDbEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7Z0JBQ3JCLElBQUksQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFDO1lBQ3BDLENBQUMsRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUNqQyxDQUFDO0lBQ0wsQ0FBQztJQUVNLE9BQU87UUFDVixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN0QixZQUFZLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQ2xDLElBQUksQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFDO1FBQ3BDLENBQUM7UUFFRCxJQUFJLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQ3BDLElBQUksQ0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDO2dCQUN0QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsU0FBUyxDQUFDO1lBQ3RDLENBQUMsRUFBRSxJQUFJLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUNuQyxDQUFDO0lBQ0wsQ0FBQzt3R0F4RlEscUJBQXFCOzRGQUFyQixxQkFBcUIscUpDekJsQywra0NBeUNBOzs0RkRoQmEscUJBQXFCO2tCQU5qQyxTQUFTOytCQUNJLGlCQUFpQixpQkFHWixpQkFBaUIsQ0FBQyxJQUFJOzhCQWE5QixTQUFTO3NCQURmLEtBQUs7Z0JBT0MsRUFBRTtzQkFEUixLQUFLO2dCQVVDLFdBQVc7c0JBRGpCLEtBQUs7Z0JBU0ssT0FBTztzQkFEakIsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBPbkRlc3Ryb3ksIFRlbXBsYXRlUmVmLCBWaWV3RW5jYXBzdWxhdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBMb2FkaW5nU3RhdGVJbmRpY2F0b3JzIH0gZnJvbSAnLi9jb21wb25lbnRzL2xvYWRpbmctc3RhdGUtaW5kaWNhdG9ycyc7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIENvbXBvbmVudGUgZGUgZXN0YWRvIGRlIGNhcnJlZ2FtZW50byBxdWUgZXhpYmUgdW0gaW5kaWNhZG9yIGFuaW1hZG8gZW5xdWFudG9cbiAqIHVtIHByb2Nlc3NvIGFzc8OtbmNyb25vIGVzdMOhIGVtIGFuZGFtZW50by4gU3Vwb3J0YSBibG9xdWVpbyBkYSBqYW5lbGEgaW50ZWlyYVxuICogZSBkaWZlcmVudGVzIHRpcG9zIGRlIGluZGljYWRvcmVzIHZpc3VhaXMuXG4gKiBUYW1iw6ltIHBvZGUgc2VyIHVzYWRvIGNvbW8gZGlyZXRpdmEgZXN0cnV0dXJhbCBgW3NMb2FkaW5nU3RhdGVdYC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgaHRtbFxuICogPHMtbG9hZGluZy1zdGF0ZSBbbG9hZGluZ109XCJpc0xvYWRpbmdcIiBbYmxvY2tXaW5kb3ddPVwidHJ1ZVwiPlxuICogICA8cD5Db250ZcO6ZG8gY2FycmVnYWRvITwvcD5cbiAqIDwvcy1sb2FkaW5nLXN0YXRlPlxuICogYGBgXG4gKlxuICogQGNhdGVnb3J5IEZlZWRiYWNrXG4gKi9cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAncy1sb2FkaW5nLXN0YXRlJyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vbG9hZGluZy1zdGF0ZS5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbJy4vbG9hZGluZy1zdGF0ZS5jb21wb25lbnQuc2NzcyddLFxuICAgIGVuY2Fwc3VsYXRpb246IFZpZXdFbmNhcHN1bGF0aW9uLk5vbmUsXG59KVxuZXhwb3J0IGNsYXNzIExvYWRpbmdTdGF0ZUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gICAgcHVibGljIHN0YXRpYyBuZXh0SWQgPSAwO1xuXG4gICAgLyoqXG4gICAgICogQGRlc2NyaXB0aW9uIFRpcG8gZGUgaW5kaWNhZG9yIHZpc3VhbCBleGliaWRvIGR1cmFudGUgbyBjYXJyZWdhbWVudG8uXG4gICAgICogLSBgTG9hZGluZ1N0YXRlSW5kaWNhdG9ycy5Eb3RzYDogaW5kaWNhZG9yIGRlIHBvbnRvcyBhbmltYWRvcyAocGFkcsOjbykuXG4gICAgICogLSBgTG9hZGluZ1N0YXRlSW5kaWNhdG9ycy5Mb2dvYDogaW5kaWNhZG9yIGNvbSBsb2dvdGlwbyBhbmltYWRvLlxuICAgICAqXG4gICAgICogQGRlZmF1bHQgTG9hZGluZ1N0YXRlSW5kaWNhdG9ycy5Eb3RzXG4gICAgICovXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgaW5kaWNhdG9yOiBMb2FkaW5nU3RhdGVJbmRpY2F0b3JzID0gTG9hZGluZ1N0YXRlSW5kaWNhdG9ycy5Eb3RzO1xuXG4gICAgLyoqXG4gICAgICogQGRlc2NyaXB0aW9uIElkZW50aWZpY2Fkb3Igw7puaWNvIGRvIGNvbXBvbmVudGUuIEdlcmFkbyBhdXRvbWF0aWNhbWVudGUgc2UgbsOjbyBpbmZvcm1hZG8uXG4gICAgICovXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgaWQgPSBgcy1sb2FkaW5nLXN0YXRlLSR7TG9hZGluZ1N0YXRlQ29tcG9uZW50Lm5leHRJZCsrfWA7XG5cbiAgICAvKipcbiAgICAgKiBAZGVzY3JpcHRpb24gUXVhbmRvIGB0cnVlYCwgYmxvcXVlaWEgYSBqYW5lbGEgaW50ZWlyYSBkdXJhbnRlIG8gY2FycmVnYW1lbnRvLFxuICAgICAqIHNvYnJlcG9uZG8gdG9kbyBvIGNvbnRlw7pkbyBkYSBww6FnaW5hIGNvbSBvIGluZGljYWRvci5cbiAgICAgKlxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgYmxvY2tXaW5kb3cgPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIEBkZXNjcmlwdGlvbiBDb250cm9sYSBvIGVzdGFkbyBkZSBjYXJyZWdhbWVudG8gZG8gY29tcG9uZW50ZS4gQ2FtcG8gb2JyaWdhdMOzcmlvLlxuICAgICAqIFF1YW5kbyBgdHJ1ZWAsIGV4aWJlIG8gaW5kaWNhZG9yIGRlIGNhcnJlZ2FtZW50byBhcMOzcyB1bSBicmV2ZSBkZWxheS5cbiAgICAgKiBRdWFuZG8gYGZhbHNlYCwgcmVtb3ZlIG8gaW5kaWNhZG9yIGFww7NzIHVtIGJyZXZlIGRlbGF5IChldml0YSBmbGFzaCkuXG4gICAgICovXG4gICAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSlcbiAgICBwdWJsaWMgc2V0IGxvYWRpbmcobG9hZGluZzogYm9vbGVhbikge1xuICAgICAgICB0aGlzLl9sb2FkaW5nID0gbG9hZGluZztcbiAgICAgICAgbG9hZGluZyA/IHRoaXMuYmxvY2soKSA6IHRoaXMudW5ibG9jaygpO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgbG9hZGluZygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRpbmc7XG4gICAgfVxuXG4gICAgcHVibGljIGNvbnRlbnRzOiBUZW1wbGF0ZVJlZjxhbnk+IHwgbnVsbCA9IG51bGw7XG4gICAgcHVibGljIGJsb2NraW5nID0gZmFsc2U7XG5cbiAgICBwcml2YXRlIF9sb2FkaW5nID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBCTE9DS19USU1FT1VUX1ZBTFVFID0gMzAwO1xuICAgIHByaXZhdGUgVU5CTE9DS19USU1FT1VUX1ZBTFVFID0gMjAwO1xuICAgIHByaXZhdGUgYmxvY2tUaW1lb3V0SWQ6IGFueTtcbiAgICBwcml2YXRlIHVuYmxvY2tUaW1lb3V0SWQ6IGFueTtcblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuYmxvY2tUaW1lb3V0SWQpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aGlzLmJsb2NrVGltZW91dElkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLnVuYmxvY2tUaW1lb3V0SWQpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aGlzLnVuYmxvY2tUaW1lb3V0SWQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGJsb2NrKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy51bmJsb2NrVGltZW91dElkKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy51bmJsb2NrVGltZW91dElkKTtcbiAgICAgICAgICAgIHRoaXMudW5ibG9ja1RpbWVvdXRJZCA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy5ibG9ja1RpbWVvdXRJZCkge1xuICAgICAgICAgICAgdGhpcy5ibG9ja1RpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuYmxvY2tpbmcgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHRoaXMuYmxvY2tUaW1lb3V0SWQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9LCB0aGlzLkJMT0NLX1RJTUVPVVRfVkFMVUUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHVuYmxvY2soKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmJsb2NrVGltZW91dElkKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5ibG9ja1RpbWVvdXRJZCk7XG4gICAgICAgICAgICB0aGlzLmJsb2NrVGltZW91dElkID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF0aGlzLnVuYmxvY2tUaW1lb3V0SWQpIHtcbiAgICAgICAgICAgIHRoaXMudW5ibG9ja1RpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuYmxvY2tpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB0aGlzLnVuYmxvY2tUaW1lb3V0SWQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9LCB0aGlzLlVOQkxPQ0tfVElNRU9VVF9WQUxVRSk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbiIsIjxkaXZcbiAgICBbaWRdPVwiaWRcIlxuICAgIGNsYXNzPVwicy1sb2FkaW5nLXN0YXRlXCJcbiAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICdzLWxvYWRpbmctc3RhdGUtLWxvYWRpbmcnOiBsb2FkaW5nLFxuICAgICAgICAncy1sb2FkaW5nLXN0YXRlLS1ibG9ja2luZyc6IGJsb2NraW5nLFxuICAgICAgICAncy1sb2FkaW5nLXN0YXRlLS1mdWxsc2NyZWVuJzogYmxvY2tXaW5kb3dcbiAgICB9XCJcbj5cbiAgICA8ZGl2XG4gICAgICAgIFtpZF09XCJpZCArICctbG9hZGVyJ1wiXG4gICAgICAgIGNsYXNzPVwibG9hZGVyXCJcbiAgICA+XG4gICAgICAgIDxkaXZcbiAgICAgICAgICAgIFtpZF09XCJpZCArICctc3Bpbm5lcidcIlxuICAgICAgICAgICAgY2xhc3M9XCJzcGlubmVyXCJcbiAgICAgICAgPlxuICAgICAgICAgICAgQHN3aXRjaCAoaW5kaWNhdG9yKSB7XG4gICAgICAgICAgICAgICAgQGNhc2UgKCdsb2dvJykge1xuICAgICAgICAgICAgICAgICAgICA8cy1sb2dvLWluZGljYXRvcj48L3MtbG9nby1pbmRpY2F0b3I+XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIEBjYXNlICgnZG90cycpIHtcbiAgICAgICAgICAgICAgICAgICAgPHMtZG90cy1pbmRpY2F0b3I+PC9zLWRvdHMtaW5kaWNhdG9yPlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXZcbiAgICAgICAgICAgIFtpZF09XCJpZCArICctb3ZlcmxheSdcIlxuICAgICAgICAgICAgY2xhc3M9XCJvdmVybGF5XCJcbiAgICAgICAgPjwvZGl2PlxuICAgIDwvZGl2PlxuICAgIDxkaXZcbiAgICAgICAgW2lkXT1cImlkICsgJy1jb250ZW50cydcIlxuICAgICAgICBjbGFzcz1cInN0YXRlLWNvbnRlbnRzXCJcbiAgICA+XG4gICAgICAgIDxuZy1jb250YWluZXIgKm5nVGVtcGxhdGVPdXRsZXQ9XCJjb250ZW50cyB8fCBvcmlnaW5hbENvbnRlbnRcIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPG5nLXRlbXBsYXRlICNvcmlnaW5hbENvbnRlbnQ+XG4gICAgICAgICAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==
|