@mk-kit/ui 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  **Themable, accessible Angular component library for admin dashboards, back-offices and internal tools.**
4
4
 
5
- 175+ standalone components, directives and services — data tables, charts,
5
+ 185+ components (225+ selectors with directives and pipes) and 16 services — data tables, charts,
6
6
  date & time pickers, editors, kanban, overlays, an app shell — written for
7
7
  Angular 22 with signals and `OnPush` from day one. Every visual value is a
8
8
  `--mk-*` CSS custom property; light and dark ship out of the box; WCAG 2.1 AA
9
- is the target, not the marketing. MIT licensed. Zero runtime dependencies
10
- beyond Angular.
9
+ is the target, not the marketing. MIT licensed. No third-party runtime
10
+ dependencies — only Angular and the sibling `@mk-kit/validators` (the
11
+ identifier checks, 14 kB, tree-shakeable).
11
12
 
12
13
  - **Docs & live demos:** <https://mk-kit.dev>
13
14
  - **API reference:** <https://mk-kit.dev/api> — also as [api.json](https://mk-kit.dev/api.json), [llms.txt](https://mk-kit.dev/llms.txt) and [llms-full.txt](https://mk-kit.dev/llms-full.txt) for tools and AI assistants
@@ -103,10 +104,12 @@ import { MkDatePicker } from '@mk-kit/ui/datetime';
103
104
  ```
104
105
 
105
106
  Available: `core`, `forms`, `datetime`, `table`, `data`, `navigation`,
106
- `feedback`, `directives`, `dnd`, `media`, `icon`, `button`, `checkbox`, `chip`,
107
- `context-menu`, `layout`, `chat`, `query-builder`, `dynamic-form`, `rich-text`, `block-editor`,
108
- plus `testing` (component harnesses for specs — never re-exported from the
109
- umbrella). `sideEffects: false` throughout.
107
+ `feedback`, `directives`, `dnd`, `media`, `icon`, `icon/extended` (the opt-in
108
+ 305-icon set), `button`, `checkbox`, `chip`, `context-menu`, `layout`, `chat`,
109
+ `query-builder`, `dynamic-form`, `rich-text`, `block-editor`, plus `testing`
110
+ (component harnesses for specs) and `locales/pl` (the Polish string pack) —
111
+ the last two are never re-exported from the umbrella. `sideEffects: false`
112
+ throughout.
110
113
 
111
114
  ## Theming
112
115
 
@@ -12,6 +12,7 @@ import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
12
12
  import { mkMoveItemInArray, MkDropList, MkDrag, MkDragHandle } from '@mk-kit/ui/dnd';
13
13
  import { MkTree } from '@mk-kit/ui/navigation';
14
14
  import { mkApplyMask, MkMask, mkMaskCaret } from '@mk-kit/ui/directives';
15
+ import { detectCardBrand, isLuhn, IBAN_LENGTHS, ibanMod97, isIban, isNip } from '@mk-kit/validators';
15
16
  export * from '@mk-kit/ui/checkbox';
16
17
 
17
18
  /**
@@ -7983,41 +7984,13 @@ const MK_CARD_BRAND_NAMES = {
7983
7984
  diners: 'Diners Club',
7984
7985
  jcb: 'JCB',
7985
7986
  };
7986
- /** Detect the card network from the leading digits (IIN ranges). */
7987
+ /** Detect the card network from the leading digits `detectCardBrand` from `@mk-kit/validators`. */
7987
7988
  function mkDetectCardBrand(digits) {
7988
- if (!digits)
7989
- return null;
7990
- if (/^3[47]/.test(digits))
7991
- return 'amex';
7992
- if (/^(30[0-5]|36|38)/.test(digits))
7993
- return 'diners';
7994
- if (/^35/.test(digits))
7995
- return 'jcb';
7996
- if (/^(6011|64[4-9]|65)/.test(digits))
7997
- return 'discover';
7998
- if (/^(5[1-5]|22[2-9]|2[3-6]|27[0-2])/.test(digits))
7999
- return 'mastercard';
8000
- if (/^4/.test(digits))
8001
- return 'visa';
8002
- return null;
7989
+ return detectCardBrand(digits);
8003
7990
  }
8004
- /** Luhn (mod 10) checksum over a digit string. */
7991
+ /** Luhn (mod 10) checksum over a digit string — `isLuhn` from `@mk-kit/validators`. */
8005
7992
  function mkLuhnCheck(digits) {
8006
- if (!/^\d+$/.test(digits))
8007
- return false;
8008
- let sum = 0;
8009
- let double = false;
8010
- for (let i = digits.length - 1; i >= 0; i--) {
8011
- let d = digits.charCodeAt(i) - 48;
8012
- if (double) {
8013
- d *= 2;
8014
- if (d > 9)
8015
- d -= 9;
8016
- }
8017
- sum += d;
8018
- double = !double;
8019
- }
8020
- return sum % 10 === 0;
7993
+ return /^\d+$/.test(digits) && isLuhn(digits);
8021
7994
  }
8022
7995
  /** Grouping masks per brand (digit counts differ). */
8023
7996
  const BRAND_MASKS = {
@@ -8201,42 +8174,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8201
8174
  ], template: "<input\n type=\"text\"\n class=\"mk-card-number-input__field\"\n [id]=\"inputId\"\n autocomplete=\"cc-number\"\n spellcheck=\"false\"\n [mkMask]=\"mask()\"\n [value]=\"masked()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : i18n.cardNumber\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n (unmaskedChange)=\"onDigitsChange($event)\"\n (blur)=\"onBlur()\"\n/>\n\n@if (showBrand() && brandName()) {\n <span class=\"mk-card-number-input__brand\">\n {{ brandName() }}\n <span class=\"mk-visually-hidden\">{{ i18n.cardBrand(brandName()) }}</span>\n </span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;gap:var(--mk-space-2);width:100%;height:var(--mk-control-height-md);padding-inline-end:var(--mk-space-2);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);font-family:var(--mk-font-sans);overflow:hidden;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-card-number-input--sm){height:var(--mk-control-height-sm);border-radius:var(--mk-radius-sm)}:host(.mk-card-number-input--lg){height:var(--mk-control-height-lg)}:host(:hover:not(.mk-card-number-input--disabled)){border-color:var(--mk-border-strong)}:host(:focus-within){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-card-number-input--invalid){border-color:var(--mk-danger)}:host(.mk-card-number-input--invalid:focus-within){outline-color:var(--mk-danger)}:host(.mk-card-number-input--disabled){background-color:var(--mk-surface-2);opacity:.7}.mk-card-number-input__field{flex:1 1 auto;min-width:0;padding:0 var(--mk-space-3);font-family:inherit;font-size:var(--mk-font-size-md);font-variant-numeric:tabular-nums;color:var(--mk-text);background:transparent;border:none;outline:none}.mk-card-number-input__field::placeholder{color:var(--mk-text-subtle)}.mk-card-number-input__field:disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-card-number-input__brand{flex:none;padding:2px var(--mk-space-2);font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium);color:var(--mk-neutral-subtle-text);background-color:var(--mk-neutral-subtle);border-radius:var(--mk-radius-pill);white-space:nowrap;-webkit-user-select:none;user-select:none}:host(.mk-card-number-input--sm) .mk-card-number-input__field{font-size:var(--mk-font-size-sm)}:host(.mk-card-number-input--lg) .mk-card-number-input__field{font-size:var(--mk-font-size-lg)}@media(prefers-reduced-motion:reduce){:host{transition:none}}@media(pointer:coarse){.mk-card-number-input__field{font-size:max(var(--mk-font-size-md),16px)}:host(.mk-card-number-input--sm) .mk-card-number-input__field{font-size:max(var(--mk-font-size-sm),16px)}}\n"] }]
8202
8175
  }], propDecorators: { showBrand: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBrand", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], brandChange: [{ type: i0.Output, args: ["brandChange"] }] } });
8203
8176
 
8204
- /** IBAN length per ISO 13616 country code (SEPA + common others). */
8205
- const MK_IBAN_LENGTHS = {
8206
- AD: 24, AE: 23, AL: 28, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22,
8207
- BR: 29, CH: 21, CR: 22, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20,
8208
- EG: 29, ES: 24, FI: 18, FO: 18, FR: 27, GB: 22, GE: 22, GI: 23, GL: 18,
8209
- GR: 27, HR: 21, HU: 28, IE: 22, IL: 23, IS: 26, IT: 27, JO: 30, KW: 30,
8210
- KZ: 20, LB: 28, LI: 21, LT: 20, LU: 20, LV: 21, MC: 27, MD: 24, ME: 22,
8211
- MK: 19, MT: 31, NL: 18, NO: 15, PK: 24, PL: 28, PT: 25, QA: 29, RO: 24,
8212
- RS: 22, SA: 24, SE: 24, SI: 19, SK: 24, SM: 27, TN: 24, TR: 26, UA: 29,
8213
- VA: 22, XK: 20,
8214
- };
8215
- /** The maximum IBAN length the standard allows. */
8216
- const IBAN_MAX = 34;
8217
8177
  /**
8218
- * ISO 13616 mod-97 check over a compact (no spaces, uppercase) IBAN.
8219
- * Assumes the shape `CC00…` has already been length-checked by the caller.
8178
+ * IBAN lengths per country re-exported from `@mk-kit/validators`
8179
+ * (`IBAN_LENGTHS`, the SWIFT registry).
8220
8180
  */
8181
+ const MK_IBAN_LENGTHS = IBAN_LENGTHS;
8182
+ /** Longest IBAN in the registry (Malta, 31) rounded up to the ISO 13616 maximum. */
8183
+ const IBAN_MAX = 34;
8184
+ /** ISO 7064 MOD 97-10 over a compact (uppercase, no separators) IBAN. */
8221
8185
  function mkIbanChecksum(compact) {
8222
- if (!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(compact))
8223
- return false;
8224
- const rearranged = compact.slice(4) + compact.slice(0, 4);
8225
- let rem = 0;
8226
- for (const ch of rearranged) {
8227
- const code = ch.charCodeAt(0);
8228
- // A→10 … Z→35 contribute two digits; plain digits contribute one.
8229
- if (code >= 65)
8230
- rem = (rem * 100 + (code - 55)) % 97;
8231
- else
8232
- rem = (rem * 10 + (code - 48)) % 97;
8233
- }
8234
- return rem === 1;
8186
+ return /^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(compact) && ibanMod97(compact) === 1;
8235
8187
  }
8236
- /** Full IBAN validity: known country, exact length, mod-97 check. */
8188
+ /**
8189
+ * Shape, the registry length for the country and the checksum — delegates
8190
+ * to `isIban` from `@mk-kit/validators`. Unlike that function this one keeps
8191
+ * the input kit's stricter rule that the country must be in the table.
8192
+ */
8237
8193
  function mkIbanIsValid(compact) {
8238
- const length = MK_IBAN_LENGTHS[compact.slice(0, 2)];
8239
- return (length !== undefined && compact.length === length && mkIbanChecksum(compact));
8194
+ return MK_IBAN_LENGTHS[compact.slice(0, 2)] !== undefined && isIban(compact);
8240
8195
  }
8241
8196
  /**
8242
8197
  * Reactive-forms validator for an IBAN (any supported country). Empty values
@@ -8429,21 +8384,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8429
8384
  ], template: "<input\n type=\"text\"\n class=\"mk-iban-input__field\"\n [id]=\"inputId\"\n autocomplete=\"off\"\n autocapitalize=\"characters\"\n spellcheck=\"false\"\n [value]=\"grouped()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : i18n.iban\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;width:100%;font-family:var(--mk-font-sans)}.mk-iban-input__field{width:100%;min-width:0;height:var(--mk-control-height-md);padding:0 var(--mk-space-3);font-family:var(--mk-font-mono);font-size:var(--mk-font-size-md);color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);outline:none;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-iban-input--sm) .mk-iban-input__field{height:var(--mk-control-height-sm);padding:0 var(--mk-space-2);font-size:var(--mk-font-size-sm);border-radius:var(--mk-radius-sm)}:host(.mk-iban-input--lg) .mk-iban-input__field{height:var(--mk-control-height-lg);padding:0 var(--mk-space-4);font-size:var(--mk-font-size-lg)}.mk-iban-input__field::placeholder{color:var(--mk-text-subtle);font-family:var(--mk-font-sans)}.mk-iban-input__field:hover:not(:disabled){border-color:var(--mk-border-strong)}.mk-iban-input__field:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-iban-input--invalid) .mk-iban-input__field{border-color:var(--mk-danger)}:host(.mk-iban-input--invalid) .mk-iban-input__field:focus-visible{outline-color:var(--mk-danger)}.mk-iban-input__field:disabled{background-color:var(--mk-surface-2);color:var(--mk-text-disabled);border-color:var(--mk-border-subtle);cursor:not-allowed;opacity:.7}@media(prefers-reduced-motion:reduce){.mk-iban-input__field{transition:none}}@media(pointer:coarse){.mk-iban-input__field{font-size:max(var(--mk-font-size-md),16px)}:host(.mk-iban-input--sm) .mk-iban-input__field{font-size:max(var(--mk-font-size-sm),16px)}}\n"] }]
8430
8385
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
8431
8386
 
8432
- /** NIP weights, applied to the first nine digits (PL). */
8433
- const NIP_WEIGHTS = [6, 5, 7, 2, 3, 4, 5, 6, 7];
8434
8387
  /**
8435
- * Polish NIP checksum: the weighted sum of the first nine digits modulo 11 is
8436
- * the tenth (check) digit. A remainder of 10 can never be a digit, so such a
8437
- * number is invalid whatever its last digit.
8388
+ * Polish NIP checksum delegates to `isNip` from `@mk-kit/validators`
8389
+ * (weights 6-5-7-2-3-4-5-6-7 mod 11; a remainder of 10 is invalid).
8438
8390
  */
8439
8391
  function mkNipChecksum(compact) {
8440
- if (!/^\d{10}$/.test(compact))
8441
- return false;
8442
- let sum = 0;
8443
- for (let i = 0; i < 9; i++)
8444
- sum += NIP_WEIGHTS[i] * Number(compact[i]);
8445
- const check = sum % 11;
8446
- return check < 10 && check === Number(compact[9]);
8392
+ return /^\d{10}$/.test(compact) && isNip(compact);
8447
8393
  }
8448
8394
  /**
8449
8395
  * The built-in tax-identifier formats (alphabetical by ISO code). Only Poland