@praxisui/tabs 8.0.0-beta.9 → 8.0.0-beta.90

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,47 @@ 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
+ - `configChange: TabsConfigChangeEvent` Emite `inputPatch.config` quando autoria assistida altera `TabsMetadata`, permitindo que hosts persistam a nova configuração no manifesto/página canônica.
86
+ - `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`.
87
+
88
+ ## Uso Controlado por Composição
89
+
90
+ `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`.
91
+
92
+ Padrão recomendado para páginas dinâmicas:
93
+
94
+ - grave a seleção do usuário com `selectedIndexChange -> state`;
95
+ - projete o estado de volta com `state -> selectedIndex`;
96
+ - declare `selectedIndex` depois de `config` em `bindingOrder`, para que a configuração seja carregada antes da seleção controlada;
97
+ - não persista `inputs.selectedIndex` estático em recipes quando a seleção vem de `composition.links`.
98
+
99
+ 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`.
100
+
101
+ Exemplo canônico:
102
+
103
+ ```json
104
+ {
105
+ "links": [
106
+ {
107
+ "id": "tabs.selectedIndexChange->state.navigation.activeTabIndex",
108
+ "from": { "kind": "component-port", "ref": { "widget": "workspaceTabs", "port": "selectedIndexChange" } },
109
+ "to": { "kind": "state", "ref": { "path": "navigation.activeTabIndex", "layer": "values", "write": true } }
110
+ },
111
+ {
112
+ "id": "state.navigation.activeTabIndex->tabs.selectedIndex",
113
+ "from": { "kind": "state", "ref": { "path": "navigation.activeTabIndex", "layer": "values" } },
114
+ "to": { "kind": "component-port", "ref": { "widget": "workspaceTabs", "port": "selectedIndex" } }
115
+ }
116
+ ]
117
+ }
118
+ ```
85
119
 
86
120
  Persistência
87
121
  - Quando `tabsId` é fornecido, a configuração é salva/recuperada em `AsyncConfigStorage` na chave `tabs:<component_id>`.
@@ -110,8 +144,8 @@ export interface TabsMetadata {
110
144
  behavior?: { lazyLoad?: boolean; closeable?: boolean; reorderable?: boolean };
111
145
  accessibility?: { highContrast?: boolean; reduceMotion?: boolean };
112
146
  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 };
147
+ tabs?: Array<{ id?: string; textLabel?: string; icon?: string; disabled?: boolean; visible?: boolean; labelClass?: string|string[]; bodyClass?: string|string[]; content?: any[]; widgets?: WidgetDefinition[] }>;
148
+ 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
149
  }
116
150
  ```
117
151
 
@@ -146,18 +180,59 @@ Quick Setup
146
180
  - `openQuickSetup()` abre `TabsQuickSetupComponent` para criação rápida de abas/links.
147
181
  - `applied$`/`saved$` aplicam a configuração ao componente.
148
182
 
183
+ ## Agentic Authoring
184
+
185
+ `@praxisui/tabs` publica `PRAXIS_TABS_AUTHORING_MANIFEST` para orientar edições assistidas por IA sobre `TabsMetadata`.
186
+
187
+ - **Editable targets:** `tab`, `tabLabel`, `tabIcon`, `tabContent`, `activeTab`, `visibility`, `disabledState` e `layout`.
188
+ - **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`.
189
+ - **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.
190
+ - **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.
191
+ - **Current-area authoring:** pedidos como “nesta aba”, “aba atual” ou “dentro da aba” devem materializar `tab.content.set` no item ativo/resolvido, não `tab.add`; criar nova aba exige intenção explícita como “nova aba” ou “adicionar aba”.
192
+ - **Registry projection:** o manifesto é exportado no `public-api` e projetado em `components['praxis-tabs'].authoringManifest` pelo AI Registry.
193
+
194
+ ### Praxis Semantic Assistant
195
+
196
+ When `enableCustomization=true`, `praxis-tabs` opens the shared `PraxisAiAssistantShellComponent` and registers a minimized global session instead of embedding the legacy `PraxisAiAssistantComponent`.
197
+
198
+ - The assistant context is safe and semantic: component identity, tabs id, current mode, tab/link counts, field-name digest, manifest reference and governance hints.
199
+ - 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.
200
+ - 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`.
201
+ - `TabsAiAdapter.applyPatch(...)` remains available for legacy/editor internals, but the new assistant turn flow does not use it as an ungoverned patch path.
202
+
149
203
  ## Eventos e Conexões
150
204
 
151
- `widgetEvent` reemite eventos de widgets internos com contexto da origem, permitindo conexões no Builder/Graph:
205
+ Para conexoes canonicas de widgets internos, use `composition.links` com endpoint `component-port + nestedPath`.
206
+
207
+ Exemplo de output de uma lista dentro da primeira tab:
208
+
209
+ ```json
210
+ {
211
+ "kind": "component-port",
212
+ "ref": {
213
+ "widget": "tabs-widget",
214
+ "nestedPath": [
215
+ { "kind": "tab", "id": "employees-list", "index": 0 },
216
+ { "kind": "widget", "key": "employees-list", "componentType": "praxis-list" }
217
+ ],
218
+ "port": "itemClick",
219
+ "direction": "output"
220
+ }
221
+ }
222
+ ```
223
+
224
+ Regras:
225
+
226
+ - `ref.widget` e a instancia top-level de `praxis-tabs`;
227
+ - `nestedPath` e relativo a essa instancia e deve terminar em `kind: "widget"` com `key` estavel;
228
+ - `ref.port` e a porta real do widget filho;
229
+ - inputs para filhos nested devem atualizar a configuracao declarativa do filho, nao depender de dot-path publico sobre `config`.
230
+
231
+ `widgetEvent` continua existindo como bridge avancada/legado para transporte de eventos internos com contexto da origem:
152
232
 
153
233
  - 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>`
234
+ - Nao use `widgetEvent` como caminho principal para authoring novo de nested ports.
235
+ - Nao use dot-path de `config.tabs[].widgets[]` como contrato publico para novos links nested.
161
236
 
162
237
  ## Lazy Load
163
238
 
@@ -167,7 +242,7 @@ Quick Setup
167
242
  ## Exemplo Mínimo
168
243
 
169
244
  ```html
170
- <praxis-tabs [config]="tabsCfg" [tabsId]="'cliente-tabs'" [enableCustomization]="true" (widgetEvent)="onWidgetEvent($event)"></praxis-tabs>
245
+ <praxis-tabs [config]="tabsCfg" [tabsId]="'cliente-tabs'" [enableCustomization]="true"></praxis-tabs>
171
246
  ```
172
247
 
173
248
  ```ts