@praxisui/tabs 8.0.0-beta.2 → 8.0.0-beta.21

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 CHANGED
@@ -75,13 +75,46 @@ Inputs
75
75
  - `componentInstanceId?: string` Opcional para desambiguar múltiplas instâncias com o mesmo `tabsId` na mesma rota.
76
76
  - `form?: FormGroup` FormGroup opcional para campos dinâmicos declarados em `content`.
77
77
  - `context?: any` Contexto propagado a widgets internos (via `DynamicWidgetLoader`).
78
+ - `selectedIndex?: number` Índice ativo controlado por composição; não reemite `selectedIndexChange`.
78
79
  - `enableCustomization?: boolean` Exibe botão de edição quando verdadeiro (abre o editor).
79
80
 
80
81
  Outputs
81
82
  - `selectedIndexChange: number` Índice selecionado atualizado (ambos modos).
82
83
  - `selectedTabChange: MatTabChangeEvent` Evento nativo do MatTabGroup.
83
84
  - `focusChange, animationDone, indexFocused, selectFocusedIndex` Eventos nativos do Angular Material.
84
- - `widgetEvent: { tabId?, tabIndex?, linkId?, linkIndex?, sourceId, output?, payload? }` Reemissão de eventos dos widgets internos com contexto da aba/link.
85
+ - `widgetEvent: WidgetEventEnvelope` Bridge avançada/legado para transporte de eventos dos widgets internos com contexto da aba/link. Para conexões novas de widgets internos, use `composition.links` com `component-port + nestedPath`.
86
+
87
+ ## Uso Controlado por Composição
88
+
89
+ `selectedIndex` é a porta pública para controlar a aba ativa a partir de estado externo ou de outro componente. Quando esse input é aplicado, o componente atualiza a aba ativa sem reemitir `selectedIndexChange`, evitando ciclos entre `state -> selectedIndex` e `selectedIndexChange -> state`.
90
+
91
+ Padrão recomendado para páginas dinâmicas:
92
+
93
+ - grave a seleção do usuário com `selectedIndexChange -> state`;
94
+ - projete o estado de volta com `state -> selectedIndex`;
95
+ - declare `selectedIndex` depois de `config` em `bindingOrder`, para que a configuração seja carregada antes da seleção controlada;
96
+ - não persista `inputs.selectedIndex` estático em recipes quando a seleção vem de `composition.links`.
97
+
98
+ Quando existe configuração persistida por `tabsId`, o valor controlado por `selectedIndex` vence a restauração local depois do carregamento da config. Essa projeção controlada não grava uma nova preferência no storage; somente interações diretas do usuário persistem seleção. Isso mantém o estado canônico da composição como fonte de verdade da navegação ativa sem transformar navegação transitória em configuração salva. Em modo `nav`, o índice controla `nav.links`; em modo `group`, controla `tabs`.
99
+
100
+ Exemplo canônico:
101
+
102
+ ```json
103
+ {
104
+ "links": [
105
+ {
106
+ "id": "tabs.selectedIndexChange->state.navigation.activeTabIndex",
107
+ "from": { "kind": "component-port", "ref": { "widget": "workspaceTabs", "port": "selectedIndexChange" } },
108
+ "to": { "kind": "state", "ref": { "path": "navigation.activeTabIndex", "layer": "values", "write": true } }
109
+ },
110
+ {
111
+ "id": "state.navigation.activeTabIndex->tabs.selectedIndex",
112
+ "from": { "kind": "state", "ref": { "path": "navigation.activeTabIndex", "layer": "values" } },
113
+ "to": { "kind": "component-port", "ref": { "widget": "workspaceTabs", "port": "selectedIndex" } }
114
+ }
115
+ ]
116
+ }
117
+ ```
85
118
 
86
119
  Persistência
87
120
  - Quando `tabsId` é fornecido, a configuração é salva/recuperada em `AsyncConfigStorage` na chave `tabs:<component_id>`.
@@ -110,8 +143,8 @@ export interface TabsMetadata {
110
143
  behavior?: { lazyLoad?: boolean; closeable?: boolean; reorderable?: boolean };
111
144
  accessibility?: { highContrast?: boolean; reduceMotion?: boolean };
112
145
  group?: { alignTabs?: 'start' | 'center' | 'end'; headerPosition?: 'above'|'below'; selectedIndex?: number; dynamicHeight?: boolean; disableRipple?: boolean; disablePagination?: boolean; fitInkBarToContent?: boolean; stretchTabs?: boolean; color?: 'primary'|'accent'|'warn'; backgroundColor?: 'primary'|'accent'|'warn'|undefined; animationDuration?: string; ariaLabel?: string; ariaLabelledby?: string; };
113
- tabs?: Array<{ id?: string; textLabel?: string; disabled?: boolean; labelClass?: string|string[]; bodyClass?: string|string[]; content?: any[]; widgets?: WidgetDefinition[] }>;
114
- nav?: { links: Array<{ id?: string; label: string; disabled?: boolean; content?: any[]; widgets?: WidgetDefinition[] }>; selectedIndex?: number; disableRipple?: boolean; disablePagination?: boolean; fitInkBarToContent?: boolean; stretchTabs?: boolean; color?: 'primary'|'accent'|'warn'; backgroundColor?: 'primary'|'accent'|'warn'|undefined; animationDuration?: string; ariaLabel?: string; ariaLabelledby?: string };
146
+ tabs?: Array<{ id?: string; textLabel?: string; icon?: string; disabled?: boolean; visible?: boolean; labelClass?: string|string[]; bodyClass?: string|string[]; content?: any[]; widgets?: WidgetDefinition[] }>;
147
+ nav?: { links: Array<{ id?: string; label: string; icon?: string; disabled?: boolean; visible?: boolean; content?: any[]; widgets?: WidgetDefinition[] }>; selectedIndex?: number; disableRipple?: boolean; disablePagination?: boolean; fitInkBarToContent?: boolean; stretchTabs?: boolean; color?: 'primary'|'accent'|'warn'; backgroundColor?: 'primary'|'accent'|'warn'|undefined; animationDuration?: string; ariaLabel?: string; ariaLabelledby?: string };
115
148
  }
116
149
  ```
117
150
 
@@ -146,18 +179,58 @@ Quick Setup
146
179
  - `openQuickSetup()` abre `TabsQuickSetupComponent` para criação rápida de abas/links.
147
180
  - `applied$`/`saved$` aplicam a configuração ao componente.
148
181
 
182
+ ## Agentic Authoring
183
+
184
+ `@praxisui/tabs` publica `PRAXIS_TABS_AUTHORING_MANIFEST` para orientar edições assistidas por IA sobre `TabsMetadata`.
185
+
186
+ - **Editable targets:** `tab`, `tabLabel`, `tabIcon`, `tabContent`, `activeTab`, `visibility`, `disabledState` e `layout`.
187
+ - **Operation families:** `tab.add`, `tab.remove`, `tab.label.set`, `tab.icon.set`, `tab.order.set`, `tab.disabled.set`, `tab.visible.set`, `tab.active.set`, `layout.variant.set` e `tab.content.set`.
188
+ - **Validation:** ids de abas/links devem ser estáveis e únicos, remoção destrutiva exige confirmação, `group` e `nav` não devem virar modos primários concorrentes, e o round-trip precisa preservar ordem, ids e selected index.
189
+ - **Runtime/editor parity:** `tabs[].icon`, `tabs[].visible`, `nav.links[].icon` e `nav.links[].visible` são campos canônicos editáveis; itens com `visible: false` não são renderizados na navegação.
190
+ - **Registry projection:** o manifesto é exportado no `public-api` e projetado em `components['praxis-tabs'].authoringManifest` pelo AI Registry.
191
+
192
+ ### Praxis Semantic Assistant
193
+
194
+ When `enableCustomization=true`, `praxis-tabs` opens the shared `PraxisAiAssistantShellComponent` and registers a minimized global session instead of embedding the legacy `PraxisAiAssistantComponent`.
195
+
196
+ - The assistant context is safe and semantic: component identity, tabs id, current mode, tab/link counts, field-name digest, manifest reference and governance hints.
197
+ - Business-rule, policy, compliance, publication, materialization or enforcement prompts are routed to governed `domain-rules/intake` handoff and are not applied as local tabs configuration.
198
+ - Free JSON patches from the backend are rejected. Local apply remains blocked until the response is compiled from a manifest-backed `componentEditPlan` validated against `PRAXIS_TABS_AUTHORING_MANIFEST`.
199
+ - `TabsAiAdapter.applyPatch(...)` remains available for legacy/editor internals, but the new assistant turn flow does not use it as an ungoverned patch path.
200
+
149
201
  ## Eventos e Conexões
150
202
 
151
- `widgetEvent` reemite eventos de widgets internos com contexto da origem, permitindo conexões no Builder/Graph:
203
+ Para conexoes canonicas de widgets internos, use `composition.links` com endpoint `component-port + nestedPath`.
204
+
205
+ Exemplo de output de uma lista dentro da primeira tab:
206
+
207
+ ```json
208
+ {
209
+ "kind": "component-port",
210
+ "ref": {
211
+ "widget": "tabs-widget",
212
+ "nestedPath": [
213
+ { "kind": "tab", "id": "employees-list", "index": 0 },
214
+ { "kind": "widget", "key": "employees-list", "componentType": "praxis-list" }
215
+ ],
216
+ "port": "itemClick",
217
+ "direction": "output"
218
+ }
219
+ }
220
+ ```
221
+
222
+ Regras:
223
+
224
+ - `ref.widget` e a instancia top-level de `praxis-tabs`;
225
+ - `nestedPath` e relativo a essa instancia e deve terminar em `kind: "widget"` com `key` estavel;
226
+ - `ref.port` e a porta real do widget filho;
227
+ - inputs para filhos nested devem atualizar a configuracao declarativa do filho, nao depender de dot-path publico sobre `config`.
228
+
229
+ `widgetEvent` continua existindo como bridge avancada/legado para transporte de eventos internos com contexto da origem:
152
230
 
153
231
  - Forma do evento: `{ tabId?, tabIndex?, linkId?, linkIndex?, sourceId, output?, payload }`.
154
- - De um componente interno para fora:
155
- - From: `{ widget: '<key do tabs>', output: 'widgetEvent' }`
156
- - Map (exemplo tabela interna): `payload.payload.id`
157
- - To: `<widget externo>.<input>`
158
- - De fora para um componente interno (dot-path):
159
- - Grupo: `inputs.config.tabs[<idx>].widgets[<widx>].inputs.<input>`
160
- - Nav: `inputs.config.nav.links[<idx>].widgets[<widx>].inputs.<input>`
232
+ - Nao use `widgetEvent` como caminho principal para authoring novo de nested ports.
233
+ - Nao use dot-path de `config.tabs[].widgets[]` como contrato publico para novos links nested.
161
234
 
162
235
  ## Lazy Load
163
236
 
@@ -167,7 +240,7 @@ Quick Setup
167
240
  ## Exemplo Mínimo
168
241
 
169
242
  ```html
170
- <praxis-tabs [config]="tabsCfg" [tabsId]="'cliente-tabs'" [enableCustomization]="true" (widgetEvent)="onWidgetEvent($event)"></praxis-tabs>
243
+ <praxis-tabs [config]="tabsCfg" [tabsId]="'cliente-tabs'" [enableCustomization]="true"></praxis-tabs>
171
244
  ```
172
245
 
173
246
  ```ts