shirkasoft-ui-components
v1.3.2
Published
Angular UI Components Library — themeable via CSS custom properties
Maintainers
Readme
Shirkasoft UI Components
Angular 21+ UI components library built with Tailwind CSS, standalone components, Angular signals, and light/dark theme support via CSS custom properties.
Installation
pnpm add shirkasoft-ui-components
# or
npm install shirkasoft-ui-componentsPeer Dependencies
| Package | Minimum version | | ------------------ | --------------- | | @angular/common | ^21.2.0 | | @angular/core | ^21.2.0 | | @angular/forms | ^21.2.0 | | @angular/router | ^21.2.0 | | rxjs | ^7.8.0 | | @jsverse/transloco | >= 8.0.0 | | @lucide/angular | ^1.17.0 | | chart.js | ^4.4.0 |
Theme Setup
Import the theme in your global styles.css:
@import '@shirkasoft/ui-components/theme.css';The theme is based on --shk-* CSS variables. To customize colors, override the variables in :root (light mode) and .dark (dark mode):
:root {
--shk-primary-500: #3b82f6;
--shk-primary-600: #2563eb;
/* ... */
}Dark mode
Add the dark class to the <html> element to enable dark mode.
document.documentElement.classList.toggle('dark');Components
| # | Component | Selector | ControlValueAccessor | Associated service |
| --- | ------------- | -------------------- | -------------------- | ---------------------- |
| 1 | TextField | shk-text-field | ✅ | — |
| 2 | Toggle | shk-toggle | ✅ | — |
| 3 | Select | shk-select | ✅ | — |
| 4 | DatePicker | shk-date-picker | ✅ | — |
| 5 | PriceInput | shk-price-input | — | — |
| 6 | FileUpload | shk-file-upload | — | — |
| 7 | Table | shk-table | — | — |
| 8 | Modal | shk-modal | — | ModalService |
| 9 | Notification | shk-notification | — | NotificationService |
| 10 | ConfirmDialog | shk-confirm-dialog | — | ConfirmDialogService |
| 11 | Chart | shk-chart | — | — |
| 12 | Tooltip | shk-tooltip | — | — |
| 13 | Sidebar | shk-sidebar | — | — |
| 14 | Rail Sidebar | shk-rail-sidebar | — | — |
| 15 | Skeleton | shk-skeleton | — | — |
| 16 | Tag | shk-tag | — | — |
| 17 | Button | shk-button | — | — |
| 18 | Knob | shk-knob | ✅ | — |
| 19 | Loading | shk-loading-* | — | — |
| 20 | Tabs | shk-tabs | — | — |
TextField (shk-text-field)
Text input with support for input, textarea, password, search, credit card formatting, and error validation. Implements ControlValueAccessor.
<shk-text-field
[label]="'Name'"
[control]="myControl"
[type]="'text'"
[placeholder]="'Enter your name'"
[disabled]="false"
[errorMessages]="{ required: 'This field is required' }"
[isTextArea]="false"
[searchMode]="false"
(searchButtonClick)="onSearch()"
/>| Input | Type | Default |
| ------------------- | --------------------------- | ---------------- |
| id | string | '' |
| label | string | '' |
| type | string | 'text' |
| placeholder | string | '' |
| disabled | boolean | false |
| control | FormControl | undefined |
| formGroup | FormGroup | undefined |
| errorMessage | string | '' |
| errorMessages | { [key: string]: string } | {} |
| isTextArea | boolean | false |
| textAreaHeight | string | 'h-28' |
| searchMode | boolean | false |
| searchButtonClick | output<void> | — |
| numbersOnly | boolean | false |
| allowDecimals | boolean | false |
| maxLength | number | undefined |
| formatCard | boolean | false |
| preventNegative | boolean | false |
| displayValue | string | '' |
| autocomplete | string | 'new-password' |
Also exposes
inputElementandtextareaElementasviewChild.
Toggle (shk-toggle)
Switch or checkbox. Implements ControlValueAccessor.
<shk-toggle
[label]="'Enable notifications'"
[mode]="'toggle'"
[(checked)]="isChecked"
(checkedChange)="onChange($event)"
/>| Input | Type | Default |
| --------- | ------------------------ | ----------- |
| label | string | '' |
| mode | 'toggle' \| 'checkbox' | 'toggle' |
| checked | boolean | undefined |
| Output | Type |
| --------------- | --------- |
| checkedChange | boolean |
Select (shk-select)
Searchable dropdown with pagination, multiple selection, custom options, and validation. Implements ControlValueAccessor.
<shk-select
[options]="options"
[label]="'Country'"
[control]="countryControl"
[multiple]="false"
[isSearchable]="true"
[usePagination]="true"
[itemsPerPage]="10"
[allowCustomEntries]="false"
[placeholder]="'Select...'"
[isLoading]="false"
(selectionChange)="onSelect($event)"
(search)="onSearch($event)"
/>interface SelectOption {
label: string;
value: any;
custom?: boolean;
}| Input | Type | Default |
| ---------------------- | --------------------------- | ---------------------------------------- |
| options | SelectOption[] | [] |
| label | string | undefined |
| placeholder | string | 'Select...' |
| disabled | boolean | false |
| control | FormControl | undefined |
| formGroup | FormGroup | undefined |
| multiple | boolean | false |
| isSearchable | boolean | false |
| usePagination | boolean | false |
| itemsPerPage | number | 10 |
| allowCustomEntries | boolean | false |
| isLoading | boolean | false |
| isAllDataLoaded | boolean | false |
| preserveSearchOnLoad | boolean | false |
| dropdownUpward | boolean | false |
| showEmptyOption | boolean | true |
| emptyMessageKey | string | 'common.no_records' |
| validCombinations | string[][] | undefined |
| errorMessages | { [key: string]: string } | { required: 'This field is required' } |
| Output | Type |
| ----------------- | -------- |
| selectionChange | any |
| search | string |
DatePicker (shk-date-picker)
Date, month, or year picker. Implements ControlValueAccessor. Supports 'date', 'month', 'year' views.
<shk-date-picker
[label]="'Start date'"
[control]="dateControl"
[view]="'date'"
[locale]="'es'"
[minValue]="'2024-01-01'"
[maxValue]="'2026-12-31'"
/>| Input | Type | Default |
| --------------- | ----------------------------- | ----------- |
| id | string | '' |
| label | string | '' |
| placeholder | string | '' |
| view | 'date' \| 'month' \| 'year' | 'date' |
| control | FormControl | undefined |
| formGroup | FormGroup | undefined |
| disabled | boolean | false |
| errorMessage | string | '' |
| errorMessages | { [key: string]: string } | {} |
| minValue | string | '' |
| maxValue | string | '' |
| locale | string | 'es' |
Return value depends on view:
'date'→'YYYY-MM-DD''month'→'YYYY-MM''year'→'YYYY'
PriceInput (shk-price-input)
Numeric input with toggle between amount ($) and percentage (%). Uses Angular model() for two-way binding.
<shk-price-input
[(value)]="price"
[(isPercentage)]="isPercent"
[label]="'Price'"
[min]="0"
[max]="10000"
[required]="true"
[showError]="hasError"
[errorMessage]="'Invalid value'"
/>| Input | Type | Default |
| -------------- | ---------------- | ------------ |
| value | model<number> | required |
| isPercentage | model<boolean> | false |
| label | string | '' |
| inputId | string | '' |
| required | boolean | false |
| disabled | boolean | false |
| min | number | 0 |
| max | number | Infinity |
| showError | boolean | false |
| errorMessage | string | '' |
FileUpload (shk-file-upload)
File upload with drag & drop, image preview, file type and size validation.
<shk-file-upload
[label]="'Upload your photo'"
[accept]="'image/*'"
[maxFileSize]="5 * 1024 * 1024"
[maxFiles]="3"
[multiple]="true"
(fileSelected)="onFilesSelected($event)"
(fileRemoved)="onFileRemoved()"
(fileError)="onFileError($event)"
/>interface FileUploadError {
type: 'size' | 'type';
message: string;
file: File;
}| Input | Type | Default |
| -------------------- | --------- | ----------------------- |
| label | string | 'Upload file' |
| accept | string | 'image/*' |
| maxFileSize | number | 2 * 1024 * 1024 (2MB) |
| maxFiles | number | 0 (unlimited) |
| multiple | boolean | false |
| fileUploadText | string | 'Select file' |
| changeFilesText | string | 'Change files' |
| fileRecommendation | string | '' |
| Output | Type |
| -------------- | ----------------- |
| fileSelected | File[] |
| fileRemoved | void |
| fileError | FileUploadError |
| fileWarning | FileUploadError |
Table (shk-table)
Data table with sorting, column filters, global search, pagination, row actions, and header actions. Supports client and server-side mode.
<shk-table
[data]="users"
[columns]="columns"
[loading]="isLoading"
[serverSide]="true"
[totalRecords]="totalUsers"
[rowActions]="rowActions"
[headerActions]="headerActions"
[rowsPerPage]="25"
[showSearch]="true"
(pageChange)="onPageChange($event)"
(searchChange)="onSearch($event)"
(refresh)="loadData()"
/>interface Column {
field: string;
header: string;
sortable?: boolean;
filter?: boolean;
filterPlaceholder?: string;
width?: string;
defaultSort?: true;
filterType?: 'text' | 'exact' | 'select';
filterOptions?: { label: string; value: any }[];
template?: 'text' | 'tag';
format?: (row: any) => string;
tagValue?: (row: any) => string;
tagSeverity?: (row: any) => 'success' | 'danger' | 'warn' | 'info' | undefined;
tagStyle?: (row: any) => { [key: string]: string } | undefined;
}
interface TableAction {
label: string;
icon: string;
onClick: () => void;
class?: string;
isVisible?: () => boolean;
isDisabled?: () => boolean;
}
interface RowAction {
label: string | ((data: any) => string);
icon: string | ((data: any) => string);
onClick: (rowData: any) => void;
class?: string | ((data: any) => string);
isVisible?: (rowData: any) => boolean;
isDisabled?: (rowData: any) => boolean;
}
interface PageChangeEvent {
first: number;
rows: number;
page: number;
pageCount: number;
}
interface FilterChangeEvent {
filters: { [key: string]: any };
}| Input | Type | Default |
| -------------------- | ------------------------------------ | -------------- |
| data | any[] | [] |
| columns | Column[] | [] |
| rowsPerPage | number | 10 |
| rowsPerPageOptions | number[] | [10, 25, 50] |
| loading | boolean | false |
| showActionRow | boolean | true |
| headerActions | TableAction[] | [] |
| rowActions | RowAction[] | [] |
| hasShadow | boolean | true |
| defaultSortField | string | '' |
| defaultSortOrder | number | 1 |
| showSearch | boolean | true |
| searchPlaceholder | string | '' |
| emptyMessage | string | '' |
| serverSide | boolean | false |
| totalRecords | number | 0 |
| filters | { label: string; value: string }[] | [] |
| activeFilter | string | '' |
| customTemplates | { [key: string]: any } | {} |
| Output | Type |
| -------------- | ------------------- |
| pageChange | PageChangeEvent |
| filterChange | FilterChangeEvent |
| searchChange | string |
| filterClick | string |
| refresh | void |
Modal (shk-modal)
Dynamic modal that loads components via ModalService. Supports forms, fullscreen expansion, and accept/cancel buttons.
<shk-modal />interface ModalConfig {
title: string;
component: Type<any>;
data?: Record<string, any>;
width?: string;
showButtons?: boolean;
showExpandButton?: boolean;
acceptLabel?: string;
cancelLabel?: string;
onClose?: () => void;
}The injected component must expose:
form?: FormGroup(optional, for validation)onSubmit()→ callssubmitSuccessorsubmitErrorsubmitSuccess?: EventEmitter<void>(optional)submitError?: EventEmitter<void>(optional)handleCancel?: () => void(optional)
constructor(private modalSrv: ModalService) {}
openModal() {
this.modalSrv.open({
title: 'Edit user',
component: EditUserComponent,
data: { userId: 123 },
width: '600px',
showButtons: true,
showExpandButton: true,
});
}
closeModal() {
this.modalSrv.close();
}Service methods:
| Method | Description |
| --------------------------- | ---------------------------------- |
| open(config: ModalConfig) | Opens a new modal |
| close() | Closes the current modal |
| accept() | Closes the modal (alias for close) |
| clear() | Closes all modals |
Notification (shk-notification)
Toast notification system with configurable positions and optional progress bar.
<shk-notification position="right-top" />interface Notification {
id: string;
message: string;
type: 'success' | 'error' | 'warning' | 'info';
progress?: number;
showProgress?: boolean;
}constructor(private notifSrv: NotificationService) {}
showNotif() {
const id = this.notifSrv.addNotification(
'Operation successful',
'success',
false, // showProgress
3000 // duration (ms)
);
}
updateProgress(id: string, progress: number) {
this.notifSrv.updateProgress(id, progress);
}| Input | Type | Default |
| ---------- | ------------------------------------------- | -------------- |
| position | 'center-top' \| 'right-top' \| 'left-top' | 'center-top' |
Service methods:
| Method | Description |
| ------------------------------------------------------- | ------------------------- |
| addNotification(msg, type?, showProgress?, duration?) | Adds a notification |
| updateProgress(id, progress) | Updates the progress bar |
| removeNotification(notification) | Removes a notification |
| removeNotificationById(id) | Removes by ID |
| clearAll() | Removes all notifications |
ConfirmDialog (shk-confirm-dialog)
Programmatic confirmation dialog. Used via ConfirmDialogService which dynamically creates it.
<!-- No need to add it to the template -->interface ConfirmConfig {
title?: string;
message: string;
confirmLabel?: string;
cancelLabel?: string;
loadingText?: string;
type?: 'danger' | 'info' | 'warning';
showCancel?: boolean;
loading?: boolean;
}constructor(private confirmSrv: ConfirmDialogService) {}
async deleteItem() {
const confirmed = await this.confirmSrv.confirm({
title: 'Delete user',
message: 'Are you sure you want to delete this user?',
confirmLabel: 'Delete',
type: 'danger',
});
if (confirmed) {
// proceed with deletion
}
}| Input | Type | Default |
| -------------- | --------------------------------- | ------------------------------------- |
| title | string | 'Confirm action' |
| message | string | 'Are you sure you want to proceed?' |
| confirmLabel | string | 'Confirm' |
| cancelLabel | string | 'Cancel' |
| loadingText | string | 'Processing…' |
| type | 'danger' \| 'info' \| 'warning' | 'danger' |
| loading | boolean | false |
| showCancel | boolean | true |
| Output | Type |
| -------- | ------ |
| closed | void |
Chart (shk-chart)
Chart.js wrapper supporting the main chart types.
<shk-chart [type]="'bar'" [data]="chartData" [options]="chartOptions" />import { COLOR_PALETTE, readThemeColors } from 'shirkasoft-ui-components';
const colors = readThemeColors(); // current theme colors| Input | Type | Default |
| --------- | ------------------------------------------------------- | ------------------ |
| type | 'line' \| 'bar' \| 'pie' \| 'doughnut' \| 'polarArea' | 'bar' |
| data | any | { datasets: [] } |
| options | any | {} |
Exported utils:
| Export | Description |
| ------------------- | ---------------------------------------------- |
| COLOR_PALETTE | Predefined color array (bg + border) |
| readThemeColors() | Reads current theme colors (--shk-surface-*) |
Tooltip (shk-tooltip)
Tooltip with two modes: hover (CSS positioning) and fixed (JS-calculated positioning).
<shk-tooltip [text]="'Additional info'" [position]="'bottom'" [mode]="'hover'">
<button>Hover me</button>
</shk-tooltip>| Input | Type | Default |
| ---------- | ------------------------------ | ---------- |
| text | string | '' |
| position | 'top' \| 'bottom' \| 'right' | 'bottom' |
| mode | 'hover' \| 'fixed' | 'hover' |
| offset | number | 8 |
Sidebar (shk-sidebar)
Brand/logo header, round collapse toggle with tooltip, expandable groups with nested children, standalone items, Lucide icons, badges, separators, disabled items, active-item highlighting (primary-600 with white text), projected header/content/footer slots, tooltips on collapsed items, and a responsive off-canvas mode below 1024px. Uses the same theme tokens (--shk-*) as the rest of the library.
<shk-sidebar
[items]="items"
[(collapsed)]="collapsed"
[activeItemId]="activeId"
title="WS Admin"
subtitle="Panel de control"
logo="/logo.svg"
[defaultExpanded]="['products']"
(itemClick)="onItemClick($event)"
>
<div sidebarFooter>
<button (click)="logout()">Cerrar sesión</button>
</div>
</shk-sidebar>interface SidebarItem {
id: string;
label: string;
icon?: string; // Lucide icon name
badge?: string | number;
disabled?: boolean;
separator?: boolean; // renders a divider before the item
route?: string; // navigation target (optional)
roles?: string[]; // when `userRole` is set, items are filtered
children?: SidebarItem[]; // renders an expandable group with nested items
}
interface SidebarGroup {
id?: string;
label?: string;
items: SidebarItem[];
}| Input | Type | Default |
| ----------------- | --------------------------- | --------------------------------------------- |
| items | SidebarItem[] | [] |
| groups | SidebarGroup[] | [] (when set, overrides items) |
| title | string | '' |
| subtitle | string | '' |
| logo | string | '' (image URL shown in the brand header) |
| collapsed | model<boolean> | false |
| mobileOpen | model<boolean> | false (off-canvas state on mobile) |
| collapsible | boolean | true |
| showToggle | boolean | true |
| responsive | boolean | true (off-canvas below 1024px) |
| fixed | boolean | false (uses position: fixed + h-screen) |
| hasShadow | boolean | true |
| expandedWidth | string | '16rem' |
| collapsedWidth | string | '5rem' |
| activeItemId | string | '' |
| userRole | string | '' (filters items by roles) |
| defaultExpanded | string[] | [] (groups expanded on init) |
| labelPipe | (label: string) => string | identity (e.g. pass a Transloco translate fn) |
| expandTooltip | string | 'Expandir' |
| collapseTooltip | string | 'Colapsar' |
| Output | Type |
| ----------- | ------------- |
| itemClick | SidebarItem |
| toggle | boolean |
Projected slots:
| Slot | Description |
| ---------------- | --------------------------------------------------- |
| sidebarHeader | Brand/logo area (rendered before the title) |
| sidebarContent | Custom content between the header and the nav |
| sidebarFooter | Footer area (e.g. logout button); hidden when empty |
Icons are resolved by name via a Lucide map with a
Menufallback. Use[fixed]="true"for a full-screen sidebar (as in ws-admin) or leave it embedded in a container for previews.
Rail Sidebar (shk-rail-sidebar)
Sidebar estilo rail + nav, una columna vertical de iconos (una por sección) y un panel con el menú de la sección activa, con subgrupos expandibles, toggle de idioma, botón de logout y colapso.
<shk-rail-sidebar
[sections]="sections"
[activeUrl]="router.url"
[(activeSectionKey)]="activeSectionKey"
[(collapsed)]="collapsed"
[activeLang]="transloco.getActiveLang()"
appName="WAC Bookings"
logo="/logo.png"
(sectionSelected)="onSectionSelected($event)"
(itemClick)="onItemClick($event)"
(setLang)="onSetLanguage($event)"
(logoutRequested)="requestLogout()"
/>interface RailSidebarSection {
key: string;
label: string;
icon?: string; // nombre del icono Lucide
routerLink?: string; // ruta de la sección (selección automática por URL)
matchPrefixes?: string[]; // prefijos de URL que activan la sección
items?: RailSidebarItem[]; // menú del panel cuando la sección está activa
}
interface RailSidebarItem {
id: string;
label: string;
icon?: string;
routerLink?: string;
children?: RailSidebarItem[]; // subgrupo expandible
roles?: string[]; // filtro por rol (userRole)
badge?: string | number;
disabled?: boolean;
separator?: boolean;
}| Input | Type | Default |
| ------------------ | ---------------------- | --------------------------------------------------------- |
| sections | RailSidebarSection[] | [] |
| activeUrl | string | '' — activa la sección por matchPrefixes/routerLink |
| activeSectionKey | string (model) | '' |
| collapsed | boolean (model) | false |
| activeLang | string | 'es' |
| langs | string[] | ['es', 'en'] — idiomas al togglear |
| logo | string | '' — imagen del workspace |
| appName | string | '' — título del panel |
| userRole | string | '' — filtra items.roles |
| labelPipe | (label) => string | identidad (para transloco) |
| railWidth | string | '4.5rem' |
| expandedWidth | string | '16.5rem' |
| Output | Type |
| ----------------- | ---------------------- |
| sectionSelected | { key, routerLink? } |
| itemClick | RailSidebarItem |
| setLang | string |
| logoutRequested | void |
| toggleSidebar | void |
| workspaceClick | void |
Skeleton (shk-skeleton)
Placeholder block shown while content is loading. Rectangular or circular, with wave, pulse or no animation.
<shk-skeleton height="1.5rem" width="60%" />
<shk-skeleton shape="circle" size="5rem" />
<shk-skeleton width="16rem" height="5rem" borderRadius="12px" animation="pulse" />| Input | Type | Default |
| -------------- | ----------------------------- | ------------ |
| shape | 'rectangle' \| 'circle' | 'rectangle'|
| size | string | '' (circle)|
| width | string | '100%' |
| height | string | '1rem' |
| borderRadius | string | '0.25rem' |
| animation | 'wave' \| 'pulse' \| 'none' | 'wave' |
| style | object | {} |
| styleClass | string | '' |
Tag (shk-tag)
Small label used to highlight a value, status or category.
<shk-tag value="Activo" severity="success" [rounded]="true" />
<shk-tag value="Precio" severity="contrast" icon="dollar-sign" />type TagSeverity = 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'contrast';| Input | Type | Default |
| ------------ | -------------- | ------------- |
| value | string \| number | '' |
| severity | TagSeverity | 'secondary' |
| rounded | boolean | false |
| icon | string | '' (lucide) |
| style | object | {} |
| styleClass | string | '' |
Button (shk-button)
Generic button with severity variants, lucide icons, sizes and a loading state. Icons are resolved by name from a built-in lucide registry (plus, pencil, trash-2, search, arrow-right, …).
<shk-button label="Añadir" icon="plus" (onClick)="create()" />
<shk-button label="Eliminar" severity="danger" [outlined]="true" />
<shk-button [label]="loading() ? 'Cargando…' : 'Guardar'" [loading]="loading()" />
<shk-button icon="search" severity="secondary" [rounded]="true" ariaLabel="Buscar" />type ButtonSeverity = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'help' | 'contrast';
type ButtonSize = 'small' | 'large';
type ButtonIconPosition = 'left' | 'right' | 'top' | 'bottom';| Input | Type | Default |
| ------------- | ------------------ | ------------ |
| label | string | '' |
| icon | string | '' (lucide)|
| iconPos | ButtonIconPosition| 'left' |
| severity | ButtonSeverity | 'primary' |
| outlined | boolean | false |
| text | boolean | false |
| raised | boolean | false |
| rounded | boolean | false |
| size | ButtonSize | undefined |
| disabled | boolean | false |
| loading | boolean | false |
| loadingIcon | string | 'refresh' |
| loadingText | string | '' |
| type | 'button' \| 'submit' \| 'reset' | 'button' |
| ariaLabel | string | '' |
| title | string | '' |
| style | object | {} |
| styleClass | string | '' |
| Output | Type |
| -------- | ------------ |
| onClick| MouseEvent |
Knob (shk-knob)
SVG radial knob with pointer drag and keyboard (arrow keys) support. Implements ControlValueAccessor (reactive forms / [(ngModel)]), and also offers [(value)].
<shk-knob [formControl]="scoreCtrl" [min]="0" [max]="10" [step]="0.5" [size]="120" />
<shk-knob [(value)]="value" valueColor="#3b82f6" rangeColor="#e2e8f0" />| Input | Type | Default |
| ------------- | --------- | -------------------------------- |
| value | model<number> | 0 |
| min | number | 0 |
| max | number | 100 |
| step | number | 1 |
| size | number | 100 (px) |
| strokeWidth | number | 14 |
| valueColor | string | var(--shk-primary-600) |
| rangeColor | string | var(--shk-surface-200) |
| showValue | boolean | true |
| disabled | boolean | false |
| readonly | boolean | false |
| ariaLabel | string | '' |
| Output | Type |
| -------- | -------- |
| change | number |
| end | number |
Loading (shk-loading-*)
A set of reusable loading indicators: spinner, indeterminate/determinate bar, dots and overlay.
<shk-loading-spinner [size]="'2.5rem'" [thickness]="4" label="Cargando…" />
<shk-loading-bar [indeterminate]="true" height="0.5rem" />
<shk-loading-bar [indeterminate]="false" [progress]="progress()" />
<shk-loading-dots [count]="5" label="Enviando…" />
<shk-loading-overlay [visible]="loading" message="Cargando datos…" [backdrop]="'light'">
<!-- content to cover -->
</shk-loading-overlay>shk-loading-spinner
| Input | Type | Default |
| ---------- | -------- | ------- |
| size | string | '2rem'|
| thickness| number | 3 |
| color | string | primary token |
| label | string | '' |
shk-loading-bar
| Input | Type | Default |
| -------------- | --------- | ------------ |
| height | string | '0.25rem' |
| indeterminate| boolean | true |
| progress | number | 0 (0-100) |
| color | string | primary token|
| rounded | boolean | true |
shk-loading-dots
| Input | Type | Default |
| ------- | -------- | ------------ |
| size | string | '0.5rem' |
| color | string | primary token|
| count | number | 3 |
| label | string | '' |
shk-loading-overlay
| Input | Type | Default |
| ------------ | ---------------------------------- | -------------- |
| visible | boolean | false |
| message | string | '' |
| spinnerSize| string | '2.5rem' |
| color | string | primary token |
| backdrop | 'transparent' \| 'light' \| 'dark' | 'transparent' |
Tabs (shk-tabs)
Tabbed content with a PrimeNG-like nested API. Values can be numbers or strings; the active value is two-way bindable.
<shk-tabs [value]="activeTab()" (valueChange)="onTabChange($event)" [scrollable]="true">
<shk-tablist [scrollable]="true">
<shk-tab [value]="0">General</shk-tab>
<shk-tab [value]="1">Detalle</shk-tab>
<shk-tab [value]="2" [disabled]="true">Bloqueado</shk-tab>
</shk-tablist>
<shk-tabpanels>
<shk-tabpanel [value]="0">Contenido General</shk-tabpanel>
<shk-tabpanel [value]="1">Contenido Detalle</shk-tabpanel>
</shk-tabpanels>
</shk-tabs>| Selector | Inputs | Outputs |
| ------------- | ------------------------ | --------------- |
| shk-tabs | value (model), scrollable | valueChange |
| shk-tablist | scrollable, styleClass | — |
| shk-tab | value, disabled, styleClass | — |
| shk-tabpanels| — | — |
| shk-tabpanel| value | — |
Panels are rendered lazily: only the active panel exists in the DOM.
Development
# Build the library
pnpm build
# Serve the showcase (demo app)
pnpm serve
# Build the showcase
pnpm build:showcase
# Watch for library changes
pnpm watchVersioning
pnpm version:patch # 1.0.23 → 1.0.24 (creates commit + tag locally)
pnpm version:minor # 1.0.23 → 1.1.0
pnpm version:major # 1.0.23 → 2.0.0Push code (without publishing)
git push origin mainPush code + publish to npm
git push origin main --tagsPublishing
CI/CD via GitHub Actions
| Workflow | Trigger | Description |
| ------------------- | --------------------------------------------- | ------------------------------------------------------------------------- |
| Publish to npm | Manual (workflow_dispatch) or push v* tag | Builds library, publishes to npm, builds showcase, deploys to public repo |
| Deploy Showcase | Push to main/master or manual | Builds library + showcase, deploys to GitHub Pages public repo |
Manual publish (without GitHub Actions)
bash scripts/publish-lib.shLicense
MIT © Shirkasoft
