@kigi/components 1.62.5 → 1.62.7-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kigi/components",
3
- "version": "1.62.5",
3
+ "version": "1.62.7-beta.1",
4
4
  "description": "@kigi/components",
5
5
  "main": "src/components/index.ts",
6
6
  "scripts": {
@@ -1,9 +1,10 @@
1
1
  import * as angular from 'angular'
2
- import { mbgInputCpfCnpj } from './mbg-input-cpfcnpj'
2
+ import { mbgInputCpfCnpj, mbgAlphanumericCnpjMask } from './mbg-input-cpfcnpj'
3
3
 
4
4
  const mbgInputCpfCnpjModule = angular
5
5
  .module('mbg.components.mbgInputCpfCnpj', [])
6
6
  .component('mbgInputCpfCnpj', mbgInputCpfCnpj)
7
+ .directive('mbgAlphanumericCnpjMask', mbgAlphanumericCnpjMask)
7
8
  .name
8
9
 
9
10
  export { mbgInputCpfCnpjModule }
@@ -1,16 +1,32 @@
1
1
  <div class="mbg-input-wrapper mb-input-cpfcnpj-wrapper"
2
2
  ng-if="$ctrl.props"
3
3
  mbg-autocomplete-off>
4
- <input type="text"
5
- ng-model="$ctrl.ngModel"
6
- ng-change="$ctrl.onChange()"
7
- placeholder="{{ $ctrl.props.placeholder }}"
8
- ng-required="$ctrl.ngRequired"
9
- ng-disabled="$ctrl.ngDisabled"
10
- ng-blur="$ctrl.ngBlur({ $event })"
11
- ng-focus="$ctrl.ngFocus({ $event })"
12
- ng-keyup="$ctrl.ngKeyup({ $event })"
13
- ng-keypress="$ctrl.ngKeypress({ $event })"
14
- ng-keydown="$ctrl.ngKeydown({ $event })"
15
- ui-br-cpfcnpj-mask />
16
- </div>
4
+ <input type="text"
5
+ ng-if="$ctrl.allowAlphanumeric"
6
+ ng-model="$ctrl.ngModel"
7
+ name="{{$ctrl.name}}"
8
+ ng-change="$ctrl.onChange()"
9
+ placeholder="{{ $ctrl.props.placeholder }}"
10
+ ng-required="$ctrl.ngRequired"
11
+ ng-disabled="$ctrl.ngDisabled"
12
+ ng-blur="$ctrl.ngBlur({ $event })"
13
+ ng-focus="$ctrl.ngFocus({ $event })"
14
+ ng-keyup="$ctrl.ngKeyup({ $event })"
15
+ ng-keypress="$ctrl.ngKeypress({ $event })"
16
+ ng-keydown="$ctrl.ngKeydown({ $event })"
17
+ mbg-alphanumeric-cnpj-mask />
18
+ <input type="text"
19
+ ng-if="!$ctrl.allowAlphanumeric"
20
+ ng-model="$ctrl.ngModel"
21
+ name="{{$ctrl.name}}"
22
+ ng-change="$ctrl.onChange()"
23
+ placeholder="{{ $ctrl.props.placeholder }}"
24
+ ng-required="$ctrl.ngRequired"
25
+ ng-disabled="$ctrl.ngDisabled"
26
+ ng-blur="$ctrl.ngBlur({ $event })"
27
+ ng-focus="$ctrl.ngFocus({ $event })"
28
+ ng-keyup="$ctrl.ngKeyup({ $event })"
29
+ ng-keypress="$ctrl.ngKeypress({ $event })"
30
+ ng-keydown="$ctrl.ngKeydown({ $event })"
31
+ ui-br-cpfcnpj-mask />
32
+ </div>
@@ -1,99 +1,307 @@
1
1
  import './mbg-input-cpfcnpj.scss'
2
2
  import template from './mbg-input-cpfcnpj.html'
3
3
 
4
- class MbgInputCpfCnpjController {
5
- private ngChange
6
- private ngModel
7
- private ngRequired
8
- private ngDisabled
9
- private props
10
- public valid
11
-
12
- constructor(public $scope, public $element, public $attrs) {
13
- if ($attrs.ngRequired === '') { this.ngRequired = true }
14
- if ($attrs.ngDisabled === '') { this.ngDisabled = true }
15
- this.props = {
16
- placeholder: $attrs.placeholder || '',
17
- }
4
+ // ---------------------------------------------------------------------------
5
+ // Mask formatting helpers
6
+ // ---------------------------------------------------------------------------
7
+
8
+ const CPF_MASK = '###.###.###-##'
9
+ const CNPJ_MASK = '##.###.###/####-##'
10
+
11
+ function cleanValue(value: string): string {
12
+ return (value || '').replace(/[^A-Za-z0-9]/g, '').toUpperCase()
13
+ }
14
+
15
+ function hasLetters(value: string): boolean {
16
+ return /[A-Za-z]/.test(value)
17
+ }
18
+
19
+ function formatCpf(clean: string): string {
20
+ const digits = clean.replace(/[^0-9]/g, '').substring(0, 11)
21
+ const result: string[] = []
22
+ let digitIdx = 0
23
+
24
+ for (let i = 0; i < CPF_MASK.length && digitIdx < digits.length; i++) {
25
+ if (CPF_MASK[i] === '#') {
26
+ result.push(digits[digitIdx])
27
+ digitIdx++
28
+ } else {
29
+ result.push(CPF_MASK[i])
18
30
  }
31
+ }
19
32
 
20
- ngBlur(evt) {
21
- this.valid = evt.$event.target.value
22
- this.valid = this.valid.toString()
23
- this.valid = this.valid.replace(/[^0-9]/g, '')
24
- if (this.valid.length === 11) {
25
- this.validCpf(this.valid)
33
+ return result.join('')
34
+ }
35
+
36
+ function formatCnpj(clean: string): string {
37
+ const safe = clean.substring(0, 14)
38
+ const result: string[] = []
39
+ let cleanIndex = 0
40
+
41
+ for (let i = 0; i < CNPJ_MASK.length && cleanIndex < safe.length; i++) {
42
+ const maskChar = CNPJ_MASK[i]
43
+
44
+ if (maskChar === '#') {
45
+ const char = safe[cleanIndex]
46
+ const isCheckDigitPosition = cleanIndex >= 12
47
+
48
+ if (isCheckDigitPosition) {
49
+ if (/[0-9]/.test(char)) {
50
+ result.push(char)
26
51
  }
27
- if (this.valid.length === 14) {
28
- this.validCnpj(this.valid)
29
- } else {
30
- return false
52
+ cleanIndex++
53
+ } else {
54
+ if (/[A-Z0-9]/.test(char)) {
55
+ result.push(char)
31
56
  }
57
+ cleanIndex++
58
+ }
59
+ } else {
60
+ result.push(maskChar)
32
61
  }
62
+ }
33
63
 
34
- CalcDigits(digits, positions = 10, sumDigits = 0) {
35
- digits = digits.toString()
36
- for (let i = 0; i < digits.length; i++) {
37
- sumDigits = sumDigits + (digits[i] * positions)
38
- positions--
39
- if (positions < 2) {
40
- positions = 9
41
- }
42
- }
43
- sumDigits = sumDigits % 11
44
- if (sumDigits < 2) {
45
- sumDigits = 0
46
- } else {
47
- sumDigits = 11 - sumDigits
48
- }
49
- let cnpj = digits + sumDigits
50
- return cnpj
64
+ return result.join('')
65
+ }
66
+
67
+ function smartFormat(value: string): string {
68
+ const clean = normalizeForModel(value)
69
+ if (clean.length === 0) return ''
70
+ if (hasLetters(clean)) return formatCnpj(clean)
71
+ if (clean.length <= 11) return formatCpf(clean)
72
+ return formatCnpj(clean)
73
+ }
74
+
75
+ function normalizeForModel(value: string): string {
76
+ const clean = cleanValue(value)
77
+ if (!clean) return ''
78
+ if (hasLetters(clean)) return clean.substring(0, 14)
79
+ if (clean.length <= 11) return clean
80
+ return clean.substring(0, 14)
81
+ }
82
+
83
+ function isRepeatedNumeric(value: string): boolean {
84
+ return /^(\d)\1+$/.test(value)
85
+ }
86
+
87
+ function getDigitValue(char: string): number {
88
+ return char.charCodeAt(0) - 48
89
+ }
90
+
91
+ function calculateCpfDigit(base: string, startWeight: number): number {
92
+ let sum = 0
93
+
94
+ for (let i = 0; i < base.length; i++) {
95
+ sum += Number(base[i]) * (startWeight - i)
96
+ }
97
+
98
+ const remainder = sum % 11
99
+ return remainder < 2 ? 0 : 11 - remainder
100
+ }
101
+
102
+ function isValidCpf(value: string): boolean {
103
+ if (!/^\d{11}$/.test(value) || isRepeatedNumeric(value)) return false
104
+
105
+ const firstDigit = calculateCpfDigit(value.slice(0, 9), 10)
106
+ const secondDigit = calculateCpfDigit(`${value.slice(0, 9)}${firstDigit}`, 11)
107
+ return value === `${value.slice(0, 9)}${firstDigit}${secondDigit}`
108
+ }
109
+
110
+ function calculateCnpjDigit(base: string, weights: number[]): number {
111
+ let sum = 0
112
+
113
+ for (let i = 0; i < base.length; i++) {
114
+ sum += getDigitValue(base[i]) * weights[i]
115
+ }
116
+
117
+ const remainder = sum % 11
118
+ return remainder < 2 ? 0 : 11 - remainder
119
+ }
120
+
121
+ function isValidCnpj(value: string): boolean {
122
+ if (!/^[A-Z0-9]{12}[0-9]{2}$/.test(value)) return false
123
+ if (/^\d{14}$/.test(value) && isRepeatedNumeric(value)) return false
124
+
125
+ const base = value.slice(0, 12)
126
+ const firstDigit = calculateCnpjDigit(base, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2])
127
+ const secondDigit = calculateCnpjDigit(
128
+ `${base}${firstDigit}`,
129
+ [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2],
130
+ )
131
+
132
+ return value.slice(12) === `${firstDigit}${secondDigit}`
133
+ }
134
+
135
+ function isCpfCandidate(value: string): boolean {
136
+ return /^\d*$/.test(value) && value.length <= 11
137
+ }
138
+
139
+ function isCnpjCandidate(value: string): boolean {
140
+ return value.length > 11 || hasLetters(value)
141
+ }
142
+
143
+ function isDocumentValid(value: string): boolean {
144
+ if (!value) return true
145
+ if (isCpfCandidate(value)) {
146
+ if (value.length < 11) return true
147
+ return isValidCpf(value)
148
+ }
149
+ if (isCnpjCandidate(value)) {
150
+ if (value.length < 14) return true
151
+ if (value.length > 14) return false
152
+ return isValidCnpj(value)
153
+ }
154
+ return false
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Directive: mbg-alphanumeric-cnpj-mask
159
+ // ---------------------------------------------------------------------------
160
+
161
+ class MbgAlphanumericCnpjMaskDirective {
162
+ static $inject = []
163
+
164
+ require = 'ngModel'
165
+ restrict = 'A'
166
+
167
+ link = (_scope, _element, _attrs, ngModel) => {
168
+ ngModel.$parsers.push((viewValue: string) => {
169
+ const clean = normalizeForModel(viewValue || '')
170
+ const formatted = smartFormat(clean)
171
+
172
+ if ((viewValue || '') !== formatted) {
173
+ ngModel.$setViewValue(formatted)
174
+ ngModel.$render()
175
+ }
176
+
177
+ return clean
178
+ })
179
+
180
+ ngModel.$formatters.push((modelValue: string) => {
181
+ return smartFormat(modelValue || '')
182
+ })
183
+
184
+ ngModel.$validators.cpf = (modelValue: string) => {
185
+ const clean = normalizeForModel(modelValue || '')
186
+ if (!clean || !isCpfCandidate(clean)) return true
187
+ if (clean.length < 11) return true
188
+ return isValidCpf(clean)
51
189
  }
52
190
 
53
- validCpf(value) {
54
- let digits = value.substr(0, 9)
55
- let firstCalc = this.CalcDigits(digits)
56
- let newCpf = this.CalcDigits(firstCalc, 11)
57
- if (newCpf === value) {
58
- return true
59
- } else {
60
- return false
61
- }
191
+ ngModel.$validators.cnpj = (modelValue: string) => {
192
+ const clean = normalizeForModel(modelValue || '')
193
+ if (!clean || !isCnpjCandidate(clean)) return true
194
+ if (clean.length < 14) return true
195
+ if (clean.length > 14) return false
196
+ return isValidCnpj(clean)
62
197
  }
63
198
 
64
- validCnpj(value) {
65
- let original = value
66
- let firstNumbers = value.substr(0, 12)
67
- let firstCalc = this.CalcDigits(firstNumbers, 5)
68
- let secondCalc = this.CalcDigits(firstCalc, 6)
69
- if (secondCalc === original) {
70
- return true
71
- }
72
- return false
199
+ ngModel.$validators.cpfcnpj = (modelValue: string) => {
200
+ const clean = normalizeForModel(modelValue || '')
201
+ return isDocumentValid(clean)
73
202
  }
203
+ }
204
+ }
74
205
 
75
- onChange() {
76
- if (this.ngChange) {
77
- this.ngChange({})
78
- }
206
+ const mbgAlphanumericCnpjMask = () => new MbgAlphanumericCnpjMaskDirective()
207
+
208
+ // ---------------------------------------------------------------------------
209
+ // Component: mbg-input-cpfcnpj
210
+ // ---------------------------------------------------------------------------
211
+
212
+ class MbgInputCpfCnpjController {
213
+ private ngChange
214
+ private ngModel
215
+ private ngRequired
216
+ private ngDisabled
217
+ private allowAlphanumeric: boolean
218
+ private name
219
+ private props
220
+ public valid
221
+
222
+ constructor(
223
+ public $scope,
224
+ public $element,
225
+ public $attrs,
226
+ ) {
227
+ if ($attrs.ngRequired === '') {
228
+ this.ngRequired = true
229
+ }
230
+ if ($attrs.ngDisabled === '') {
231
+ this.ngDisabled = true
232
+ }
233
+ this.props = {
234
+ placeholder: $attrs.placeholder || '',
235
+ }
236
+ }
237
+
238
+ $onInit() {
239
+ this.allowAlphanumeric = this.allowAlphanumeric === true
240
+ }
241
+
242
+ ngBlur(evt) {
243
+ this.valid = evt.$event.target.value
244
+ this.valid = this.valid.toString()
245
+ this.valid = this.valid.replace(/[^0-9]/g, '')
246
+ if (this.valid.length === 11) {
247
+ this.validCpf(this.valid)
248
+ }
249
+ if (this.valid.length === 14) {
250
+ this.validCnpj(this.valid)
251
+ } else {
252
+ return false
79
253
  }
254
+ }
255
+
256
+ CalcDigits(digits, positions = 10, sumDigits = 0) {
257
+ digits = digits.toString()
258
+ for (let i = 0; i < digits.length; i++) {
259
+ sumDigits += digits[i] * positions
260
+ positions--
261
+ if (positions < 2) positions = 9
262
+ }
263
+ sumDigits = sumDigits % 11
264
+ if (sumDigits < 2) sumDigits = 0
265
+ else sumDigits = 11 - sumDigits
266
+ return digits + sumDigits
267
+ }
268
+
269
+ validCpf(value) {
270
+ const digits = value.substr(0, 9)
271
+ const firstCalc = this.CalcDigits(digits)
272
+ const newCpf = this.CalcDigits(firstCalc, 11)
273
+ return newCpf === value
274
+ }
275
+
276
+ validCnpj(value) {
277
+ const firstNumbers = value.substr(0, 12)
278
+ const firstCalc = this.CalcDigits(firstNumbers, 5)
279
+ const secondCalc = this.CalcDigits(firstCalc, 6)
280
+ return secondCalc === value
281
+ }
282
+
283
+ onChange() {
284
+ if (this.ngChange) this.ngChange({})
285
+ }
80
286
  }
81
287
  MbgInputCpfCnpjController.$inject = ['$scope', '$element', '$attrs']
82
288
 
83
289
  const mbgInputCpfCnpj = {
84
- bindings: {
85
- ngModel: '=',
86
- ngChange: '&?',
87
- ngRequired: '=?',
88
- ngDisabled: '=?',
89
- ngBlur: '&?',
90
- ngFocus: '&?',
91
- ngKeyup: '&?',
92
- ngKeypress: '&?',
93
- ngKeydown: '&?',
94
- },
95
- template,
96
- controller: MbgInputCpfCnpjController,
97
- }
98
-
99
- export { mbgInputCpfCnpj }
290
+ bindings: {
291
+ ngModel: '=',
292
+ ngChange: '&?',
293
+ ngRequired: '=?',
294
+ ngDisabled: '=?',
295
+ ngBlur: '&?',
296
+ ngFocus: '&?',
297
+ ngKeyup: '&?',
298
+ ngKeypress: '&?',
299
+ ngKeydown: '&?',
300
+ allowAlphanumeric: '<?',
301
+ name: '@?',
302
+ },
303
+ template,
304
+ controller: MbgInputCpfCnpjController,
305
+ }
306
+
307
+ export { mbgInputCpfCnpj, mbgAlphanumericCnpjMask }