@sixbell-telco/sdk
v3.1.0
Published
A collection of reusable components designed for use in Sixbell Telco Angular projects
Downloads
367
Maintainers
Readme
Sixbell Telco SDK - Component Library
Welcome to the Sixbell Telco SDK component library. This is a comprehensive Angular component library featuring 25+ pre-built, production-ready UI components built with Angular 19, Tailwind CSS v4, and DaisyUI.
The package is public on npm, but the repository is private and there is no separate public docs page. Because of that, this README keeps setup guidance, provider examples, translation examples, and usage references that public consumers need.
Overview
The SDK provides:
- 25+ Production-Ready Components: Button, Card, Form inputs, Data Table, Dialog, Dropdown, and more
- Utility Services: Theme management, i18n translation, logging, runtime config, and formatting
- Directive Support: Text, auto-focus, click-outside, and only-numbers
- Tailwind CSS v4 + DaisyUI: Modern styling with customizable themes
- Multi-Entry Point Architecture: Granular imports for optimized bundle sizes
- Angular 19 Signal-Based: Modern reactive patterns with Angular signals
Pre-requirements
- Node.js: ^18.19.1 || ^20.11.1 || ^22.0.0
- npm: 9.6.7 || ^10.0.0 || ^11.0.0
- Angular: 19.2.x or later
- Tailwind CSS: v4.x
Available Components
General Components
| Component | Description | | ------------- | ----------------------------------------------------------------------------- | | Accordion | Expandable/collapsible content sections | | Avatar | User profile pictures with initials fallback | | Badge | Status and label indicators | | Button | Interactive buttons with variants and sizes | | Card | Compound content shell with contextual headers, sections, and divided footers | | Countdown | Countdown timer with customizable formatting | | Data Table | Advanced grid with sorting, filtering, pagination, row actions | | Dialog | Modal dialogs with animations and positioning | | Divider | Semantic divider with orientation, placement, and margin control | | Dropdown | Menu dropdown for actions and links | | Dropdown Menu | Advanced nested menu system | | Dual List | Dual list selection for managing items | | File Upload | File uploader and dropzone entry point | | Icon | SVG icon component with ng-icons integration | | Link | Hyperlink component with router support | | Notification | Toast-style notifications | | Overlay | Generic overlay with trigger and content | | Paginator | Pagination controls for data | | Product Card | Product display card with image and actions | | Progress | Progress bar with variants | | Sonner Toast | Modern toast notifications via ngx-sonner | | Tab | Tabbed navigation interface | | Table | Data table with inline and dropdown row actions | | Text | Polymorphic text component with semantic HTML tags | | Tooltip | Floating tooltip with positioning | | Wizard | Multi-step wizard with validation |
Form Components
| Component | Description | | ---------- | ----------------------------------------------- | | Checkbox | Checkbox input with multiple variants | | Combobox | Searchable dropdown with multi-select support | | Datepicker | Single and range date selection with time | | Form Item | Layout wrapper for fields + full-width errors | | Form Error | Error message display for form fields | | Form Group | Vertical spacing helper for grouped fields | | Input | Text input with variants, icons, and validation | | Radio | Radio button group selection | | Range | Slider input for numeric ranges | | Select | Dropdown select with object support | | Switch | Toggle switch for boolean values | | Textarea | Multi-line text input with character count | | Toggle | Toggle button for boolean states |
Form Errors Layout
For full-width validation messages, wrap each field and its error with st-form-item and place st-form-errors as a sibling (not inside the field component).
<st-form-item>
<st-input formControlName="controlInput" label="Input" [parentForm]="form"></st-input>
<st-form-errors [parentForm]="form" formField="controlInput" />
</st-form-item>Migration notes: projects/sdk/components/forms/form-error/MIGRATION.md
Optional Label Indicator
Form fields can display an optional indicator inline with the legend. Enable it with showOptional and customize the text with optionalText.
<st-input label="Email" [showOptional]="true" [optionalText]="'sdk.formFields.optional'"></st-input>Add the translation key to your SDK translations (example shown for English):
{
"sdk": {
"formFields": {
"optional": "Optional"
}
}
}Utility Services
| Service | Purpose | Documentation | | ------------------- | -------------------------------------------------- | ------------------------------- | | Logger Service | Theme-aware logging with IndexedDB persistence | README | | Theme Service | Runtime theme management with multi-tenant support | README | | Translation Service | Multi-language i18n with ngx-translate integration | README | | Formatting Service | Reactive locale-aware formatting utilities | README | | Runtime Config | Last-known-good runtime config loader | README |
Directives
| Directive | Purpose | | ------------- | ----------------------------------------------------- | | auto-focus | Auto-focus elements when visible | | click-outside | Detect clicks outside an element | | only-numbers | Restrict input to numeric values only | | text | Apply preset-based typography with line-clamp support |
Installation
Step 1: Install Dependencies
npm install @sixbell-telco/sdk @tailwindcss/typography daisyui @midudev/tailwind-animationsStep 2: Import SDK Styles
In your global styles.css:
@import '../node_modules/@sixbell-telco/sdk/_index.css';
@import 'tailwindcss';
@source '../src';⚠️ Important: Import
_index.cssbefore the tailwind import.
Step 3: Configure Microfrontend (if applicable)
Add path mapping to tsconfig.json:
{
"compilerOptions": {
"paths": {
"@sixbell-telco/sdk/*": ["./node_modules/@sixbell-telco/sdk/*"]
}
}
}Step 4: Optional - VSCode Configuration
Install the Tailwind CSS extension and add to .vscode/settings.json:
{
"editor.quickSuggestions": {
"strings": "on"
},
"files.associations": {
"*.css": "tailwindcss"
},
"tailwindCSS.classFunctions": ["tw", "clsx", "cva", "cn", "tw\\.[a-z-]+"]
}These settings improve your development experience:
editor.quickSuggestions: Enables autosuggestions within string literalsfiles.associations: Ensures CSS files are treated as Tailwind CSS filestailwindCSS.classFunctions: Enables Tailwind intellisense in utility functions liketw(),clsx(), andcva(), etc that are used for conditional class name composition
With these settings, VSCode will provide class autocompletion for the library's custom attributes and utility functions.
Themes and translation configuration (an optional Logger)
Configure all services using runtime providers in your app.config.ts. This approach loads configuration from JSON files at runtime, eliminating the need to recompile when updating themes or languages.
Logger Service Setup
Configure logging with different levels for development and production:
import { provideLogger } from '@sixbell-telco/sdk/utils/logger';
export const appConfig: ApplicationConfig = {
providers: [
provideLogger({
enableLogging: true,
logLevel: isDevMode() ? 'debug' : 'warn', // trace | debug | info | warn | error | fatal
showTimestamp: true,
persistToIndexedDB: true, // Persistent storage for debugging
silentMode: !isDevMode(),
}),
],
};Translation Service Setup
The Translation Service loads language configuration from a runtime JSON file:
import { provideRuntimeTranslation } from '@sixbell-telco/sdk/utils/translation';
export const appConfig: ApplicationConfig = {
providers: [provideRuntimeTranslation('/assets/translation/translation.json')],
};The translation.json file defines available languages and their translation paths:
{
"meta": {
"updatedAt": "2026-02-02T00:00:00Z",
"hash": "showcase-translations-v2",
"schemaVersion": "3"
},
"additionalLanguages": [],
"defaultLang": "es",
"excludeLanguages": [],
"translationPaths": ["/assets/i18n/", "/assets/i18n/sdk/", "/assets/themes/i18n"],
"locale": {
"defaultLocale": "en-US",
"languageToLocale": {
"en": "en-US",
"es": "es-CL",
"pt": "pt-BR"
}
}
}Theme Service Setup
The Theme Service loads theme configuration from a runtime JSON file:
import { provideRuntimeTheme } from '@sixbell-telco/sdk/utils/theme';
export const appConfig: ApplicationConfig = {
providers: [provideRuntimeTheme('/assets/themes/themes.json')],
};The themes.json file defines available themes and their variants:
{
"meta": {
"updatedAt": "2026-02-02T00:00:00Z",
"hash": "showcase-themes-v1",
"schemaVersion": "2"
},
"themes": ["/assets/themes/catalog/sixbell_telco.json", "/assets/themes/catalog/wom.json"],
"defaultTheme": "sixbell_telco",
"defaultScheme": "system",
"excludeThemes": []
}Complete Configuration Example
// app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { provideLogger } from '@sixbell-telco/sdk/utils/logger';
import { provideRuntimeTheme } from '@sixbell-telco/sdk/utils/theme';
import { provideRuntimeTranslation } from '@sixbell-telco/sdk/utils/translation';
import { from, timer } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const runtimeUpdateStream$ = timer(0, environment.runtime.pollIntervalMs).pipe(
switchMap(() => from(fetch(`${environment.runtime.apiBaseUrl}/api/runtime/updates/poll`))),
switchMap((response) => from(response.json())),
);
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
provideLogger({
enableLogging: isDevMode(),
logLevel: isDevMode() ? 'debug' : 'warn',
showTimestamp: true,
showDate: true,
persistToIndexedDB: true,
idbRetentionDays: 7,
idbMaxLogs: 5000,
silentMode: !isDevMode(),
}),
...(() => {
const mode = environment.runtime.defaultMode;
if (mode === 'default') {
return [provideRuntimeTranslation('/assets/translation/translation.json'), provideRuntimeTheme('/assets/themes/themes.json')];
}
if (mode === 'stream' && environment.runtime.enableStream) {
return [
provideRuntimeTranslation(`${environment.runtime.apiBaseUrl}/api/runtime/translation/config`, {
updateStream: runtimeUpdateStream$,
}),
provideRuntimeTheme(`${environment.runtime.apiBaseUrl}/api/runtime/theme/config`, {
updateStream: runtimeUpdateStream$,
}),
];
}
return [
provideRuntimeTranslation(`${environment.runtime.apiBaseUrl}/api/runtime/translation/config`, {
sse: {
url: `${environment.runtime.apiBaseUrl}/api/runtime/updates?resource=translation`,
eventType: 'message',
},
}),
provideRuntimeTheme(`${environment.runtime.apiBaseUrl}/api/runtime/theme/config`, {
sse: {
url: `${environment.runtime.apiBaseUrl}/api/runtime/updates?resource=theme`,
eventType: 'message',
},
}),
];
})(),
],
};Reactive Formatting Setup
Use SDK formatting pipes and services instead of Angular native locale pipes:
{{ createdAt | stDate:'DD/MM/YYYY HH:mm:ss' }} {{ total | stDecimal:'1.0-2' }} {{ amount | stCurrency:'CLP':'symbol':'1.0-0' }} {{ ratio |
stPercent:'1.0-2' }}import { Component, computed, inject } from '@angular/core';
import { FormattingService } from '@sixbell-telco/sdk/utils/formatting';
@Component({
selector: 'app-formatting-example',
template: `{{ formattedAmount() }}`,
})
export class FormattingExampleComponent {
private readonly formatting = inject(FormattingService);
amount = 1234.5;
formattedAmount = computed(() =>
this.formatting.formatCurrency(this.amount, 'CLP', {
display: 'symbol',
digitsInfo: '1.0-0',
}),
);
}Key Benefits of Runtime Configuration:
- No Recompilation: Update themes and languages without rebuilding your application
- Multi-Tenant Support: Easily switch themes and language sets per deployment environment
- Caching Strategy: RuntimeConfigStore keeps last-known-good config in Dexie
- Hot Updates: Changes to
themes.jsonandtranslation.jsontake effect immediately - Persistent Preferences: User language and theme preferences are stored and restored automatically
Adding Custom Themes
Themes are managed through a runtime configuration file (themes.json) with theme JSON files. This approach allows for:
- Zero Recompilation: Add/modify themes without rebuilding
- Multi-Tenant Support: Different themes per deployment
- Dynamic Assets: Brand-specific logos and images per theme
- Runtime Fonts: Load custom fonts for each theme
- Hot Updates: Changes take effect immediately
1. Update themes.json
Add your custom theme to /assets/themes/themes.json:
{
"meta": {
"updatedAt": "2026-02-02T00:00:00Z",
"hash": "showcase-themes-v1",
"schemaVersion": "2"
},
"themes": ["/assets/themes/catalog/sixbell_telco.json", "/assets/themes/catalog/wom.json"],
"defaultTheme": "sixbell_telco",
"defaultScheme": "system",
"excludeThemes": []
}Configuration Structure:
themes: theme JSON pathsdefaultTheme: initial theme on app startupdefaultScheme: initial color scheme ('system','light', or'dark')
2. Define DaisyUI Themes in Global Styles
In your global styles.css, import the SDK and define your theme CSS:
@import '../node_modules/@sixbell-telco/sdk/_index.css';
@import 'tailwindcss';
@source '../src';3. Use Themes in Components
The ThemeService automatically loads available themes from themes.json:
import { Component, ChangeDetectionStrategy, inject, computed } from '@angular/core';
import { ThemeService, ThemeAssetsService } from '@sixbell-telco/sdk/utils/theme';
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-theme-switcher',
standalone: true,
imports: [CommonModule, ButtonComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
<select (change)="themeService.setTheme($event.target.value)">
@for (theme of themeService.getAvailableThemes(); track theme) {
<option [value]="theme" [selected]="themeService.selectedTheme() === theme">
{{ themeService.getLocalizedThemeName(theme) }}
</option>
}
</select>
<select (change)="themeService.setScheme($event.target.value)">
@for (scheme of themeService.getAvailableSchemes(); track scheme) {
<option [value]="scheme" [selected]="themeService.selectedScheme() === scheme">
{{ themeService.getLocalizedSchemeName(scheme) }}
</option>
}
</select>
<img [src]="logoUrl()" alt="Brand Logo" />
</div>
`,
})
export class ThemeSwitcherComponent {
themeService = inject(ThemeService);
private assetsService = inject(ThemeAssetsService);
logoUrl = computed(
() => this.assetsService.getAssetUrl(this.themeService.selectedTheme(), 'logo', this.themeService.resolvedScheme()) || '/default-logo.svg',
);
}4. Add Theme Names to Translations
Store theme and scheme display names in translation files. Create /assets/themes/i18n/en.json:
English (en.json):
{
"sdk": {
"theme": {
"catalog": {
"sixbell_telco": "Sixbell telco",
"wom": "Wom"
},
"schemes": {
"system": "System",
"light": "Light",
"dark": "Dark"
}
}
}
}Spanish (es.json):
{
"sdk": {
"theme": {
"catalog": {
"sixbell_telco": "Sixbell telco",
"wom": "Wom"
},
"schemes": {
"system": "Sistema",
"light": "Claro",
"dark": "Oscuro"
}
}
}
}Portuguese (pt.json):
{
"sdk": {
"theme": {
"catalog": {
"sixbell_telco": "Sixbell telco",
"wom": "Wom"
},
"schemes": {
"system": "Sistema",
"light": "Claro",
"dark": "Escuro"
}
}
}
}Your themes are now fully configured. The ThemeService automatically:
- Loads theme definitions from
themes.json - Injects CSS variables via signals
- Resolves dynamic assets based on current theme and scheme
- Persists user preferences to localStorage
- Detects system dark mode preference when scheme is set to
'system'
Adding Custom Languages
Languages are now managed through a runtime configuration file (translation.json), eliminating the need to reconfigure and recompile. This approach allows for:
- Zero Recompilation: Add or modify languages without rebuilding
- Multi-Tenant Languages: Different language sets per deployment environment
- Hot Updates: Changes to translation configuration take effect immediately
- Flexible Structure: Organize translation files any way you need
1. Create or Update translation.json
Create /assets/translation/translation.json in your application:
{
"meta": {
"updatedAt": "2026-02-02T00:00:00Z",
"hash": "showcase-translations-v2",
"schemaVersion": "3"
},
"additionalLanguages": [],
"defaultLang": "es",
"excludeLanguages": [],
"translationPaths": ["/assets/i18n/", "/assets/i18n/sdk/", "/assets/themes/i18n"],
"locale": {
"defaultLocale": "en-US",
"languageToLocale": {
"en": "en-US",
"es": "es-CL",
"pt": "pt-BR"
}
}
}Configuration Structure:
defaultLang: language code used on app initializationtranslationPaths: list of directories that contain{lang}.jsonadditionalLanguages: extra languages beyond the built-in SDK defaultsexcludeLanguages: built-in languages to remove from the available listlocale.languageToLocale: mapping used byFormattingServiceand the formatting pipes
2. Create Translation Files
Create language translation files at the paths defined in translation.json. The minimal translation structure includes SDK translations:
English (/assets/i18n/sdk/en.json):
{
"sdk": {
"fileUpload": {
"dropzone": {
"selectPrompt": "Click to select files or drag and drop here",
"maxSizeLabel": "Max size:",
"allowedTypesLabel": "Allowed types:",
"invalidFileType": "Invalid file type",
"fileTooLarge": "File too large",
"fileCounter": "{{current}} of {{max}} files",
"maxFilesExceeded": "Maximum {{max}} file(s) allowed",
"noValidFiles": "No valid files selected",
"fileActions": {
"play": "Play audio",
"pause": "Pause audio",
"download": "Download file",
"remove": "Remove file"
}
},
"fileUploader": {
"allowedTypesLabel": "Allowed types:"
}
},
"audioPlayer": {
"trackListTitle": "Tracks",
"trackInfo": {
"title": "No title",
"description": "No description"
}
},
"countdown": {
"days": "Days",
"hours": "Hours",
"minutes": "Minutes",
"seconds": "Seconds"
},
"dualList": {
"searchPlaceHolder": "Search"
},
"combobox": {
"searchPlaceholder": "Search",
"placeholder": "Select",
"noResultsFound": "No results found",
"clearAll": "Clear All",
"clearSelection": "Clear selection",
"searchOptions": "Search options",
"loading": "Loading..."
},
"select": {
"placeholder": "Select"
},
"formErrors": {
"validation": {
"required": "*This field is required",
"email": "*Please enter a valid email address",
"minLength": "*Must be at least {{min}} characters",
"maxLength": "*Must be no more than {{max}} characters",
"min": "*Value must be at least {{min}}",
"max": "*Value must be no more than {{max}}",
"pattern": "*Invalid format",
"unhandledError": "*Unhandled error"
}
},
"formFields": {
"optional": "Optional"
},
"textarea": {
"maxCharacters": "{{current}} of {{max}} characters"
},
"table": {
"entriesByPage": "Entries per page",
"perPage": "{{count}} / page",
"totalEntries": "{{total}} entries",
"entriesRange": "{{range}} of {{total}} entries",
"emptyListMessage": "No items to show",
"noData": {
"title": "No data available",
"message": "There are no records to display at this time."
},
"noResults": {
"title": "No results found",
"message": "Try adjusting your search criteria"
}
},
"dataTable": {
"entriesByPage": "Entries per page",
"perPage": "{{count}} / page",
"totalEntries": "{{total}} entries",
"entriesRange": "{{range}} of {{total}} entries",
"emptyListMessage": "No items to show",
"noData": {
"title": "No data available",
"message": "There are no records to display at this time."
},
"noResults": {
"title": "No results found",
"message": "Try adjusting your search criteria"
}
},
"wizard": {
"wizardMarker": {
"completed": "Completed",
"inProgress": "In progress",
"pending": "Pending"
},
"wizardWrapper": {
"back": "Back",
"previous": "Previous",
"end": "End",
"next": "Next",
"step": "Step {{index}}"
}
},
"datePicker": {
"quickActions": {
"title": "Quick Select",
"lastHour": "Last hour",
"last3Hours": "Last 3 hours",
"last6Hours": "Last 6 hours",
"last12Hours": "Last 12 hours",
"last24Hours": "Last 24 hours",
"last7Days": "Last 7 days",
"last30Days": "Last 30 days",
"last6Months": "Last 6 months",
"lastYear": "Last year",
"clear": "Clear"
},
"labels": {
"from": "From",
"to": "To"
}
}
}
}Spanish (/assets/i18n/es.json):
{
"sdk": {
"fileUpload": {
"dropzone": {
"selectPrompt": "Haz clic para seleccionar archivos o arrastra y suelta aquí",
"maxSizeLabel": "Tamaño máximo:",
"allowedTypesLabel": "Tipos permitidos:",
"invalidFileType": "Tipo de archivo no válido",
"fileTooLarge": "Archivo demasiado grande",
"fileCounter": "{{current}} de {{max}} archivos",
"maxFilesExceeded": "Máximo de {{max}} archivo(s) permitido",
"noValidFiles": "No se han seleccionado archivos válidos",
"fileActions": {
"play": "Reproducir audio",
"pause": "Pausar audio",
"download": "Descargar archivo",
"remove": "Quitar archivo"
}
},
"fileUploader": {
"allowedTypesLabel": "Tipos permitidos:"
}
},
"audioPlayer": {
"trackListTitle": "Pistas",
"trackInfo": {
"title": "Sin título",
"description": "Sin descripción"
}
},
"countdown": {
"days": "Días",
"hours": "Horas",
"minutes": "Minutos",
"seconds": "Segundos"
},
"dualList": {
"searchPlaceHolder": "Buscar"
},
"combobox": {
"searchPlaceholder": "Buscar",
"placeholder": "Seleccione",
"noResultsFound": "No se encontraron resultados",
"clearAll": "Limpiar Todo",
"clearSelection": "Limpiar selección",
"searchOptions": "Buscar opciones",
"loading": "Cargando..."
},
"select": {
"placeholder": "Seleccione"
},
"formErrors": {
"validation": {
"required": "*Este campo es obligatorio",
"email": "*Por favor ingresa una dirección de correo válida",
"minLength": "*Debe tener al menos {{min}} caracteres",
"maxLength": "*No debe tener más de {{max}} caracteres",
"min": "*El valor debe ser al menos {{min}}",
"max": "*El valor no debe superar {{max}}",
"pattern": "*Formato inválido",
"unhandledError": "*Error inesperado"
}
},
"textarea": {
"maxCharacters": "{{current}} de {{max}} caracteres"
},
"table": {
"entriesByPage": "Entradas por página",
"perPage": "{{count}} / página",
"totalEntries": "{{total}} entradas",
"entriesRange": "{{range}} de {{total}} entradas",
"emptyListMessage": "No hay elementos para mostrar"
},
"dataTable": {
"entriesByPage": "Entradas por página",
"perPage": "{{count}} / página",
"totalEntries": "{{total}} entradas",
"entriesRange": "{{range}} de {{total}} entradas",
"emptyListMessage": "No hay elementos para mostrar",
"noData": {
"title": "No hay datos disponibles",
"message": "No hay registros para mostrar en este momento."
},
"noResults": {
"title": "No se encontraron resultados",
"message": "Intenta ajustar tus criterios de búsqueda"
}
},
"wizard": {
"wizardMarker": {
"completed": "Completado",
"inProgress": "En progreso",
"pending": "Pendiente"
},
"wizardWrapper": {
"back": "Atrás",
"previous": "Anterior",
"end": "Finalizar",
"next": "Siguiente",
"step": "Paso {{index}}"
}
},
"datePicker": {
"quickActions": {
"title": "Selección Rápida",
"lastHour": "Última hora",
"last3Hours": "Últimas 3 horas",
"last6Hours": "Últimas 6 horas",
"last12Hours": "Últimas 12 horas",
"last24Hours": "Últimas 24 horas",
"last7Days": "Últimos 7 días",
"last30Days": "Últimos 30 días",
"last6Months": "Últimos 6 meses",
"lastYear": "Último año",
"clear": "Limpiar"
},
"labels": {
"from": "Desde",
"to": "Hasta"
}
}
}
}Portuguese (/assets/i18n/pt.json):
{
"sdk": {
"fileUpload": {
"dropzone": {
"selectPrompt": "Haz clic para seleccionar archivos o arrastra y suelta aquí",
"maxSizeLabel": "Tamaño máximo:",
"allowedTypesLabel": "Tipos permitidos:",
"invalidFileType": "Tipo de archivo no válido",
"fileTooLarge": "Archivo demasiado grande",
"fileCounter": "{{current}} de {{max}} archivos",
"maxFilesExceeded": "Máximo de {{max}} archivo(s) permitido",
"noValidFiles": "No se han seleccionado archivos válidos",
"fileActions": {
"play": "Reproducir audio",
"pause": "Pausar audio",
"download": "Descargar archivo",
"remove": "Quitar archivo"
}
},
"fileUploader": {
"allowedTypesLabel": "Tipos permitidos:"
}
},
"audioPlayer": {
"trackListTitle": "Trilhas",
"trackInfo": {
"title": "Sem título",
"description": "Sem descrição"
}
},
"countdown": {
"days": "Dias",
"hours": "Horas",
"minutes": "Minutos",
"seconds": "Segundos"
},
"dualList": {
"searchPlaceHolder": "Pesquisar"
},
"combobox": {
"searchPlaceholder": "Pesquisar",
"placeholder": "Selecione",
"noResultsFound": "Nenhum resultado encontrado",
"clearAll": "Limpar Tudo",
"clearSelection": "Limpar seleção",
"searchOptions": "Pesquisar opções",
"loading": "Carregando..."
},
"select": {
"placeholder": "Selecione"
},
"formErrors": {
"validation": {
"required": "*Este campo é obrigatório",
"email": "*Por favor, insira um endereço de e-mail válido",
"minLength": "*Deve ter pelo menos {{min}} caracteres",
"maxLength": "*Deve ter no máximo {{max}} caracteres",
"min": "*O valor deve ser de pelo menos {{min}}",
"max": "*O valor deve ser de no máximo {{max}}",
"pattern": "*Formato inválido",
"unhandledError": "*Erro não tratado"
}
},
"textarea": {
"maxCharacters": "{{current}} de {{max}} caracteres"
},
"table": {
"entriesByPage": "Entradas por página",
"perPage": "{{count}} / página",
"totalEntries": "{{total}} entradas",
"entriesRange": "{{range}} de {{total}} entradas",
"emptyListMessage": "Nenhum item para mostrar"
},
"dataTable": {
"entriesByPage": "Entradas por página",
"perPage": "{{count}} / página",
"totalEntries": "{{total}} entradas",
"entriesRange": "{{range}} de {{total}} entradas",
"emptyListMessage": "Nenhum item para mostrar",
"noData": {
"title": "Nenhum dado disponível",
"message": "Não há registros para exibir neste momento."
},
"noResults": {
"title": "Nenhum resultado encontrado",
"message": "Tente ajustar seus critérios de pesquisa"
}
},
"wizard": {
"wizardMarker": {
"completed": "Concluído",
"inProgress": "Em andamento",
"pending": "Pendente"
},
"wizardWrapper": {
"back": "Voltar",
"previous": "Anterior",
"end": "Fim",
"next": "Próximo",
"step": "Etapa {{index}}"
}
},
"datePicker": {
"quickActions": {
"title": "Seleção Rápida",
"lastHour": "Última hora",
"last3Hours": "Últimas 3 horas",
"last6Hours": "Últimas 6 horas",
"last12Hours": "Últimas 12 horas",
"last24Hours": "Últimas 24 horas",
"last7Days": "Últimos 7 dias",
"last30Days": "Últimos 30 dias",
"last6Months": "Últimos 6 meses",
"lastYear": "Último ano",
"clear": "Limpar"
},
"labels": {
"from": "De",
"to": "Até"
}
}
}
}3. Access Language Functionality in Components
The TranslationService automatically loads available languages from translation.json:
// In any component
import { TranslationService } from '@sixbell-telco/sdk/utils/translation';
import { inject } from '@angular/core';
export class MyComponent {
private translationService = inject(TranslationService);
availableLanguages = this.translationService.getAvailableLanguagesConfig();
currentLanguage = this.translationService.currentLanguage;
setLanguage(languageCode: string) {
this.translationService.setLanguage(languageCode);
}
getLanguageName(languageCode: string): string {
return this.translationService.getLocalizedLanguageName(languageCode);
}
}4. Use Translations in Templates
Translations are automatically loaded and available through the translate pipe:
<!-- Access any translation key -->
<h1>{{ 'ui.showcase' | translate }}</h1>
<!-- With parameters -->
<p>{{ 'welcome.message' | translate: { name: userName } }}</p>
<!-- In component -->
<select (change)="setLanguage($event.target.value)">
@for (lang of availableLanguages; track lang.code) {
<option [value]="lang.code">{{ getLanguageName(lang.code) }}</option>
}
</select>5. Add Custom Application Translations
Extend the translation files with your application-specific keys:
English (/assets/i18n/en.json):
{
"app": {
"title": "My Application",
"welcome": "Welcome to our app",
"features": {
"description": "Powerful features for your business"
}
},
"sdk": {
"formFields": {
"optional": "Optional"
}
}
}Your application is now configured with full multi-language support. Language preferences are automatically persisted and restored on return visits.
Usage Examples
Using Components
Here's a basic example of using a button component:
// app.component.ts
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
@Component({
imports: [ButtonComponent],
template: `
<div>
<h1>Awesome button</h1>
<st-button variant="primary">Click me!</st-button>
</div>
`
})Using Text Directive & Component
The Text Directive (stText) applies preset-based typography styles directly on any HTML element. The Text Component (st-text) is a polymorphic wrapper that renders the correct semantic HTML tag.
Both support class overrides — any Tailwind class you put in class="" will override the corresponding directive/component default via tailwind-merge.
Text Directive
import { TextDirective } from '@sixbell-telco/sdk/directives/text';
@Component({
imports: [TextDirective],
template: `
<!-- Basic presets (1 = largest heading, 10 = smallest body) -->
<h1 stText="1">Hero Title</h1>
<p stText="7">Body text (default preset)</p>
<span stText="9">Small label</span>
<!-- Override weight and color via inputs -->
<p stText="7" stTextWeight="bold" stTextColor="primary">Bold primary text</p>
<!-- Override any prop via class (wins over defaults) -->
<p stText="7" class="text-error font-semibold">Error text with semibold weight</p>
<p stText="7" class="text-3xl leading-loose">Larger size and loose line-height</p>
<!-- Line clamp -->
<p stText="7" stTextLineClamp="2">Long text clamped to 2 lines...</p>
`
})Available inputs:
| Input | Default | Options |
| ----------------- | ----------- | ----------------------------------------------------------------------------------- |
| stText | '7' | '1'–'10' (preset scale) |
| stTextWeight | 'normal' | 'bold', 'semibold', 'medium', 'normal', 'light', 'inherit' |
| stTextColor | 'inherit' | 'base', 'base-placeholder', 'primary', 'secondary', 'accent', 'inherit' |
| stTextOverflow | 'clip' | 'clip', 'ellipsis', 'truncate' |
| stTextLineClamp | undefined | '1'–'6', 'none' |
Class overrides: Any Tailwind utility in class="" overrides the corresponding directive default (color, weight, font-size, line-height, tracking, etc.) via tailwind-merge.
Text Component
import { TextComponent } from '@sixbell-telco/sdk/components/text';
@Component({
imports: [TextComponent],
template: `
<!-- Semantic tags with presets -->
<st-text as="h1" preset="1">Hero Title</st-text>
<st-text as="h2" preset="2">Page Title</st-text>
<st-text as="p" preset="7">Body paragraph</st-text>
<st-text as="span" preset="9" color="primary">Inline label</st-text>
<!-- Override via inputs -->
<st-text as="p" preset="7" weight="bold" color="accent">Bold accent text</st-text>
<!-- Override via class (wins over defaults) -->
<st-text as="p" preset="7" class="text-error font-medium">Custom error text</st-text>
<st-text as="h1" preset="1" class="mb-4 border-b border-accent pb-2">Heading with border</st-text>
`
})Available inputs:
| Input | Default | Options |
| ----------- | ----------- | ----------------------------------------------------------------------------------- |
| as | 'p' | 'h1'–'h6', 'p', 'span', 'div', 'label' |
| preset | '7' | '1'–'10' |
| weight | undefined | 'bold', 'semibold', 'medium', 'normal', 'light', 'inherit' |
| color | 'base' | 'base', 'base-placeholder', 'primary', 'secondary', 'accent', 'inherit' |
| overflow | 'clip' | 'clip', 'ellipsis', 'truncate' |
| lineClamp | undefined | '1'–'6', 'none' |
Using Services in Components
// component.ts
import { ThemeService } from '@sixbell-telco/sdk/utils/theme';
import { TranslationService } from '@sixbell-telco/sdk/utils/translation';
@Component({
template: `
<div [class]="themeService.finalTheme()">
<h1>{{ 'welcome.title' | translate }}</h1>
<p>Current theme: {{ themeService.selectedTheme() }}</p>
<p>Dark mode: {{ themeService.isDarkTheme() ? 'Yes' : 'No' }}</p>
<!-- Theme selector -->
<select (change)="changeTheme($event.target.value)">
@for (theme of themeService.getAvailableThemes(); track theme) {
<option [value]="theme">{{ theme }}</option>
}
</select>
<!-- Language selector -->
<select (change)="changeLanguage($event.target.value)">
@for (lang of translationService.getAvailableLanguagesConfig(); track lang.code) {
<option [value]="lang.code">{{ translationService.getLocalizedLanguageName(lang.code) }}</option>
}
</select>
</div>
`,
})
export class MyComponent {
themeService = inject(ThemeService);
translationService = inject(TranslationService);
changeTheme(theme: string) {
this.themeService.setTheme(theme);
}
changeLanguage(language: string) {
this.translationService.setLanguage(language);
}
}Quick Start
Using Components
import { Component } from '@angular/core';
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
import {
CardActionsComponent,
CardBodyComponent,
CardComponent,
CardFooterComponent,
CardHeaderComponent,
CardSectionBodyComponent,
CardSectionComponent,
CardSectionGroupComponent,
CardSubtitleComponent,
CardTitleComponent,
} from '@sixbell-telco/sdk/components/card';
@Component({
selector: 'app-example',
standalone: true,
imports: [
ButtonComponent,
CardActionsComponent,
CardBodyComponent,
CardComponent,
CardFooterComponent,
CardHeaderComponent,
CardSectionBodyComponent,
CardSectionComponent,
CardSectionGroupComponent,
CardSubtitleComponent,
CardTitleComponent,
],
template: `
<st-card>
<st-card-header>
<st-card-title>Hello World</st-card-title>
<st-card-subtitle>Small supporting text</st-card-subtitle>
</st-card-header>
<st-card-body>
<st-card-section-group [divided]="true">
<st-card-section>
<st-card-section-body>Welcome to the new compound card API.</st-card-section-body>
</st-card-section>
</st-card-section-group>
</st-card-body>
<st-card-footer [divided]="true">
<st-card-actions>
<st-button variant="primary">Click me!</st-button>
</st-card-actions>
</st-card-footer>
</st-card>
`,
})
export class ExampleComponent {}Using Form Components
import { Component, signal } from '@angular/core';
import { ReactiveFormsModule, FormControl, FormGroup } from '@angular/forms';
import { InputComponent } from '@sixbell-telco/sdk/components/forms/input';
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
@Component({
selector: 'app-form-example',
standalone: true,
imports: [InputComponent, ButtonComponent, ReactiveFormsModule],
template: `
<form [formGroup]="form">
<st-input formControlName="email" placeholder="Enter email"></st-input>
<st-button (click)="submit()">Submit</st-button>
</form>
`,
})
export class FormExampleComponent {
form = new FormGroup({
email: new FormControl(''),
});
submit() {
console.log(this.form.value);
}
}Using Services
import { Component, inject, effect } from '@angular/core';
import { ThemeService } from '@sixbell-telco/sdk/utils/theme';
import { TranslationService } from '@sixbell-telco/sdk/utils/translation';
import { LoggerService } from '@sixbell-telco/sdk/utils/logger';
@Component({
selector: 'app-services-example',
template: `
<div>
<p>Current Theme: {{ themeService.selectedTheme() }}</p>
<p>Dark Mode: {{ themeService.isDarkTheme() ? 'Yes' : 'No' }}</p>
<button (click)="toggleTheme()">Toggle Theme</button>
</div>
`,
})
export class ServicesExampleComponent {
themeService = inject(ThemeService);
translationService = inject(TranslationService);
logger = inject(LoggerService);
constructor() {
effect(() => {
this.logger.info(`Theme changed to: ${this.themeService.selectedTheme()}`);
});
}
toggleTheme() {
const newScheme = this.themeService.isDarkTheme() ? 'light' : 'dark';
this.themeService.setScheme(newScheme);
}
}Configuration
Theme Configuration
Runtime themes are loaded from themes.json. See Theme Service README for detailed configuration.
Translation Configuration
Runtime translations are loaded from translation.json. See Translation Service README for detailed configuration.
Logger Configuration
Configure logging levels and output. See Logger Service README for detailed options.
Architecture
Multi-Entry Point Imports
Always use specific imports for optimal tree-shaking:
// ✅ Correct - specific imports
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
import { ThemeService } from '@sixbell-telco/sdk/utils/theme';
// ❌ Avoid - barrel exports
import { ButtonComponent } from '@sixbell-telco/sdk';Component Prefix
All SDK components use the st- prefix:
<st-button>Button</st-button>
<st-input type="text" />
<st-card>Card Content</st-card>
<st-divider>OR</st-divider>Signal-Based API
Components use Angular 19 signals for reactive inputs:
variant = input<'primary' | 'secondary'>();
isDisabled = input(false);
@Component({
template: `<button [disabled]="isDisabled()">{{ variant() }}</button>`,
})Testing
The SDK includes Jest configuration. Run tests with:
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportSee .agents/skills/angular-testing-jest-ngmocks/SKILL.md for Angular 19 testing patterns.
Documentation
- Component Stories: View component documentation in Storybook
- Live Examples: Showcase app at
projects/showcase/ - API Documentation: JSDoc comments in component files
- Changelog: See CHANGELOG.md
Agent skills release scope
The SDK skill at .agents/skills/sixbell-sdk/ follows the same lifecycle as projects/sdk/CHANGELOG.md:
- During active development, the skill metadata must point to
Unreleased. - When a release is created, update the skill metadata to the released version or tag.
- When development starts again, switch the skill metadata back to
Unreleased.
This keeps the skill aligned with the current codebase instead of the last published package.
Contributing
When creating new components:
- Use
st-prefix for selectors - Follow CVA pattern for styling variants
- Use signal-based inputs
- Add comprehensive JSDoc comments
- Create Storybook stories
- Add unit tests with Jest
- Update CHANGELOG.md
Resources
- Angular Documentation
- Tailwind CSS
- DaisyUI
- class-variance-authority
- ng-icons
- Angular CDK
License
MIT - See LICENSE file for details
Support
For issues, questions, or feature requests, please visit the project repository or contact the Sixbell Telco team.
Last updated: March 19, 2026
