From 88be120f496bfd2b8c17295e993a0326aa5f7f9b Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 6 Dec 2024 16:18:24 +0300 Subject: [PATCH 1/3] feat(layout): `Form` add new component --- .cspell.json | 2 +- projects/cdk/utils/di/create-options.ts | 7 +- .../utils/miscellaneous/provide-options.ts | 6 +- .../core/components/button/button.options.ts | 13 +-- .../notification/notification.options.ts | 16 +--- projects/demo/src/modules/app/app.routes.ts | 7 +- projects/demo/src/modules/app/demo-routes.ts | 1 + projects/demo/src/modules/app/pages.ts | 6 ++ .../components/form/examples/1/index.html | 87 +++++++++++++++++++ .../components/form/examples/1/index.ts | 50 +++++++++++ .../components/form/examples/import/import.md | 18 ++++ .../form/examples/import/template.md | 9 ++ .../src/modules/components/form/index.html | 24 +++++ .../src/modules/components/form/index.less | 3 + .../demo/src/modules/components/form/index.ts | 14 +++ .../navigation/examples/1/index.html | 60 ++++++++++--- .../components/navigation/examples/1/index.ts | 5 +- .../segmented/segmented.component.ts | 7 +- .../layout/components/card/card.styles.less | 2 +- .../layout/components/form/form.directive.ts | 84 ++++++++++++++++++ .../layout/components/form/form.styles.less | 63 ++++++++++++++ projects/layout/components/form/index.ts | 1 + .../layout/components/form/ng-package.json | 5 ++ .../components/header/header.directive.ts | 11 ++- projects/layout/components/index.ts | 1 + .../components/navigation/main.style.less | 1 + 26 files changed, 458 insertions(+), 45 deletions(-) create mode 100644 projects/demo/src/modules/components/form/examples/1/index.html create mode 100644 projects/demo/src/modules/components/form/examples/1/index.ts create mode 100644 projects/demo/src/modules/components/form/examples/import/import.md create mode 100644 projects/demo/src/modules/components/form/examples/import/template.md create mode 100644 projects/demo/src/modules/components/form/index.html create mode 100644 projects/demo/src/modules/components/form/index.less create mode 100644 projects/demo/src/modules/components/form/index.ts create mode 100644 projects/layout/components/form/form.directive.ts create mode 100644 projects/layout/components/form/form.styles.less create mode 100644 projects/layout/components/form/index.ts create mode 100644 projects/layout/components/form/ng-package.json diff --git a/.cspell.json b/.cspell.json index a0f99167dd00..cfbdc7d7445f 100644 --- a/.cspell.json +++ b/.cspell.json @@ -3,7 +3,7 @@ "import": ["@taiga-ui/cspell-config/cspell.config.js"], "files": ["*/*.*"], "ignorePaths": ["**/projects/i18n/languages/**", "**/addon-commerce/utils/get-currency-symbol.ts"], - "ignoreWords": ["Wachovia", "bottomsheet", "appbar", "qwertypgj_", "antialiasing"], + "ignoreWords": ["Wachovia", "bottomsheet", "appbar", "qwertypgj_", "antialiasing", "xxxs"], "ignoreRegExpList": ["\\(https?://.*?\\)", "\\/{1}.+\\/{1}", "\\%2F.+", "\\%2C.+", "\\ɵ.+", "\\ыва.+"], "overrides": [ { diff --git a/projects/cdk/utils/di/create-options.ts b/projects/cdk/utils/di/create-options.ts index d2f84baa356f..eb96d6e11f46 100644 --- a/projects/cdk/utils/di/create-options.ts +++ b/projects/cdk/utils/di/create-options.ts @@ -1,9 +1,12 @@ -import type {FactoryProvider, InjectionToken} from '@angular/core'; +import type {FactoryProvider, InjectionToken, ProviderToken} from '@angular/core'; import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; export function tuiCreateOptions( defaults: T, -): [token: InjectionToken, provider: (item: Partial) => FactoryProvider] { +): [ + token: InjectionToken, + provider: (item: ProviderToken> | Partial) => FactoryProvider, +] { const token = tuiCreateToken(defaults); return [token, (options) => tuiProvideOptions(token, options, defaults)]; diff --git a/projects/cdk/utils/miscellaneous/provide-options.ts b/projects/cdk/utils/miscellaneous/provide-options.ts index 9dd009236edf..5bbb4b14de5c 100644 --- a/projects/cdk/utils/miscellaneous/provide-options.ts +++ b/projects/cdk/utils/miscellaneous/provide-options.ts @@ -1,16 +1,16 @@ -import type {FactoryProvider, InjectionToken} from '@angular/core'; +import type {FactoryProvider, InjectionToken, ProviderToken} from '@angular/core'; import {inject} from '@angular/core'; export function tuiProvideOptions( provide: InjectionToken, - options: Partial, + options: ProviderToken> | Partial, fallback: T, ): FactoryProvider { return { provide, useFactory: (): T => ({ ...(inject(provide, {optional: true, skipSelf: true}) || fallback), - ...options, + ...(inject(options as any, {optional: true}) || options), }), }; } diff --git a/projects/core/components/button/button.options.ts b/projects/core/components/button/button.options.ts index 2d390e343baf..31b929ab97bf 100644 --- a/projects/core/components/button/button.options.ts +++ b/projects/core/components/button/button.options.ts @@ -1,5 +1,4 @@ -import type {FactoryProvider} from '@angular/core'; -import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; +import {tuiCreateOptions} from '@taiga-ui/cdk'; import type {TuiAppearanceOptions} from '@taiga-ui/core/directives/appearance'; import type {TuiSizeXL, TuiSizeXS} from '@taiga-ui/core/types'; @@ -12,10 +11,6 @@ export const TUI_BUTTON_DEFAULT_OPTIONS: TuiButtonOptions = { size: 'l', }; -export const TUI_BUTTON_OPTIONS = tuiCreateToken(TUI_BUTTON_DEFAULT_OPTIONS); - -export function tuiButtonOptionsProvider( - options: Partial, -): FactoryProvider { - return tuiProvideOptions(TUI_BUTTON_OPTIONS, options, TUI_BUTTON_DEFAULT_OPTIONS); -} +export const [TUI_BUTTON_OPTIONS, tuiButtonOptionsProvider] = tuiCreateOptions( + TUI_BUTTON_DEFAULT_OPTIONS, +); diff --git a/projects/core/components/notification/notification.options.ts b/projects/core/components/notification/notification.options.ts index 16a6364d4725..810167730129 100644 --- a/projects/core/components/notification/notification.options.ts +++ b/projects/core/components/notification/notification.options.ts @@ -1,6 +1,5 @@ -import type {Provider} from '@angular/core'; +import {tuiCreateOptions} from '@taiga-ui/cdk'; import type {TuiStringHandler} from '@taiga-ui/cdk/types'; -import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; import type {TuiAppearanceOptions} from '@taiga-ui/core/directives/appearance'; import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core/types'; @@ -31,14 +30,5 @@ export const TUI_NOTIFICATION_DEFAULT_OPTIONS: TuiNotificationOptions = { /** * Default parameters for notification alert component */ -export const TUI_NOTIFICATION_OPTIONS = tuiCreateToken(TUI_NOTIFICATION_DEFAULT_OPTIONS); - -export function tuiNotificationOptionsProvider( - options: Partial, -): Provider { - return tuiProvideOptions( - TUI_NOTIFICATION_OPTIONS, - options, - TUI_NOTIFICATION_DEFAULT_OPTIONS, - ); -} +export const [TUI_NOTIFICATION_OPTIONS, tuiNotificationOptionsProvider] = + tuiCreateOptions(TUI_NOTIFICATION_DEFAULT_OPTIONS); diff --git a/projects/demo/src/modules/app/app.routes.ts b/projects/demo/src/modules/app/app.routes.ts index 389bcf09f238..e07ad4f1c191 100644 --- a/projects/demo/src/modules/app/app.routes.ts +++ b/projects/demo/src/modules/app/app.routes.ts @@ -324,6 +324,11 @@ export const ROUTES: Routes = [ loadComponent: async () => import('../components/input-color'), title: 'InputColor', }), + route({ + path: DemoRoute.FormLayout, + loadComponent: async () => import('../components/form'), + title: 'Form', + }), route({ path: DemoRoute.Group, loadComponent: async () => import('../components/group'), @@ -746,7 +751,7 @@ export const ROUTES: Routes = [ route({ path: DemoRoute.Form, loadComponent: async () => import('../markup/form'), - title: 'Form', + title: 'Form ', }), route({ path: DemoRoute.Lists, diff --git a/projects/demo/src/modules/app/demo-routes.ts b/projects/demo/src/modules/app/demo-routes.ts index 37daefbb4124..e056dd65fac4 100644 --- a/projects/demo/src/modules/app/demo-routes.ts +++ b/projects/demo/src/modules/app/demo-routes.ts @@ -59,6 +59,7 @@ export const DemoRoute = { Expand: '/components/expand', ElasticContainer: '/components/elastic-container', FieldError: '/pipes/field-error', + FormLayout: '/components/form', InputFiles: '/components/input-files', InputColor: '/components/input-color', Group: '/components/group', diff --git a/projects/demo/src/modules/app/pages.ts b/projects/demo/src/modules/app/pages.ts index 8e67d539d559..d978607349e5 100644 --- a/projects/demo/src/modules/app/pages.ts +++ b/projects/demo/src/modules/app/pages.ts @@ -421,6 +421,12 @@ export const pages: DocRoutePages = [ keywords: 'фильтр, filters', route: DemoRoute.Filter, }, + { + section: 'Components', + title: 'Form', + keywords: 'форма, поле, кнопка, группировка, группа', + route: DemoRoute.FormLayout, + }, { section: 'Components', title: 'Group', diff --git a/projects/demo/src/modules/components/form/examples/1/index.html b/projects/demo/src/modules/components/form/examples/1/index.html new file mode 100644 index 000000000000..797ebade1f2f --- /dev/null +++ b/projects/demo/src/modules/components/form/examples/1/index.html @@ -0,0 +1,87 @@ +
+
+

+ Registration form + Tell us about yourself +

+ + + + + + +
+ +

+ Authenticity required + Please be honest and use your real data +

+
+ + + + + + + + + + + + +
+ + +
+ diff --git a/projects/demo/src/modules/components/form/examples/1/index.ts b/projects/demo/src/modules/components/form/examples/1/index.ts new file mode 100644 index 000000000000..dcdf47d39e7b --- /dev/null +++ b/projects/demo/src/modules/components/form/examples/1/index.ts @@ -0,0 +1,50 @@ +import {AsyncPipe, NgIf} from '@angular/common'; +import {Component} from '@angular/core'; +import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import { + TuiAppearance, + TuiButton, + TuiError, + TuiIcon, + TuiNotification, + TuiTextfield, + TuiTitle, +} from '@taiga-ui/core'; +import {TuiFieldErrorPipe, TuiSegmented, TuiSwitch, TuiTooltip} from '@taiga-ui/kit'; +import {TuiCardLarge, TuiForm, TuiHeader} from '@taiga-ui/layout'; + +@Component({ + standalone: true, + imports: [ + NgIf, + ReactiveFormsModule, + TuiCardLarge, + TuiAppearance, + TuiForm, + TuiHeader, + TuiTitle, + TuiSegmented, + TuiTextfield, + TuiButton, + TuiNotification, + TuiIcon, + TuiTooltip, + TuiSwitch, + TuiError, + TuiFieldErrorPipe, + AsyncPipe, + ], + templateUrl: './index.html', + encapsulation, + changeDetection, +}) +export default class Example { + protected readonly form = new FormGroup({ + name: new FormControl('', Validators.required), + email: new FormControl(''), + subscribe: new FormControl(false), + basic: new FormControl(true), + }); +} diff --git a/projects/demo/src/modules/components/form/examples/import/import.md b/projects/demo/src/modules/components/form/examples/import/import.md new file mode 100644 index 000000000000..dd65e0b7cd0c --- /dev/null +++ b/projects/demo/src/modules/components/form/examples/import/import.md @@ -0,0 +1,18 @@ +```ts +import {TuiAppearance} from '@taiga-ui/core'; +import {TuiCardLarge, TuiForm} from '@taiga-ui/layout'; + +// ... + +@Component({ + standalone: true, + imports: [ + // ... + TuiCardLarge, + TuiAppearance, + TuiForm, + ], + // ... +}) +export class Example {} +``` diff --git a/projects/demo/src/modules/components/form/examples/import/template.md b/projects/demo/src/modules/components/form/examples/import/template.md new file mode 100644 index 000000000000..b2a7f6fb927c --- /dev/null +++ b/projects/demo/src/modules/components/form/examples/import/template.md @@ -0,0 +1,9 @@ +```html +
+ +
+``` diff --git a/projects/demo/src/modules/components/form/index.html b/projects/demo/src/modules/components/form/index.html new file mode 100644 index 000000000000..6bddac439cfe --- /dev/null +++ b/projects/demo/src/modules/components/form/index.html @@ -0,0 +1,24 @@ + + + Size sets DI options, therefore it only works for static values like + tuiForm="m" + . If you need dynamic binding for the size, you would have to set it for buttons, segmented control and header + manually as well. + + + + + + + diff --git a/projects/demo/src/modules/components/form/index.less b/projects/demo/src/modules/components/form/index.less new file mode 100644 index 000000000000..b7cd891b0b2b --- /dev/null +++ b/projects/demo/src/modules/components/form/index.less @@ -0,0 +1,3 @@ +.bar { + block-size: 6.25rem; +} diff --git a/projects/demo/src/modules/components/form/index.ts b/projects/demo/src/modules/components/form/index.ts new file mode 100644 index 000000000000..8d7fbeb20e2c --- /dev/null +++ b/projects/demo/src/modules/components/form/index.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {TuiDemo} from '@demo/utils'; + +@Component({ + standalone: true, + imports: [TuiDemo], + templateUrl: './index.html', + styleUrls: ['./index.less'], + changeDetection, +}) +export default class Page { + protected readonly examples = ['Basic']; +} diff --git a/projects/demo/src/modules/components/navigation/examples/1/index.html b/projects/demo/src/modules/components/navigation/examples/1/index.html index fcc07b263026..febbb7ceee88 100644 --- a/projects/demo/src/modules/components/navigation/examples/1/index.html +++ b/projects/demo/src/modules/components/navigation/examples/1/index.html @@ -299,16 +299,54 @@ Primary -
-

- Some random content - A subtitle -

-
+ +
+
+

+ Registration form + Tell us about yourself +

+
+ + + + +
+ + +
+
+
+

+ Sidebar content + Use CSS grid to position +

+
+
diff --git a/projects/demo/src/modules/components/navigation/examples/1/index.ts b/projects/demo/src/modules/components/navigation/examples/1/index.ts index bb67455a4743..81d29209a54c 100644 --- a/projects/demo/src/modules/components/navigation/examples/1/index.ts +++ b/projects/demo/src/modules/components/navigation/examples/1/index.ts @@ -13,6 +13,7 @@ import { TuiDropdown, TuiDropdownService, TuiIcon, + TuiTextfield, TuiTitle, } from '@taiga-ui/core'; import { @@ -25,7 +26,7 @@ import { TuiSwitch, TuiTabs, } from '@taiga-ui/kit'; -import {TuiCardLarge, TuiHeader, TuiNavigation} from '@taiga-ui/layout'; +import {TuiCardLarge, TuiForm, TuiHeader, TuiNavigation} from '@taiga-ui/layout'; const ICON = "data:image/svg+xml,%0A%3Csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='32' height='32' rx='8' fill='url(%23paint0_linear_2036_35276)'/%3E%3Cmask id='mask0_2036_35276' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='6' y='5' width='20' height='21'%3E%3Cpath d='M18.2399 9.36607C21.1347 10.1198 24.1992 9.8808 26 7.4922C26 7.4922 21.5645 5 16.4267 5C11.2888 5 5.36726 8.69838 6.05472 16.6053C6.38707 20.4279 6.65839 23.7948 6.65839 23.7948C8.53323 22.1406 9.03427 19.4433 8.97983 16.9435C8.93228 14.7598 9.55448 12.1668 12.1847 10.4112C14.376 8.94865 16.4651 8.90397 18.2399 9.36607Z' fill='url(%23paint1_linear_2036_35276)'/%3E%3Cpath d='M11.3171 20.2647C9.8683 17.1579 10.7756 11.0789 16.4267 11.0789C20.4829 11.0789 23.1891 12.8651 22.9447 18.9072C22.9177 19.575 22.9904 20.2455 23.2203 20.873C23.7584 22.3414 24.7159 24.8946 24.7159 24.8946C23.6673 24.5452 22.8325 23.7408 22.4445 22.7058L21.4002 19.921L21.2662 19.3848C21.0202 18.4008 20.136 17.7104 19.1217 17.7104H17.5319L17.6659 18.2466C17.9119 19.2306 18.7961 19.921 19.8104 19.921L22.0258 26H10.4754C10.7774 24.7006 12.0788 23.2368 11.3171 20.2647Z' fill='url(%23paint2_linear_2036_35276)'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_2036_35276)'%3E%3Crect x='4' y='4' width='24' height='24' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_2036_35276' x1='0' y1='0' x2='32' y2='32' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23A681D4'/%3E%3Cstop offset='1' stop-color='%237D31D4'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_2036_35276' x1='6.0545' y1='24.3421' x2='28.8119' y2='3.82775' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0.0001' stop-opacity='0.996458'/%3E%3Cstop offset='0.317708'/%3E%3Cstop offset='1' stop-opacity='0.32'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint2_linear_2036_35276' x1='6.0545' y1='24.3421' x2='28.8119' y2='3.82775' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0.0001' stop-opacity='0.996458'/%3E%3Cstop offset='0.317708'/%3E%3Cstop offset='1' stop-opacity='0.32'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E%0A"; @@ -56,6 +57,8 @@ const ICON = TuiSwitch, TuiTabs, TuiTitle, + TuiForm, + TuiTextfield, ], templateUrl: './index.html', encapsulation, diff --git a/projects/kit/components/segmented/segmented.component.ts b/projects/kit/components/segmented/segmented.component.ts index 9302e6a9c990..85158f5c03e1 100644 --- a/projects/kit/components/segmented/segmented.component.ts +++ b/projects/kit/components/segmented/segmented.component.ts @@ -10,6 +10,7 @@ import { } from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {ResizeObserverService} from '@ng-web-apis/resize-observer'; +import {tuiCreateOptions} from '@taiga-ui/cdk'; import {tuiZonefree} from '@taiga-ui/cdk/observables'; import {tuiInjectElement, tuiIsHTMLElement} from '@taiga-ui/cdk/utils/dom'; import {tuiPx} from '@taiga-ui/cdk/utils/miscellaneous'; @@ -18,6 +19,10 @@ import {tuiBadgeNotificationOptionsProvider} from '@taiga-ui/kit/components/badg import {TuiSegmentedDirective} from './segmented.directive'; +export const [TUI_SEGMENTED_OPTIONS, tuiSegmentedOptionsProvider] = tuiCreateOptions({ + size: 's' as TuiSizeS | TuiSizeL, +}); + @Component({ standalone: true, selector: 'tui-segmented', @@ -39,7 +44,7 @@ export class TuiSegmented implements OnChanges { .subscribe(() => this.refresh()); @Input() - public size: TuiSizeL | TuiSizeS = 's'; + public size: TuiSizeL | TuiSizeS = inject(TUI_SEGMENTED_OPTIONS).size; @Input() public activeItemIndex = 0; diff --git a/projects/layout/components/card/card.styles.less b/projects/layout/components/card/card.styles.less index b55c5176a060..9df6c5272e1d 100644 --- a/projects/layout/components/card/card.styles.less +++ b/projects/layout/components/card/card.styles.less @@ -73,7 +73,7 @@ --t-space: 1.25rem; } - &:not([tuiCell], [tuiHeader]) { + &:not([tuiCell], [tuiHeader], [tuiForm]) { flex-direction: column; gap: var(--t-space); align-items: stretch; diff --git a/projects/layout/components/form/form.directive.ts b/projects/layout/components/form/form.directive.ts new file mode 100644 index 000000000000..ffdc3638c852 --- /dev/null +++ b/projects/layout/components/form/form.directive.ts @@ -0,0 +1,84 @@ +import { + ChangeDetectionStrategy, + Component, + Directive, + inject, + type InjectionToken, + Input, + type Provider, + ViewEncapsulation, +} from '@angular/core'; +import type {TuiStringHandler} from '@taiga-ui/cdk'; +import {tuiWithStyles} from '@taiga-ui/cdk/utils/miscellaneous'; +import { + tuiButtonOptionsProvider, + tuiNotificationOptionsProvider, + type TuiSizeL, + type TuiSizeS, + TuiTextfieldOptionsDirective, +} from '@taiga-ui/core'; +import {TUI_SEGMENTED_OPTIONS, TUI_SWITCH_OPTIONS} from '@taiga-ui/kit'; +import {TUI_HEADER_OPTIONS} from '@taiga-ui/layout/components/header'; + +const HEADER_SIZE: Record = { + s: 'xxxs', + m: 'xs', + l: 's', +}; + +@Component({ + standalone: true, + template: '', + styleUrls: ['./form.styles.less'], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'tui-form', + }, +}) +class TuiFormStyles {} + +@Directive({ + standalone: true, + selector: '[tuiForm]', + host: { + tuiForm: '', + '[attr.data-size]': 'size', + }, + hostDirectives: [ + { + directive: TuiTextfieldOptionsDirective, + inputs: [ + 'tuiTextfieldSize: tuiForm', + 'tuiTextfieldAppearance', + 'tuiTextfieldCleaner', + ], + }, + ], + providers: [ + tuiButtonOptionsProvider(TuiForm), + tuiNotificationOptionsProvider(TuiForm), + projectSize(TUI_HEADER_OPTIONS, (size) => HEADER_SIZE[size]!), + projectSize(TUI_SWITCH_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), + projectSize(TUI_SEGMENTED_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), + ], +}) +export class TuiForm { + protected readonly nothing = tuiWithStyles(TuiFormStyles); + + @Input('tuiForm') + size: TuiSizeS | TuiSizeL = 'l'; +} + +function projectSize( + provide: InjectionToken, + project: TuiStringHandler, +): Provider { + return { + provide, + useFactory: () => ({ + ...inject(provide, {skipSelf: true}), + size: project(inject(TuiForm).size), + }), + }; +} diff --git a/projects/layout/components/form/form.styles.less b/projects/layout/components/form/form.styles.less new file mode 100644 index 000000000000..ad50c74cee7e --- /dev/null +++ b/projects/layout/components/form/form.styles.less @@ -0,0 +1,63 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +[tuiForm] { + display: flex; + flex-direction: column; + align-items: stretch; + + &[data-size='s'] { + gap: 0.75rem; + font: var(--tui-font-text-s); + + tui-error { + margin-top: -0.75rem; + } + + [tuiHeader] { + padding-bottom: 0.5rem; + + [tuiSubtitle] { + font: var(--tui-font-text-ui-s); + } + } + } + + &[data-size='m'] { + gap: 1rem; + font: var(--tui-font-text-s); + + tui-error { + margin-top: -1rem; + } + } + + &[data-size='l'] { + gap: 1.25rem; + font: var(--tui-font-text-m); + + tui-error { + margin-top: -1.25rem; + } + } + + [tuiHeader] { + padding-bottom: 0.25rem; + } + + footer { + display: flex; + gap: 0.75rem; + margin-top: 0.25rem; + } + + [tuiLabel]:not([data-orientation='vertical']) { + font: inherit; + } + + &[data-size='s'], + &[data-size='m'] { + [tuiLabel]:not([data-orientation='vertical']) [tuiTooltip] { + block-size: 1.25rem; + } + } +} diff --git a/projects/layout/components/form/index.ts b/projects/layout/components/form/index.ts new file mode 100644 index 000000000000..0e186e8f6cea --- /dev/null +++ b/projects/layout/components/form/index.ts @@ -0,0 +1 @@ +export * from './form.directive'; diff --git a/projects/layout/components/form/ng-package.json b/projects/layout/components/form/ng-package.json new file mode 100644 index 000000000000..bebf62dcb5e5 --- /dev/null +++ b/projects/layout/components/form/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/projects/layout/components/header/header.directive.ts b/projects/layout/components/header/header.directive.ts index bb743e8badc5..7447917bd1fb 100644 --- a/projects/layout/components/header/header.directive.ts +++ b/projects/layout/components/header/header.directive.ts @@ -2,15 +2,21 @@ import { ChangeDetectionStrategy, Component, Directive, + inject, Input, ViewEncapsulation, } from '@angular/core'; +import {tuiCreateOptions} from '@taiga-ui/cdk'; import {tuiWithStyles} from '@taiga-ui/cdk/utils/miscellaneous'; import {tuiButtonOptionsProvider} from '@taiga-ui/core/components/button'; import type {TuiSizeXXL, TuiSizeXXS} from '@taiga-ui/core/types'; import {tuiAvatarOptionsProvider} from '@taiga-ui/kit/components/avatar'; import {tuiBadgeOptionsProvider} from '@taiga-ui/kit/components/badge'; +export const [TUI_HEADER_OPTIONS, tuiHeaderOptionsProvider] = tuiCreateOptions({ + size: 's' as TuiSizeXXL | TuiSizeXXS, +}); + @Component({ standalone: true, template: '', @@ -33,12 +39,13 @@ class TuiHeaderStyles {} ], host: { tuiHeader: '', - '[attr.data-size]': 'size || "s"', + '[attr.data-size]': 'size || options.size', }, }) export class TuiHeader { + protected readonly options = inject(TUI_HEADER_OPTIONS); protected readonly nothing = tuiWithStyles(TuiHeaderStyles); @Input('tuiHeader') - public size: TuiSizeXXL | TuiSizeXXS | '' = 's'; + public size: TuiSizeXXL | TuiSizeXXS | '' = ''; } diff --git a/projects/layout/components/index.ts b/projects/layout/components/index.ts index 63939d44f0a5..274f45e66e9e 100644 --- a/projects/layout/components/index.ts +++ b/projects/layout/components/index.ts @@ -3,6 +3,7 @@ export * from '@taiga-ui/layout/components/block-details'; export * from '@taiga-ui/layout/components/block-status'; export * from '@taiga-ui/layout/components/card'; export * from '@taiga-ui/layout/components/cell'; +export * from '@taiga-ui/layout/components/form'; export * from '@taiga-ui/layout/components/header'; export * from '@taiga-ui/layout/components/navigation'; export * from '@taiga-ui/layout/components/search'; diff --git a/projects/layout/components/navigation/main.style.less b/projects/layout/components/navigation/main.style.less index 8902ef048f1b..0f2a8532a0a3 100644 --- a/projects/layout/components/navigation/main.style.less +++ b/projects/layout/components/navigation/main.style.less @@ -5,6 +5,7 @@ main[tuiNavigationMain] { display: grid; grid-template-columns: repeat(12, minmax(0, 8.5625rem)); + align-items: start; gap: 0 1rem; justify-content: center; flex: 1; From 9a578723c6921cd2caa7bd014463a80a3b5c22fe Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 6 Dec 2024 16:54:30 +0300 Subject: [PATCH 2/3] chore: fix --- .../layout/components/form/form.directive.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/projects/layout/components/form/form.directive.ts b/projects/layout/components/form/form.directive.ts index ffdc3638c852..d8a151e29fdc 100644 --- a/projects/layout/components/form/form.directive.ts +++ b/projects/layout/components/form/form.directive.ts @@ -8,16 +8,14 @@ import { type Provider, ViewEncapsulation, } from '@angular/core'; -import type {TuiStringHandler} from '@taiga-ui/cdk'; +import type {TuiStringHandler} from '@taiga-ui/cdk/types'; import {tuiWithStyles} from '@taiga-ui/cdk/utils/miscellaneous'; -import { - tuiButtonOptionsProvider, - tuiNotificationOptionsProvider, - type TuiSizeL, - type TuiSizeS, - TuiTextfieldOptionsDirective, -} from '@taiga-ui/core'; -import {TUI_SEGMENTED_OPTIONS, TUI_SWITCH_OPTIONS} from '@taiga-ui/kit'; +import {tuiButtonOptionsProvider} from '@taiga-ui/core/components/button'; +import {tuiNotificationOptionsProvider} from '@taiga-ui/core/components/notification'; +import {TuiTextfieldOptionsDirective} from '@taiga-ui/core/components/textfield'; +import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core/types'; +import {TUI_SEGMENTED_OPTIONS} from '@taiga-ui/kit/components/segmented'; +import {TUI_SWITCH_OPTIONS} from '@taiga-ui/kit/components/switch'; import {TUI_HEADER_OPTIONS} from '@taiga-ui/layout/components/header'; const HEADER_SIZE: Record = { From 1799941421d2fed284c588f59242d1c5a399e49d Mon Sep 17 00:00:00 2001 From: taiga-family-bot Date: Fri, 6 Dec 2024 14:01:53 +0000 Subject: [PATCH 3/3] chore: apply changes after linting [bot] --- projects/cdk/utils/di/create-options.ts | 2 +- .../utils/miscellaneous/provide-options.ts | 2 +- .../core/components/button/button.options.ts | 2 +- .../notification/notification.options.ts | 2 +- .../components/form/examples/1/index.ts | 18 ++++++------- .../components/navigation/examples/1/index.ts | 4 +-- .../segmented/segmented.component.ts | 4 +-- .../layout/components/form/form.directive.ts | 27 +++++++++---------- .../components/header/header.directive.ts | 2 +- 9 files changed, 31 insertions(+), 32 deletions(-) diff --git a/projects/cdk/utils/di/create-options.ts b/projects/cdk/utils/di/create-options.ts index eb96d6e11f46..86525cefc7d9 100644 --- a/projects/cdk/utils/di/create-options.ts +++ b/projects/cdk/utils/di/create-options.ts @@ -5,7 +5,7 @@ export function tuiCreateOptions( defaults: T, ): [ token: InjectionToken, - provider: (item: ProviderToken> | Partial) => FactoryProvider, + provider: (item: Partial | ProviderToken>) => FactoryProvider, ] { const token = tuiCreateToken(defaults); diff --git a/projects/cdk/utils/miscellaneous/provide-options.ts b/projects/cdk/utils/miscellaneous/provide-options.ts index 5bbb4b14de5c..32c7d3493c49 100644 --- a/projects/cdk/utils/miscellaneous/provide-options.ts +++ b/projects/cdk/utils/miscellaneous/provide-options.ts @@ -3,7 +3,7 @@ import {inject} from '@angular/core'; export function tuiProvideOptions( provide: InjectionToken, - options: ProviderToken> | Partial, + options: Partial | ProviderToken>, fallback: T, ): FactoryProvider { return { diff --git a/projects/core/components/button/button.options.ts b/projects/core/components/button/button.options.ts index 31b929ab97bf..3a18fa46fd19 100644 --- a/projects/core/components/button/button.options.ts +++ b/projects/core/components/button/button.options.ts @@ -1,4 +1,4 @@ -import {tuiCreateOptions} from '@taiga-ui/cdk'; +import {tuiCreateOptions} from '@taiga-ui/cdk/utils/di'; import type {TuiAppearanceOptions} from '@taiga-ui/core/directives/appearance'; import type {TuiSizeXL, TuiSizeXS} from '@taiga-ui/core/types'; diff --git a/projects/core/components/notification/notification.options.ts b/projects/core/components/notification/notification.options.ts index 810167730129..53c94c4add50 100644 --- a/projects/core/components/notification/notification.options.ts +++ b/projects/core/components/notification/notification.options.ts @@ -1,5 +1,5 @@ -import {tuiCreateOptions} from '@taiga-ui/cdk'; import type {TuiStringHandler} from '@taiga-ui/cdk/types'; +import {tuiCreateOptions} from '@taiga-ui/cdk/utils/di'; import type {TuiAppearanceOptions} from '@taiga-ui/core/directives/appearance'; import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core/types'; diff --git a/projects/demo/src/modules/components/form/examples/1/index.ts b/projects/demo/src/modules/components/form/examples/1/index.ts index dcdf47d39e7b..40db010e17c5 100644 --- a/projects/demo/src/modules/components/form/examples/1/index.ts +++ b/projects/demo/src/modules/components/form/examples/1/index.ts @@ -18,23 +18,23 @@ import {TuiCardLarge, TuiForm, TuiHeader} from '@taiga-ui/layout'; @Component({ standalone: true, imports: [ + AsyncPipe, NgIf, ReactiveFormsModule, - TuiCardLarge, TuiAppearance, + TuiButton, + TuiCardLarge, + TuiError, + TuiFieldErrorPipe, TuiForm, TuiHeader, - TuiTitle, + TuiIcon, + TuiNotification, TuiSegmented, + TuiSwitch, TuiTextfield, - TuiButton, - TuiNotification, - TuiIcon, + TuiTitle, TuiTooltip, - TuiSwitch, - TuiError, - TuiFieldErrorPipe, - AsyncPipe, ], templateUrl: './index.html', encapsulation, diff --git a/projects/demo/src/modules/components/navigation/examples/1/index.ts b/projects/demo/src/modules/components/navigation/examples/1/index.ts index 81d29209a54c..2a1d486ef0fa 100644 --- a/projects/demo/src/modules/components/navigation/examples/1/index.ts +++ b/projects/demo/src/modules/components/navigation/examples/1/index.ts @@ -50,15 +50,15 @@ const ICON = TuiDataListDropdownManager, TuiDropdown, TuiFade, + TuiForm, TuiHeader, TuiIcon, TuiNavigation, TuiRepeatTimes, TuiSwitch, TuiTabs, - TuiTitle, - TuiForm, TuiTextfield, + TuiTitle, ], templateUrl: './index.html', encapsulation, diff --git a/projects/kit/components/segmented/segmented.component.ts b/projects/kit/components/segmented/segmented.component.ts index 85158f5c03e1..d05d8b76f055 100644 --- a/projects/kit/components/segmented/segmented.component.ts +++ b/projects/kit/components/segmented/segmented.component.ts @@ -10,8 +10,8 @@ import { } from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {ResizeObserverService} from '@ng-web-apis/resize-observer'; -import {tuiCreateOptions} from '@taiga-ui/cdk'; import {tuiZonefree} from '@taiga-ui/cdk/observables'; +import {tuiCreateOptions} from '@taiga-ui/cdk/utils/di'; import {tuiInjectElement, tuiIsHTMLElement} from '@taiga-ui/cdk/utils/dom'; import {tuiPx} from '@taiga-ui/cdk/utils/miscellaneous'; import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core/types'; @@ -20,7 +20,7 @@ import {tuiBadgeNotificationOptionsProvider} from '@taiga-ui/kit/components/badg import {TuiSegmentedDirective} from './segmented.directive'; export const [TUI_SEGMENTED_OPTIONS, tuiSegmentedOptionsProvider] = tuiCreateOptions({ - size: 's' as TuiSizeS | TuiSizeL, + size: 's' as TuiSizeL | TuiSizeS, }); @Component({ diff --git a/projects/layout/components/form/form.directive.ts b/projects/layout/components/form/form.directive.ts index d8a151e29fdc..ed777416db99 100644 --- a/projects/layout/components/form/form.directive.ts +++ b/projects/layout/components/form/form.directive.ts @@ -1,11 +1,10 @@ +import type {InjectionToken, Provider} from '@angular/core'; import { ChangeDetectionStrategy, Component, Directive, inject, - type InjectionToken, Input, - type Provider, ViewEncapsulation, } from '@angular/core'; import type {TuiStringHandler} from '@taiga-ui/cdk/types'; @@ -39,10 +38,13 @@ class TuiFormStyles {} @Directive({ standalone: true, selector: '[tuiForm]', - host: { - tuiForm: '', - '[attr.data-size]': 'size', - }, + providers: [ + tuiButtonOptionsProvider(TuiForm), + tuiNotificationOptionsProvider(TuiForm), + projectSize(TUI_HEADER_OPTIONS, (size) => HEADER_SIZE[size]!), + projectSize(TUI_SWITCH_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), + projectSize(TUI_SEGMENTED_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), + ], hostDirectives: [ { directive: TuiTextfieldOptionsDirective, @@ -53,19 +55,16 @@ class TuiFormStyles {} ], }, ], - providers: [ - tuiButtonOptionsProvider(TuiForm), - tuiNotificationOptionsProvider(TuiForm), - projectSize(TUI_HEADER_OPTIONS, (size) => HEADER_SIZE[size]!), - projectSize(TUI_SWITCH_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), - projectSize(TUI_SEGMENTED_OPTIONS, (size) => (size === 'l' ? 'm' : 's')), - ], + host: { + tuiForm: '', + '[attr.data-size]': 'size', + }, }) export class TuiForm { protected readonly nothing = tuiWithStyles(TuiFormStyles); @Input('tuiForm') - size: TuiSizeS | TuiSizeL = 'l'; + public size: TuiSizeL | TuiSizeS = 'l'; } function projectSize( diff --git a/projects/layout/components/header/header.directive.ts b/projects/layout/components/header/header.directive.ts index 7447917bd1fb..a94f8884e4f8 100644 --- a/projects/layout/components/header/header.directive.ts +++ b/projects/layout/components/header/header.directive.ts @@ -6,7 +6,7 @@ import { Input, ViewEncapsulation, } from '@angular/core'; -import {tuiCreateOptions} from '@taiga-ui/cdk'; +import {tuiCreateOptions} from '@taiga-ui/cdk/utils/di'; import {tuiWithStyles} from '@taiga-ui/cdk/utils/miscellaneous'; import {tuiButtonOptionsProvider} from '@taiga-ui/core/components/button'; import type {TuiSizeXXL, TuiSizeXXS} from '@taiga-ui/core/types';