Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(experimental): Textfield add new component #6298

Merged
merged 3 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"retrowave",
"replicants",
"tuiiconbutton",
"hitbox"
"hitbox",
"Textfieldd"
],
"ignoreRegExpList": ["\\(https?://.*?\\)", "\\/{1}.+\\/{1}", "\\%2F.+", "\\%2C.+", "\\ɵ.+", "\\ыва.+"],
"overrides": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {AbstractControl, NG_VALIDATORS, Validator} from '@angular/forms';
],
})
export class TuiNativeValidatorDirective implements Validator {
private readonly el: HTMLInputElement = inject(ElementRef).nativeElement;
private readonly host: HTMLInputElement = inject(ElementRef).nativeElement;
private readonly zone = inject(NgZone);
private control?: AbstractControl;

Expand All @@ -33,4 +33,8 @@ export class TuiNativeValidatorDirective implements Validator {

return null;
}

private get el(): HTMLInputElement {
return this.host.querySelector('input,textarea,select') || this.host;
}
}
17 changes: 15 additions & 2 deletions projects/cdk/services/directive-styles.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import {
ComponentFactoryResolver,
createComponent,
EnvironmentInjector,
Inject,
inject,
Injectable,
INJECTOR,
Injector,
Type,
} from '@angular/core';

// TODO: Add cleanup with DestroyRef in Angular 16+ and replace service with just a map from a token
export function tuiWithStyles(component: Type<unknown>): void {
const map = inject(TuiDirectiveStylesService).map;
const environmentInjector = inject(EnvironmentInjector);

if (!map.has(component)) {
map.set(component, createComponent(component, {environmentInjector}));
}
}

/**
* Service to use styles with directives
* @deprecated use {@link tuiWithStyles} instead
*/
@Injectable({
providedIn: 'root',
})
export class TuiDirectiveStylesService {
private readonly map = new Map<Type<unknown>, unknown>();
readonly map = new Map();

constructor(
@Inject(ComponentFactoryResolver)
Expand Down
16 changes: 10 additions & 6 deletions projects/core/directives/dropdown/dropdown-open.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,18 @@ export class TuiDropdownOpenDirective implements OnChanges {
@HostListener('document:keydown.silent', ['$event'])
onKeydown({key, target, defaultPrevented}: KeyboardEvent): void {
if (
!defaultPrevented &&
tuiIsEditingKey(key) &&
this.editable &&
tuiIsHTMLElement(target) &&
!tuiIsElementEditable(target)
defaultPrevented ||
!tuiIsEditingKey(key) ||
!this.editable ||
!this.focused ||
!tuiIsHTMLElement(target) ||
(tuiIsElementEditable(target) && target !== this.host)
) {
this.host.focus({preventScroll: true});
return;
}

this.update(true);
this.host.focus({preventScroll: true});
}

ngOnChanges(): void {
Expand Down
11 changes: 3 additions & 8 deletions projects/core/directives/hint/hint-describe.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {tuiAsDriver, TuiDriver} from '@taiga-ui/core/abstract';
import {
debounce,
distinctUntilChanged,
fromEvent,
map,
merge,
of,
Expand All @@ -30,13 +31,7 @@ export class TuiHintDescribeDirective extends TuiDriver implements OnChanges {
private readonly id$ = new ReplaySubject(1);

private readonly stream$ = this.id$.pipe(
tuiIfMap(
() =>
tuiTypedFromEvent(this.doc, 'keydown', {
capture: true,
}),
tuiIsPresent,
),
tuiIfMap(() => fromEvent(this.doc, 'keydown', {capture: true}), tuiIsPresent),
switchMap(() =>
this.focused
? of(false)
Expand All @@ -53,7 +48,7 @@ export class TuiHintDescribeDirective extends TuiDriver implements OnChanges {
);

@Input()
tuiHintDescribe: string | '' | null = '';
tuiHintDescribe?: string | null = '';

readonly type = 'hint';

Expand Down
1 change: 1 addition & 0 deletions projects/core/styles/theme/appearance.less
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
@import 'appearance/primary.less';
@import 'appearance/secondary.less';
@import 'appearance/status.less';
@import 'appearance/textfield.less';
35 changes: 35 additions & 0 deletions projects/core/styles/theme/appearance/textfield.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import '../../taiga-ui-local.less';

[tuiAppearance][data-appearance='textfield'] {
--t-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.1);
background: var(--tui-base-01);
color: var(--tui-text-01);
box-shadow: var(--t-shadow);
outline: 1px solid var(--tui-base-03);
outline-offset: -1px;

&:valid[data-invalid='true'],
&:invalid:not([data-invalid='false']) {
outline-color: var(--tui-negative);
}

&:read-only {
box-shadow: none;
outline-color: var(--tui-base-04) !important;
}
.transition(~'box-shadow, background, outline-color');

.appearance-hover({
--t-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.16);
});

.appearance-focus({
box-shadow: none;
outline: 0.125rem solid var(--tui-primary);
outline-offset: -0.125rem;
});

.appearance-disabled({
box-shadow: none;
});
}
9 changes: 9 additions & 0 deletions projects/demo/src/modules/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,15 @@ export const ROUTES: Routes = [
title: 'Surface',
},
},
{
path: 'experimental/textfield',
loadChildren: async () =>
(await import('../experimental/textfield/textfield.module'))
.ExampleTuiTextfieldModule,
data: {
title: 'Textfield',
},
},
{
path: 'experimental/thumbnail-card',
loadChildren: async () =>
Expand Down
6 changes: 6 additions & 0 deletions projects/demo/src/modules/app/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,12 @@ export const pages: TuiDocPages = [
keywords: 'card, container, wrapper, image, blur, overlay',
route: '/experimental/surface',
},
{
section: 'Experimental',
title: 'Textfield',
keywords: 'form, input, select, textarea, combobox, ввод, форма, поле',
route: '/experimental/textfield',
},
{
section: 'Experimental',
title: 'Title',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
<div [tuiTextfieldCleaner]="true">
<h2 class="tui-heading-5">Label inside</h2>
<tui-textfield
*ngFor="let size of sizes"
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
[tuiTextfieldSize]="size"
>
<label tuiLabel>I am a label</label>
<input
placeholder="I am placeholder"
tuiTextfieldd
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>

<h2 class="tui-heading-5">Label outside</h2>
<label
*ngFor="let size of sizes"
tuiLabel
>
For long labels it is better to use label outside as it can wrap to multiple lines
<tui-textfield
#textfield
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
[tuiTextfieldSize]="size"
>
<input
tuiTextfieldd
[placeholder]="textfield.focused ? 'I am placeholder' : 'I am a short label'"
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>
</label>

<h2 class="tui-heading-5">Disabled</h2>
<tui-textfield
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
>
<label tuiLabel>I am a label</label>
<input
placeholder="I am placeholder"
tuiTextfieldd
[disabled]="true"
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>

<h2 class="tui-heading-5">Read only</h2>
<tui-textfield
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
>
<label tuiLabel>I am a label</label>
<input
placeholder="I am placeholder"
tuiTextfieldd
[readOnly]="true"
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>

<h2 class="tui-heading-5">Invalid</h2>
<tui-textfield
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
>
<label tuiLabel>I am a label</label>
<input
placeholder="I am placeholder"
tuiTextfieldd
[invalid]="true"
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>
<tui-textfield
#textfield
iconLeft="tuiIconSearchLarge"
iconRight="tuiIconSettingsLarge"
[filler]="filler"
>
<input
tuiTextfieldd
[invalid]="true"
[placeholder]="textfield.focused ? 'I am placeholder' : 'I am a short label'"
[(ngModel)]="value"
/>
<tui-icon icon="tuiIconBellLarge"></tui-icon>
<tui-tooltip content="I am a hint"></tui-tooltip>
</tui-textfield>
</div>

<div [tuiTextfieldCleaner]="true">
<h2 class="tui-heading-5">&nbsp;</h2>
<tui-primitive-textfield
*ngFor="let size of sizes"
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[tuiTextfieldFiller]="filler"
[tuiTextfieldSize]="size"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>

<h2 class="tui-heading-5">&nbsp;</h2>
<label
*ngFor="let size of sizes"
tuiLabel
>
For long labels it is better to use label outside as it can wrap to multiple lines
<tui-primitive-textfield
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[tuiTextfieldFiller]="filler"
[tuiTextfieldLabelOutside]="true"
[tuiTextfieldSize]="size"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>
</label>

<h2 class="tui-heading-5">&nbsp;</h2>
<tui-primitive-textfield
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[disabled]="true"
[tuiTextfieldFiller]="filler"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>

<h2 class="tui-heading-5">&nbsp;</h2>
<tui-primitive-textfield
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[readOnly]="true"
[tuiTextfieldFiller]="filler"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>

<h2 class="tui-heading-5">&nbsp;</h2>
<tui-primitive-textfield
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[invalid]="true"
[tuiTextfieldFiller]="filler"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>
<tui-primitive-textfield
tuiHintContent="I am a hint"
tuiTextfieldCustomContent="tuiIconBellLarge"
tuiTextfieldIcon="tuiIconSettingsLarge"
tuiTextfieldIconLeft="tuiIconSearchLarge"
[invalid]="true"
[tuiTextfieldFiller]="filler"
[tuiTextfieldLabelOutside]="true"
[(value)]="value"
>
I am a label
</tui-primitive-textfield>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
:host {
display: flex;
gap: 1rem;
width: 40rem;
}

div {
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
}
Loading
Loading