An Angular 22 component library providing UI components and CSS utilities.
- Angular:
^22.0.0
- Standalone components · Signals · Vitest
- License: MIT
See CHANGELOG.md for full version history.
| ngx-pk-ui version | Angular version | Notes |
|---|
| v3.x | 22 | Fully supported |
| v2.x | 21 | Fully supported |
| v2.x | 20 | Likely works (use --legacy-peer-deps) |
| v2.x | 19 | Mostly works |
| v2.x | 17–18 | Partial support |
| Page | Route | Description |
|---|
| Login | /examples/login | Full-bleed login card with form validation and password strength meter |
| Chat App | /examples/chat | Icon sidenav, conversation list, and message pane |
| Dashboard | /examples/dashboard | pk-sidenav + inline topbar + stat cards + content cards |
npm install ngx-pk-ui
| Component | Type | Description |
|---|
pk-accordion | Component | Collapsible panels with single / multi-open mode |
pk-tabs | Component | Tab container with content projection |
pk-toastr | Component + Service | Toast notifications |
pk-alert | Component + Service | Modal alert / confirm / input dialogs |
pk-modal | Component | Flexible modal overlay with slot components, size and theme |
pk-icon | Component | SVG icon set + Material Symbols mode |
pk-datagrid | Module + Components | Datagrid with sort, filter, resize, pagination, row detail, row selection |
pk-datepicker | Component | Datepicker with PkLocale support, range, and clear action |
pk-progress | Component | Line/circle progress with status variants |
pk-treeview | Module + Components | Hierarchical tree with expand/collapse and selection modes |
pk-select | Component | Single/multi select with optional search |
pk-autocomplete | Component | Local/async autocomplete input |
pk-sidenav | Component | Left/right side navigation — full/icon/auto mode, multi-level, 4 themes, CSS-variable override |
pk-tooltip | Directive | Hover tooltip — 10 color variants, 4 positions |
pk-calendar | Component | Year/Month/Week/Day/Agenda views, drag & drop, TH/EN locale |
pk-file-upload | Component | Drag & drop file upload with browser-native preview (image/PDF/text) |
pk-heatmap | Component | GitHub-style contribution heatmap — 4 color schemes, 17-locale labels, legend |
pk-markdown-viewer | Component | Standalone Markdown renderer — Print/Export toolbar, light/dark theme |
pk-timeline | Component | Vertical/horizontal timeline with icons, images, and active state |
pk-split | Component | Resizable split pane — drag divider to resize panels, horizontal or vertical |
pk-barcode | Component | Inline SVG barcode — Code 128, Code 39, EAN-13, EAN-8; download PNG/SVG |
pk-qrcode | Component | Inline SVG QR code — versions 1–40, EC levels L/M/Q/H, center logo, download PNG/SVG |
pk-code-reader | Component | QR code & barcode scanner — native BarcodeDetector API, camera / upload / paste, overlay, beep, torch, camera switch |
pk-radio-group | Component | Custom-styled radio button group — ControlValueAccessor, vertical/horizontal layout, per-option disabled |
pk-timepicker | Component | Time picker — spinner / number / dropdown input styles, 24H/12H, ControlValueAccessor, format hms/hm/h |
pk-context-menu | Directive + Service | Right-click context menu — [pkContextMenu] directive, 7 themes, vertical/horizontal layout, keyboard navigation, auto-position |
import { PkAccordion, PkAccordionItem } from 'ngx-pk-ui';
@Component({
imports: [PkAccordion, PkAccordionItem],
})
<!-- Single-open (default) -->
<pk-accordion>
<pk-accordion-item label="Section 1">Content goes here.</pk-accordion-item>
<pk-accordion-item label="Section 2" [open]="true">Starts expanded.</pk-accordion-item>
<pk-accordion-item label="Disabled" [disabled]="true">Cannot open.</pk-accordion-item>
</pk-accordion>
<!-- Multi-open -->
<pk-accordion [multi]="true">
<pk-accordion-item label="A">...</pk-accordion-item>
<pk-accordion-item label="B">...</pk-accordion-item>
</pk-accordion>
| Input | Component | Type | Default | Description |
|---|
multi | PkAccordion | boolean | false | Allow multiple panels open simultaneously |
label | PkAccordionItem | string | required | Panel header text |
open | PkAccordionItem | boolean | false | Expand on init |
disabled | PkAccordionItem | boolean | false | Prevent toggling |
import { PkSplit, PkSplitPanel } from 'ngx-pk-ui';
@Component({
imports: [PkSplit, PkSplitPanel],
})
<!-- Horizontal — left / right (default) -->
<pk-split style="height: 400px">
<pk-split-panel><p>Left panel</p></pk-split-panel>
<pk-split-panel><p>Right panel</p></pk-split-panel>
</pk-split>
<!-- Vertical — top / bottom -->
<pk-split direction="vertical" style="height: 400px">
<pk-split-panel><p>Top panel</p></pk-split-panel>
<pk-split-panel><p>Bottom panel</p></pk-split-panel>
</pk-split>
<!-- Custom sizes -->
<pk-split [initialSize]="30" [minSize]="15" [gutterSize]="8" (sizeChange)="onResize($event)">
<pk-split-panel>...</pk-split-panel>
<pk-split-panel>...</pk-split-panel>
</pk-split>
| Input/Output | Type | Default | Description |
|---|
direction | 'horizontal'|'vertical' | 'horizontal' | Layout direction — left/right or top/bottom |
initialSize | number | 50 | Starting size of first panel (%) |
minSize | number | 10 | Minimum size of either panel (%) |
gutterSize | number | 6 | Divider thickness (px) |
(sizeChange) | [number, number] | — | Emits [sizeA%, sizeB%] on every drag |
import { PkTimepicker } from 'ngx-pk-ui';
import type { PkTimeFormat, PkTimeType, PkTimeInputType } from 'ngx-pk-ui';
@Component({
imports: [PkTimepicker, FormsModule],
})
<!-- Basic (24H, hm, inputs) -->
<pk-timepicker [(ngModel)]="time" />
<!-- 12H mode, hms format, number spinner -->
<pk-timepicker [(ngModel)]="time" type="12H" format="hms" inputType="spinner" />
<!-- Dropdown mode, custom height -->
<pk-timepicker [(ngModel)]="time" inputType="dropdown" [customStyle]="{ height: '42px' }" />
| Input/Output | Type | Default | Description |
|---|
format | 'hms'|'hm'|'h' | 'hm' | Fields shown |
type | '24H'|'12H' | '24H' | Clock mode — 12H shows AM/PM toggle |
inputType | 'spinner'|'number'|'dropdown' | 'number' | UI style |
customClass | string | '' | Extra CSS class on container |
customStyle | Record<string,string> | {} | Inline styles (e.g. override height) |
(onTimeChange) | string | — | Emits 24H time string on every change |
Value is always a 24H string — "14:30", "14:30:05", or "14". Default height 35px.
| File | Description |
|---|
pk-grid.css | Responsive 12-column grid |
pk-btn.css | Button variants and groups |
pk-spinner.css | Loading spinners |
pk-badge.css | Badges and dot indicators |
pk-card.css | Card layouts |
pk-table.css | Styled tables with striped, hover, bordered, size and color variants |
pk-toggle.css | Toggle switch replacing <input type="checkbox"> |
pk-form.css | Floating label fields for input, select, textarea — prefix/suffix, counter, group layout |
pk-layout.css | Fixed top navbar + sidebar layout shell — all CSS variables, responsive |
pk-font.css | Thai & Lao Google Fonts helper classes (opt-in, not in pk-ui.css) |
pk-icon-font.css | Material Symbols font classes |
// angular.json
"styles": ["node_modules/ngx-pk-ui/styles/pk-ui.css"]
Or import individually:
@import 'ngx-pk-ui/styles/pk-btn.css';
@import 'ngx-pk-ui/styles/pk-grid.css';
@import 'ngx-pk-ui/styles/pk-table.css';
<!-- Basic -->
<table class="pk-table">
<thead><tr><th>Name</th><th>Status</th></tr></thead>
<tbody><tr><td>Alice</td><td>Active</td></tr></tbody>
</table>
<!-- Striped + hover + colored header -->
<table class="pk-table pk-table-primary pk-table-striped pk-table-hover">...</table>
<!-- Responsive wrapper -->
<div class="pk-table-responsive">
<table class="pk-table pk-table-bordered">...</table>
</div>
| Class | Description |
|---|
pk-table | Base styles (required) |
pk-table-striped | Alternating row background |
pk-table-hover | Row highlight on hover |
pk-table-bordered | Border on every cell |
pk-table-sm | Reduced padding |
pk-table-compact | Minimal padding + smaller font |
pk-table-primary/success/warn/error | Colored <thead> |
pk-table-responsive | Wrapper <div> — horizontal scroll on overflow |
<!-- Minimal -->
<label class="pk-toggle">
<input type="checkbox" />
<span class="pk-toggle__track"></span>
</label>
<!-- With label + color variant -->
<label class="pk-toggle pk-toggle-success">
<input type="checkbox" checked />
<span class="pk-toggle__track"></span>
<span class="pk-toggle__label">Enable feature</span>
</label>
<!-- Angular two-way binding -->
<label class="pk-toggle">
<input type="checkbox" [(ngModel)]="isEnabled" />
<span class="pk-toggle__track"></span>
<span class="pk-toggle__label">{{ isEnabled ? 'On' : 'Off' }}</span>
</label>
| Class | Description |
|---|
pk-toggle | Wrapper <label> — required |
pk-toggle__track | Track + thumb <span> — required |
pk-toggle__label | Optional text label <span> |
pk-toggle-success/warn/error | Checked color variant |
| `pk-toggle-sm$ | \text{Small} (34 \times 18 \text{px}) |
| pk−toggle−lg | \text{Large} (56 \times 30 \text{px}) |
| $pk-toggle-label-left` | Move label to the left of the track |
Floating label form fields — pure CSS, works with input, select, and textarea.
The input must have placeholder=" " (single space) for the float animation to work.
<!-- Basic input -->
<div class="pk-field">
<input class="pk-field__input" type="text" placeholder=" " id="name" />
<label class="pk-field__label" for="name">Full name</label>
</div>
<!-- Prefix / Suffix -->
<div class="pk-field">
<div class="pk-field__wrap">
<span class="pk-field__prefix">@</span>
<input class="pk-field__input" type="text" placeholder=" " id="user" />
<span class="pk-field__suffix">.com</span>
</div>
<label class="pk-field__label" for="user">Username</label>
</div>
<!-- Error state with message -->
<div class="pk-field pk-field--error">
<input class="pk-field__input" type="email" placeholder=" " id="email" />
<label class="pk-field__label" for="email">Email</label>
<span class="pk-field__error">Invalid email address</span>
</div>
<!-- Form group (2-column) -->
<div class="pk-form-group pk-form-group--2">
<div class="pk-field"> ... </div>
<div class="pk-field"> ... </div>
</div>
Wrapper modifier classes:
| Class | Description |
|---|
pk-field | Required wrapper |
pk-field--filled | Filled background, bottom border only (Material style) |
pk-field--underline | No border box, underline only |
pk-field--pill | Fully rounded corners |
pk-field--outlined | Adds glow ring on focus |
pk-field--sm | Small (42 px height) |
pk-field--lg | Large (60 px height) |
pk-field--success | Green border + label |
pk-field--error | Red border + label |
Element classes:
| Class | Element | Description |
|---|
pk-field__input | input / select / textarea | Form control — must have placeholder=" " |
pk-field__label | label | Floating label — must come after the input in the DOM |
pk-field__wrap | div | Flex row container for prefix/suffix + input |
pk-field__prefix | span | Left-attached addon |
pk-field__suffix | span | Right-attached addon |
pk-field__hint | span | Helper text (grey) |
pk-field__error | span | Error message (red) |
pk-field__footer | div | Flex row: hint left + counter right |
pk-field__counter | span | Character counter (right-aligned) |
Layout helpers:
| Class | Description |
|---|
pk-form-group | Responsive flex row of fields |
pk-form-group--2 | 2-column layout |
pk-form-group--3 | 3-column layout |
pk-form-section | Section divider label (uppercase, border-bottom) |
pk-font.css is opt-in — it is NOT included in pk-ui.css. Import separately:
// angular.json
"styles": [
"node_modules/ngx-pk-ui/styles/pk-ui.css",
"node_modules/ngx-pk-ui/styles/pk-font.css"
]
<p class="pk-font-sarabun">สวัสดี</p>
<p class="pk-font-kanit">สวัสดี</p>
<p class="pk-font-phetsarath">ສະບາຍດີ</p>
| Class | Font | Script |
|---|
pk-font-bai-jamjuree | Bai Jamjuree | Thai |
pk-font-chakra-petch | Chakra Petch | Thai |
pk-font-charm | Charm | Thai |
pk-font-charmonman | Charmonman | Thai |
pk-font-kanit | Kanit | Thai |
pk-font-mitr | Mitr | Thai |
pk-font-noto-sans-thai | Noto Sans Thai | Thai |
pk-font-pattaya | Pattaya | Thai |
pk-font-prompt | Prompt | Thai |
pk-font-sarabun | Sarabun | Thai |
pk-font-sriracha | Sriracha | Thai |
pk-font-srisakdi | Srisakdi | Thai |
pk-font-thasadith | Thasadith | Thai |
pk-font-trirong | Trirong | Thai |
pk-font-noto-sans-lao | Noto Sans Lao | Lao |
pk-font-noto-sans-lao-looped | Noto Sans Lao Looped | Lao |
pk-font-noto-serif-lao | Noto Serif Lao | Lao |
pk-font-phetsarath | Phetsarath | Lao |
Note: Google Sans is proprietary (not on Google Fonts CDN) and is excluded.
# Build the library
ng build ngx-pk-ui
# Run all tests (Vitest, headless)
ng test ngx-pk-ui --no-watch
# Run one spec file
npx vitest run projects/ngx-pk-ui/src/lib/pk-tabs/pk-tabs.spec.ts
# Serve the example app
npm run start:example
# Publish to npm (after build)
npm publish dist/ngx-pk-ui
Developed with the assistance of GitHub Copilot.