From f805ed5975d355b36a793bdd5e6d5c361c108599 Mon Sep 17 00:00:00 2001 From: Alex Inkin Date: Fri, 20 Oct 2023 12:55:53 +0400 Subject: [PATCH] feat(experimental): `Toggle`, `Checkbox`, `Radio` add new components (#5319) Co-authored-by: taiga-family-bot --- projects/demo/src/modules/app/app.routes.ts | 27 +- projects/demo/src/modules/app/pages.ts | 20 +- .../experimental/avatar/avatar.template.html | 6 +- .../checkbox/checkbox.component.ts | 23 ++ .../experimental/checkbox/checkbox.module.ts | 25 ++ .../checkbox/checkbox.template.html | 50 ++++ .../checkbox/examples/1/index.html | 62 ++++ .../checkbox/examples/1/index.less | 24 ++ .../experimental/checkbox/examples/1/index.ts | 28 ++ .../checkbox/examples/import/import-module.md | 13 + .../examples/import/insert-template.md | 7 + .../experimental/fade/fade.template.html | 6 +- .../experimental/radio/examples/1/index.html | 59 ++++ .../experimental/radio/examples/1/index.less | 24 ++ .../experimental/radio/examples/1/index.ts | 28 ++ .../radio/examples/import/import-module.md | 13 + .../radio/examples/import/insert-template.md | 7 + .../experimental/radio/radio.component.ts | 23 ++ .../experimental/radio/radio.module.ts | 25 ++ .../experimental/radio/radio.template.html | 50 ++++ .../experimental/surface/surface.component.ts | 6 +- .../experimental/toggle/examples/1/index.html | 61 ++++ .../experimental/toggle/examples/1/index.less | 24 ++ .../experimental/toggle/examples/1/index.ts | 28 ++ .../toggle/examples/import/import-module.md | 13 + .../toggle/examples/import/insert-template.md | 7 + .../experimental/toggle/toggle.component.ts | 23 ++ .../experimental/toggle/toggle.module.ts | 25 ++ .../experimental/toggle/toggle.template.html | 50 ++++ .../components/checkbox/checkbox.component.ts | 88 ++++++ .../components/checkbox/checkbox.module.ts | 9 + .../components/checkbox/checkbox.options.ts | 27 ++ .../components/checkbox/checkbox.style.less | 166 +++++++++++ .../experimental/components/checkbox/index.ts | 3 + .../components/checkbox/ng-package.json | 8 + projects/experimental/components/index.ts | 3 + .../experimental/components/radio/index.ts | 2 + .../components/radio/ng-package.json | 8 + .../components/radio/radio.component.ts | 31 ++ .../components/radio/radio.module.ts | 9 + .../components/radio/radio.style.less | 132 +++++++++ .../experimental/components/toggle/index.ts | 2 + .../components/toggle/ng-package.json | 8 + .../components/toggle/toggle.component.ts | 62 ++++ .../components/toggle/toggle.module.ts | 9 + .../components/toggle/toggle.options.ts | 21 ++ .../components/toggle/toggle.style.less | 267 ++++++++++++++++++ projects/icons/all.ts | 4 +- .../icons/scripts/prepare-feather-icons.ts | 3 +- projects/icons/src/tuiIconCheck.svg | 1 + projects/icons/src/tuiIconCheckLarge.svg | 1 + 51 files changed, 1603 insertions(+), 18 deletions(-) create mode 100644 projects/demo/src/modules/experimental/checkbox/checkbox.component.ts create mode 100644 projects/demo/src/modules/experimental/checkbox/checkbox.module.ts create mode 100644 projects/demo/src/modules/experimental/checkbox/checkbox.template.html create mode 100644 projects/demo/src/modules/experimental/checkbox/examples/1/index.html create mode 100644 projects/demo/src/modules/experimental/checkbox/examples/1/index.less create mode 100644 projects/demo/src/modules/experimental/checkbox/examples/1/index.ts create mode 100644 projects/demo/src/modules/experimental/checkbox/examples/import/import-module.md create mode 100644 projects/demo/src/modules/experimental/checkbox/examples/import/insert-template.md create mode 100644 projects/demo/src/modules/experimental/radio/examples/1/index.html create mode 100644 projects/demo/src/modules/experimental/radio/examples/1/index.less create mode 100644 projects/demo/src/modules/experimental/radio/examples/1/index.ts create mode 100644 projects/demo/src/modules/experimental/radio/examples/import/import-module.md create mode 100644 projects/demo/src/modules/experimental/radio/examples/import/insert-template.md create mode 100644 projects/demo/src/modules/experimental/radio/radio.component.ts create mode 100644 projects/demo/src/modules/experimental/radio/radio.module.ts create mode 100644 projects/demo/src/modules/experimental/radio/radio.template.html create mode 100644 projects/demo/src/modules/experimental/toggle/examples/1/index.html create mode 100644 projects/demo/src/modules/experimental/toggle/examples/1/index.less create mode 100644 projects/demo/src/modules/experimental/toggle/examples/1/index.ts create mode 100644 projects/demo/src/modules/experimental/toggle/examples/import/import-module.md create mode 100644 projects/demo/src/modules/experimental/toggle/examples/import/insert-template.md create mode 100644 projects/demo/src/modules/experimental/toggle/toggle.component.ts create mode 100644 projects/demo/src/modules/experimental/toggle/toggle.module.ts create mode 100644 projects/demo/src/modules/experimental/toggle/toggle.template.html create mode 100644 projects/experimental/components/checkbox/checkbox.component.ts create mode 100644 projects/experimental/components/checkbox/checkbox.module.ts create mode 100644 projects/experimental/components/checkbox/checkbox.options.ts create mode 100644 projects/experimental/components/checkbox/checkbox.style.less create mode 100644 projects/experimental/components/checkbox/index.ts create mode 100644 projects/experimental/components/checkbox/ng-package.json create mode 100644 projects/experimental/components/radio/index.ts create mode 100644 projects/experimental/components/radio/ng-package.json create mode 100644 projects/experimental/components/radio/radio.component.ts create mode 100644 projects/experimental/components/radio/radio.module.ts create mode 100644 projects/experimental/components/radio/radio.style.less create mode 100644 projects/experimental/components/toggle/index.ts create mode 100644 projects/experimental/components/toggle/ng-package.json create mode 100644 projects/experimental/components/toggle/toggle.component.ts create mode 100644 projects/experimental/components/toggle/toggle.module.ts create mode 100644 projects/experimental/components/toggle/toggle.options.ts create mode 100644 projects/experimental/components/toggle/toggle.style.less diff --git a/projects/demo/src/modules/app/app.routes.ts b/projects/demo/src/modules/app/app.routes.ts index 7097a5c5ff90..960f1653a9b1 100644 --- a/projects/demo/src/modules/app/app.routes.ts +++ b/projects/demo/src/modules/app/app.routes.ts @@ -203,7 +203,32 @@ export const ROUTES: Routes = [ loadChildren: async () => (await import(`../experimental/avatar/avatar.module`)).ExampleTuiAvatarModule, data: { - title: `Avatar`, + title: `Avatar `, + }, + }, + { + path: `experimental/checkbox`, + loadChildren: async () => + (await import(`../experimental/checkbox/checkbox.module`)) + .ExampleTuiCheckboxModule, + data: { + title: `Checkbox `, + }, + }, + { + path: `experimental/radio`, + loadChildren: async () => + (await import(`../experimental/radio/radio.module`)).ExampleTuiRadioModule, + data: { + title: `Radio `, + }, + }, + { + path: `experimental/toggle`, + loadChildren: async () => + (await import(`../experimental/toggle/toggle.module`)).ExampleTuiToggleModule, + data: { + title: `Toggle `, }, }, { diff --git a/projects/demo/src/modules/app/pages.ts b/projects/demo/src/modules/app/pages.ts index de5610549820..4813a4648c81 100644 --- a/projects/demo/src/modules/app/pages.ts +++ b/projects/demo/src/modules/app/pages.ts @@ -819,7 +819,7 @@ export const pages: TuiDocPages = [ // Experimental { section: `Experimental`, - title: `Avatar`, + title: `Avatar `, keywords: `аватар, image, pic, icon, картинка, изображение, avatar, stack`, route: `/experimental/avatar`, }, @@ -883,6 +883,24 @@ export const pages: TuiDocPages = [ keywords: `card, container, wrapper, image, blur, overlay`, route: `/experimental/surface`, }, + { + section: `Experimental`, + title: `Checkbox `, + keywords: `чек, радио, ввод, форма, form, checkbox, radio, toggle`, + route: `/experimental/checkbox`, + }, + { + section: `Experimental`, + title: `Radio `, + keywords: `чек, радио, ввод, форма, form, checkbox, radio, toggle`, + route: `/experimental/radio`, + }, + { + section: `Experimental`, + title: `Toggle `, + keywords: `чек, радио, ввод, форма, form, checkbox, radio, toggle`, + route: `/experimental/toggle`, + }, // Charts { section: `Charts`, diff --git a/projects/demo/src/modules/experimental/avatar/avatar.template.html b/projects/demo/src/modules/experimental/avatar/avatar.template.html index e28b6cef909b..a27f4371c19f 100644 --- a/projects/demo/src/modules/experimental/avatar/avatar.template.html +++ b/projects/demo/src/modules/experimental/avatar/avatar.template.html @@ -108,11 +108,7 @@
  1. -

    - Import - TuiAvatarModule - into a module where you want to use our component -

    +

    Import module

    + + + This code is + experimental + and is a subject to change. Expect final solution to be shipped in the next major version + + +

    A checkbox component that is able to imitate native control on mobile platforms.

    + + + + Use + --tui-accent + CSS variable to customize color of native control emulation + + + +
    + + +
      +
    1. +

      Import module

      + + +
    2. + +
    3. +

      Add to the template:

      + + +
    4. +
    +
    + diff --git a/projects/demo/src/modules/experimental/checkbox/examples/1/index.html b/projects/demo/src/modules/experimental/checkbox/examples/1/index.html new file mode 100644 index 000000000000..20560d1c28d0 --- /dev/null +++ b/projects/demo/src/modules/experimental/checkbox/examples/1/index.html @@ -0,0 +1,62 @@ +
    + + + + + + + + + + + + + + + + +
    diff --git a/projects/demo/src/modules/experimental/checkbox/examples/1/index.less b/projects/demo/src/modules/experimental/checkbox/examples/1/index.less new file mode 100644 index 000000000000..4131092f7d4e --- /dev/null +++ b/projects/demo/src/modules/experimental/checkbox/examples/1/index.less @@ -0,0 +1,24 @@ +:host { + display: flex; + --tui-accent: var(--tui-info-fill); +} + +.wrapper { + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + flex: 1; + gap: 1rem; + padding: 1rem; + + &_web { + border: 1px solid var(--tui-base-04); + border-left-width: 0; + + &:first-child { + border-right-width: 0; + border-left-width: 1px; + } + } +} diff --git a/projects/demo/src/modules/experimental/checkbox/examples/1/index.ts b/projects/demo/src/modules/experimental/checkbox/examples/1/index.ts new file mode 100644 index 000000000000..2d60b620fdc4 --- /dev/null +++ b/projects/demo/src/modules/experimental/checkbox/examples/1/index.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiPlatform} from '@taiga-ui/cdk'; +import {TuiSizeS} from '@taiga-ui/core'; + +@Component({ + selector: 'tui-checkbox-example-1', + templateUrl: './index.html', + styleUrls: ['./index.less'], + changeDetection, + encapsulation, +}) +export class TuiCheckboxExample1 implements OnInit { + readonly platforms: readonly TuiPlatform[] = ['web', 'web', 'android', 'ios']; + readonly invalidTrue = new FormControl(true, () => ({invalid: true})); + readonly invalidFalse = new FormControl(false, () => ({invalid: true})); + + ngOnInit(): void { + this.invalidTrue.markAsTouched(); + this.invalidFalse.markAsTouched(); + } + + getSize(first: boolean): TuiSizeS { + return first ? 'm' : 's'; + } +} diff --git a/projects/demo/src/modules/experimental/checkbox/examples/import/import-module.md b/projects/demo/src/modules/experimental/checkbox/examples/import/import-module.md new file mode 100644 index 000000000000..8975ebfec1e1 --- /dev/null +++ b/projects/demo/src/modules/experimental/checkbox/examples/import/import-module.md @@ -0,0 +1,13 @@ +```ts +import {NgModule} from '@angular/core'; +import {TuiCheckboxModule} from '@taiga-ui/experimental'; +// ... + +@NgModule({ + imports: [ + // ... + TuiCheckboxModule, + ], +}) +export class MyModule {} +``` diff --git a/projects/demo/src/modules/experimental/checkbox/examples/import/insert-template.md b/projects/demo/src/modules/experimental/checkbox/examples/import/insert-template.md new file mode 100644 index 000000000000..4d88d2840e5f --- /dev/null +++ b/projects/demo/src/modules/experimental/checkbox/examples/import/insert-template.md @@ -0,0 +1,7 @@ +```html + +``` diff --git a/projects/demo/src/modules/experimental/fade/fade.template.html b/projects/demo/src/modules/experimental/fade/fade.template.html index 5f7b5bbbde07..9092a014ec0e 100644 --- a/projects/demo/src/modules/experimental/fade/fade.template.html +++ b/projects/demo/src/modules/experimental/fade/fade.template.html @@ -89,11 +89,7 @@
    1. -

      - Import - TuiFadeModule - into a module where you want to use our component -

      +

      Import module

      + + + + + + + + + + + + + + + diff --git a/projects/demo/src/modules/experimental/radio/examples/1/index.less b/projects/demo/src/modules/experimental/radio/examples/1/index.less new file mode 100644 index 000000000000..4131092f7d4e --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/examples/1/index.less @@ -0,0 +1,24 @@ +:host { + display: flex; + --tui-accent: var(--tui-info-fill); +} + +.wrapper { + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + flex: 1; + gap: 1rem; + padding: 1rem; + + &_web { + border: 1px solid var(--tui-base-04); + border-left-width: 0; + + &:first-child { + border-right-width: 0; + border-left-width: 1px; + } + } +} diff --git a/projects/demo/src/modules/experimental/radio/examples/1/index.ts b/projects/demo/src/modules/experimental/radio/examples/1/index.ts new file mode 100644 index 000000000000..7d8c5fe74813 --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/examples/1/index.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiPlatform} from '@taiga-ui/cdk'; +import {TuiSizeS} from '@taiga-ui/core'; + +@Component({ + selector: 'tui-radio-example-1', + templateUrl: './index.html', + styleUrls: ['./index.less'], + changeDetection, + encapsulation, +}) +export class TuiRadioExample1 implements OnInit { + readonly platforms: readonly TuiPlatform[] = ['web', 'web', 'android', 'ios']; + readonly invalidTrue = new FormControl(true, () => ({invalid: true})); + readonly invalidFalse = new FormControl(false, () => ({invalid: true})); + + ngOnInit(): void { + this.invalidTrue.markAsTouched(); + this.invalidFalse.markAsTouched(); + } + + getSize(first: boolean): TuiSizeS { + return first ? 'm' : 's'; + } +} diff --git a/projects/demo/src/modules/experimental/radio/examples/import/import-module.md b/projects/demo/src/modules/experimental/radio/examples/import/import-module.md new file mode 100644 index 000000000000..0c7bd1c75b68 --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/examples/import/import-module.md @@ -0,0 +1,13 @@ +```ts +import {NgModule} from '@angular/core'; +import {TuiRadioModule} from '@taiga-ui/experimental'; +// ... + +@NgModule({ + imports: [ + // ... + TuiRadioModule, + ], +}) +export class MyModule {} +``` diff --git a/projects/demo/src/modules/experimental/radio/examples/import/insert-template.md b/projects/demo/src/modules/experimental/radio/examples/import/insert-template.md new file mode 100644 index 000000000000..2f44e7498e29 --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/examples/import/insert-template.md @@ -0,0 +1,7 @@ +```html + +``` diff --git a/projects/demo/src/modules/experimental/radio/radio.component.ts b/projects/demo/src/modules/experimental/radio/radio.component.ts new file mode 100644 index 000000000000..25fb6e671fd0 --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/radio.component.ts @@ -0,0 +1,23 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {TuiDocExample, TuiRawLoaderContent} from '@taiga-ui/addon-doc'; + +@Component({ + selector: 'example-radio', + templateUrl: './radio.template.html', + changeDetection, +}) +export class ExampleTuiRadioComponent { + readonly exampleModule: TuiRawLoaderContent = import( + './examples/import/import-module.md?raw' + ); + + readonly exampleHtml: TuiRawLoaderContent = import( + './examples/import/insert-template.md?raw' + ); + + readonly example1: TuiDocExample = { + TypeScript: import('./examples/1/index.ts?raw'), + HTML: import('./examples/1/index.html?raw'), + }; +} diff --git a/projects/demo/src/modules/experimental/radio/radio.module.ts b/projects/demo/src/modules/experimental/radio/radio.module.ts new file mode 100644 index 000000000000..45cbcd168630 --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/radio.module.ts @@ -0,0 +1,25 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {tuiGetDocModules} from '@taiga-ui/addon-doc'; +import {TuiPlatformModule} from '@taiga-ui/cdk'; +import {TuiNotificationModule} from '@taiga-ui/core'; +import {TuiRadioModule} from '@taiga-ui/experimental'; + +import {TuiRadioExample1} from './examples/1'; +import {ExampleTuiRadioComponent} from './radio.component'; + +@NgModule({ + imports: [ + FormsModule, + ReactiveFormsModule, + CommonModule, + TuiNotificationModule, + TuiPlatformModule, + TuiRadioModule, + tuiGetDocModules(ExampleTuiRadioComponent), + ], + declarations: [ExampleTuiRadioComponent, TuiRadioExample1], + exports: [ExampleTuiRadioComponent], +}) +export class ExampleTuiRadioModule {} diff --git a/projects/demo/src/modules/experimental/radio/radio.template.html b/projects/demo/src/modules/experimental/radio/radio.template.html new file mode 100644 index 000000000000..edef1085e6eb --- /dev/null +++ b/projects/demo/src/modules/experimental/radio/radio.template.html @@ -0,0 +1,50 @@ + + + + This code is + experimental + and is a subject to change. Expect final solution to be shipped in the next major version + + +

      A radio component that is able to imitate native control on mobile platforms.

      + + + + Use + --tui-accent + CSS variable to customize color of native control emulation + + + +
      + + +
        +
      1. +

        Import module

        + + +
      2. + +
      3. +

        Add to the template:

        + + +
      4. +
      +
      +
      diff --git a/projects/demo/src/modules/experimental/surface/surface.component.ts b/projects/demo/src/modules/experimental/surface/surface.component.ts index 7b440fc7c516..f0a3e0ec9fca 100644 --- a/projects/demo/src/modules/experimental/surface/surface.component.ts +++ b/projects/demo/src/modules/experimental/surface/surface.component.ts @@ -1,6 +1,6 @@ import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; -import {RawLoaderContent, TuiDocExample} from '@taiga-ui/addon-doc'; +import {TuiDocExample, TuiRawLoaderContent} from '@taiga-ui/addon-doc'; @Component({ selector: 'example-surface', @@ -8,11 +8,11 @@ import {RawLoaderContent, TuiDocExample} from '@taiga-ui/addon-doc'; changeDetection, }) export class ExampleTuiSurfaceComponent { - readonly exampleModule: RawLoaderContent = import( + readonly exampleModule: TuiRawLoaderContent = import( './examples/import/import-module.md?raw' ); - readonly exampleHtml: RawLoaderContent = import( + readonly exampleHtml: TuiRawLoaderContent = import( './examples/import/insert-template.md?raw' ); diff --git a/projects/demo/src/modules/experimental/toggle/examples/1/index.html b/projects/demo/src/modules/experimental/toggle/examples/1/index.html new file mode 100644 index 000000000000..749b3a9249d2 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/examples/1/index.html @@ -0,0 +1,61 @@ +
      + + + + + + + + + + + + + + +
      diff --git a/projects/demo/src/modules/experimental/toggle/examples/1/index.less b/projects/demo/src/modules/experimental/toggle/examples/1/index.less new file mode 100644 index 000000000000..4131092f7d4e --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/examples/1/index.less @@ -0,0 +1,24 @@ +:host { + display: flex; + --tui-accent: var(--tui-info-fill); +} + +.wrapper { + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + flex: 1; + gap: 1rem; + padding: 1rem; + + &_web { + border: 1px solid var(--tui-base-04); + border-left-width: 0; + + &:first-child { + border-right-width: 0; + border-left-width: 1px; + } + } +} diff --git a/projects/demo/src/modules/experimental/toggle/examples/1/index.ts b/projects/demo/src/modules/experimental/toggle/examples/1/index.ts new file mode 100644 index 000000000000..61913c1a5ca4 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/examples/1/index.ts @@ -0,0 +1,28 @@ +import {Component, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiPlatform} from '@taiga-ui/cdk'; +import {TuiSizeS} from '@taiga-ui/core'; + +@Component({ + selector: 'tui-toggle-example-1', + templateUrl: './index.html', + styleUrls: ['./index.less'], + changeDetection, + encapsulation, +}) +export class TuiToggleExample1 implements OnInit { + readonly platforms: readonly TuiPlatform[] = ['web', 'web', 'android', 'ios']; + readonly invalidTrue = new FormControl(true, () => ({invalid: true})); + readonly invalidFalse = new FormControl(false, () => ({invalid: true})); + + ngOnInit(): void { + this.invalidTrue.markAsTouched(); + this.invalidFalse.markAsTouched(); + } + + getSize(first: boolean): TuiSizeS { + return first ? 'm' : 's'; + } +} diff --git a/projects/demo/src/modules/experimental/toggle/examples/import/import-module.md b/projects/demo/src/modules/experimental/toggle/examples/import/import-module.md new file mode 100644 index 000000000000..b8385a693797 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/examples/import/import-module.md @@ -0,0 +1,13 @@ +```ts +import {NgModule} from '@angular/core'; +import {TuiToggleModule} from '@taiga-ui/experimental'; +// ... + +@NgModule({ + imports: [ + // ... + TuiToggleModule, + ], +}) +export class MyModule {} +``` diff --git a/projects/demo/src/modules/experimental/toggle/examples/import/insert-template.md b/projects/demo/src/modules/experimental/toggle/examples/import/insert-template.md new file mode 100644 index 000000000000..468edc79a359 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/examples/import/insert-template.md @@ -0,0 +1,7 @@ +```html + +``` diff --git a/projects/demo/src/modules/experimental/toggle/toggle.component.ts b/projects/demo/src/modules/experimental/toggle/toggle.component.ts new file mode 100644 index 000000000000..a7e1a32acdf3 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/toggle.component.ts @@ -0,0 +1,23 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {TuiDocExample, TuiRawLoaderContent} from '@taiga-ui/addon-doc'; + +@Component({ + selector: 'example-toggle', + templateUrl: './toggle.template.html', + changeDetection, +}) +export class ExampleTuiToggleComponent { + readonly exampleModule: TuiRawLoaderContent = import( + './examples/import/import-module.md?raw' + ); + + readonly exampleHtml: TuiRawLoaderContent = import( + './examples/import/insert-template.md?raw' + ); + + readonly example1: TuiDocExample = { + TypeScript: import('./examples/1/index.ts?raw'), + HTML: import('./examples/1/index.html?raw'), + }; +} diff --git a/projects/demo/src/modules/experimental/toggle/toggle.module.ts b/projects/demo/src/modules/experimental/toggle/toggle.module.ts new file mode 100644 index 000000000000..322561190be2 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/toggle.module.ts @@ -0,0 +1,25 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {tuiGetDocModules} from '@taiga-ui/addon-doc'; +import {TuiPlatformModule} from '@taiga-ui/cdk'; +import {TuiNotificationModule} from '@taiga-ui/core'; +import {TuiToggleModule} from '@taiga-ui/experimental'; + +import {TuiToggleExample1} from './examples/1'; +import {ExampleTuiToggleComponent} from './toggle.component'; + +@NgModule({ + imports: [ + FormsModule, + ReactiveFormsModule, + CommonModule, + TuiToggleModule, + TuiNotificationModule, + TuiPlatformModule, + tuiGetDocModules(ExampleTuiToggleComponent), + ], + declarations: [ExampleTuiToggleComponent, TuiToggleExample1], + exports: [ExampleTuiToggleComponent], +}) +export class ExampleTuiToggleModule {} diff --git a/projects/demo/src/modules/experimental/toggle/toggle.template.html b/projects/demo/src/modules/experimental/toggle/toggle.template.html new file mode 100644 index 000000000000..63f674bc2972 --- /dev/null +++ b/projects/demo/src/modules/experimental/toggle/toggle.template.html @@ -0,0 +1,50 @@ + + + + This code is + experimental + and is a subject to change. Expect final solution to be shipped in the next major version + + +

      A toggle component that is able to imitate native control on mobile platforms.

      + + + + Use + --tui-accent + CSS variable to customize color of native control emulation + + + +
      + + +
        +
      1. +

        Import module

        + + +
      2. + +
      3. +

        Add to the template:

        + + +
      4. +
      +
      +
      diff --git a/projects/experimental/components/checkbox/checkbox.component.ts b/projects/experimental/components/checkbox/checkbox.component.ts new file mode 100644 index 000000000000..0cf3ba6e4a03 --- /dev/null +++ b/projects/experimental/components/checkbox/checkbox.component.ts @@ -0,0 +1,88 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + HostBinding, + Inject, + Input, + OnInit, + Optional, + Self, +} from '@angular/core'; +import {NgControl} from '@angular/forms'; +import { + TUI_BASE_HREF, + TUI_PLATFORM, + tuiControlValue, + TuiDestroyService, + tuiIsString, + TuiPlatform, + tuiWatch, +} from '@taiga-ui/cdk'; +import { + MODE_PROVIDER, + TUI_MODE, + TUI_SVG_OPTIONS, + TuiBrightness, + TuiSvgOptions, +} from '@taiga-ui/core'; +import {Observable} from 'rxjs'; +import {distinctUntilChanged, takeUntil} from 'rxjs/operators'; + +import {TUI_CHECKBOX_OPTIONS, TuiCheckboxOptions} from './checkbox.options'; + +@Component({ + selector: 'input[type="checkbox"][tuiCheckbox]', + template: '', + styleUrls: ['./checkbox.style.less'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [MODE_PROVIDER, TuiDestroyService], + host: { + '($.data-mode.attr)': 'mode$', + '[disabled]': '!control || control.disabled', + '[attr.data-size]': 'size', + '[attr.data-platform]': 'platform', + '[class._invalid]': 'control?.invalid && control?.touched', + '[class._readonly]': '!control', + }, +}) +export class TuiCheckboxComponent implements OnInit { + @Input() + size = this.options.size; + + constructor( + @Inject(ChangeDetectorRef) private readonly cdr: ChangeDetectorRef, + @Inject(TUI_BASE_HREF) private readonly baseHref: string, + @Inject(TUI_SVG_OPTIONS) private readonly svg: TuiSvgOptions, + @Inject(TUI_CHECKBOX_OPTIONS) private readonly options: TuiCheckboxOptions, + @Self() @Inject(TuiDestroyService) private readonly destroy$: Observable, + @Inject(ElementRef) private readonly el: ElementRef, + @Inject(TUI_MODE) readonly mode$: Observable, + @Inject(TUI_PLATFORM) readonly platform: TuiPlatform, + @Optional() @Inject(NgControl) readonly control: NgControl | null, + ) {} + + @HostBinding('style.--t-mask') + get icon(): string { + const option = this.el.nativeElement.indeterminate + ? this.options.icons.indeterminate + : this.options.icons.checked; + const icon = tuiIsString(option) ? option : option(this.size); + const mask = icon.includes('/') ? icon : this.svg.path(icon, this.baseHref); + + return `url(${mask})`; + } + + ngOnInit(): void { + if (!this.control?.valueChanges) { + return; + } + + tuiControlValue(this.control) + .pipe(distinctUntilChanged(), tuiWatch(this.cdr), takeUntil(this.destroy$)) + .subscribe(value => { + this.el.nativeElement.indeterminate = value === null; + }); + } +} diff --git a/projects/experimental/components/checkbox/checkbox.module.ts b/projects/experimental/components/checkbox/checkbox.module.ts new file mode 100644 index 000000000000..2e5bfd0c0657 --- /dev/null +++ b/projects/experimental/components/checkbox/checkbox.module.ts @@ -0,0 +1,9 @@ +import {NgModule} from '@angular/core'; + +import {TuiCheckboxComponent} from './checkbox.component'; + +@NgModule({ + declarations: [TuiCheckboxComponent], + exports: [TuiCheckboxComponent], +}) +export class TuiCheckboxModule {} diff --git a/projects/experimental/components/checkbox/checkbox.options.ts b/projects/experimental/components/checkbox/checkbox.options.ts new file mode 100644 index 000000000000..89074a71a348 --- /dev/null +++ b/projects/experimental/components/checkbox/checkbox.options.ts @@ -0,0 +1,27 @@ +import {Provider} from '@angular/core'; +import {tuiCreateToken, tuiProvideOptions, TuiTypedMapper} from '@taiga-ui/cdk'; +import {TuiSizeS} from '@taiga-ui/core'; + +export interface TuiCheckboxOptions { + readonly size: TuiSizeS; + readonly icons: Readonly<{ + checked: TuiTypedMapper<[TuiSizeS], string> | string; + indeterminate: TuiTypedMapper<[TuiSizeS], string> | string; + }>; +} + +export const TUI_CHECKBOX_DEFAULT_OPTIONS: TuiCheckboxOptions = { + size: `m`, + icons: { + checked: size => (size === `m` ? `tuiIconCheckLarge` : `tuiIconCheck`), + indeterminate: size => (size === `m` ? `tuiIconMinusLarge` : `tuiIconMinus`), + }, +}; + +export const TUI_CHECKBOX_OPTIONS = tuiCreateToken(TUI_CHECKBOX_DEFAULT_OPTIONS); + +export function tuiCheckboxOptionsProvider( + options: Partial, +): Provider { + return tuiProvideOptions(TUI_CHECKBOX_OPTIONS, options, TUI_CHECKBOX_DEFAULT_OPTIONS); +} diff --git a/projects/experimental/components/checkbox/checkbox.style.less b/projects/experimental/components/checkbox/checkbox.style.less new file mode 100644 index 000000000000..ab142ad601ed --- /dev/null +++ b/projects/experimental/components/checkbox/checkbox.style.less @@ -0,0 +1,166 @@ +@import 'taiga-ui-local'; + +:host { + .transition(~'background, box-shadow'); + position: relative; + appearance: none; + cursor: pointer; + margin: 0; + outline: none; + box-shadow: inset 0 0 0 0.0625rem var(--tui-clear-active); + + &:disabled { + pointer-events: none; + + &:not(._readonly) { + opacity: var(--tui-disabled-opacity); + } + } + + &:focus-visible { + box-shadow: inset 0 0 0 0.125rem var(--tui-focus) !important; + } + + &:checked:before, + &:indeterminate:before { + mask-image: var(--t-mask); + } + + &:before { + .fullsize(); + content: ''; + background: currentColor; + mask-image: url('data:image/svg+xml,'); + mask-size: 100%; + } + + &._invalid { + box-shadow: inset 0 0 0 0.0625rem var(--tui-error-bg-hover); + } + + &[data-platform='ios'] { + width: 1.375rem; + height: 1.375rem; + border-radius: 100%; + color: var(--tui-base-01); + transition: none; + + &:checked, + &:indeterminate { + background: var(--tui-accent); + box-shadow: none; + + &:disabled:not(._readonly) { + background: var(--tui-base-04); + } + + &:before { + display: block; + } + + &._invalid { + background: var(--tui-error-fill); + } + } + + &:before { + display: none; + + --t-mask: url('data:image/svg+xml,'); + } + + &:indeterminate:before { + --t-mask: url('data:image/svg+xml,'); + } + } + + &[data-platform='android'] { + width: 1.125rem; + height: 1.125rem; + color: var(--tui-base-01); + border-radius: 0.125rem; + box-shadow: inset 0 0 0 0.125rem var(--tui-base-04); + + &:checked, + &:indeterminate { + background: var(--tui-accent); + box-shadow: none; + + &._invalid { + background: var(--tui-error-fill); + } + + &:disabled:not(._readonly) { + background: var(--tui-base-04); + } + + &:before { + clip-path: inset(0); + transition-delay: 0s; + } + } + + &:before { + transition: + clip-path var(--tui-duration) ease-in-out, + mask 0s var(--tui-duration) ease-in-out; + clip-path: inset(0 100% 0 0); + + --t-mask: url('data:image/svg+xml,'); + } + + &:indeterminate:before { + --t-mask: url('data:image/svg+xml,'); + } + + &._invalid { + box-shadow: inset 0 0 0 0.125rem var(--tui-error-bg-hover); + } + } + + &[data-platform='web'] { + width: 1.5rem; + height: 1.5rem; + border-radius: var(--tui-radius-s); + color: var(--tui-primary-text); + + &[data-size='s'] { + width: 1rem; + height: 1rem; + border-radius: var(--tui-radius-xs); + } + + &:checked, + &:indeterminate { + box-shadow: none; + background: var(--tui-primary); + + &:hover { + background: var(--tui-primary-hover); + } + + &:active { + background: var(--tui-primary-active); + } + + &:before { + transform: scale(1); + transition: + transform var(--tui-duration) ease-in-out, + mask 0s ease-in-out; + } + } + + &:before { + transform: scale(0); + transition: + transform var(--tui-duration) ease-in-out, + mask 0s var(--tui-duration) ease-in-out; + } + + &._invalid:checked, + &._invalid:indeterminate { + background: var(--tui-error-fill); + } + } +} diff --git a/projects/experimental/components/checkbox/index.ts b/projects/experimental/components/checkbox/index.ts new file mode 100644 index 000000000000..41366876b271 --- /dev/null +++ b/projects/experimental/components/checkbox/index.ts @@ -0,0 +1,3 @@ +export * from './checkbox.component'; +export * from './checkbox.module'; +export * from './checkbox.options'; diff --git a/projects/experimental/components/checkbox/ng-package.json b/projects/experimental/components/checkbox/ng-package.json new file mode 100644 index 000000000000..bab5ebcdb74a --- /dev/null +++ b/projects/experimental/components/checkbox/ng-package.json @@ -0,0 +1,8 @@ +{ + "lib": { + "entryFile": "index.ts", + "styleIncludePaths": [ + "../../../core/styles" + ] + } +} diff --git a/projects/experimental/components/index.ts b/projects/experimental/components/index.ts index 3acf7da15bb4..f51044036a09 100644 --- a/projects/experimental/components/index.ts +++ b/projects/experimental/components/index.ts @@ -4,5 +4,8 @@ export * from '@taiga-ui/experimental/components/badge'; export * from '@taiga-ui/experimental/components/badge-notification'; export * from '@taiga-ui/experimental/components/badged-content'; export * from '@taiga-ui/experimental/components/button'; +export * from '@taiga-ui/experimental/components/checkbox'; export * from '@taiga-ui/experimental/components/compass'; +export * from '@taiga-ui/experimental/components/radio'; export * from '@taiga-ui/experimental/components/rating'; +export * from '@taiga-ui/experimental/components/toggle'; diff --git a/projects/experimental/components/radio/index.ts b/projects/experimental/components/radio/index.ts new file mode 100644 index 000000000000..e7d9e17e55bd --- /dev/null +++ b/projects/experimental/components/radio/index.ts @@ -0,0 +1,2 @@ +export * from './radio.component'; +export * from './radio.module'; diff --git a/projects/experimental/components/radio/ng-package.json b/projects/experimental/components/radio/ng-package.json new file mode 100644 index 000000000000..bab5ebcdb74a --- /dev/null +++ b/projects/experimental/components/radio/ng-package.json @@ -0,0 +1,8 @@ +{ + "lib": { + "entryFile": "index.ts", + "styleIncludePaths": [ + "../../../core/styles" + ] + } +} diff --git a/projects/experimental/components/radio/radio.component.ts b/projects/experimental/components/radio/radio.component.ts new file mode 100644 index 000000000000..e6faf2dd20e8 --- /dev/null +++ b/projects/experimental/components/radio/radio.component.ts @@ -0,0 +1,31 @@ +import {ChangeDetectionStrategy, Component, Inject, Input, Optional} from '@angular/core'; +import {NgControl} from '@angular/forms'; +import {TUI_PLATFORM, TuiPlatform} from '@taiga-ui/cdk'; +import {MODE_PROVIDER, TUI_MODE, TuiBrightness, TuiSizeS} from '@taiga-ui/core'; +import {Observable} from 'rxjs'; + +@Component({ + selector: 'input[type="radio"][tuiRadio]', + template: '', + styleUrls: ['./radio.style.less'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [MODE_PROVIDER], + host: { + '($.data-mode.attr)': 'mode$', + '[disabled]': '!control || control.disabled', + '[attr.data-size]': 'size', + '[attr.data-platform]': 'platform', + '[class._invalid]': 'control?.invalid && control?.touched', + '[class._readonly]': '!control', + }, +}) +export class TuiRadioComponent { + @Input() + size: TuiSizeS = 'm'; + + constructor( + @Inject(TUI_MODE) readonly mode$: Observable, + @Inject(TUI_PLATFORM) readonly platform: TuiPlatform, + @Optional() @Inject(NgControl) readonly control: NgControl | null, + ) {} +} diff --git a/projects/experimental/components/radio/radio.module.ts b/projects/experimental/components/radio/radio.module.ts new file mode 100644 index 000000000000..355923bba159 --- /dev/null +++ b/projects/experimental/components/radio/radio.module.ts @@ -0,0 +1,9 @@ +import {NgModule} from '@angular/core'; + +import {TuiRadioComponent} from './radio.component'; + +@NgModule({ + declarations: [TuiRadioComponent], + exports: [TuiRadioComponent], +}) +export class TuiRadioModule {} diff --git a/projects/experimental/components/radio/radio.style.less b/projects/experimental/components/radio/radio.style.less new file mode 100644 index 000000000000..b08be65d9526 --- /dev/null +++ b/projects/experimental/components/radio/radio.style.less @@ -0,0 +1,132 @@ +@import 'taiga-ui-local'; + +:host { + .transition(~'background, box-shadow'); + + --t-size: 1.5rem; + position: relative; + width: var(--t-size); + height: var(--t-size); + appearance: none; + cursor: pointer; + margin: 0; + border-radius: 100%; + outline: none; + box-shadow: inset 0 0 0 0.0625rem var(--tui-clear-active); + + &:disabled { + pointer-events: none; + + &:not(._readonly) { + opacity: var(--tui-disabled-opacity); + } + } + + &:focus-visible { + box-shadow: inset 0 0 0 0.125rem var(--tui-focus) !important; + } + + &:after { + .fullsize(); + .transition(transform); + content: ''; + border-radius: 100%; + background: currentColor; + transform: scale(0); + } + + &:checked:after { + transform: scale(0.5); + } + + &._invalid { + box-shadow: inset 0 0 0 0.0625rem var(--tui-error-bg-hover); + } + + &[data-platform='ios'] { + width: 1.375rem; + height: 1.375rem; + border-radius: 100%; + box-shadow: inset 0 0 0 0.0625rem var(--tui-base-04); + color: var(--tui-base-01); + transition: none; + + &:checked { + background: var(--tui-accent); + box-shadow: none; + + &:disabled:not(._readonly) { + background: var(--tui-base-04); + } + + &:before { + .fullsize(); + content: ''; + background: var(--tui-base-01); + mask-image: url('data:image/svg+xml,'); + mask-size: 100%; + } + + &._invalid { + background: var(--tui-error-fill); + } + } + + &:after { + display: none; + } + } + + &[data-platform='android'] { + .transition(color); + + --t-size: 1.125rem; + box-shadow: inset 0 0 0 0.125rem; + color: var(--tui-base-04); + + &:disabled:not(._readonly) { + color: var(--tui-base-04); + } + + &:checked { + color: var(--tui-accent); + + &:after { + transform: scale(0.555); + } + } + + &._invalid { + color: var(--tui-error-bg-hover); + + &:checked { + color: var(--tui-error-fill); + } + } + } + + &[data-platform='web'] { + color: var(--tui-primary-text); + + &[data-size='s'] { + --t-size: 1rem; + } + + &:checked { + box-shadow: none; + background: var(--tui-primary); + + &:hover { + background: var(--tui-primary-hover); + } + + &:active { + background: var(--tui-primary-active); + } + } + + &._invalid:checked { + background: var(--tui-error-fill); + } + } +} diff --git a/projects/experimental/components/toggle/index.ts b/projects/experimental/components/toggle/index.ts new file mode 100644 index 000000000000..6cec914ec3c8 --- /dev/null +++ b/projects/experimental/components/toggle/index.ts @@ -0,0 +1,2 @@ +export * from './toggle.component'; +export * from './toggle.module'; diff --git a/projects/experimental/components/toggle/ng-package.json b/projects/experimental/components/toggle/ng-package.json new file mode 100644 index 000000000000..bab5ebcdb74a --- /dev/null +++ b/projects/experimental/components/toggle/ng-package.json @@ -0,0 +1,8 @@ +{ + "lib": { + "entryFile": "index.ts", + "styleIncludePaths": [ + "../../../core/styles" + ] + } +} diff --git a/projects/experimental/components/toggle/toggle.component.ts b/projects/experimental/components/toggle/toggle.component.ts new file mode 100644 index 000000000000..55c7b16c111c --- /dev/null +++ b/projects/experimental/components/toggle/toggle.component.ts @@ -0,0 +1,62 @@ +import { + ChangeDetectionStrategy, + Component, + HostBinding, + Inject, + Input, + Optional, +} from '@angular/core'; +import {NgControl} from '@angular/forms'; +import {TUI_BASE_HREF, TUI_PLATFORM, tuiIsString, TuiPlatform} from '@taiga-ui/cdk'; +import { + MODE_PROVIDER, + TUI_MODE, + TUI_SVG_OPTIONS, + TuiBrightness, + TuiSvgOptions, +} from '@taiga-ui/core'; +import {Observable} from 'rxjs'; + +import {TUI_TOGGLE_OPTIONS, TuiToggleOptions} from './toggle.options'; + +@Component({ + selector: 'input[type="checkbox"][tuiToggle]', + template: '', + styleUrls: ['./toggle.style.less'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [MODE_PROVIDER], + host: { + '($.data-mode.attr)': 'mode$', + '[disabled]': '!control || control.disabled', + '[attr.data-size]': 'size', + '[attr.data-platform]': 'platform', + '[class._invalid]': 'control?.invalid && control?.touched', + '[class._readonly]': '!control', + '[class._icons]': 'showIcons', + }, +}) +export class TuiToggleComponent { + @Input() + size = this.options.size; + + @Input() + showIcons = this.options.showIcons; + + constructor( + @Inject(TUI_BASE_HREF) private readonly baseHref: string, + @Inject(TUI_SVG_OPTIONS) private readonly svg: TuiSvgOptions, + @Inject(TUI_TOGGLE_OPTIONS) private readonly options: TuiToggleOptions, + @Inject(TUI_MODE) readonly mode$: Observable, + @Inject(TUI_PLATFORM) readonly platform: TuiPlatform, + @Optional() @Inject(NgControl) readonly control: NgControl | null, + ) {} + + @HostBinding('style.--t-mask') + get icon(): string { + const {options, svg, baseHref, size} = this; + const icon = tuiIsString(options.icon) ? options.icon : options.icon(size); + const mask = icon.includes('/') ? icon : svg.path(icon, baseHref); + + return `url(${mask})`; + } +} diff --git a/projects/experimental/components/toggle/toggle.module.ts b/projects/experimental/components/toggle/toggle.module.ts new file mode 100644 index 000000000000..71069c9ce1cc --- /dev/null +++ b/projects/experimental/components/toggle/toggle.module.ts @@ -0,0 +1,9 @@ +import {NgModule} from '@angular/core'; + +import {TuiToggleComponent} from './toggle.component'; + +@NgModule({ + declarations: [TuiToggleComponent], + exports: [TuiToggleComponent], +}) +export class TuiToggleModule {} diff --git a/projects/experimental/components/toggle/toggle.options.ts b/projects/experimental/components/toggle/toggle.options.ts new file mode 100644 index 000000000000..4cc16d15b94f --- /dev/null +++ b/projects/experimental/components/toggle/toggle.options.ts @@ -0,0 +1,21 @@ +import {Provider} from '@angular/core'; +import {tuiCreateToken, tuiProvideOptions, TuiTypedMapper} from '@taiga-ui/cdk'; +import {TuiSizeS} from '@taiga-ui/core'; + +export interface TuiToggleOptions { + readonly showIcons: boolean; + readonly size: TuiSizeS; + readonly icon: TuiTypedMapper<[TuiSizeS], string> | string; +} + +export const TUI_TOGGLE_DEFAULT_OPTIONS: TuiToggleOptions = { + showIcons: true, + size: `m`, + icon: `tuiIconCheck`, +}; + +export const TUI_TOGGLE_OPTIONS = tuiCreateToken(TUI_TOGGLE_DEFAULT_OPTIONS); + +export function tuiToggleOptionsProvider(options: Partial): Provider { + return tuiProvideOptions(TUI_TOGGLE_OPTIONS, options, TUI_TOGGLE_DEFAULT_OPTIONS); +} diff --git a/projects/experimental/components/toggle/toggle.style.less b/projects/experimental/components/toggle/toggle.style.less new file mode 100644 index 000000000000..39f51c5df325 --- /dev/null +++ b/projects/experimental/components/toggle/toggle.style.less @@ -0,0 +1,267 @@ +@import 'taiga-ui-local'; + +:host { + .transition(~'background, box-shadow'); + position: relative; + appearance: none; + cursor: pointer; + margin: 0; + outline: none; + + &:disabled { + pointer-events: none; + + &:not(._readonly) { + opacity: var(--tui-disabled-opacity); + } + } + + &:focus-visible { + box-shadow: inset 0 0 0 0.125rem var(--tui-focus) !important; + } + + &[data-platform='ios'] { + height: 1.9375rem; + width: 3.1875rem; + border-radius: 2rem; + color: var(--tui-text-01-night); + background: var(--tui-base-04); + + &:after { + .transition(~'transform, width'); + content: ''; + display: block; + width: 1.9375rem; + height: 1.9375rem; + border-radius: 2rem; + background: currentColor; + transform: scale(0.871); + box-shadow: + 0 0.25rem 0.125rem rgba(0, 0, 0, 0.06), + 0 0.25rem 0.5rem rgba(0, 0, 0, 0.15); + } + + &:active:after { + width: 2.3rem; + } + + &:checked { + background: var(--tui-accent); + + &:after { + transform: scale(0.871) translateX(1.44rem); + } + + &:active:after { + transform: scale(0.871) translateX(1.0775rem); + } + } + + &._invalid { + background: var(--tui-error-bg); + + &:checked { + background: var(--tui-error-fill); + } + } + } + + &[data-platform='android'] { + .transition(~'color, opacity'); + height: 1.25rem; + width: 2.5rem; + color: var(--tui-base-05); + background: transparent; + + &:before { + content: ''; + display: block; + height: 100%; + border: 0.1875rem solid transparent; + box-sizing: border-box; + border-radius: 2rem; + background: currentColor; + background-clip: content-box; + opacity: 0.5; + } + + &:after { + .transition(transform); + content: ''; + position: absolute; + top: 0; + left: 0; + width: 1.25rem; + height: 1.25rem; + border-radius: 100%; + background: #ececec; + box-shadow: + 0 0.0625rem 0.0625rem rgba(0, 0, 0, 0.24), + 0 0 0.0625rem rgba(0, 0, 0, 0.12), + inset 0 0.0625rem rgba(255, 255, 255, 0.12); + } + + &:checked { + color: var(--tui-accent); + + &:after { + background: currentColor; + transform: translateX(1.25rem); + } + } + + &._invalid { + color: var(--tui-error-fill); + + &:not(:checked) { + opacity: 0.4; + } + + &:after { + background: var(--tui-error-fill); + } + } + } + + &[data-platform='web'] { + width: 3rem; + height: 1.5rem; + border-radius: 2rem; + color: var(--tui-primary-text); + overflow: hidden; + + &:checked { + background: var(--tui-primary); + + &:hover { + background: var(--tui-primary-hover); + } + + &:active { + background: var(--tui-primary-active); + } + } + + &:not(:checked) { + background: var(--tui-secondary); + + &:hover { + background: var(--tui-secondary-hover); + } + + &:active { + background: var(--tui-secondary-active); + } + + &[data-mode='onDark'] { + background: var(--tui-clear-inverse); + + &:hover { + background: var(--tui-clear-inverse-hover); + } + + &:active { + background: var(--tui-clear-inverse-hover); + } + } + } + + &:before, + &:after { + .transition(transform); + content: ''; + position: absolute; + height: 100%; + width: 1.5rem; + } + + &:before { + display: none; + background: currentColor; + mask: var(--t-mask) no-repeat center; + transform: translateX(-1.5rem); + } + + &:after { + right: 0; + border-radius: 100%; + transform: scale(0.5); + box-shadow: -3rem 0 0 0.25rem var(--tui-text-01-night); + } + + &:checked:before { + transform: none; + } + + &:checked:after { + transform: scale(0.5) translateX(3rem); + } + + &[data-size='s'] { + height: 1rem; + width: 2rem; + + &:before { + width: 1rem; + transform: translateX(-1rem) scale(0.875); + } + + &:after { + width: 1rem; + box-shadow: -2rem 0 0 0.25rem var(--tui-text-01-night); + } + + &:checked:before { + transform: scale(0.875); + } + + &:checked:after { + transform: scale(0.5) translateX(2rem); + } + } + + &._invalid { + background: var(--tui-error-bg); + + &:hover, + &:active { + background: var(--tui-error-bg-hover); + } + + &:checked { + background: var(--tui-error-fill); + } + + &[data-mode='onDark'] { + background: var(--tui-error-bg-night); + + &:hover, + &:active { + background: var(--tui-error-bg-night-hover); + } + + &:checked { + background: var(--tui-error-fill-night); + } + } + } + + &._icons { + &:before { + display: block; + } + + &:after { + box-shadow: + inset 0 0 0 0.25rem var(--tui-clear-active), + -3rem 0 0 0.25rem var(--tui-text-01-night); + } + + &[data-size='s']:after { + box-shadow: + inset 0 0 0 0.125rem var(--tui-clear-active), + -2rem 0 0 0.25rem var(--tui-text-01-night); + } + } + } +} diff --git a/projects/icons/all.ts b/projects/icons/all.ts index db28d96e8230..db5e8cc39852 100644 --- a/projects/icons/all.ts +++ b/projects/icons/all.ts @@ -337,7 +337,7 @@ const tuiIconChartLineLarge = ''; const tuiIconCheck = - ''; + ''; const tuiIconCheckCircle = ''; @@ -346,7 +346,7 @@ const tuiIconCheckCircleLarge = ''; const tuiIconCheckLarge = - ''; + ''; const tuiIconCheckList = ''; diff --git a/projects/icons/scripts/prepare-feather-icons.ts b/projects/icons/scripts/prepare-feather-icons.ts index 79541ea5477e..811bdf5da455 100644 --- a/projects/icons/scripts/prepare-feather-icons.ts +++ b/projects/icons/scripts/prepare-feather-icons.ts @@ -1,5 +1,6 @@ const path = require(`path`); const fs = require(`fs`); +const NO_FILL = [`check.svg`]; (function main(): void { const src = path.join( @@ -18,7 +19,7 @@ const fs = require(`fs`); const content = fs.readFileSync(path.join(src, filename), `utf-8`); const processed = content - .replace(` fill="none"`, ``) + .replace(` fill="none"`, NO_FILL.includes(filename) ? ` fill="none"` : ``) .replace(/class="[a-zA-Z0-9:;.\s()\-,]*"/, ``); fs.writeFileSync(path.join(dest, processName(filename, `Large`)), processed); diff --git a/projects/icons/src/tuiIconCheck.svg b/projects/icons/src/tuiIconCheck.svg index 4efcfda7f090..2dd231711e43 100644 --- a/projects/icons/src/tuiIconCheck.svg +++ b/projects/icons/src/tuiIconCheck.svg @@ -7,6 +7,7 @@ width="16" height="16" viewBox="0 0 24 24" + fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" diff --git a/projects/icons/src/tuiIconCheckLarge.svg b/projects/icons/src/tuiIconCheckLarge.svg index 6670f13b1b64..aa5604487873 100644 --- a/projects/icons/src/tuiIconCheckLarge.svg +++ b/projects/icons/src/tuiIconCheckLarge.svg @@ -7,6 +7,7 @@ width="24" height="24" viewBox="0 0 24 24" + fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"