From 4bff8a28239a504014e848910e8520b761e73c6e Mon Sep 17 00:00:00 2001 From: taiga-family-bot <140712314+taiga-family-bot@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:33:23 +0300 Subject: [PATCH 1/7] chore(deps): update taiga-ui (#7612) --- package-lock.json | 29 +++++++++++++++-------------- package.json | 4 ++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2dde3eee18f..47d2ad26e2d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,8 @@ "@schematics/angular": "16.2.12", "@taiga-ui/browserslist-config": "0.6.0", "@taiga-ui/commitlint-config": "0.7.5", - "@taiga-ui/cspell-config": "0.40.0", - "@taiga-ui/eslint-plugin-experience": "0.75.3", + "@taiga-ui/cspell-config": "0.40.1", + "@taiga-ui/eslint-plugin-experience": "0.75.4", "@taiga-ui/prettier-config": "0.11.9", "@taiga-ui/stylelint-config": "0.22.3", "@taiga-ui/tsconfig": "0.17.0", @@ -9054,16 +9054,17 @@ "link": true }, "node_modules/@taiga-ui/cspell-config": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/cspell-config/-/cspell-config-0.40.0.tgz", - "integrity": "sha512-mNcKBiNRJtFsIJId24gF4b7Dwa65p/YKEgQOItCzrRxILGsd6zJ6FlLwyhBVxinC/W+OdQFxFaWFFW3jQGwmAg==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@taiga-ui/cspell-config/-/cspell-config-0.40.1.tgz", + "integrity": "sha512-uy8KmM+k/Z7QAuGx2LdQUsSNw4x0Ac7ovXCtNfxO79fEjQG6RE7jHuoBbhPSneRJbX6iJjsPejWiOeZNsOFkTA==", "dev": true, + "license": "Apache-2.0", "peerDependencies": { "@cspell/dict-ar": "^1.1.0", "@cspell/dict-lorem-ipsum": "^4.0.0", "@cspell/dict-ru_ru": "^2.2.1", "@cspell/dict-scientific-terms-us": "^3.0.1", - "cspell": "^8.8.3" + "cspell": "^8.8.4" } }, "node_modules/@taiga-ui/demo": { @@ -9088,9 +9089,9 @@ } }, "node_modules/@taiga-ui/eslint-plugin-experience": { - "version": "0.75.3", - "resolved": "https://registry.npmjs.org/@taiga-ui/eslint-plugin-experience/-/eslint-plugin-experience-0.75.3.tgz", - "integrity": "sha512-yPaFhbIYNnEpoMIOpb3EyRPv7L1z+QICczltYmxKcuqpFUALlKEAf8KGuWqMnRlhKupRL0lCwNb/f2TfcPRvOA==", + "version": "0.75.4", + "resolved": "https://registry.npmjs.org/@taiga-ui/eslint-plugin-experience/-/eslint-plugin-experience-0.75.4.tgz", + "integrity": "sha512-4ntWlhSP2H300G2PyusBT1cGn36+uE6VY2owWPNAWTHT0tei/9NZowyzyFKg11ltMXOZLJ3vpX6zP1I0UWJRog==", "dev": true, "peerDependencies": { "@angular-eslint/eslint-plugin": "^18.0.1", @@ -9098,11 +9099,11 @@ "@angular-eslint/template-parser": "^18.0.1", "@babel/eslint-parser": "^7.24.6", "@babel/eslint-plugin": "^7.24.6", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", - "@typescript-eslint/types": "^7.11.0", - "@typescript-eslint/typescript-estree": "^7.11.0", - "@typescript-eslint/utils": "^7.11.0", + "@typescript-eslint/eslint-plugin": "^7.12.0", + "@typescript-eslint/parser": "^7.12.0", + "@typescript-eslint/types": "^7.12.0", + "@typescript-eslint/typescript-estree": "^7.12.0", + "@typescript-eslint/utils": "^7.12.0", "babel-plugin-macros": "^3.1.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", diff --git a/package.json b/package.json index f67ca9afed90..a641f4ceae22 100644 --- a/package.json +++ b/package.json @@ -118,8 +118,8 @@ "@schematics/angular": "16.2.12", "@taiga-ui/browserslist-config": "0.6.0", "@taiga-ui/commitlint-config": "0.7.5", - "@taiga-ui/cspell-config": "0.40.0", - "@taiga-ui/eslint-plugin-experience": "0.75.3", + "@taiga-ui/cspell-config": "0.40.1", + "@taiga-ui/eslint-plugin-experience": "0.75.4", "@taiga-ui/prettier-config": "0.11.9", "@taiga-ui/stylelint-config": "0.22.3", "@taiga-ui/tsconfig": "0.17.0", From bf37dc358beddea4927c01d597c35cb5cebe6861 Mon Sep 17 00:00:00 2001 From: taiga-family-bot <140712314+taiga-family-bot@users.noreply.github.com> Date: Tue, 4 Jun 2024 04:02:47 +0300 Subject: [PATCH 2/7] chore(deps): update dependency @types/node to v20.14.1 (#7613) --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 47d2ad26e2d4..979f8983625a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "@tinkoff/ng-event-plugins": "3.2.0", "@types/glob": "8.1.0", "@types/loader-utils": "2.0.6", - "@types/node": "20.14.0", + "@types/node": "20.14.1", "@types/webpack-env": "1.18.5", "cpy-cli": "5.0.0", "glob": "10.4.1", @@ -9648,9 +9648,10 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.0.tgz", - "integrity": "sha512-5cHBxFGJx6L4s56Bubp4fglrEpmyJypsqI6RgzMfBHWUJQGWAAi8cWcgetEbZXHYXo9C2Fa4EEds/uSyS4cxmA==", + "version": "20.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.1.tgz", + "integrity": "sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==", + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } diff --git a/package.json b/package.json index a641f4ceae22..ff8d27452184 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "@tinkoff/ng-event-plugins": "3.2.0", "@types/glob": "8.1.0", "@types/loader-utils": "2.0.6", - "@types/node": "20.14.0", + "@types/node": "20.14.1", "@types/webpack-env": "1.18.5", "cpy-cli": "5.0.0", "glob": "10.4.1", From 850f6a06ab253fe24aef8b61936992115b65892b Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 4 Jun 2024 12:10:03 +0300 Subject: [PATCH 3/7] chore: include inputs/outputs of host directive `ActiveZone` in `DropdownOpen`'s API (#7615) --- .../core/directives/dropdown/dropdown-open.directive.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/projects/core/directives/dropdown/dropdown-open.directive.ts b/projects/core/directives/dropdown/dropdown-open.directive.ts index a15b0be2a2c6..2da21c1c789b 100644 --- a/projects/core/directives/dropdown/dropdown-open.directive.ts +++ b/projects/core/directives/dropdown/dropdown-open.directive.ts @@ -49,7 +49,14 @@ function shouldClose( @Directive({ standalone: true, selector: '[tuiDropdownOpen],[tuiDropdownOpenChange]', - hostDirectives: [TuiObscuredDirective, TuiActiveZoneDirective], + hostDirectives: [ + TuiObscuredDirective, + { + directive: TuiActiveZoneDirective, + inputs: ['tuiActiveZoneParent'], + outputs: ['tuiActiveZoneChange'], + }, + ], providers: [ TuiDropdownDriver, tuiAsDriver(TuiDropdownDriver), From ccaf5a648a2679f4b4b95c146838f61fdbda3293 Mon Sep 17 00:00:00 2001 From: Alex Inkin Date: Tue, 4 Jun 2024 17:10:48 +0800 Subject: [PATCH 4/7] refactor(addon-commerce): `InputCard` migrate to new textfield (#7581) Signed-off-by: waterplea Co-authored-by: taiga-family-bot --- .../abstract-input-card.ts | 5 +- .../components/input-card-grouped/index.ts | 1 + .../input-card-grouped.component.ts | 2 +- .../test/input-card-grouped.component.spec.ts | 8 +- .../components/input-card/index.ts | 3 +- .../input-card/input-card.component.ts | 135 --- .../input-card/input-card.directive.ts | 75 ++ .../input-card/input-card.options.ts | 4 +- .../input-card/input-card.style.less | 19 - .../input-card/input-card.template.html | 28 - .../test/input-card.component.spec.ts | 231 ----- .../components/input-cvc/index.ts | 2 +- .../input-cvc/input-cvc.component.ts | 102 -- .../input-cvc/input-cvc.directive.ts | 43 + .../components/input-cvc/input-cvc.style.less | 15 - .../input-cvc/input-cvc.template.html | 24 - .../test/input-cvc.component.spec.ts | 74 -- .../components/input-expire/index.ts | 2 +- .../input-expire/input-expire.component.ts | 77 -- .../input-expire/input-expire.directive.ts | 33 + .../input-expire/input-expire.style.less | 13 - .../input-expire/input-expire.template.html | 23 - .../test/input-expire.component.spec.ts | 46 - .../thumbnail-card.component.ts | 1 - .../utils/payment-system-icons.ts | 6 +- projects/cdk/constants/used-icons.ts | 6 +- .../steps/constants/identifiers-to-replace.ts | 6 +- .../textfield/textfield.component.ts | 9 +- .../components/textfield/textfield.module.ts | 2 +- .../components/textfield/textfield.style.less | 2 + .../styles/theme/appearance/textfield.less | 2 +- .../tests/addon-commerce/input-expire.spec.ts | 8 +- .../utils/page-objects/input-card.po.ts | 9 +- .../components/cell/examples/3/index.ts | 3 +- .../input-card/examples/1/index.html | 60 +- .../input-card/examples/1/index.less | 14 + .../components/input-card/examples/1/index.ts | 45 +- .../input-card/examples/2/index.html | 16 + .../components/input-card/examples/2/index.ts | 26 + .../input-card/examples/import/import.md | 8 +- .../input-card/examples/import/template.md | 27 +- .../modules/components/input-card/index.html | 204 +--- .../modules/components/input-card/index.less | 23 - .../modules/components/input-card/index.ts | 128 +-- .../components/input/examples/4/index.html | 16 +- .../components/input/examples/4/index.ts | 16 +- .../components/label/examples/4/index.ts | 4 +- .../components/textfield/examples/1/index.ts | 4 +- .../components/textfield/examples/2/index.ts | 4 +- .../components/textfield/examples/3/index.ts | 4 +- .../textfield/examples/import/import.md | 4 +- projects/demo/used-icons.ts | 2 +- projects/icons/all.ts | 62 +- projects/icons/src/tuiIconAmexOutline.svg | 11 + .../icons/src/tuiIconDinersClubOutline.svg | 15 + projects/icons/src/tuiIconDiscoverOutline.svg | 14 + .../icons/src/tuiIconElectronMonoOutline.svg | 72 +- projects/icons/src/tuiIconElectronOutline.svg | 23 + projects/icons/src/tuiIconHumoOutline.svg | 955 ++++++++++++++++++ projects/icons/src/tuiIconJCBOutline.svg | 20 + projects/icons/src/tuiIconMaestroOutline.svg | 16 + .../icons/src/tuiIconMastercardOutline.svg | 19 + projects/icons/src/tuiIconMirMonoOutline.svg | 32 +- projects/icons/src/tuiIconMirOutline.svg | 26 + projects/icons/src/tuiIconRuPayOutline.svg | 38 + projects/icons/src/tuiIconUnionPayOutline.svg | 24 + projects/icons/src/tuiIconUzcardOutline.svg | 29 + projects/icons/src/tuiIconVerveOutline.svg | 24 + projects/icons/src/tuiIconVisaMonoOutline.svg | 34 +- projects/icons/src/tuiIconVisaOutline.svg | 7 + 70 files changed, 1706 insertions(+), 1339 deletions(-) rename projects/addon-commerce/components/{input-card => input-card-grouped}/abstract-input-card.ts (93%) delete mode 100644 projects/addon-commerce/components/input-card/input-card.component.ts create mode 100644 projects/addon-commerce/components/input-card/input-card.directive.ts delete mode 100644 projects/addon-commerce/components/input-card/input-card.style.less delete mode 100644 projects/addon-commerce/components/input-card/input-card.template.html delete mode 100644 projects/addon-commerce/components/input-card/test/input-card.component.spec.ts delete mode 100644 projects/addon-commerce/components/input-cvc/input-cvc.component.ts create mode 100644 projects/addon-commerce/components/input-cvc/input-cvc.directive.ts delete mode 100644 projects/addon-commerce/components/input-cvc/input-cvc.style.less delete mode 100644 projects/addon-commerce/components/input-cvc/input-cvc.template.html delete mode 100644 projects/addon-commerce/components/input-cvc/test/input-cvc.component.spec.ts delete mode 100644 projects/addon-commerce/components/input-expire/input-expire.component.ts create mode 100644 projects/addon-commerce/components/input-expire/input-expire.directive.ts delete mode 100644 projects/addon-commerce/components/input-expire/input-expire.style.less delete mode 100644 projects/addon-commerce/components/input-expire/input-expire.template.html delete mode 100644 projects/addon-commerce/components/input-expire/test/input-expire.component.spec.ts create mode 100644 projects/demo/src/modules/components/input-card/examples/1/index.less create mode 100644 projects/demo/src/modules/components/input-card/examples/2/index.html create mode 100644 projects/demo/src/modules/components/input-card/examples/2/index.ts delete mode 100644 projects/demo/src/modules/components/input-card/index.less create mode 100644 projects/icons/src/tuiIconAmexOutline.svg create mode 100644 projects/icons/src/tuiIconDinersClubOutline.svg create mode 100644 projects/icons/src/tuiIconDiscoverOutline.svg create mode 100644 projects/icons/src/tuiIconElectronOutline.svg create mode 100644 projects/icons/src/tuiIconHumoOutline.svg create mode 100644 projects/icons/src/tuiIconJCBOutline.svg create mode 100644 projects/icons/src/tuiIconMaestroOutline.svg create mode 100644 projects/icons/src/tuiIconMastercardOutline.svg create mode 100644 projects/icons/src/tuiIconMirOutline.svg create mode 100644 projects/icons/src/tuiIconRuPayOutline.svg create mode 100644 projects/icons/src/tuiIconUnionPayOutline.svg create mode 100644 projects/icons/src/tuiIconUzcardOutline.svg create mode 100644 projects/icons/src/tuiIconVerveOutline.svg create mode 100644 projects/icons/src/tuiIconVisaOutline.svg diff --git a/projects/addon-commerce/components/input-card/abstract-input-card.ts b/projects/addon-commerce/components/input-card-grouped/abstract-input-card.ts similarity index 93% rename from projects/addon-commerce/components/input-card/abstract-input-card.ts rename to projects/addon-commerce/components/input-card-grouped/abstract-input-card.ts index 43d9dfafff09..1744c30b2f20 100644 --- a/projects/addon-commerce/components/input-card/abstract-input-card.ts +++ b/projects/addon-commerce/components/input-card-grouped/abstract-input-card.ts @@ -1,12 +1,11 @@ import {Directive, EventEmitter, inject, Input, Output} from '@angular/core'; +import type {TuiInputCardOptions} from '@taiga-ui/addon-commerce/components/input-card'; import type {TuiPaymentSystem} from '@taiga-ui/addon-commerce/types'; import {TUI_PAYMENT_SYSTEM_ICONS} from '@taiga-ui/addon-commerce/utils'; import type {TuiFocusableElementAccessor, TuiNativeFocusableElement} from '@taiga-ui/cdk'; import {AbstractTuiNullableControl, tuiPure} from '@taiga-ui/cdk'; import type {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; -import type {TuiInputCardOptions} from './input-card.options'; - @Directive() export abstract class AbstractTuiInputCard< T, @@ -23,7 +22,7 @@ export abstract class AbstractTuiInputCard< public cardSrc: PolymorpheusContent; @Input() - public autocompleteEnabled = this.options.autocompleteEnabled; + public autocompleteEnabled = this.options.autocomplete; @Output() public readonly binChange = new EventEmitter(); diff --git a/projects/addon-commerce/components/input-card-grouped/index.ts b/projects/addon-commerce/components/input-card-grouped/index.ts index f6f45ce8efee..f633d64886bd 100644 --- a/projects/addon-commerce/components/input-card-grouped/index.ts +++ b/projects/addon-commerce/components/input-card-grouped/index.ts @@ -1,3 +1,4 @@ +export * from './abstract-input-card'; export * from './input-card-grouped.component'; export * from './input-card-grouped.options'; export * from './input-card-grouped.providers'; diff --git a/projects/addon-commerce/components/input-card-grouped/input-card-grouped.component.ts b/projects/addon-commerce/components/input-card-grouped/input-card-grouped.component.ts index d0911fd87e73..49b9fb09400b 100644 --- a/projects/addon-commerce/components/input-card-grouped/input-card-grouped.component.ts +++ b/projects/addon-commerce/components/input-card-grouped/input-card-grouped.component.ts @@ -17,7 +17,6 @@ import {MaskitoDirective} from '@maskito/angular'; import type {MaskitoOptions} from '@maskito/core'; import {maskitoDateOptionsGenerator} from '@maskito/kit'; import {ResizeObserverModule} from '@ng-web-apis/resize-observer'; -import {AbstractTuiInputCard} from '@taiga-ui/addon-commerce/components/input-card'; import {TUI_CARD_MASK} from '@taiga-ui/addon-commerce/constants'; import {TuiFormatCardPipe} from '@taiga-ui/addon-commerce/pipes'; import type {TuiCard, TuiCodeCVCLength} from '@taiga-ui/addon-commerce/types'; @@ -56,6 +55,7 @@ import {TuiChevronDirective} from '@taiga-ui/kit'; import type {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; import {PolymorpheusModule} from '@tinkoff/ng-polymorpheus'; +import {AbstractTuiInputCard} from './abstract-input-card'; import type {TuiInputCardGroupedOptions} from './input-card-grouped.options'; import {TUI_INPUT_CARD_GROUPED_OPTIONS} from './input-card-grouped.options'; import {TUI_INPUT_CARD_GROUPED_TEXTS} from './input-card-grouped.providers'; diff --git a/projects/addon-commerce/components/input-card-grouped/test/input-card-grouped.component.spec.ts b/projects/addon-commerce/components/input-card-grouped/test/input-card-grouped.component.spec.ts index c3bcef157784..2a1484698618 100644 --- a/projects/addon-commerce/components/input-card-grouped/test/input-card-grouped.component.spec.ts +++ b/projects/addon-commerce/components/input-card-grouped/test/input-card-grouped.component.spec.ts @@ -206,8 +206,8 @@ describe('InputCardGrouped', () => { it('input-card-grouped have a default icon', () => { expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.defaultIcon).toBe('tuiIconVisaMono'); - expect(testComponent.component.icon).toBe('tuiIconVisaMono'); + expect(testComponent.component.defaultIcon).toBe('tuiIconVisa'); + expect(testComponent.component.icon).toBe('tuiIconVisa'); expect(testComponent.control.value).toEqual({card: '4111 1111 1111 1111'}); expect(expectCardOutlet()).toBeFalsy(); @@ -219,7 +219,7 @@ describe('InputCardGrouped', () => { testComponent.component.cardSrc = 'tuiIconMastercard'; expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.defaultIcon).toBe('tuiIconVisaMono'); + expect(testComponent.component.defaultIcon).toBe('tuiIconVisa'); expect(testComponent.component.icon).toBe('tuiIconMastercard'); expect(testComponent.control.value).toEqual({card: '4111 1111 1111 1111'}); @@ -233,7 +233,7 @@ describe('InputCardGrouped', () => { fixture.componentInstance.customIconTemplate; expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.defaultIcon).toBe('tuiIconVisaMono'); + expect(testComponent.component.defaultIcon).toBe('tuiIconVisa'); expect(testComponent.component.icon).toBeInstanceOf(TemplateRef); expect(testComponent.control.value).toEqual({card: '4111 1111 1111 1111'}); diff --git a/projects/addon-commerce/components/input-card/index.ts b/projects/addon-commerce/components/input-card/index.ts index 26d1774d17e1..8eb9ce2646d3 100644 --- a/projects/addon-commerce/components/input-card/index.ts +++ b/projects/addon-commerce/components/input-card/index.ts @@ -1,3 +1,2 @@ -export * from './abstract-input-card'; -export * from './input-card.component'; +export * from './input-card.directive'; export * from './input-card.options'; diff --git a/projects/addon-commerce/components/input-card/input-card.component.ts b/projects/addon-commerce/components/input-card/input-card.component.ts deleted file mode 100644 index ebe14a686809..000000000000 --- a/projects/addon-commerce/components/input-card/input-card.component.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type {AfterViewInit} from '@angular/core'; -import { - ChangeDetectionStrategy, - Component, - HostBinding, - inject, - ViewChild, -} from '@angular/core'; -import {MaskitoDirective, MaskitoPipe} from '@maskito/angular'; -import type {MaskitoOptions} from '@maskito/core'; -import {TUI_CARD_MASK} from '@taiga-ui/addon-commerce/constants'; -import {tuiAsControl, tuiAsFocusableItemAccessor} from '@taiga-ui/cdk'; -import type {TuiSizeL, TuiSizeS, TuiTextfieldHost} from '@taiga-ui/core'; -import { - TUI_TEXTFIELD_SIZE, - tuiAsTextfieldHost, - TuiPrimitiveTextfieldComponent, - TuiPrimitiveTextfieldModule, - TuiSvgComponent, - TuiTextfieldControllerModule, -} from '@taiga-ui/core'; - -import {AbstractTuiInputCard} from './abstract-input-card'; -import {TUI_INPUT_CARD_OPTIONS} from './input-card.options'; - -@Component({ - standalone: true, - selector: 'tui-input-card', - imports: [ - TuiPrimitiveTextfieldModule, - TuiTextfieldControllerModule, - MaskitoDirective, - MaskitoPipe, - TuiSvgComponent, - ], - templateUrl: './input-card.template.html', - styleUrls: ['./input-card.style.less'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - tuiAsFocusableItemAccessor(TuiInputCardComponent), - tuiAsControl(TuiInputCardComponent), - tuiAsTextfieldHost(TuiInputCardComponent), - ], -}) -export class TuiInputCardComponent - extends AbstractTuiInputCard - implements AfterViewInit, TuiTextfieldHost -{ - @ViewChild(TuiPrimitiveTextfieldComponent) - private readonly input?: TuiPrimitiveTextfieldComponent; - - private readonly textfieldSize = inject(TUI_TEXTFIELD_SIZE); - - protected readonly maskOptions: MaskitoOptions = { - mask: TUI_CARD_MASK, - }; - - constructor() { - super(inject(TUI_INPUT_CARD_OPTIONS)); - } - - public get card(): string { - return this.value ?? ''; - } - - public get nativeFocusableElement(): HTMLInputElement | null { - return this.input?.nativeFocusableElement ?? null; - } - - public get focused(): boolean { - return !!this.input && this.input.focused; - } - - public get inputMode(): TuiTextfieldHost['inputMode'] { - return 'text'; - } - - public override get value(): string { - return super.value ?? ''; - } - - public override set value(value: string | null) { - super.value = value ?? ''; - } - - public process(_input: HTMLInputElement): void {} - - public onValueChange(value: string): void { - const parsed = value.split(' ').join(''); - const currentBin = this.bin; - - this.value = parsed; - - const newBin = this.bin; - - if (currentBin !== newBin) { - this.binChange.emit(newBin); - } - } - - public onFocused(focused: boolean): void { - this.updateFocused(focused); - } - - public ngAfterViewInit(): void { - if (!this.nativeFocusableElement) { - return; - } - - this.nativeFocusableElement.inputMode = 'numeric'; - this.nativeFocusableElement.placeholder = - this.nativeFocusableElement.placeholder || '0000 0000 0000 0000'; - } - - public override writeValue(value: string | null): void { - const currentBin = this.bin; - - super.writeValue(value); - - const newBin = this.bin; - - if (currentBin !== newBin) { - this.binChange.emit(newBin); - } - } - - @HostBinding('attr.data-size') - protected get size(): TuiSizeL | TuiSizeS { - return this.textfieldSize.size; - } - - protected override getFallbackValue(): string { - return ''; - } -} diff --git a/projects/addon-commerce/components/input-card/input-card.directive.ts b/projects/addon-commerce/components/input-card/input-card.directive.ts new file mode 100644 index 000000000000..527cfda84823 --- /dev/null +++ b/projects/addon-commerce/components/input-card/input-card.directive.ts @@ -0,0 +1,75 @@ +import type {OnInit} from '@angular/core'; +import {Directive, inject, Input, Output} from '@angular/core'; +import {DefaultValueAccessor, NgControl} from '@angular/forms'; +import {MaskitoDirective} from '@maskito/angular'; +import {TUI_CARD_MASK} from '@taiga-ui/addon-commerce/constants'; +import type {TuiPaymentSystem} from '@taiga-ui/addon-commerce/types'; +import {TUI_PAYMENT_SYSTEM_ICONS} from '@taiga-ui/addon-commerce/utils'; +import {tuiControlValue, tuiPure} from '@taiga-ui/cdk'; +import {TUI_ICON_RESOLVER} from '@taiga-ui/core'; +import {distinctUntilChanged, map, skip, startWith, switchMap, timer} from 'rxjs'; + +import {TUI_INPUT_CARD_OPTIONS} from './input-card.options'; + +@Directive({ + standalone: true, + selector: 'input[tuiInputCard]', + host: { + inputmode: 'numeric', + placeholder: '0000 0000 0000 0000', + '[autocomplete]': 'autocomplete ? "cc-number" : "off"', + '[style.background-image]': 'backgroundImage', + }, + hostDirectives: [MaskitoDirective], +}) +export class TuiInputCard implements OnInit { + private readonly icons = inject(TUI_PAYMENT_SYSTEM_ICONS); + private readonly options = inject(TUI_INPUT_CARD_OPTIONS); + private readonly resolver = inject(TUI_ICON_RESOLVER); + private readonly control = inject(NgControl); + private readonly mask = inject(MaskitoDirective); + private readonly accessor = inject(DefaultValueAccessor, { + self: true, + optional: true, + }); + + @Input() + public autocomplete = this.options.autocomplete; + + @Input() + public icon: string | null = null; + + @Output() + public readonly binChange = timer(0).pipe( + switchMap(() => tuiControlValue(this.control)), + map(value => (value.length < 6 ? null : value.replace(' ', '').slice(0, 6))), + startWith(null), + distinctUntilChanged(), + skip(1), + ); + + public ngOnInit(): void { + this.mask.options = {mask: TUI_CARD_MASK}; + this.mask.ngOnChanges(); + + if (!this.accessor) { + return; + } + + const onChanges = this.accessor.onChange.bind(this.accessor); + + this.accessor.onChange = (value: string) => onChanges(value.replaceAll(' ', '')); + } + + protected get backgroundImage(): string | null { + const system = this.getPaymentSystem(this.control.value); + const url = this.icon || (system && this.resolver(this.icons[system])); + + return url && this.icon !== '' ? `url(${url})` : null; + } + + @tuiPure + private getPaymentSystem(value: string): TuiPaymentSystem | null { + return this.options.paymentSystemHandler(value); + } +} diff --git a/projects/addon-commerce/components/input-card/input-card.options.ts b/projects/addon-commerce/components/input-card/input-card.options.ts index a6d43909563a..9bd53a1610fa 100644 --- a/projects/addon-commerce/components/input-card/input-card.options.ts +++ b/projects/addon-commerce/components/input-card/input-card.options.ts @@ -6,7 +6,7 @@ import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk'; import type {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; export interface TuiInputCardOptions { - readonly autocompleteEnabled: boolean; + readonly autocomplete: boolean; cardSrc: PolymorpheusContent; readonly paymentSystemHandler: TuiHandler< string | null | undefined, @@ -17,7 +17,7 @@ export interface TuiInputCardOptions { export const TUI_INPUT_CARD_DEFAULT_OPTIONS: TuiInputCardOptions = { cardSrc: '', paymentSystemHandler: tuiGetPaymentSystem, - autocompleteEnabled: false, + autocomplete: false, }; export const TUI_INPUT_CARD_OPTIONS = tuiCreateToken(TUI_INPUT_CARD_DEFAULT_OPTIONS); diff --git a/projects/addon-commerce/components/input-card/input-card.style.less b/projects/addon-commerce/components/input-card/input-card.style.less deleted file mode 100644 index bc699072ff3a..000000000000 --- a/projects/addon-commerce/components/input-card/input-card.style.less +++ /dev/null @@ -1,19 +0,0 @@ -@import '@taiga-ui/core/styles/taiga-ui-local'; - -:host { - display: block; - border-radius: var(--tui-radius-m); - text-align: left; -} - -.t-input { - border-radius: inherit; - text-align: inherit; -} - -.t-payment-system { - width: 2rem; - height: 2rem; - // Visa logo color - color: #1434cb; -} diff --git a/projects/addon-commerce/components/input-card/input-card.template.html b/projects/addon-commerce/components/input-card/input-card.template.html deleted file mode 100644 index f742c29cf78f..000000000000 --- a/projects/addon-commerce/components/input-card/input-card.template.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/projects/addon-commerce/components/input-card/test/input-card.component.spec.ts b/projects/addon-commerce/components/input-card/test/input-card.component.spec.ts deleted file mode 100644 index 58b9b35acb7f..000000000000 --- a/projects/addon-commerce/components/input-card/test/input-card.component.spec.ts +++ /dev/null @@ -1,231 +0,0 @@ -import {Component, TemplateRef, ViewChild} from '@angular/core'; -import type {ComponentFixture} from '@angular/core/testing'; -import {TestBed} from '@angular/core/testing'; -import {FormControl, ReactiveFormsModule} from '@angular/forms'; -import {TuiInputCardComponent} from '@taiga-ui/addon-commerce'; -import type {Mock} from 'jest-mock'; - -describe('InputCard', () => { - @Component({ - template: ` - - - - - - `, - }) - class TestComponent { - @ViewChild(TuiInputCardComponent, {static: true}) - public component!: TuiInputCardComponent; - - @ViewChild('customIconTemplateRef', {read: TemplateRef}) - public customIconTemplateRef!: TemplateRef; - - public control = new FormControl(''); - - public onBinChange: (event: string | null) => void = jest.fn(); - } - - let fixture: ComponentFixture; - let testComponent: TestComponent; - - beforeEach(async () => { - TestBed.configureTestingModule({ - imports: [ReactiveFormsModule, TuiInputCardComponent], - declarations: [TestComponent], - }); - await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestComponent); - testComponent = fixture.componentInstance; - fixture.detectChanges(); - }); - - describe('focusable', () => { - it('touched', () => { - testComponent.component.onFocused(true); - - expect(testComponent.component.control?.touched).toBe(false); - - testComponent.component.onFocused(false); - - expect(testComponent.component.control?.touched).toBe(true); - }); - }); - - describe('binChange', () => { - it('Less than 6 characters', () => { - testComponent.control.setValue('12345'); - - expect(testComponent.onBinChange).not.toHaveBeenCalled(); - }); - - it('6 and more characters', () => { - testComponent.control.setValue('123456789'); - - expect(testComponent.onBinChange).toHaveBeenCalledWith('123456'); - }); - - it('trigger onBinChange only when bin has changed', () => { - testComponent.component.onValueChange('1234 5678 1111 2222'); - - expect(testComponent.onBinChange).toHaveBeenCalledWith('123456'); - expect(testComponent.component.value).toBe('1234567811112222'); - expect(testComponent.component.bin).toBe('123456'); - - testComponent.component.onValueChange('2222 4444 5555 6666'); - expect(testComponent.onBinChange).toHaveBeenCalledTimes(2); - - testComponent.component.onValueChange('2222 4444 5555 6666'); - expect(testComponent.onBinChange).toHaveBeenCalledTimes(2); - }); - - it('The value has changed, the first 6 characters are unchanged', () => { - testComponent.control.setValue('123456789'); - (testComponent.onBinChange as Mock).mockClear(); - testComponent.control.setValue('123456987'); - - expect(testComponent.onBinChange).not.toHaveBeenCalled(); - }); - - it('The value has changed, the first 6 characters have changed', () => { - testComponent.control.setValue('123456789'); - (testComponent.onBinChange as Mock).mockClear(); - testComponent.control.setValue('654321789'); - - expect(testComponent.onBinChange).toHaveBeenCalledWith('654321'); - }); - - it('The value has changed to less than 6 characters', () => { - testComponent.control.setValue('123456789'); - (testComponent.onBinChange as Mock).mockClear(); - testComponent.control.setValue('123'); - - expect(testComponent.onBinChange).toHaveBeenCalledWith(null); - }); - }); - - describe('paymentSystem', () => { - it('visa', () => { - testComponent.control.setValue('4111 1111 1111 1111'); - - expect(testComponent.component.paymentSystem).toBe('visa'); - }); - - it('electron', () => { - testComponent.control.setValue('4917300800000000'); - - expect(testComponent.component.paymentSystem).toBe('electron'); - }); - - it('mir', () => { - testComponent.control.setValue('2200654321000000'); - - expect(testComponent.component.paymentSystem).toBe('mir'); - }); - - it('mastercard', () => { - testComponent.control.setValue('5500 0000 0000 0004'); - - expect(testComponent.component.paymentSystem).toBe('mastercard'); - }); - - it('maestro', () => { - testComponent.control.setValue('6759649826438453'); - - expect(testComponent.component.paymentSystem).toBe('maestro'); - }); - }); - - describe('Formatting', () => { - it('4', async () => { - await testFormat('1234', '1234'); - }); - - it('7', async () => { - await testFormat('1234567', '1234 567'); - }); - - it('10', async () => { - await testFormat('1234567890', '1234 5678 90'); - }); - - it('12', async () => { - await testFormat('123456789012', '1234 5678 9012'); - }); - - it('13', async () => { - await testFormat('4000000000000', '4000 0000 0000 0'); - }); - - it('14', async () => { - await testFormat('40000000000000', '4000 0000 0000 00'); - }); - - it('15', async () => { - await testFormat('400000000000000', '4000 0000 0000 000'); - }); - - it('16', async () => { - await testFormat('4000000000000000', '4000 0000 0000 0000'); - }); - - it('17', async () => { - await testFormat('40000000000000000', '4000 0000 0000 0000 0'); - }); - - it('18', async () => { - await testFormat('400000000000000000', '4000 0000 0000 0000 00'); - }); - - it('19', async () => { - await testFormat('4000000000000000000', '4000 0000 0000 0000 000'); - }); - }); - - describe('customIconSource', () => { - beforeEach(() => testComponent.control.setValue('4111 1111 1111 1111')); - - it('input-card component has a default icon', () => { - expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.icon).toBe('tuiIconVisaMono'); - expect(testComponent.control.value).toBe('4111 1111 1111 1111'); - }); - - it('input-card component has a tuiIconElectron icon', () => { - testComponent.component.cardSrc = 'tuiIconElectron'; - expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.icon).toBe('tuiIconElectron'); - expect(testComponent.control.value).toBe('4111 1111 1111 1111'); - }); - - it('input-card component has an icon source as TemplateRef', () => { - testComponent.component.cardSrc = - fixture.componentInstance.customIconTemplateRef; - expect(testComponent.control.valid).toBe(true); - expect(testComponent.component.icon).toBeInstanceOf(TemplateRef); - expect(testComponent.control.value).toBe('4111 1111 1111 1111'); - }); - }); - - async function testFormat(value: string, formatted: string): Promise { - testComponent.control.setValue(value); - fixture.detectChanges(); - - await fixture.whenStable(); - - fixture.detectChanges(); - - await fixture.whenStable(); - - fixture.detectChanges(); - expect(getValue()).toBe(formatted); - } - - function getValue(): string { - return testComponent.component.nativeFocusableElement?.value || ''; - } -}); diff --git a/projects/addon-commerce/components/input-cvc/index.ts b/projects/addon-commerce/components/input-cvc/index.ts index 9dda6b14d3f0..90fb4e1f4d63 100644 --- a/projects/addon-commerce/components/input-cvc/index.ts +++ b/projects/addon-commerce/components/input-cvc/index.ts @@ -1 +1 @@ -export * from './input-cvc.component'; +export * from './input-cvc.directive'; diff --git a/projects/addon-commerce/components/input-cvc/input-cvc.component.ts b/projects/addon-commerce/components/input-cvc/input-cvc.component.ts deleted file mode 100644 index 194755f3bf40..000000000000 --- a/projects/addon-commerce/components/input-cvc/input-cvc.component.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - inject, - Input, - ViewChild, -} from '@angular/core'; -import {MaskitoDirective} from '@maskito/angular'; -import type {MaskitoOptions} from '@maskito/core'; -import type {TuiCodeCVCLength} from '@taiga-ui/addon-commerce/types'; -import type {TuiFocusableElementAccessor, TuiNativeFocusableElement} from '@taiga-ui/cdk'; -import { - AbstractTuiControl, - TUI_DIGIT_REGEXP, - tuiAsControl, - tuiAsFocusableItemAccessor, -} from '@taiga-ui/cdk'; -import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core'; -import { - TUI_TEXTFIELD_LABEL_OUTSIDE, - TUI_TEXTFIELD_SIZE, - TuiPrimitiveTextfieldComponent, - TuiPrimitiveTextfieldModule, -} from '@taiga-ui/core'; - -@Component({ - standalone: true, - selector: 'tui-input-cvc', - imports: [TuiPrimitiveTextfieldModule, MaskitoDirective], - templateUrl: './input-cvc.template.html', - styleUrls: ['./input-cvc.style.less'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - tuiAsFocusableItemAccessor(TuiInputCVCComponent), - tuiAsControl(TuiInputCVCComponent), - ], -}) -export class TuiInputCVCComponent - extends AbstractTuiControl - implements TuiFocusableElementAccessor -{ - @ViewChild(TuiPrimitiveTextfieldComponent) - private readonly input?: TuiPrimitiveTextfieldComponent; - - private readonly textfieldLabelOutside = inject(TUI_TEXTFIELD_LABEL_OUTSIDE); - private readonly textfieldSize = inject(TUI_TEXTFIELD_SIZE); - - @Input() - public autocompleteEnabled = false; - - @Input() - public hidden = true; - - public exampleText = '000'; - - public maskOptions: MaskitoOptions = { - mask: new Array(3).fill(TUI_DIGIT_REGEXP), - }; - - @Input() - public set length(length: TuiCodeCVCLength) { - this.exampleText = '0'.repeat(length); - this.maskOptions = { - mask: new Array(length).fill(TUI_DIGIT_REGEXP), - }; - } - - public get nativeFocusableElement(): TuiNativeFocusableElement | null { - return this.input?.nativeFocusableElement ?? null; - } - - public get focused(): boolean { - return !!this.input && this.input.focused; - } - - @HostBinding('attr.data-size') - protected get size(): TuiSizeL | TuiSizeS { - return this.textfieldSize.size; - } - - protected get autocomplete(): string { - return this.autocompleteEnabled ? 'cc-csc' : 'off'; - } - - protected get computedPlaceholder(): string { - return this.textfieldLabelOutside.labelOutside ? '' : this.exampleText; - } - - protected onFocused(focused: boolean): void { - this.updateFocused(focused); - } - - /** deprecated use 'value' setter */ - protected onValueChange(value: string): void { - this.value = value; - } - - protected getFallbackValue(): string { - return ''; - } -} diff --git a/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts b/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts new file mode 100644 index 000000000000..db83d6480cea --- /dev/null +++ b/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts @@ -0,0 +1,43 @@ +import type {OnChanges, OnInit} from '@angular/core'; +import {Directive, inject, Input} from '@angular/core'; +import {MaskitoDirective} from '@maskito/angular'; +import {TUI_INPUT_CARD_OPTIONS} from '@taiga-ui/addon-commerce/components/input-card'; +import {TUI_DIGIT_REGEXP} from '@taiga-ui/cdk'; + +@Directive({ + standalone: true, + selector: 'input[tuiInputCVC]', + host: { + inputmode: 'numeric', + '[autocomplete]': 'autocomplete ? "cc-csc" : "off"', + '[placeholder]': '"0".repeat(length)', + '[style.-webkit-text-security]': 'hidden ? "disc" : null', + '(copy.prevent)': '(0)', + }, + hostDirectives: [MaskitoDirective], +}) +export class TuiInputCVC implements OnInit, OnChanges { + private readonly mask = inject(MaskitoDirective); + + @Input() + public autocomplete = inject(TUI_INPUT_CARD_OPTIONS).autocomplete; + + @Input() + public hidden = true; + + @Input() + public length: 3 | 4 = 3; + + public ngOnInit(): void { + this.refresh(); + } + + public ngOnChanges(): void { + this.refresh(); + } + + private refresh(): void { + this.mask.options = {mask: new Array(this.length).fill(TUI_DIGIT_REGEXP)}; + this.mask.ngOnChanges(); + } +} diff --git a/projects/addon-commerce/components/input-cvc/input-cvc.style.less b/projects/addon-commerce/components/input-cvc/input-cvc.style.less deleted file mode 100644 index b4ad0a1aa6fd..000000000000 --- a/projects/addon-commerce/components/input-cvc/input-cvc.style.less +++ /dev/null @@ -1,15 +0,0 @@ -:host { - display: block; - max-width: 11rem; - border-radius: var(--tui-radius-m); - text-align: left; -} - -.t-input { - border-radius: inherit; - text-align: inherit; - - &_hidden { - -webkit-text-security: disc; - } -} diff --git a/projects/addon-commerce/components/input-cvc/input-cvc.template.html b/projects/addon-commerce/components/input-cvc/input-cvc.template.html deleted file mode 100644 index 48391db1729c..000000000000 --- a/projects/addon-commerce/components/input-cvc/input-cvc.template.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - diff --git a/projects/addon-commerce/components/input-cvc/test/input-cvc.component.spec.ts b/projects/addon-commerce/components/input-cvc/test/input-cvc.component.spec.ts deleted file mode 100644 index 80c4da971500..000000000000 --- a/projects/addon-commerce/components/input-cvc/test/input-cvc.component.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {Component, ViewChild} from '@angular/core'; -import type {ComponentFixture} from '@angular/core/testing'; -import {TestBed} from '@angular/core/testing'; -import {FormControl, ReactiveFormsModule} from '@angular/forms'; -import {TuiInputCVCComponent} from '@taiga-ui/addon-commerce'; -import {TUI_DIGIT_REGEXP} from '@taiga-ui/cdk'; - -describe('InputCVC', () => { - @Component({ - template: ` - - - `, - }) - class TestComponent { - @ViewChild('default') - public default!: TuiInputCVCComponent; - - @ViewChild('custom') - public custom!: TuiInputCVCComponent; - - public control = new FormControl(''); - } - - let fixture: ComponentFixture; - let testComponent: TestComponent; - - beforeEach(async () => { - TestBed.configureTestingModule({ - imports: [ReactiveFormsModule, TuiInputCVCComponent], - declarations: [TestComponent], - }); - await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestComponent); - testComponent = fixture.componentInstance; - fixture.detectChanges(); - }); - - describe('Default', () => { - it('exampleText', () => { - expect(testComponent.default.exampleText).toBe('000'); - }); - - it('MaskOptions', () => { - expect(testComponent.default.maskOptions).toEqual({ - mask: [TUI_DIGIT_REGEXP, TUI_DIGIT_REGEXP, TUI_DIGIT_REGEXP], - }); - }); - }); - - describe('Modified', () => { - it('exampleText', () => { - expect(testComponent.custom.exampleText).toBe('0000'); - }); - - it('MaskOptions', () => { - expect(testComponent.custom.maskOptions).toEqual({ - mask: [ - TUI_DIGIT_REGEXP, - TUI_DIGIT_REGEXP, - TUI_DIGIT_REGEXP, - TUI_DIGIT_REGEXP, - ], - }); - }); - }); -}); diff --git a/projects/addon-commerce/components/input-expire/index.ts b/projects/addon-commerce/components/input-expire/index.ts index 5228ec51fc73..6dd428c4e352 100644 --- a/projects/addon-commerce/components/input-expire/index.ts +++ b/projects/addon-commerce/components/input-expire/index.ts @@ -1 +1 @@ -export * from './input-expire.component'; +export * from './input-expire.directive'; diff --git a/projects/addon-commerce/components/input-expire/input-expire.component.ts b/projects/addon-commerce/components/input-expire/input-expire.component.ts deleted file mode 100644 index e35c76adf4e9..000000000000 --- a/projects/addon-commerce/components/input-expire/input-expire.component.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - HostBinding, - inject, - Input, - ViewChild, -} from '@angular/core'; -import {MaskitoDirective} from '@maskito/angular'; -import {maskitoDateOptionsGenerator} from '@maskito/kit'; -import type {TuiFocusableElementAccessor} from '@taiga-ui/cdk'; -import { - AbstractTuiControl, - tuiAsControl, - tuiAsFocusableItemAccessor, -} from '@taiga-ui/cdk'; -import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core'; -import { - TUI_TEXTFIELD_SIZE, - TuiPrimitiveTextfieldComponent, - TuiPrimitiveTextfieldModule, -} from '@taiga-ui/core'; - -@Component({ - standalone: true, - selector: 'tui-input-expire', - imports: [TuiPrimitiveTextfieldModule, MaskitoDirective], - templateUrl: './input-expire.template.html', - styleUrls: ['./input-expire.style.less'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - tuiAsFocusableItemAccessor(TuiInputExpireComponent), - tuiAsControl(TuiInputExpireComponent), - ], -}) -export class TuiInputExpireComponent - extends AbstractTuiControl - implements TuiFocusableElementAccessor -{ - @ViewChild(TuiPrimitiveTextfieldComponent) - private readonly input?: TuiPrimitiveTextfieldComponent; - - private readonly textfieldSize = inject(TUI_TEXTFIELD_SIZE); - - protected readonly maskOptions = maskitoDateOptionsGenerator({ - mode: 'mm/yy', - separator: '/', - }); - - @Input() - public autocompleteEnabled = false; - - public get nativeFocusableElement(): HTMLInputElement | null { - return this.input?.nativeFocusableElement ?? null; - } - - public get focused(): boolean { - return !!this.input && this.input.focused; - } - - @HostBinding('attr.data-size') - protected get size(): TuiSizeL | TuiSizeS { - return this.textfieldSize.size; - } - - protected get autocomplete(): string { - return this.autocompleteEnabled ? 'cc-exp' : 'off'; - } - - protected onFocused(focused: boolean): void { - this.updateFocused(focused); - } - - protected getFallbackValue(): string { - return ''; - } -} diff --git a/projects/addon-commerce/components/input-expire/input-expire.directive.ts b/projects/addon-commerce/components/input-expire/input-expire.directive.ts new file mode 100644 index 000000000000..985f19ee70da --- /dev/null +++ b/projects/addon-commerce/components/input-expire/input-expire.directive.ts @@ -0,0 +1,33 @@ +import type {OnInit} from '@angular/core'; +import {Directive, inject, Input} from '@angular/core'; +import {MaskitoDirective} from '@maskito/angular'; +import {maskitoDateOptionsGenerator} from '@maskito/kit'; +import {TUI_INPUT_CARD_OPTIONS} from '@taiga-ui/addon-commerce/components/input-card'; + +const MASK = maskitoDateOptionsGenerator({ + mode: 'mm/yy', + separator: '/', +}); + +@Directive({ + standalone: true, + selector: 'input[tuiInputExpire]', + host: { + inputmode: 'numeric', + placeholder: '00/00', + translate: 'no', + '[autocomplete]': 'autocomplete ? "cc-exp" : "off"', + }, + hostDirectives: [MaskitoDirective], +}) +export class TuiInputExpire implements OnInit { + private readonly mask = inject(MaskitoDirective); + + @Input() + public autocomplete = inject(TUI_INPUT_CARD_OPTIONS).autocomplete; + + public ngOnInit(): void { + this.mask.options = MASK; + this.mask.ngOnChanges(); + } +} diff --git a/projects/addon-commerce/components/input-expire/input-expire.style.less b/projects/addon-commerce/components/input-expire/input-expire.style.less deleted file mode 100644 index 8133891bc40c..000000000000 --- a/projects/addon-commerce/components/input-expire/input-expire.style.less +++ /dev/null @@ -1,13 +0,0 @@ -@import '@taiga-ui/core/styles/taiga-ui-local'; - -:host { - display: block; - max-width: 11rem; - border-radius: var(--tui-radius-m); - text-align: left; -} - -.t-input { - border-radius: inherit; - text-align: inherit; -} diff --git a/projects/addon-commerce/components/input-expire/input-expire.template.html b/projects/addon-commerce/components/input-expire/input-expire.template.html deleted file mode 100644 index 1d3f33f9de8f..000000000000 --- a/projects/addon-commerce/components/input-expire/input-expire.template.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/projects/addon-commerce/components/input-expire/test/input-expire.component.spec.ts b/projects/addon-commerce/components/input-expire/test/input-expire.component.spec.ts deleted file mode 100644 index 6e4db593eee6..000000000000 --- a/projects/addon-commerce/components/input-expire/test/input-expire.component.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {Component, ViewChild} from '@angular/core'; -import type {ComponentFixture} from '@angular/core/testing'; -import {TestBed} from '@angular/core/testing'; -import {FormsModule} from '@angular/forms'; -import {By} from '@angular/platform-browser'; -import {TuiInputExpireComponent} from '@taiga-ui/addon-commerce'; - -describe('InputExpire', () => { - @Component({ - template: ` - - `, - }) - class TestComponent { - @ViewChild(TuiInputExpireComponent) - public input!: TuiInputExpireComponent; - - public value = ''; - } - - let fixture: ComponentFixture; - let testComponent: TestComponent; - let input: HTMLInputElement; - - beforeEach(async () => { - TestBed.configureTestingModule({ - imports: [FormsModule, TuiInputExpireComponent], - declarations: [TestComponent], - }); - await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestComponent); - testComponent = fixture.componentInstance; - fixture.detectChanges(); - input = fixture.debugElement.query(By.css('input')).nativeElement; - - await fixture.whenStable(); - }); - - it('does not change the correct input', () => { - input.value = '12/12'; - input.dispatchEvent(new Event('input', {bubbles: true})); - - expect(testComponent.value).toBe('12/12'); - expect(input.value).toBe('12/12'); - }); -}); diff --git a/projects/addon-commerce/components/thumbnail-card/thumbnail-card.component.ts b/projects/addon-commerce/components/thumbnail-card/thumbnail-card.component.ts index 438cb587e7ba..e1ec15a8ae1c 100644 --- a/projects/addon-commerce/components/thumbnail-card/thumbnail-card.component.ts +++ b/projects/addon-commerce/components/thumbnail-card/thumbnail-card.component.ts @@ -37,7 +37,6 @@ export class TuiThumbnailCardComponent { @Input() public iconRight = ''; - // TODO: Revisit this approach in 4.0 when icons are moved away from InputCard options protected get isMono(): boolean { switch (this.paymentSystem) { case 'mir': diff --git a/projects/addon-commerce/utils/payment-system-icons.ts b/projects/addon-commerce/utils/payment-system-icons.ts index 1b5cbcf69bac..48668a26ede1 100644 --- a/projects/addon-commerce/utils/payment-system-icons.ts +++ b/projects/addon-commerce/utils/payment-system-icons.ts @@ -4,9 +4,9 @@ import {tuiCreateToken} from '@taiga-ui/cdk'; export const TUI_PAYMENT_SYSTEM_ICONS: InjectionToken> = tuiCreateToken({ - mir: 'tuiIconMirMono', - visa: 'tuiIconVisaMono', - electron: 'tuiIconElectronMono', + mir: 'tuiIconMir', + visa: 'tuiIconVisa', + electron: 'tuiIconElectron', mastercard: 'tuiIconMastercard', maestro: 'tuiIconMaestro', amex: 'tuiIconAmex', diff --git a/projects/cdk/constants/used-icons.ts b/projects/cdk/constants/used-icons.ts index cf0726954e7a..2ed1667232a7 100644 --- a/projects/cdk/constants/used-icons.ts +++ b/projects/cdk/constants/used-icons.ts @@ -5,9 +5,9 @@ * Array of icons used in taiga-ui components */ export const TUI_USED_ICONS = [ - 'tuiIconMirMono', - 'tuiIconVisaMono', - 'tuiIconElectronMono', + 'tuiIconMir', + 'tuiIconVisa', + 'tuiIconElectron', 'tuiIconMastercard', 'tuiIconMaestro', 'tuiIconAmex', diff --git a/projects/cdk/schematics/ng-update/v4/steps/constants/identifiers-to-replace.ts b/projects/cdk/schematics/ng-update/v4/steps/constants/identifiers-to-replace.ts index 1083941ecaff..4bd9f79ee2f0 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/constants/identifiers-to-replace.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/constants/identifiers-to-replace.ts @@ -1349,7 +1349,7 @@ export const IDENTIFIERS_TO_REPLACE: ReplacementIdentifierMulti[] = [ moduleSpecifier: '@taiga-ui/addon-commerce', }, to: { - name: 'TuiInputCardComponent', + name: 'TuiInputCard', moduleSpecifier: '@taiga-ui/addon-mobile', }, }, @@ -1369,7 +1369,7 @@ export const IDENTIFIERS_TO_REPLACE: ReplacementIdentifierMulti[] = [ moduleSpecifier: '@taiga-ui/addon-commerce', }, to: { - name: 'TuiInputCVCComponent', + name: 'TuiInputCVC', moduleSpecifier: '@taiga-ui/addon-mobile', }, }, @@ -1379,7 +1379,7 @@ export const IDENTIFIERS_TO_REPLACE: ReplacementIdentifierMulti[] = [ moduleSpecifier: '@taiga-ui/addon-commerce', }, to: { - name: 'TuiInputExpireComponent', + name: 'TuiInputExpire', moduleSpecifier: '@taiga-ui/addon-mobile', }, }, diff --git a/projects/core/components/textfield/textfield.component.ts b/projects/core/components/textfield/textfield.component.ts index a4c571e13d72..4e1f589e3bed 100644 --- a/projects/core/components/textfield/textfield.component.ts +++ b/projects/core/components/textfield/textfield.component.ts @@ -42,17 +42,10 @@ export interface TuiTextfieldContext extends TuiContext { readonly active: boolean; } -// TODO: Move to legacy package before 4.0 @Component({ standalone: true, selector: 'tui-textfield', - imports: [ - CommonModule, - ResizeObserverModule, - TuiTextfieldDirective, - TuiButtonDirective, - PolymorpheusModule, - ], + imports: [CommonModule, ResizeObserverModule, TuiButtonDirective, PolymorpheusModule], templateUrl: './textfield.template.html', styleUrls: ['./textfield.style.less'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/projects/core/components/textfield/textfield.module.ts b/projects/core/components/textfield/textfield.module.ts index 24e66e61e84f..5326f2e4e0d3 100644 --- a/projects/core/components/textfield/textfield.module.ts +++ b/projects/core/components/textfield/textfield.module.ts @@ -23,4 +23,4 @@ import {TuiTextfieldOptionsDirective} from './textfield.options'; TuiTextfieldOptionsDirective, ], }) -export class TuiTextfieldModule {} +export class TuiTextfield {} diff --git a/projects/core/components/textfield/textfield.style.less b/projects/core/components/textfield/textfield.style.less index 25409c4ba3cd..fc7c354cf75c 100644 --- a/projects/core/components/textfield/textfield.style.less +++ b/projects/core/components/textfield/textfield.style.less @@ -154,6 +154,7 @@ ::ng-deep input, ::ng-deep select { pointer-events: auto; + background: padding-box transparent no-repeat center right 1rem / 2rem; &:read-only ~ .t-filler { display: none; @@ -225,6 +226,7 @@ .t-content { display: flex; + align-items: center; gap: 0.25rem; margin-inline-start: auto; } diff --git a/projects/core/styles/theme/appearance/textfield.less b/projects/core/styles/theme/appearance/textfield.less index a541a3d903a9..90bccd074efd 100644 --- a/projects/core/styles/theme/appearance/textfield.less +++ b/projects/core/styles/theme/appearance/textfield.less @@ -5,7 +5,7 @@ --t-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.1); - background: var(--tui-base-01); + background-color: var(--tui-base-01); color: var(--tui-text-01); box-shadow: var(--t-shadow); outline: 1px solid var(--tui-base-03); diff --git a/projects/demo-playwright/tests/addon-commerce/input-expire.spec.ts b/projects/demo-playwright/tests/addon-commerce/input-expire.spec.ts index 21a7316edb2b..989448724b09 100644 --- a/projects/demo-playwright/tests/addon-commerce/input-expire.spec.ts +++ b/projects/demo-playwright/tests/addon-commerce/input-expire.spec.ts @@ -1,4 +1,4 @@ -import {TuiDocumentationPagePO, tuiGoto, TuiInputCardPO} from '@demo-playwright/utils'; +import {tuiGoto, TuiInputCardPO} from '@demo-playwright/utils'; import type {Locator} from '@playwright/test'; import {expect, test} from '@playwright/test'; @@ -6,11 +6,9 @@ test.describe('InputExpire', () => { let expiryTextfield: Locator; test.beforeEach(async ({page}) => { - await tuiGoto(page, 'components/input-card/API'); + await tuiGoto(page, 'components/input-card'); - const {apiPageExample} = new TuiDocumentationPagePO(page); - - expiryTextfield = new TuiInputCardPO(apiPageExample).expiryTextfield; + expiryTextfield = new TuiInputCardPO(page).expiryTextfield; await expect(expiryTextfield).toBeEmpty(); }); diff --git a/projects/demo-playwright/utils/page-objects/input-card.po.ts b/projects/demo-playwright/utils/page-objects/input-card.po.ts index 9115ba669c65..c6df4b7a21aa 100644 --- a/projects/demo-playwright/utils/page-objects/input-card.po.ts +++ b/projects/demo-playwright/utils/page-objects/input-card.po.ts @@ -1,10 +1,7 @@ -import type {Locator} from '@playwright/test'; +import type {Page} from '@playwright/test'; export class TuiInputCardPO { - public readonly expiryTextfield = this.host - .locator('tui-input-expire') - .locator('input') - .first(); + public readonly expiryTextfield = this.page.locator('input[tuiInputExpire]').first(); - constructor(private readonly host: Locator) {} + constructor(private readonly page: Page) {} } diff --git a/projects/demo/src/modules/components/cell/examples/3/index.ts b/projects/demo/src/modules/components/cell/examples/3/index.ts index 3e3e35daaad9..e88ada68338d 100644 --- a/projects/demo/src/modules/components/cell/examples/3/index.ts +++ b/projects/demo/src/modules/components/cell/examples/3/index.ts @@ -3,7 +3,7 @@ import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiThumbnailCardComponent} from '@taiga-ui/addon-commerce'; -import {TuiIconComponent} from '@taiga-ui/core'; +import {TuiIconComponent, TuiTitleDirective} from '@taiga-ui/core'; import { TuiAvatarComponent, TuiAvatarStackComponent, @@ -25,6 +25,7 @@ import {TuiCellDirective} from '@taiga-ui/layout'; TuiCheckboxComponent, TuiThumbnailCardComponent, FormsModule, + TuiTitleDirective, ], templateUrl: './index.html', styleUrls: ['./index.less'], diff --git a/projects/demo/src/modules/components/input-card/examples/1/index.html b/projects/demo/src/modules/components/input-card/examples/1/index.html index 805297f5b609..5147c657562a 100644 --- a/projects/demo/src/modules/components/input-card/examples/1/index.html +++ b/projects/demo/src/modules/components/input-card/examples/1/index.html @@ -1,31 +1,35 @@ -

- form - tag is used for better autocomplete -

-
- - Card number + + + - - - Expire date - - - CVC/CVV - + +
+ + + + + + + + +
+ +

{{ form.value | json }}

diff --git a/projects/demo/src/modules/components/input-card/examples/1/index.less b/projects/demo/src/modules/components/input-card/examples/1/index.less new file mode 100644 index 000000000000..322ee68f16a6 --- /dev/null +++ b/projects/demo/src/modules/components/input-card/examples/1/index.less @@ -0,0 +1,14 @@ +form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +section { + display: flex; + gap: inherit; + + tui-textfield { + flex: 1; + } +} diff --git a/projects/demo/src/modules/components/input-card/examples/1/index.ts b/projects/demo/src/modules/components/input-card/examples/1/index.ts index cbd8be9bf33f..968f0f95f75f 100644 --- a/projects/demo/src/modules/components/input-card/examples/1/index.ts +++ b/projects/demo/src/modules/components/input-card/examples/1/index.ts @@ -1,31 +1,50 @@ -import {Component} from '@angular/core'; +import {AsyncPipe, JsonPipe} from '@angular/common'; +import {Component, inject} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import { - TuiInputCardComponent, - TuiInputCVCComponent, - TuiInputExpireComponent, + tuiCreateLuhnValidator, + TuiInputCard, + tuiInputCardOptionsProvider, + TuiInputCVC, + TuiInputExpire, } from '@taiga-ui/addon-commerce'; -import {TuiGroupDirective, TuiPrimitiveTextfieldModule} from '@taiga-ui/core'; +import { + TuiAlertService, + TuiErrorComponent, + TuiTextfield, + tuiTextfieldOptionsProvider, +} from '@taiga-ui/core'; +import {TuiFieldErrorPipe} from '@taiga-ui/kit'; @Component({ standalone: true, imports: [ - TuiGroupDirective, ReactiveFormsModule, - TuiInputCardComponent, - TuiInputExpireComponent, - TuiInputCVCComponent, - TuiPrimitiveTextfieldModule, + TuiTextfield, + TuiInputCVC, + TuiInputExpire, + TuiInputCard, + TuiErrorComponent, + TuiFieldErrorPipe, + AsyncPipe, + JsonPipe, ], templateUrl: './index.html', + styleUrls: ['./index.less'], encapsulation, changeDetection, + providers: [ + tuiInputCardOptionsProvider({autocomplete: true}), + tuiTextfieldOptionsProvider({cleaner: false}), + ], }) export default class ExampleComponent { + private readonly alerts = inject(TuiAlertService); + protected readonly form = new FormGroup({ - card: new FormControl(''), + card: new FormControl('', tuiCreateLuhnValidator('Card number is invalid')), expire: new FormControl(''), cvc: new FormControl(''), }); @@ -55,4 +74,8 @@ export default class ExampleComponent { return 'https://ng-web-apis.github.io/dist/assets/images/payment-request.svg'; } } + + protected onBinChange(bin: string | null): void { + this.alerts.open(String(bin), {label: '(binChange)'}).subscribe(); + } } diff --git a/projects/demo/src/modules/components/input-card/examples/2/index.html b/projects/demo/src/modules/components/input-card/examples/2/index.html new file mode 100644 index 000000000000..d33444dcc3cb --- /dev/null +++ b/projects/demo/src/modules/components/input-card/examples/2/index.html @@ -0,0 +1,16 @@ + + + + + diff --git a/projects/demo/src/modules/components/input-card/examples/2/index.ts b/projects/demo/src/modules/components/input-card/examples/2/index.ts new file mode 100644 index 000000000000..5d4e69fcf3c8 --- /dev/null +++ b/projects/demo/src/modules/components/input-card/examples/2/index.ts @@ -0,0 +1,26 @@ +import {NgIf} from '@angular/common'; +import {Component} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiInputCard, TuiThumbnailCardComponent} from '@taiga-ui/addon-commerce'; +import {TuiTextfield} from '@taiga-ui/core'; +import {TuiCheckboxComponent} from '@taiga-ui/kit'; + +@Component({ + standalone: true, + imports: [ + NgIf, + FormsModule, + TuiTextfield, + TuiInputCard, + TuiThumbnailCardComponent, + TuiCheckboxComponent, + ], + templateUrl: './index.html', + encapsulation, + changeDetection, +}) +export default class ExampleComponent { + protected card = '1234123412341234'; +} diff --git a/projects/demo/src/modules/components/input-card/examples/import/import.md b/projects/demo/src/modules/components/input-card/examples/import/import.md index 430657a4ae58..9c2974a88f04 100644 --- a/projects/demo/src/modules/components/input-card/examples/import/import.md +++ b/projects/demo/src/modules/components/input-card/examples/import/import.md @@ -1,5 +1,5 @@ ```ts -import {TuiInputCardComponent, TuiInputCVCComponent, TuiInputExpireComponent} from '@taiga-ui/addon-commerce'; +import {TuiInputCard, TuiInputCVC, TuiInputExpire} from '@taiga-ui/addon-commerce'; // ... @@ -7,9 +7,9 @@ import {TuiInputCardComponent, TuiInputCVCComponent, TuiInputExpireComponent} fr standalone: true, imports: [ // ... - TuiInputCardComponent, - TuiInputCVCComponent, - TuiInputExpireComponent, + TuiInputCard, + TuiInputCVC, + TuiInputExpire, ], // ... }) diff --git a/projects/demo/src/modules/components/input-card/examples/import/template.md b/projects/demo/src/modules/components/input-card/examples/import/template.md index 9903b3ae2d69..8c6ec7286601 100644 --- a/projects/demo/src/modules/components/input-card/examples/import/template.md +++ b/projects/demo/src/modules/components/input-card/examples/import/template.md @@ -1,7 +1,28 @@ ```html
- Card number - Expire date - CVC/CVV + + + + + + + + + + + +
``` diff --git a/projects/demo/src/modules/components/input-card/index.html b/projects/demo/src/modules/components/input-card/index.html index 36dd4550ffe2..54ea47424148 100644 --- a/projects/demo/src/modules/components/input-card/index.html +++ b/projects/demo/src/modules/components/input-card/index.html @@ -10,192 +10,38 @@ InputExpire and InputCVC - to input a card + to input a card. Use + tuiCreateLuhnValidator(message) + to create a + Validator + that uses Luhn algorithm

- - - - - -
- - Card number - - - Expire date - - - CVC/CVV - - - -
-
-
+ [content]="1 | tuiExample" + >

- Add - tuiCreateLuhnValidator(customMessage?) - to control validators to validate it with Luhn algorithm + form + tag is used for better autocomplete support

-
- - - TuiInputCardComponent - - - - Disabled state (use - formControl.disable() - ) - - - SVG card icon - - - BIN value (card first 6 symbols) - - - - - - - - TuiInputExpireComponent - - - - Disabled state (use - formControl.disable() - ) - - - Browser autocomplete of card - - - - - +
- - TuiInputCVCComponent - - - - Disabled state (use - formControl.disable() - ) - - - Browser autocomplete of card - - - Code length - - - - - - + +

+ Pass empty string to disable + icon + if you want to use custom content instead. +

+
- + diff --git a/projects/demo/src/modules/components/input-card/index.less b/projects/demo/src/modules/components/input-card/index.less deleted file mode 100644 index af1f592a06ab..000000000000 --- a/projects/demo/src/modules/components/input-card/index.less +++ /dev/null @@ -1,23 +0,0 @@ -@import '@taiga-ui/core/styles/taiga-ui-local'; - -.form { - display: flex; - flex-wrap: wrap; -} - -.control { - flex: 1; - margin-bottom: 0.25rem; - - &:not(:last-child) { - margin-right: 1.25rem; - } -} - -.error { - min-width: 100%; -} - -.title { - font: var(--tui-font-heading-5); -} diff --git a/projects/demo/src/modules/components/input-card/index.ts b/projects/demo/src/modules/components/input-card/index.ts index c044f4ea863c..926e795dfdb3 100644 --- a/projects/demo/src/modules/components/input-card/index.ts +++ b/projects/demo/src/modules/components/input-card/index.ts @@ -1,133 +1,11 @@ -import {Component, ViewChild} from '@angular/core'; -import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; +import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDemo} from '@demo/utils'; -import type {TuiCodeCVCLength} from '@taiga-ui/addon-commerce'; -import { - tuiCreateLuhnValidator, - TuiInputCardComponent, - TuiInputCVCComponent, - TuiInputExpireComponent, -} from '@taiga-ui/addon-commerce'; -import {TuiDocDocumentationPropertyConnectorDirective} from '@taiga-ui/addon-doc'; -import {tuiProvide} from '@taiga-ui/cdk'; -import type {TuiHintOptions} from '@taiga-ui/core'; -import {TuiErrorComponent, TuiHint, TuiTextfieldControllerModule} from '@taiga-ui/core'; -import {TuiAccordion, TuiFieldErrorPipe} from '@taiga-ui/kit'; - -import {ABSTRACT_PROPS_ACCESSOR} from '../abstract/abstract-props-accessor'; -import {AbstractExampleTuiControl} from '../abstract/control'; -import {InheritedDocumentationComponent} from '../abstract/inherited-documentation'; @Component({ standalone: true, - imports: [ - TuiDemo, - TuiHint, - ReactiveFormsModule, - TuiInputCardComponent, - TuiInputExpireComponent, - TuiTextfieldControllerModule, - TuiInputCVCComponent, - TuiErrorComponent, - TuiFieldErrorPipe, - TuiAccordion, - InheritedDocumentationComponent, - ], + imports: [TuiDemo], templateUrl: './index.html', - styleUrls: ['./index.less'], changeDetection, - providers: [tuiProvide(ABSTRACT_PROPS_ACCESSOR, PageComponent)], }) -export default class PageComponent extends AbstractExampleTuiControl { - @ViewChild('documentationPropertyBinChange', { - read: TuiDocDocumentationPropertyConnectorDirective, - }) - protected binChangeProperty?: TuiDocDocumentationPropertyConnectorDirective; - - protected card = ''; - - protected readonly lengthVariants: TuiCodeCVCLength[] = [3, 4]; - - protected length = this.lengthVariants[0]; - - protected hintContentCVC = null; - - protected hintDirectionCVC: TuiHintOptions['direction'] = 'bottom-left'; - - protected hintAppearanceCVC = ''; - - protected readonly cards: Record = { - common: 'https://ng-web-apis.github.io/dist/assets/images/common.svg', - universal: 'https://ng-web-apis.github.io/dist/assets/images/universal.svg', - intersection: - 'https://ng-web-apis.github.io/dist/assets/images/intersection-observer.svg', - mutation: - 'https://ng-web-apis.github.io/dist/assets/images/mutation-observer.svg', - }; - - protected cardSrcVariants = Object.keys(this.cards); - - protected cardSrcSelected: string | null = null; - - protected autocompleteEnabledCard = false; - - protected autocompleteEnabledCVC = false; - - protected autocompleteEnabledExpire = false; - - public control = new FormGroup({ - card: new FormControl('', [ - Validators.required, - tuiCreateLuhnValidator('Invalid card number'), - ]), - expire: new FormControl('', Validators.required), - cvc: new FormControl('', Validators.required), - }); - - public override cleaner = false; - - public override exampleText = '0000 0000 0000 0000'; - - protected get cardSrc(): string | null { - return this.cardSrcSelected === null ? null : this.cards[this.cardSrcSelected]; - } - - protected get disabledCard(): boolean { - return this.isDisabled('card'); - } - - protected set disabledCard(value: boolean) { - this.toggleDisabled(value, 'card'); - } - - protected get disabledExpire(): boolean { - return this.isDisabled('expire'); - } - - protected set disabledExpire(value: boolean) { - this.toggleDisabled(value, 'expire'); - } - - protected get disabledCVC(): boolean { - return this.isDisabled('cvc'); - } - - protected set disabledCVC(value: boolean) { - this.toggleDisabled(value, 'cvc'); - } - - protected isDisabled(control: string): boolean { - return this.control.get(control)!.disabled; - } - - protected toggleDisabled(value: boolean, control: string): void { - if (value) { - this.control.get(control)!.disable(); - - return; - } - - this.control.get(control)!.enable(); - } -} +export default class PageComponent {} diff --git a/projects/demo/src/modules/components/input/examples/4/index.html b/projects/demo/src/modules/components/input/examples/4/index.html index 880aff5488fc..be3cc510e7df 100644 --- a/projects/demo/src/modules/components/input/examples/4/index.html +++ b/projects/demo/src/modules/components/input/examples/4/index.html @@ -60,11 +60,13 @@ - - Card number - + + + + diff --git a/projects/demo/src/modules/components/input/examples/4/index.ts b/projects/demo/src/modules/components/input/examples/4/index.ts index 1e2147366026..a16ffe33b86f 100644 --- a/projects/demo/src/modules/components/input/examples/4/index.ts +++ b/projects/demo/src/modules/components/input/examples/4/index.ts @@ -4,13 +4,14 @@ import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {assets} from '@demo/utils'; -import { - TuiAmountPipe, - TuiCurrency, - TuiInputCardComponent, -} from '@taiga-ui/addon-commerce'; +import {TuiAmountPipe, TuiCurrency, TuiInputCard} from '@taiga-ui/addon-commerce'; import {TUI_DEFAULT_MATCHER, tuiControlValue, TuiLetDirective} from '@taiga-ui/cdk'; -import {TuiDataList, TuiInitialsPipe, TuiTextfieldControllerModule} from '@taiga-ui/core'; +import { + TuiDataList, + TuiInitialsPipe, + TuiTextfield, + TuiTextfieldControllerModule, +} from '@taiga-ui/core'; import {TuiAvatarComponent, TuiDataListWrapper} from '@taiga-ui/kit'; import {TuiInputModule} from '@taiga-ui/legacy'; import type {PolymorpheusContent} from '@tinkoff/ng-polymorpheus'; @@ -112,7 +113,8 @@ const USERS = [ TuiInitialsPipe, TuiDataListWrapper, TuiAmountPipe, - TuiInputCardComponent, + TuiTextfield, + TuiInputCard, ], templateUrl: './index.html', styleUrls: ['./index.less'], diff --git a/projects/demo/src/modules/components/label/examples/4/index.ts b/projects/demo/src/modules/components/label/examples/4/index.ts index 98ad5887ce74..3dede02024a3 100644 --- a/projects/demo/src/modules/components/label/examples/4/index.ts +++ b/projects/demo/src/modules/components/label/examples/4/index.ts @@ -2,11 +2,11 @@ import {Component} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {TuiTextfieldModule} from '@taiga-ui/core'; +import {TuiTextfield} from '@taiga-ui/core'; @Component({ standalone: true, - imports: [ReactiveFormsModule, TuiTextfieldModule], + imports: [ReactiveFormsModule, TuiTextfield], templateUrl: './index.html', encapsulation, changeDetection, diff --git a/projects/demo/src/modules/components/textfield/examples/1/index.ts b/projects/demo/src/modules/components/textfield/examples/1/index.ts index 9cd635e5866d..affaabe194c0 100644 --- a/projects/demo/src/modules/components/textfield/examples/1/index.ts +++ b/projects/demo/src/modules/components/textfield/examples/1/index.ts @@ -2,12 +2,12 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {TuiIconComponent, TuiTextfieldModule} from '@taiga-ui/core'; +import {TuiIconComponent, TuiTextfield} from '@taiga-ui/core'; import {TuiTooltipModule} from '@taiga-ui/experimental'; @Component({ standalone: true, - imports: [FormsModule, TuiTextfieldModule, TuiIconComponent, TuiTooltipModule], + imports: [FormsModule, TuiTextfield, TuiIconComponent, TuiTooltipModule], templateUrl: './index.html', styleUrls: ['./index.less'], encapsulation, diff --git a/projects/demo/src/modules/components/textfield/examples/2/index.ts b/projects/demo/src/modules/components/textfield/examples/2/index.ts index 765e4d0be297..da2d78fff513 100644 --- a/projects/demo/src/modules/components/textfield/examples/2/index.ts +++ b/projects/demo/src/modules/components/textfield/examples/2/index.ts @@ -2,12 +2,12 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {TuiIconComponent, TuiTextfieldModule} from '@taiga-ui/core'; +import {TuiIconComponent, TuiTextfield} from '@taiga-ui/core'; import {TuiTooltipModule} from '@taiga-ui/experimental'; @Component({ standalone: true, - imports: [FormsModule, TuiTextfieldModule, TuiIconComponent, TuiTooltipModule], + imports: [FormsModule, TuiTextfield, TuiIconComponent, TuiTooltipModule], templateUrl: './index.html', styleUrls: ['./index.less'], encapsulation, diff --git a/projects/demo/src/modules/components/textfield/examples/3/index.ts b/projects/demo/src/modules/components/textfield/examples/3/index.ts index d1cb32a62432..cdd2431f7e41 100644 --- a/projects/demo/src/modules/components/textfield/examples/3/index.ts +++ b/projects/demo/src/modules/components/textfield/examples/3/index.ts @@ -2,7 +2,7 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {TuiDropdownModule, TuiIconComponent, TuiTextfieldModule} from '@taiga-ui/core'; +import {TuiDropdownModule, TuiIconComponent, TuiTextfield} from '@taiga-ui/core'; import {TuiTooltipModule} from '@taiga-ui/experimental'; import { TuiChevronDirective, @@ -15,7 +15,7 @@ import { imports: [ FormsModule, TuiDropdownModule, - TuiTextfieldModule, + TuiTextfield, TuiIconComponent, TuiTooltipModule, TuiChevronDirective, diff --git a/projects/demo/src/modules/components/textfield/examples/import/import.md b/projects/demo/src/modules/components/textfield/examples/import/import.md index 2ab7b9469b16..588f8ff36550 100644 --- a/projects/demo/src/modules/components/textfield/examples/import/import.md +++ b/projects/demo/src/modules/components/textfield/examples/import/import.md @@ -1,13 +1,13 @@ ```ts import {Component} from '@angular/core'; -import {TuiTextfieldModule} from '@taiga-ui/core'; +import {TuiTextfield} from '@taiga-ui/core'; // ... @Component({ standalone: true, imports: [ // ... - TuiTextfieldModule, + TuiTextfield, ], }) export class MyComponent {} diff --git a/projects/demo/used-icons.ts b/projects/demo/used-icons.ts index 5a308c255d70..e67f6abd150d 100644 --- a/projects/demo/used-icons.ts +++ b/projects/demo/used-icons.ts @@ -96,6 +96,7 @@ export const TUI_USED_ICONS = [ 'tuiIconAliPay', 'tuiIconAmazonPay', 'tuiIconAndroidPay', + 'tuiIconDollarSign', 'tuiIconFileLarge', 'tuiIconDropletLarge', 'tuiIconUploadCloudLarge', @@ -110,7 +111,6 @@ export const TUI_USED_ICONS = [ 'tuiIconXCircle', 'tuiIconCheckCircleLarge', 'tuiIconExternalLink', - 'tuiIconDollarSign', 'tuiIconDownload', 'tuiIconEyeOffLarge', 'tuiIconEdit2', diff --git a/projects/icons/all.ts b/projects/icons/all.ts index 4ea3bb293eb1..37a83982b4ef 100644 --- a/projects/icons/all.ts +++ b/projects/icons/all.ts @@ -102,6 +102,9 @@ const tuiIconAmazonPay = const tuiIconAmex = ''; +const tuiIconAmexOutline = + ''; + const tuiIconAnchor = ''; @@ -906,6 +909,9 @@ const tuiIconDesktopLarge = const tuiIconDinersClub = ''; +const tuiIconDinersClubOutline = + ''; + const tuiIconDisc = ''; @@ -918,6 +924,9 @@ const tuiIconDiscOutline = const tuiIconDiscover = ''; +const tuiIconDiscoverOutline = + ''; + const tuiIconDislikeLarge = ''; @@ -1039,7 +1048,10 @@ const tuiIconElectronMono = ''; const tuiIconElectronMonoOutline = - ''; + ''; + +const tuiIconElectronOutline = + ''; const tuiIconExpand = ''; @@ -1416,6 +1428,9 @@ const tuiIconHrOutline = const tuiIconHumo = ''; +const tuiIconHumoOutline = + ''; + const tuiIconImage = ''; @@ -1476,6 +1491,9 @@ const tuiIconItalicOutline = const tuiIconJCB = ''; +const tuiIconJCBOutline = + ''; + const tuiIconKey = ''; @@ -1596,6 +1614,9 @@ const tuiIconMaestro = const tuiIconMaestroMono = ''; +const tuiIconMaestroOutline = + ''; + const tuiIconMail = ''; @@ -1629,6 +1650,9 @@ const tuiIconMastercard = const tuiIconMastercardMono = ''; +const tuiIconMastercardOutline = + ''; + const tuiIconMaximize = ''; @@ -1753,7 +1777,10 @@ const tuiIconMirMono = ''; const tuiIconMirMonoOutline = - ''; + ''; + +const tuiIconMirOutline = + ''; const tuiIconMobile = ''; @@ -2184,6 +2211,9 @@ const tuiIconRssOutline = const tuiIconRuPay = ''; +const tuiIconRuPayOutline = + ''; + const tuiIconSamsungPay = ''; @@ -2787,6 +2817,9 @@ const tuiIconUndoOutline = const tuiIconUnionPay = ''; +const tuiIconUnionPayOutline = + ''; + const tuiIconUnlinkLarge = ''; @@ -2877,9 +2910,15 @@ const tuiIconUsersOutline = const tuiIconUzcard = ''; +const tuiIconUzcardOutline = + ''; + const tuiIconVerve = ''; +const tuiIconVerveOutline = + ''; + const tuiIconVideo = ''; @@ -2908,7 +2947,10 @@ const tuiIconVisaMono = ''; const tuiIconVisaMonoOutline = - ''; + ''; + +const tuiIconVisaOutline = + ''; const tuiIconVoicemail = ''; @@ -3114,6 +3156,7 @@ export { tuiIconAlignRightOutline, tuiIconAmazonPay, tuiIconAmex, + tuiIconAmexOutline, tuiIconAnchor, tuiIconAnchorLarge, tuiIconAnchorOutline, @@ -3382,10 +3425,12 @@ export { tuiIconDeleteOutline, tuiIconDesktopLarge, tuiIconDinersClub, + tuiIconDinersClubOutline, tuiIconDisc, tuiIconDiscLarge, tuiIconDiscOutline, tuiIconDiscover, + tuiIconDiscoverOutline, tuiIconDislikeLarge, tuiIconDivide, tuiIconDivideCircle, @@ -3427,6 +3472,7 @@ export { tuiIconElectron, tuiIconElectronMono, tuiIconElectronMonoOutline, + tuiIconElectronOutline, tuiIconExpand, tuiIconExternal, tuiIconExternalLarge, @@ -3552,6 +3598,7 @@ export { tuiIconHrLarge, tuiIconHrOutline, tuiIconHumo, + tuiIconHumoOutline, tuiIconImage, tuiIconImageLarge, tuiIconImageOutline, @@ -3572,6 +3619,7 @@ export { tuiIconItalicLarge, tuiIconItalicOutline, tuiIconJCB, + tuiIconJCBOutline, tuiIconKey, tuiIconKeyLarge, tuiIconKeyOutline, @@ -3612,6 +3660,7 @@ export { tuiIconLogOutOutline, tuiIconMaestro, tuiIconMaestroMono, + tuiIconMaestroOutline, tuiIconMail, tuiIconMailLarge, tuiIconMailOutline, @@ -3623,6 +3672,7 @@ export { tuiIconMapPinOutline, tuiIconMastercard, tuiIconMastercardMono, + tuiIconMastercardOutline, tuiIconMaximize, tuiIconMaximize2, tuiIconMaximize2Large, @@ -3665,6 +3715,7 @@ export { tuiIconMir, tuiIconMirMono, tuiIconMirMonoOutline, + tuiIconMirOutline, tuiIconMobile, tuiIconMobileLarge, tuiIconMonitor, @@ -3808,6 +3859,7 @@ export { tuiIconRssLarge, tuiIconRssOutline, tuiIconRuPay, + tuiIconRuPayOutline, tuiIconSamsungPay, tuiIconSave, tuiIconSaveLarge, @@ -4009,6 +4061,7 @@ export { tuiIconUndoLarge, tuiIconUndoOutline, tuiIconUnionPay, + tuiIconUnionPayOutline, tuiIconUnlinkLarge, tuiIconUnlinkOutline, tuiIconUnlock, @@ -4039,7 +4092,9 @@ export { tuiIconUsersLarge, tuiIconUsersOutline, tuiIconUzcard, + tuiIconUzcardOutline, tuiIconVerve, + tuiIconVerveOutline, tuiIconVideo, tuiIconVideoLarge, tuiIconVideoOff, @@ -4050,6 +4105,7 @@ export { tuiIconVisa, tuiIconVisaMono, tuiIconVisaMonoOutline, + tuiIconVisaOutline, tuiIconVoicemail, tuiIconVoicemailLarge, tuiIconVoicemailOutline, diff --git a/projects/icons/src/tuiIconAmexOutline.svg b/projects/icons/src/tuiIconAmexOutline.svg new file mode 100644 index 000000000000..6b7c153a40ff --- /dev/null +++ b/projects/icons/src/tuiIconAmexOutline.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/projects/icons/src/tuiIconDinersClubOutline.svg b/projects/icons/src/tuiIconDinersClubOutline.svg new file mode 100644 index 000000000000..d8b2a58f7b26 --- /dev/null +++ b/projects/icons/src/tuiIconDinersClubOutline.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/projects/icons/src/tuiIconDiscoverOutline.svg b/projects/icons/src/tuiIconDiscoverOutline.svg new file mode 100644 index 000000000000..0342bc3e0373 --- /dev/null +++ b/projects/icons/src/tuiIconDiscoverOutline.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/projects/icons/src/tuiIconElectronMonoOutline.svg b/projects/icons/src/tuiIconElectronMonoOutline.svg index 42f717a1d2a4..8c4bc45596fd 100644 --- a/projects/icons/src/tuiIconElectronMonoOutline.svg +++ b/projects/icons/src/tuiIconElectronMonoOutline.svg @@ -1,51 +1,23 @@ - - - - - - - - - - - - - + + + + + + + diff --git a/projects/icons/src/tuiIconElectronOutline.svg b/projects/icons/src/tuiIconElectronOutline.svg new file mode 100644 index 000000000000..1ab6b59a7e9c --- /dev/null +++ b/projects/icons/src/tuiIconElectronOutline.svg @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/projects/icons/src/tuiIconHumoOutline.svg b/projects/icons/src/tuiIconHumoOutline.svg new file mode 100644 index 000000000000..2ca2f27b6a94 --- /dev/null +++ b/projects/icons/src/tuiIconHumoOutline.svg @@ -0,0 +1,955 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/icons/src/tuiIconJCBOutline.svg b/projects/icons/src/tuiIconJCBOutline.svg new file mode 100644 index 000000000000..6f32589f8891 --- /dev/null +++ b/projects/icons/src/tuiIconJCBOutline.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/projects/icons/src/tuiIconMaestroOutline.svg b/projects/icons/src/tuiIconMaestroOutline.svg new file mode 100644 index 000000000000..4eb8ff971285 --- /dev/null +++ b/projects/icons/src/tuiIconMaestroOutline.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/projects/icons/src/tuiIconMastercardOutline.svg b/projects/icons/src/tuiIconMastercardOutline.svg new file mode 100644 index 000000000000..f74fe1a3b7ba --- /dev/null +++ b/projects/icons/src/tuiIconMastercardOutline.svg @@ -0,0 +1,19 @@ + + + + + + + diff --git a/projects/icons/src/tuiIconMirMonoOutline.svg b/projects/icons/src/tuiIconMirMonoOutline.svg index 77add71825ad..1d5b2b1167ab 100644 --- a/projects/icons/src/tuiIconMirMonoOutline.svg +++ b/projects/icons/src/tuiIconMirMonoOutline.svg @@ -1,28 +1,6 @@ - - - - - - - - + + diff --git a/projects/icons/src/tuiIconMirOutline.svg b/projects/icons/src/tuiIconMirOutline.svg new file mode 100644 index 000000000000..46d63e539d80 --- /dev/null +++ b/projects/icons/src/tuiIconMirOutline.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/projects/icons/src/tuiIconRuPayOutline.svg b/projects/icons/src/tuiIconRuPayOutline.svg new file mode 100644 index 000000000000..dcfe2b1384bc --- /dev/null +++ b/projects/icons/src/tuiIconRuPayOutline.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + diff --git a/projects/icons/src/tuiIconUnionPayOutline.svg b/projects/icons/src/tuiIconUnionPayOutline.svg new file mode 100644 index 000000000000..0433fe0ed1ed --- /dev/null +++ b/projects/icons/src/tuiIconUnionPayOutline.svg @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/projects/icons/src/tuiIconUzcardOutline.svg b/projects/icons/src/tuiIconUzcardOutline.svg new file mode 100644 index 000000000000..9acd7d43f1e1 --- /dev/null +++ b/projects/icons/src/tuiIconUzcardOutline.svg @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/projects/icons/src/tuiIconVerveOutline.svg b/projects/icons/src/tuiIconVerveOutline.svg new file mode 100644 index 000000000000..0b52f57baa2a --- /dev/null +++ b/projects/icons/src/tuiIconVerveOutline.svg @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/projects/icons/src/tuiIconVisaMonoOutline.svg b/projects/icons/src/tuiIconVisaMonoOutline.svg index 7290c395f1fb..b9af41071f8a 100644 --- a/projects/icons/src/tuiIconVisaMonoOutline.svg +++ b/projects/icons/src/tuiIconVisaMonoOutline.svg @@ -1,29 +1,7 @@ - - - - - - - - + + diff --git a/projects/icons/src/tuiIconVisaOutline.svg b/projects/icons/src/tuiIconVisaOutline.svg new file mode 100644 index 000000000000..e72c559b8a01 --- /dev/null +++ b/projects/icons/src/tuiIconVisaOutline.svg @@ -0,0 +1,7 @@ + + + From 9601fd67a827aa998e265add48b0a9b4e5a236a3 Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 4 Jun 2024 12:20:24 +0300 Subject: [PATCH 5/7] refactor(core): use `childNodes.length` instead of `textContent` to detect label for `Textfield` (#7616) --- projects/core/components/textfield/textfield.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/core/components/textfield/textfield.component.ts b/projects/core/components/textfield/textfield.component.ts index 4e1f589e3bed..340aefa8836e 100644 --- a/projects/core/components/textfield/textfield.component.ts +++ b/projects/core/components/textfield/textfield.component.ts @@ -147,6 +147,6 @@ export class TuiTextfieldComponent } protected get hasLabel(): boolean { - return Boolean(this.label?.nativeElement?.textContent?.trim()); + return Boolean(this.label?.nativeElement?.childNodes.length); } } From e51a2b93b5bbaee028d1cdb1f2916ab272a83830 Mon Sep 17 00:00:00 2001 From: Vladimir Potekhin <46284632+vladimirpotekhin@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:22:10 +0300 Subject: [PATCH 6/7] fix(core): close `Hint` and `Dropdown` when host is detached from dom (#7614) --- .../directives/dropdown/dropdown.component.ts | 6 +- .../core/directives/hint/hint.component.ts | 6 ++ projects/demo-cypress/src/tests/hint.cy.ts | 67 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 projects/demo-cypress/src/tests/hint.cy.ts diff --git a/projects/core/directives/dropdown/dropdown.component.ts b/projects/core/directives/dropdown/dropdown.component.ts index 578f2e2fbc10..64886be951e3 100644 --- a/projects/core/directives/dropdown/dropdown.component.ts +++ b/projects/core/directives/dropdown/dropdown.component.ts @@ -67,7 +67,11 @@ export class TuiDropdownComponent implements OnInit { takeUntilDestroyed(), ) .subscribe(([top, left]) => { - this.update(top, left); + if (this.directive.el.isConnected) { + this.update(top, left); + } else { + this.directive.toggle(false); + } }); public ngOnInit(): void { diff --git a/projects/core/directives/hint/hint.component.ts b/projects/core/directives/hint/hint.component.ts index b4c31f7d7566..41bafb130023 100644 --- a/projects/core/directives/hint/hint.component.ts +++ b/projects/core/directives/hint/hint.component.ts @@ -117,6 +117,12 @@ export class TuiHintComponent { @tuiPure private update(top: number, left: number): void { + if (!this.hover.el.isConnected) { + this.hover.toggle(false); + + return; + } + const {height, width} = this.el.getBoundingClientRect(); const rect = this.accessor.getClientRect(); const viewport = this.viewport.getClientRect(); diff --git a/projects/demo-cypress/src/tests/hint.cy.ts b/projects/demo-cypress/src/tests/hint.cy.ts new file mode 100644 index 000000000000..2506ea9409ae --- /dev/null +++ b/projects/demo-cypress/src/tests/hint.cy.ts @@ -0,0 +1,67 @@ +import {Component, Input} from '@angular/core'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {TuiSwipeDirective} from '@taiga-ui/cdk'; +import {TuiHint, TuiRootComponent} from '@taiga-ui/core'; +import {NG_EVENT_PLUGINS} from '@tinkoff/ng-event-plugins'; +import type {MountResponse} from 'cypress/angular'; + +describe('TuiHint', () => { + let component: TestComponent; + let wrapper: MountResponse; + + @Component({ + selector: 'my-host', + template: ` + + + + `, + }) + class HostComponent { + @Input() + public hideElement = false; + } + + @Component({ + template: ` + + + + + + `, + }) + class TestComponent { + public hide = false; + } + + beforeEach(() => + cy + .mount(TestComponent, { + imports: [ + BrowserAnimationsModule, + TuiRootComponent, + TuiSwipeDirective, + TuiHint, + ], + declarations: [HostComponent], + providers: [NG_EVENT_PLUGINS], + }) + .then(wrap => { + component = wrap.component; + wrapper = wrap; + }), + ); + + it('hint should hidden when detached from dom', () => { + cy.get('button').trigger('mouseenter'); + cy.get('tui-hint') + .should('exist') + .then(() => { + component.hide = true; + wrapper.fixture.detectChanges(); + + cy.get('tui-hint').should('not.exist'); + }); + }); +}); From ed55717aa4adfeb4ba7312941d8234aa467a1d9a Mon Sep 17 00:00:00 2001 From: taiga-family-bot <140712314+taiga-family-bot@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:37:04 +0300 Subject: [PATCH 7/7] chore(deps): update taiga-ui to v0.94.0 (#7618) --- package-lock.json | 65 +++++++++++++++++++++++++---------------------- package.json | 14 +++++----- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 979f8983625a..7154cc22e5e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,13 +28,13 @@ "@nx/jest": "18.3.4", "@nx/workspace": "18.3.4", "@schematics/angular": "16.2.12", - "@taiga-ui/browserslist-config": "0.6.0", - "@taiga-ui/commitlint-config": "0.7.5", - "@taiga-ui/cspell-config": "0.40.1", - "@taiga-ui/eslint-plugin-experience": "0.75.4", - "@taiga-ui/prettier-config": "0.11.9", - "@taiga-ui/stylelint-config": "0.22.3", - "@taiga-ui/tsconfig": "0.17.0", + "@taiga-ui/browserslist-config": "0.94.0", + "@taiga-ui/commitlint-config": "0.94.0", + "@taiga-ui/cspell-config": "0.94.0", + "@taiga-ui/eslint-plugin-experience": "0.94.0", + "@taiga-ui/prettier-config": "0.94.0", + "@taiga-ui/stylelint-config": "0.94.0", + "@taiga-ui/tsconfig": "0.94.0", "@tinkoff/ng-event-plugins": "3.2.0", "@types/glob": "8.1.0", "@types/loader-utils": "2.0.6", @@ -9030,20 +9030,22 @@ "link": true }, "node_modules/@taiga-ui/browserslist-config": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/browserslist-config/-/browserslist-config-0.6.0.tgz", - "integrity": "sha512-lCbOPwSH3FsZx0rKQvqsTmR8SSBvuKauqO616cQJmS9R4PdmbT4eHW1OIwmCTIRO1BN6qLeMjqq4mlq3JjqeBw==", - "dev": true + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/browserslist-config/-/browserslist-config-0.94.0.tgz", + "integrity": "sha512-aLM1p8DRXO+zjPmlcWEmnAXebYRdhbYUlX/9H/VC194ohIul3FOFoZSrN4bJ6/Rb6sHXJEXYqQV3zBHR1VRsYQ==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/@taiga-ui/cdk": { "resolved": "projects/cdk", "link": true }, "node_modules/@taiga-ui/commitlint-config": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@taiga-ui/commitlint-config/-/commitlint-config-0.7.5.tgz", - "integrity": "sha512-igi0fdyG6KsQ259cCFG2zU1Svd3KKD/0aSiwPJIMglqMhaj+TIqMezbX7d3XYcWwnrQftSovV0jBA/DuII5dhA==", + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/commitlint-config/-/commitlint-config-0.94.0.tgz", + "integrity": "sha512-IGD9DCIx7Dilq3gaJ7io6vl9rXX6DyqdSE+y13h5syiGnHrlRa5XzrYL8JM0YseXU6UIDy55WjBImn6TMfPtEA==", "dev": true, + "license": "Apache-2.0", "peerDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2" @@ -9054,9 +9056,9 @@ "link": true }, "node_modules/@taiga-ui/cspell-config": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/@taiga-ui/cspell-config/-/cspell-config-0.40.1.tgz", - "integrity": "sha512-uy8KmM+k/Z7QAuGx2LdQUsSNw4x0Ac7ovXCtNfxO79fEjQG6RE7jHuoBbhPSneRJbX6iJjsPejWiOeZNsOFkTA==", + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/cspell-config/-/cspell-config-0.94.0.tgz", + "integrity": "sha512-Bmpg3xokETcGpLJ3ehWsshvgqYw9HP8rzK6v4hCC3fAVT2qlReDW/O4pHlkEmVXYXkOmCM5n24FUaviNBdyalQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -9089,9 +9091,9 @@ } }, "node_modules/@taiga-ui/eslint-plugin-experience": { - "version": "0.75.4", - "resolved": "https://registry.npmjs.org/@taiga-ui/eslint-plugin-experience/-/eslint-plugin-experience-0.75.4.tgz", - "integrity": "sha512-4ntWlhSP2H300G2PyusBT1cGn36+uE6VY2owWPNAWTHT0tei/9NZowyzyFKg11ltMXOZLJ3vpX6zP1I0UWJRog==", + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/eslint-plugin-experience/-/eslint-plugin-experience-0.94.0.tgz", + "integrity": "sha512-/bI5TUBTxFG0zV5V5Wm7LhxumxfeLt3CuJIsU8eEdaLl8eGvTtU0bjLDbEnp85Pg5WjQ5rm9zrwxqF3aE5Ej3A==", "dev": true, "peerDependencies": { "@angular-eslint/eslint-plugin": "^18.0.1", @@ -9154,10 +9156,11 @@ "link": true }, "node_modules/@taiga-ui/prettier-config": { - "version": "0.11.9", - "resolved": "https://registry.npmjs.org/@taiga-ui/prettier-config/-/prettier-config-0.11.9.tgz", - "integrity": "sha512-co9io3CBgSpHkclOZGZVMsPISTpLh9NSN6INJR+rC7NKadxs4PBaanji6HsuIA29GztIQ/OX1TOLW2QzzNQkOw==", + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/prettier-config/-/prettier-config-0.94.0.tgz", + "integrity": "sha512-iW/daJTFryAy4sfLYXCWMC9to+TfE6B4JwzspmG0HhLNMlEIQzwKhAV09BXUt4PHnpZfqdmdKzwaIPMRWWDyUA==", "dev": true, + "license": "Apache-2.0", "peerDependencies": { "@prettier/plugin-xml": "^3.4.1", "prettier": "^3.3.0", @@ -9168,10 +9171,11 @@ } }, "node_modules/@taiga-ui/stylelint-config": { - "version": "0.22.3", - "resolved": "https://registry.npmjs.org/@taiga-ui/stylelint-config/-/stylelint-config-0.22.3.tgz", - "integrity": "sha512-QXb37gSmNIfa41I4nJG4rAF0rEzabN5jccz/WNNReWSi8esDjEGvUBdzxmUzDsDjnyWnqa4ItwC7Fzee88bFfQ==", + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/stylelint-config/-/stylelint-config-0.94.0.tgz", + "integrity": "sha512-Hzgk4AvHZ787duHH8hYNqjzO48KFvJ2AdRfUTHmERSKLUqMrieUPMNbDPdRO3zhl3kR8XAeF6wk4uLFdi1qsSA==", "dev": true, + "license": "Apache-2.0", "peerDependencies": { "@miller-svt/stylelint-no-px": "^1.0.1", "postcss": "^8.4.38", @@ -9190,10 +9194,11 @@ "link": true }, "node_modules/@taiga-ui/tsconfig": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/tsconfig/-/tsconfig-0.17.0.tgz", - "integrity": "sha512-q5lPtI7kSdf/k4Z8xXatNUEHVmKTUoaU/DRBmz1Fd+SMLbecR6V/qX1uQ5Fm4IcJBCEC13a36SrI9qTYDamv+Q==", - "dev": true + "version": "0.94.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/tsconfig/-/tsconfig-0.94.0.tgz", + "integrity": "sha512-yVGP2gPyXQxOb8FkU3HJv9bu9xxg+FDWPcEx0ahdCrGHfXDcG0VN0nbjhBqOwFkE+MN08aP5Dzoe8gf0gCbdUg==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/@tinkoff/ng-dompurify": { "version": "4.0.0", diff --git a/package.json b/package.json index ff8d27452184..52f4c51667f8 100644 --- a/package.json +++ b/package.json @@ -116,13 +116,13 @@ "@nx/jest": "18.3.4", "@nx/workspace": "18.3.4", "@schematics/angular": "16.2.12", - "@taiga-ui/browserslist-config": "0.6.0", - "@taiga-ui/commitlint-config": "0.7.5", - "@taiga-ui/cspell-config": "0.40.1", - "@taiga-ui/eslint-plugin-experience": "0.75.4", - "@taiga-ui/prettier-config": "0.11.9", - "@taiga-ui/stylelint-config": "0.22.3", - "@taiga-ui/tsconfig": "0.17.0", + "@taiga-ui/browserslist-config": "0.94.0", + "@taiga-ui/commitlint-config": "0.94.0", + "@taiga-ui/cspell-config": "0.94.0", + "@taiga-ui/eslint-plugin-experience": "0.94.0", + "@taiga-ui/prettier-config": "0.94.0", + "@taiga-ui/stylelint-config": "0.94.0", + "@taiga-ui/tsconfig": "0.94.0", "@tinkoff/ng-event-plugins": "3.2.0", "@types/glob": "8.1.0", "@types/loader-utils": "2.0.6",