@egose/shadcn-theme-ng 0.1.0 → 0.3.0
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 +2 -2
- package/accordion/README.md +405 -2
- package/alert/README.md +372 -2
- package/alert-dialog/README.md +471 -5
- package/aspect-ratio/README.md +272 -5
- package/autocomplete/README.md +502 -2
- package/autocomplete/fesm2022/autocomplete.mjs +1 -1
- package/avatar/README.md +357 -5
- package/badge/README.md +318 -2
- package/basic-alert/README.md +353 -2
- package/breadcrumb/README.md +406 -5
- package/button/README.md +482 -2
- package/button/fesm2022/button.mjs +85 -107
- package/button/types/button.d.ts +5 -8
- package/button-group/README.md +318 -5
- package/calendar/README.md +357 -2
- package/card/README.md +331 -5
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +4 -1
- package/checkbox/README.md +320 -2
- package/checkbox/fesm2022/checkbox.mjs +6 -7
- package/checkbox/types/checkbox.d.ts +1 -1
- package/collapsible/README.md +332 -5
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +5 -2
- package/command/README.md +435 -5
- package/confirmation-dialog/README.md +301 -2
- package/context-menu/README.md +366 -5
- package/date-picker/README.md +469 -2
- package/date-picker/fesm2022/date-picker.mjs +150 -32
- package/date-picker/types/date-picker.d.ts +102 -9
- package/dialog/README.md +448 -2
- package/drawer/README.md +395 -5
- package/dropdown-menu/README.md +417 -5
- package/empty/README.md +329 -5
- package/field/README.md +385 -5
- package/form-autocomplete/README.md +177 -0
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
- package/form-autocomplete/package.json +24 -0
- package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
- package/form-checkbox/README.md +322 -2
- package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
- package/form-checkbox/types/form-checkbox.d.ts +23 -4
- package/form-combobox/README.md +202 -0
- package/form-combobox/fesm2022/form-combobox.mjs +147 -0
- package/form-combobox/package.json +24 -0
- package/form-combobox/types/form-combobox.d.ts +73 -0
- package/form-date-picker/README.md +348 -2
- package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
- package/form-date-picker/types/form-date-picker.d.ts +17 -1
- package/form-date-picker-multi/README.md +191 -0
- package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
- package/form-date-picker-multi/package.json +24 -0
- package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
- package/form-date-range-picker/README.md +253 -0
- package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
- package/form-date-range-picker/package.json +24 -0
- package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
- package/form-field/README.md +356 -2
- package/form-field-simple/README.md +340 -2
- package/form-input-otp/README.md +194 -0
- package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
- package/form-input-otp/package.json +24 -0
- package/form-input-otp/types/form-input-otp.d.ts +58 -0
- package/form-month-year-picker/README.md +188 -0
- package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
- package/form-month-year-picker/package.json +24 -0
- package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
- package/form-native-select/README.md +205 -0
- package/form-native-select/fesm2022/form-native-select.mjs +103 -0
- package/form-native-select/package.json +24 -0
- package/form-native-select/types/form-native-select.d.ts +57 -0
- package/form-phone-input/README.md +188 -0
- package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
- package/form-phone-input/package.json +24 -0
- package/form-phone-input/types/form-phone-input.d.ts +58 -0
- package/form-radio-group/README.md +198 -0
- package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
- package/form-radio-group/package.json +24 -0
- package/form-radio-group/types/form-radio-group.d.ts +63 -0
- package/form-searchable-multiselect/README.md +371 -2
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
- package/form-select/README.md +360 -2
- package/form-select/fesm2022/form-select.mjs +20 -11
- package/form-select/types/form-select.d.ts +18 -1
- package/form-slider/README.md +182 -0
- package/form-slider/fesm2022/form-slider.mjs +106 -0
- package/form-slider/package.json +24 -0
- package/form-slider/types/form-slider.d.ts +60 -0
- package/form-switch/README.md +173 -0
- package/form-switch/fesm2022/form-switch.mjs +100 -0
- package/form-switch/package.json +24 -0
- package/form-switch/types/form-switch.d.ts +50 -0
- package/form-text-input/README.md +381 -2
- package/form-text-input/fesm2022/form-text-input.mjs +19 -10
- package/form-text-input/types/form-text-input.d.ts +18 -1
- package/form-textarea/README.md +357 -2
- package/form-textarea/fesm2022/form-textarea.mjs +19 -10
- package/form-textarea/types/form-textarea.d.ts +18 -1
- package/form-toggle/README.md +186 -0
- package/form-toggle/fesm2022/form-toggle.mjs +159 -0
- package/form-toggle/package.json +24 -0
- package/form-toggle/types/form-toggle.d.ts +82 -0
- package/form-toggle-group/README.md +176 -0
- package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
- package/form-toggle-group/package.json +24 -0
- package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
- package/hover-card/README.md +256 -5
- package/icon/README.md +239 -2
- package/input/README.md +269 -2
- package/input-group/README.md +335 -5
- package/input-group/fesm2022/input-group.mjs +22 -12
- package/input-group/types/input-group.d.ts +4 -1
- package/input-otp/README.md +375 -5
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +3 -3
- package/kbd/README.md +291 -5
- package/label/README.md +272 -2
- package/layout-simple/README.md +193 -2
- package/layout-simple/fesm2022/layout-simple.mjs +472 -236
- package/layout-simple/types/layout-simple.d.ts +174 -137
- package/menu/README.md +417 -2
- package/menubar/README.md +343 -5
- package/native-select/README.md +323 -5
- package/native-select/fesm2022/native-select.mjs +18 -6
- package/native-select/types/native-select.d.ts +7 -2
- package/navigation-menu/README.md +369 -5
- package/package.json +57 -1
- package/pagination/README.md +388 -5
- package/phone-input/README.md +114 -0
- package/phone-input/fesm2022/phone-input.mjs +191 -0
- package/phone-input/package.json +24 -0
- package/phone-input/types/phone-input.d.ts +67 -0
- package/popover/README.md +331 -2
- package/progress/README.md +311 -5
- package/radio-group/README.md +364 -2
- package/radio-group/fesm2022/radio-group.mjs +5 -1
- package/resizable/README.md +269 -5
- package/scroll-area/README.md +233 -5
- package/searchable-multiselect/README.md +323 -2
- package/select/README.md +437 -2
- package/separator/README.md +222 -2
- package/sheet/README.md +311 -2
- package/sidebar/README.md +457 -5
- package/skeleton/README.md +217 -5
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +3 -3
- package/sonner/README.md +346 -2
- package/spinner/README.md +284 -2
- package/switch/README.md +310 -2
- package/switch/fesm2022/switch.mjs +7 -5
- package/switch/types/switch.d.ts +2 -1
- package/table/README.md +423 -5
- package/tabs/README.md +411 -2
- package/tabs/fesm2022/tabs.mjs +2 -2
- package/textarea/README.md +282 -5
- package/toggle/README.md +270 -5
- package/toggle-group/README.md +340 -5
- package/tooltip/README.md +269 -2
- package/typography/README.md +271 -5
- package/utils/README.md +303 -2
package/pagination/README.md
CHANGED
|
@@ -1,11 +1,394 @@
|
|
|
1
|
-
# Pagination
|
|
1
|
+
# Pagination (`@egose/shadcn-theme-ng/pagination`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Paging controls in two flavors (shadcn/ui `pagination` equivalent): composable primitives (`hlm-pagination` shell + content/item/link/previous/next/ellipsis) for hand-rolled pagers, and two drop-in numbered pagers (`hlm-numbered-pagination` for client-side `[(currentPage)]`/`[(itemsPerPage)]` state, `hlm-numbered-pagination-query-params` for router `?page=`-driven state). Page-window math (`createPageArray`, `outOfBoundCorrection`) is exported for custom UIs. No brain primitive underneath — state is local signals/models plus Angular `RouterLink`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Ships as `@egose/shadcn-theme-ng/pagination` and `@egose/shadcn-theme-ng-tw/pagination` (tw: variant). See the [package README](../../README.md) for installation, peer dependencies, Tailwind setup, and testing. Do not publish this project directory independently.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
# Plain Tailwind (no prefix):
|
|
11
|
+
npm install @egose/shadcn-theme-ng
|
|
12
|
+
|
|
13
|
+
# Or the tw:-prefixed variant:
|
|
14
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies are inherited from the package root (see [package README](../../README.md)). This subpath itself declares `@angular/common`, `@angular/core`, `@spartan-ng/brain` as peers plus a `tslib` runtime dependency; at runtime the numbered pagers also import `@egose/shadcn-theme-ng/select`, and links use `@angular/router`. No extra install step is needed beyond the package install above.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
Real exported symbols (from `src/public-api.ts`):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
HlmPagination, // directive: [hlmPagination],hlm-pagination
|
|
26
|
+
HlmPaginationContent, // directive: ul[hlmPaginationContent]
|
|
27
|
+
HlmPaginationItem, // directive: li[hlmPaginationItem]
|
|
28
|
+
HlmPaginationLink, // directive: [hlmPaginationLink]
|
|
29
|
+
HlmPaginationPrevious, // component: hlm-pagination-previous
|
|
30
|
+
HlmPaginationNext, // component: hlm-pagination-next
|
|
31
|
+
HlmPaginationEllipsis, // component: hlm-pagination-ellipsis
|
|
32
|
+
HlmNumberedPagination, // component: hlm-numbered-pagination
|
|
33
|
+
HlmNumberedPaginationQueryParams, // component: hlm-numbered-pagination-query-params
|
|
34
|
+
outOfBoundCorrection, // (totalItems, itemsPerPage, currentPage) => number
|
|
35
|
+
createPageArray, // (currentPage, itemsPerPage, totalItems, range) => (number | '...')[]
|
|
36
|
+
HlmPaginationImports, // all nine components above
|
|
37
|
+
HlmPaginationModule, // NgModule wrapping HlmPaginationImports
|
|
38
|
+
} from '@egose/shadcn-theme-ng/pagination';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Standalone usage:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { Component } from '@angular/core';
|
|
45
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
46
|
+
|
|
47
|
+
@Component({
|
|
48
|
+
selector: 'app-demo',
|
|
49
|
+
standalone: true,
|
|
50
|
+
imports: [...HlmPaginationImports],
|
|
51
|
+
template: ` <hlm-numbered-pagination [(currentPage)]="page" [(itemsPerPage)]="pageSize" [totalItems]="230" /> `,
|
|
52
|
+
})
|
|
53
|
+
export class DemoComponent {
|
|
54
|
+
page = 1;
|
|
55
|
+
pageSize = 10;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
NgModule usage:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { NgModule } from '@angular/core';
|
|
63
|
+
import { HlmPaginationModule } from '@egose/shadcn-theme-ng/pagination';
|
|
64
|
+
|
|
65
|
+
@NgModule({ imports: [HlmPaginationModule] })
|
|
66
|
+
export class DemoModule {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For the `tw:` build, swap the specifier to `@egose/shadcn-theme-ng-tw/pagination`. Symbol names are identical.
|
|
70
|
+
|
|
71
|
+
## Anatomy / Structure
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!-- Hand-rolled pager -->
|
|
75
|
+
<nav hlmPagination aria-label="Search results pages">
|
|
76
|
+
<ul hlmPaginationContent>
|
|
77
|
+
<li hlmPaginationItem>
|
|
78
|
+
<hlm-pagination-previous />
|
|
79
|
+
</li>
|
|
80
|
+
<li hlmPaginationItem>
|
|
81
|
+
<a hlmPaginationLink [isActive]="true">1</a>
|
|
82
|
+
</li>
|
|
83
|
+
<li hlmPaginationItem>
|
|
84
|
+
<a hlmPaginationLink link="/items" [queryParams]="{ page: 2 }">2</a>
|
|
85
|
+
</li>
|
|
86
|
+
<li hlmPaginationItem>
|
|
87
|
+
<hlm-pagination-ellipsis />
|
|
88
|
+
</li>
|
|
89
|
+
<li hlmPaginationItem>
|
|
90
|
+
<hlm-pagination-next [iconOnly]="true" />
|
|
91
|
+
</li>
|
|
92
|
+
</ul>
|
|
93
|
+
</nav>
|
|
94
|
+
|
|
95
|
+
<!-- Drop-in numbered pager (client state) -->
|
|
96
|
+
<hlm-numbered-pagination
|
|
97
|
+
[(currentPage)]="page"
|
|
98
|
+
[(itemsPerPage)]="pageSize"
|
|
99
|
+
[totalItems]="total"
|
|
100
|
+
[maxSize]="7"
|
|
101
|
+
[showEdges]="true"
|
|
102
|
+
[pageSizes]="[10, 20, 50, 100]"
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<!-- Drop-in pager bound to router ?page= -->
|
|
106
|
+
<hlm-numbered-pagination-query-params
|
|
107
|
+
[(currentPage)]="page"
|
|
108
|
+
[(itemsPerPage)]="pageSize"
|
|
109
|
+
[totalItems]="total"
|
|
110
|
+
link="."
|
|
111
|
+
/>
|
|
11
112
|
```
|
|
113
|
+
|
|
114
|
+
| Class | Selector | Role |
|
|
115
|
+
| ---------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------ |
|
|
116
|
+
| `HlmPagination` | `[hlmPagination],hlm-pagination` | `<nav role="navigation">` shell |
|
|
117
|
+
| `HlmPaginationContent` | `ul[hlmPaginationContent]` | Row list (`flex items-center gap-1`) |
|
|
118
|
+
| `HlmPaginationItem` | `li[hlmPaginationItem]` | Item wrapper (slot only, no styling) |
|
|
119
|
+
| `HlmPaginationLink` | `[hlmPaginationLink]` | Page link (`RouterLink` host; active → `outline` button, else `ghost`) |
|
|
120
|
+
| `HlmPaginationPrevious` | `hlm-pagination-previous` | Chevron-left + "Previous" link |
|
|
121
|
+
| `HlmPaginationNext` | `hlm-pagination-next` | "Next" + chevron-right link |
|
|
122
|
+
| `HlmPaginationEllipsis` | `hlm-pagination-ellipsis` | `…` gap marker + sr-only text |
|
|
123
|
+
| `HlmNumberedPagination` | `hlm-numbered-pagination` | Full pager: count summary + nav + page-size `hlm-select` |
|
|
124
|
+
| `HlmNumberedPaginationQueryParams` | `hlm-numbered-pagination-query-params` | Same, but every link navigates with `queryParamsHandling="merge"` and `?page=` |
|
|
125
|
+
|
|
126
|
+
## API reference
|
|
127
|
+
|
|
128
|
+
### Primitives
|
|
129
|
+
|
|
130
|
+
| Selector | Inputs | Notes |
|
|
131
|
+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
132
|
+
| `[hlmPagination],hlm-pagination` (`HlmPagination`) | `ariaLabel` (alias `aria-label`): `string`, default `'pagination'` | `role="navigation"` + `aria-label` binding |
|
|
133
|
+
| `ul[hlmPaginationContent]` | — | Layout only |
|
|
134
|
+
| `li[hlmPaginationItem]` | — | Slot only |
|
|
135
|
+
| `[hlmPaginationLink]` (`HlmPaginationLink`) | `isActive: boolean` (default `false`, boolean coercion) → `data-active` + `aria-current="page"`; `size: ButtonVariants['size']` (default `'icon'`); `link: RouterLink['routerLink']` (optional — no `link` renders a `cursor-pointer` span-like anchor); plus full `RouterLink` host inputs (`queryParams`, `fragment`, `queryParamsHandling`, `state`, `info`, `relativeTo`, `preserveFragment`, `skipLocationChange`, `replaceUrl`, `target`) | Styling: `buttonVariants({ variant: isActive ? 'outline' : 'ghost', size })` |
|
|
136
|
+
| `hlm-pagination-previous` | `class`, `link`, `queryParams`, `queryParamsHandling` (all `RouterLink` types); `ariaLabel` (alias `aria-label`, default `'Go to previous page'`); `text` (default `'Previous'`); `iconOnly: boolean` (default `false`) | Label hidden on small screens unless `iconOnly` (`sr-only` vs `hidden sm:block`); size collapses to `icon` when `iconOnly`; non-icon adds `ps-2!` |
|
|
137
|
+
| `hlm-pagination-next` | Same shape; defaults `'Go to next page'` / `'Next'`; non-icon adds `pe-2!` | Mirrored; chevron flips in RTL (`rtl:rotate-180`) |
|
|
138
|
+
| `hlm-pagination-ellipsis` | `srOnlyText: string` (default `'More pages'`) | `size-9` centered `lucideEllipsis` + sr-only text |
|
|
139
|
+
|
|
140
|
+
### `HlmNumberedPagination` (`hlm-numbered-pagination`)
|
|
141
|
+
|
|
142
|
+
| Member | Kind | Type / Default | Notes |
|
|
143
|
+
| -------------- | -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
144
|
+
| `currentPage` | **model (required)** | `number` | Two-way: `[(currentPage)]`. Clicks set it; out-of-range values are auto-corrected via `outOfBoundCorrection` |
|
|
145
|
+
| `itemsPerPage` | **model (required)** | `number` | Two-way: `[(itemsPerPage)]`, bound to the page-size `hlm-select` |
|
|
146
|
+
| `totalItems` | input (required) | `number` (number coercion) | Server or client total; `0`/negative collapses to a single empty page |
|
|
147
|
+
| `maxSize` | input | `number`, default `7` (number coercion) | Max page links in the window (ellipsis logic from `ngx-pagination`) |
|
|
148
|
+
| `showEdges` | input | `boolean`, default `true` (boolean coercion) | Shows prev/next edge buttons (hidden on first/last page respectively) |
|
|
149
|
+
| `pageSizes` | input | `number[]`, default `[10, 20, 50, 100]` | Page-size menu; the current size is merged in + sorted if missing |
|
|
150
|
+
|
|
151
|
+
Layout: `flex justify-between` row — left count summary (`<b>total</b> total items | <b>pages</b> pages`), center pager, right `hlm-select` page-size picker. Previous/next are plain click handlers (`goToPrevious/goToNext`); page links set `currentPage` directly without routing.
|
|
152
|
+
|
|
153
|
+
### `HlmNumberedPaginationQueryParams` (`hlm-numbered-pagination-query-params`)
|
|
154
|
+
|
|
155
|
+
Same models/inputs as `HlmNumberedPagination` **plus**:
|
|
156
|
+
|
|
157
|
+
| Member | Kind | Type / Default | Notes |
|
|
158
|
+
| ------ | ----- | ----------------------- | -------------------------------------------------------- |
|
|
159
|
+
| `link` | input | `string`, default `'.'` | Base path for every page link (current route by default) |
|
|
160
|
+
|
|
161
|
+
Every link (prev/numbered/next) navigates via `RouterLink` with `[queryParams]="{ page }"` and `queryParamsHandling="merge"`. The active page renders with `link === undefined` (no navigation) + `isActive`.
|
|
162
|
+
|
|
163
|
+
### Helpers
|
|
164
|
+
|
|
165
|
+
| Symbol | Signature | Notes |
|
|
166
|
+
| ------------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
167
|
+
| `outOfBoundCorrection(totalItems, itemsPerPage, currentPage)` | `(number, number, number) => number` | Clamps to `[1, totalPages]` (from `ngx-pagination`) |
|
|
168
|
+
| `createPageArray(currentPage, itemsPerPage, totalItems, paginationRange)` | `(number, number, number, number) => (number \| '...')[]` | Window with `'...'` gaps; powers both numbered pagers |
|
|
169
|
+
| `Page` | `type Page = number \| '...'` | Not exported from public-api (internal to the pager file) — redeclare locally if you type custom windows |
|
|
170
|
+
|
|
171
|
+
## Examples
|
|
172
|
+
|
|
173
|
+
### 1. Hand-rolled pager with router links
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { Component, signal } from '@angular/core';
|
|
177
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
178
|
+
|
|
179
|
+
@Component({
|
|
180
|
+
selector: 'app-pager-basic',
|
|
181
|
+
standalone: true,
|
|
182
|
+
imports: [...HlmPaginationImports],
|
|
183
|
+
template: `
|
|
184
|
+
<nav hlmPagination aria-label="Article pages">
|
|
185
|
+
<ul hlmPaginationContent>
|
|
186
|
+
<li hlmPaginationItem>
|
|
187
|
+
<hlm-pagination-previous link="/articles" [queryParams]="{ page: page() - 1 }" />
|
|
188
|
+
</li>
|
|
189
|
+
@for (p of [1, 2, 3]; track p) {
|
|
190
|
+
<li hlmPaginationItem>
|
|
191
|
+
<a hlmPaginationLink link="/articles" [queryParams]="{ page: p }" [isActive]="p === page()">
|
|
192
|
+
{{ p }}
|
|
193
|
+
</a>
|
|
194
|
+
</li>
|
|
195
|
+
}
|
|
196
|
+
<li hlmPaginationItem>
|
|
197
|
+
<hlm-pagination-next link="/articles" [queryParams]="{ page: page() + 1 }" />
|
|
198
|
+
</li>
|
|
199
|
+
</ul>
|
|
200
|
+
</nav>
|
|
201
|
+
`,
|
|
202
|
+
})
|
|
203
|
+
export class PagerBasicComponent {
|
|
204
|
+
readonly page = signal(1);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 2. Drop-in client-side pager + sliced list
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { Component, signal, computed } from '@angular/core';
|
|
212
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
213
|
+
|
|
214
|
+
@Component({
|
|
215
|
+
selector: 'app-pager-client',
|
|
216
|
+
standalone: true,
|
|
217
|
+
imports: [...HlmPaginationImports],
|
|
218
|
+
template: `
|
|
219
|
+
<ul class="tw:grid tw:gap-1 tw:py-2">
|
|
220
|
+
@for (item of visible(); track item) {
|
|
221
|
+
<li class="tw:border tw:rounded tw:px-2 tw:py-1 tw:text-sm">{{ item }}</li>
|
|
222
|
+
}
|
|
223
|
+
</ul>
|
|
224
|
+
|
|
225
|
+
<hlm-numbered-pagination
|
|
226
|
+
[(currentPage)]="page"
|
|
227
|
+
[(itemsPerPage)]="pageSize"
|
|
228
|
+
[totalItems]="items().length"
|
|
229
|
+
[maxSize]="5"
|
|
230
|
+
[pageSizes]="[5, 10, 25]"
|
|
231
|
+
/>
|
|
232
|
+
`,
|
|
233
|
+
})
|
|
234
|
+
export class PagerClientComponent {
|
|
235
|
+
readonly items = signal(Array.from({ length: 42 }, (_, i) => `Item ${i + 1}`));
|
|
236
|
+
readonly page = signal(1);
|
|
237
|
+
readonly pageSize = signal(10);
|
|
238
|
+
|
|
239
|
+
readonly visible = computed(() => {
|
|
240
|
+
const start = (this.page() - 1) * this.pageSize();
|
|
241
|
+
return this.items().slice(start, start + this.pageSize());
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 3. Router `?page=` pager (server-driven)
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { Component, signal, effect } from '@angular/core';
|
|
250
|
+
import { ActivatedRoute } from '@angular/router';
|
|
251
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
252
|
+
|
|
253
|
+
@Component({
|
|
254
|
+
selector: 'app-pager-query',
|
|
255
|
+
standalone: true,
|
|
256
|
+
imports: [...HlmPaginationImports],
|
|
257
|
+
template: `
|
|
258
|
+
<hlm-numbered-pagination-query-params
|
|
259
|
+
[(currentPage)]="page"
|
|
260
|
+
[(itemsPerPage)]="pageSize"
|
|
261
|
+
[totalItems]="total()"
|
|
262
|
+
link="."
|
|
263
|
+
/>
|
|
264
|
+
<p class="tw:text-sm tw:text-muted-foreground">URL holds ?page={{ page }}.</p>
|
|
265
|
+
`,
|
|
266
|
+
})
|
|
267
|
+
export class PagerQueryComponent {
|
|
268
|
+
readonly page = signal(1);
|
|
269
|
+
readonly pageSize = signal(10);
|
|
270
|
+
readonly total = signal(320);
|
|
271
|
+
|
|
272
|
+
constructor(route: ActivatedRoute) {
|
|
273
|
+
effect(() => {
|
|
274
|
+
// keep ?page= as the source of truth on back/forward + deep links
|
|
275
|
+
const sub = route.queryParamMap.subscribe((params) => {
|
|
276
|
+
const p = Number(params.get('page') ?? 1);
|
|
277
|
+
this.page.set(Number.isFinite(p) && p >= 1 ? Math.floor(p) : 1);
|
|
278
|
+
});
|
|
279
|
+
return () => sub.unsubscribe();
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 4. Icon-only edges, custom labels, custom aria
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { Component } from '@angular/core';
|
|
289
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
290
|
+
|
|
291
|
+
@Component({
|
|
292
|
+
selector: 'app-pager-edges',
|
|
293
|
+
standalone: true,
|
|
294
|
+
imports: [...HlmPaginationImports],
|
|
295
|
+
template: `
|
|
296
|
+
<nav hlmPagination aria-label="Gallery pages">
|
|
297
|
+
<ul hlmPaginationContent>
|
|
298
|
+
<li hlmPaginationItem>
|
|
299
|
+
<hlm-pagination-previous [iconOnly]="true" text="Back" aria-label="Previous gallery page" />
|
|
300
|
+
</li>
|
|
301
|
+
<li hlmPaginationItem>
|
|
302
|
+
<a hlmPaginationLink [isActive]="true">7</a>
|
|
303
|
+
</li>
|
|
304
|
+
<li hlmPaginationItem>
|
|
305
|
+
<hlm-pagination-ellipsis srOnlyText="Skipped pages" />
|
|
306
|
+
</li>
|
|
307
|
+
<li hlmPaginationItem>
|
|
308
|
+
<a hlmPaginationLink>12</a>
|
|
309
|
+
</li>
|
|
310
|
+
<li hlmPaginationItem>
|
|
311
|
+
<hlm-pagination-next [iconOnly]="true" text="Forward" aria-label="Next gallery page" />
|
|
312
|
+
</li>
|
|
313
|
+
</ul>
|
|
314
|
+
</nav>
|
|
315
|
+
`,
|
|
316
|
+
})
|
|
317
|
+
export class PagerEdgesComponent {}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 5. Custom window with exported helpers
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { Component, signal, computed } from '@angular/core';
|
|
324
|
+
import { HlmPaginationImports, createPageArray } from '@egose/shadcn-theme-ng/pagination';
|
|
325
|
+
|
|
326
|
+
@Component({
|
|
327
|
+
selector: 'app-pager-custom-window',
|
|
328
|
+
standalone: true,
|
|
329
|
+
imports: [...HlmPaginationImports],
|
|
330
|
+
template: `
|
|
331
|
+
<nav hlmPagination>
|
|
332
|
+
<ul hlmPaginationContent>
|
|
333
|
+
@for (p of window(); track p) {
|
|
334
|
+
<li hlmPaginationItem>
|
|
335
|
+
@if (p === '...') {
|
|
336
|
+
<hlm-pagination-ellipsis />
|
|
337
|
+
} @else {
|
|
338
|
+
<a hlmPaginationLink [isActive]="p === page()" (click)="page.set(p)">{{ p }}</a>
|
|
339
|
+
}
|
|
340
|
+
</li>
|
|
341
|
+
}
|
|
342
|
+
</ul>
|
|
343
|
+
</nav>
|
|
344
|
+
`,
|
|
345
|
+
})
|
|
346
|
+
export class PagerCustomWindowComponent {
|
|
347
|
+
readonly page = signal(6);
|
|
348
|
+
readonly window = computed(() => createPageArray(this.page(), 10, 300, 7));
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### 6. Hide edges + compact sizes
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
import { Component, signal } from '@angular/core';
|
|
356
|
+
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
357
|
+
|
|
358
|
+
@Component({
|
|
359
|
+
selector: 'app-pager-compact',
|
|
360
|
+
standalone: true,
|
|
361
|
+
imports: [...HlmPaginationImports],
|
|
362
|
+
template: `
|
|
363
|
+
<hlm-numbered-pagination
|
|
364
|
+
[(currentPage)]="page"
|
|
365
|
+
[(itemsPerPage)]="pageSize"
|
|
366
|
+
[totalItems]="95"
|
|
367
|
+
[maxSize]="5"
|
|
368
|
+
[showEdges]="false"
|
|
369
|
+
[pageSizes]="[5, 10]"
|
|
370
|
+
/>
|
|
371
|
+
`,
|
|
372
|
+
})
|
|
373
|
+
export class PagerCompactComponent {
|
|
374
|
+
readonly page = signal(1);
|
|
375
|
+
readonly pageSize = signal(5);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Accessibility notes
|
|
380
|
+
|
|
381
|
+
- The shell is `role="navigation"` with a configurable `aria-label` — always set a distinguishing label when several pagers share a page.
|
|
382
|
+
- Active links expose `aria-current="page"`; ellipsis exposes configurable sr-only text; prev/next expose configurable `aria-label`s (`iconOnly` collapses visible text to `sr-only`, keeping the accessible name).
|
|
383
|
+
- Numbered pagers announce totals as plain text ("N total items | M pages"); for live search results, mirror the count into an `aria-live` region in your own template.
|
|
384
|
+
- Keyboard: links are native anchors (tab/enter work); the client-state pager uses clickable anchors without `link` for page numbers — they remain focusable via `cursor-pointer` styling but prefer the query-params pager when deep-linkability matters.
|
|
385
|
+
|
|
386
|
+
## Theming / CSS variables
|
|
387
|
+
|
|
388
|
+
No theming inputs. Links reuse `buttonVariants` (`ghost`/`outline`, `icon`/`default` sizes); the pager chrome is uncolored layout. Tune density with `maxSize`/`showEdges`/`pageSizes` and per-instance `class` on prev/next.
|
|
389
|
+
|
|
390
|
+
## Related subpaths
|
|
391
|
+
|
|
392
|
+
- `@egose/shadcn-theme-ng/button` (`buttonVariants`) — sizing/variant engine behind `HlmPaginationLink`.
|
|
393
|
+
- `@egose/shadcn-theme-ng/select` — page-size picker inside both numbered pagers.
|
|
394
|
+
- `@egose/shadcn-theme-ng/table` — common parent for paged data grids.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Phone Input (`@egose/shadcn-theme-ng/phone-input`)
|
|
2
|
+
|
|
3
|
+
A masked phone-number input. The form model holds raw digits (`"4155552671"`); the visible text is formatted (`"(415) 555-2671"`). Caret position is preserved across mask characters while typing or deleting — no DOM-selector lookups, no manual control subscriptions.
|
|
4
|
+
|
|
5
|
+
> **Ships as:** `@egose/shadcn-theme-ng/phone-input` and `@egose/shadcn-theme-ng-tw/phone-input`
|
|
6
|
+
> (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
|
|
7
|
+
> emitted Tailwind class strings differ. See the [package README](../../README.md) for install
|
|
8
|
+
> steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Plain Tailwind (no prefix)
|
|
14
|
+
npm install @egose/shadcn-theme-ng
|
|
15
|
+
|
|
16
|
+
# tw:-prefixed Tailwind variant
|
|
17
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Imports
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { HlmPhoneInput, formatNanpPhoneNumber, type PhoneNumberFormatter } from '@egose/shadcn-theme-ng/phone-input';
|
|
24
|
+
// tw variant: replace with '@egose/shadcn-theme-ng-tw/phone-input'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`HlmPhoneInput` implements `ControlValueAccessor`: bind it with `formControlName`, `formControl`, or `ngModel`. Prefer `EgFormPhoneInput` (`@egose/shadcn-theme-ng/form-phone-input`) for the label/error/hint wrapper.
|
|
28
|
+
|
|
29
|
+
## API reference
|
|
30
|
+
|
|
31
|
+
### `HlmPhoneInput` — `hlm-phone-input`
|
|
32
|
+
|
|
33
|
+
| Input | Type | Default | Description |
|
|
34
|
+
| ------------------- | ---------------------------- | ----------------------- | -------------------------------------------------- |
|
|
35
|
+
| `inputId` | `string` | `hlm-phone-input-<n>` | Native input id. |
|
|
36
|
+
| `name` | `string \| undefined` | — | Native `name` forwarded to the inner `<input>`. |
|
|
37
|
+
| `ariaLabel` | `string \| undefined` | — | Accessible label. |
|
|
38
|
+
| `ariaDescribedby` | `string \| null` | `null` | ID of the describing element. |
|
|
39
|
+
| `placeholder` | `string` | `''` | Input placeholder. |
|
|
40
|
+
| `autocomplete` | `string` | `'tel'` | Native autocomplete token. |
|
|
41
|
+
| `readonly` | `boolean` | `false` | Native `readonly` state. |
|
|
42
|
+
| `disabled` | `boolean` | `false` | Interaction lock (form control stays enabled). |
|
|
43
|
+
| `forceInvalid` | `boolean` | `false` | Forces invalid styling. |
|
|
44
|
+
| `maxDigits` | `number` | `10` | Maximum digit count kept in the model. |
|
|
45
|
+
| `modelFormat` | `'digits' \| 'formatted'` | `'digits'` | Model shape: raw digits or formatted display text. |
|
|
46
|
+
| `formatPhoneNumber` | `(digits: string) => string` | `formatNanpPhoneNumber` | Display formatter; receives raw digits. |
|
|
47
|
+
| `class` | `ClassValue` | `''` | Extra classes on the inner `<input>`. |
|
|
48
|
+
| `inputClass` | `ClassValue` | `''` | Extra classes on the inner `<input>`. |
|
|
49
|
+
|
|
50
|
+
Model values may be `string | null | undefined`; non-digit characters are stripped defensively on write. An empty model renders as empty text (no dangling mask characters) and emits `null`. With `modelFormat: 'formatted'` the model holds display text instead of digits — useful when a backend expects the masked value; validators then apply to the formatted string.
|
|
51
|
+
|
|
52
|
+
### `formatNanpPhoneNumber(digits)`
|
|
53
|
+
|
|
54
|
+
North-American numbering plan formatter: `4` → `(4`, `4155` → `(415) 5`, `4155552671` → `(415) 555-2671`, truncated to 10 digits. Pass a custom `PhoneNumberFormatter` for other plans (e.g. chunk-and-join national formats) together with a matching `maxDigits`.
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### 1. Standalone with formControl
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { Component, inject } from '@angular/core';
|
|
62
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
63
|
+
import { HlmPhoneInput } from '@egose/shadcn-theme-ng/phone-input';
|
|
64
|
+
|
|
65
|
+
@Component({
|
|
66
|
+
selector: 'app-callback',
|
|
67
|
+
standalone: true,
|
|
68
|
+
imports: [ReactiveFormsModule, HlmPhoneInput],
|
|
69
|
+
template: `
|
|
70
|
+
<form [formGroup]="form">
|
|
71
|
+
<label for="callback-phone">Callback number</label>
|
|
72
|
+
<hlm-phone-input formControlName="phone" inputId="callback-phone" />
|
|
73
|
+
</form>
|
|
74
|
+
<p>Digits: {{ form.value.phone ?? '—' }}</p>
|
|
75
|
+
`,
|
|
76
|
+
})
|
|
77
|
+
export class CallbackComponent {
|
|
78
|
+
private readonly fb = inject(FormBuilder);
|
|
79
|
+
readonly form = this.fb.group({ phone: ['', [Validators.required, Validators.minLength(10)]] });
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Custom format (UK-style 5+6 grouping)
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
@Component({
|
|
87
|
+
selector: 'app-uk-phone',
|
|
88
|
+
standalone: true,
|
|
89
|
+
imports: [ReactiveFormsModule, HlmPhoneInput],
|
|
90
|
+
template: ` <hlm-phone-input [formControl]="control" [formatPhoneNumber]="ukFormat" [maxDigits]="11" /> `,
|
|
91
|
+
})
|
|
92
|
+
export class UkPhoneComponent {
|
|
93
|
+
readonly control = new FormControl<string | null>(null);
|
|
94
|
+
readonly ukFormat = (digits: string) => {
|
|
95
|
+
const d = digits.replace(/\D/g, '').slice(0, 11);
|
|
96
|
+
if (d.length <= 5) return d;
|
|
97
|
+
return `${d.slice(0, 5)} ${d.slice(5)}`;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Accessibility notes
|
|
103
|
+
|
|
104
|
+
- Wire a `<label for>` to `inputId`; pass `ariaLabel`/`ariaDescribedby` when composing manually (the `eg-form-phone-input` wrapper does this automatically).
|
|
105
|
+
- Keep `autocomplete="tel"` (the default) so password managers and mobile keyboards offer phone autofill.
|
|
106
|
+
|
|
107
|
+
## Theming / CSS variables
|
|
108
|
+
|
|
109
|
+
No component-specific CSS variables; the inner input carries the shared `HlmInput` base. Append utilities via `class` / `inputClass`.
|
|
110
|
+
|
|
111
|
+
## Related subpaths
|
|
112
|
+
|
|
113
|
+
- `@egose/shadcn-theme-ng/form-phone-input` — `eg-form-phone-input` label/error/hint wrapper.
|
|
114
|
+
- `@egose/shadcn-theme-ng/input` — `hlmInput` directive styling the inner input.
|