@neovici/cosmoz-input 2.0.0-beta.1 → 3.1.0
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/dist/cosmoz-input.d.ts +6 -0
- package/dist/cosmoz-input.js +40 -0
- package/dist/cosmoz-textarea.d.ts +6 -0
- package/{cosmoz-textarea.js → dist/cosmoz-textarea.js} +10 -31
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/render.d.ts +9 -0
- package/dist/render.js +27 -0
- package/dist/styles.d.ts +1 -0
- package/{styles.js → dist/styles.js} +1 -1
- package/dist/use-input.d.ts +10 -0
- package/dist/use-input.js +63 -0
- package/package.json +22 -10
- package/cosmoz-input.js +0 -61
- package/render.js +0 -32
- package/use-input.js +0 -90
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BaseInput } from './use-input';
|
|
2
|
+
import { Render, ObjectFromList } from './render';
|
|
3
|
+
declare const observedAttributes: string[];
|
|
4
|
+
declare type CosmozInput = HTMLElement & ObjectFromList<typeof observedAttributes> & BaseInput & Render;
|
|
5
|
+
export declare const Input: (host: CosmozInput) => import("lit-html").TemplateResult<1>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { html } from 'lit-html'; // eslint-disable-line object-curly-newline
|
|
2
|
+
import { live } from 'lit-html/directives/live.js';
|
|
3
|
+
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
|
4
|
+
import { component } from 'haunted';
|
|
5
|
+
import { useInput, useAllowedPattern } from './use-input';
|
|
6
|
+
import { render, attributes } from './render';
|
|
7
|
+
const observedAttributes = [
|
|
8
|
+
'type',
|
|
9
|
+
'pattern',
|
|
10
|
+
'allowed-pattern',
|
|
11
|
+
'min',
|
|
12
|
+
'max',
|
|
13
|
+
'step',
|
|
14
|
+
...attributes,
|
|
15
|
+
];
|
|
16
|
+
export const Input = (host) => {
|
|
17
|
+
const { type = 'text', pattern, allowedPattern, autocomplete, value, placeholder, readonly, disabled, min, max, step, maxlength, } = host, { onChange, onFocus, onInput } = useInput(host), onBeforeInput = useAllowedPattern(allowedPattern);
|
|
18
|
+
return render(html `<input
|
|
19
|
+
id="input"
|
|
20
|
+
part="input"
|
|
21
|
+
type=${type}
|
|
22
|
+
pattern=${ifDefined(pattern)}
|
|
23
|
+
autocomplete=${ifDefined(autocomplete)}
|
|
24
|
+
placeholder=${placeholder || ' '}
|
|
25
|
+
?readonly=${readonly}
|
|
26
|
+
?aria-disabled=${disabled}
|
|
27
|
+
?disabled=${disabled}
|
|
28
|
+
.value=${live(value ?? '')}
|
|
29
|
+
maxlength=${ifDefined(maxlength)}
|
|
30
|
+
@beforeinput=${onBeforeInput}
|
|
31
|
+
@input=${onInput}
|
|
32
|
+
@change=${onChange}
|
|
33
|
+
@focus=${onFocus}
|
|
34
|
+
@blur=${onFocus}
|
|
35
|
+
min=${ifDefined(min)}
|
|
36
|
+
max=${ifDefined(max)}
|
|
37
|
+
step=${ifDefined(step)}
|
|
38
|
+
/>`, host);
|
|
39
|
+
};
|
|
40
|
+
customElements.define('cosmoz-input', component(Input, { observedAttributes }));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BaseInput } from './use-input';
|
|
2
|
+
import { Render, ObjectFromList } from './render';
|
|
3
|
+
declare const observedAttributes: string[];
|
|
4
|
+
declare type CosmozInput = HTMLElement & ObjectFromList<typeof observedAttributes> & BaseInput & Render;
|
|
5
|
+
export declare const Textarea: (host: CosmozInput) => import("lit-html").TemplateResult<1>;
|
|
6
|
+
export {};
|
|
@@ -1,41 +1,20 @@
|
|
|
1
1
|
import { html } from 'lit-html'; // eslint-disable-line object-curly-newline
|
|
2
2
|
import { live } from 'lit-html/directives/live.js';
|
|
3
3
|
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
|
4
|
-
|
|
5
4
|
import { component } from 'haunted';
|
|
6
5
|
import { useInput, useAutosize } from './use-input';
|
|
7
6
|
import { render, attributes } from './render';
|
|
8
|
-
|
|
7
|
+
const observedAttributes = ['rows', ...attributes];
|
|
9
8
|
export const Textarea = (host) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
placeholder,
|
|
14
|
-
readonly,
|
|
15
|
-
disabled,
|
|
16
|
-
rows,
|
|
17
|
-
cols,
|
|
18
|
-
maxlength,
|
|
19
|
-
} = host,
|
|
20
|
-
{ onChange, onFocus, onInput } = useInput(host);
|
|
21
|
-
|
|
22
|
-
useAutosize(host);
|
|
23
|
-
|
|
24
|
-
return render(
|
|
25
|
-
html`
|
|
9
|
+
const { autocomplete, value, placeholder, readonly, disabled, rows, cols, maxlength, } = host, { onChange, onFocus, onInput } = useInput(host);
|
|
10
|
+
useAutosize(host);
|
|
11
|
+
return render(html `
|
|
26
12
|
<textarea id="input" part="input" style="resize: none"
|
|
27
|
-
autocomplete=${ifDefined(autocomplete)}
|
|
28
|
-
placeholder || ' '
|
|
29
|
-
|
|
13
|
+
autocomplete=${ifDefined(autocomplete)}
|
|
14
|
+
placeholder=${placeholder || ' '}
|
|
15
|
+
rows=${rows ?? 1} cols=${ifDefined(cols)}
|
|
30
16
|
?readonly=${readonly} ?aria-disabled=${disabled} ?disabled=${disabled}
|
|
31
17
|
.value=${live(value ?? '')} maxlength=${ifDefined(maxlength)} @input=${onInput}
|
|
32
|
-
@change=${onChange} @focus=${onFocus} @blur=${onFocus}>`,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
observedAttributes = ['rows', ...attributes];
|
|
37
|
-
|
|
38
|
-
customElements.define(
|
|
39
|
-
'cosmoz-textarea',
|
|
40
|
-
component(Textarea, { observedAttributes })
|
|
41
|
-
);
|
|
18
|
+
@change=${onChange} @focus=${onFocus} @blur=${onFocus}>`, host);
|
|
19
|
+
};
|
|
20
|
+
customElements.define('cosmoz-textarea', component(Textarea, { observedAttributes }));
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare type ObjectFromList<T extends ReadonlyArray<string>, V = string> = {
|
|
2
|
+
[K in (T extends ReadonlyArray<infer U> ? U : never)]: V;
|
|
3
|
+
};
|
|
4
|
+
export interface Render {
|
|
5
|
+
label?: string;
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
errorMessage?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const render: <T>(control: T, { label, invalid, errorMessage }: Render) => import("lit-html").TemplateResult<1>, attributes: string[];
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { html } from 'lit-html';
|
|
2
|
+
import { when } from 'lit-html/directives/when.js';
|
|
3
|
+
import { styles } from './styles';
|
|
4
|
+
export const render = (control, { label, invalid, errorMessage }) => html `
|
|
5
|
+
<style>
|
|
6
|
+
${styles}
|
|
7
|
+
</style>
|
|
8
|
+
<div class="float" part="float"> </div>
|
|
9
|
+
<div class="wrap" part="wrap">
|
|
10
|
+
<slot name="prefix"></slot>
|
|
11
|
+
<div class="control" part="control">
|
|
12
|
+
${control}
|
|
13
|
+
${when(label, () => html `<label for="input" part="label">${label}</label>`)}
|
|
14
|
+
</div>
|
|
15
|
+
<slot name="suffix"></slot>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="line" part="line"></div>
|
|
18
|
+
${when(invalid && errorMessage, () => html `<div class="error" part="error">${errorMessage}</div>`)}
|
|
19
|
+
`, attributes = [
|
|
20
|
+
'autocomplete',
|
|
21
|
+
'readonly',
|
|
22
|
+
'disabled',
|
|
23
|
+
'maxlength',
|
|
24
|
+
'invalid',
|
|
25
|
+
'no-label-float',
|
|
26
|
+
'always-float-label',
|
|
27
|
+
];
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface BaseInput extends HTMLElement {
|
|
2
|
+
value?: string | number;
|
|
3
|
+
maxRows?: number;
|
|
4
|
+
focused?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const useInput: <T extends BaseInput>(host: T) => {
|
|
7
|
+
onChange: (e: Event) => boolean;
|
|
8
|
+
onFocus: (e: FocusEvent) => void;
|
|
9
|
+
onInput: (e: InputEvent) => void;
|
|
10
|
+
}, useAllowedPattern: (allowedPattern: string | RegExp) => (<T extends InputEvent>(e: T) => void) | undefined, autosize: (input: HTMLElement) => void, limit: (input: HTMLElement, maxRows?: number) => void, useAutosize: <T extends BaseInput>(host: T) => void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo } from 'haunted';
|
|
2
|
+
import { useImperativeApi } from '@neovici/cosmoz-utils/hooks/use-imperative-api';
|
|
3
|
+
import { notifyProperty } from '@neovici/cosmoz-utils/hooks/use-notify-property';
|
|
4
|
+
// TODO: use useRef instead of callback with querySelector
|
|
5
|
+
export const useInput = (host) => {
|
|
6
|
+
const root = host.shadowRoot, onChange = useCallback((e) => host.dispatchEvent(new Event(e.type, { bubbles: e.bubbles })), []), onInput = useCallback((e) => notifyProperty(host, 'value', e.target.value), []), onFocus = useCallback((e) => notifyProperty(host, 'focused', e.type === 'focus'), []), focus = useCallback(() => root.querySelector('#input')?.focus(), []), validate = useCallback(() => {
|
|
7
|
+
const valid = root.querySelector('#input')?.checkValidity();
|
|
8
|
+
host.toggleAttribute('invalid', !valid);
|
|
9
|
+
return valid;
|
|
10
|
+
}, []);
|
|
11
|
+
useImperativeApi({ focus, validate }, [focus, validate]);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const onMouseDown = (e) => {
|
|
14
|
+
if (e.defaultPrevented ||
|
|
15
|
+
e.target.matches('input, textarea, label')) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
e.preventDefault(); // don't blur
|
|
19
|
+
if (!host.matches(':focus-within')) {
|
|
20
|
+
// if input not focused
|
|
21
|
+
focus(); // focus input
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
root.addEventListener('mousedown', onMouseDown);
|
|
25
|
+
return () => root.removeEventListener('mousedown', onMouseDown);
|
|
26
|
+
}, [focus]);
|
|
27
|
+
return {
|
|
28
|
+
onChange,
|
|
29
|
+
onFocus,
|
|
30
|
+
onInput,
|
|
31
|
+
};
|
|
32
|
+
}, useAllowedPattern = (allowedPattern) => useMemo(() => {
|
|
33
|
+
if (allowedPattern == null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const regexp = new RegExp(allowedPattern, 'u');
|
|
37
|
+
return (e) => {
|
|
38
|
+
if (!e.defaultPrevented && e.data && !regexp.test(e.data)) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}, [allowedPattern]), autosize = (input) => {
|
|
43
|
+
input.style.height = '';
|
|
44
|
+
input.style.height = `${input.scrollHeight}px`;
|
|
45
|
+
}, limit = (input, maxRows = 0) => {
|
|
46
|
+
if (maxRows > 0) {
|
|
47
|
+
const rows = input.getAttribute('rows') ?? '', height = input.style.height;
|
|
48
|
+
input.style.height = '';
|
|
49
|
+
input.setAttribute('rows', maxRows);
|
|
50
|
+
input.style.maxHeight = input.getBoundingClientRect().height + 'px';
|
|
51
|
+
input.style.height = height;
|
|
52
|
+
input.setAttribute('rows', rows);
|
|
53
|
+
}
|
|
54
|
+
}, useAutosize = (host) => {
|
|
55
|
+
const { value, maxRows } = host, input = useMemo(() => () => host.shadowRoot.querySelector('#input'), []);
|
|
56
|
+
useEffect(() => limit(input(), maxRows), [maxRows, input]);
|
|
57
|
+
useEffect(() => autosize(input()), [input, value]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const el = input(), observer = new ResizeObserver(() => requestAnimationFrame(() => autosize(el)));
|
|
60
|
+
observer.observe(el);
|
|
61
|
+
return () => observer.unobserve(el);
|
|
62
|
+
}, [input]);
|
|
63
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neovici/cosmoz-input",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "A input web component",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lit-html",
|
|
@@ -16,13 +16,16 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"author": "",
|
|
19
|
-
"main": "
|
|
19
|
+
"main": "dist/index.js",
|
|
20
20
|
"directories": {
|
|
21
21
|
"test": "test"
|
|
22
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/"
|
|
25
|
+
],
|
|
23
26
|
"scripts": {
|
|
24
|
-
"lint": "eslint --cache
|
|
25
|
-
"
|
|
27
|
+
"lint": "tsc && eslint --cache .",
|
|
28
|
+
"build": "tsc -p tsconfig.build.json",
|
|
26
29
|
"start": "wds",
|
|
27
30
|
"test": "wtr --coverage",
|
|
28
31
|
"test:watch": "wtr --watch",
|
|
@@ -45,17 +48,26 @@
|
|
|
45
48
|
"publishConfig": {
|
|
46
49
|
"access": "public"
|
|
47
50
|
},
|
|
48
|
-
"files": [
|
|
49
|
-
"*.js",
|
|
50
|
-
"lib/**/*.js"
|
|
51
|
-
],
|
|
52
51
|
"commitlint": {
|
|
53
52
|
"extends": [
|
|
54
53
|
"@commitlint/config-conventional"
|
|
55
|
-
]
|
|
54
|
+
],
|
|
55
|
+
"rules": {
|
|
56
|
+
"body-max-line-length": [
|
|
57
|
+
1,
|
|
58
|
+
"always",
|
|
59
|
+
100
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"exports": {
|
|
64
|
+
".": "./dist/index.js",
|
|
65
|
+
"./use-input": "./dist/use-input.js",
|
|
66
|
+
"./input": "./dist/cosmoz-input.js",
|
|
67
|
+
"./textarea": "./dist/cosmoz-textarea.js"
|
|
56
68
|
},
|
|
57
69
|
"dependencies": {
|
|
58
|
-
"@neovici/cosmoz-utils": "^
|
|
70
|
+
"@neovici/cosmoz-utils": "^5.1.0",
|
|
59
71
|
"haunted": "^5.0.0",
|
|
60
72
|
"lit-html": "^2.0.0"
|
|
61
73
|
},
|
package/cosmoz-input.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { html } from 'lit-html'; // eslint-disable-line object-curly-newline
|
|
2
|
-
import { live } from 'lit-html/directives/live.js';
|
|
3
|
-
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
|
4
|
-
|
|
5
|
-
import { component } from 'haunted';
|
|
6
|
-
import { useInput, useAllowedPattern } from './use-input';
|
|
7
|
-
import { render, attributes } from './render';
|
|
8
|
-
|
|
9
|
-
export const Input = (host) => {
|
|
10
|
-
const {
|
|
11
|
-
type = 'text',
|
|
12
|
-
pattern,
|
|
13
|
-
allowedPattern,
|
|
14
|
-
autocomplete,
|
|
15
|
-
value,
|
|
16
|
-
placeholder,
|
|
17
|
-
readonly,
|
|
18
|
-
disabled,
|
|
19
|
-
min,
|
|
20
|
-
max,
|
|
21
|
-
step,
|
|
22
|
-
maxlength,
|
|
23
|
-
} = host,
|
|
24
|
-
{ onChange, onFocus, onInput } = useInput(host),
|
|
25
|
-
onBeforeInput = useAllowedPattern(allowedPattern);
|
|
26
|
-
return render(
|
|
27
|
-
html`<input
|
|
28
|
-
id="input"
|
|
29
|
-
part="input"
|
|
30
|
-
type=${type}
|
|
31
|
-
pattern=${ifDefined(pattern)}
|
|
32
|
-
autocomplete=${ifDefined(autocomplete)}
|
|
33
|
-
placeholder=${placeholder || ' '}
|
|
34
|
-
?readonly=${readonly}
|
|
35
|
-
?aria-disabled=${disabled}
|
|
36
|
-
?disabled=${disabled}
|
|
37
|
-
.value=${live(value ?? '')}
|
|
38
|
-
maxlength=${ifDefined(maxlength)}
|
|
39
|
-
@beforeinput=${onBeforeInput}
|
|
40
|
-
@input=${onInput}
|
|
41
|
-
@change=${onChange}
|
|
42
|
-
@focus=${onFocus}
|
|
43
|
-
@blur=${onFocus}
|
|
44
|
-
min=${ifDefined(min)}
|
|
45
|
-
max=${ifDefined(max)}
|
|
46
|
-
step=${ifDefined(step)}
|
|
47
|
-
/>`,
|
|
48
|
-
host
|
|
49
|
-
);
|
|
50
|
-
},
|
|
51
|
-
observedAttributes = [
|
|
52
|
-
'type',
|
|
53
|
-
'pattern',
|
|
54
|
-
'allowed-pattern',
|
|
55
|
-
'min',
|
|
56
|
-
'max',
|
|
57
|
-
'step',
|
|
58
|
-
...attributes,
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
customElements.define('cosmoz-input', component(Input, { observedAttributes }));
|
package/render.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { html, nothing } from 'lit-html';
|
|
2
|
-
import { styles } from './styles';
|
|
3
|
-
|
|
4
|
-
export const render = (control, { label, invalid, errorMessage }) => html`
|
|
5
|
-
<style>
|
|
6
|
-
${styles}
|
|
7
|
-
</style>
|
|
8
|
-
<div class="float" part="float"> </div>
|
|
9
|
-
<div class="wrap" part="wrap">
|
|
10
|
-
<slot name="prefix"></slot>
|
|
11
|
-
<div class="control" part="control">
|
|
12
|
-
${control}
|
|
13
|
-
${label
|
|
14
|
-
? html`<label for="input" part="label">${label}</label>`
|
|
15
|
-
: nothing}
|
|
16
|
-
</div>
|
|
17
|
-
<slot name="suffix"></slot>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="line" part="line"></div>
|
|
20
|
-
${invalid && errorMessage
|
|
21
|
-
? html`<div class="error" part="error">${errorMessage}</div>`
|
|
22
|
-
: nothing}
|
|
23
|
-
`,
|
|
24
|
-
attributes = [
|
|
25
|
-
'autocomplete',
|
|
26
|
-
'readonly',
|
|
27
|
-
'disabled',
|
|
28
|
-
'maxlength',
|
|
29
|
-
'invalid',
|
|
30
|
-
'no-label-float',
|
|
31
|
-
'always-float-label',
|
|
32
|
-
];
|
package/use-input.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo } from 'haunted';
|
|
2
|
-
import { useImperativeApi } from '@neovici/cosmoz-utils/lib/hooks/use-imperative-api';
|
|
3
|
-
import { notifyProperty } from '@neovici/cosmoz-utils/lib/hooks/use-notify-property';
|
|
4
|
-
|
|
5
|
-
export const useInput = (host) => {
|
|
6
|
-
const root = host.shadowRoot,
|
|
7
|
-
onChange = useCallback(
|
|
8
|
-
(e) => host.dispatchEvent(new Event(e.type, { bubbles: e.bubbles })),
|
|
9
|
-
[]
|
|
10
|
-
),
|
|
11
|
-
onInput = useCallback(
|
|
12
|
-
(e) => notifyProperty(host, 'value', e.target.value),
|
|
13
|
-
[]
|
|
14
|
-
),
|
|
15
|
-
onFocus = useCallback(
|
|
16
|
-
(e) => notifyProperty(host, 'focused', e.type === 'focus'),
|
|
17
|
-
[]
|
|
18
|
-
),
|
|
19
|
-
focus = useCallback(() => root.querySelector('#input')?.focus(), []),
|
|
20
|
-
validate = useCallback(() => {
|
|
21
|
-
const valid = root.querySelector('#input')?.checkValidity();
|
|
22
|
-
host.toggleAttribute('invalid', !valid);
|
|
23
|
-
return valid;
|
|
24
|
-
}, []);
|
|
25
|
-
|
|
26
|
-
useImperativeApi({ focus, validate }, [focus, validate]);
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
const onMouseDown = (e) => {
|
|
30
|
-
if (e.defaultPrevented || e.target.matches('input, textarea, label')) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
e.preventDefault(); // don't blur
|
|
34
|
-
if (!host.matches(':focus-within')) {
|
|
35
|
-
// if input not focused
|
|
36
|
-
focus(); // focus input
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
root.addEventListener('mousedown', onMouseDown);
|
|
41
|
-
return () => root.removeEventListener('mousedown', onMouseDown);
|
|
42
|
-
}, [focus]);
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
onChange,
|
|
46
|
-
onFocus,
|
|
47
|
-
onInput,
|
|
48
|
-
};
|
|
49
|
-
},
|
|
50
|
-
useAllowedPattern = (allowedPattern) =>
|
|
51
|
-
useMemo(() => {
|
|
52
|
-
if (allowedPattern == null) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const regexp = new RegExp(allowedPattern, 'u');
|
|
56
|
-
return (e) => {
|
|
57
|
-
if (!e.defaultPrevent && e.data && !regexp.test(e.data)) {
|
|
58
|
-
e.preventDefault();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}, [allowedPattern]),
|
|
62
|
-
autosize = (input) => {
|
|
63
|
-
input.style.height = '';
|
|
64
|
-
input.style.height = `${input.scrollHeight}px`;
|
|
65
|
-
},
|
|
66
|
-
limit = (input, maxRows) => {
|
|
67
|
-
if (maxRows > 0) {
|
|
68
|
-
const rows = input.getAttribute('rows'),
|
|
69
|
-
height = input.style.height;
|
|
70
|
-
input.style.height = '';
|
|
71
|
-
input.setAttribute('rows', maxRows);
|
|
72
|
-
input.style.maxHeight = input.getBoundingClientRect().height + 'px';
|
|
73
|
-
input.style.height = height;
|
|
74
|
-
input.setAttribute('rows', rows);
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
useAutosize = (host) => {
|
|
78
|
-
const { value, maxRows } = host,
|
|
79
|
-
input = useMemo(() => () => host.shadowRoot.querySelector('#input'), []);
|
|
80
|
-
useEffect(() => limit(input(), maxRows), [maxRows, input]);
|
|
81
|
-
useEffect(() => autosize(input()), [input, value]);
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
const el = input(),
|
|
84
|
-
observer = new ResizeObserver(() =>
|
|
85
|
-
requestAnimationFrame(() => autosize(el))
|
|
86
|
-
);
|
|
87
|
-
observer.observe(el);
|
|
88
|
-
return () => observer.unobserve(el);
|
|
89
|
-
}, [input]);
|
|
90
|
-
};
|