@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.
Files changed (162) hide show
  1. package/README.md +2 -2
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  8. package/avatar/README.md +357 -5
  9. package/badge/README.md +318 -2
  10. package/basic-alert/README.md +353 -2
  11. package/breadcrumb/README.md +406 -5
  12. package/button/README.md +482 -2
  13. package/button/fesm2022/button.mjs +85 -107
  14. package/button/types/button.d.ts +5 -8
  15. package/button-group/README.md +318 -5
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/checkbox/fesm2022/checkbox.mjs +6 -7
  22. package/checkbox/types/checkbox.d.ts +1 -1
  23. package/collapsible/README.md +332 -5
  24. package/combobox/README.md +507 -5
  25. package/combobox/fesm2022/combobox.mjs +5 -2
  26. package/command/README.md +435 -5
  27. package/confirmation-dialog/README.md +301 -2
  28. package/context-menu/README.md +366 -5
  29. package/date-picker/README.md +469 -2
  30. package/date-picker/fesm2022/date-picker.mjs +150 -32
  31. package/date-picker/types/date-picker.d.ts +102 -9
  32. package/dialog/README.md +448 -2
  33. package/drawer/README.md +395 -5
  34. package/dropdown-menu/README.md +417 -5
  35. package/empty/README.md +329 -5
  36. package/field/README.md +385 -5
  37. package/form-autocomplete/README.md +177 -0
  38. package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
  39. package/form-autocomplete/package.json +24 -0
  40. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  41. package/form-checkbox/README.md +322 -2
  42. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  43. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  44. package/form-combobox/README.md +202 -0
  45. package/form-combobox/fesm2022/form-combobox.mjs +147 -0
  46. package/form-combobox/package.json +24 -0
  47. package/form-combobox/types/form-combobox.d.ts +73 -0
  48. package/form-date-picker/README.md +348 -2
  49. package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
  50. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  51. package/form-date-picker-multi/README.md +191 -0
  52. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
  53. package/form-date-picker-multi/package.json +24 -0
  54. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  55. package/form-date-range-picker/README.md +253 -0
  56. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
  57. package/form-date-range-picker/package.json +24 -0
  58. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  59. package/form-field/README.md +356 -2
  60. package/form-field-simple/README.md +340 -2
  61. package/form-input-otp/README.md +194 -0
  62. package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
  63. package/form-input-otp/package.json +24 -0
  64. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  65. package/form-month-year-picker/README.md +188 -0
  66. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
  67. package/form-month-year-picker/package.json +24 -0
  68. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  69. package/form-native-select/README.md +205 -0
  70. package/form-native-select/fesm2022/form-native-select.mjs +103 -0
  71. package/form-native-select/package.json +24 -0
  72. package/form-native-select/types/form-native-select.d.ts +57 -0
  73. package/form-phone-input/README.md +188 -0
  74. package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
  75. package/form-phone-input/package.json +24 -0
  76. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  77. package/form-radio-group/README.md +198 -0
  78. package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
  79. package/form-radio-group/package.json +24 -0
  80. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  81. package/form-searchable-multiselect/README.md +371 -2
  82. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  83. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  84. package/form-select/README.md +360 -2
  85. package/form-select/fesm2022/form-select.mjs +20 -11
  86. package/form-select/types/form-select.d.ts +18 -1
  87. package/form-slider/README.md +182 -0
  88. package/form-slider/fesm2022/form-slider.mjs +106 -0
  89. package/form-slider/package.json +24 -0
  90. package/form-slider/types/form-slider.d.ts +60 -0
  91. package/form-switch/README.md +173 -0
  92. package/form-switch/fesm2022/form-switch.mjs +100 -0
  93. package/form-switch/package.json +24 -0
  94. package/form-switch/types/form-switch.d.ts +50 -0
  95. package/form-text-input/README.md +381 -2
  96. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  97. package/form-text-input/types/form-text-input.d.ts +18 -1
  98. package/form-textarea/README.md +357 -2
  99. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  100. package/form-textarea/types/form-textarea.d.ts +18 -1
  101. package/form-toggle/README.md +186 -0
  102. package/form-toggle/fesm2022/form-toggle.mjs +159 -0
  103. package/form-toggle/package.json +24 -0
  104. package/form-toggle/types/form-toggle.d.ts +82 -0
  105. package/form-toggle-group/README.md +176 -0
  106. package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
  107. package/form-toggle-group/package.json +24 -0
  108. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  109. package/hover-card/README.md +256 -5
  110. package/icon/README.md +239 -2
  111. package/input/README.md +269 -2
  112. package/input-group/README.md +335 -5
  113. package/input-group/fesm2022/input-group.mjs +22 -12
  114. package/input-group/types/input-group.d.ts +4 -1
  115. package/input-otp/README.md +375 -5
  116. package/item/README.md +385 -5
  117. package/item/fesm2022/item.mjs +3 -3
  118. package/kbd/README.md +291 -5
  119. package/label/README.md +272 -2
  120. package/layout-simple/README.md +193 -2
  121. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  122. package/layout-simple/types/layout-simple.d.ts +174 -137
  123. package/menu/README.md +417 -2
  124. package/menubar/README.md +343 -5
  125. package/native-select/README.md +323 -5
  126. package/native-select/fesm2022/native-select.mjs +18 -6
  127. package/native-select/types/native-select.d.ts +7 -2
  128. package/navigation-menu/README.md +369 -5
  129. package/package.json +57 -1
  130. package/pagination/README.md +388 -5
  131. package/phone-input/README.md +114 -0
  132. package/phone-input/fesm2022/phone-input.mjs +191 -0
  133. package/phone-input/package.json +24 -0
  134. package/phone-input/types/phone-input.d.ts +67 -0
  135. package/popover/README.md +331 -2
  136. package/progress/README.md +311 -5
  137. package/radio-group/README.md +364 -2
  138. package/radio-group/fesm2022/radio-group.mjs +5 -1
  139. package/resizable/README.md +269 -5
  140. package/scroll-area/README.md +233 -5
  141. package/searchable-multiselect/README.md +323 -2
  142. package/select/README.md +437 -2
  143. package/separator/README.md +222 -2
  144. package/sheet/README.md +311 -2
  145. package/sidebar/README.md +457 -5
  146. package/skeleton/README.md +217 -5
  147. package/slider/README.md +273 -5
  148. package/slider/fesm2022/slider.mjs +3 -3
  149. package/sonner/README.md +346 -2
  150. package/spinner/README.md +284 -2
  151. package/switch/README.md +310 -2
  152. package/switch/fesm2022/switch.mjs +7 -5
  153. package/switch/types/switch.d.ts +2 -1
  154. package/table/README.md +423 -5
  155. package/tabs/README.md +411 -2
  156. package/tabs/fesm2022/tabs.mjs +2 -2
  157. package/textarea/README.md +282 -5
  158. package/toggle/README.md +270 -5
  159. package/toggle-group/README.md +340 -5
  160. package/tooltip/README.md +269 -2
  161. package/typography/README.md +271 -5
  162. package/utils/README.md +303 -2
@@ -0,0 +1,191 @@
1
+ import * as i0 from '@angular/core';
2
+ import { forwardRef, input, booleanAttribute, computed, signal, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+ import * as i1 from '@spartan-ng/brain/field';
5
+ import { BrnFieldControl } from '@spartan-ng/brain/field';
6
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
7
+ import { hlm } from '@egose/shadcn-theme-ng/utils';
8
+
9
+ const HLM_PHONE_INPUT_VALUE_ACCESSOR = {
10
+ provide: NG_VALUE_ACCESSOR,
11
+ useExisting: forwardRef(() => HlmPhoneInput),
12
+ multi: true,
13
+ };
14
+ function formatNanpPhoneNumber(digits) {
15
+ const d = digits.replace(/\D/g, '').slice(0, 10);
16
+ if (d.length === 0)
17
+ return '';
18
+ if (d.length <= 3)
19
+ return `(${d}`;
20
+ if (d.length <= 6)
21
+ return `(${d.slice(0, 3)}) ${d.slice(3)}`;
22
+ return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
23
+ }
24
+ /**
25
+ * A masked phone input. The form model holds raw digits (`"4155552671"`);
26
+ * the visible text is formatted (`"(415) 555-2671"`). Caret position is
27
+ * preserved across mask characters while typing or deleting.
28
+ */
29
+ class HlmPhoneInput {
30
+ static _id = 0;
31
+ inputId = input(`hlm-phone-input-${HlmPhoneInput._id++}`, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
33
+ name = input(undefined, /* @ts-ignore */
34
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
35
+ ariaLabel = input(undefined, /* @ts-ignore */
36
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
37
+ ariaDescribedby = input(null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : /* istanbul ignore next */ []));
39
+ placeholder = input('', /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
41
+ autocomplete = input('tel', /* @ts-ignore */
42
+ ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
43
+ readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
44
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
45
+ forceInvalid = input(false, { ...(ngDevMode ? { debugName: "forceInvalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
46
+ /** Maximum digit count kept in the model. Defaults to 10 (NANP). */
47
+ maxDigits = input(10, /* @ts-ignore */
48
+ ...(ngDevMode ? [{ debugName: "maxDigits" }] : /* istanbul ignore next */ []));
49
+ /**
50
+ * Shape of the form model: raw digits (`"4155552671"`, the default —
51
+ * validator- and storage-friendly) or formatted display text
52
+ * (`"(415) 555-2671"`).
53
+ */
54
+ modelFormat = input('digits', /* @ts-ignore */
55
+ ...(ngDevMode ? [{ debugName: "modelFormat" }] : /* istanbul ignore next */ []));
56
+ /** Display formatter. Receives raw digits, returns display text. */
57
+ formatPhoneNumber = input(formatNanpPhoneNumber, /* @ts-ignore */
58
+ ...(ngDevMode ? [{ debugName: "formatPhoneNumber" }] : /* istanbul ignore next */ []));
59
+ /** Extra classes merged onto the inner `<input>` (over the `HlmInput` base). */
60
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
61
+ inputClass = input('', /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "inputClass" }] : /* istanbul ignore next */ []));
63
+ $inputClass = computed(() => hlm(this.userClass(), this.inputClass()), /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "$inputClass" }] : /* istanbul ignore next */ []));
65
+ _formDisabled = signal(false, /* @ts-ignore */
66
+ ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
67
+ disabledState = computed(() => this.disabled() || this._formDisabled(), /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
69
+ /** Raw digits held in the form model. */
70
+ _digits = signal('', /* @ts-ignore */
71
+ ...(ngDevMode ? [{ debugName: "_digits" }] : /* istanbul ignore next */ []));
72
+ /** Formatted text shown in the input. */
73
+ _display = computed(() => this.formatPhoneNumber()(this._digits()), /* @ts-ignore */
74
+ ...(ngDevMode ? [{ debugName: "_display" }] : /* istanbul ignore next */ []));
75
+ _onChange;
76
+ _onTouched;
77
+ _emitModel(digits) {
78
+ if (digits === this._digits())
79
+ return;
80
+ this._digits.set(digits);
81
+ if (digits === '') {
82
+ this._onChange?.(null);
83
+ }
84
+ else if (this.modelFormat() === 'formatted') {
85
+ this._onChange?.(this.formatPhoneNumber()(digits));
86
+ }
87
+ else {
88
+ this._onChange?.(digits);
89
+ }
90
+ }
91
+ _handleInput(event) {
92
+ const element = event.target;
93
+ const caret = element.selectionStart ?? element.value.length;
94
+ const digitsBeforeCaret = element.value.slice(0, caret).replace(/\D/g, '').length;
95
+ const digits = element.value.replace(/\D/g, '').slice(0, Math.max(0, this.maxDigits()));
96
+ const formatted = this.formatPhoneNumber()(digits);
97
+ const target = Math.min(digitsBeforeCaret, digits.length);
98
+ let position = 0;
99
+ let seen = 0;
100
+ while (position < formatted.length && seen < target) {
101
+ if (/\d/.test(formatted[position]))
102
+ seen++;
103
+ position++;
104
+ }
105
+ element.value = formatted;
106
+ element.setSelectionRange(position, position);
107
+ this._emitModel(digits);
108
+ }
109
+ _handleBlur() {
110
+ this._onTouched?.();
111
+ }
112
+ /** CONTROL VALUE ACCESSOR */
113
+ writeValue(value) {
114
+ this._digits.set(String(value ?? '')
115
+ .replace(/\D/g, '')
116
+ .slice(0, Math.max(0, this.maxDigits())));
117
+ }
118
+ registerOnChange(fn) {
119
+ this._onChange = fn;
120
+ }
121
+ registerOnTouched(fn) {
122
+ this._onTouched = fn;
123
+ }
124
+ setDisabledState(isDisabled) {
125
+ this._formDisabled.set(isDisabled);
126
+ }
127
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmPhoneInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
128
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: HlmPhoneInput, isStandalone: true, selector: "hlm-phone-input", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, maxDigits: { classPropertyName: "maxDigits", publicName: "maxDigits", isSignal: true, isRequired: false, transformFunction: null }, modelFormat: { classPropertyName: "modelFormat", publicName: "modelFormat", isSignal: true, isRequired: false, transformFunction: null }, formatPhoneNumber: { classPropertyName: "formatPhoneNumber", publicName: "formatPhoneNumber", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-disabled": "disabledState() ? \"\" : null" }, classAttribute: "block w-full min-w-0" }, providers: [HLM_PHONE_INPUT_VALUE_ACCESSOR], hostDirectives: [{ directive: i1.BrnFieldControl }], ngImport: i0, template: `
129
+ <input
130
+ #input
131
+ hlmInput
132
+ [id]="inputId()"
133
+ [attr.name]="name()"
134
+ [attr.aria-label]="ariaLabel()"
135
+ [attr.aria-describedby]="ariaDescribedby()"
136
+ [placeholder]="placeholder()"
137
+ [autocomplete]="autocomplete()"
138
+ [readonly]="readonly()"
139
+ [disabled]="disabledState()"
140
+ [forceInvalid]="forceInvalid()"
141
+ [class]="$inputClass()"
142
+ [value]="_display()"
143
+ (input)="_handleInput($event)"
144
+ (blur)="_handleBlur()"
145
+ />
146
+ `, isInline: true, dependencies: [{ kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
147
+ }
148
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmPhoneInput, decorators: [{
149
+ type: Component,
150
+ args: [{
151
+ selector: 'hlm-phone-input',
152
+ imports: [HlmInput],
153
+ providers: [HLM_PHONE_INPUT_VALUE_ACCESSOR],
154
+ changeDetection: ChangeDetectionStrategy.OnPush,
155
+ hostDirectives: [BrnFieldControl],
156
+ host: {
157
+ class: "block w-full min-w-0",
158
+ '[attr.data-disabled]': 'disabledState() ? "" : null',
159
+ },
160
+ template: `
161
+ <input
162
+ #input
163
+ hlmInput
164
+ [id]="inputId()"
165
+ [attr.name]="name()"
166
+ [attr.aria-label]="ariaLabel()"
167
+ [attr.aria-describedby]="ariaDescribedby()"
168
+ [placeholder]="placeholder()"
169
+ [autocomplete]="autocomplete()"
170
+ [readonly]="readonly()"
171
+ [disabled]="disabledState()"
172
+ [forceInvalid]="forceInvalid()"
173
+ [class]="$inputClass()"
174
+ [value]="_display()"
175
+ (input)="_handleInput($event)"
176
+ (blur)="_handleBlur()"
177
+ />
178
+ `,
179
+ }]
180
+ }], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], forceInvalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceInvalid", required: false }] }], maxDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDigits", required: false }] }], modelFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "modelFormat", required: false }] }], formatPhoneNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatPhoneNumber", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], inputClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClass", required: false }] }] } });
181
+
182
+ /*
183
+ * Public API Surface of phone-input
184
+ */
185
+
186
+ /**
187
+ * Generated bundle index. Do not edit.
188
+ */
189
+
190
+ export { HLM_PHONE_INPUT_VALUE_ACCESSOR, HlmPhoneInput, formatNanpPhoneNumber };
191
+
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "phone-input",
3
+ "version": "0.0.1",
4
+ "peerDependencies": {
5
+ "@angular/common": "^20.3.0 || ^22.0.0",
6
+ "@angular/core": "^20.3.0 || ^22.0.0"
7
+ },
8
+ "dependencies": {
9
+ "tslib": "^2.3.0"
10
+ },
11
+ "sideEffects": false,
12
+ "module": "fesm2022/phone-input.mjs",
13
+ "typings": "types/phone-input.d.ts",
14
+ "exports": {
15
+ "./package.json": {
16
+ "default": "./package.json"
17
+ },
18
+ ".": {
19
+ "types": "./types/phone-input.d.ts",
20
+ "default": "./fesm2022/phone-input.mjs"
21
+ }
22
+ },
23
+ "type": "module"
24
+ }
@@ -0,0 +1,67 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { ControlValueAccessor } from '@angular/forms';
3
+ import { BooleanInput } from '@angular/cdk/coercion';
4
+ import { ClassValue } from 'clsx';
5
+ import { ChangeFn, TouchFn } from '@spartan-ng/brain/forms';
6
+ import * as i1 from '@spartan-ng/brain/field';
7
+
8
+ declare const HLM_PHONE_INPUT_VALUE_ACCESSOR: {
9
+ provide: _angular_core.InjectionToken<readonly ControlValueAccessor[]>;
10
+ useExisting: _angular_core.Type<any>;
11
+ multi: boolean;
12
+ };
13
+ /** Formats digit strings for display. Defaults to NANP `(XXX) XXX-XXXX` with an open paren while typing. */
14
+ type PhoneNumberFormatter = (digits: string) => string;
15
+ declare function formatNanpPhoneNumber(digits: string): string;
16
+ /**
17
+ * A masked phone input. The form model holds raw digits (`"4155552671"`);
18
+ * the visible text is formatted (`"(415) 555-2671"`). Caret position is
19
+ * preserved across mask characters while typing or deleting.
20
+ */
21
+ declare class HlmPhoneInput implements ControlValueAccessor {
22
+ private static _id;
23
+ readonly inputId: _angular_core.InputSignal<string>;
24
+ readonly name: _angular_core.InputSignal<string | undefined>;
25
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
26
+ readonly ariaDescribedby: _angular_core.InputSignal<string | null>;
27
+ readonly placeholder: _angular_core.InputSignal<string>;
28
+ readonly autocomplete: _angular_core.InputSignal<string>;
29
+ readonly readonly: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
30
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
31
+ readonly forceInvalid: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
32
+ /** Maximum digit count kept in the model. Defaults to 10 (NANP). */
33
+ readonly maxDigits: _angular_core.InputSignal<number>;
34
+ /**
35
+ * Shape of the form model: raw digits (`"4155552671"`, the default —
36
+ * validator- and storage-friendly) or formatted display text
37
+ * (`"(415) 555-2671"`).
38
+ */
39
+ readonly modelFormat: _angular_core.InputSignal<"digits" | "formatted">;
40
+ /** Display formatter. Receives raw digits, returns display text. */
41
+ readonly formatPhoneNumber: _angular_core.InputSignal<PhoneNumberFormatter>;
42
+ /** Extra classes merged onto the inner `<input>` (over the `HlmInput` base). */
43
+ readonly userClass: _angular_core.InputSignal<ClassValue>;
44
+ readonly inputClass: _angular_core.InputSignal<ClassValue>;
45
+ protected readonly $inputClass: _angular_core.Signal<string>;
46
+ private readonly _formDisabled;
47
+ protected readonly disabledState: _angular_core.Signal<boolean>;
48
+ /** Raw digits held in the form model. */
49
+ protected readonly _digits: _angular_core.WritableSignal<string>;
50
+ /** Formatted text shown in the input. */
51
+ protected readonly _display: _angular_core.Signal<string>;
52
+ protected _onChange?: ChangeFn<string | null>;
53
+ protected _onTouched?: TouchFn;
54
+ private _emitModel;
55
+ protected _handleInput(event: Event): void;
56
+ protected _handleBlur(): void;
57
+ /** CONTROL VALUE ACCESSOR */
58
+ writeValue(value: string | null | undefined): void;
59
+ registerOnChange(fn: ChangeFn<string | null>): void;
60
+ registerOnTouched(fn: TouchFn): void;
61
+ setDisabledState(isDisabled: boolean): void;
62
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HlmPhoneInput, never>;
63
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HlmPhoneInput, "hlm-phone-input", never, { "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaDescribedby": { "alias": "ariaDescribedby"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "autocomplete": { "alias": "autocomplete"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "forceInvalid": { "alias": "forceInvalid"; "required": false; "isSignal": true; }; "maxDigits": { "alias": "maxDigits"; "required": false; "isSignal": true; }; "modelFormat": { "alias": "modelFormat"; "required": false; "isSignal": true; }; "formatPhoneNumber": { "alias": "formatPhoneNumber"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "inputClass": { "alias": "inputClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.BrnFieldControl; inputs: {}; outputs: {}; }]>;
64
+ }
65
+
66
+ export { HLM_PHONE_INPUT_VALUE_ACCESSOR, HlmPhoneInput, formatNanpPhoneNumber };
67
+ export type { PhoneNumberFormatter };
package/popover/README.md CHANGED
@@ -1,3 +1,332 @@
1
- # Popover Subpath
1
+ # Popover (`@egose/shadcn-theme-ng/popover`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/popover` or `@egose/shadcn-theme-ng-tw/popover`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ Floating panel anchored to a button (shadcn/ui `popover` equivalent). Thin shadcn styling directives over the spartan-ng `BrnPopover` brain family: a stateful `hlm-popover` root, a `button[hlmPopoverTrigger]` opener, portal/content pieces for the floating panel, and header/title/description typography helpers.
4
+
5
+ Ships as `@egose/shadcn-theme-ng/popover` and `@egose/shadcn-theme-ng-tw/popover` (tw: variant). See the [package README](../../README.md) for installation, peer dependencies, Tailwind setup, and testing. Do not publish this project directory independently.
6
+
7
+ ## Installation
8
+
9
+ ```bash
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` as peers plus a `tslib` runtime dependency; at runtime it uses `@spartan-ng/brain/popover` and `@spartan-ng/brain/core`. 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
+ HlmPopover, // directive: [hlmPopover],hlm-popover
26
+ HlmPopoverTrigger, // directive: button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]
27
+ HlmPopoverPortal, // directive: [hlmPopoverPortal]
28
+ HlmPopoverContent, // directive: [hlmPopoverContent],hlm-popover-content
29
+ HlmPopoverHeader, // directive: [hlmPopoverHeader],hlm-popover-header
30
+ HlmPopoverTitle, // directive: [hlmPopoverTitle]
31
+ HlmPopoverDescription, // directive: [hlmPopoverDescription]
32
+ HlmPopoverImports, // all seven above
33
+ HlmPopoverModule, // NgModule wrapping HlmPopoverImports
34
+ } from '@egose/shadcn-theme-ng/popover';
35
+ ```
36
+
37
+ Standalone usage:
38
+
39
+ ```ts
40
+ import { Component } from '@angular/core';
41
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
42
+
43
+ @Component({
44
+ selector: 'app-demo',
45
+ standalone: true,
46
+ imports: [...HlmPopoverImports],
47
+ template: `
48
+ <hlm-popover>
49
+ <button hlmPopoverTrigger type="button">Open</button>
50
+ <hlm-popover-content *hlmPopoverPortal>
51
+ <p>Hello</p>
52
+ </hlm-popover-content>
53
+ </hlm-popover>
54
+ `,
55
+ })
56
+ export class DemoComponent {}
57
+ ```
58
+
59
+ NgModule usage:
60
+
61
+ ```ts
62
+ import { NgModule } from '@angular/core';
63
+ import { HlmPopoverModule } from '@egose/shadcn-theme-ng/popover';
64
+
65
+ @NgModule({ imports: [HlmPopoverModule] })
66
+ export class DemoModule {}
67
+ ```
68
+
69
+ For the `tw:` build, swap the specifier to `@egose/shadcn-theme-ng-tw/popover`. Symbol names are identical.
70
+
71
+ ## Anatomy / Structure
72
+
73
+ ```html
74
+ <hlm-popover>
75
+ <!-- opener (must be a <button>) -->
76
+ <button hlmPopoverTrigger type="button">Settings</button>
77
+
78
+ <!-- floating panel: content styling + portal projection -->
79
+ <hlm-popover-content *hlmPopoverPortal class="tw:w-80">
80
+ <div hlmPopoverHeader>
81
+ <h4 hlmPopoverTitle>Dimensions</h4>
82
+ <p hlmPopoverDescription>Set the size of the layer.</p>
83
+ </div>
84
+ <!-- panel body -->
85
+ </hlm-popover-content>
86
+ </hlm-popover>
87
+ ```
88
+
89
+ The two-directive split on the panel is load-bearing: `hlmPopoverContent` paints the panel (fixed `w-72`, animations, `data-state` reflection), while `*hlmPopoverPortal` (a `BrnPopoverContent` host) projects it into the overlay at the anchored position. Always use them together as `hlm-popover-content *hlmPopoverPortal`.
90
+
91
+ | Class | Selector | Role |
92
+ | ----------------------- | -------------------------------------------------------- | ----------------------------------------------------- |
93
+ | `HlmPopover` | `[hlmPopover],hlm-popover` | Stateful root (`BrnPopover` host) |
94
+ | `HlmPopoverTrigger` | `button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]` | Opener (`BrnPopoverTrigger` host; must be `<button>`) |
95
+ | `HlmPopoverPortal` | `[hlmPopoverPortal]` | Overlay projection (`BrnPopoverContent` host) |
96
+ | `HlmPopoverContent` | `[hlmPopoverContent],hlm-popover-content` | Panel styling + `data-state` mirror |
97
+ | `HlmPopoverHeader` | `[hlmPopoverHeader],hlm-popover-header` | Header stack (`flex flex-col gap-1 text-sm`) |
98
+ | `HlmPopoverTitle` | `[hlmPopoverTitle]` | Title (`font-medium`) |
99
+ | `HlmPopoverDescription` | `[hlmPopoverDescription]` | Subtitle (`text-muted-foreground`) |
100
+
101
+ ## API reference
102
+
103
+ | Selector | Inputs (incl. host passthroughs) | Outputs | Notes |
104
+ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
105
+ | `[hlmPopover],hlm-popover` (`HlmPopover`) | `align`, `attachTo`, `autoFocus`, `closeOnOutsidePointerEvents`, `offsetX`, `sideOffset`, `state` (all via `BrnPopover`) | `stateChanged`, `closed` (via `BrnPopover`) | No shadcn inputs of its own; `data-slot="popover"` |
106
+ | `button[hlmPopoverTrigger]` (`HlmPopoverTrigger`) | `id`, `hlmPopoverTriggerFor` (→ `brnPopoverTriggerFor`), `type` (all via `BrnPopoverTrigger`) | — | `data-slot="popover-trigger"`. `hlmPopoverTriggerFor` targets an explicit popover when the trigger lives outside the root |
107
+ | `[hlmPopoverPortal]` (`HlmPopoverPortal`) | `context`, `class` (via `BrnPopoverContent`) | — | Structural directive (`*hlmPopoverPortal`); `class` merges onto the overlay panel |
108
+ | `[hlmPopoverContent]` (`HlmPopoverContent`) | — | — | Exposes `state` (brain state signal, default `signal('closed')`); mirrors it to `data-state` via `Renderer2` for the `data-open:`/`data-closed:` animations |
109
+ | `[hlmPopoverHeader]` / `[hlmPopoverTitle]` / `[hlmPopoverDescription]` | — | — | Typography only |
110
+
111
+ Panel styling (`HlmPopoverContent`): `bg-popover text-popover-foreground rounded-md p-4 text-sm shadow-md ring-1 ring-foreground/10 relative flex w-72 flex-col gap-4 outline-none` plus open/close zoom+fade animations keyed off `data-state`/`data-side`.
112
+
113
+ ## Examples
114
+
115
+ ### 1. Basic popover with header
116
+
117
+ ```ts
118
+ import { Component } from '@angular/core';
119
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
120
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
121
+
122
+ @Component({
123
+ selector: 'app-popover-basic',
124
+ standalone: true,
125
+ imports: [...HlmPopoverImports, HlmButton],
126
+ template: `
127
+ <hlm-popover>
128
+ <button hlmPopoverTrigger hlmButton variant="secondary" appearance="outline" type="button">Open settings</button>
129
+ <hlm-popover-content *hlmPopoverPortal>
130
+ <div hlmPopoverHeader>
131
+ <h4 hlmPopoverTitle>Dimensions</h4>
132
+ <p hlmPopoverDescription>Set the size of the layer.</p>
133
+ </div>
134
+ <div class="tw:grid tw:gap-2 tw:text-sm">
135
+ <label class="tw:grid tw:gap-1"
136
+ >Width <input class="tw:border tw:rounded tw:px-2 tw:py-1" value="100%"
137
+ /></label>
138
+ <label class="tw:grid tw:gap-1"
139
+ >Height <input class="tw:border tw:rounded tw:px-2 tw:py-1" value="24px"
140
+ /></label>
141
+ </div>
142
+ </hlm-popover-content>
143
+ </hlm-popover>
144
+ `,
145
+ })
146
+ export class PopoverBasicComponent {}
147
+ ```
148
+
149
+ ### 2. Placement, offsets, and state events
150
+
151
+ ```ts
152
+ import { Component, signal } from '@angular/core';
153
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
154
+
155
+ @Component({
156
+ selector: 'app-popover-placement',
157
+ standalone: true,
158
+ imports: [...HlmPopoverImports],
159
+ template: `
160
+ <hlm-popover
161
+ align="start"
162
+ [sideOffset]="8"
163
+ [closeOnOutsidePointerEvents]="true"
164
+ (stateChanged)="open.set($event === 'open')"
165
+ (closed)="open.set(false)"
166
+ >
167
+ <button hlmPopoverTrigger type="button">Aligned start, offset 8</button>
168
+ <hlm-popover-content *hlmPopoverPortal>
169
+ <p class="tw:text-sm">Panel state: {{ open() ? 'open' : 'closed' }}.</p>
170
+ </hlm-popover-content>
171
+ </hlm-popover>
172
+ `,
173
+ })
174
+ export class PopoverPlacementComponent {
175
+ readonly open = signal(false);
176
+ }
177
+ ```
178
+
179
+ `align`, `sideOffset`, `closeOnOutsidePointerEvents` are brain inputs on `HlmPopover`; `stateChanged`/`closed` are its outputs.
180
+
181
+ ### 3. Form inside a popover (template-driven)
182
+
183
+ ```ts
184
+ import { Component } from '@angular/core';
185
+ import { FormsModule } from '@angular/forms';
186
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
187
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
188
+ import { HlmLabelImports } from '@egose/shadcn-theme-ng/label';
189
+
190
+ @Component({
191
+ selector: 'app-popover-form',
192
+ standalone: true,
193
+ imports: [FormsModule, ...HlmPopoverImports, HlmInput, ...HlmLabelImports],
194
+ template: `
195
+ <hlm-popover>
196
+ <button hlmPopoverTrigger type="button">Invite member</button>
197
+ <hlm-popover-content *hlmPopoverPortal class="tw:w-80">
198
+ <div hlmPopoverHeader>
199
+ <h4 hlmPopoverTitle>Invite</h4>
200
+ <p hlmPopoverDescription>They receive an email invitation.</p>
201
+ </div>
202
+ <div class="tw:grid tw:gap-1.5">
203
+ <label hlmLabel for="invite-email">Email</label>
204
+ <input hlmInput id="invite-email" [(ngModel)]="email" type="email" placeholder="ada@example.com" />
205
+ <button type="button" (click)="invite()">Send invite</button>
206
+ </div>
207
+ </hlm-popover-content>
208
+ </hlm-popover>
209
+ `,
210
+ })
211
+ export class PopoverFormComponent {
212
+ email = '';
213
+
214
+ invite(): void {
215
+ console.log('invite', this.email);
216
+ }
217
+ }
218
+ ```
219
+
220
+ ### 4. Custom panel width via portal `class`
221
+
222
+ The portal's `class` input lands on the overlay host; the content directive keeps its own `w-72` unless you override it on the element as well.
223
+
224
+ ```ts
225
+ import { Component } from '@angular/core';
226
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
227
+
228
+ @Component({
229
+ selector: 'app-popover-wide',
230
+ standalone: true,
231
+ imports: [...HlmPopoverImports],
232
+ template: `
233
+ <hlm-popover>
234
+ <button hlmPopoverTrigger type="button">Wide panel</button>
235
+ <hlm-popover-content *hlmPopoverPortal class="tw:w-96">
236
+ <div hlmPopoverHeader>
237
+ <h4 hlmPopoverTitle>Preview</h4>
238
+ <p hlmPopoverDescription>A wider panel for rich content.</p>
239
+ </div>
240
+ <div class="tw:grid tw:grid-cols-2 tw:gap-2 tw:text-sm">
241
+ <div class="tw:rounded tw:bg-muted tw:p-2">Column A</div>
242
+ <div class="tw:rounded tw:bg-muted tw:p-2">Column B</div>
243
+ </div>
244
+ </hlm-popover-content>
245
+ </hlm-popover>
246
+ `,
247
+ })
248
+ export class PopoverWideComponent {}
249
+ ```
250
+
251
+ ### 5. External trigger (`hlmPopoverTriggerFor`)
252
+
253
+ ```ts
254
+ import { Component } from '@angular/core';
255
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
256
+
257
+ @Component({
258
+ selector: 'app-popover-external',
259
+ standalone: true,
260
+ imports: [...HlmPopoverImports],
261
+ template: `
262
+ <hlm-popover #pop="hlmPopover">
263
+ <hlm-popover-content *hlmPopoverPortal>
264
+ <p class="tw:text-sm">Triggered from outside the root.</p>
265
+ </hlm-popover-content>
266
+ </hlm-popover>
267
+
268
+ <button hlmPopoverTriggerFor [hlmPopoverTriggerFor]="pop" type="button">Open from here</button>
269
+ `,
270
+ })
271
+ export class PopoverExternalComponent {}
272
+ ```
273
+
274
+ > The exact template-ref export name follows the brain `BrnPopover` directive (`#pop="…"`) — verify against your installed `@spartan-ng/brain` version if the alias differs.
275
+
276
+ ### 6. Async content + loading state
277
+
278
+ ```ts
279
+ import { Component, signal } from '@angular/core';
280
+ import { HlmPopoverImports } from '@egose/shadcn-theme-ng/popover';
281
+
282
+ @Component({
283
+ selector: 'app-popover-async',
284
+ standalone: true,
285
+ imports: [...HlmPopoverImports],
286
+ template: `
287
+ <hlm-popover (stateChanged)="onState($event)">
288
+ <button hlmPopoverTrigger type="button">Recent activity</button>
289
+ <hlm-popover-content *hlmPopoverPortal>
290
+ <div hlmPopoverHeader>
291
+ <h4 hlmPopoverTitle>Activity</h4>
292
+ <p hlmPopoverDescription>{{ loading() ? 'Loading…' : 'Last 3 events.' }}</p>
293
+ </div>
294
+ @for (e of events(); track e) {
295
+ <p class="tw:text-sm">{{ e }}</p>
296
+ } @empty {
297
+ <p class="tw:text-sm tw:text-muted-foreground">Nothing yet.</p>
298
+ }
299
+ </hlm-popover-content>
300
+ </hlm-popover>
301
+ `,
302
+ })
303
+ export class PopoverAsyncComponent {
304
+ readonly events = signal<string[]>([]);
305
+ readonly loading = signal(false);
306
+
307
+ async onState(state: string): Promise<void> {
308
+ if (state !== 'open' || this.events().length) return;
309
+ this.loading.set(true);
310
+ await new Promise((r) => setTimeout(r, 400));
311
+ this.events.set(['Deploy succeeded', 'Review requested', 'Comment added']);
312
+ this.loading.set(false);
313
+ }
314
+ }
315
+ ```
316
+
317
+ ## Accessibility notes
318
+
319
+ - Triggers must be `<button>` (both selector variants require it) with a text label; the brain layer wires `aria-expanded`, `aria-controls`, and focus return.
320
+ - Keep a heading (`hlmPopoverTitle`) as the first element when the panel conveys a topic — screen-reader users land inside the panel on open.
321
+ - `autoFocus` (brain input) controls initial focus; form panels should focus the first field, read-only panels should keep focus on the trigger side and let users tab in.
322
+ - `Escape`/outside-pointer close comes from the brain defaults (`closeOnOutsidePointerEvents` is configurable); don't trap focus manually.
323
+
324
+ ## Theming / CSS variables
325
+
326
+ No theming inputs. The panel uses `bg-popover text-popover-foreground ring-foreground/10`; header/title/description use text tokens. Widen via portal `class` (example 4); state-driven animations read the mirrored `data-state`.
327
+
328
+ ## Related subpaths
329
+
330
+ - `@egose/shadcn-theme-ng/tooltip` — hover hint vs this component's click-anchored panel.
331
+ - `@egose/shadcn-theme-ng/dropdown-menu`, `@egose/shadcn-theme-ng/select` — menu/select overlays built on the same brain overlay concepts.
332
+ - `@egose/shadcn-theme-ng/input`, `@egose/shadcn-theme-ng/label` — form controls commonly hosted inside popover panels.