# @fiscal-io/ds-components

Design System Angular da Fiscal.io. A partir da **v8**, o pacote é fatiado em
entry points para o bundler não puxar Gantt, PDF Viewer, File Manager, Charts
etc. quando a aplicação só usa botão, grid ou layout.

## Breaking change (v7 → v8)

O barrel único `import { DsButtonComponent, DsGridModule } from '@fiscal-io/ds-components'`
acabou. Serviços de tema/locale/toast/sidebar continuam no entry principal;
cada componente vive no seu path.

```ts
import { ThemeService, DS_THEME_CONFIG, DsToastService } from '@fiscal-io/ds-components';
import { DsButtonComponent } from '@fiscal-io/ds-components/button';
import { DsGridModule, GridComponent, ColumnModel } from '@fiscal-io/ds-components/grid';
import { DsPdfViewerModule } from '@fiscal-io/ds-components/pdf-viewer';
import { DsNavbarComponent } from '@fiscal-io/ds-components/navbar';
import { DsSidebarComponent } from '@fiscal-io/ds-components/sidebar';
```

O consumidor declara só a lib (+ Angular). Syncfusion, Bootstrap e
bootstrap-icons vêm como **dependencies** do pacote — não precisa listar
`@syncfusion/*` no app. Continue importando por entry point
(`@fiscal-io/ds-components/button`, `/grid`, …) para o bundler tree-shakar.

Os widgets Syncfusion **não** usam o CSS stock dos pacotes `ej2-*` (isso
quebrava hover da grid e botões outline). O visual vem dos temas custom:

- `assets/themes/syncfusion-light.css`
- `assets/themes/syncfusion-dark.css`

Copie esses arquivos no `angular.json` e deixe o `ThemeService` carregar o
`<link id="sf-theme">` (ele cria/reordena o link sozinho).

## Estilos

```scss
// styles.scss — Bootstrap + ícones + overrides DS (sem CSS stock Syncfusion)
@use '@fiscal-io/ds-components/styles';
@use '@fiscal-io/ds-components/styles/variables' as ds;
```

```json
// angular.json — assets
{
  "glob": "syncfusion-*.css",
  "input": "node_modules/@fiscal-io/ds-components/src/lib/assets/themes/",
  "output": "assets/themes"
}
```

| Export | Conteúdo |
| --- | --- |
| `styles` / `styles/chrome` | Bootstrap + ícones + overrides DS (toast, color-picker, uploader, toolbar da grid) |
| `styles/core` | Bootstrap, fontes, ícones, resets |
| `styles/all` | Alias de chrome (Storybook / playground) |

O CSS dos widgets Syncfusion fica nos temas em `assets/themes/`, não nos
exports `styles/*`.

## Entry points

| Path | Exporta |
| --- | --- |
| `@fiscal-io/ds-components` | `ThemeService`, `DS_THEME_CONFIG`, `DsLocaleService`, `DsSidebarService`, `DsToastService` |
| `/button` `/checkbox` `/radio-button` `/toggle-switch` `/chips` | Controles de botão/seleção |
| `/input` `/color-picker` `/uploader` | Inputs |
| `/datepicker` `/daterangepicker` | Calendários |
| `/dropdown-list` `/dropdown-button` | Dropdowns |
| `/navbar` `/sidebar` `/breadcrumb` `/avatar` `/user-menu` `/apps-menu` | Layout |
| `/toast` `/stepper` `/tab` `/accordion` | Navegação / feedback |
| `/grid` | `DsGridModule`, diretiva `dsGrid`, tipos do EJ2 Grid |
| `/pdf-viewer` `/charts` `/gantt` `/file-manager` `/spreadsheet` `/scheduler` `/kanban` `/maps` `/diagram` `/document-editor` `/ribbon` `/rich-text-editor` `/query-builder` `/tree-grid` `/in-place-editor` | Passthroughs Syncfusion (`*Module`, sem `*AllModule`) |
| `/feature-card` `/error-page` `/form-validator` | Sem Syncfusion pesado |

## Grid: EJ2 nativo com defaults do Design System

`dsGrid` não encapsula nem traduz a API da Syncfusion. Ele é uma diretiva aplicada
ao próprio `ejs-grid`, portanto inputs, outputs, métodos, colunas, templates e
comportamentos são exatamente os documentados para o EJ2 Grid.

Importe `DsGridModule` e acrescente o atributo `dsGrid`:

```ts
import { DsGridModule } from '@fiscal-io/ds-components/grid';

@Component({
  imports: [DsGridModule],
})
export class OrdersComponent {}
```

```html
<ejs-grid dsGrid #grid id="orders-grid-v1" [dataSource]="orders()" [allowReordering]="true" [enablePersistence]="true" (recordClick)="openOrder($event.rowData)">
  <e-columns>
    <e-column field="id" headerText="ID" isPrimaryKey="true"></e-column>
    <e-column field="status" headerText="Status">
      <ng-template #template let-order>
        <strong>{{ order.status }}</strong>
      </ng-template>
    </e-column>
  </e-columns>
</ejs-grid>
```

O `#grid` acima é o `GridComponent` original. Métodos da documentação funcionam
diretamente, sem helpers do Design System:

```ts
grid.autoFitColumns();
grid.clearFiltering();
grid.reorderColumns("status", "id");
grid.excelExport();
```

### Defaults preservados

A diretiva mantém os defaults booleanos históricos da antiga `ds-grid`. Um
binding explícito sempre vence o default:

```html
<ejs-grid dsGrid [allowPaging]="false"></ejs-grid>
```

Os defaults DS são exportados em `DS_GRID_DEFAULTS` e incluem filtro, ordenação,
resize e paginação habilitados por padrão. Persistência e reordenação continuam
desabilitadas até serem habilitadas pelo consumidor.

### Persistência

Use a persistência nativa exatamente como na Syncfusion:

```html
<ejs-grid dsGrid id="orders-grid-v2" [enablePersistence]="true" [allowReordering]="true" [allowResizing]="true"> </ejs-grid>
```

Não existem aliases, estado inicial ou adapters próprios do Design System. Use
diretamente a API nativa: `id`, `enablePersistence`, `getPersistData()` e os
demais recursos documentados pela Syncfusion.

### Custom binding

Entregue o formato nativo `{ result, count }` e trate `dataStateChange` conforme
a documentação da Syncfusion. Mantenha o objeto em um campo ou signal estável,
em vez de criar um literal novo a cada ciclo de detecção:

```html
<ejs-grid dsGrid #grid [dataSource]="dataSource()" (created)="loadInitial(grid)" (dataStateChange)="load($event)"></ejs-grid>
```

O EJ2 não dispara `dataStateChange` no primeiro render. Quando a primeira busca
também precisa respeitar sort, filtro, página ou estado persistido, obtenha o
estado inicial pela própria API nativa:

```ts
readonly dataSource = signal({ result: [], count: 0 });

loadInitial(grid: GridComponent): void {
  const data = grid.getDataModule();
  this.load(data.getStateEventArgument(data.generateQuery()));
}
```

Nenhuma busca, paginação, ordenação ou filtragem é executada pelo Design System.
Essa decisão pertence ao consumidor da grid.

### Toolbar, ações em lote e contador de seleção

Ações em lote e contadores usam a toolbar nativa do EJ2, configurada no próprio
`ejs-grid` via `[toolbar]` ou `#toolbarTemplate`, exatamente como na
documentação oficial. A diretiva expõe `selectedCount` (via `exportAs: 'dsGrid'`)
com a contagem de linhas selecionadas na página atual:

```html
<ejs-grid dsGrid #ds="dsGrid" [dataSource]="rows()">
  <ng-template #toolbarTemplate>
    <div class="d-flex align-items-center gap-2 w-100">
      <button type="button" (click)="removeSelected()" [disabled]="ds.selectedCount() === 0">
        Remover
      </button>
      <span>{{ ds.selectedCount() }} itens selecionados</span>
    </div>
  </ng-template>
  <e-columns>
    <e-column type="checkbox" width="48"></e-column>
    <e-column field="id" headerText="ID"></e-column>
  </e-columns>
</ejs-grid>
```

Para toolbars com itens padrão (edição, exportação etc.), use `[toolbar]`:

```html
<ejs-grid dsGrid [toolbar]="['Add', 'Edit', 'Delete', 'Update', 'Cancel']"></ejs-grid>
```

## Building

```bash
npx ng build @fiscal-io/ds-components
```

## Publishing

```bash
cd dist/fiscal-io/ds-components
npm publish --access public
```
