Step {{ step }}
Icons and initials
Image, video and content
Fallback to initials or icon
{{ getSetName($index) }} {{ (item[setIndex] || 0) * 1000 | tuiAmount: 'RUB' }}
} @if (contentType === 'with icon') {
}
Custom status
Use CSS for support colors
@for (_ of '-'.repeat(20); track $index) {Sizes
tui-block-content css class
@for (_ of '-'.repeat(20); track $index) { All work and no play makes Jack a dull boy
}.text-truncate() mixin
.text-truncate() mixin with
Fade
directive
Fade
directive on entire component
itemsLimit: number
tuiAppearanceMode to emulate :checked / :invalid CSS state for outline appearance: ``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiButton} from '@taiga-ui/core'; import {TuiFade} from '@taiga-ui/kit'; @Component({ imports: [TuiButton, TuiFade], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example {} ``` **LESS:** ```less .button { inline-size: 15rem; justify-content: space-between; } ``` #### Link **Template:** ```html Documents Music Library ``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiButton} from '@taiga-ui/core'; @Component({ imports: [TuiButton], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example {} ``` **LESS:** ```less :host { display: flex; gap: 1rem; } ``` #### Label **Template:** ```html ``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiButton} from '@taiga-ui/core'; @Component({ imports: [TuiButton], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example {} ``` **LESS:** ```less :host { display: flex; gap: 1rem; } label:has(input:focus-visible) { outline-color: var(--tui-border-focus); } input { position: absolute; inset: 0; block-size: 100%; inline-size: 100%; cursor: pointer; opacity: 0; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {TuiDocAppearance} from '@demo/components/appearance'; import {TuiDocIcons} from '@demo/components/icons'; import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import {TuiButton, type TuiSizeL, type TuiSizeXS} from '@taiga-ui/core'; import {TuiButtonLoading} from '@taiga-ui/kit'; @Component({ imports: [TuiButton, TuiButtonLoading, TuiDemo, TuiDocAppearance, TuiDocIcons], templateUrl: './index.html', changeDetection, }) export default class Page { protected readonly examples = [ 'Sizes', 'Appearance', 'Icons', 'Loading', 'Options with DI', 'Vertical', 'Two labels', 'Link', 'Label', ]; protected readonly sizes: ReadonlyArray
Selected month: {{ value }}
Hovered month: {{ hoveredMonth }}
``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {type TuiMonth} from '@taiga-ui/cdk'; import {TuiCalendarMonth} from '@taiga-ui/kit'; @Component({ imports: [TuiCalendarMonth], templateUrl: './index.html', encapsulation, changeDetection, }) export default class Example { protected value: TuiMonth | null = null; protected hoveredMonth: TuiMonth | null = null; protected onMonthClick(month: TuiMonth): void { this.value = month; } protected onMonthHovered(month: TuiMonth | null): void { this.hoveredMonth = month; } } ``` #### Range **Template:** ```htmlSelected value: {{ value }}
``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiMonth, TuiMonthRange} from '@taiga-ui/cdk'; import {TuiCalendarMonth, tuiCalendarMonthOptionsProvider} from '@taiga-ui/kit'; @Component({ imports: [TuiCalendarMonth], templateUrl: './index.html', encapsulation, changeDetection, providers: [tuiCalendarMonthOptionsProvider({rangeMode: true})], }) export default class Example { protected value: TuiMonth | TuiMonthRange | null = null; protected max = TuiMonth.currentLocal().append({year: 1}); protected min = new TuiMonth(2019, 7); protected onMonthClick(month: TuiMonth): void { this.value = this.value instanceof TuiMonth ? TuiMonthRange.sort(this.value, month) : month; } } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {RouterModule} from '@angular/router'; import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import { TUI_FALSE_HANDLER, TUI_FIRST_DAY, TUI_LAST_DAY, type TuiBooleanHandler, TuiDay, TuiMonth, TuiMonthRange, TuiYear, } from '@taiga-ui/cdk'; import {TuiLink} from '@taiga-ui/core'; import {TuiCalendarMonth} from '@taiga-ui/kit'; @Component({ imports: [RouterModule, TuiCalendarMonth, TuiDemo, TuiLink], templateUrl: './index.html', changeDetection, }) export default class Example { protected readonly examples = ['Basic', 'Range']; protected readonly minVariants = [ TUI_FIRST_DAY, new TuiMonth(2019, 2), new TuiMonth(2007, 0), ]; protected readonly maxVariants = [ TUI_LAST_DAY, new TuiMonth(2020, 2), new TuiMonth(2023, 0), new TuiMonth(2019, 4), ]; protected min = this.minVariants[0]!; protected max = this.maxVariants[0]!; protected maxLength = 0; protected minLength = 0; protected readonly disabledItemHandlerVariants: ReadonlyArray< TuiBooleanHandler
You are seeing {{ selected }}. @if (!isLastVisible) {
Some clients could encounter an error “Account not found” when selecting the account on the checkout screen
| Project | Version | Description |
|---|---|---|
| Taiga UI | 4.32.0 | Angular UI kit for awesome people |
| Maskito | 3.5.0 | Holy Grail of input masking for the Web |
Cell is used inside DataList options and Textfield template by default. hostDirectives: [TuiConnected]
{{ currentQuestion + 1 }}. {{ questionTitles[currentQuestion] }}
Your answers
@for (options of results; track options; let i = $index) {{{ i + 1 }}. {{ questionTitles[i] }}
@for (question of questions[i]; track question; let j = $index) { }Use CSS to set a custom priority for the fade:
Form control:
{{ control.value | json }}
Form control:
{{ control.value | json }}
PolymorpheusContent so it can be template too!',
yes: 'That is great!',
no: 'Who cares?',
};
this.dialogs
.openPolymorpheusContent, so it can be anything you like!';
protected onClick(): void {
this.dialogs
.open
Bank account
When you copy, the result will have a space prepended and appended: " Taiga UI " .
Alexander Inkin
Roman Sedov
{{ value }}
Email: {{ emailValue }}
Chosen date: {{ dateValue }}
Range date: {{ rangeValue }}
Dol - {{ dollar }}, Eur - {{ euro }}
Don't forget to set ID for accessible label
This is shown fullscreen regardless of content height, you can use margin-top: auto to make sure footer is at the bottom of the page.
Using both built-in "taiga" appearance and custom "compact" appearance to alter built-in styles
Overriding default appearance completely and taking the styles of dialog fully upon oneself, leaving only behavior like focus trap and closable/dismissible interactions to Taiga UI
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate perspiciatis exercitationem nemo velit aliquam voluptates non porro, vel, nihil laudantium sapiente ex omnis corrupti assumenda voluptatibus, architecto sequi saepe consectetur ratione qui. Beatae, sapiente explicabo velit facere repudiandae veniam et soluta quia qui expedita voluptate accusamus dolor adipisci. Illo quia sint consequatur unde nulla fuga eum officiis, impedit dolorem? Vel itaque temporibus nihil quia? Provident earum aperiam autem veritatis hic doloremque unde nesciunt accusantium nisi corrupti.
Content
}Content
}
| Name | Price |
|---|---|
| {{ data[$index]?.name }} |
|
Chapman: Mr Wentworth just told me to come in here and say that there was trouble at the mill, that's all - I didn't expect a kind of Spanish Inquisition.
NOBODY expects the Spanish Inquisition!
I am eagerly loaded but hidden
} You can use ElasticContainer to animate height changes
Just some more content
Making this section bigger than loader }line-clamp CSS rule to keep text under a certain number of lines Form value: {{ form.value | json }}
Form value: {{ form.value | json }}
Form value: {{ form.value | json }}
Subtitle
Directive helps to avoid extra layers of HTML
``` **TypeScript:** ```ts 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 {TuiGroup, TuiRadio, TuiTitle} from '@taiga-ui/core'; import {TuiBlock} from '@taiga-ui/kit'; @Component({ imports: [ReactiveFormsModule, TuiBlock, TuiGroup, TuiRadio, TuiTitle], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly form = new FormGroup({value: new FormControl('')}); } ``` **LESS:** ```less .content { padding: 0.5rem 0; white-space: normal; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import {TuiButton, TuiGroup, type TuiOrientation, type TuiSizeL} from '@taiga-ui/core'; @Component({ imports: [TuiButton, TuiDemo, TuiGroup], templateUrl: './index.html', styleUrl: './index.less', changeDetection, }) export default class Page { protected rounded = true; protected collapsed = false; protected readonly examples = [ 'Inputs', 'ButtonGroup', 'Vertical group', 'Directive', ]; protected readonly orientationVariants: readonly TuiOrientation[] = [ 'horizontal', 'vertical', ]; protected orientation = this.orientationVariants[0]!; protected readonly sizeVariants: readonly TuiSizeL[] = ['m', 'l']; protected size = this.sizeVariants[1]!; protected readonly routes = DemoRoute; } ``` ### LESS ```less .group { max-inline-size: 30.25rem; } ``` --- # components/Header - **Package**: `LAYOUT` - **Type**: components ### Usage Examples #### Sizes **Template:** ```html @for (size of sizes; track $index) {Subtitle
}Opensource
Component library
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium ad aliquam asperiores atque autem consequatur cumque cupiditate delectus doloremque doloribus ea earum eius eos error esse est eum eveniet
Awesome one
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium ad aliquam asperiores atque autem consequatur cumque cupiditate delectus doloremque doloribus ea earum eius eos error esse est eum eveniet
The power of dreams
Best masking library for the web
Some description text
} ``` **TypeScript:** ```ts import {KeyValuePipe} from '@angular/common'; import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiButton, TuiTitle} from '@taiga-ui/core'; import {TuiHeader} from '@taiga-ui/layout'; @Component({ imports: [KeyValuePipe, TuiButton, TuiHeader, TuiTitle], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly variants = { 'A rather long title': 'Long action text', 'Long title text': 'OK', Title: 'Even Longer action text', }; } ``` **LESS:** ```less [tuiHeader] { flex-wrap: wrap; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDemo} from '@demo/utils'; import {tuiDocExampleOptionsProvider} from '@taiga-ui/addon-doc'; @Component({ imports: [TuiDemo], templateUrl: './index.html', changeDetection, providers: [tuiDocExampleOptionsProvider({fullsize: true})], }) export default class Page { protected readonly examples = ['Sizes', 'Accessories', 'Interactive', 'Wrapping']; } ``` --- # components/Icon - **Package**: `CORE` - **Type**: components A component to show icons and color them with CSS. Taiga UI ships with Lucide icons . Same mechanism is used in all iconStart / iconEnd inputs across the library. ### Example ```html
Material Symbols Outlined has user friendly naming, the actual word home gets turned into a house icon:
FontAwesome uses Private Use Area of Unicode to turn \f004 into a heart icon, which you can pass as the icon name:
Alternatively, you can pass empty icon name and use FontAwesome classes to get the same result for the basic case, because it also uses ::before pseudo-element:
Ultimately, what you put after @font. goes into CSS content and gets displayed using font defined via --tui-font-icon variable.
The rest is determined by the font you use and you should consult its documentation.
``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiIcon} from '@taiga-ui/core'; @Component({ imports: [TuiIcon], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example {} ``` **LESS:** ```less .material { --tui-font-icon: 'Material Symbols Outlined'; } .awesome { --tui-font-icon: 'Font Awesome 6 Free'; } ``` ### TypeScript ```ts import {Component, inject} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDemo} from '@demo/utils'; import {TuiIcon} from '@taiga-ui/core'; import {TUI_PREVIEW_ICONS} from '@taiga-ui/kit'; @Component({ imports: [TuiDemo, TuiIcon], templateUrl: './index.html', styleUrl: './index.less', changeDetection, }) export default class Page { protected readonly used = Object.values(inject(TUI_PREVIEW_ICONS)); // compatibility with proprietary icons protected readonly iconVariants = [ '@tui.info', '@tui.heart', ...this.used, 'https://raw.githubusercontent.com/MarsiBarsi/readme-icons/main/github.svg', "\"data:image/svg+xml,\"", '', ]; protected readonly backgroundVariants = [ '', '@tui.info-filled', '@tui.heart-filled', ...new Set( this.used.map((icon) => (icon.includes('filled') ? icon : `${icon}-filled`)), ), ]; protected readonly examples = [ 'Basic', 'Parameters', 'Features', 'Bundled', 'Resolver', 'External', 'Background', 'Icon fonts', ]; protected icon = '@tui.heart'; protected background = ''; protected badge = ''; } ``` ### LESS ```less tui-icon { color: var(--tui-status-info); &::after { color: var(--tui-status-warning); } } ``` --- # components/Input - **Package**: `CORE` - **Type**: components Input is a basic string textfield. All other input components are built on its basis. ### Example ```html{{ form.value | json }}
``` **TypeScript:** ```ts import {JsonPipe} from '@angular/common'; import {Component, inject, signal} from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {tuiCreateLuhnValidator, TuiInputCard} from '@taiga-ui/addon-commerce'; import { TuiError, TuiNotificationService, tuiTextfieldOptionsProvider, } from '@taiga-ui/core'; @Component({ imports: [JsonPipe, ReactiveFormsModule, TuiError, TuiInputCard], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, providers: [tuiTextfieldOptionsProvider({cleaner: signal(true)})], }) export default class Example { private readonly alerts = inject(TuiNotificationService); protected readonly form = new FormGroup({ card: new FormControl('', tuiCreateLuhnValidator('Card number is invalid')), expire: new FormControl(''), cvc: new FormControl(''), }); protected onBinChange(bin: string | null): void { this.alerts.open(String(bin), {label: '(binChange)'}).subscribe(); } } ``` **LESS:** ```less form { display: flex; flex-direction: column; gap: 1rem; } section { display: flex; gap: inherit; tui-textfield { flex: 1; } } ``` #### Card **Template:** ```html
| Options |
|---|
|
|
|
|
{{ value | json }}
``` **TypeScript:** ```ts import {JsonPipe} 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 {TuiDay} from '@taiga-ui/cdk'; import {TuiInputDate, tuiInputDateOptionsProvider} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiInputDate], templateUrl: './index.html', encapsulation, changeDetection, providers: [ tuiInputDateOptionsProvider({ valueTransformer: { fromControlValue: (value: Date | null): TuiDay | null => value && TuiDay.fromUtcNativeDate(value), toControlValue: (value: TuiDay | null): Date | null => value?.toUtcNativeDate() || null, }, }), ], }) export default class Example { protected value: Date | null = null; } ``` #### Format **Template:** ```html{{ value | json }}
``` **TypeScript:** ```ts import {JsonPipe} 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 {TuiDay} from '@taiga-ui/cdk'; import {TuiTextfield} from '@taiga-ui/core'; import {TuiInputDateMulti, tuiInputDateMultiOptionsProvider} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiInputDateMulti, TuiTextfield], templateUrl: './index.html', encapsulation, changeDetection, providers: [ tuiInputDateMultiOptionsProvider({ valueTransformer: { fromControlValue: (value: Date[]): TuiDay[] => value.map((d) => TuiDay.fromUtcNativeDate(d)), toControlValue: (value: TuiDay[]): Date[] => value.map((day) => day.toUtcNativeDate()), }, }), ], }) export default class Example { protected value: Date[] | null = [ TuiDay.currentLocal().append({day: -1}).toLocalNativeDate(), TuiDay.currentLocal().toLocalNativeDate(), TuiDay.currentLocal().append({day: 1}).toLocalNativeDate(), ]; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; import {TuiDocAppearance} from '@demo/components/appearance'; import {TuiDocControl} from '@demo/components/control'; import {TuiDocDropdown} from '@demo/components/dropdown'; import {TuiDocIcons} from '@demo/components/icons'; import {TuiDocItemsHandlers} from '@demo/components/items-handlers'; import {TuiDocTextfield} from '@demo/components/textfield'; import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import {TUI_FIRST_DAY, TUI_LAST_DAY, TuiDay} from '@taiga-ui/cdk'; import {TuiDropdown} from '@taiga-ui/core'; import {TuiInputChip, TuiInputDate, TuiInputDateMulti} from '@taiga-ui/kit'; @Component({ imports: [ ReactiveFormsModule, TuiDemo, TuiDocAppearance, TuiDocControl, TuiDocDropdown, TuiDocIcons, TuiDocItemsHandlers, TuiDocTextfield, TuiDropdown, TuiInputChip, TuiInputDate, TuiInputDateMulti, ], templateUrl: './index.html', changeDetection, }) export default class Example { protected readonly control = new FormControl(); protected readonly routes = DemoRoute; public readonly examples = [ 'Basic', 'Chip', 'Disabled items', 'Format', 'customization', 'Value transformer', ]; protected readonly dates = [ TUI_FIRST_DAY, TuiDay.currentLocal(), TuiDay.currentLocal().append({year: 1, month: 1}), TuiDay.currentLocal().append({year: -1, month: -1}), TUI_LAST_DAY, ] as const; protected min = this.dates[0]; protected max = this.dates[4]; protected readonly handler = (item: TuiDay): boolean => item.dayOfWeek() > 4; } ``` --- # components/InputDateRange - **Package**: `KIT` - **Type**: components InputDateRange = Textfield + CalendarRange + ❤️ ### Example ```html{{ value | json }}
``` **TypeScript:** ```ts import {JsonPipe} 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 {TuiDay, TuiTime} from '@taiga-ui/cdk'; import {TuiInputDateTime, tuiInputDateTimeOptionsProvider} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiInputDateTime], templateUrl: './index.html', encapsulation, changeDetection, providers: [ tuiInputDateTimeOptionsProvider({ valueTransformer: { fromControlValue: (value: Date | null): [TuiDay, TuiTime | null] | null => value && [ TuiDay.fromUtcNativeDate(value), new TuiTime(value.getUTCHours(), value.getUTCMinutes()), ], toControlValue: (value: [TuiDay, TuiTime | null] | null): Date | null => { const {hours = 0, minutes = 0} = value?.[1] ?? {}; return ( value && new Date(value[0].toUtcNativeDate().setUTCHours(hours, minutes)) ); }, }, }), ], }) export default class Example { protected value: Date | null = new Date(Date.UTC(2024, 7, 9, 12, 17)); } ``` #### Example 6 **Template:** ```html
Writing practice
Learning to write type underscore and hyphen
Control value:
{{ control.value | json }}
Control value:
{{ value | json }}
```
**TypeScript:**
```ts
import {JsonPipe} 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 {TuiInputNumber} from '@taiga-ui/kit';
@Component({
imports: [FormsModule, JsonPipe, TuiInputNumber],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {
protected value = 0.5;
}
```
#### Large integer and large decimal parts
JavaScript’s built‑in number type loses precision when a value contains too many digits in either the integer part, the decimal part, or both. The built‑in bigint type avoids this issue but supports only integers. If you need to preserve all extremely large digits of both parts, you can override the built‑in TuiValueTransformer and store the number as an {significand: bigint; exp: number} instead.
**Template:**
```html
Control value:
{{ stringified() }}
```
**TypeScript:**
```ts
import {Component, computed, signal} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TuiNumberFormat} from '@taiga-ui/core';
import {TuiInputNumber} from '@taiga-ui/kit';
import {BigIntWithDecimal, type ControlValue} from './transformer';
@Component({
imports: [BigIntWithDecimal, FormsModule, TuiInputNumber, TuiNumberFormat],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {
protected readonly infinity = Infinity;
protected readonly value = signal
Control value:
{{ stringified() }}
Control value: {{ value.toString() }}
value: {{ value() }}
```
**TypeScript:**
```ts
import {Component, computed, signal} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TUI_DEFAULT_MATCHER} from '@taiga-ui/cdk';
import {
TuiCell,
type TuiFilterByInputOptions,
TuiFilterByInputPipe,
TuiTitle,
} from '@taiga-ui/core';
import {TuiAvatar, TuiDataListWrapper, TuiInputPhone} from '@taiga-ui/kit';
class User {
constructor(
public readonly firstName: string,
public readonly lastName: string,
public readonly phone: string,
public readonly avatarUrl: string | null = null,
public readonly disabled = false,
) {}
public toString(): string {
return `${this.firstName} ${this.lastName}`;
}
}
const DATA: readonly User[] = [
new User(
'Alex',
'Inkin',
'+11234567890',
'https://avatars.githubusercontent.com/u/11832552',
),
new User(
'Vladimir',
'Potekhin',
'+13213213213',
'https://avatars.githubusercontent.com/u/46284632',
),
new User(
'Nikita',
'Barsukov',
'+18005553535',
'https://avatars.githubusercontent.com/u/35179038',
),
new User(
'Roman',
'Sedov',
'+18003000600',
'https://avatars.githubusercontent.com/u/10106368',
),
new User(
'Yulia',
'Tsareva',
'+13332221110',
'https://avatars.githubusercontent.com/u/8158578',
),
];
@Component({
imports: [
FormsModule,
TuiAvatar,
TuiCell,
TuiDataListWrapper,
TuiFilterByInputPipe,
TuiInputPhone,
TuiTitle,
],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {
public readonly value = signal('');
public readonly items = DATA;
public readonly user = computed(() =>
this.items.find(({phone}) => phone === this.value()),
);
protected selectUser(user: User): void {
this.value.set(user.phone);
}
protected onInput(value: string): void {
const [user] = this.filter(this.items, value);
if (value === user?.toString() || value === user?.phone) {
this.value.set(user.phone);
}
}
protected readonly filter: TuiFilterByInputOptions{{ value }}
``` **TypeScript:** ```ts import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {type TuiValueTransformer} from '@taiga-ui/cdk'; import {TuiInputPhone, tuiInputPhoneOptionsProvider} from '@taiga-ui/kit'; const VALUE_TRANSFORMER: TuiValueTransformerControl value:
``` **TypeScript:** ```ts import {JsonPipe} 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 {type TuiKeySteps} from '@taiga-ui/core'; import {TuiInputRange} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiInputRange], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected value = [100_000, 500_000]; protected readonly min = 0; protected readonly max = 1_000_000; protected readonly step = 5; protected readonly ticksLabels = ['0', '10K', '100K', '500k', '1000K']; protected readonly segments = this.ticksLabels.length - 1; protected readonly keySteps: TuiKeySteps = [ // [percent, value] [0, this.min], [25, 10_000], [50, 100_000], [75, 500_000], [100, this.max], ]; } ``` **LESS:** ```less @import '@taiga-ui/styles/utils'; .ticks-labels { .tui-slider-ticks-labels(); } ``` #### Example 6 **Template:** ```htmlControl value:
``` **TypeScript:** ```ts import {JsonPipe} 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 {TuiInputRange} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiInputRange], templateUrl: './index.html', encapsulation, changeDetection, }) export default class Example { protected value = [0.25, 0.75]; // Form control can only contain decimal number which is multiple of this constant protected quantum = 0.05; // But granularity of each discrete slider step is equal to this constant protected readonly step = 0.25; } ``` #### Example 7 **Template:** ```htmlControl value:
``` **TypeScript:** ```ts import {I18nPluralPipe, JsonPipe} 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 {CHAR_ZERO_WIDTH_SPACE} from '@taiga-ui/cdk'; import {tuiInputNumberOptionsProvider, TuiInputRange} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, I18nPluralPipe, JsonPipe, TuiInputRange], templateUrl: './index.html', encapsulation, changeDetection, providers: [ tuiInputNumberOptionsProvider({ minusSign: CHAR_ZERO_WIDTH_SPACE, prefix: CHAR_ZERO_WIDTH_SPACE, // Make minus non-erasable }), ], }) export default class Example { protected value = [-30, 0]; // See https://angular.dev/api/common/I18nPluralPipe#example protected readonly pluralize = { '=-1': ' day ago', other: ' days ago', }; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; import {TuiDocControl} from '@demo/components/control'; import {TuiDocNumberFormat} from '@demo/components/number-format'; import {TuiDocTextfield} from '@demo/components/textfield'; import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import {type TuiContext} from '@taiga-ui/cdk'; import { type TuiKeySteps, TuiLoader, TuiNumberFormat, TuiTextfield, TuiTitle, } from '@taiga-ui/core'; import {TuiInputRange} from '@taiga-ui/kit'; import {PolymorpheusComponent, type PolymorpheusContent} from '@taiga-ui/polymorpheus'; @Component({ imports: [ ReactiveFormsModule, TuiDemo, TuiDocControl, TuiDocNumberFormat, TuiDocTextfield, TuiInputRange, TuiNumberFormat, TuiTextfield, TuiTitle, ], templateUrl: './index.html', changeDetection, }) export default class PageComponent { protected readonly routes = DemoRoute; protected readonly control = new FormControl([0, 10]); public readonly examples = [ 'Override number format', 'Affixes', 'Visual segments + labels for ticks', 'Custom value content', 'KeySteps', 'Quantum', 'Using negative values with hidden minus sign', ]; protected readonly suffixVariants = [ ['$', '$'], ['€', '€'], [' ₽', ' ₽'], ['%', '%'], [' kg', ' kg'], ]; protected keyStepsVariants: readonly TuiKeySteps[] = [ [ [0, 0], [50, 1_000], [100, 10_000], ], ]; protected readonly contentVariants: Array< readonly [ PolymorpheusContent Control value: {{ value }}
Control value:
{{ value | json }}
```
**TypeScript:**
```ts
import {JsonPipe} 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 {TuiInputSlider} from '@taiga-ui/kit';
@Component({
imports: [FormsModule, JsonPipe, TuiInputSlider],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {
protected value = 0.5;
// Form control can only contain decimal number which is multiple of this constant
protected quantum = 0.05;
// But granularity of each discrete slider step is equal to this constant
protected readonly step = 0.25;
}
```
### TypeScript
```ts
import {Component, computed, type Signal, signal} from '@angular/core';
import {FormControl, ReactiveFormsModule} from '@angular/forms';
import {TuiDocControl} from '@demo/components/control';
import {TuiDocIcons} from '@demo/components/icons';
import {TuiDocNumberFormat} from '@demo/components/number-format';
import {TuiDocTextfield} from '@demo/components/textfield';
import {changeDetection} from '@demo/emulate/change-detection';
import {DemoRoute} from '@demo/routes';
import {TuiDemo} from '@demo/utils';
import {type TuiContext} from '@taiga-ui/cdk';
import {type TuiKeySteps, TuiNumberFormat, TuiTitle} from '@taiga-ui/core';
import {TuiInputSlider} from '@taiga-ui/kit';
@Component({
imports: [
ReactiveFormsModule,
TuiDemo,
TuiDocControl,
TuiDocIcons,
TuiDocNumberFormat,
TuiDocTextfield,
TuiInputSlider,
TuiNumberFormat,
TuiTitle,
],
templateUrl: './index.html',
changeDetection,
})
export default class PageComponent {
protected readonly routes = DemoRoute;
protected readonly control = new FormControl(0);
protected readonly min = signal(0);
protected readonly max = signal(100);
protected prefix = '';
protected postfix = '';
protected quantum = 0;
protected segments: number[] | number = 1;
protected step = 1;
protected keySteps: TuiKeySteps | null = null;
protected thumbSize = 12;
protected readonly segmentsVariants = [1, 5, 3, [0.2, 0.5], [0.1, 0.3]];
protected readonly keyStepsVariants: SignalControl value:
{{ control.value | json }}
```
**TypeScript:**
```ts
import {JsonPipe} from '@angular/common';
import {Component} from '@angular/core';
import {FormControl, ReactiveFormsModule} from '@angular/forms';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TuiTime} from '@taiga-ui/cdk';
import {TuiInputTime} from '@taiga-ui/kit';
@Component({
imports: [JsonPipe, ReactiveFormsModule, TuiInputTime],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {
protected readonly control = new FormControlControl value:
{{ value | json }}
```
**TypeScript:**
```ts
import {JsonPipe} 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 {maskitoParseTime} from '@maskito/kit';
import {TuiTime} from '@taiga-ui/cdk';
import {TuiInputTime, tuiInputTimeOptionsProvider} from '@taiga-ui/kit';
@Component({
imports: [FormsModule, JsonPipe, TuiInputTime],
templateUrl: './index.html',
encapsulation,
changeDetection,
providers: [
tuiInputTimeOptionsProvider({
valueTransformer: {
fromControlValue(controlValue: string): TuiTime | null {
return controlValue
? TuiTime.fromAbsoluteMilliseconds(
maskitoParseTime(controlValue, {mode: 'HH:MM'}),
)
: null;
},
toControlValue(time: TuiTime | null): string {
return time ? time.toString() : '';
},
},
}),
],
})
export default class Example {
protected value = '';
}
```
#### Native picker
**Template:**
```html
@for (mode of supportedModes; track mode) { Control value: {{ value }}
Value: {{ value() || '—' }}
``` ### API - Inputs | Property | Type | Description | |----------|-----|----------| | [columns] | `number` | Number of grid columns | ### Usage Examples #### Basic keypad **Template:** ```htmlValue: {{ value() || '—' }}
``` **TypeScript:** ```ts import {Component, signal} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiKeypad} from '@taiga-ui/addon-mobile'; import {TuiIcon} from '@taiga-ui/core'; @Component({ imports: [TuiIcon, TuiKeypad], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly digits = ['1', '2', '3', '4', '5', '6', '7', '8', '9']; protected readonly value = signal(''); protected append(digit: string): void { this.value.update((current) => `${current}${digit}`); } protected backspace(): void { this.value.update((current) => current.slice(0, -1)); } protected clear(): void { this.value.set(''); } } ``` **LESS:** ```less .keypad { inline-size: 15rem; } ``` #### Custom keys **Template:** ```html{{ masked() || '—' }}
tuiAmount pipe is used to format currency and fraction
Liked: {{ liked }}
Liked: {{ likeForm.value.liked }}
white-space: nowrap to expand to the right 1. Display only the first line, in a popup display remaining lines.
2. Do not use `tui-line-clamp`, use `text-overflow: ellipsis` instead.
{{ user.email }}
User ID: {{ user.id }}
First Name: {{ user.firstName }}
Last Name: {{ user.lastName }}
User ID: {{ user.id }}
First Name: {{ user.firstName }}
Last Name: {{ user.lastName }}
TuiLineDaysChart is used to show data of several months to simplify working with different number of days in months
@for (chart of days(); track chart) { Chart {{ $index + 1 }} }
If you use no appearance, links would
It would also work well together with dark theme and other text colors
If you want, you cantext-decoration-line: underline or switch text-decoration-style: dashed
```
**TypeScript:**
```ts
import {Component} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TuiLink} from '@taiga-ui/core';
@Component({
imports: [TuiLink],
templateUrl: './index.html',
styleUrl: './index.less',
encapsulation,
changeDetection,
})
export default class Example {}
```
**LESS:**
```less
[tuiTheme='dark'] {
background: var(--tui-background-base);
color: var(--tui-text-secondary);
padding: 1rem;
margin: 1rem -1rem;
}
```
#### Long text
**Template:**
```html
Link can be written inline in text.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the standard dummy text block and if no extra whitespace is introduced, the icon would wrap together with the last word inside the tag
```
**TypeScript:**
```ts
import {Component} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TuiLink} from '@taiga-ui/core';
@Component({
imports: [TuiLink],
templateUrl: './index.html',
encapsulation,
changeDetection,
})
export default class Example {}
```
### TypeScript
```ts
import {Component} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {TuiDemo} from '@demo/utils';
@Component({
imports: [TuiDemo],
templateUrl: './index.html',
changeDetection,
})
export default class Page {
protected readonly examples = ['Basic', 'Icons', 'Appearances', 'Long text'];
}
```
---
# components/List
- **Package**: `LAYOUT`
- **Type**: components
### Usage Examples
#### Bulleted
**Template:**
```html
--tui-list-markervariable on the list itselftuiDropdownAppearance=""
tuiTheme="dark" on the button so it fits the header readonly dark = inject(TUI_DARK_MODE)
[attr.tuiTheme]="dark() ? 'dark' : 'light'"
Document Picture-in-Picture is not supported in your browser, fallback to regular popout window will be used
}Calling...
Hello everyone! This is some important document in A4 format, although it is made using html
This shows that the component preview can work with absolutely any content: this way you can show any template, image, pdf or even iframe with your favorite site. We will put this content in the center of the portal and provide the user with control over it, and we will provide you with convenient levers to change it and process actions.
Use '"> 's CSS-property color to set solid color of progress indicator.
Set component's input property color to get more complex color combinations.
Use directive tuiProgressFixedGradient to make gradient fixed.
Use tuiProgressColorSegments directive to to get multicolor segments.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Custom
Control value:
``` **TypeScript:** ```ts import {I18nPluralPipe, JsonPipe} 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 {TuiIcon} from '@taiga-ui/core'; import {TuiRange} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, I18nPluralPipe, JsonPipe, TuiIcon, TuiRange], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly min = 0; protected readonly max = 100; protected readonly step = 25; protected readonly segments = 4; protected readonly labels = Array.from( {length: this.segments + 1}, (_, i) => this.min + this.step * i, ); protected value = [0, 25]; // https://angular.dev/api/common/I18nPluralPipe#example protected pluralMap = {'=0': '0', '=1': '# item', '=100': 'MAX', other: '# items'}; } ``` **LESS:** ```less @import '@taiga-ui/styles/utils'; .range { z-index: 1; /* (Optionally) expand clickable area as you wish */ &::after { inset-block-start: -0.5rem; inset-block-end: -1.5rem; } } .ticks-labels { .tui-slider-ticks-labels(); } tui-icon::before { font-size: 1rem; } ``` #### KeySteps **Template:** ```htmlControl value:
``` **TypeScript:** ```ts import {JsonPipe} 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 {type TuiKeySteps} from '@taiga-ui/core'; import {TuiRange} from '@taiga-ui/kit'; @Component({ imports: [FormsModule, JsonPipe, TuiRange], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly ticksLabels = ['0', '10K', '100K', '500k', '1000K']; protected readonly segments = this.ticksLabels.length - 1; // 12.5% (of total distance) per step protected readonly stepPercentage = 100 / (2 * this.segments); protected value = [0, 100_000]; protected readonly keySteps: TuiKeySteps = [ // [percent, value] [0, 0], [25, 10_000], [50, 100_000], [75, 500_000], [100, 1_000_000], ]; } ``` **LESS:** ```less @import '@taiga-ui/styles/utils'; .range { z-index: 1; /* (Optionally) expand clickable area as you wish */ &::after { inset-block-start: -0.5rem; inset-block-end: -1.5rem; } } .ticks-labels { .tui-slider-ticks-labels(); } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDemo} from '@demo/utils'; import {type TuiKeySteps, TuiTitle} from '@taiga-ui/core'; import {TuiRange} from '@taiga-ui/kit'; @Component({ imports: [ReactiveFormsModule, TuiDemo, TuiRange, TuiTitle], templateUrl: './index.html', styles: ` :host tui-doc-example ::ng-deep tui-range { max-inline-size: 19rem; } `, changeDetection, }) export default class Page { protected readonly examples = ['Size', 'Segments', 'KeySteps']; protected readonly control = new FormControl([0, 0]); protected readonly limitVariants: readonly number[] = [Infinity, 100, 50, 10, 5, 1]; protected min = 0; protected max = 100; protected margin = 0; protected limit = Infinity; protected step = 1; protected segments = 1; protected thumbSize = 12; protected readonly keyStepsVariants: readonly TuiKeySteps[] = [ [ [0, 0], [50, 1_000], [100, 10_000], ], ]; protected keySteps: TuiKeySteps | null = null; protected get disabled(): boolean { return this.control.disabled; } protected set disabled(value: boolean) { if (value) { this.control.disable(); return; } this.control.enable(); } } ``` --- # components/Rating - **Package**: `KIT` - **Type**: components ### Usage Examples #### Basic **Template:** ```html{{ items | json }}
{{ enabled | json }}
``` **TypeScript:** ```ts import {JsonPipe} from '@angular/common'; import {Component, inject} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiReorder} from '@taiga-ui/addon-table'; @Component({ imports: [JsonPipe, TuiReorder], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected items = inject``` **TypeScript:** ```ts import {Component, signal} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; import {TuiScrollWheel} from '@taiga-ui/experimental'; @Component({ imports: [TuiScrollWheel], templateUrl: './index.html', styleUrl: './index.less', encapsulation, changeDetection, }) export default class Example { protected readonly hours = signal(-2); protected readonly minutes = signal(-2); protected getHours(index: number): string { return (((index % 24) + 24) % 24).toString().padStart(2, '0'); } protected getMinutes(index: number): string { return (((index % 60) + 60) % 60).toString().padStart(2, '0'); } } ``` **LESS:** ```less @import '@taiga-ui/styles/utils'; :host { display: grid; grid-template-columns: 1fr auto 1fr; font: var(--tui-typography-heading-h6); place-items: center; } tui-scroll-wheel { block-size: 15rem; mask-image: linear-gradient(transparent, black 7.5rem, transparent); justify-self: start; &:first-child { justify-self: end; } } div { .transition(color); display: flex; align-items: center; justify-content: center; block-size: 3rem; font: var(--tui-typography-heading-h6); font-variant: tabular-nums; scroll-snap-align: start; color: var(--tui-text-tertiary); &._current { color: var(--tui-text-primary); } } p { grid-column: 1 / -1; } ``` ### TypeScript ```ts import {Component} from '@angular/core'; import {changeDetection} from '@demo/emulate/change-detection'; import {TuiDemo} from '@demo/utils'; import {TuiTitle} from '@taiga-ui/core'; @Component({ imports: [TuiDemo, TuiTitle], templateUrl: './index.html', changeDetection, }) export default class Page {} ``` --- # components/Scrollbar - **Package**: `CORE` - **Type**: components Scrollbar implements a custom scrollbar in Taiga UI style. Native scrollbar is hidden to keep native platform scroll experience Use TUI_SCROLL_REF token to get a scrollable container. For example, when working with virtual scroll. ### Usage Examples #### Vertical **Template:** ```html
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab assumenda delectus dolor eveniet maiores nobis quaerat quo velit vero voluptatem? Aliquam at deserunt excepturi id officiis porro quo quos voluptatum?
Ab aperiam beatae consequatur magnam mollitia necessitatibus quam qui quibusdam soluta, suscipit. Aut doloremque eum, hic quae ratione sunt suscipit! Eaque esse illo libero minima molestiae neque, nobis velit voluptates?
Animi est facere maxime porro quae quibusdam quos totam? Consectetur eligendi, explicabo magnam maxime sit voluptatibus. Assumenda beatae deserunt dolorem earum et eum harum in maxime quae, quam quos rem.
Adipisci commodi consectetur id iure praesentium quam quisquam unde veniam. Corporis cum dicta distinctio error excepturi, impedit quidem veritatis? Cupiditate eos illum ipsum labore, modi omnis repudiandae velit veniam voluptatem.
Asperiores dolorum, ex facilis hic maiores modi neque nisi nobis nostrum numquam placeat quod repellendus sequi velit voluptate! Adipisci atque deleniti eligendi ex tenetur. Beatae cumque dolore impedit perferendis repellat.
top: {{ scrollTop }}
left: {{ scrollLeft }}
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas repudiandae sint ut voluptatem! Optio.
Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident?
Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima numquam sapiente sunt.
Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam omnis quidem, tempora, ut!
Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias deserunt distinctio modi perferendis?
Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem!
Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates.
Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore voluptates!
Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut!
Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta ullam voluptate.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas repudiandae sint ut voluptatem! Optio.
Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident?
Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima numquam sapiente sunt.
Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam omnis quidem, tempora, ut!
Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias deserunt distinctio modi perferendis?
Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem!
Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates.
Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore voluptates!
Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut!
Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta ullam voluptate.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas repudiandae sint ut voluptatem! Optio.
Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident?
Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima numquam sapiente sunt.
Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam omnis quidem, tempora, ut!
Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias deserunt distinctio modi perferendis?
Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem!
Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates.
Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore voluptates!
Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut!
Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta ullam voluptate.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas repudiandae sint ut voluptatem! Optio.
Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident?
Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima numquam sapiente sunt.
Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam omnis quidem, tempora, ut!
Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias deserunt distinctio modi perferendis?
Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem!
Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates.
Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore voluptates!
Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut!
Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta ullam voluptate.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti dignissimos, doloremque. Aperiam assumenda atque aut blanditiis corporis, eum, facilis harum laudantium magni necessitatibus nobis quas repudiandae sint ut voluptatem! Optio.
Accusamus aperiam assumenda aut consectetur, corporis delectus, dolor eaque eius est hic impedit labore possimus provident quas rem, rerum sequi sint tempora tempore ut? Debitis esse neque odio odit provident?
Cum eum illo, ipsa iure nostrum ut voluptates? Autem blanditiis corporis debitis deserunt ex expedita facilis fuga, illum iusto magnam praesentium provident recusandae repudiandae, totam, voluptatem. Minima numquam sapiente sunt.
Beatae consectetur cupiditate dignissimos ducimus eos excepturi labore pariatur placeat quia similique. Architecto aspernatur cumque debitis distinctio esse, facere fugit harum ipsum libero minus neque numquam omnis quidem, tempora, ut!
Mollitia, perspiciatis sunt! Architecto aspernatur assumenda beatae, blanditiis commodi consequuntur debitis et id, laboriosam maxime molestiae neque nihil officiis omnis, quam quos sint veritatis voluptate? Alias deserunt distinctio modi perferendis?
Ab aspernatur aut cumque cupiditate deleniti, dolorem ducimus eligendi eos facere harum hic ipsam ipsum iste itaque modi nam necessitatibus nostrum nulla omnis quae repellat, sapiente sit tempore. Ipsam, quidem!
Ab debitis deleniti distinctio est ex facere magni nemo numquam placeat quia, quibusdam sequi! Aliquid at consectetur culpa ea enim facilis, harum hic, inventore iste possimus praesentium quas tempora voluptates.
Aliquam eligendi ipsam modi nemo numquam obcaecati officia, quidem unde? Accusantium amet, animi deleniti dolorum ea earum eos, expedita ipsa minima modi, pariatur perspiciatis porro quibusdam quo repellat tempore voluptates!
Ab assumenda fugiat magni natus officiis perferendis ratione rem repellendus tenetur. At commodi laudantium modi, natus nobis nulla odio odit sed sint tempora tenetur voluptas? At odio praesentium quas ut!
Atque aut consectetur consequuntur debitis eius facere ipsa ipsam maiores minima minus mollitia qui quos repudiandae sapiente, soluta? Ad, amet dolore doloribus ducimus eos exercitationem molestiae quisquam soluta ullam voluptate.
Parent
{{ form.value | json }}