@neumorphism-ui/elements 1.0.0-next.0 → 1.0.0-next.2
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 +12 -1
- package/cdn/neu.min.js +184 -51
- package/custom-elements.json +2232 -282
- package/dist/all.d.ts +8 -0
- package/dist/all.js +8 -0
- package/dist/checkbox.d.ts +3 -0
- package/dist/checkbox.js +4 -0
- package/dist/components/button/button.d.ts +0 -2
- package/dist/components/button/button.js +3 -36
- package/dist/components/checkbox/checkbox.d.ts +44 -0
- package/dist/components/checkbox/checkbox.def.d.ts +31 -0
- package/dist/components/checkbox/checkbox.def.js +37 -0
- package/dist/components/checkbox/checkbox.js +40 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +28 -0
- package/dist/components/icon/system-icons.d.ts +10 -0
- package/dist/components/icon/system-icons.js +22 -11
- package/dist/components/number-field/number-field.d.ts +84 -0
- package/dist/components/number-field/number-field.def.d.ts +125 -0
- package/dist/components/number-field/number-field.def.js +62 -0
- package/dist/components/number-field/number-field.js +121 -0
- package/dist/components/number-field/number-field.styles.d.ts +1 -0
- package/dist/components/number-field/number-field.styles.js +15 -0
- package/dist/components/option/option-data.d.ts +8 -0
- package/dist/components/option/option-data.js +12 -0
- package/dist/components/option/option.d.ts +19 -0
- package/dist/components/option/option.def.d.ts +10 -0
- package/dist/components/option/option.def.js +19 -0
- package/dist/components/option/option.js +17 -0
- package/dist/components/option/option.styles.d.ts +2 -0
- package/dist/components/option/option.styles.js +5 -0
- package/dist/components/radio/radio-link.d.ts +11 -0
- package/dist/components/radio/radio-link.js +5 -0
- package/dist/components/radio/radio.d.ts +29 -0
- package/dist/components/radio/radio.def.d.ts +11 -0
- package/dist/components/radio/radio.def.js +25 -0
- package/dist/components/radio/radio.js +43 -0
- package/dist/components/radio/radio.styles.d.ts +1 -0
- package/dist/components/radio/radio.styles.js +21 -0
- package/dist/components/radio-group/radio-group.d.ts +77 -0
- package/dist/components/radio-group/radio-group.def.d.ts +40 -0
- package/dist/components/radio-group/radio-group.def.js +55 -0
- package/dist/components/radio-group/radio-group.js +184 -0
- package/dist/components/radio-group/radio-group.styles.d.ts +1 -0
- package/dist/components/radio-group/radio-group.styles.js +15 -0
- package/dist/components/select/select.d.ts +119 -0
- package/dist/components/select/select.def.d.ts +38 -0
- package/dist/components/select/select.def.js +59 -0
- package/dist/components/select/select.js +385 -0
- package/dist/components/select/select.styles.d.ts +1 -0
- package/dist/components/select/select.styles.js +39 -0
- package/dist/components/shared/checkable.d.ts +45 -0
- package/dist/components/shared/checkable.js +108 -0
- package/dist/components/shared/field.styles.d.ts +1 -0
- package/dist/components/shared/field.styles.js +36 -0
- package/dist/components/shared/press.d.ts +7 -0
- package/dist/components/shared/press.js +42 -0
- package/dist/components/shared/step.d.ts +2 -0
- package/dist/components/shared/step.js +2 -0
- package/dist/components/shared/text-control.d.ts +72 -0
- package/dist/components/shared/text-control.js +191 -0
- package/dist/components/slider/slider.d.ts +69 -0
- package/dist/components/slider/slider.def.d.ts +39 -0
- package/dist/components/slider/slider.def.js +76 -0
- package/dist/components/slider/slider.js +170 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +16 -0
- package/dist/components/switch/switch.d.ts +4 -30
- package/dist/components/switch/switch.js +5 -117
- package/dist/components/text-field/implicit-submit.d.ts +5 -0
- package/dist/components/text-field/implicit-submit.js +8 -3
- package/dist/components/text-field/text-field.d.ts +9 -36
- package/dist/components/text-field/text-field.js +21 -178
- package/dist/components/text-field/text-field.styles.d.ts +1 -1
- package/dist/components/text-field/text-field.styles.js +2 -35
- package/dist/components/textarea/textarea.d.ts +98 -0
- package/dist/components/textarea/textarea.def.d.ts +139 -0
- package/dist/components/textarea/textarea.def.js +65 -0
- package/dist/components/textarea/textarea.js +101 -0
- package/dist/components/textarea/textarea.styles.d.ts +1 -0
- package/dist/components/textarea/textarea.styles.js +9 -0
- package/dist/core/define.d.ts +16 -1
- package/dist/core/form-control.js +3 -1
- package/dist/core/validation.d.ts +6 -2
- package/dist/core/validation.js +15 -8
- package/dist/number-field.d.ts +3 -0
- package/dist/number-field.js +4 -0
- package/dist/option.d.ts +3 -0
- package/dist/option.js +4 -0
- package/dist/radio-group.d.ts +4 -0
- package/dist/radio-group.js +6 -0
- package/dist/radio.d.ts +3 -0
- package/dist/radio.js +4 -0
- package/dist/select.d.ts +3 -0
- package/dist/select.js +4 -0
- package/dist/slider.d.ts +3 -0
- package/dist/slider.js +4 -0
- package/dist/ssr.d.ts +9 -5
- package/dist/ssr.js +25 -5
- package/dist/textarea.d.ts +3 -0
- package/dist/textarea.js +4 -0
- package/package.json +41 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { hostTheme } from '../../core/styles.js';
|
|
2
|
+
export const sliderStyles = `${hostTheme}
|
|
3
|
+
:host { display: block; min-block-size: 1.25rem; touch-action: pan-y; cursor: pointer; user-select: none; }
|
|
4
|
+
:host(:disabled), :host([disabled]) { cursor: not-allowed; }
|
|
5
|
+
.base { position: relative; display: flex; align-items: center; block-size: 1.25rem; padding-inline: 0.625rem; }
|
|
6
|
+
.track { position: relative; flex: 1; block-size: 0.5rem; border-radius: 999px; background: var(--_neu-bg); box-shadow: var(--_neu-inset-sm); }
|
|
7
|
+
.fill { position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: calc(var(--_neu-pos) * 100%); border-radius: inherit; background: var(--_neu-accent-soft); }
|
|
8
|
+
.thumb {
|
|
9
|
+
position: absolute; inset-block-start: 50%; inset-inline-start: calc(var(--_neu-pos) * 100%);
|
|
10
|
+
inline-size: 1.25rem; block-size: 1.25rem; margin-block-start: -0.625rem; margin-inline-start: -0.625rem;
|
|
11
|
+
border-radius: 50%; background: var(--_neu-bg); box-shadow: var(--_neu-raised-sm);
|
|
12
|
+
transition: box-shadow var(--_neu-dur) ease;
|
|
13
|
+
}
|
|
14
|
+
:host(:focus-visible) .thumb { box-shadow: var(--_neu-raised-sm), var(--_neu-focus); }
|
|
15
|
+
:host(:disabled) .base, :host([disabled]) .base { opacity: 0.55; }
|
|
16
|
+
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class NeuSwitch extends
|
|
1
|
+
import { CheckableElement } from '../shared/checkable.js';
|
|
2
|
+
export declare class NeuSwitch extends CheckableElement {
|
|
3
3
|
static def: import("../../ssr.js").ComponentDef<{
|
|
4
4
|
readonly checked: {
|
|
5
5
|
readonly type: "boolean";
|
|
@@ -28,33 +28,7 @@ export declare class NeuSwitch extends FormControlElement {
|
|
|
28
28
|
readonly reflect: true;
|
|
29
29
|
};
|
|
30
30
|
}>;
|
|
31
|
-
|
|
32
|
-
name: string;
|
|
33
|
-
value: string;
|
|
34
|
-
disabled: boolean;
|
|
35
|
-
required: boolean;
|
|
36
|
-
size: 'sm' | 'md' | 'lg';
|
|
37
|
-
private current;
|
|
38
|
-
private dirty;
|
|
31
|
+
protected readonly anchor = "track";
|
|
39
32
|
constructor();
|
|
40
|
-
|
|
41
|
-
set checked(value: boolean);
|
|
42
|
-
get defaultChecked(): boolean;
|
|
43
|
-
set defaultChecked(value: boolean);
|
|
44
|
-
/**
|
|
45
|
-
* Native checkbox order: flip now, let click listeners see it, then input + change once the
|
|
46
|
-
* click has finished dispatching (within it, unless propagation was stopped before window).
|
|
47
|
-
*/
|
|
48
|
-
private onClick;
|
|
49
|
-
private onKeyDown;
|
|
50
|
-
/** Like a native checkbox, Space toggles on keyup only after its own keydown. */
|
|
51
|
-
private onKeyUp;
|
|
52
|
-
private setCurrent;
|
|
53
|
-
protected willUpdate(changed: ReadonlySet<string>): void;
|
|
54
|
-
protected update(changed: ReadonlySet<string>): void;
|
|
55
|
-
protected formValue(): string | null;
|
|
56
|
-
protected formState(): string;
|
|
57
|
-
protected validate(): void;
|
|
58
|
-
protected resetToDefault(): void;
|
|
59
|
-
protected restoreState(state: string): void;
|
|
33
|
+
protected paint(): void;
|
|
60
34
|
}
|
|
@@ -1,125 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FormControlElement } from '../../core/form-control.js';
|
|
3
|
-
import { requiredCheckboxMessage } from '../../core/validation.js';
|
|
1
|
+
import { CheckableElement } from '../shared/checkable.js';
|
|
4
2
|
import { switchDef } from './switch.def.js';
|
|
5
|
-
export class NeuSwitch extends
|
|
3
|
+
export class NeuSwitch extends CheckableElement {
|
|
6
4
|
static def = switchDef;
|
|
7
|
-
|
|
8
|
-
current;
|
|
9
|
-
dirty = false;
|
|
5
|
+
anchor = 'track';
|
|
10
6
|
constructor() {
|
|
11
7
|
super();
|
|
12
|
-
this.current = this.props.checked === true;
|
|
13
8
|
this.internals.role = 'switch';
|
|
14
|
-
this.addEventListener('click', (event) => this.onClick(event));
|
|
15
|
-
this.addEventListener('keydown', (event) => this.onKeyDown(event));
|
|
16
|
-
this.addEventListener('keyup', (event) => this.onKeyUp(event));
|
|
17
|
-
// Disarm when focus moves away, not when only the window loses focus.
|
|
18
|
-
this.addEventListener('blur', () => {
|
|
19
|
-
if (!isActiveInRoot(this))
|
|
20
|
-
this.toggleState('pressed', false);
|
|
21
|
-
});
|
|
22
|
-
// Focus coming back means Space may have been released while it was away.
|
|
23
|
-
this.addEventListener('focus', () => this.toggleState('pressed', false));
|
|
24
9
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
set checked(value) {
|
|
29
|
-
this.dirty = true;
|
|
30
|
-
this.setCurrent(Boolean(value));
|
|
31
|
-
}
|
|
32
|
-
get defaultChecked() {
|
|
33
|
-
return this.props.checked === true;
|
|
34
|
-
}
|
|
35
|
-
set defaultChecked(value) {
|
|
36
|
-
this.toggleAttribute('checked', Boolean(value));
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Native checkbox order: flip now, let click listeners see it, then input + change once the
|
|
40
|
-
* click has finished dispatching (within it, unless propagation was stopped before window).
|
|
41
|
-
*/
|
|
42
|
-
onClick(event) {
|
|
43
|
-
const before = this.current;
|
|
44
|
-
this.dirty = true;
|
|
45
|
-
this.setCurrent(!before);
|
|
46
|
-
afterDispatch(event, () => {
|
|
47
|
-
if (event.defaultPrevented) {
|
|
48
|
-
this.setCurrent(before);
|
|
49
|
-
}
|
|
50
|
-
else if (this.isConnected && this.current === !before) {
|
|
51
|
-
this.markInteracted();
|
|
52
|
-
dispatch(this, 'input');
|
|
53
|
-
dispatch(this, 'change');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
onKeyDown(event) {
|
|
58
|
-
if (event.key === ' ') {
|
|
59
|
-
if (event.defaultPrevented)
|
|
60
|
-
return;
|
|
61
|
-
// Cancelled at once: deferred, an ancestor that stops propagation would let the page scroll.
|
|
62
|
-
// An ancestor claims Space in the capture phase, or by cancelling the keyup.
|
|
63
|
-
event.preventDefault();
|
|
64
|
-
this.toggleState('pressed', true);
|
|
65
|
-
}
|
|
66
|
-
else if (event.key === 'Enter' && !event.repeat) {
|
|
67
|
-
// Not cancelled here, so a handler further up can still cancel the toggle.
|
|
68
|
-
afterDispatch(event, () => {
|
|
69
|
-
if (!event.defaultPrevented)
|
|
70
|
-
this.click();
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/** Like a native checkbox, Space toggles on keyup only after its own keydown. */
|
|
75
|
-
onKeyUp(event) {
|
|
76
|
-
if (event.key !== ' ' || !this.internals.states.has('pressed'))
|
|
77
|
-
return;
|
|
78
|
-
this.toggleState('pressed', false);
|
|
79
|
-
afterDispatch(event, () => {
|
|
80
|
-
if (!event.defaultPrevented)
|
|
81
|
-
this.click();
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
setCurrent(value) {
|
|
85
|
-
this.current = value;
|
|
86
|
-
this.toggleState('checked', value);
|
|
87
|
-
this.internals.ariaChecked = value ? 'true' : 'false';
|
|
88
|
-
this.ref('track')?.classList.toggle('on', value);
|
|
89
|
-
this.commit();
|
|
90
|
-
}
|
|
91
|
-
willUpdate(changed) {
|
|
92
|
-
if (changed.has('checked') && !this.dirty)
|
|
93
|
-
this.current = this.props.checked === true;
|
|
94
|
-
}
|
|
95
|
-
update(changed) {
|
|
96
|
-
if (changed.has('disabled'))
|
|
97
|
-
this.syncDisabledState();
|
|
98
|
-
if (changed.has('checked'))
|
|
99
|
-
this.setCurrent(this.current);
|
|
100
|
-
else if (changed.has('required') || changed.has('value'))
|
|
101
|
-
this.commit();
|
|
102
|
-
}
|
|
103
|
-
formValue() {
|
|
104
|
-
return this.current ? String(this.props.value) : null;
|
|
105
|
-
}
|
|
106
|
-
formState() {
|
|
107
|
-
return this.current ? 'checked' : 'unchecked';
|
|
108
|
-
}
|
|
109
|
-
validate() {
|
|
110
|
-
if (this.props.required === true && !this.current) {
|
|
111
|
-
this.internals.setValidity({ valueMissing: true }, requiredCheckboxMessage(), this.ref('track') ?? undefined);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
this.internals.setValidity({});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
resetToDefault() {
|
|
118
|
-
this.dirty = false;
|
|
119
|
-
this.setCurrent(this.props.checked === true);
|
|
120
|
-
}
|
|
121
|
-
restoreState(state) {
|
|
122
|
-
this.dirty = true;
|
|
123
|
-
this.setCurrent(state === 'checked');
|
|
10
|
+
paint() {
|
|
11
|
+
this.ref('track')?.classList.toggle('on', this.checked);
|
|
124
12
|
}
|
|
125
13
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static brand of the custom elements that block implicit submission like a text input. A
|
|
3
|
+
* subclass inherits it, so it counts under any tag name.
|
|
4
|
+
*/
|
|
5
|
+
export declare const BLOCKS_IMPLICIT_SUBMIT: unique symbol;
|
|
1
6
|
/**
|
|
2
7
|
* Enter in a text control submits like a native input (HTML §4.10.21.2). With a default button
|
|
3
8
|
* (the form's first submit button, form="id" ones included) it clicks that button unless it is
|
|
@@ -14,6 +14,13 @@ const BLOCKING_TYPES = new Set([
|
|
|
14
14
|
'datetime-local',
|
|
15
15
|
'number',
|
|
16
16
|
]);
|
|
17
|
+
/**
|
|
18
|
+
* Static brand of the custom elements that block implicit submission like a text input. A
|
|
19
|
+
* subclass inherits it, so it counts under any tag name.
|
|
20
|
+
*/
|
|
21
|
+
export const BLOCKS_IMPLICIT_SUBMIT = Symbol.for('neu-ui.blocks-implicit-submit');
|
|
22
|
+
const blocks = (el) => (el instanceof HTMLInputElement && BLOCKING_TYPES.has(el.type)) ||
|
|
23
|
+
el.constructor[BLOCKS_IMPLICIT_SUBMIT] === true;
|
|
17
24
|
/**
|
|
18
25
|
* Enter in a text control submits like a native input (HTML §4.10.21.2). With a default button
|
|
19
26
|
* (the form's first submit button, form="id" ones included) it clicks that button unless it is
|
|
@@ -33,9 +40,7 @@ export function implicitSubmit(form) {
|
|
|
33
40
|
defaultButton.click();
|
|
34
41
|
return;
|
|
35
42
|
}
|
|
36
|
-
|
|
37
|
-
el.localName === 'neu-text-field');
|
|
38
|
-
if (blocking.length !== 1)
|
|
43
|
+
if (elements.filter(blocks).length !== 1)
|
|
39
44
|
return;
|
|
40
45
|
markSubmitAttempt(form);
|
|
41
46
|
form.requestSubmit();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { TextControlElement } from '../shared/text-control.js';
|
|
2
|
+
import { BLOCKS_IMPLICIT_SUBMIT } from './implicit-submit.js';
|
|
3
|
+
export declare class NeuTextField extends TextControlElement {
|
|
3
4
|
static def: import("../../ssr.js").ComponentDef<{
|
|
4
5
|
readonly type: {
|
|
5
6
|
readonly type: "string";
|
|
@@ -70,47 +71,19 @@ export declare class NeuTextField extends FormControlElement {
|
|
|
70
71
|
readonly reflect: true;
|
|
71
72
|
};
|
|
72
73
|
}>;
|
|
74
|
+
static readonly [BLOCKS_IMPLICIT_SUBMIT] = true;
|
|
73
75
|
static extraProperties: string[];
|
|
74
76
|
type: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url';
|
|
75
|
-
name: string;
|
|
76
|
-
label: string;
|
|
77
|
-
placeholder: string;
|
|
78
|
-
helpText: string;
|
|
79
|
-
errorText: string;
|
|
80
|
-
required: boolean;
|
|
81
|
-
disabled: boolean;
|
|
82
|
-
readOnly: boolean;
|
|
83
77
|
minLength: number;
|
|
84
78
|
maxLength: number;
|
|
85
79
|
pattern: string;
|
|
86
|
-
autocomplete: string;
|
|
87
80
|
size: 'sm' | 'md' | 'lg';
|
|
88
|
-
|
|
89
|
-
private
|
|
90
|
-
/** A value set before the shadow root exists. */
|
|
91
|
-
private pendingValue;
|
|
92
|
-
get value(): string;
|
|
93
|
-
set value(next: string);
|
|
94
|
-
get defaultValue(): string;
|
|
95
|
-
set defaultValue(next: string);
|
|
96
|
-
/** Focuses the inner input directly, so the caret stays where it was (no select-all). */
|
|
97
|
-
focus(options?: FocusOptions): void;
|
|
98
|
-
select(): void;
|
|
99
|
-
setSelectionRange(start: number, end: number, direction?: 'forward' | 'backward' | 'none'): void;
|
|
100
|
-
private get p();
|
|
101
|
-
private get input();
|
|
81
|
+
protected readonly commitProps: string[];
|
|
82
|
+
private get tp();
|
|
102
83
|
/** A detached input of this type holding `value`, sanitized the way the browser does it. */
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
private validityProbe;
|
|
106
|
-
protected rendered(): void;
|
|
84
|
+
protected probe(value: string): HTMLInputElement;
|
|
85
|
+
protected controlKeyDown(event: KeyboardEvent): void;
|
|
107
86
|
protected update(changed: ReadonlySet<string>): void;
|
|
108
|
-
/**
|
|
109
|
-
protected willUpdate(changed: ReadonlySet<string>): void;
|
|
110
|
-
protected formValue(): string;
|
|
111
|
-
/** Like a native input, a password or autocomplete=off field is never saved for restore. */
|
|
87
|
+
/** Like a native input, a password field is never saved for restore. */
|
|
112
88
|
protected formState(): string | null;
|
|
113
|
-
protected validate(): void;
|
|
114
|
-
protected resetToDefault(): void;
|
|
115
|
-
protected restoreState(state: string): void;
|
|
116
89
|
}
|
|
@@ -1,207 +1,50 @@
|
|
|
1
|
-
import { afterDispatch,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { implicitSubmit } from './implicit-submit.js';
|
|
1
|
+
import { afterDispatch, setOptionalAttribute } from '../../core/dom.js';
|
|
2
|
+
import { baseCommitProps, TextControlElement } from '../shared/text-control.js';
|
|
3
|
+
import { BLOCKS_IMPLICIT_SUBMIT, implicitSubmit } from './implicit-submit.js';
|
|
5
4
|
import { textFieldDef } from './text-field.def.js';
|
|
6
|
-
|
|
7
|
-
const COMMIT_PROPS = [
|
|
8
|
-
'value',
|
|
9
|
-
'type',
|
|
10
|
-
'required',
|
|
11
|
-
'disabled',
|
|
12
|
-
'readOnly',
|
|
13
|
-
'minLength',
|
|
14
|
-
'maxLength',
|
|
15
|
-
'pattern',
|
|
16
|
-
'errorText',
|
|
17
|
-
'autocomplete',
|
|
18
|
-
];
|
|
19
|
-
function needsCommit(changed) {
|
|
20
|
-
return COMMIT_PROPS.some((key) => changed.has(key));
|
|
21
|
-
}
|
|
22
|
-
export class NeuTextField extends FormControlElement {
|
|
5
|
+
export class NeuTextField extends TextControlElement {
|
|
23
6
|
static def = textFieldDef;
|
|
7
|
+
static [BLOCKS_IMPLICIT_SUBMIT] = true;
|
|
24
8
|
static extraProperties = ['defaultValue'];
|
|
25
9
|
// inputMode and enterKeyHint are inherited from HTMLElement with the right type.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/** A value set before the shadow root exists. */
|
|
29
|
-
pendingValue = null;
|
|
30
|
-
get value() {
|
|
31
|
-
return this.input?.value ?? this.pendingValue ?? this.p.value;
|
|
32
|
-
}
|
|
33
|
-
set value(next) {
|
|
34
|
-
const text = next === null || next === undefined ? '' : String(next);
|
|
35
|
-
this.dirty = true;
|
|
36
|
-
const input = this.input;
|
|
37
|
-
if (input)
|
|
38
|
-
input.value = text;
|
|
39
|
-
else
|
|
40
|
-
this.pendingValue = text;
|
|
41
|
-
this.commit();
|
|
42
|
-
}
|
|
43
|
-
get defaultValue() {
|
|
44
|
-
return this.p.value;
|
|
45
|
-
}
|
|
46
|
-
set defaultValue(next) {
|
|
47
|
-
this.setAttribute('value', String(next));
|
|
48
|
-
}
|
|
49
|
-
/** Focuses the inner input directly, so the caret stays where it was (no select-all). */
|
|
50
|
-
focus(options) {
|
|
51
|
-
const input = this.input;
|
|
52
|
-
if (input)
|
|
53
|
-
input.focus(options);
|
|
54
|
-
else
|
|
55
|
-
super.focus(options);
|
|
56
|
-
}
|
|
57
|
-
select() {
|
|
58
|
-
this.input?.select();
|
|
59
|
-
}
|
|
60
|
-
setSelectionRange(start, end, direction) {
|
|
61
|
-
this.input?.setSelectionRange(start, end, direction);
|
|
62
|
-
}
|
|
63
|
-
get p() {
|
|
10
|
+
commitProps = [...baseCommitProps, 'type', 'pattern'];
|
|
11
|
+
get tp() {
|
|
64
12
|
return this.props;
|
|
65
13
|
}
|
|
66
|
-
get input() {
|
|
67
|
-
return this.ref('input');
|
|
68
|
-
}
|
|
69
14
|
/** A detached input of this type holding `value`, sanitized the way the browser does it. */
|
|
70
15
|
probe(value) {
|
|
71
16
|
const probe = document.createElement('input');
|
|
72
|
-
probe.type = this.
|
|
17
|
+
probe.type = this.tp.type;
|
|
73
18
|
probe.value = value;
|
|
19
|
+
if (this.tp.pattern)
|
|
20
|
+
probe.setAttribute('pattern', this.tp.pattern);
|
|
74
21
|
return probe;
|
|
75
22
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const p = this.p;
|
|
79
|
-
const probe = this.probe(this.value);
|
|
80
|
-
probe.required = p.required;
|
|
81
|
-
probe.readOnly = p.readOnly;
|
|
82
|
-
probe.disabled = this.isDisabled;
|
|
83
|
-
if (p.pattern)
|
|
84
|
-
probe.setAttribute('pattern', p.pattern);
|
|
85
|
-
if (p.minLength >= 0)
|
|
86
|
-
probe.setAttribute('minlength', String(p.minLength));
|
|
87
|
-
if (p.maxLength >= 0)
|
|
88
|
-
probe.setAttribute('maxlength', String(p.maxLength));
|
|
89
|
-
return probe;
|
|
90
|
-
}
|
|
91
|
-
rendered() {
|
|
92
|
-
const input = this.input;
|
|
93
|
-
// A declarative root adopted without its input (cut short by the parser): nothing to wire up.
|
|
94
|
-
if (!input)
|
|
23
|
+
controlKeyDown(event) {
|
|
24
|
+
if (event.key !== 'Enter' || event.isComposing)
|
|
95
25
|
return;
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
input.value = this.pendingValue;
|
|
102
|
-
this.pendingValue = null;
|
|
103
|
-
}
|
|
104
|
-
input.addEventListener('input', () => {
|
|
105
|
-
this.dirty = true;
|
|
106
|
-
this.commit();
|
|
107
|
-
});
|
|
108
|
-
input.addEventListener('change', () => {
|
|
109
|
-
this.markInteracted();
|
|
110
|
-
dispatch(this, 'change');
|
|
111
|
-
});
|
|
112
|
-
input.addEventListener('keydown', (event) => {
|
|
113
|
-
if (event.key !== 'Enter' || event.isComposing)
|
|
114
|
-
return;
|
|
115
|
-
// Like a native input: submit once the keydown has finished dispatching, unless a listener
|
|
116
|
-
// (on the host or further up) cancelled it.
|
|
117
|
-
afterDispatch(event, () => {
|
|
118
|
-
if (!event.defaultPrevented)
|
|
119
|
-
implicitSubmit(this.internals.form);
|
|
120
|
-
});
|
|
26
|
+
// Like a native input: submit once the keydown has finished dispatching, unless a listener
|
|
27
|
+
// (on the host or further up) cancelled it.
|
|
28
|
+
afterDispatch(event, () => {
|
|
29
|
+
if (!event.defaultPrevented)
|
|
30
|
+
implicitSubmit(this.internals.form);
|
|
121
31
|
});
|
|
122
32
|
}
|
|
123
33
|
update(changed) {
|
|
124
|
-
const p = this.
|
|
34
|
+
const p = this.tp;
|
|
125
35
|
const input = this.input;
|
|
126
36
|
if (input) {
|
|
127
37
|
if (changed.has('type') && input.type !== p.type)
|
|
128
38
|
input.type = p.type;
|
|
129
|
-
if (changed.has('value')) {
|
|
130
|
-
if (input.defaultValue !== p.value)
|
|
131
|
-
input.defaultValue = p.value;
|
|
132
|
-
if (!this.dirty)
|
|
133
|
-
input.value = p.value;
|
|
134
|
-
}
|
|
135
|
-
if (changed.has('placeholder'))
|
|
136
|
-
setOptionalAttribute(input, 'placeholder', p.placeholder);
|
|
137
|
-
if (changed.has('required'))
|
|
138
|
-
input.required = p.required;
|
|
139
|
-
if (changed.has('disabled'))
|
|
140
|
-
input.disabled = this.isDisabled;
|
|
141
|
-
if (changed.has('readOnly'))
|
|
142
|
-
input.readOnly = p.readOnly;
|
|
143
|
-
if (changed.has('minLength')) {
|
|
144
|
-
setOptionalAttribute(input, 'minlength', p.minLength >= 0 ? String(p.minLength) : '');
|
|
145
|
-
}
|
|
146
|
-
if (changed.has('maxLength')) {
|
|
147
|
-
setOptionalAttribute(input, 'maxlength', p.maxLength >= 0 ? String(p.maxLength) : '');
|
|
148
|
-
}
|
|
149
39
|
if (changed.has('pattern'))
|
|
150
40
|
setOptionalAttribute(input, 'pattern', p.pattern);
|
|
151
|
-
if (changed.has('autocomplete'))
|
|
152
|
-
setOptionalAttribute(input, 'autocomplete', p.autocomplete);
|
|
153
41
|
if (changed.has('inputMode'))
|
|
154
42
|
setOptionalAttribute(input, 'inputmode', p.inputMode);
|
|
155
|
-
if (changed.has('enterKeyHint'))
|
|
156
|
-
setOptionalAttribute(input, 'enterkeyhint', p.enterKeyHint);
|
|
157
43
|
}
|
|
158
|
-
|
|
159
|
-
setText(this.ref('label-text'), p.label);
|
|
160
|
-
if (changed.has('helpText'))
|
|
161
|
-
setText(this.ref('help-text'), p.helpText);
|
|
162
|
-
if (needsCommit(changed))
|
|
163
|
-
this.commit();
|
|
44
|
+
super.update(changed);
|
|
164
45
|
}
|
|
165
|
-
/**
|
|
166
|
-
willUpdate(changed) {
|
|
167
|
-
if (!this.isConnected && !this.input && needsCommit(changed))
|
|
168
|
-
this.commit();
|
|
169
|
-
}
|
|
170
|
-
formValue() {
|
|
171
|
-
return this.value;
|
|
172
|
-
}
|
|
173
|
-
/** Like a native input, a password or autocomplete=off field is never saved for restore. */
|
|
46
|
+
/** Like a native input, a password field is never saved for restore. */
|
|
174
47
|
formState() {
|
|
175
|
-
|
|
176
|
-
if (p.type === 'password' || p.autocomplete.trim().toLowerCase() === 'off')
|
|
177
|
-
return null;
|
|
178
|
-
return this.value;
|
|
179
|
-
}
|
|
180
|
-
validate() {
|
|
181
|
-
const input = this.input;
|
|
182
|
-
const source = input ?? this.validityProbe();
|
|
183
|
-
// A disabled or readonly input still sets flags such as typeMismatch, but is barred from
|
|
184
|
-
// validation and has no message; setValidity() would throw on flags without a message.
|
|
185
|
-
if (!source.willValidate || source.validity.valid)
|
|
186
|
-
this.internals.setValidity({});
|
|
187
|
-
else {
|
|
188
|
-
this.internals.setValidity(copyValidity(source.validity), source.validationMessage, input ?? undefined);
|
|
189
|
-
}
|
|
190
|
-
setText(this.ref('error-text'), this.p.errorText || input?.validationMessage || '');
|
|
191
|
-
}
|
|
192
|
-
resetToDefault() {
|
|
193
|
-
this.dirty = false;
|
|
194
|
-
this.pendingValue = null;
|
|
195
|
-
const input = this.input;
|
|
196
|
-
if (input)
|
|
197
|
-
input.value = this.p.value;
|
|
198
|
-
}
|
|
199
|
-
restoreState(state) {
|
|
200
|
-
this.dirty = true;
|
|
201
|
-
const input = this.input;
|
|
202
|
-
if (input)
|
|
203
|
-
input.value = state;
|
|
204
|
-
else
|
|
205
|
-
this.pendingValue = state;
|
|
48
|
+
return this.tp.type === 'password' ? null : super.formState();
|
|
206
49
|
}
|
|
207
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const textFieldStyles = "\n:host {\n --_neu-bg: var(--neu-bg, #e0e5ec);\n --_neu-accent-c: var(--neu-accent, #6d5dfc);\n --_neu-d: var(--neu-distance, 6px);\n --_neu-k: var(--neu-intensity, 1);\n --_neu-r: var(--neu-radius, 12px);\n --_neu-danger: var(--neu-danger, #e5484d);\n --_neu-dur: var(--neu-duration, 150ms);\n --_neu-sd: var(--neu-shadow-dark, color-mix(in oklab, var(--_neu-bg), black min(100%, calc(var(--_neu-shade, 18%) * var(--_neu-k)))));\n --_neu-sl: var(--neu-shadow-light, color-mix(in oklab, var(--_neu-bg), white min(100%, calc(var(--_neu-light, 100%) * var(--_neu-k)))));\n --_neu-fg: var(--neu-fg, color-mix(in oklab, var(--_neu-bg), var(--_neu-ink, black) var(--_neu-fg-mix, 32%)));\n --_neu-fg-muted: var(--neu-fg-muted, color-mix(in oklab, var(--_neu-bg), var(--_neu-ink, black) var(--_neu-fg-muted-mix, 18%)));\n --_neu-accent-soft: color-mix(in oklab, var(--_neu-accent-c) 25%, var(--_neu-bg));\n --_neu-focus: 0 0 0 3px color-mix(in oklab, var(--_neu-accent-c) 45%, transparent);\n --_neu-raised: var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) var(--_neu-sd), calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n --_neu-raised-sm: calc(var(--_neu-d) * 0.5) calc(var(--_neu-d) * 0.5) var(--_neu-d) var(--_neu-sd), calc(var(--_neu-d) * -0.5) calc(var(--_neu-d) * -0.5) var(--_neu-d) var(--_neu-sl);\n --_neu-inset: inset var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) var(--_neu-sd), inset calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n --_neu-inset-sm: inset calc(var(--_neu-d) * 0.5) calc(var(--_neu-d) * 0.5) var(--_neu-d) var(--_neu-sd), inset calc(var(--_neu-d) * -0.5) calc(var(--_neu-d) * -0.5) var(--_neu-d) var(--_neu-sl);\n font-family: var(--neu-font);\n color: var(--_neu-fg);\n -webkit-tap-highlight-color: transparent;\n}\n:host(:not(:defined)) { visibility: inherit !important; min-block-size: auto !important; }\n:host([hidden]) { display: none !important; }\n:host(:focus) { outline: none; }\n*, *::before, *::after { box-sizing: border-box; }\n\n:host { display: block; }\n:host(:disabled), :host([disabled]) { cursor: not-allowed; }\n.base { display: flex; flex-direction: column; }\n.label-text, ::slotted([slot='label']) {\n display: block; margin-block-end: 0.375rem;\n font-size: 0.8125rem; font-weight: 600; color: var(--_neu-fg-muted);\n}\n.label-text:empty { display: none; }\n.control {\n display: flex; align-items: center; min-block-size: 2.75rem; padding-inline: 0.875rem;\n border-radius: var(--_neu-r); background: var(--_neu-bg); box-shadow: var(--_neu-inset);\n transition: box-shadow var(--_neu-dur) ease;\n}\n:host([size='sm']) .control { min-block-size: 2.25rem; padding-inline: 0.75rem; box-shadow: var(--_neu-inset-sm); }\n:host([size='lg']) .control { min-block-size: 3.25rem; padding-inline: 1rem; }\n:host(:focus-within) .control { box-shadow: var(--_neu-inset), var(--_neu-focus); }\n:host(:state(user-invalid)) .control {\n box-shadow:\n inset var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) color-mix(in oklab, var(--_neu-danger) 35%, var(--_neu-sd)),\n inset calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n}\n:host(:disabled) .control, :host([disabled]) .control { box-shadow: var(--_neu-inset-sm); opacity: 0.6; }\n.input {\n flex: 1; min-inline-size: 0; align-self: stretch; padding: 0; border: none; outline: none;\n background: transparent; color: inherit; font: inherit; font-size: 0.9375rem;\n}\n.input::placeholder { color: var(--_neu-fg-muted); opacity: 1; }\n.
|
|
1
|
+
export declare const textFieldStyles = "\n:host {\n --_neu-bg: var(--neu-bg, #e0e5ec);\n --_neu-accent-c: var(--neu-accent, #6d5dfc);\n --_neu-d: var(--neu-distance, 6px);\n --_neu-k: var(--neu-intensity, 1);\n --_neu-r: var(--neu-radius, 12px);\n --_neu-danger: var(--neu-danger, #e5484d);\n --_neu-dur: var(--neu-duration, 150ms);\n --_neu-sd: var(--neu-shadow-dark, color-mix(in oklab, var(--_neu-bg), black min(100%, calc(var(--_neu-shade, 18%) * var(--_neu-k)))));\n --_neu-sl: var(--neu-shadow-light, color-mix(in oklab, var(--_neu-bg), white min(100%, calc(var(--_neu-light, 100%) * var(--_neu-k)))));\n --_neu-fg: var(--neu-fg, color-mix(in oklab, var(--_neu-bg), var(--_neu-ink, black) var(--_neu-fg-mix, 32%)));\n --_neu-fg-muted: var(--neu-fg-muted, color-mix(in oklab, var(--_neu-bg), var(--_neu-ink, black) var(--_neu-fg-muted-mix, 18%)));\n --_neu-accent-soft: color-mix(in oklab, var(--_neu-accent-c) 25%, var(--_neu-bg));\n --_neu-focus: 0 0 0 3px color-mix(in oklab, var(--_neu-accent-c) 45%, transparent);\n --_neu-raised: var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) var(--_neu-sd), calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n --_neu-raised-sm: calc(var(--_neu-d) * 0.5) calc(var(--_neu-d) * 0.5) var(--_neu-d) var(--_neu-sd), calc(var(--_neu-d) * -0.5) calc(var(--_neu-d) * -0.5) var(--_neu-d) var(--_neu-sl);\n --_neu-inset: inset var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) var(--_neu-sd), inset calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n --_neu-inset-sm: inset calc(var(--_neu-d) * 0.5) calc(var(--_neu-d) * 0.5) var(--_neu-d) var(--_neu-sd), inset calc(var(--_neu-d) * -0.5) calc(var(--_neu-d) * -0.5) var(--_neu-d) var(--_neu-sl);\n font-family: var(--neu-font);\n color: var(--_neu-fg);\n -webkit-tap-highlight-color: transparent;\n}\n:host(:not(:defined)) { visibility: inherit !important; min-block-size: auto !important; }\n:host([hidden]) { display: none !important; }\n:host(:focus) { outline: none; }\n*, *::before, *::after { box-sizing: border-box; }\n\n:host { display: block; }\n:host(:disabled), :host([disabled]) { cursor: not-allowed; }\n.base { display: flex; flex-direction: column; }\n.label-text, ::slotted([slot='label']) {\n display: block; margin-block-end: 0.375rem;\n font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted);\n}\n.label-text:empty { display: none; }\n.control {\n display: flex; align-items: center; min-block-size: 2.75rem; padding-inline: 0.875rem;\n border-radius: var(--_neu-r); background: var(--_neu-bg); box-shadow: var(--_neu-inset);\n transition: box-shadow var(--_neu-dur) ease;\n}\n:host([size='sm']) .control { min-block-size: 2.25rem; padding-inline: 0.75rem; box-shadow: var(--_neu-inset-sm); }\n:host([size='lg']) .control { min-block-size: 3.25rem; padding-inline: 1rem; }\n:host(:focus-within) .control { box-shadow: var(--_neu-inset), var(--_neu-focus); }\n:host(:state(user-invalid)) .control {\n box-shadow:\n inset var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) color-mix(in oklab, var(--_neu-danger) 35%, var(--_neu-sd)),\n inset calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);\n}\n:host(:disabled) .control, :host([disabled]) .control { box-shadow: var(--_neu-inset-sm); opacity: 0.6; }\n.input {\n flex: 1; min-inline-size: 0; align-self: stretch; padding: 0; border: none; outline: none;\n background: transparent; color: inherit; font: inherit; font-size: 0.9375rem;\n}\n.input::placeholder { color: var(--_neu-fg-muted); opacity: 1; }\n.help { font-size: 0.75rem; color: var(--_neu-fg-muted); }\n.help-text, ::slotted([slot='help-text']) { display: block; margin-block-start: 0.375rem; }\n.help-text:empty { display: none; }\n.error-text { display: none; margin-block-start: 0.375rem; color: var(--_neu-danger); }\n:host(:state(user-invalid)) slot[name='help-text'] { display: none; }\n:host(:state(user-invalid)) .error-text:not(:empty) { display: block; }\n\n.prefix, .suffix { display: inline-flex; }\n::slotted([slot='prefix']) { margin-inline-end: 0.5rem; }\n::slotted([slot='suffix']) { margin-inline-start: 0.5rem; }\n";
|
|
@@ -1,39 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const textFieldStyles = `${
|
|
3
|
-
:host { display: block; }
|
|
4
|
-
:host(:disabled), :host([disabled]) { cursor: not-allowed; }
|
|
5
|
-
.base { display: flex; flex-direction: column; }
|
|
6
|
-
.label-text, ::slotted([slot='label']) {
|
|
7
|
-
display: block; margin-block-end: 0.375rem;
|
|
8
|
-
font-size: 0.8125rem; font-weight: 600; color: var(--_neu-fg-muted);
|
|
9
|
-
}
|
|
10
|
-
.label-text:empty { display: none; }
|
|
11
|
-
.control {
|
|
12
|
-
display: flex; align-items: center; min-block-size: 2.75rem; padding-inline: 0.875rem;
|
|
13
|
-
border-radius: var(--_neu-r); background: var(--_neu-bg); box-shadow: var(--_neu-inset);
|
|
14
|
-
transition: box-shadow var(--_neu-dur) ease;
|
|
15
|
-
}
|
|
16
|
-
:host([size='sm']) .control { min-block-size: 2.25rem; padding-inline: 0.75rem; box-shadow: var(--_neu-inset-sm); }
|
|
17
|
-
:host([size='lg']) .control { min-block-size: 3.25rem; padding-inline: 1rem; }
|
|
18
|
-
:host(:focus-within) .control { box-shadow: var(--_neu-inset), var(--_neu-focus); }
|
|
19
|
-
:host(:state(user-invalid)) .control {
|
|
20
|
-
box-shadow:
|
|
21
|
-
inset var(--_neu-d) var(--_neu-d) calc(var(--_neu-d) * 2) color-mix(in oklab, var(--_neu-danger) 35%, var(--_neu-sd)),
|
|
22
|
-
inset calc(var(--_neu-d) * -1) calc(var(--_neu-d) * -1) calc(var(--_neu-d) * 2) var(--_neu-sl);
|
|
23
|
-
}
|
|
24
|
-
:host(:disabled) .control, :host([disabled]) .control { box-shadow: var(--_neu-inset-sm); opacity: 0.6; }
|
|
25
|
-
.input {
|
|
26
|
-
flex: 1; min-inline-size: 0; align-self: stretch; padding: 0; border: none; outline: none;
|
|
27
|
-
background: transparent; color: inherit; font: inherit; font-size: 0.9375rem;
|
|
28
|
-
}
|
|
29
|
-
.input::placeholder { color: var(--_neu-fg-muted); opacity: 1; }
|
|
1
|
+
import { fieldStyles } from '../shared/field.styles.js';
|
|
2
|
+
export const textFieldStyles = `${fieldStyles}
|
|
30
3
|
.prefix, .suffix { display: inline-flex; }
|
|
31
4
|
::slotted([slot='prefix']) { margin-inline-end: 0.5rem; }
|
|
32
5
|
::slotted([slot='suffix']) { margin-inline-start: 0.5rem; }
|
|
33
|
-
.help { font-size: 0.75rem; color: var(--_neu-fg-muted); }
|
|
34
|
-
.help-text, ::slotted([slot='help-text']) { display: block; margin-block-start: 0.375rem; }
|
|
35
|
-
.help-text:empty { display: none; }
|
|
36
|
-
.error-text { display: none; margin-block-start: 0.375rem; color: var(--_neu-danger); }
|
|
37
|
-
:host(:state(user-invalid)) slot[name='help-text'] { display: none; }
|
|
38
|
-
:host(:state(user-invalid)) .error-text:not(:empty) { display: block; }
|
|
39
6
|
`;
|