vira 33.5.1 → 33.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,7 @@ export * from './vira-link.element.js';
33
33
  export * from './vira-modal.element.js';
34
34
  export * from './vira-progress.element.js';
35
35
  export * from './vira-relative-time.element.js';
36
+ export * from './vira-slider.element.js';
36
37
  export * from './vira-tabs.element.js';
37
38
  export * from './vira-tag.element.js';
38
39
  export * from './vira-text-area.element.js';
@@ -33,6 +33,7 @@ export * from './vira-link.element.js';
33
33
  export * from './vira-modal.element.js';
34
34
  export * from './vira-progress.element.js';
35
35
  export * from './vira-relative-time.element.js';
36
+ export * from './vira-slider.element.js';
36
37
  export * from './vira-tabs.element.js';
37
38
  export * from './vira-tag.element.js';
38
39
  export * from './vira-text-area.element.js';
@@ -0,0 +1,35 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ /**
3
+ * Inputs for {@link ViraSlider}.
4
+ *
5
+ * @category Internal
6
+ */
7
+ export type ViraSliderInputs = {
8
+ value: number;
9
+ } & PartialWithUndefined<{
10
+ /** Defaults to `0`. */
11
+ min: number;
12
+ /** Defaults to `100`. */
13
+ max: number;
14
+ /** Defaults to `1`. */
15
+ step: number;
16
+ isDisabled: boolean;
17
+ /** The slider's accessible name. It is not rendered visibly. */
18
+ label: string;
19
+ }>;
20
+ /**
21
+ * A themed replacement for `<input type="range">`. The track, fill, and thumb are drawn with Vira's
22
+ * form colors, so they follow the current theme (including dark mode) where a browser's native
23
+ * range styling does not. An invisible native range input sits on top, so keyboard, pointer, and
24
+ * screen reader behavior are the browser's own.
25
+ *
26
+ * `valueChange` fires on every change while dragging. The slider is controlled: it only moves when
27
+ * `value` is updated in response.
28
+ *
29
+ * @category Input
30
+ * @category Elements
31
+ * @see https://electrovir.github.io/vira/book/elements/vira-slider
32
+ */
33
+ export declare const ViraSlider: import("element-vir").DeclarativeElementDefinition<"vira-slider", Readonly<ViraSliderInputs>, {}, {
34
+ valueChange: import("element-vir").DefineElementEvent<number>;
35
+ }, "vira-slider-", "vira-slider-track-height" | "vira-slider-thumb-size" | "vira-slider-track-color" | "vira-slider-fill-color" | "vira-slider-thumb-color" | "vira-slider-thumb-hover-color" | "vira-slider-thumb-active-color", readonly [], readonly []>;
@@ -0,0 +1,179 @@
1
+ import { extractEventTarget } from '@augment-vir/web';
2
+ import { classMap, css, defineElementEvent, html, ifDefined, listen } from 'element-vir';
3
+ import { viraDisabledStyles } from '../styles/disabled.js';
4
+ import { createFocusStyles } from '../styles/focus.js';
5
+ import { viraFormCssVars } from '../styles/form-styles.js';
6
+ import { defineViraElement } from '../util/define-vira-element.js';
7
+ /**
8
+ * A themed replacement for `<input type="range">`. The track, fill, and thumb are drawn with Vira's
9
+ * form colors, so they follow the current theme (including dark mode) where a browser's native
10
+ * range styling does not. An invisible native range input sits on top, so keyboard, pointer, and
11
+ * screen reader behavior are the browser's own.
12
+ *
13
+ * `valueChange` fires on every change while dragging. The slider is controlled: it only moves when
14
+ * `value` is updated in response.
15
+ *
16
+ * @category Input
17
+ * @category Elements
18
+ * @see https://electrovir.github.io/vira/book/elements/vira-slider
19
+ */
20
+ export const ViraSlider = defineViraElement()({
21
+ tagName: 'vira-slider',
22
+ cssVars: {
23
+ 'vira-slider-track-height': '4px',
24
+ 'vira-slider-thumb-size': '16px',
25
+ 'vira-slider-track-color': viraFormCssVars['vira-form-border-color'].value,
26
+ 'vira-slider-fill-color': viraFormCssVars['vira-form-accent-primary-color'].value,
27
+ 'vira-slider-thumb-color': viraFormCssVars['vira-form-accent-primary-color'].value,
28
+ 'vira-slider-thumb-hover-color': viraFormCssVars['vira-form-accent-primary-hover-color'].value,
29
+ 'vira-slider-thumb-active-color': viraFormCssVars['vira-form-accent-primary-active-color'].value,
30
+ },
31
+ styles({ cssVars }) {
32
+ return css `
33
+ :host {
34
+ display: inline-flex;
35
+ width: 160px;
36
+ vertical-align: middle;
37
+ }
38
+
39
+ .slider {
40
+ position: relative;
41
+ flex-grow: 1;
42
+ height: ${cssVars['vira-slider-thumb-size'].value};
43
+
44
+ &.disabled {
45
+ ${viraDisabledStyles};
46
+ }
47
+ }
48
+
49
+ .track,
50
+ .fill {
51
+ position: absolute;
52
+ top: 50%;
53
+ left: 0;
54
+ height: ${cssVars['vira-slider-track-height'].value};
55
+ transform: translateY(-50%);
56
+ border-radius: ${cssVars['vira-slider-track-height'].value};
57
+ pointer-events: none;
58
+ }
59
+
60
+ .track {
61
+ width: 100%;
62
+ background-color: ${cssVars['vira-slider-track-color'].value};
63
+ }
64
+
65
+ .fill {
66
+ background-color: ${cssVars['vira-slider-fill-color'].value};
67
+ }
68
+
69
+ .thumb {
70
+ position: absolute;
71
+ top: 0;
72
+ width: ${cssVars['vira-slider-thumb-size'].value};
73
+ height: ${cssVars['vira-slider-thumb-size'].value};
74
+ box-sizing: border-box;
75
+ border-radius: 50%;
76
+ border: 2px solid ${viraFormCssVars['vira-form-background-color'].value};
77
+ background-color: ${cssVars['vira-slider-thumb-color'].value};
78
+ pointer-events: none;
79
+ }
80
+
81
+ input {
82
+ position: absolute;
83
+ inset: 0;
84
+ width: 100%;
85
+ height: 100%;
86
+ margin: 0;
87
+ padding: 0;
88
+ appearance: none;
89
+ background: transparent;
90
+ opacity: 0;
91
+ cursor: pointer;
92
+
93
+ /*
94
+ The invisible native thumb must match the drawn thumb's width so the browser
95
+ maps pointer positions to the same values the drawn thumb shows.
96
+ */
97
+ &::-webkit-slider-thumb {
98
+ appearance: none;
99
+ width: ${cssVars['vira-slider-thumb-size'].value};
100
+ height: ${cssVars['vira-slider-thumb-size'].value};
101
+ }
102
+ &::-moz-range-thumb {
103
+ width: ${cssVars['vira-slider-thumb-size'].value};
104
+ height: ${cssVars['vira-slider-thumb-size'].value};
105
+ border: none;
106
+ }
107
+
108
+ &:hover ~ .thumb {
109
+ background-color: ${cssVars['vira-slider-thumb-hover-color'].value};
110
+ }
111
+ &:active ~ .thumb {
112
+ background-color: ${cssVars['vira-slider-thumb-active-color'].value};
113
+ }
114
+ &:focus-visible:not(:active) ~ .thumb::after {
115
+ ${createFocusStyles({
116
+ elementBorderSize: '2px',
117
+ borderRadius: '50%',
118
+ noNesting: true,
119
+ })}
120
+ }
121
+ }
122
+ `;
123
+ },
124
+ events: {
125
+ valueChange: defineElementEvent(),
126
+ },
127
+ render({ inputs, dispatch, events, cssVars }) {
128
+ const min = inputs.min ?? 0;
129
+ const max = inputs.max ?? 100;
130
+ const fraction = max > min ? Math.min(1, Math.max(0, (inputs.value - min) / (max - min))) : 0;
131
+ const thumbOffset = css `calc((100% - ${cssVars['vira-slider-thumb-size'].value}) * ${fraction})`;
132
+ return html `
133
+ <div
134
+ class="slider ${classMap({
135
+ disabled: !!inputs.isDisabled,
136
+ })}"
137
+ >
138
+ <div class="track"></div>
139
+ <div
140
+ class="fill"
141
+ style=${css `
142
+ width: calc(
143
+ ${thumbOffset} + ${cssVars['vira-slider-thumb-size'].value} / 2
144
+ );
145
+ `}
146
+ ></div>
147
+ <input
148
+ type="range"
149
+ min=${min}
150
+ max=${max}
151
+ step=${inputs.step ?? 1}
152
+ aria-label=${ifDefined(inputs.label || undefined)}
153
+ ?disabled=${inputs.isDisabled}
154
+ .value=${String(inputs.value)}
155
+ ${listen('input', (event) => {
156
+ const inputElement = extractEventTarget(event, HTMLInputElement);
157
+ const newValue = Number(inputElement.value);
158
+ /**
159
+ * Snap the native input back to the controlled value; it only moves once
160
+ * the new value is assigned back in.
161
+ */
162
+ inputElement.value = String(inputs.value);
163
+ if (newValue !== inputs.value) {
164
+ dispatch(new events.valueChange({
165
+ detail: newValue,
166
+ }));
167
+ }
168
+ })}
169
+ />
170
+ <div
171
+ class="thumb"
172
+ style=${css `
173
+ left: ${thumbOffset};
174
+ `}
175
+ ></div>
176
+ </div>
177
+ `;
178
+ },
179
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vira",
3
- "version": "33.5.1",
3
+ "version": "33.6.0",
4
4
  "description": "A simple and highly versatile design system using element-vir.",
5
5
  "keywords": [
6
6
  "design",