@egose/shadcn-theme-ng 0.5.3 → 0.6.1
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 +131 -12
- package/autocomplete/fesm2022/autocomplete.mjs +14 -4
- package/autocomplete/types/autocomplete.d.ts +4 -1
- package/checkbox/README.md +6 -2
- package/checkbox/fesm2022/checkbox.mjs +11 -7
- package/checkbox/types/checkbox.d.ts +6 -4
- package/combobox/fesm2022/combobox.mjs +13 -7
- package/combobox/types/combobox.d.ts +8 -2
- package/data-table/README.md +174 -0
- package/data-table/fesm2022/data-table.mjs +713 -0
- package/data-table/package.json +25 -0
- package/data-table/types/data-table.d.ts +495 -0
- package/date-picker/README.md +41 -9
- package/date-picker/fesm2022/date-picker.mjs +265 -59
- package/date-picker/types/date-picker.d.ts +58 -27
- package/dropdown-menu/README.md +2 -0
- package/form-autocomplete/README.md +4 -2
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +19 -6
- package/form-autocomplete/types/form-autocomplete.d.ts +9 -3
- package/form-combobox/README.md +4 -2
- package/form-combobox/fesm2022/form-combobox.mjs +23 -6
- package/form-combobox/types/form-combobox.d.ts +10 -3
- package/form-input-otp/README.md +7 -5
- package/form-input-otp/fesm2022/form-input-otp.mjs +20 -8
- package/form-input-otp/types/form-input-otp.d.ts +9 -3
- package/form-searchable-multiselect/README.md +15 -1
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +15 -3
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +9 -1
- package/form-slider/README.md +3 -1
- package/form-slider/fesm2022/form-slider.mjs +19 -6
- package/form-slider/types/form-slider.d.ts +9 -3
- package/form-text-input/fesm2022/form-text-input.mjs +1 -1
- package/form-textarea/fesm2022/form-textarea.mjs +1 -1
- package/form-toggle/README.md +22 -18
- package/form-toggle/fesm2022/form-toggle.mjs +22 -9
- package/form-toggle/types/form-toggle.d.ts +6 -2
- package/input/README.md +7 -12
- package/input/fesm2022/input.mjs +7 -4
- package/input/types/input.d.ts +3 -1
- package/input-otp/README.md +33 -9
- package/input-otp/fesm2022/input-otp.mjs +127 -11
- package/input-otp/types/input-otp.d.ts +24 -4
- package/layout-simple/fesm2022/layout-simple.mjs +1 -1
- package/package.json +6 -1
- package/pagination/README.md +21 -12
- package/pagination/fesm2022/pagination.mjs +73 -65
- package/pagination/types/pagination.d.ts +21 -8
- package/phone-input/README.md +16 -0
- package/phone-input/fesm2022/phone-input.mjs +42 -4
- package/phone-input/types/phone-input.d.ts +5 -2
- package/searchable-multiselect/README.md +62 -40
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +59 -32
- package/searchable-multiselect/types/searchable-multiselect.d.ts +23 -6
- package/slider/fesm2022/slider.mjs +9 -5
- package/slider/types/slider.d.ts +6 -2
- package/stepper/README.md +39 -19
- package/stepper/fesm2022/stepper.mjs +64 -12
- package/stepper/types/stepper.d.ts +12 -1
- package/table/README.md +2 -0
- package/table/fesm2022/table.mjs +12 -8
- package/table/types/table.d.ts +7 -2
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, ChangeDetectionStrategy, Component,
|
|
2
|
+
import { Directive, input, booleanAttribute, forwardRef, ChangeDetectionStrategy, Component, inject, computed, numberAttribute, NgModule } from '@angular/core';
|
|
3
3
|
import { classes } from '@egose/shadcn-theme-ng/utils';
|
|
4
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
+
import * as i1 from '@spartan-ng/brain/field';
|
|
6
|
+
import { BrnFieldControlDescribedBy, provideBrnLabelable, BrnFieldControl } from '@spartan-ng/brain/field';
|
|
7
|
+
import { BrnInputOtp, BrnInputOtpSlot } from '@spartan-ng/brain/input-otp';
|
|
4
8
|
import { NgIcon, provideIcons } from '@ng-icons/core';
|
|
5
9
|
import { lucideMinus } from '@ng-icons/lucide';
|
|
6
|
-
import { BrnInputOtpSlot } from '@spartan-ng/brain/input-otp';
|
|
7
10
|
|
|
8
11
|
class HlmInputOtp {
|
|
9
12
|
constructor() {
|
|
@@ -20,6 +23,83 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
20
23
|
}]
|
|
21
24
|
}], ctorParameters: () => [] });
|
|
22
25
|
|
|
26
|
+
/**
|
|
27
|
+
* BrnInputOtp's CVA and editing behavior with declarative native-input ARIA forwarding.
|
|
28
|
+
* Use with HlmInputOtpSlot; the legacy brn-input-otp skin remains available.
|
|
29
|
+
*/
|
|
30
|
+
class HlmInputOtpControl extends BrnInputOtp {
|
|
31
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
32
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
33
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
34
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
35
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: HlmInputOtpControl, isStandalone: true, selector: "hlm-input-otp", inputs: { ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
36
|
+
{ provide: BrnInputOtp, useExisting: forwardRef(() => HlmInputOtpControl) },
|
|
37
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HlmInputOtpControl), multi: true },
|
|
38
|
+
provideBrnLabelable(HlmInputOtpControl),
|
|
39
|
+
], usesInheritance: true, hostDirectives: [{ directive: i1.BrnFieldControl }, { directive: HlmInputOtp }], ngImport: i0, template: `
|
|
40
|
+
<ng-content />
|
|
41
|
+
<div [style]="containerStyles()">
|
|
42
|
+
<input
|
|
43
|
+
#otpInput
|
|
44
|
+
data-slot="input-otp"
|
|
45
|
+
[id]="inputId()"
|
|
46
|
+
[class]="inputClass()"
|
|
47
|
+
[autocomplete]="inputAutocomplete()"
|
|
48
|
+
[style]="inputStyles()"
|
|
49
|
+
[disabled]="_disabled()"
|
|
50
|
+
[inputMode]="inputMode()"
|
|
51
|
+
[value]="value() ?? ''"
|
|
52
|
+
brnFieldControlDescribedBy
|
|
53
|
+
[aria-describedby]="ariaDescribedBy()"
|
|
54
|
+
[attr.aria-required]="required() || null"
|
|
55
|
+
[attr.aria-invalid]="invalid?.() ? 'true' : null"
|
|
56
|
+
(input)="onInputChange($event)"
|
|
57
|
+
(paste)="onPaste($event)"
|
|
58
|
+
(focus)="_focused.set(true)"
|
|
59
|
+
(blur)="onBlur()"
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
63
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpControl, decorators: [{
|
|
65
|
+
type: Component,
|
|
66
|
+
args: [{
|
|
67
|
+
selector: 'hlm-input-otp',
|
|
68
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
69
|
+
imports: [BrnFieldControlDescribedBy],
|
|
70
|
+
hostDirectives: [BrnFieldControl, HlmInputOtp],
|
|
71
|
+
providers: [
|
|
72
|
+
{ provide: BrnInputOtp, useExisting: forwardRef(() => HlmInputOtpControl) },
|
|
73
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HlmInputOtpControl), multi: true },
|
|
74
|
+
provideBrnLabelable(HlmInputOtpControl),
|
|
75
|
+
],
|
|
76
|
+
template: `
|
|
77
|
+
<ng-content />
|
|
78
|
+
<div [style]="containerStyles()">
|
|
79
|
+
<input
|
|
80
|
+
#otpInput
|
|
81
|
+
data-slot="input-otp"
|
|
82
|
+
[id]="inputId()"
|
|
83
|
+
[class]="inputClass()"
|
|
84
|
+
[autocomplete]="inputAutocomplete()"
|
|
85
|
+
[style]="inputStyles()"
|
|
86
|
+
[disabled]="_disabled()"
|
|
87
|
+
[inputMode]="inputMode()"
|
|
88
|
+
[value]="value() ?? ''"
|
|
89
|
+
brnFieldControlDescribedBy
|
|
90
|
+
[aria-describedby]="ariaDescribedBy()"
|
|
91
|
+
[attr.aria-required]="required() || null"
|
|
92
|
+
[attr.aria-invalid]="invalid?.() ? 'true' : null"
|
|
93
|
+
(input)="onInputChange($event)"
|
|
94
|
+
(paste)="onPaste($event)"
|
|
95
|
+
(focus)="_focused.set(true)"
|
|
96
|
+
(blur)="onBlur()"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
`,
|
|
100
|
+
}]
|
|
101
|
+
}], propDecorators: { ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
|
|
102
|
+
|
|
23
103
|
class HlmInputOtpFakeCaret {
|
|
24
104
|
constructor() {
|
|
25
105
|
classes(() => "pointer-events-none absolute inset-0 flex items-center justify-center");
|
|
@@ -74,16 +154,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
74
154
|
}], ctorParameters: () => [] });
|
|
75
155
|
|
|
76
156
|
class HlmInputOtpSlot {
|
|
157
|
+
_control = inject(HlmInputOtpControl, { optional: true });
|
|
158
|
+
_slot = computed(() => this._control?.context()[this.index()], /* @ts-ignore */
|
|
159
|
+
...(ngDevMode ? [{ debugName: "_slot" }] : /* istanbul ignore next */ []));
|
|
77
160
|
/** The index of the slot to render the char or a fake caret */
|
|
78
161
|
index = input.required({ ...(ngDevMode ? { debugName: "index" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
79
162
|
constructor() {
|
|
80
|
-
classes(() => "dark:bg-input/30 border-input has-[
|
|
163
|
+
classes(() => "dark:bg-input/30 border-input has-[[data-active=\"true\"]]:border-ring has-[[data-active=\"true\"]]:ring-ring/50 has-[[data-active=\"true\"]]:data-[matches-spartan-invalid=true]:ring-destructive/20 dark:has-[[data-active=\"true\"]]:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive has-[[data-active=\"true\"]]:data-[matches-spartan-invalid=true]:border-destructive size-9 border-y border-e text-sm shadow-xs transition-all outline-none first:rounded-s-md first:border-s last:rounded-e-md has-[[data-active=\"true\"]]:ring-3 relative flex items-center justify-center has-[[data-active=\"true\"]]:z-10");
|
|
81
164
|
}
|
|
82
165
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpSlot, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
83
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
84
|
-
|
|
85
|
-
<
|
|
86
|
-
|
|
166
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmInputOtpSlot, isStandalone: true, selector: "hlm-input-otp-slot", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "data-slot": "input-otp-slot" } }, ngImport: i0, template: `
|
|
167
|
+
@if (_control) {
|
|
168
|
+
<span
|
|
169
|
+
[attr.data-active]="_slot()?.isActive"
|
|
170
|
+
[attr.data-invalid]="_control.invalid?.() ? 'true' : null"
|
|
171
|
+
[attr.data-touched]="_control.touched?.() ? 'true' : null"
|
|
172
|
+
[attr.data-dirty]="_control.dirty?.() ? 'true' : null"
|
|
173
|
+
[attr.data-matches-spartan-invalid]="_control.spartanInvalid?.() ? 'true' : null"
|
|
174
|
+
>
|
|
175
|
+
{{ _slot()?.char }}
|
|
176
|
+
@if (_slot()?.hasFakeCaret) {
|
|
177
|
+
<hlm-input-otp-fake-caret />
|
|
178
|
+
}
|
|
179
|
+
</span>
|
|
180
|
+
} @else {
|
|
181
|
+
<brn-input-otp-slot [index]="index()">
|
|
182
|
+
<hlm-input-otp-fake-caret />
|
|
183
|
+
</brn-input-otp-slot>
|
|
184
|
+
}
|
|
87
185
|
`, isInline: true, dependencies: [{ kind: "component", type: BrnInputOtpSlot, selector: "brn-input-otp-slot", inputs: ["index", "forceInvalid"] }, { kind: "component", type: HlmInputOtpFakeCaret, selector: "hlm-input-otp-fake-caret" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
88
186
|
}
|
|
89
187
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpSlot, decorators: [{
|
|
@@ -94,9 +192,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
94
192
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
95
193
|
host: { 'data-slot': 'input-otp-slot' },
|
|
96
194
|
template: `
|
|
97
|
-
|
|
98
|
-
<
|
|
99
|
-
|
|
195
|
+
@if (_control) {
|
|
196
|
+
<span
|
|
197
|
+
[attr.data-active]="_slot()?.isActive"
|
|
198
|
+
[attr.data-invalid]="_control.invalid?.() ? 'true' : null"
|
|
199
|
+
[attr.data-touched]="_control.touched?.() ? 'true' : null"
|
|
200
|
+
[attr.data-dirty]="_control.dirty?.() ? 'true' : null"
|
|
201
|
+
[attr.data-matches-spartan-invalid]="_control.spartanInvalid?.() ? 'true' : null"
|
|
202
|
+
>
|
|
203
|
+
{{ _slot()?.char }}
|
|
204
|
+
@if (_slot()?.hasFakeCaret) {
|
|
205
|
+
<hlm-input-otp-fake-caret />
|
|
206
|
+
}
|
|
207
|
+
</span>
|
|
208
|
+
} @else {
|
|
209
|
+
<brn-input-otp-slot [index]="index()">
|
|
210
|
+
<hlm-input-otp-fake-caret />
|
|
211
|
+
</brn-input-otp-slot>
|
|
212
|
+
}
|
|
100
213
|
`,
|
|
101
214
|
}]
|
|
102
215
|
}], ctorParameters: () => [], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }] } });
|
|
@@ -106,6 +219,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
106
219
|
*/
|
|
107
220
|
const HlmInputOtpImports = [
|
|
108
221
|
HlmInputOtp,
|
|
222
|
+
HlmInputOtpControl,
|
|
109
223
|
HlmInputOtpGroup,
|
|
110
224
|
HlmInputOtpSeparator,
|
|
111
225
|
HlmInputOtpSlot,
|
|
@@ -114,10 +228,12 @@ const HlmInputOtpImports = [
|
|
|
114
228
|
class HlmInputOtpModule {
|
|
115
229
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
116
230
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: HlmInputOtpModule, imports: [HlmInputOtp,
|
|
231
|
+
HlmInputOtpControl,
|
|
117
232
|
HlmInputOtpGroup,
|
|
118
233
|
HlmInputOtpSeparator,
|
|
119
234
|
HlmInputOtpSlot,
|
|
120
235
|
HlmInputOtpFakeCaret], exports: [HlmInputOtp,
|
|
236
|
+
HlmInputOtpControl,
|
|
121
237
|
HlmInputOtpGroup,
|
|
122
238
|
HlmInputOtpSeparator,
|
|
123
239
|
HlmInputOtpSlot,
|
|
@@ -137,5 +253,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
137
253
|
* Generated bundle index. Do not edit.
|
|
138
254
|
*/
|
|
139
255
|
|
|
140
|
-
export { HlmInputOtp, HlmInputOtpFakeCaret, HlmInputOtpGroup, HlmInputOtpImports, HlmInputOtpModule, HlmInputOtpSeparator, HlmInputOtpSlot };
|
|
256
|
+
export { HlmInputOtp, HlmInputOtpControl, HlmInputOtpFakeCaret, HlmInputOtpGroup, HlmInputOtpImports, HlmInputOtpModule, HlmInputOtpSeparator, HlmInputOtpSlot };
|
|
141
257
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { NumberInput } from '@angular/cdk/coercion';
|
|
2
|
+
import { BooleanInput, NumberInput } from '@angular/cdk/coercion';
|
|
3
|
+
import { BrnInputOtp } from '@spartan-ng/brain/input-otp';
|
|
4
|
+
import * as i1 from '@spartan-ng/brain/field';
|
|
3
5
|
|
|
4
6
|
declare class HlmInputOtp {
|
|
5
7
|
constructor();
|
|
@@ -7,6 +9,18 @@ declare class HlmInputOtp {
|
|
|
7
9
|
static ɵdir: i0.ɵɵDirectiveDeclaration<HlmInputOtp, "brn-input-otp[hlmInputOtp], brn-input-otp[hlm]", never, {}, {}, never, never, true, never>;
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* BrnInputOtp's CVA and editing behavior with declarative native-input ARIA forwarding.
|
|
14
|
+
* Use with HlmInputOtpSlot; the legacy brn-input-otp skin remains available.
|
|
15
|
+
*/
|
|
16
|
+
declare class HlmInputOtpControl extends BrnInputOtp {
|
|
17
|
+
/** Native input description IDs, merged with enclosing Spartan field descriptions. */
|
|
18
|
+
readonly ariaDescribedBy: i0.InputSignal<string | null>;
|
|
19
|
+
readonly required: i0.InputSignalWithTransform<boolean, BooleanInput>;
|
|
20
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<HlmInputOtpControl, never>;
|
|
21
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<HlmInputOtpControl, "hlm-input-otp", never, { "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, [{ directive: typeof i1.BrnFieldControl; inputs: {}; outputs: {}; }, { directive: typeof HlmInputOtp; inputs: {}; outputs: {}; }]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
declare class HlmInputOtpFakeCaret {
|
|
11
25
|
constructor();
|
|
12
26
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmInputOtpFakeCaret, never>;
|
|
@@ -26,6 +40,12 @@ declare class HlmInputOtpSeparator {
|
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
declare class HlmInputOtpSlot {
|
|
43
|
+
protected readonly _control: HlmInputOtpControl | null;
|
|
44
|
+
protected readonly _slot: i0.Signal<{
|
|
45
|
+
char: string | null;
|
|
46
|
+
isActive: boolean;
|
|
47
|
+
hasFakeCaret: boolean;
|
|
48
|
+
} | undefined>;
|
|
29
49
|
/** The index of the slot to render the char or a fake caret */
|
|
30
50
|
readonly index: i0.InputSignalWithTransform<number, NumberInput>;
|
|
31
51
|
constructor();
|
|
@@ -33,11 +53,11 @@ declare class HlmInputOtpSlot {
|
|
|
33
53
|
static ɵcmp: i0.ɵɵComponentDeclaration<HlmInputOtpSlot, "hlm-input-otp-slot", never, { "index": { "alias": "index"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
34
54
|
}
|
|
35
55
|
|
|
36
|
-
declare const HlmInputOtpImports: readonly [typeof HlmInputOtp, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret];
|
|
56
|
+
declare const HlmInputOtpImports: readonly [typeof HlmInputOtp, typeof HlmInputOtpControl, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret];
|
|
37
57
|
declare class HlmInputOtpModule {
|
|
38
58
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmInputOtpModule, never>;
|
|
39
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<HlmInputOtpModule, never, [typeof HlmInputOtp, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret], [typeof HlmInputOtp, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret]>;
|
|
59
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<HlmInputOtpModule, never, [typeof HlmInputOtp, typeof HlmInputOtpControl, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret], [typeof HlmInputOtp, typeof HlmInputOtpControl, typeof HlmInputOtpGroup, typeof HlmInputOtpSeparator, typeof HlmInputOtpSlot, typeof HlmInputOtpFakeCaret]>;
|
|
40
60
|
static ɵinj: i0.ɵɵInjectorDeclaration<HlmInputOtpModule>;
|
|
41
61
|
}
|
|
42
62
|
|
|
43
|
-
export { HlmInputOtp, HlmInputOtpFakeCaret, HlmInputOtpGroup, HlmInputOtpImports, HlmInputOtpModule, HlmInputOtpSeparator, HlmInputOtpSlot };
|
|
63
|
+
export { HlmInputOtp, HlmInputOtpControl, HlmInputOtpFakeCaret, HlmInputOtpGroup, HlmInputOtpImports, HlmInputOtpModule, HlmInputOtpSeparator, HlmInputOtpSlot };
|
|
@@ -169,7 +169,7 @@ class EgLayoutSearch {
|
|
|
169
169
|
buttons[Math.min(index, buttons.length - 1)]?.nativeElement.focus();
|
|
170
170
|
}
|
|
171
171
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
172
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSearch, isStandalone: true, selector: "eg-layout-search", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, retryText: { classPropertyName: "retryText", publicName: "retryText", isSignal: true, isRequired: false, transformFunction: null }, loader: { classPropertyName: "loader", publicName: "loader", isSignal: true, isRequired: false, transformFunction: null }, resultLabel: { classPropertyName: "resultLabel", publicName: "resultLabel", isSignal: true, isRequired: false, transformFunction: null }, resultTemplate: { classPropertyName: "resultTemplate", publicName: "resultTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resultSelected: "resultSelected" }, host: { classAttribute: "block min-w-0" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "resultButtons", predicate: ["resultButton"], descendants: true, isSignal: true }], ngImport: i0, template: "\n <hlm-popover\n [state]=\"isOpen() ? 'open' : 'closed'\"\n (stateChanged)=\"setOpen($event === 'open')\"\n (closed)=\"setOpen(false)\"\n >\n <button\n hlmPopoverTrigger\n type=\"button\"\n class=\"flex min-h-11 w-full items-center gap-2 rounded-lg border border-border bg-foreground/3 px-3 text-left text-sm text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring md:w-56\"\n >\n <ng-icon [svg]=\"searchIcon\" size=\"16px\" aria-hidden=\"true\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ placeholder() }}</span>\n </button>\n <hlm-popover-content *hlmPopoverPortal class=\"w-80 max-w-[calc(100vw-2rem)] p-2\">\n <input\n #searchInput\n hlmInput\n type=\"search\"\n autocomplete=\"off\"\n [value]=\"query()\"\n (input)=\"query.set($any($event.target).value)\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"placeholder()\"\n (keydown.arrowdown)=\"focusResult(0, $event)\"\n class=\"mb-2 min-h-11\"\n />\n <div [attr.aria-busy]=\"isLoading()\" class=\"max-h-72 overflow-y-auto overscroll-contain\">\n @if (isLoading()) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ loadingText() }}\n </p>\n } @else if (hasError()) {\n <div role=\"status\" class=\"space-y-3 px-3 py-4 text-sm\">\n <p>{{ errorText() }}</p>\n <button\n type=\"button\"\n (click)=\"retry()\"\n class=\"min-h-11 rounded-md border border-border px-3 font-medium cursor-pointer focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ retryText() }}\n </button>\n </div>\n } @else if (!results().length) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ emptyText() }}\n </p>\n } @else {\n @for (result of results(); track $index; let index = $index) {\n <button\n #resultButton\n type=\"button\"\n (click)=\"selectResult(result)\"\n (keydown.arrowdown)=\"focusResult(index + 1, $event)\"\n (keydown.arrowup)=\"focusResult(index - 1, $event)\"\n (keydown.home)=\"focusResult(0, $event)\"\n (keydown.end)=\"focusResult(results().length - 1, $event)\"\n class=\"flex min-h-11 w-full items-center rounded-md px-3 py-2 text-left text-sm cursor-pointer hover:bg-foreground/5 focus-visible:bg-foreground/10 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n >\n @if (resultTemplate(); as template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: result }\" />\n } @else {\n {{ labelFor(result) }}\n }\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ", isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "directive", type: HlmPopoverPortal, selector: "[hlmPopoverPortal]" }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
172
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSearch, isStandalone: true, selector: "eg-layout-search", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, retryText: { classPropertyName: "retryText", publicName: "retryText", isSignal: true, isRequired: false, transformFunction: null }, loader: { classPropertyName: "loader", publicName: "loader", isSignal: true, isRequired: false, transformFunction: null }, resultLabel: { classPropertyName: "resultLabel", publicName: "resultLabel", isSignal: true, isRequired: false, transformFunction: null }, resultTemplate: { classPropertyName: "resultTemplate", publicName: "resultTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resultSelected: "resultSelected" }, host: { classAttribute: "block min-w-0" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "resultButtons", predicate: ["resultButton"], descendants: true, isSignal: true }], ngImport: i0, template: "\n <hlm-popover\n [state]=\"isOpen() ? 'open' : 'closed'\"\n (stateChanged)=\"setOpen($event === 'open')\"\n (closed)=\"setOpen(false)\"\n >\n <button\n hlmPopoverTrigger\n type=\"button\"\n class=\"flex min-h-11 w-full items-center gap-2 rounded-lg border border-border bg-foreground/3 px-3 text-left text-sm text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring md:w-56\"\n >\n <ng-icon [svg]=\"searchIcon\" size=\"16px\" aria-hidden=\"true\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ placeholder() }}</span>\n </button>\n <hlm-popover-content *hlmPopoverPortal class=\"w-80 max-w-[calc(100vw-2rem)] p-2\">\n <input\n #searchInput\n hlmInput\n type=\"search\"\n autocomplete=\"off\"\n [value]=\"query()\"\n (input)=\"query.set($any($event.target).value)\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"placeholder()\"\n (keydown.arrowdown)=\"focusResult(0, $event)\"\n class=\"mb-2 min-h-11\"\n />\n <div [attr.aria-busy]=\"isLoading()\" class=\"max-h-72 overflow-y-auto overscroll-contain\">\n @if (isLoading()) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ loadingText() }}\n </p>\n } @else if (hasError()) {\n <div role=\"status\" class=\"space-y-3 px-3 py-4 text-sm\">\n <p>{{ errorText() }}</p>\n <button\n type=\"button\"\n (click)=\"retry()\"\n class=\"min-h-11 rounded-md border border-border px-3 font-medium cursor-pointer focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ retryText() }}\n </button>\n </div>\n } @else if (!results().length) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ emptyText() }}\n </p>\n } @else {\n @for (result of results(); track $index; let index = $index) {\n <button\n #resultButton\n type=\"button\"\n (click)=\"selectResult(result)\"\n (keydown.arrowdown)=\"focusResult(index + 1, $event)\"\n (keydown.arrowup)=\"focusResult(index - 1, $event)\"\n (keydown.home)=\"focusResult(0, $event)\"\n (keydown.end)=\"focusResult(results().length - 1, $event)\"\n class=\"flex min-h-11 w-full items-center rounded-md px-3 py-2 text-left text-sm cursor-pointer hover:bg-foreground/5 focus-visible:bg-foreground/10 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n >\n @if (resultTemplate(); as template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: result }\" />\n } @else {\n {{ labelFor(result) }}\n }\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ", isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "directive", type: HlmPopoverPortal, selector: "[hlmPopoverPortal]" }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby", "size"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
173
173
|
}
|
|
174
174
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSearch, decorators: [{
|
|
175
175
|
type: Component,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@egose/shadcn-theme-ng",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Angular shadcn UI theme",
|
|
5
5
|
"author": "Junmin Ahn",
|
|
6
6
|
"bundles": [
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@angular/router": ">=22.0.0 <23.0.0",
|
|
32
32
|
"@ng-icons/core": ">=35.0.1 <36.0.0",
|
|
33
33
|
"@spartan-ng/brain": ">=1.3.2 <2.0.0",
|
|
34
|
+
"@tanstack/angular-table": ">=9.1.2 <10.0.0",
|
|
34
35
|
"rxjs": ">=7.8.0 <8.0.0"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
@@ -126,6 +127,10 @@
|
|
|
126
127
|
"types": "./context-menu/types/context-menu.d.ts",
|
|
127
128
|
"default": "./context-menu/fesm2022/context-menu.mjs"
|
|
128
129
|
},
|
|
130
|
+
"./data-table": {
|
|
131
|
+
"types": "./data-table/types/data-table.d.ts",
|
|
132
|
+
"default": "./data-table/fesm2022/data-table.mjs"
|
|
133
|
+
},
|
|
129
134
|
"./date-picker": {
|
|
130
135
|
"types": "./date-picker/types/date-picker.d.ts",
|
|
131
136
|
"default": "./date-picker/fesm2022/date-picker.mjs"
|
package/pagination/README.md
CHANGED
|
@@ -144,12 +144,21 @@ For the `tw:` build, swap the specifier to `@egose/shadcn-theme-ng-tw/pagination
|
|
|
144
144
|
| `currentPage` | **model (required)** | `number` | Two-way: `[(currentPage)]`. Clicks set it; out-of-range values are auto-corrected via `outOfBoundCorrection` |
|
|
145
145
|
| `itemsPerPage` | **model (required)** | `number` | Two-way: `[(itemsPerPage)]`, bound to the page-size `hlm-select` |
|
|
146
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) |
|
|
147
|
+
| `maxSize` | input | `number`, default `7` (number coercion) | Window entries including ellipses; floored and bounded to 1–100; invalid/non-positive uses 7 |
|
|
148
148
|
| `showEdges` | input | `boolean`, default `true` (boolean coercion) | Shows prev/next edge buttons (hidden on first/last page respectively) |
|
|
149
149
|
| `pageSizes` | input | `number[]`, default `[10, 20, 50, 100]` | Page-size menu; the current size is merged in + sorted if missing |
|
|
150
150
|
|
|
151
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
152
|
|
|
153
|
+
#### Normalization and numeric limits (both numbered pagers)
|
|
154
|
+
|
|
155
|
+
- Page count is `ceil(totalItems / itemsPerPage)`, at least 1 and at most `Number.MAX_SAFE_INTEGER`. Finite positive fractional totals/sizes retain that division behavior, including sizes below 1. Overflowing positive division saturates at the safe-integer page limit.
|
|
156
|
+
- Empty, negative or non-finite totals use one page. Non-positive or non-finite sizes (`0`, negative, `NaN`, `Infinity`) also use one page, **without rewriting `itemsPerPage`**. Supply a finite positive size to resume paging. The models are numeric; template number attributes such as `totalItems` and `maxSize` are coerced by Angular.
|
|
157
|
+
- Finite `currentPage` values are floored and clamped to `[1, lastPage]`; non-finite values become 1. Changes to totals, size or the external page are reconciled by an effect, emitting `currentPageChange` once per correction. Valid models emit nothing. Bind `[(currentPage)]` to receive corrections.
|
|
158
|
+
- All derived signals, including the page list, are side-effect-free. Rendering uses the corrected page immediately. Previous/next disappear at their bounds, and direct client-state navigation handlers are also clamped. Empty results show active page 1 and neither edge.
|
|
159
|
+
- `maxSize` / helper `paginationRange` is a window-entry budget, including ellipses. Finite positive values are floored and clamped to **1–100**; non-finite/non-positive values fall back to **7**. Ranges below 5 use a contiguous window containing the active page; larger ranges retain the first/last/ellipsis layout. Work and allocation are proportional to `min(pageCount, normalizedRange)`, never to an unbounded total or range. The array-returning helper API is unchanged.
|
|
160
|
+
- The summary displays the supplied total; normalization controls page count/navigation and does not repair the application's data. For asynchronous server loading, keep the last known total until the new count arrives if a temporary zero should not reset the page.
|
|
161
|
+
|
|
153
162
|
### `HlmNumberedPaginationQueryParams` (`hlm-numbered-pagination-query-params`)
|
|
154
163
|
|
|
155
164
|
Same models/inputs as `HlmNumberedPagination` **plus**:
|
|
@@ -160,11 +169,13 @@ Same models/inputs as `HlmNumberedPagination` **plus**:
|
|
|
160
169
|
|
|
161
170
|
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
171
|
|
|
172
|
+
The component **does not subscribe to the route or navigate on correction/size changes**. The parent reads `?page=` and supplies `currentPage`; link clicks navigate, then the parent's route subscription updates that model. Corrections emit `currentPageChange` but leave the URL unchanged. To canonicalize a corrected page in the URL, handle that output in the parent with `router.navigate([], { relativeTo: route, queryParams: { page }, queryParamsHandling: 'merge', replaceUrl: true })`. Keep unrelated parameters when doing so. Page-size URL persistence is also parent-owned.
|
|
173
|
+
|
|
163
174
|
### Helpers
|
|
164
175
|
|
|
165
176
|
| Symbol | Signature | Notes |
|
|
166
177
|
| ------------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
167
|
-
| `outOfBoundCorrection(totalItems, itemsPerPage, currentPage)` | `(number, number, number) => number` |
|
|
178
|
+
| `outOfBoundCorrection(totalItems, itemsPerPage, currentPage)` | `(number, number, number) => number` | Pure floor/clamp using the normalization policy above; empty/invalid paging returns 1 |
|
|
168
179
|
| `createPageArray(currentPage, itemsPerPage, totalItems, paginationRange)` | `(number, number, number, number) => (number \| '...')[]` | Window with `'...'` gaps; powers both numbered pagers |
|
|
169
180
|
| `Page` | `type Page = number \| '...'` | Not exported from public-api (internal to the pager file) — redeclare locally if you type custom windows |
|
|
170
181
|
|
|
@@ -246,7 +257,8 @@ export class PagerClientComponent {
|
|
|
246
257
|
### 3. Router `?page=` pager (server-driven)
|
|
247
258
|
|
|
248
259
|
```ts
|
|
249
|
-
import { Component, signal
|
|
260
|
+
import { Component, signal } from '@angular/core';
|
|
261
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
250
262
|
import { ActivatedRoute } from '@angular/router';
|
|
251
263
|
import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
252
264
|
|
|
@@ -261,7 +273,7 @@ import { HlmPaginationImports } from '@egose/shadcn-theme-ng/pagination';
|
|
|
261
273
|
[totalItems]="total()"
|
|
262
274
|
link="."
|
|
263
275
|
/>
|
|
264
|
-
<p class="tw:text-sm tw:text-muted-foreground">
|
|
276
|
+
<p class="tw:text-sm tw:text-muted-foreground">Displayed page: {{ page() }}.</p>
|
|
265
277
|
`,
|
|
266
278
|
})
|
|
267
279
|
export class PagerQueryComponent {
|
|
@@ -270,13 +282,10 @@ export class PagerQueryComponent {
|
|
|
270
282
|
readonly total = signal(320);
|
|
271
283
|
|
|
272
284
|
constructor(route: ActivatedRoute) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
this.page.set(Number.isFinite(p) && p >= 1 ? Math.floor(p) : 1);
|
|
278
|
-
});
|
|
279
|
-
return () => sub.unsubscribe();
|
|
285
|
+
// Read deep links and back/forward changes; the pager corrects model bounds.
|
|
286
|
+
// Corrections here update the displayed page, not the URL.
|
|
287
|
+
route.queryParamMap.pipe(takeUntilDestroyed()).subscribe((params) => {
|
|
288
|
+
this.page.set(Number(params.get('page') ?? 1));
|
|
280
289
|
});
|
|
281
290
|
}
|
|
282
291
|
}
|
|
@@ -330,7 +339,7 @@ import { HlmPaginationImports, createPageArray } from '@egose/shadcn-theme-ng/pa
|
|
|
330
339
|
template: `
|
|
331
340
|
<nav hlmPagination>
|
|
332
341
|
<ul hlmPaginationContent>
|
|
333
|
-
@for (p of window(); track
|
|
342
|
+
@for (p of window(); track $index) {
|
|
334
343
|
<li hlmPaginationItem>
|
|
335
344
|
@if (p === '...') {
|
|
336
345
|
<hlm-pagination-ellipsis />
|