@neumorphism-ui/elements 1.0.0-next.2 → 1.0.0-next.3
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/cdn/neu.min.js +31 -39
- package/custom-elements.json +495 -248
- package/dist/components/avatar/avatar.d.ts +0 -3
- package/dist/components/avatar/avatar.def.d.ts +0 -3
- package/dist/components/avatar/avatar.def.js +4 -18
- package/dist/components/avatar/avatar.docs.d.ts +2 -0
- package/dist/components/avatar/avatar.docs.js +22 -0
- package/dist/components/avatar/avatar.js +7 -1
- package/dist/components/badge/badge.d.ts +0 -2
- package/dist/components/badge/badge.def.d.ts +0 -2
- package/dist/components/badge/badge.def.js +1 -14
- package/dist/components/badge/badge.docs.d.ts +2 -0
- package/dist/components/badge/badge.docs.js +17 -0
- package/dist/components/button/button.def.d.ts +0 -5
- package/dist/components/button/button.def.js +3 -25
- package/dist/components/button/button.docs.d.ts +2 -0
- package/dist/components/button/button.docs.js +29 -0
- package/dist/components/button-group/button-group.d.ts +0 -2
- package/dist/components/button-group/button-group.def.d.ts +0 -2
- package/dist/components/button-group/button-group.def.js +1 -10
- package/dist/components/button-group/button-group.docs.d.ts +2 -0
- package/dist/components/button-group/button-group.docs.js +13 -0
- package/dist/components/checkbox/checkbox.d.ts +1 -5
- package/dist/components/checkbox/checkbox.def.d.ts +1 -5
- package/dist/components/checkbox/checkbox.def.js +6 -22
- package/dist/components/checkbox/checkbox.docs.d.ts +2 -0
- package/dist/components/checkbox/checkbox.docs.js +27 -0
- package/dist/components/divider/divider.d.ts +0 -1
- package/dist/components/divider/divider.def.d.ts +0 -1
- package/dist/components/divider/divider.def.js +0 -9
- package/dist/components/divider/divider.docs.d.ts +2 -0
- package/dist/components/divider/divider.docs.js +12 -0
- package/dist/components/icon/icon.d.ts +0 -3
- package/dist/components/icon/icon.def.d.ts +0 -3
- package/dist/components/icon/icon.def.js +3 -10
- package/dist/components/icon/icon.docs.d.ts +2 -0
- package/dist/components/icon/icon.docs.js +13 -0
- package/dist/components/icon/system-icons.d.ts +1 -1
- package/dist/components/icon/system-icons.js +3 -3
- package/dist/components/icon-button/icon-button.d.ts +0 -5
- package/dist/components/icon-button/icon-button.def.d.ts +0 -5
- package/dist/components/icon-button/icon-button.def.js +3 -17
- package/dist/components/icon-button/icon-button.docs.d.ts +2 -0
- package/dist/components/icon-button/icon-button.docs.js +26 -0
- package/dist/components/number-field/number-field.d.ts +0 -5
- package/dist/components/number-field/number-field.def.d.ts +0 -10
- package/dist/components/number-field/number-field.def.js +5 -27
- package/dist/components/number-field/number-field.docs.d.ts +2 -0
- package/dist/components/number-field/number-field.docs.js +45 -0
- package/dist/components/number-field/number-field.js +2 -2
- package/dist/components/number-field/number-field.styles.d.ts +1 -1
- package/dist/components/option/option.d.ts +0 -2
- package/dist/components/option/option.def.d.ts +0 -2
- package/dist/components/option/option.def.js +2 -9
- package/dist/components/option/option.docs.d.ts +2 -0
- package/dist/components/option/option.docs.js +12 -0
- package/dist/components/radio/radio.d.ts +0 -2
- package/dist/components/radio/radio.def.d.ts +0 -2
- package/dist/components/radio/radio.def.js +2 -15
- package/dist/components/radio/radio.docs.d.ts +2 -0
- package/dist/components/radio/radio.docs.js +18 -0
- package/dist/components/radio-group/radio-group.d.ts +3 -4
- package/dist/components/radio-group/radio-group.def.d.ts +0 -4
- package/dist/components/radio-group/radio-group.def.js +4 -19
- package/dist/components/radio-group/radio-group.docs.d.ts +2 -0
- package/dist/components/radio-group/radio-group.docs.js +37 -0
- package/dist/components/radio-group/radio-group.js +28 -4
- package/dist/components/radio-group/radio-group.styles.d.ts +1 -1
- package/dist/components/radio-group/radio-group.styles.js +3 -5
- package/dist/components/select/select.d.ts +1 -5
- package/dist/components/select/select.def.d.ts +0 -4
- package/dist/components/select/select.def.js +5 -29
- package/dist/components/select/select.docs.d.ts +2 -0
- package/dist/components/select/select.docs.js +65 -0
- package/dist/components/select/select.js +16 -10
- package/dist/components/select/select.styles.d.ts +1 -1
- package/dist/components/select/select.styles.js +1 -5
- package/dist/components/shared/field.styles.d.ts +1 -1
- package/dist/components/shared/field.styles.js +2 -4
- package/dist/components/slider/slider.d.ts +0 -2
- package/dist/components/slider/slider.def.d.ts +0 -2
- package/dist/components/slider/slider.def.js +2 -24
- package/dist/components/slider/slider.docs.d.ts +2 -0
- package/dist/components/slider/slider.docs.js +45 -0
- package/dist/components/spinner/spinner.d.ts +0 -1
- package/dist/components/spinner/spinner.def.d.ts +0 -1
- package/dist/components/spinner/spinner.def.js +1 -13
- package/dist/components/spinner/spinner.docs.d.ts +2 -0
- package/dist/components/spinner/spinner.docs.js +17 -0
- package/dist/components/surface/surface.d.ts +0 -4
- package/dist/components/surface/surface.def.d.ts +0 -4
- package/dist/components/surface/surface.def.js +3 -12
- package/dist/components/surface/surface.docs.d.ts +2 -0
- package/dist/components/surface/surface.docs.js +15 -0
- package/dist/components/switch/switch.d.ts +0 -5
- package/dist/components/switch/switch.def.d.ts +0 -5
- package/dist/components/switch/switch.def.js +5 -21
- package/dist/components/switch/switch.docs.d.ts +2 -0
- package/dist/components/switch/switch.docs.js +26 -0
- package/dist/components/text-field/text-field.d.ts +0 -6
- package/dist/components/text-field/text-field.def.d.ts +0 -12
- package/dist/components/text-field/text-field.def.js +5 -30
- package/dist/components/text-field/text-field.docs.d.ts +2 -0
- package/dist/components/text-field/text-field.docs.js +45 -0
- package/dist/components/text-field/text-field.js +2 -2
- package/dist/components/text-field/text-field.styles.d.ts +1 -1
- package/dist/components/textarea/textarea.d.ts +0 -6
- package/dist/components/textarea/textarea.def.d.ts +0 -12
- package/dist/components/textarea/textarea.def.js +5 -26
- package/dist/components/textarea/textarea.docs.d.ts +2 -0
- package/dist/components/textarea/textarea.docs.js +40 -0
- package/dist/components/textarea/textarea.styles.d.ts +1 -1
- package/dist/components/theme-docs.d.ts +3 -0
- package/dist/components/theme-docs.js +8 -0
- package/dist/core/define.d.ts +4 -1
- package/dist/core/dom.d.ts +4 -0
- package/dist/core/dom.js +7 -0
- package/dist/core/form-control.js +4 -1
- package/dist/core/neu-element.d.ts +2 -2
- package/dist/core/neu-element.js +10 -7
- package/dist/core/props.d.ts +13 -1
- package/dist/core/props.js +13 -3
- package/dist/core/styles.d.ts +0 -2
- package/dist/core/styles.js +0 -7
- package/dist/docs.d.ts +3 -0
- package/dist/docs.js +41 -0
- package/dist/ssr.d.ts +1 -1
- package/dist/ssr.js +1 -1
- package/package.json +5 -1
|
@@ -18,23 +18,19 @@ export declare const radioGroupDef: import("../../core/define.js").ComponentDef<
|
|
|
18
18
|
};
|
|
19
19
|
readonly name: {
|
|
20
20
|
readonly type: "string";
|
|
21
|
-
readonly reflect: true;
|
|
22
21
|
};
|
|
23
22
|
readonly value: {
|
|
24
23
|
readonly type: "string";
|
|
25
24
|
};
|
|
26
25
|
readonly required: {
|
|
27
26
|
readonly type: "boolean";
|
|
28
|
-
readonly reflect: true;
|
|
29
27
|
};
|
|
30
28
|
readonly disabled: {
|
|
31
29
|
readonly type: "boolean";
|
|
32
|
-
readonly reflect: true;
|
|
33
30
|
};
|
|
34
31
|
readonly orientation: {
|
|
35
32
|
readonly type: "string";
|
|
36
33
|
readonly default: "vertical";
|
|
37
34
|
readonly values: readonly ["vertical", "horizontal"];
|
|
38
|
-
readonly reflect: true;
|
|
39
35
|
};
|
|
40
36
|
}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { defineComponent } from '../../core/define.js';
|
|
2
2
|
import { html } from '../../core/html.js';
|
|
3
|
-
import { themeCssProperties } from '../../core/styles.js';
|
|
4
3
|
import { radioGroupStyles } from './radio-group.styles.js';
|
|
5
4
|
/**
|
|
6
5
|
* Escapes a value for a double-quoted CSS string. Besides the characters that end the string,
|
|
@@ -25,31 +24,17 @@ export const radioGroupDef = defineComponent({
|
|
|
25
24
|
tag: 'neu-radio-group',
|
|
26
25
|
props: {
|
|
27
26
|
label: { type: 'string' },
|
|
28
|
-
name: { type: 'string'
|
|
27
|
+
name: { type: 'string' },
|
|
29
28
|
value: { type: 'string' },
|
|
30
|
-
required: { type: 'boolean'
|
|
31
|
-
disabled: { type: 'boolean'
|
|
29
|
+
required: { type: 'boolean' },
|
|
30
|
+
disabled: { type: 'boolean' },
|
|
32
31
|
orientation: {
|
|
33
32
|
type: 'string',
|
|
34
33
|
default: 'vertical',
|
|
35
34
|
values: ['vertical', 'horizontal'],
|
|
36
|
-
reflect: true,
|
|
37
35
|
},
|
|
38
36
|
},
|
|
39
37
|
form: { valueProp: 'value' },
|
|
40
38
|
styles: radioGroupStyles,
|
|
41
|
-
template: (p) => html `<div part="base" class="base"><span part="label" class="label" data-ref="label">${p.label}</span><span class="items"><slot></slot></span><style data-ref="mark">${markRule(p.value)}</style></div>`,
|
|
42
|
-
docs: {
|
|
43
|
-
summary: 'A group of neu-radio elements with one selected value, arrow-key navigation and form support. The value attribute is the default value.',
|
|
44
|
-
events: [
|
|
45
|
-
{ name: 'input', description: 'Fired when the user selects a radio.' },
|
|
46
|
-
{ name: 'change', description: 'Fired after input when the user selects a radio.' },
|
|
47
|
-
],
|
|
48
|
-
slots: [{ name: '', description: 'neu-radio elements.' }],
|
|
49
|
-
parts: [
|
|
50
|
-
{ name: 'base', description: 'Outer wrapper.' },
|
|
51
|
-
{ name: 'label', description: 'The group label.' },
|
|
52
|
-
],
|
|
53
|
-
cssProperties: themeCssProperties,
|
|
54
|
-
},
|
|
39
|
+
template: (p) => html `<div part="base" class="base"><span part="label" class="label"><slot name="label"><span class="label-text" data-ref="label-text">${p.label}</span></slot></span><span class="items"><slot></slot></span><style data-ref="mark">${markRule(p.value)}</style></div>`,
|
|
55
40
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { themeCssProperties } from '../theme-docs.js';
|
|
2
|
+
export const radioGroupDocs = {
|
|
3
|
+
summary: 'A group of neu-radio elements with one selected value, arrow-key navigation and form support. The value attribute is the default value.',
|
|
4
|
+
descriptions: {
|
|
5
|
+
label: 'Visible label that names the group.',
|
|
6
|
+
name: 'The name under which the selected value is submitted with a form.',
|
|
7
|
+
value: 'The value of the currently selected radio.',
|
|
8
|
+
required: 'Makes the form invalid until a radio is selected.',
|
|
9
|
+
disabled: 'Prevents interaction with every radio in the group.',
|
|
10
|
+
orientation: 'The direction the radios are laid out in.',
|
|
11
|
+
defaultValue: 'The value selected at the start, which a form reset restores; set by the value attribute.',
|
|
12
|
+
},
|
|
13
|
+
events: [
|
|
14
|
+
{ name: 'input', description: 'Fired when the user selects a radio.' },
|
|
15
|
+
{ name: 'change', description: 'Fired after input when the user selects a radio.' },
|
|
16
|
+
],
|
|
17
|
+
slots: [
|
|
18
|
+
{ name: '', description: 'neu-radio elements.' },
|
|
19
|
+
{ name: 'label', description: 'Rich label; replaces the label attribute.' },
|
|
20
|
+
],
|
|
21
|
+
parts: [
|
|
22
|
+
{ name: 'base', description: 'Outer wrapper.' },
|
|
23
|
+
{ name: 'label', description: 'The group label.' },
|
|
24
|
+
],
|
|
25
|
+
cssProperties: themeCssProperties,
|
|
26
|
+
keyboard: [
|
|
27
|
+
{
|
|
28
|
+
name: 'ArrowDown / ArrowRight',
|
|
29
|
+
description: 'Moves to the next radio and selects it, wrapping and skipping disabled radios; ArrowLeft and ArrowRight swap in right-to-left text.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'ArrowUp / ArrowLeft',
|
|
33
|
+
description: 'Moves to the previous radio and selects it, wrapping and skipping disabled radios; ArrowLeft and ArrowRight swap in right-to-left text.',
|
|
34
|
+
},
|
|
35
|
+
{ name: 'Space', description: 'Selects the focused radio.' },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dispatch, setText } from '../../core/dom.js';
|
|
1
|
+
import { dispatch, inSlotted, setText } from '../../core/dom.js';
|
|
2
2
|
import { FormControlElement } from '../../core/form-control.js';
|
|
3
3
|
import { requiredMessage } from '../../core/validation.js';
|
|
4
4
|
import { RESYNC, SET_CHECKED, SET_GROUP_DISABLED } from '../radio/radio-link.js';
|
|
@@ -127,14 +127,27 @@ export class NeuRadioGroup extends FormControlElement {
|
|
|
127
127
|
setText(this.ref('mark'), rule === '' ? '' : rule.value);
|
|
128
128
|
this.commit();
|
|
129
129
|
}
|
|
130
|
+
/** The group's name: the slotted label's text when there is one, else the label attribute. */
|
|
131
|
+
nameGroup() {
|
|
132
|
+
const slot = this.shadowRoot?.querySelector('slot[name="label"]');
|
|
133
|
+
const text = (slot?.assignedNodes() ?? [])
|
|
134
|
+
.map((node) => node.textContent)
|
|
135
|
+
.join('')
|
|
136
|
+
.replace(/\s+/g, ' ')
|
|
137
|
+
.trim();
|
|
138
|
+
this.internals.ariaLabel = text || String(this.props.label) || null;
|
|
139
|
+
}
|
|
140
|
+
rendered() {
|
|
141
|
+
this.shadowRoot.querySelector('slot[name="label"]').addEventListener('slotchange', () => this.nameGroup());
|
|
142
|
+
}
|
|
130
143
|
willUpdate(changed) {
|
|
131
144
|
if (changed.has('value') && !this.dirty)
|
|
132
145
|
this.current = this.getAttribute('value');
|
|
133
146
|
}
|
|
134
147
|
update(changed) {
|
|
135
148
|
if (changed.has('label')) {
|
|
136
|
-
setText(this.ref('label'), String(this.props.label));
|
|
137
|
-
this.
|
|
149
|
+
setText(this.ref('label-text'), String(this.props.label));
|
|
150
|
+
this.nameGroup();
|
|
138
151
|
}
|
|
139
152
|
if (changed.has('required'))
|
|
140
153
|
this.internals.ariaRequired = this.props.required ? 'true' : null;
|
|
@@ -144,13 +157,24 @@ export class NeuRadioGroup extends FormControlElement {
|
|
|
144
157
|
this.sync();
|
|
145
158
|
}
|
|
146
159
|
connected() {
|
|
147
|
-
|
|
160
|
+
// Radios coming, going or changing value or disabled resync the group. Frameworks rewrite the
|
|
161
|
+
// slotted label's text in place, which fires no slotchange; that renames the group only.
|
|
162
|
+
this.observer ??= new MutationObserver((records) => {
|
|
163
|
+
const label = records.filter((record) => inSlotted(this, record.target));
|
|
164
|
+
if (label.length)
|
|
165
|
+
this.nameGroup();
|
|
166
|
+
if (records.some((r) => r.type !== 'characterData' && !label.includes(r)))
|
|
167
|
+
this.sync();
|
|
168
|
+
});
|
|
148
169
|
this.observer.observe(this, {
|
|
149
170
|
childList: true,
|
|
150
171
|
subtree: true,
|
|
172
|
+
characterData: true,
|
|
151
173
|
attributes: true,
|
|
152
174
|
attributeFilter: ['disabled', 'value'],
|
|
153
175
|
});
|
|
176
|
+
// The label may have changed while the group was detached and nothing was observing.
|
|
177
|
+
this.nameGroup();
|
|
154
178
|
this.sync();
|
|
155
179
|
}
|
|
156
180
|
disconnected() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const radioGroupStyles = "\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 {
|
|
1
|
+
export declare const radioGroupStyles = "\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 { display: block; font-size: 0.8125rem; font-weight: 600; color: var(--_neu-fg-muted); }\n.label-text, ::slotted([slot='label']) { display: block; margin-block-end: 0.5rem; }\n.label-text:empty { display: none; }\n.items { display: flex; flex-direction: column; align-items: flex-start; gap: 0.75rem; }\n:host([orientation='horizontal']) .items { flex-direction: row; flex-wrap: wrap; align-items: center; }\n:host(:disabled) .items, :host([disabled]) .items { opacity: 0.55; cursor: not-allowed; }\n:host(:state(user-invalid)) .label { color: var(--_neu-danger); }\n";
|
|
@@ -3,11 +3,9 @@ export const radioGroupStyles = `${hostTheme}
|
|
|
3
3
|
:host { display: block; }
|
|
4
4
|
:host(:disabled), :host([disabled]) { cursor: not-allowed; }
|
|
5
5
|
.base { display: flex; flex-direction: column; }
|
|
6
|
-
.label {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
.label:empty { display: none; }
|
|
6
|
+
.label { display: block; font-size: 0.8125rem; font-weight: 600; color: var(--_neu-fg-muted); }
|
|
7
|
+
.label-text, ::slotted([slot='label']) { display: block; margin-block-end: 0.5rem; }
|
|
8
|
+
.label-text:empty { display: none; }
|
|
11
9
|
.items { display: flex; flex-direction: column; align-items: flex-start; gap: 0.75rem; }
|
|
12
10
|
:host([orientation='horizontal']) .items { flex-direction: row; flex-wrap: wrap; align-items: center; }
|
|
13
11
|
:host(:disabled) .items, :host([disabled]) .items { opacity: 0.55; cursor: not-allowed; }
|
|
@@ -6,7 +6,6 @@ export declare class NeuSelect extends FormControlElement {
|
|
|
6
6
|
};
|
|
7
7
|
readonly name: {
|
|
8
8
|
readonly type: "string";
|
|
9
|
-
readonly reflect: true;
|
|
10
9
|
};
|
|
11
10
|
readonly label: {
|
|
12
11
|
readonly type: "string";
|
|
@@ -22,17 +21,14 @@ export declare class NeuSelect extends FormControlElement {
|
|
|
22
21
|
};
|
|
23
22
|
readonly required: {
|
|
24
23
|
readonly type: "boolean";
|
|
25
|
-
readonly reflect: true;
|
|
26
24
|
};
|
|
27
25
|
readonly disabled: {
|
|
28
26
|
readonly type: "boolean";
|
|
29
|
-
readonly reflect: true;
|
|
30
27
|
};
|
|
31
28
|
readonly size: {
|
|
32
29
|
readonly type: "string";
|
|
33
30
|
readonly default: "md";
|
|
34
31
|
readonly values: readonly ["sm", "md", "lg"];
|
|
35
|
-
readonly reflect: true;
|
|
36
32
|
};
|
|
37
33
|
}>;
|
|
38
34
|
static extraProperties: string[];
|
|
@@ -81,7 +77,7 @@ export declare class NeuSelect extends FormControlElement {
|
|
|
81
77
|
private activeIndex;
|
|
82
78
|
/** Builds the listbox from the options and places it; the active option stays if it can. */
|
|
83
79
|
private render;
|
|
84
|
-
/** Makes option `i` the active one (none for -1)
|
|
80
|
+
/** Makes option `i` the active one (none for -1); `scroll` brings it into view. */
|
|
85
81
|
private activate;
|
|
86
82
|
/** Puts the listbox under the combobox at its width, or above it when that has more room. */
|
|
87
83
|
private readonly place;
|
|
@@ -7,7 +7,6 @@ export declare const selectDef: import("../../core/define.js").ComponentDef<{
|
|
|
7
7
|
};
|
|
8
8
|
readonly name: {
|
|
9
9
|
readonly type: "string";
|
|
10
|
-
readonly reflect: true;
|
|
11
10
|
};
|
|
12
11
|
readonly label: {
|
|
13
12
|
readonly type: "string";
|
|
@@ -23,16 +22,13 @@ export declare const selectDef: import("../../core/define.js").ComponentDef<{
|
|
|
23
22
|
};
|
|
24
23
|
readonly required: {
|
|
25
24
|
readonly type: "boolean";
|
|
26
|
-
readonly reflect: true;
|
|
27
25
|
};
|
|
28
26
|
readonly disabled: {
|
|
29
27
|
readonly type: "boolean";
|
|
30
|
-
readonly reflect: true;
|
|
31
28
|
};
|
|
32
29
|
readonly size: {
|
|
33
30
|
readonly type: "string";
|
|
34
31
|
readonly default: "md";
|
|
35
32
|
readonly values: readonly ["sm", "md", "lg"];
|
|
36
|
-
readonly reflect: true;
|
|
37
33
|
};
|
|
38
34
|
}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { defineComponent } from '../../core/define.js';
|
|
2
2
|
import { html, unsafeHTML } from '../../core/html.js';
|
|
3
|
-
import { themeCssProperties } from '../../core/styles.js';
|
|
4
3
|
import { chevronDownBody, svgWrap } from '../icon/system-icons.js';
|
|
5
4
|
import { selectStyles } from './select.styles.js';
|
|
6
5
|
/** The option a select shows for `value`: the first one whose value matches, as in the client. */
|
|
@@ -12,14 +11,14 @@ export const selectDef = defineComponent({
|
|
|
12
11
|
tag: 'neu-select',
|
|
13
12
|
props: {
|
|
14
13
|
value: { type: 'string' },
|
|
15
|
-
name: { type: 'string'
|
|
14
|
+
name: { type: 'string' },
|
|
16
15
|
label: { type: 'string' },
|
|
17
16
|
placeholder: { type: 'string' },
|
|
18
17
|
helpText: { type: 'string' },
|
|
19
18
|
errorText: { type: 'string' },
|
|
20
|
-
required: { type: 'boolean'
|
|
21
|
-
disabled: { type: 'boolean'
|
|
22
|
-
size: { type: 'string', default: 'md', values: ['sm', 'md', 'lg']
|
|
19
|
+
required: { type: 'boolean' },
|
|
20
|
+
disabled: { type: 'boolean' },
|
|
21
|
+
size: { type: 'string', default: 'md', values: ['sm', 'md', 'lg'] },
|
|
23
22
|
},
|
|
24
23
|
delegatesFocus: true,
|
|
25
24
|
form: { valueProp: 'value' },
|
|
@@ -31,29 +30,6 @@ export const selectDef = defineComponent({
|
|
|
31
30
|
// Options the server could not read (e.g. a custom component's children) leave a set value's
|
|
32
31
|
// display empty rather than showing the placeholder; update() fills it after hydration.
|
|
33
32
|
const unread = !picked && p.value !== '' && ctx?.options?.length === 0;
|
|
34
|
-
return html `<div part="base" class="base"><span part="label" class="label" id="label" data-ref="label">${p.label}</span><div part="combobox" class="combobox" id="combobox" role="combobox"${p.disabled ? html ` aria-disabled="true"` : html ` tabindex="0"`} aria-haspopup="listbox" aria-expanded="false" aria-controls="listbox" aria-labelledby="label" aria-describedby="help"${p.required ? html ` aria-required="true"` : ''} data-ref="combobox"><span part="display" class="display${picked || unread ? '' : ' placeholder'}" data-ref="display">${picked ? picked.label : unread ? '' : p.placeholder}</span><span part="chevron" class="chevron">${chevron}</span></div><div part="listbox" class="listbox" id="listbox" role="listbox" aria-labelledby="label" popover="auto" data-ref="listbox"></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>`;
|
|
35
|
-
},
|
|
36
|
-
docs: {
|
|
37
|
-
summary: 'A single-value select (the APG select-only combobox): a raised trigger and a popover listbox built from the neu-option children, which are data only. The value attribute is the default value.',
|
|
38
|
-
events: [
|
|
39
|
-
{ name: 'input', description: 'Fired when the user chooses an option with a new value.' },
|
|
40
|
-
{ name: 'change', description: 'Fired after input when the user chooses a new value.' },
|
|
41
|
-
{ name: 'neu-open', description: 'Fired when the listbox opens.' },
|
|
42
|
-
{ name: 'neu-close', description: 'Fired when the listbox closes.' },
|
|
43
|
-
],
|
|
44
|
-
slots: [
|
|
45
|
-
{ name: 'help-text', description: 'Rich help text; replaces the help-text attribute.' },
|
|
46
|
-
],
|
|
47
|
-
parts: [
|
|
48
|
-
{ name: 'base', description: 'Outer wrapper.' },
|
|
49
|
-
{ name: 'label', description: 'The label text.' },
|
|
50
|
-
{ name: 'combobox', description: 'The raised trigger that holds focus.' },
|
|
51
|
-
{ name: 'display', description: 'The selected label, or the placeholder.' },
|
|
52
|
-
{ name: 'chevron', description: 'The chevron icon.' },
|
|
53
|
-
{ name: 'listbox', description: 'The popover list of options.' },
|
|
54
|
-
{ name: 'option', description: 'One option in the listbox.' },
|
|
55
|
-
{ name: 'help-text', description: 'Help or error text area.' },
|
|
56
|
-
],
|
|
57
|
-
cssProperties: themeCssProperties,
|
|
33
|
+
return html `<div part="base" class="base"><span part="label" class="label" id="label" data-ref="label"><slot name="label"><span class="label-text" data-ref="label-text">${p.label}</span></slot></span><div part="combobox" class="combobox" id="combobox" role="combobox"${p.disabled ? html ` aria-disabled="true"` : html ` tabindex="0"`} aria-haspopup="listbox" aria-expanded="false" aria-controls="listbox" aria-labelledby="label" aria-describedby="help"${p.required ? html ` aria-required="true"` : ''} data-ref="combobox"><span part="display" class="display${picked || unread ? '' : ' placeholder'}" data-ref="display">${picked ? picked.label : unread ? '' : p.placeholder}</span><span part="chevron" class="chevron">${chevron}</span></div><div part="listbox" class="listbox" id="listbox" role="listbox" aria-labelledby="label" popover="auto" data-ref="listbox"></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>`;
|
|
58
34
|
},
|
|
59
35
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { themeCssProperties } from '../theme-docs.js';
|
|
2
|
+
export const selectDocs = {
|
|
3
|
+
summary: 'A single-value select (the APG select-only combobox): a raised trigger and a popover listbox built from the neu-option children, which are data only. The value attribute is the default value.',
|
|
4
|
+
descriptions: {
|
|
5
|
+
value: 'The value of the currently chosen option.',
|
|
6
|
+
name: 'The name under which the value is submitted with a form.',
|
|
7
|
+
label: 'Visible label text that names the select.',
|
|
8
|
+
placeholder: 'Text shown while no option is chosen.',
|
|
9
|
+
helpText: 'Guidance shown below the select; the help-text slot replaces it.',
|
|
10
|
+
errorText: "Shown in place of the browser's validation message once the select is invalid after user interaction; does not itself make the select invalid.",
|
|
11
|
+
required: 'Makes the form invalid until an option is chosen.',
|
|
12
|
+
disabled: 'Prevents interaction and leaves the select out of form submission.',
|
|
13
|
+
size: 'The select size.',
|
|
14
|
+
defaultValue: 'The value chosen at the start, which a form reset restores; set by the value attribute.',
|
|
15
|
+
},
|
|
16
|
+
events: [
|
|
17
|
+
{ name: 'input', description: 'Fired when the user chooses an option with a new value.' },
|
|
18
|
+
{ name: 'change', description: 'Fired after input when the user chooses a new value.' },
|
|
19
|
+
{ name: 'neu-open', description: 'Fired when the listbox opens.' },
|
|
20
|
+
{ name: 'neu-close', description: 'Fired when the listbox closes.' },
|
|
21
|
+
],
|
|
22
|
+
slots: [
|
|
23
|
+
{ name: 'label', description: 'Rich label; replaces the label attribute.' },
|
|
24
|
+
{ name: 'help-text', description: 'Rich help text; replaces the help-text attribute.' },
|
|
25
|
+
],
|
|
26
|
+
parts: [
|
|
27
|
+
{ name: 'base', description: 'Outer wrapper.' },
|
|
28
|
+
{ name: 'label', description: 'The label text.' },
|
|
29
|
+
{ name: 'combobox', description: 'The raised trigger that holds focus.' },
|
|
30
|
+
{ name: 'display', description: 'The selected label, or the placeholder.' },
|
|
31
|
+
{ name: 'chevron', description: 'The chevron icon.' },
|
|
32
|
+
{ name: 'listbox', description: 'The popover list of options.' },
|
|
33
|
+
{ name: 'option', description: 'One option in the listbox.' },
|
|
34
|
+
{ name: 'help-text', description: 'Help or error text area.' },
|
|
35
|
+
],
|
|
36
|
+
cssProperties: themeCssProperties,
|
|
37
|
+
keyboard: [
|
|
38
|
+
{ name: 'ArrowDown / ArrowUp / Enter / Space', description: 'When closed: opens the listbox.' },
|
|
39
|
+
{
|
|
40
|
+
name: 'Home / End',
|
|
41
|
+
description: 'When closed: opens the listbox on the first or last option.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'Printable characters',
|
|
45
|
+
description: 'When closed: opens the listbox on the first matching option.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'ArrowDown / ArrowUp',
|
|
49
|
+
description: 'When open: moves to the next or previous option.',
|
|
50
|
+
},
|
|
51
|
+
{ name: 'Home / End', description: 'When open: moves to the first or last option.' },
|
|
52
|
+
{ name: 'PageDown / PageUp', description: 'When open: moves ten options.' },
|
|
53
|
+
{ name: 'Enter / Space', description: 'When open: chooses the active option and closes.' },
|
|
54
|
+
{ name: 'Escape', description: 'When open: closes without changing the value.' },
|
|
55
|
+
{
|
|
56
|
+
name: 'Tab',
|
|
57
|
+
description: 'When open: chooses the active option, closes and moves focus on.',
|
|
58
|
+
},
|
|
59
|
+
{ name: 'Alt+ArrowUp', description: 'When open: chooses the active option and closes.' },
|
|
60
|
+
{
|
|
61
|
+
name: 'Printable characters',
|
|
62
|
+
description: 'When open: moves to the matching option; characters typed within 500 ms add up.',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dispatch, setOptionalAttribute, setText } from '../../core/dom.js';
|
|
1
|
+
import { dispatch, inSlotted, setOptionalAttribute, setText } from '../../core/dom.js';
|
|
2
2
|
import { FormControlElement } from '../../core/form-control.js';
|
|
3
3
|
import { requiredMessage } from '../../core/validation.js';
|
|
4
4
|
import { readOption } from '../option/option-data.js';
|
|
@@ -107,15 +107,15 @@ export class NeuSelect extends FormControlElement {
|
|
|
107
107
|
const selected = picked ? this.opts.indexOf(picked) : -1;
|
|
108
108
|
this.activate(kept >= 0 && !this.opts[kept].disabled ? kept : selected >= 0 ? selected : this.step(0, 1));
|
|
109
109
|
}
|
|
110
|
-
/** Makes option `i` the active one (none for -1)
|
|
111
|
-
activate(i) {
|
|
110
|
+
/** Makes option `i` the active one (none for -1); `scroll` brings it into view. */
|
|
111
|
+
activate(i, scroll = true) {
|
|
112
112
|
const listbox = this.ref('listbox');
|
|
113
113
|
this.activeSource = this.opts[i]?.source;
|
|
114
114
|
const items = [...listbox.children];
|
|
115
115
|
items.forEach((item, n) => item.classList.toggle('active', n === i));
|
|
116
116
|
const item = items[i];
|
|
117
117
|
setOptionalAttribute(this.ref('combobox'), 'aria-activedescendant', item ? item.id : '');
|
|
118
|
-
if (!item)
|
|
118
|
+
if (!item || !scroll)
|
|
119
119
|
return;
|
|
120
120
|
// Keep the listbox padding in view too, so Home and End scroll to the very top and bottom.
|
|
121
121
|
const pad = items[0].offsetTop;
|
|
@@ -225,10 +225,12 @@ export class NeuSelect extends FormControlElement {
|
|
|
225
225
|
this.activate(found);
|
|
226
226
|
}
|
|
227
227
|
onKey(event) {
|
|
228
|
-
|
|
228
|
+
const { key, altKey, ctrlKey } = event;
|
|
229
|
+
const printable = key.length === 1 && key !== ' ' && (!altKey || ctrlKey);
|
|
230
|
+
// Ctrl+Alt is AltGr on many layouts: a character it types is not a shortcut. Other Ctrl+Alt
|
|
231
|
+
// chords (assistive tech, the OS) are.
|
|
232
|
+
if (this.isDisabled || event.metaKey || (ctrlKey && !(altKey && printable)))
|
|
229
233
|
return;
|
|
230
|
-
const { key, altKey } = event;
|
|
231
|
-
const printable = key.length === 1 && key !== ' ' && !altKey;
|
|
232
234
|
const active = this.activeIndex();
|
|
233
235
|
const last = this.opts.length - 1;
|
|
234
236
|
const moves = {
|
|
@@ -297,7 +299,7 @@ export class NeuSelect extends FormControlElement {
|
|
|
297
299
|
listbox.addEventListener('pointermove', (event) => {
|
|
298
300
|
const i = this.optionAt(event);
|
|
299
301
|
if (i >= 0 && i !== this.activeIndex() && !this.opts[i]?.disabled)
|
|
300
|
-
this.activate(i);
|
|
302
|
+
this.activate(i, false);
|
|
301
303
|
});
|
|
302
304
|
listbox.addEventListener('click', (event) => {
|
|
303
305
|
const i = this.optionAt(event);
|
|
@@ -326,7 +328,7 @@ export class NeuSelect extends FormControlElement {
|
|
|
326
328
|
update(changed) {
|
|
327
329
|
const combobox = this.ref('combobox');
|
|
328
330
|
if (changed.has('label'))
|
|
329
|
-
setText(this.ref('label'), String(this.props.label));
|
|
331
|
+
setText(this.ref('label-text'), String(this.props.label));
|
|
330
332
|
if (changed.has('helpText'))
|
|
331
333
|
setText(this.ref('help-text'), String(this.props.helpText));
|
|
332
334
|
if (changed.has('required')) {
|
|
@@ -345,7 +347,11 @@ export class NeuSelect extends FormControlElement {
|
|
|
345
347
|
this.commit();
|
|
346
348
|
}
|
|
347
349
|
connected() {
|
|
348
|
-
|
|
350
|
+
// A rich label or help text holds no options: rewriting it needs no re-read.
|
|
351
|
+
this.observer ??= new MutationObserver((records) => {
|
|
352
|
+
if (records.some((record) => !inSlotted(this, record.target)))
|
|
353
|
+
this.sync();
|
|
354
|
+
});
|
|
349
355
|
this.observer.observe(this, {
|
|
350
356
|
childList: true,
|
|
351
357
|
subtree: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const selectStyles = "\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
|
|
1
|
+
export declare const selectStyles = "\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 { font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted); }\n.label-text, ::slotted([slot='label']) { display: block; margin-block-end: 0.375rem; }\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.label { display: block; }\n.combobox {\n display: flex; align-items: center; gap: 0.5rem; min-block-size: 2.75rem; padding-inline: 0.875rem 0.75rem;\n border-radius: var(--_neu-r); background: var(--_neu-bg); box-shadow: var(--_neu-raised-sm);\n font-size: 0.9375rem; cursor: pointer; user-select: none; outline: none;\n transition: box-shadow var(--_neu-dur) ease;\n}\n:host([size='sm']) .combobox { min-block-size: 2.25rem; padding-inline: 0.75rem 0.625rem; font-size: 0.875rem; }\n:host([size='lg']) .combobox { min-block-size: 3.25rem; padding-inline: 1rem 0.875rem; font-size: 1rem; }\n.combobox[aria-expanded='true'] { box-shadow: var(--_neu-inset-sm); }\n.combobox:focus-visible { box-shadow: var(--_neu-raised-sm), var(--_neu-focus); }\n.combobox[aria-expanded='true']:focus-visible { box-shadow: var(--_neu-inset-sm), var(--_neu-focus); }\n:host(:state(user-invalid)) .combobox {\n box-shadow:\n calc(var(--_neu-d) * 0.5) calc(var(--_neu-d) * 0.5) var(--_neu-d) color-mix(in oklab, var(--_neu-danger) 45%, var(--_neu-sd)),\n calc(var(--_neu-d) * -0.5) calc(var(--_neu-d) * -0.5) var(--_neu-d) var(--_neu-sl);\n}\n:host(:disabled) .combobox, :host([disabled]) .combobox { opacity: 0.6; cursor: not-allowed; }\n.display { flex: 1; min-inline-size: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\n.placeholder { color: var(--_neu-fg-muted); }\n.chevron { display: inline-flex; flex: none; color: var(--_neu-fg-muted); transition: transform var(--_neu-dur) ease; }\n.chevron svg { inline-size: 1rem; block-size: 1rem; }\n.combobox[aria-expanded='true'] .chevron { transform: rotate(180deg); }\n.listbox {\n position: fixed; inset: auto; margin: 0; max-block-size: 16rem; overflow: auto;\n padding: 0.375rem; border: none; border-radius: var(--_neu-r);\n background: var(--_neu-bg); color: var(--_neu-fg); box-shadow: var(--_neu-raised); font-size: 0.9375rem;\n}\n.option { padding: 0.5rem 0.625rem; border-radius: calc(var(--_neu-r) * 0.75); cursor: pointer; user-select: none; }\n.option.active { box-shadow: var(--_neu-inset-sm); }\n.option[aria-selected='true'] { color: var(--_neu-accent-c); font-weight: 600; }\n.option[aria-disabled='true'] { color: var(--_neu-fg-muted); cursor: not-allowed; }\n";
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { fieldStyles } from '../shared/field.styles.js';
|
|
2
2
|
export const selectStyles = `${fieldStyles}
|
|
3
|
-
.label {
|
|
4
|
-
display: block; margin-block-end: 0.375rem;
|
|
5
|
-
font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted);
|
|
6
|
-
}
|
|
7
|
-
.label:empty { display: none; }
|
|
3
|
+
.label { display: block; }
|
|
8
4
|
.combobox {
|
|
9
5
|
display: flex; align-items: center; gap: 0.5rem; min-block-size: 2.75rem; padding-inline: 0.875rem 0.75rem;
|
|
10
6
|
border-radius: var(--_neu-r); background: var(--_neu-bg); box-shadow: var(--_neu-raised-sm);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const fieldStyles = "\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
|
|
1
|
+
export declare const fieldStyles = "\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 { font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted); }\n.label-text, ::slotted([slot='label']) { display: block; margin-block-end: 0.375rem; }\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";
|
|
@@ -3,10 +3,8 @@ export const fieldStyles = `${hostTheme}
|
|
|
3
3
|
:host { display: block; }
|
|
4
4
|
:host(:disabled), :host([disabled]) { cursor: not-allowed; }
|
|
5
5
|
.base { display: flex; flex-direction: column; }
|
|
6
|
-
.label-
|
|
7
|
-
|
|
8
|
-
font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted);
|
|
9
|
-
}
|
|
6
|
+
.label { font-size: 0.8125rem; font-weight: 600; line-height: 1rem; color: var(--_neu-fg-muted); }
|
|
7
|
+
.label-text, ::slotted([slot='label']) { display: block; margin-block-end: 0.375rem; }
|
|
10
8
|
.label-text:empty { display: none; }
|
|
11
9
|
.control {
|
|
12
10
|
display: flex; align-items: center; min-block-size: 2.75rem; padding-inline: 0.875rem;
|
|
@@ -19,14 +19,12 @@ export declare class NeuSlider extends FormControlElement {
|
|
|
19
19
|
};
|
|
20
20
|
readonly name: {
|
|
21
21
|
readonly type: "string";
|
|
22
|
-
readonly reflect: true;
|
|
23
22
|
};
|
|
24
23
|
readonly label: {
|
|
25
24
|
readonly type: "string";
|
|
26
25
|
};
|
|
27
26
|
readonly disabled: {
|
|
28
27
|
readonly type: "boolean";
|
|
29
|
-
readonly reflect: true;
|
|
30
28
|
};
|
|
31
29
|
}>;
|
|
32
30
|
static extraProperties: string[];
|
|
@@ -27,13 +27,11 @@ export declare const sliderDef: import("../../core/define.js").ComponentDef<{
|
|
|
27
27
|
};
|
|
28
28
|
readonly name: {
|
|
29
29
|
readonly type: "string";
|
|
30
|
-
readonly reflect: true;
|
|
31
30
|
};
|
|
32
31
|
readonly label: {
|
|
33
32
|
readonly type: "string";
|
|
34
33
|
};
|
|
35
34
|
readonly disabled: {
|
|
36
35
|
readonly type: "boolean";
|
|
37
|
-
readonly reflect: true;
|
|
38
36
|
};
|
|
39
37
|
}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { defineComponent } from '../../core/define.js';
|
|
2
2
|
import { html } from '../../core/html.js';
|
|
3
|
-
import { themeCssProperties } from '../../core/styles.js';
|
|
4
3
|
import { stepUnit } from '../shared/step.js';
|
|
5
4
|
import { sliderStyles } from './slider.styles.js';
|
|
6
5
|
/** Decimal places of a number as written, exponent notation included. */
|
|
@@ -44,33 +43,12 @@ export const sliderDef = defineComponent({
|
|
|
44
43
|
min: { type: 'number', default: 0 },
|
|
45
44
|
max: { type: 'number', default: 100 },
|
|
46
45
|
step: { type: 'number', default: 1 },
|
|
47
|
-
name: { type: 'string'
|
|
46
|
+
name: { type: 'string' },
|
|
48
47
|
label: { type: 'string' },
|
|
49
|
-
disabled: { type: 'boolean'
|
|
48
|
+
disabled: { type: 'boolean' },
|
|
50
49
|
},
|
|
51
50
|
form: { valueProp: 'value' },
|
|
52
51
|
properties: { valueFormatter: '(value: number) => string' },
|
|
53
52
|
styles: sliderStyles,
|
|
54
53
|
template: (p) => html `<span part="base" class="base" data-ref="base" style="${sliderStyle(sliderValue(p.value, p.min, p.max, p.step), p.min, p.max)}"><span part="track" class="track" data-ref="track"><span part="fill" class="fill"></span><span part="thumb" class="thumb"></span></span></span>`,
|
|
55
|
-
docs: {
|
|
56
|
-
summary: 'A horizontal slider for a number. The value attribute is the default; the value property is the current value. The valueFormatter property turns the value into the text a screen reader announces.',
|
|
57
|
-
events: [
|
|
58
|
-
{
|
|
59
|
-
name: 'input',
|
|
60
|
-
description: 'Fired on every change while the user drags or presses a key.',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: 'change',
|
|
64
|
-
description: 'Fired after the user commits a change: on release, or per key.',
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
slots: [],
|
|
68
|
-
parts: [
|
|
69
|
-
{ name: 'base', description: 'Wrapper around the track.' },
|
|
70
|
-
{ name: 'track', description: 'The inset groove.' },
|
|
71
|
-
{ name: 'fill', description: 'The part of the track up to the thumb.' },
|
|
72
|
-
{ name: 'thumb', description: 'The raised handle.' },
|
|
73
|
-
],
|
|
74
|
-
cssProperties: themeCssProperties,
|
|
75
|
-
},
|
|
76
54
|
});
|