@neumorphism-ui/elements 1.0.0-next.1 → 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,98 @@
|
|
|
1
|
+
import { TextControlElement } from '../shared/text-control.js';
|
|
2
|
+
export declare class NeuTextarea extends TextControlElement {
|
|
3
|
+
static def: import("../../ssr.js").ComponentDef<{
|
|
4
|
+
readonly value: {
|
|
5
|
+
readonly type: "string";
|
|
6
|
+
};
|
|
7
|
+
readonly name: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly reflect: true;
|
|
10
|
+
};
|
|
11
|
+
readonly label: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
};
|
|
14
|
+
readonly placeholder: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
};
|
|
17
|
+
readonly helpText: {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
};
|
|
20
|
+
readonly errorText: {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
};
|
|
23
|
+
readonly required: {
|
|
24
|
+
readonly type: "boolean";
|
|
25
|
+
readonly reflect: true;
|
|
26
|
+
};
|
|
27
|
+
readonly disabled: {
|
|
28
|
+
readonly type: "boolean";
|
|
29
|
+
readonly reflect: true;
|
|
30
|
+
};
|
|
31
|
+
readonly readOnly: {
|
|
32
|
+
readonly type: "boolean";
|
|
33
|
+
readonly reflect: true;
|
|
34
|
+
readonly attribute: "readonly";
|
|
35
|
+
};
|
|
36
|
+
readonly minLength: {
|
|
37
|
+
readonly type: "number";
|
|
38
|
+
readonly default: -1;
|
|
39
|
+
readonly attribute: "minlength";
|
|
40
|
+
};
|
|
41
|
+
readonly maxLength: {
|
|
42
|
+
readonly type: "number";
|
|
43
|
+
readonly default: -1;
|
|
44
|
+
readonly attribute: "maxlength";
|
|
45
|
+
};
|
|
46
|
+
readonly autocomplete: {
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
};
|
|
49
|
+
readonly enterKeyHint: {
|
|
50
|
+
readonly type: "string";
|
|
51
|
+
readonly attribute: "enterkeyhint";
|
|
52
|
+
};
|
|
53
|
+
readonly rows: {
|
|
54
|
+
readonly type: "number";
|
|
55
|
+
readonly default: 3;
|
|
56
|
+
};
|
|
57
|
+
readonly resize: {
|
|
58
|
+
readonly type: "string";
|
|
59
|
+
readonly default: "vertical";
|
|
60
|
+
readonly values: readonly ["none", "vertical", "auto"];
|
|
61
|
+
readonly reflect: true;
|
|
62
|
+
};
|
|
63
|
+
readonly size: {
|
|
64
|
+
readonly type: "string";
|
|
65
|
+
readonly default: "md";
|
|
66
|
+
readonly values: readonly ["sm", "md", "lg"];
|
|
67
|
+
readonly reflect: true;
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
70
|
+
static extraProperties: string[];
|
|
71
|
+
minLength: number;
|
|
72
|
+
maxLength: number;
|
|
73
|
+
rows: number;
|
|
74
|
+
resize: 'none' | 'vertical' | 'auto';
|
|
75
|
+
size: 'sm' | 'md' | 'lg';
|
|
76
|
+
protected readonly commitProps: readonly ["value", "required", "disabled", "readOnly", "minLength", "maxLength", "errorText", "autocomplete"];
|
|
77
|
+
private get tp();
|
|
78
|
+
get value(): string;
|
|
79
|
+
set value(next: string);
|
|
80
|
+
protected probe(value: string): HTMLTextAreaElement;
|
|
81
|
+
private sizeWatch;
|
|
82
|
+
private sizeFrame;
|
|
83
|
+
private lastWidth;
|
|
84
|
+
/**
|
|
85
|
+
* Re-fits when the width changes or the box first gets a layout (it was hidden, or server
|
|
86
|
+
* rendered); its own height changes keep the width, so they do not loop.
|
|
87
|
+
*/
|
|
88
|
+
private watchSize;
|
|
89
|
+
private stopWatch;
|
|
90
|
+
/** resize="auto": fit the height to the content, never below the rows height. */
|
|
91
|
+
private grow;
|
|
92
|
+
protected connected(): void;
|
|
93
|
+
protected disconnected(): void;
|
|
94
|
+
protected rendered(): void;
|
|
95
|
+
protected update(changed: ReadonlySet<string>): void;
|
|
96
|
+
protected resetToDefault(): void;
|
|
97
|
+
protected restoreState(state: string): void;
|
|
98
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { InferProps } from '../../core/props.js';
|
|
2
|
+
declare const props: {
|
|
3
|
+
readonly value: {
|
|
4
|
+
readonly type: "string";
|
|
5
|
+
};
|
|
6
|
+
readonly name: {
|
|
7
|
+
readonly type: "string";
|
|
8
|
+
readonly reflect: true;
|
|
9
|
+
};
|
|
10
|
+
readonly label: {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
};
|
|
13
|
+
readonly placeholder: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
};
|
|
16
|
+
readonly helpText: {
|
|
17
|
+
readonly type: "string";
|
|
18
|
+
};
|
|
19
|
+
readonly errorText: {
|
|
20
|
+
readonly type: "string";
|
|
21
|
+
};
|
|
22
|
+
readonly required: {
|
|
23
|
+
readonly type: "boolean";
|
|
24
|
+
readonly reflect: true;
|
|
25
|
+
};
|
|
26
|
+
readonly disabled: {
|
|
27
|
+
readonly type: "boolean";
|
|
28
|
+
readonly reflect: true;
|
|
29
|
+
};
|
|
30
|
+
readonly readOnly: {
|
|
31
|
+
readonly type: "boolean";
|
|
32
|
+
readonly reflect: true;
|
|
33
|
+
readonly attribute: "readonly";
|
|
34
|
+
};
|
|
35
|
+
readonly minLength: {
|
|
36
|
+
readonly type: "number";
|
|
37
|
+
readonly default: -1;
|
|
38
|
+
readonly attribute: "minlength";
|
|
39
|
+
};
|
|
40
|
+
readonly maxLength: {
|
|
41
|
+
readonly type: "number";
|
|
42
|
+
readonly default: -1;
|
|
43
|
+
readonly attribute: "maxlength";
|
|
44
|
+
};
|
|
45
|
+
readonly autocomplete: {
|
|
46
|
+
readonly type: "string";
|
|
47
|
+
};
|
|
48
|
+
readonly enterKeyHint: {
|
|
49
|
+
readonly type: "string";
|
|
50
|
+
readonly attribute: "enterkeyhint";
|
|
51
|
+
};
|
|
52
|
+
readonly rows: {
|
|
53
|
+
readonly type: "number";
|
|
54
|
+
readonly default: 3;
|
|
55
|
+
};
|
|
56
|
+
readonly resize: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly default: "vertical";
|
|
59
|
+
readonly values: readonly ["none", "vertical", "auto"];
|
|
60
|
+
readonly reflect: true;
|
|
61
|
+
};
|
|
62
|
+
readonly size: {
|
|
63
|
+
readonly type: "string";
|
|
64
|
+
readonly default: "md";
|
|
65
|
+
readonly values: readonly ["sm", "md", "lg"];
|
|
66
|
+
readonly reflect: true;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export type TextareaProps = InferProps<typeof props>;
|
|
70
|
+
/** A row count the native textarea would also accept; anything else means the default of 3. */
|
|
71
|
+
export declare function rowsOf(rows: number): number;
|
|
72
|
+
export declare const textareaDef: import("../../core/define.js").ComponentDef<{
|
|
73
|
+
readonly value: {
|
|
74
|
+
readonly type: "string";
|
|
75
|
+
};
|
|
76
|
+
readonly name: {
|
|
77
|
+
readonly type: "string";
|
|
78
|
+
readonly reflect: true;
|
|
79
|
+
};
|
|
80
|
+
readonly label: {
|
|
81
|
+
readonly type: "string";
|
|
82
|
+
};
|
|
83
|
+
readonly placeholder: {
|
|
84
|
+
readonly type: "string";
|
|
85
|
+
};
|
|
86
|
+
readonly helpText: {
|
|
87
|
+
readonly type: "string";
|
|
88
|
+
};
|
|
89
|
+
readonly errorText: {
|
|
90
|
+
readonly type: "string";
|
|
91
|
+
};
|
|
92
|
+
readonly required: {
|
|
93
|
+
readonly type: "boolean";
|
|
94
|
+
readonly reflect: true;
|
|
95
|
+
};
|
|
96
|
+
readonly disabled: {
|
|
97
|
+
readonly type: "boolean";
|
|
98
|
+
readonly reflect: true;
|
|
99
|
+
};
|
|
100
|
+
readonly readOnly: {
|
|
101
|
+
readonly type: "boolean";
|
|
102
|
+
readonly reflect: true;
|
|
103
|
+
readonly attribute: "readonly";
|
|
104
|
+
};
|
|
105
|
+
readonly minLength: {
|
|
106
|
+
readonly type: "number";
|
|
107
|
+
readonly default: -1;
|
|
108
|
+
readonly attribute: "minlength";
|
|
109
|
+
};
|
|
110
|
+
readonly maxLength: {
|
|
111
|
+
readonly type: "number";
|
|
112
|
+
readonly default: -1;
|
|
113
|
+
readonly attribute: "maxlength";
|
|
114
|
+
};
|
|
115
|
+
readonly autocomplete: {
|
|
116
|
+
readonly type: "string";
|
|
117
|
+
};
|
|
118
|
+
readonly enterKeyHint: {
|
|
119
|
+
readonly type: "string";
|
|
120
|
+
readonly attribute: "enterkeyhint";
|
|
121
|
+
};
|
|
122
|
+
readonly rows: {
|
|
123
|
+
readonly type: "number";
|
|
124
|
+
readonly default: 3;
|
|
125
|
+
};
|
|
126
|
+
readonly resize: {
|
|
127
|
+
readonly type: "string";
|
|
128
|
+
readonly default: "vertical";
|
|
129
|
+
readonly values: readonly ["none", "vertical", "auto"];
|
|
130
|
+
readonly reflect: true;
|
|
131
|
+
};
|
|
132
|
+
readonly size: {
|
|
133
|
+
readonly type: "string";
|
|
134
|
+
readonly default: "md";
|
|
135
|
+
readonly values: readonly ["sm", "md", "lg"];
|
|
136
|
+
readonly reflect: true;
|
|
137
|
+
};
|
|
138
|
+
}>;
|
|
139
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { defineComponent } from '../../core/define.js';
|
|
2
|
+
import { html } from '../../core/html.js';
|
|
3
|
+
import { themeCssProperties } from '../../core/styles.js';
|
|
4
|
+
import { textareaStyles } from './textarea.styles.js';
|
|
5
|
+
const props = {
|
|
6
|
+
value: { type: 'string' },
|
|
7
|
+
name: { type: 'string', reflect: true },
|
|
8
|
+
label: { type: 'string' },
|
|
9
|
+
placeholder: { type: 'string' },
|
|
10
|
+
helpText: { type: 'string' },
|
|
11
|
+
errorText: { type: 'string' },
|
|
12
|
+
required: { type: 'boolean', reflect: true },
|
|
13
|
+
disabled: { type: 'boolean', reflect: true },
|
|
14
|
+
readOnly: { type: 'boolean', reflect: true, attribute: 'readonly' },
|
|
15
|
+
minLength: { type: 'number', default: -1, attribute: 'minlength' },
|
|
16
|
+
maxLength: { type: 'number', default: -1, attribute: 'maxlength' },
|
|
17
|
+
autocomplete: { type: 'string' },
|
|
18
|
+
enterKeyHint: { type: 'string', attribute: 'enterkeyhint' },
|
|
19
|
+
rows: { type: 'number', default: 3 },
|
|
20
|
+
resize: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
default: 'vertical',
|
|
23
|
+
values: ['none', 'vertical', 'auto'],
|
|
24
|
+
reflect: true,
|
|
25
|
+
},
|
|
26
|
+
size: { type: 'string', default: 'md', values: ['sm', 'md', 'lg'], reflect: true },
|
|
27
|
+
};
|
|
28
|
+
/** A row count the native textarea would also accept; anything else means the default of 3. */
|
|
29
|
+
export function rowsOf(rows) {
|
|
30
|
+
return Number.isInteger(rows) && rows >= 1 ? rows : 3;
|
|
31
|
+
}
|
|
32
|
+
/** Attributes of the inner <textarea> that only appear when set. */
|
|
33
|
+
function areaAttributes(p) {
|
|
34
|
+
return html `${p.placeholder ? html ` placeholder="${p.placeholder}"` : ''}${p.required ? html ` required` : ''}${p.disabled ? html ` disabled` : ''}${p.readOnly ? html ` readonly` : ''}${p.minLength >= 0 ? html ` minlength="${p.minLength}"` : ''}${p.maxLength >= 0 ? html ` maxlength="${p.maxLength}"` : ''}${p.autocomplete ? html ` autocomplete="${p.autocomplete}"` : ''}${p.enterKeyHint ? html ` enterkeyhint="${p.enterKeyHint}"` : ''}`;
|
|
35
|
+
}
|
|
36
|
+
export const textareaDef = defineComponent({
|
|
37
|
+
tag: 'neu-textarea',
|
|
38
|
+
props,
|
|
39
|
+
delegatesFocus: true,
|
|
40
|
+
form: { valueProp: 'value' },
|
|
41
|
+
styles: textareaStyles,
|
|
42
|
+
// The newline after the opening tag is dropped by the HTML parser, so a value that itself starts
|
|
43
|
+
// with a newline keeps it.
|
|
44
|
+
template: (p) => html `<div part="base" class="base"><label part="label" class="label" for="input"><slot name="label"><span class="label-text" data-ref="label-text">${p.label}</span></slot></label><div part="control" class="control"><textarea part="textarea" class="input" id="input" data-ref="input" rows="${rowsOf(p.rows)}" aria-describedby="help"${areaAttributes(p)}>
|
|
45
|
+
${p.value}</textarea></div><div part="help-text" class="help" id="help"><slot name="help-text"><span class="help-text" data-ref="help-text">${p.helpText}</span></slot><span class="error-text" data-ref="error-text">${p.errorText}</span></div></div>`,
|
|
46
|
+
docs: {
|
|
47
|
+
summary: 'A multi-line text input with an inset field. The value attribute is the default value; resize="auto" grows the field with its content.',
|
|
48
|
+
events: [
|
|
49
|
+
{ name: 'input', description: 'Fired on every edit (the native input event, composed).' },
|
|
50
|
+
{ name: 'change', description: 'Fired when an edit is committed, usually on blur.' },
|
|
51
|
+
],
|
|
52
|
+
slots: [
|
|
53
|
+
{ name: 'label', description: 'Rich label; replaces the label attribute.' },
|
|
54
|
+
{ name: 'help-text', description: 'Rich help text; replaces the help-text attribute.' },
|
|
55
|
+
],
|
|
56
|
+
parts: [
|
|
57
|
+
{ name: 'base', description: 'Outer wrapper.' },
|
|
58
|
+
{ name: 'label', description: 'The <label> element.' },
|
|
59
|
+
{ name: 'control', description: 'The inset field around the textarea.' },
|
|
60
|
+
{ name: 'textarea', description: 'The native <textarea>.' },
|
|
61
|
+
{ name: 'help-text', description: 'Help or error text area.' },
|
|
62
|
+
],
|
|
63
|
+
cssProperties: themeCssProperties,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { baseCommitProps, TextControlElement } from '../shared/text-control.js';
|
|
2
|
+
import { rowsOf, textareaDef } from './textarea.def.js';
|
|
3
|
+
export class NeuTextarea extends TextControlElement {
|
|
4
|
+
static def = textareaDef;
|
|
5
|
+
static extraProperties = ['defaultValue'];
|
|
6
|
+
// enterKeyHint is inherited from HTMLElement with the right type.
|
|
7
|
+
commitProps = baseCommitProps;
|
|
8
|
+
get tp() {
|
|
9
|
+
return this.props;
|
|
10
|
+
}
|
|
11
|
+
// An accessor override must redefine both halves; a script-set value needs a fresh height.
|
|
12
|
+
get value() {
|
|
13
|
+
return super.value;
|
|
14
|
+
}
|
|
15
|
+
set value(next) {
|
|
16
|
+
super.value = next;
|
|
17
|
+
this.grow();
|
|
18
|
+
}
|
|
19
|
+
probe(value) {
|
|
20
|
+
const probe = document.createElement('textarea');
|
|
21
|
+
probe.value = value;
|
|
22
|
+
return probe;
|
|
23
|
+
}
|
|
24
|
+
sizeWatch;
|
|
25
|
+
sizeFrame = 0;
|
|
26
|
+
lastWidth = -1;
|
|
27
|
+
/**
|
|
28
|
+
* Re-fits when the width changes or the box first gets a layout (it was hidden, or server
|
|
29
|
+
* rendered); its own height changes keep the width, so they do not loop.
|
|
30
|
+
*/
|
|
31
|
+
watchSize(area, on) {
|
|
32
|
+
if (!on) {
|
|
33
|
+
this.stopWatch();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (this.sizeWatch)
|
|
37
|
+
return;
|
|
38
|
+
this.lastWidth = -1;
|
|
39
|
+
this.sizeWatch = new ResizeObserver(([entry]) => {
|
|
40
|
+
const width = entry?.contentRect.width ?? 0;
|
|
41
|
+
if (width === this.lastWidth)
|
|
42
|
+
return;
|
|
43
|
+
this.lastWidth = width;
|
|
44
|
+
// Next frame: resizing an observed box inside its own callback reports a
|
|
45
|
+
// "ResizeObserver loop" error on window.
|
|
46
|
+
cancelAnimationFrame(this.sizeFrame);
|
|
47
|
+
this.sizeFrame = requestAnimationFrame(() => this.grow());
|
|
48
|
+
});
|
|
49
|
+
this.sizeWatch.observe(area);
|
|
50
|
+
}
|
|
51
|
+
stopWatch() {
|
|
52
|
+
cancelAnimationFrame(this.sizeFrame);
|
|
53
|
+
this.sizeWatch?.disconnect();
|
|
54
|
+
this.sizeWatch = undefined;
|
|
55
|
+
}
|
|
56
|
+
/** resize="auto": fit the height to the content, never below the rows height. */
|
|
57
|
+
grow() {
|
|
58
|
+
const area = this.input;
|
|
59
|
+
if (!area)
|
|
60
|
+
return;
|
|
61
|
+
this.watchSize(area, this.isConnected && this.tp.resize === 'auto');
|
|
62
|
+
if (this.tp.resize !== 'auto') {
|
|
63
|
+
area.style.blockSize = '';
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Back to the rows height first: scrollHeight is never smaller than the box it sits in.
|
|
67
|
+
area.style.blockSize = 'auto';
|
|
68
|
+
const fit = area.scrollHeight;
|
|
69
|
+
// Not laid out (disconnected or hidden): keep the rows height.
|
|
70
|
+
if (fit)
|
|
71
|
+
area.style.blockSize = `${fit}px`;
|
|
72
|
+
}
|
|
73
|
+
connected() {
|
|
74
|
+
super.connected();
|
|
75
|
+
this.grow();
|
|
76
|
+
}
|
|
77
|
+
disconnected() {
|
|
78
|
+
super.disconnected();
|
|
79
|
+
this.stopWatch();
|
|
80
|
+
}
|
|
81
|
+
rendered() {
|
|
82
|
+
super.rendered();
|
|
83
|
+
this.input?.addEventListener('input', () => this.grow());
|
|
84
|
+
}
|
|
85
|
+
update(changed) {
|
|
86
|
+
const area = this.input;
|
|
87
|
+
if (area && changed.has('rows'))
|
|
88
|
+
area.rows = rowsOf(this.tp.rows);
|
|
89
|
+
super.update(changed);
|
|
90
|
+
if (['value', 'rows', 'resize', 'size'].some((key) => changed.has(key)))
|
|
91
|
+
this.grow();
|
|
92
|
+
}
|
|
93
|
+
resetToDefault() {
|
|
94
|
+
super.resetToDefault();
|
|
95
|
+
this.grow();
|
|
96
|
+
}
|
|
97
|
+
restoreState(state) {
|
|
98
|
+
super.restoreState(state);
|
|
99
|
+
this.grow();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const textareaStyles = "\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.control { align-items: stretch; }\n.input {\n display: block; box-sizing: border-box; padding-block: 0.625rem; line-height: 1.4; resize: vertical;\n}\n:host([resize='none']) .input { resize: none; }\n:host([resize='auto']) .input { resize: none; overflow: hidden; }\n";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fieldStyles } from '../shared/field.styles.js';
|
|
2
|
+
export const textareaStyles = `${fieldStyles}
|
|
3
|
+
.control { align-items: stretch; }
|
|
4
|
+
.input {
|
|
5
|
+
display: block; box-sizing: border-box; padding-block: 0.625rem; line-height: 1.4; resize: vertical;
|
|
6
|
+
}
|
|
7
|
+
:host([resize='none']) .input { resize: none; }
|
|
8
|
+
:host([resize='auto']) .input { resize: none; overflow: hidden; }
|
|
9
|
+
`;
|
package/dist/core/define.d.ts
CHANGED
|
@@ -15,14 +15,29 @@ export interface FormDef {
|
|
|
15
15
|
/** The prop whose attribute holds the default value (spec §6.1). */
|
|
16
16
|
readonly valueProp: 'value' | 'checked';
|
|
17
17
|
}
|
|
18
|
+
/** One option child as the server reads it (spec §8.2): its value, label text and disabled state. */
|
|
19
|
+
export interface OptionData {
|
|
20
|
+
readonly value: string;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly disabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** What a server renderer knows beyond the attributes: the option children of a select (§8.3). */
|
|
25
|
+
export interface TemplateContext {
|
|
26
|
+
readonly options?: readonly OptionData[];
|
|
27
|
+
}
|
|
18
28
|
/** The DOM-free half of a component, shared by the element, SSR, React wrappers and docs. */
|
|
19
29
|
export interface ComponentDef<S extends PropsSchema = PropsSchema> {
|
|
20
30
|
readonly tag: string;
|
|
21
31
|
readonly props: S;
|
|
22
32
|
readonly styles: string;
|
|
23
|
-
|
|
33
|
+
/** The client always renders without `ctx`; only a server renderer passes one. */
|
|
34
|
+
template(props: InferProps<S>, ctx?: TemplateContext): RawHtml;
|
|
24
35
|
readonly delegatesFocus?: boolean;
|
|
25
36
|
readonly form?: FormDef;
|
|
37
|
+
/** Properties set only from JS (no attribute), name to TypeScript type, for wrappers and the manifest. Type-only. */
|
|
38
|
+
readonly properties?: Readonly<Record<string, string>>;
|
|
39
|
+
/** Tag of the children a server renderer reads into `TemplateContext.options`. */
|
|
40
|
+
readonly optionsFrom?: string;
|
|
26
41
|
readonly docs: ComponentDocs;
|
|
27
42
|
}
|
|
28
43
|
export declare function defineComponent<const S extends PropsSchema>(def: ComponentDef<S>): ComponentDef<S>;
|
|
@@ -47,7 +47,9 @@ function watchSubmitAttempts() {
|
|
|
47
47
|
}
|
|
48
48
|
}, { capture: true });
|
|
49
49
|
document.addEventListener('keydown', (event) => {
|
|
50
|
-
|
|
50
|
+
// Only a native input submits on Enter (neu text/number fields use their inner input); Enter
|
|
51
|
+
// in a textarea, select or switch is no attempt and must not reveal untouched fields.
|
|
52
|
+
if (event.key !== 'Enter' || !(event.composedPath()[0] instanceof HTMLInputElement))
|
|
51
53
|
return;
|
|
52
54
|
for (const node of event.composedPath()) {
|
|
53
55
|
// Not document or window: their named properties make `.form` a <form id|name="form">.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The browser's own, localised "value missing" message, borrowed once from a required native
|
|
3
|
+
* control: an input of type `kind` ("checkbox", "radio") or, for "select", a select. `fallback`
|
|
4
|
+
* covers a browser that reports none.
|
|
5
|
+
*/
|
|
6
|
+
export declare function requiredMessage(kind: 'checkbox' | 'radio' | 'select', fallback: string): string;
|
|
3
7
|
/** Copies a native input's validity so the host can report the same errors. */
|
|
4
8
|
export declare function copyValidity(validity: ValidityState): ValidityStateFlags;
|
package/dist/core/validation.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
const missing = new Map();
|
|
2
|
+
/**
|
|
3
|
+
* The browser's own, localised "value missing" message, borrowed once from a required native
|
|
4
|
+
* control: an input of type `kind` ("checkbox", "radio") or, for "select", a select. `fallback`
|
|
5
|
+
* covers a browser that reports none.
|
|
6
|
+
*/
|
|
7
|
+
export function requiredMessage(kind, fallback) {
|
|
8
|
+
let message = missing.get(kind);
|
|
9
|
+
if (message === undefined) {
|
|
10
|
+
const probe = kind === 'select'
|
|
11
|
+
? document.createElement('select')
|
|
12
|
+
: Object.assign(document.createElement('input'), { type: kind });
|
|
7
13
|
probe.required = true;
|
|
8
|
-
|
|
14
|
+
message = probe.validationMessage || fallback;
|
|
15
|
+
missing.set(kind, message);
|
|
9
16
|
}
|
|
10
|
-
return
|
|
17
|
+
return message;
|
|
11
18
|
}
|
|
12
19
|
/** Copies a native input's validity so the host can report the same errors. */
|
|
13
20
|
export function copyValidity(validity) {
|
package/dist/option.d.ts
ADDED
package/dist/option.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// A group is unusable without its radios (tokens hide undefined neu-radio), so define both.
|
|
2
|
+
import './radio.js';
|
|
3
|
+
import { NeuRadioGroup } from './components/radio-group/radio-group.js';
|
|
4
|
+
NeuRadioGroup.define();
|
|
5
|
+
export { NeuRadioGroup };
|
|
6
|
+
export { radioGroupDef } from './components/radio-group/radio-group.def.js';
|
package/dist/radio.d.ts
ADDED
package/dist/radio.js
ADDED
package/dist/select.d.ts
ADDED
package/dist/select.js
ADDED
package/dist/slider.d.ts
ADDED
package/dist/slider.js
ADDED
package/dist/ssr.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type { ComponentDef } from './core/define.js';
|
|
2
|
-
export type { ComponentDef, ComponentDocs, DocEntry, FormDef } from './core/define.js';
|
|
1
|
+
import type { ComponentDef, TemplateContext } from './core/define.js';
|
|
2
|
+
export type { ComponentDef, ComponentDocs, DocEntry, FormDef, OptionData, TemplateContext, } from './core/define.js';
|
|
3
3
|
export { formDefaultProp } from './core/define.js';
|
|
4
|
+
export { optionLabel } from './components/option/option-data.js';
|
|
4
5
|
export type { InferProps, PropDecl, PropsSchema, PropValue } from './core/props.js';
|
|
5
6
|
export { attributeName, coerceProperty, parseAttribute, parseProps, serializeAttribute, } from './core/props.js';
|
|
6
7
|
/** Every component definition by tag. DOM-free: safe to import in Node. */
|
|
7
8
|
export declare const definitions: ReadonlyMap<string, ComponentDef>;
|
|
8
9
|
/** Inner HTML of a declarative shadow root: inline styles, then the template. */
|
|
9
|
-
export declare function renderShadowContents(def: ComponentDef, attrs: Readonly<Record<string, string
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
export declare function renderShadowContents(def: ComponentDef, attrs: Readonly<Record<string, string>>, ctx?: TemplateContext): string;
|
|
11
|
+
/**
|
|
12
|
+
* The `<template shadowrootmode>` for one element, or '' for tags this library does not own.
|
|
13
|
+
* `ctx` carries what the attributes cannot, such as the options of a select (spec §8.2).
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderDSD(tag: string, attrs: Readonly<Record<string, string>>, ctx?: TemplateContext): string;
|