@goat-bravos/intern-hub-layout 1.0.2 → 1.0.4

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 (66) hide show
  1. package/fesm2022/goat-bravos-intern-hub-layout.mjs +561 -0
  2. package/fesm2022/goat-bravos-intern-hub-layout.mjs.map +1 -0
  3. package/package.json +23 -50
  4. package/types/goat-bravos-intern-hub-layout.d.ts +189 -0
  5. package/.editorconfig +0 -17
  6. package/.vscode/extensions.json +0 -4
  7. package/.vscode/launch.json +0 -20
  8. package/.vscode/mcp.json +0 -9
  9. package/.vscode/tasks.json +0 -42
  10. package/angular.json +0 -40
  11. package/ng-package.json +0 -7
  12. package/src/libs/layouts/header/header.component.html +0 -27
  13. package/src/libs/layouts/header/header.component.scss +0 -96
  14. package/src/libs/layouts/header/header.component.ts +0 -13
  15. package/src/libs/layouts/main-layout/main-layout.component.html +0 -15
  16. package/src/libs/layouts/main-layout/main-layout.component.scss +0 -35
  17. package/src/libs/layouts/main-layout/main-layout.component.ts +0 -17
  18. package/src/libs/layouts/main-layout-test/main-layout-test.component.html +0 -296
  19. package/src/libs/layouts/main-layout-test/main-layout-test.component.scss +0 -94
  20. package/src/libs/layouts/main-layout-test/main-layout-test.component.ts +0 -121
  21. package/src/libs/layouts/sidebar/sidebar.component.html +0 -7
  22. package/src/libs/layouts/sidebar/sidebar.component.scss +0 -38
  23. package/src/libs/layouts/sidebar/sidebar.component.ts +0 -19
  24. package/src/libs/shared/components/approval/approval-list/approval-list.component.html +0 -19
  25. package/src/libs/shared/components/approval/approval-list/approval-list.component.scss +0 -24
  26. package/src/libs/shared/components/approval/approval-list/approval-list.component.ts +0 -19
  27. package/src/libs/shared/components/approval/approval-list-item/approval-list-item.component.html +0 -21
  28. package/src/libs/shared/components/approval/approval-list-item/approval-list-item.component.scss +0 -28
  29. package/src/libs/shared/components/approval/approval-list-item/approval-list-item.component.ts +0 -20
  30. package/src/libs/shared/components/approval/approval-list-item/approval-list-item.model.ts +0 -8
  31. package/src/libs/shared/components/button/button-container/button-container.component.html +0 -20
  32. package/src/libs/shared/components/button/button-container/button-container.component.scss +0 -49
  33. package/src/libs/shared/components/button/button-container/button-container.component.ts +0 -38
  34. package/src/libs/shared/components/button/button-container/button-container.model.ts +0 -58
  35. package/src/libs/shared/components/button/label-button/label-button.component.html +0 -11
  36. package/src/libs/shared/components/button/label-button/label-button.component.scss +0 -13
  37. package/src/libs/shared/components/button/label-button/label-button.component.ts +0 -18
  38. package/src/libs/shared/components/functional-label/functional-label.component.html +0 -4
  39. package/src/libs/shared/components/functional-label/functional-label.component.scss +0 -58
  40. package/src/libs/shared/components/functional-label/functional-label.component.ts +0 -15
  41. package/src/libs/shared/components/input/input-calendar/input-calendar.component.html +0 -52
  42. package/src/libs/shared/components/input/input-calendar/input-calendar.component.scss +0 -98
  43. package/src/libs/shared/components/input/input-calendar/input-calendar.component.ts +0 -102
  44. package/src/libs/shared/components/input/input-label/input-label.component.html +0 -0
  45. package/src/libs/shared/components/input/input-label/input-label.component.scss +0 -0
  46. package/src/libs/shared/components/input/input-label/input-label.component.ts +0 -0
  47. package/src/libs/shared/components/input/input-stepper/input-stepper.component.html +0 -62
  48. package/src/libs/shared/components/input/input-stepper/input-stepper.component.scss +0 -211
  49. package/src/libs/shared/components/input/input-stepper/input-stepper.component.ts +0 -73
  50. package/src/libs/shared/components/input/input-text/input-text.component.html +0 -40
  51. package/src/libs/shared/components/input/input-text/input-text.component.scss +0 -143
  52. package/src/libs/shared/components/input/input-text/input-text.component.ts +0 -63
  53. package/src/libs/shared/components/pop-up/pop-up-confirm/pop-up-confirm.component.html +0 -15
  54. package/src/libs/shared/components/pop-up/pop-up-confirm/pop-up-confirm.component.scss +0 -70
  55. package/src/libs/shared/components/pop-up/pop-up-confirm/pop-up-confirm.component.ts +0 -29
  56. package/src/libs/shared/components/table/table-body/table-body.component.html +0 -18
  57. package/src/libs/shared/components/table/table-body/table-body.component.scss +0 -6
  58. package/src/libs/shared/components/table/table-body/table-body.component.ts +0 -17
  59. package/src/libs/shared/components/table/table-header/table-header.component.html +0 -27
  60. package/src/libs/shared/components/table/table-header/table-header.component.scss +0 -91
  61. package/src/libs/shared/components/table/table-header/table-header.component.ts +0 -25
  62. package/src/public-api.ts +0 -22
  63. package/tsconfig.json +0 -38
  64. package/tsconfig.lib.json +0 -17
  65. package/tsconfig.lib.prod.json +0 -11
  66. package/tsconfig.spec.json +0 -15
@@ -1,102 +0,0 @@
1
- import { Component, Input, Output, EventEmitter, ElementRef, ViewChild } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
4
-
5
- @Component({
6
- selector: 'app-input-calendar',
7
- standalone: true,
8
- imports: [CommonModule, FormsModule],
9
- templateUrl: './input-calendar.component.html',
10
- styleUrls: ['./input-calendar.component.scss'],
11
- })
12
- export class InputCalendarComponent {
13
- @Input() headerInput: string = '';
14
- @Input() placeholder: string = 'dd/mm/yyyy';
15
- @Input() readonly: boolean = false;
16
- @Input() required: boolean = false;
17
- @Input() width: string = '100%';
18
-
19
- @Output() valueChange = new EventEmitter<string>();
20
-
21
- @ViewChild('hiddenDateInput') hiddenDateInput!: ElementRef<HTMLInputElement>;
22
-
23
- displayValue: string = '';
24
- private _value: string = '';
25
-
26
- @Input()
27
- set value(val: string) {
28
- this._value = val;
29
- this.displayValue = this.formatToDisplay(val);
30
- }
31
-
32
- get value(): string {
33
- return this._value;
34
- }
35
-
36
- // Convert ISO format (yyyy-mm-dd) to display format (dd/mm/yyyy)
37
- formatToDisplay(isoDate: string): string {
38
- if (!isoDate) return '';
39
- const parts = isoDate.split('-');
40
- if (parts.length === 3) {
41
- return `${parts[2]}/${parts[1]}/${parts[0]}`;
42
- }
43
- return isoDate;
44
- }
45
-
46
- // Convert display format (dd/mm/yyyy) to ISO format (yyyy-mm-dd)
47
- formatToISO(displayDate: string): string {
48
- if (!displayDate) return '';
49
- const parts = displayDate.split('/');
50
- if (parts.length === 3) {
51
- return `${parts[2]}-${parts[1]}-${parts[0]}`;
52
- }
53
- return displayDate;
54
- }
55
-
56
- // Handle text input with auto-formatting
57
- onDisplayInput(event: Event): void {
58
- const input = event.target as HTMLInputElement;
59
- let value = input.value.replaceAll(/[^\d/]/g, '');
60
-
61
- // Auto-insert slashes at positions 2 and 5
62
- const parts = value.split('/');
63
- if (parts[0] && parts[0].length >= 2 && parts.length === 1) {
64
- value = parts[0].substring(0, 2) + '/' + parts[0].substring(2);
65
- }
66
- if (value.length >= 5 && value.split('/').length === 2 && !value.endsWith('/')) {
67
- const newParts = value.split('/');
68
- if (newParts[1] && newParts[1].length >= 2) {
69
- value = newParts[0] + '/' + newParts[1].substring(0, 2) + '/' + newParts[1].substring(2);
70
- }
71
- }
72
-
73
- // Limit to dd/mm/yyyy format
74
- if (value.length > 10) {
75
- value = value.substring(0, 10);
76
- }
77
-
78
- this.displayValue = value;
79
-
80
- // Only emit if complete date
81
- if (value.length === 10) {
82
- const isoValue = this.formatToISO(value);
83
- this._value = isoValue;
84
- this.valueChange.emit(isoValue);
85
- }
86
- }
87
-
88
- // Open native date picker
89
- openDatePicker(): void {
90
- if (!this.readonly && this.hiddenDateInput) {
91
- this.hiddenDateInput.nativeElement.showPicker();
92
- }
93
- }
94
-
95
- // Handle date picker selection
96
- onDatePickerChange(event: Event): void {
97
- const input = event.target as HTMLInputElement;
98
- this._value = input.value;
99
- this.displayValue = this.formatToDisplay(input.value);
100
- this.valueChange.emit(this._value);
101
- }
102
- }
@@ -1,62 +0,0 @@
1
- <div
2
- class="input-form"
3
- [style.width]="width"
4
- [class.disabled]="disabled"
5
- [class.state-negative]="state === 'negative'"
6
- [class.state-positive]="state === 'positive'"
7
- >
8
- <div class="input-form-content">
9
- <label class="input-form-label" [for]="componentId">
10
- <span class="label-text">
11
- {{ headerInput }}
12
- @if (required) {
13
- <span class="required-mark">*</span>
14
- }
15
- </span>
16
- </label>
17
- </div>
18
-
19
- <div class="input-stepper-content">
20
- <button
21
- type="button"
22
- class="input-stepper-button"
23
- (click)="decrement()"
24
- [disabled]="disabled"
25
- aria-label="Giảm giá trị"
26
- >
27
- <span class="stepper-icon">−</span>
28
- </button>
29
- <input
30
- [id]="componentId"
31
- type="text"
32
- class="input-stepper-input"
33
- placeholder="0"
34
- [placeholder]="placeholder || '0'"
35
- title="Giá trị"
36
- [title]="headerInput || 'Giá trị'"
37
- [readonly]="readonly"
38
- [disabled]="disabled"
39
- [required]="required"
40
- [value]="value"
41
- (input)="onInput($event)"
42
- [attr.aria-label]="headerInput || 'Giá trị'"
43
- />
44
- <button
45
- type="button"
46
- class="input-stepper-button"
47
- (click)="increment()"
48
- [disabled]="disabled"
49
- aria-label="Tăng giá trị"
50
- >
51
- <span class="stepper-icon">+</span>
52
- </button>
53
- </div>
54
-
55
- <div class="input-stepper-footer">
56
- @if (error) {
57
- <span class="input-stepper-error">{{ error }}</span>
58
- } @else if (helperText) {
59
- <span class="input-stepper-helper">{{ helperText }}</span>
60
- }
61
- </div>
62
- </div>
@@ -1,211 +0,0 @@
1
- // Input Stepper Component - System Design Format
2
-
3
- .input-form {
4
- display: flex;
5
- flex-direction: column;
6
- gap: 6px;
7
- min-width: 200px;
8
- }
9
-
10
- .input-form-content {
11
- display: flex;
12
-
13
- .input-form-label {
14
- display: block;
15
- font-size: var(--font-sm);
16
- font-weight: 700;
17
- color: var(--neutral-color-700);
18
-
19
- .label-text {
20
- display: block;
21
- margin-bottom: 4px;
22
-
23
- .required-mark {
24
- color: var(--utility-600);
25
- margin-left: 2px;
26
- }
27
- }
28
- }
29
- }
30
-
31
- .input-stepper-content {
32
- display: flex;
33
- align-items: center;
34
- border: 1px solid var(--neutral-color-200);
35
- border-radius: var(--radius-md);
36
- overflow: hidden;
37
- background: var(--neutral-color-10);
38
- height: 36px;
39
- transition:
40
- border-color 0.2s ease,
41
- box-shadow 0.2s ease;
42
-
43
- &:hover {
44
- border-color: var(--neutral-color-300);
45
- }
46
-
47
- &:focus-within {
48
- border-color: var(--brand-500);
49
- box-shadow: 0 0 0 2px rgba(var(--brand-500-rgb, 39, 64, 180), 0.15);
50
- }
51
-
52
- .input-stepper-button {
53
- display: flex;
54
- align-items: center;
55
- justify-content: center;
56
- width: 36px;
57
- min-width: 36px;
58
- flex-shrink: 0;
59
- height: 100%;
60
- border: none;
61
- background: transparent;
62
- cursor: pointer;
63
- color: var(--neutral-color-500);
64
- font-size: var(--font-sm);
65
- background-color: var(--neutral-color-10);
66
- padding: 8px;
67
- transition:
68
- background-color 0.2s ease,
69
- color 0.2s ease;
70
-
71
- &:hover:not(:disabled) {
72
- background-color: var(--neutral-color-50);
73
- }
74
-
75
- &:active:not(:disabled) {
76
- background-color: var(--neutral-color-100);
77
- }
78
-
79
- &:disabled {
80
- color: var(--neutral-color-300);
81
- cursor: not-allowed;
82
- background-color: var(--neutral-color-50);
83
- }
84
-
85
- .stepper-icon {
86
- font-size: 16px;
87
- font-weight: 600;
88
- pointer-events: none;
89
- line-height: 1;
90
- }
91
- }
92
-
93
- .input-stepper-input {
94
- flex: 1;
95
- min-width: 0;
96
- height: 100%;
97
- border: none;
98
- text-align: center;
99
- font-size: var(--font-sm);
100
- font-weight: 500;
101
- color: var(--neutral-color-875);
102
- outline: none;
103
- background: transparent;
104
- padding: 0 8px;
105
-
106
- &::placeholder {
107
- color: var(--neutral-color-400);
108
- font-weight: 400;
109
- }
110
-
111
- &:read-only {
112
- cursor: default;
113
- }
114
-
115
- &:disabled {
116
- cursor: not-allowed;
117
- background-color: var(--neutral-color-50);
118
- color: var(--neutral-color-400);
119
- }
120
-
121
- // Remove number input arrows - Modern approach
122
- &::-webkit-outer-spin-button,
123
- &::-webkit-inner-spin-button {
124
- -webkit-appearance: none;
125
- appearance: none;
126
- margin: 0;
127
- }
128
-
129
- &[type='number'] {
130
- -moz-appearance: textfield;
131
- appearance: textfield;
132
- }
133
- }
134
- }
135
-
136
- .input-stepper-footer {
137
- min-height: 18px;
138
-
139
- .input-stepper-error {
140
- font-size: var(--font-xs, 12px);
141
- color: var(--utility-600);
142
- line-height: 1.5;
143
- }
144
-
145
- .input-stepper-helper {
146
- font-size: var(--font-xs, 12px);
147
- color: var(--neutral-color-500);
148
- line-height: 1.5;
149
- }
150
- }
151
-
152
- // Disabled state for entire component
153
- .input-form.disabled {
154
- .input-stepper-content {
155
- background-color: var(--neutral-color-50);
156
- border-color: var(--neutral-color-200);
157
- cursor: not-allowed;
158
-
159
- &:hover {
160
- border-color: var(--neutral-color-200);
161
- }
162
-
163
- .input-stepper-button {
164
- cursor: not-allowed;
165
- color: var(--neutral-color-300);
166
- background-color: var(--neutral-color-50);
167
- }
168
-
169
- .input-stepper-input {
170
- cursor: not-allowed;
171
- color: var(--neutral-color-400);
172
- background-color: var(--neutral-color-50);
173
- }
174
- }
175
- }
176
-
177
- // Negative state (error/warning)
178
- .input-form.state-negative {
179
- .input-stepper-content {
180
- border-color: var(--utility-600);
181
-
182
- &:focus-within {
183
- border-color: var(--utility-600);
184
- box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.15);
185
- }
186
- }
187
-
188
- .input-stepper-footer {
189
- .input-stepper-helper {
190
- color: var(--utility-600);
191
- }
192
- }
193
- }
194
-
195
- // Positive state (success)
196
- .input-form.state-positive {
197
- .input-stepper-content {
198
- border-color: var(--success-500, #22c55e);
199
-
200
- &:focus-within {
201
- border-color: var(--success-500, #22c55e);
202
- box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.15);
203
- }
204
- }
205
-
206
- .input-stepper-footer {
207
- .input-stepper-helper {
208
- color: var(--success-500, #22c55e);
209
- }
210
- }
211
- }
@@ -1,73 +0,0 @@
1
- import { Component, EventEmitter, Input, Output } from "@angular/core";
2
- import { CommonModule } from "@angular/common";
3
- import { FormsModule } from "@angular/forms";
4
-
5
- @Component({
6
- selector: 'app-input-stepper',
7
- standalone: true,
8
- imports: [CommonModule, FormsModule],
9
- templateUrl: './input-stepper.component.html',
10
- styleUrls: ['./input-stepper.component.scss'],
11
- })
12
- export class InputStepperComponent {
13
- protected readonly componentId = `input-stepper-${Math.random().toString(36).substring(2, 9)}`;
14
- @Input() headerInput: string = '';
15
- @Input() placeholder: string = '';
16
- @Input() readonly: boolean = false;
17
- @Input() required: boolean = false;
18
- @Input() disabled: boolean = false;
19
- @Input() width: string = '100%';
20
- @Input() min: number = 0;
21
- @Input() max: number = 100;
22
- @Input() error: string = '';
23
- @Input() helperText: string = '';
24
- @Input() step: number = 1;
25
- @Input() state: 'default' | 'negative' | 'positive' = 'default';
26
-
27
- @Output() valueChange = new EventEmitter<number>();
28
-
29
- private _value: number = 0;
30
-
31
- @Input()
32
- set value(val: number) {
33
- this._value = val || 0;
34
- }
35
-
36
- get value(): number {
37
- return this._value;
38
- }
39
-
40
- increment(): void {
41
- if (this.readonly) return;
42
- const newValue = this._value + this.step;
43
- if (newValue <= this.max) {
44
- this._value = newValue;
45
- this.valueChange.emit(this._value);
46
- }
47
- }
48
-
49
- decrement(): void {
50
- if (this.readonly) return;
51
- const newValue = this._value - this.step;
52
- if (newValue >= this.min) {
53
- this._value = newValue;
54
- this.valueChange.emit(this._value);
55
- }
56
- }
57
-
58
- onInput(event: Event): void {
59
- const input = event.target as HTMLInputElement;
60
- let newValue = Number.parseInt(input.value, 10);
61
-
62
- if (Number.isNaN(newValue)) {
63
- newValue = this.min;
64
- }
65
-
66
- if (newValue < this.min) newValue = this.min;
67
- if (newValue > this.max) newValue = this.max;
68
-
69
- input.value = newValue.toString();
70
- this._value = newValue;
71
- this.valueChange.emit(this._value);
72
- }
73
- }
@@ -1,40 +0,0 @@
1
- <div class="input-form">
2
- <div class="input-form-content">
3
- <label class="input-form-label">
4
- <span class="label-text" [class.hidden]="!headerInput">
5
- {{ headerInput }}
6
- @if (required) {
7
- <span class="required-mark">*</span>
8
- }
9
- </span>
10
- <div class="input-field-wrapper">
11
- <input
12
- [type]="typeInput"
13
- class="input-form-field"
14
- [placeholder]="placeholder"
15
- [readonly]="readonly"
16
- [required]="required"
17
- [style.width]="width"
18
- [value]="value"
19
- (input)="onInput($event)"
20
- [attr.maxlength]="maxLength > 0 ? maxLength : null"
21
- />
22
- @if (icon) {
23
- <button
24
- type="button"
25
- class="input-icon-button"
26
- [class.clickable]="iconClick.observed"
27
- (click)="onIconClick()"
28
- [attr.aria-label]="icon"
29
- [disabled]="!iconClick.observed"
30
- >
31
- <i [class]="icon" class="input-icon"></i>
32
- </button>
33
- }
34
- </div>
35
- </label>
36
- </div>
37
- <div class="limit" [class.hidden]="!(maxLength > 0 && showLimit)">
38
- <span>{{ limit }}</span>
39
- </div>
40
- </div>
@@ -1,143 +0,0 @@
1
- // Input Form Component - System Design Format
2
-
3
- .input-form {
4
- display: flex;
5
- flex-direction: column;
6
- justify-content: space-between;
7
- gap: 4px;
8
- }
9
-
10
- .hidden {
11
- visibility: hidden;
12
- }
13
-
14
- .input-form-label {
15
- display: block;
16
- font-size: var(--font-sm);
17
- font-weight: 700;
18
- color: var(--neutral-color-700);
19
- }
20
-
21
- .label-text {
22
- display: block;
23
- margin-bottom: 4px;
24
- height: 20px;
25
- }
26
-
27
- .required-mark {
28
- color: var(--utility-600);
29
- margin-left: 2px;
30
- }
31
-
32
- .input-form-field {
33
- padding: 8px 12px;
34
- border: 1px solid var(--neutral-color-200);
35
- border-radius: var(--radius-md);
36
- font-size: var(--font-sm);
37
- color: var(--neutral-color-875);
38
- height: 36px;
39
- width: 100%;
40
- box-sizing: border-box;
41
- background-color: var(--neutral-color-10);
42
- transition:
43
- border-color 0.2s ease,
44
- box-shadow 0.2s ease;
45
-
46
- &::placeholder {
47
- color: var(--neutral-color-400);
48
- }
49
-
50
- &:focus {
51
- outline: none;
52
- border-color: var(--brand-500);
53
- box-shadow: 0 0 0 2px rgba(var(--brand-500-rgb, 39, 64, 180), 0.15);
54
- }
55
-
56
- &:read-only {
57
- background-color: var(--neutral-color-25);
58
- cursor: not-allowed;
59
- }
60
-
61
- // Password input - larger dots
62
- &[type='password'] {
63
- font-family: 'Verdana', sans-serif;
64
- font-size: 16px;
65
- letter-spacing: 3px;
66
-
67
- &::placeholder {
68
- font-family: inherit;
69
- font-size: var(--font-sm);
70
- letter-spacing: normal;
71
- }
72
- }
73
- }
74
-
75
- // When icon is present, add padding-right
76
- .input-field-wrapper:has(.input-icon-button) {
77
- .input-form-field {
78
- padding-right: 36px;
79
- }
80
- }
81
-
82
- .input-field-wrapper {
83
- position: relative;
84
- display: flex;
85
- align-items: center;
86
- }
87
-
88
- .input-icon-button {
89
- position: absolute;
90
- right: 10px;
91
- top: 50%;
92
- transform: translateY(-50%);
93
- background: none;
94
- border: none;
95
- padding: 0;
96
- margin: 0;
97
- display: flex;
98
- align-items: center;
99
- justify-content: center;
100
- color: var(--neutral-color-500);
101
- transition: color 0.2s ease;
102
- outline: none;
103
-
104
- &.clickable {
105
- cursor: pointer;
106
-
107
- &:hover {
108
- .input-icon {
109
- color: var(--brand-500);
110
- }
111
- }
112
- }
113
-
114
- &:disabled {
115
- cursor: default;
116
- pointer-events: none;
117
- }
118
- }
119
-
120
- // Force Font Awesome icon size
121
- .input-icon {
122
- font-size: 14px !important;
123
- width: 14px;
124
- height: 14px;
125
- display: flex;
126
- align-items: center;
127
- justify-content: center;
128
- transition: color 0.2s ease;
129
-
130
- &::before {
131
- font-size: 14px !important;
132
- }
133
- }
134
-
135
- .limit {
136
- display: flex;
137
- justify-content: flex-end;
138
-
139
- span {
140
- font-size: var(--font-xs, 12px);
141
- color: var(--neutral-color-500);
142
- }
143
- }
@@ -1,63 +0,0 @@
1
- import { Component, Input, Output, EventEmitter } from "@angular/core";
2
- import { CommonModule } from "@angular/common";
3
- import { FormsModule } from "@angular/forms";
4
-
5
- @Component({
6
- selector: 'app-input-text',
7
- standalone: true,
8
- imports: [CommonModule, FormsModule],
9
- templateUrl: './input-text.component.html',
10
- styleUrls: ['./input-text.component.scss'],
11
- })
12
- export class InputTextComponent {
13
- @Input() headerInput: string = '';
14
- @Input() placeholder: string = '';
15
- @Input() readonly: boolean = false;
16
- @Input() required: boolean = false;
17
- @Input() width: string = '100%';
18
- @Input() maxLength: number = 0; // 0 = no limit
19
- @Input() showLimit: boolean = false;
20
- @Input() icon: string = '';
21
- @Input() typeInput: string = 'text';
22
-
23
- @Output() valueChange = new EventEmitter<string>();
24
- @Output() iconClick = new EventEmitter<void>();
25
-
26
- private _value: string = '';
27
-
28
- onIconClick(): void {
29
- this.iconClick.emit();
30
- }
31
-
32
- @Input()
33
- set value(val: string) {
34
- this._value = val || '';
35
- }
36
-
37
- get value(): string {
38
- return this._value;
39
- }
40
-
41
- get currentLength(): number {
42
- return this._value?.length || 0;
43
- }
44
-
45
- get limit(): string {
46
- if (!this.showLimit || this.maxLength <= 0) return '';
47
- return `${this.currentLength}/${this.maxLength}`;
48
- }
49
-
50
- onInput(event: Event): void {
51
- const input = event.target as HTMLInputElement;
52
- let newValue = input.value;
53
-
54
- // Enforce maxLength if set
55
- if (this.maxLength > 0 && newValue.length > this.maxLength) {
56
- newValue = newValue.substring(0, this.maxLength);
57
- input.value = newValue;
58
- }
59
-
60
- this._value = newValue;
61
- this.valueChange.emit(this._value);
62
- }
63
- }