@brandup/ui-input 1.0.26 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,76 @@
1
- # brandup-ui-input
1
+ # @brandup/ui-input
2
2
 
3
- [![Build Status](https://dev.azure.com/brandup/BrandUp%20Core/_apis/build/status%2FBrandUp%2Fbrandup-ui-kit?branchName=master)]()
3
+ [![Build Status](https://dev.azure.com/brandup/BrandUp%20Core/_apis/build/status%2FBrandUp%2Fbrandup-ui-kit?branchName=main)](https://dev.azure.com/brandup/BrandUp%20Core/_build/latest?definitionId=81&branchName=main)
4
4
 
5
- ## Installation
5
+ Абстрактный базовый класс для компонентов ввода в форме. Обеспечивает единый интерфейс для `HTMLInputElement`, `HTMLTextAreaElement` и `HTMLSelectElement`.
6
6
 
7
- Install NPM package [@brandup/ui](https://www.npmjs.com/package/@brandup/ui-kit).
7
+ ## Установка
8
8
 
9
9
  ```
10
- npm i @brandup/ui-kit@latest
10
+ npm i @brandup/ui-input
11
11
  ```
12
12
 
13
13
  ## InputControl
14
14
 
15
- `InputControl<InputType>` - это базовый абстрактный класс для элементов пользовательсково ввода в форме. Реализуется
16
- относительно `HTMLInputElement`, `HTMLTextAreaElement`, `HTMLSelectElement`.
15
+ `InputControl<T, TEvents>` абстрактный класс, от которого наследуются все компоненты ввода (`TextBox`, `DropDown`). Расширяет `UIElementBound` из `@brandup/ui`.
17
16
 
18
- `InputControl` наследует интерфейс `IInputControl`, чтобы можно было абстрагироваться от реализации.
17
+ ### Свойства
18
+
19
+ | Свойство | Тип | Описание |
20
+ |---|---|---|
21
+ | `form` | `HTMLFormElement \| null` | Форма, к которой привязан элемент |
22
+ | `disabled` | `boolean` | Элемент отключён |
23
+ | `required` | `boolean` | Элемент обязателен для заполнения |
24
+ | `readonly` | `boolean` | Элемент только для чтения (`readonly` или `data-readonly`) |
25
+
26
+ ### Методы
27
+
28
+ | Метод | Описание |
29
+ |---|---|
30
+ | `validate(): boolean` | Проверяет значение через нативный `checkValidity()`. Возвращает `true`, если значение валидно |
31
+ | `focus(): void` | Устанавливает фокус и прокручивает элемент в видимую область |
32
+ | `destroy(): void` | Снимает все обработчики и освобождает ресурсы |
33
+
34
+ ### Поведение
35
+
36
+ - Добавляет CSS-класс `ui-input` на корневой элемент.
37
+ - При инициализации добавляет классы `required`, `readonly`, `disabled` в зависимости от состояния элемента.
38
+ - Перехватывает событие `invalid` нативного поля и делегирует валидацию через `validate()`.
39
+ - Перехватывает `submit` формы: если элемент невалиден — останавливает отправку и вызывает `focus()`.
40
+
41
+ ### Интерфейс IInputControl
42
+
43
+ ```typescript
44
+ import { IInputControl } from "@brandup/ui-input";
45
+
46
+ interface IInputControl {
47
+ get form(): HTMLFormElement | null;
48
+ get disabled(): boolean;
49
+ get required(): boolean;
50
+ get readonly(): boolean;
51
+
52
+ validate(): boolean;
53
+ focus(): void;
54
+ destroy(): void;
55
+ }
56
+ ```
57
+
58
+ ### Создание собственного компонента
59
+
60
+ ```typescript
61
+ import { InputControl } from "@brandup/ui-input";
62
+
63
+ class MyInput extends InputControl<HTMLInputElement> {
64
+ constructor(inputElem: HTMLInputElement) {
65
+ const wrapper = document.createElement("div");
66
+ inputElem.insertAdjacentElement("afterend", wrapper);
67
+ wrapper.appendChild(inputElem);
68
+
69
+ super("MyInput", wrapper, inputElem);
70
+ }
71
+
72
+ getValue(): string {
73
+ return this.__valueElem.value;
74
+ }
75
+ }
76
+ ```
package/package.json CHANGED
@@ -21,12 +21,12 @@
21
21
  "email": "it@brandup.online"
22
22
  },
23
23
  "license": "Apache-2.0",
24
- "version": "1.0.26",
24
+ "version": "1.0.30",
25
25
  "main": "source/index.ts",
26
26
  "types": "source/index.ts",
27
27
  "dependencies": {
28
- "@brandup/ui": "^1.0.44",
29
- "@brandup/ui-kit": "^1.0.26"
28
+ "@brandup/ui": "^2.0.3",
29
+ "@brandup/ui-kit": "^1.0.30"
30
30
  },
31
31
  "files": [
32
32
  "source",
package/source/index.ts CHANGED
@@ -1 +1 @@
1
- export * from "./input"
1
+ export * from "./input";
package/source/input.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { UIElement } from "@brandup/ui";
1
+ import { UIElementBound } from "@brandup/ui";
2
2
  import "./input.less";
3
3
 
4
4
  type InputType = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
@@ -6,37 +6,55 @@ type FormInput<T> = T extends InputType ? T : never;
6
6
 
7
7
  export const INPUT_CSS_CLASS = "ui-input";
8
8
 
9
- export abstract class InputControl<T extends InputType> extends UIElement implements IInputControl {
9
+ export abstract class InputControl<T extends InputType, TEvents = {}>
10
+ extends UIElementBound<TEvents>
11
+ implements IInputControl
12
+ {
10
13
  protected __valueElem: FormInput<T>;
11
14
  protected __submitEvent?: (e: SubmitEvent) => void;
15
+ private __invalidEvent?: (e: Event) => void;
12
16
  private __isValidating?: boolean; // true, когда выполняется checkValidity в validate.
13
17
 
14
- constructor(valueElem: FormInput<T>) {
15
- super();
18
+ constructor(typeName: string, elem: HTMLElement, valueElem: FormInput<T>) {
19
+ super(typeName, elem);
16
20
 
17
21
  this.__valueElem = valueElem;
18
22
 
23
+ // то, что раньше делал _onRenderElement-override; теперь применяем после super, чтобы видеть valueElem
24
+ elem.classList.add(INPUT_CSS_CLASS);
25
+ if (this.required) elem.classList.add("required");
26
+ if (this.readonly) elem.classList.add("readonly");
27
+ if (this.disabled) elem.classList.add("disabled");
28
+
19
29
  this.__initForm();
20
30
  }
21
31
 
22
- get form(): HTMLFormElement | null { return this.__valueElem.form; }
23
- get disabled(): boolean { return this.__valueElem.disabled; }
24
- get required(): boolean { return this.__valueElem.required; }
25
- get readonly(): boolean { return this.__valueElem.hasAttribute("readonly") || this.__valueElem.hasAttribute("data-readonly"); }
32
+ get form(): HTMLFormElement | null {
33
+ return this.__valueElem.form;
34
+ }
35
+ get disabled(): boolean {
36
+ return this.__valueElem.disabled;
37
+ }
38
+ get required(): boolean {
39
+ return this.__valueElem.required;
40
+ }
41
+ get readonly(): boolean {
42
+ return this.__valueElem.hasAttribute("readonly") || this.__valueElem.hasAttribute("data-readonly");
43
+ }
26
44
 
27
45
  private __initForm() {
28
- this.__valueElem.addEventListener("invalid", (e: Event) => {
46
+ this.__invalidEvent = (e: Event) => {
29
47
  e.preventDefault();
30
48
 
31
49
  this.__submitForm();
32
- });
50
+ };
51
+ this.__valueElem.addEventListener("invalid", this.__invalidEvent);
33
52
 
34
53
  this.__submitEvent = (e: SubmitEvent) => {
35
- if ((<HTMLButtonElement>e.submitter).formNoValidate || (<HTMLFormElement>e.target).noValidate)
54
+ if ((e.submitter as HTMLButtonElement | null)?.formNoValidate || (<HTMLFormElement>e.target).noValidate)
36
55
  return; // Не делаем валидацию, если она отключена в форме или в инициаторе события submit
37
56
 
38
- if (this.disabled)
39
- return;
57
+ if (this.disabled) return;
40
58
 
41
59
  if (!this.validate()) {
42
60
  if (!e.defaultPrevented) {
@@ -49,8 +67,7 @@ export abstract class InputControl<T extends InputType> extends UIElement implem
49
67
  }
50
68
  };
51
69
 
52
- if (this.form)
53
- this.form.addEventListener("submit", this.__submitEvent);
70
+ if (this.form) this.form.addEventListener("submit", this.__submitEvent);
54
71
  }
55
72
 
56
73
  protected __submitForm() {
@@ -59,20 +76,8 @@ export abstract class InputControl<T extends InputType> extends UIElement implem
59
76
  form.dispatchEvent(new SubmitEvent("submit", { submitter: form, cancelable: true }));
60
77
  }
61
78
 
62
- protected override _onRenderElement(elem: HTMLElement) {
63
- elem.classList.add(INPUT_CSS_CLASS);
64
-
65
- if (this.required)
66
- elem.classList.add("required");
67
- if (this.readonly)
68
- elem.classList.add("readonly");
69
- if (this.disabled)
70
- elem.classList.add("disabled");
71
- }
72
-
73
79
  validate(): boolean {
74
- if (this.__isValidating)
75
- return true;
80
+ if (this.__isValidating) return true;
76
81
 
77
82
  this.__isValidating = true;
78
83
  const result = this.__valueElem.checkValidity();
@@ -83,12 +88,13 @@ export abstract class InputControl<T extends InputType> extends UIElement implem
83
88
 
84
89
  focus(): void {
85
90
  this.__valueElem.focus();
86
- this.element?.scrollIntoView({ block: "center", inline: "center" });
91
+ this.element.scrollIntoView({ block: "center", inline: "center" });
87
92
  }
88
93
 
89
94
  override destroy() {
90
- if (this.form && this.__submitEvent)
91
- this.form.removeEventListener("submit", this.__submitEvent);
95
+ if (this.form && this.__submitEvent) this.form.removeEventListener("submit", this.__submitEvent);
96
+
97
+ if (this.__invalidEvent) this.__valueElem.removeEventListener("invalid", this.__invalidEvent);
92
98
 
93
99
  super.destroy();
94
100
  }
@@ -103,4 +109,4 @@ export interface IInputControl {
103
109
  validate(): boolean;
104
110
  focus(): void;
105
111
  destroy(): void;
106
- }
112
+ }