diff --git a/packages/webawesome/docs/docs/components/slider.md b/packages/webawesome/docs/docs/components/slider.md
index 9400458b0..f4f7cda6d 100644
--- a/packages/webawesome/docs/docs/components/slider.md
+++ b/packages/webawesome/docs/docs/components/slider.md
@@ -7,7 +7,20 @@ icon: slider
---
```html {.example}
-
+
+ Less
+ More
+
```
:::info
@@ -18,7 +31,7 @@ This component works with standard `
+```
\ No newline at end of file
diff --git a/packages/webawesome/docs/docs/resources/changelog.md b/packages/webawesome/docs/docs/resources/changelog.md
index 637c0071e..dc3c2fcc8 100644
--- a/packages/webawesome/docs/docs/resources/changelog.md
+++ b/packages/webawesome/docs/docs/resources/changelog.md
@@ -38,6 +38,16 @@ During the alpha period, things might break! We take breaking changes very serio
- Added convenience tokens for `--wa-font-size-smaller` and `--wa-font-size-larger`
- Updated components to use relative `em` values for internal padding and margin wherever appropriate
- 🚨 BREAKING: removed the `hint` property and slot from ``; please apply hints directly to `` instead
+- 🚨 BREAKING: redesigned `` with extensive new functionality
+ - Added support for range sliders with dual thumbs using the `range` attribute
+ - Added vertical orientation support with `orientation="vertical"`
+ - Added visual markers at each step with `with-markers`
+ - Added contextual reference labels with `with-references` and the `reference` slot
+ - Added tooltips showing current values with `with-tooltip`
+ - Added customizable indicator offset with `indicator-offset` attribute
+ - Added value formatting support with the `valueFormatter` property
+ - Improved the styling API to be consistent and more powerful (no more browser-specific selectors and pseudo elements to style)
+ - Updated to use consistent `with-*` attribute naming pattern
- 🚨 BREAKING: removed ``; use `` instead
- 🚨 BREAKING: completely reworked `` to be easier to use
- Added ``, greatly simplifying the dropdown's markup structure
diff --git a/packages/webawesome/src/components/input/input.ts b/packages/webawesome/src/components/input/input.ts
index 5ffe73c8f..e615f0e8d 100644
--- a/packages/webawesome/src/components/input/input.ts
+++ b/packages/webawesome/src/components/input/input.ts
@@ -5,6 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { WaClearEvent } from '../../events/clear.js';
import { HasSlotController } from '../../internal/slot.js';
+import { submitOnEnter } from '../../internal/submit-on-enter.js';
import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
@@ -12,7 +13,6 @@ import formControlStyles from '../../styles/component/form-control.css';
import appearanceStyles from '../../styles/utilities/appearance.css';
import sizeStyles from '../../styles/utilities/size.css';
import { LocalizeController } from '../../utilities/localize.js';
-import type WaButton from '../button/button.js';
import '../icon/icon.js';
import styles from './input.css';
@@ -245,51 +245,7 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
}
private handleKeyDown(event: KeyboardEvent) {
- const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
-
- // Pressing enter when focused on an input should submit the form like a native input, but we wait a tick before
- // submitting to allow users to cancel the keydown event if they need to
- if (event.key === 'Enter' && !hasModifier) {
- setTimeout(() => {
- //
- // When using an Input Method Editor (IME), pressing enter will cause the form to submit unexpectedly. One way
- // to check for this is to look at event.isComposing, which will be true when the IME is open.
- //
- // See https://github.com/shoelace-style/shoelace/pull/988
- //
- if (!event.defaultPrevented && !event.isComposing) {
- const form = this.getForm();
-
- if (!form) {
- return;
- }
-
- const formElements = [...form.elements];
-
- // If we're the only formElement, we submit like a native input.
- if (formElements.length === 1) {
- form.requestSubmit(null);
- return;
- }
-
- const button = formElements.find(
- (el: HTMLButtonElement) => el.type === 'submit' && !el.matches(':disabled'),
- ) as undefined | HTMLButtonElement | WaButton;
-
- // No button found, don't submit.
- if (!button) {
- return;
- }
-
- if (button.tagName.toLowerCase() === 'button') {
- form.requestSubmit(button);
- } else {
- // requestSubmit() wont work with ``
- button.click();
- }
- }
- });
- }
+ submitOnEnter(event, this);
}
private handlePasswordToggle() {
diff --git a/packages/webawesome/src/components/slider/slider.css b/packages/webawesome/src/components/slider/slider.css
index 13eee9922..f0ee323ac 100644
--- a/packages/webawesome/src/components/slider/slider.css
+++ b/packages/webawesome/src/components/slider/slider.css
@@ -1,214 +1,227 @@
:host {
- --thumb-color: var(--wa-form-control-activated-color);
- --thumb-gap: calc(var(--thumb-size) * 0.125);
- --thumb-shadow: initial;
- --thumb-size: calc(1em * var(--wa-form-control-value-line-height));
-
- --track-color-active: var(--wa-color-neutral-fill-normal);
- --track-color-inactive: var(--wa-color-neutral-fill-normal);
- --track-active-offset: 0%;
- --track-height: calc(var(--thumb-size) * 0.25);
- --tooltip-offset: calc(var(--wa-tooltip-arrow-size) * 1.375);
-
- display: flex;
- flex-direction: column;
- position: relative;
- min-height: max(var(--thumb-size), var(--track-height));
+ --track-size: 0.5em;
+ --thumb-width: 1.4em;
+ --thumb-height: 1.4em;
+ --marker-width: 0.1875em;
+ --marker-height: 0.1875em;
}
-input[type='range'] {
- --percent: 0%;
- -webkit-appearance: none;
- border-radius: calc(var(--track-height) / 2);
- width: 100%;
- height: var(--track-height);
- font-size: inherit;
- line-height: var(--wa-form-control-height);
- vertical-align: middle;
- margin: 0;
- --dir: right;
+:host([orientation='vertical']) {
+ width: auto;
+}
- background-image: linear-gradient(
- to var(--dir),
- var(--track-color-inactive) min(var(--percent), var(--track-active-offset)),
- var(--track-color-active) min(var(--percent), var(--track-active-offset)),
- var(--track-color-active) max(var(--percent), var(--track-active-offset)),
- var(--track-color-inactive) max(var(--percent), var(--track-active-offset))
- );
+#label:has(~ .vertical) {
+ display: block;
+ order: 2;
+ max-width: none;
+ text-align: center;
+}
+
+#description:has(~ .vertical) {
+ order: 3;
+ text-align: center;
+}
+
+/* Add extra space between slider and label, when present */
+#label:has(*:not(:empty)) ~ #slider {
+ &.horizontal {
+ margin-block-start: 0.5em;
+ }
+ &.vertical {
+ margin-block-end: 0.5em;
+ }
+}
+
+#slider {
+ &:focus {
+ outline: none;
+ }
+
+ &:focus-visible:not(.disabled) #thumb,
+ &:focus-visible:not(.disabled) #thumb-min,
+ &:focus-visible:not(.disabled) #thumb-max {
+ outline: var(--wa-focus-ring);
+ /* intentionally no offset due to border */
+ }
+}
+
+#track {
+ position: relative;
+ border-radius: 9999px;
+ background: var(--wa-color-neutral-fill-normal);
+ isolation: isolate;
+}
+
+/* Orientation */
+.horizontal #track {
+ height: var(--track-size);
+}
+
+.vertical #track {
+ order: 1;
+ width: var(--track-size);
+ height: 200px;
+}
+
+/* Disabled */
+.disabled #track {
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+/* Indicator */
+#indicator {
+ position: absolute;
+ border-radius: inherit;
+ background-color: var(--wa-form-control-activated-color);
+
+ &:dir(ltr) {
+ right: calc(100% - max(var(--start), var(--end)));
+ left: min(var(--start), var(--end));
+ }
&:dir(rtl) {
- --dir: left;
- }
-
- &::-webkit-slider-runnable-track {
- width: 100%;
- height: var(--track-height);
- border-radius: 3px;
- border: none;
- }
-
- &::-webkit-slider-thumb {
- width: var(--thumb-size);
- height: var(--thumb-size);
- border-radius: 50%;
- background-color: var(--thumb-color);
- box-shadow:
- var(--thumb-shadow, 0 0 transparent),
- 0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
- -webkit-appearance: none;
- margin-top: calc(var(--thumb-size) / -2 + var(--track-height) / 2);
- transition: var(--wa-transition-fast);
- transition-property: width, height;
- }
-
- &:enabled {
- &:focus-visible::-webkit-slider-thumb {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- }
-
- &::-webkit-slider-thumb {
- cursor: pointer;
- }
-
- &::-webkit-slider-thumb:active {
- cursor: grabbing;
- }
- }
-
- &::-moz-focus-outer {
- border: 0;
- }
-
- &::-moz-range-progress {
- background-color: var(--track-color-active);
- border-radius: 3px;
- height: var(--track-height);
- }
-
- &::-moz-range-track {
- width: 100%;
- height: var(--track-height);
- background-color: var(--track-color-inactive);
- border-radius: 3px;
- border: none;
- }
-
- &::-moz-range-thumb {
- height: var(--thumb-size);
- width: var(--thumb-size);
- border-radius: 50%;
- background-color: var(--thumb-color);
- box-shadow:
- var(--thumb-shadow, 0 0 transparent),
- 0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
- transition-property: background-color, border-color, box-shadow, color;
- transition-duration: var(--wa-transition-normal);
- transition-timing-function: var(--wa-transition-easing);
- }
-
- &:enabled {
- &:focus-visible::-moz-range-thumb {
- outline: var(--wa-focus-ring);
- outline-offset: var(--wa-focus-ring-offset);
- }
-
- &::-moz-range-thumb {
- cursor: pointer;
- }
-
- &::-moz-range-thumb:active {
- cursor: grabbing;
- }
+ right: min(var(--start), var(--end));
+ left: calc(100% - max(var(--start), var(--end)));
}
}
-/* States */
-/* nesting these styles yields broken results in Safari */
-input[type='range']:focus {
- outline: none;
+.horizontal #indicator {
+ top: 0;
+ height: 100%;
}
-:host :has(:disabled) input[type='range'] {
- opacity: 0.5;
- cursor: not-allowed;
-
- &::-moz-range-thumb,
- &::-webkit-slider-thumb {
- cursor: not-allowed;
- }
+.vertical #indicator {
+ top: calc(100% - var(--end));
+ bottom: var(--start);
+ left: 0;
+ width: 100%;
}
-/* Tooltip output */
-.tooltip {
+/* Thumbs */
+#thumb,
+#thumb-min,
+#thumb-max {
+ z-index: 3;
position: absolute;
- z-index: 1000;
- inset-inline-start: 0;
+ width: var(--thumb-width);
+ height: var(--thumb-height);
+ border: solid 0.125em var(--wa-color-surface-default);
+ border-radius: 50%;
+ background-color: var(--wa-form-control-activated-color);
+ cursor: pointer;
+}
- inset-block-end: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
- border-radius: var(--wa-tooltip-border-radius);
- background-color: var(--wa-tooltip-background-color);
- font-family: inherit;
- font-size: var(--wa-tooltip-font-size);
- line-height: var(--wa-tooltip-line-height);
- color: var(--wa-tooltip-content-color);
- opacity: 0;
- padding: 0.25em 0.5em;
- transition: var(--wa-transition-normal) opacity;
+.disabled #thumb,
+.disabled #thumb-min,
+.disabled #thumb-max {
+ cursor: inherit;
+}
+
+.horizontal #thumb,
+.horizontal #thumb-min,
+.horizontal #thumb-max {
+ top: calc(50% - var(--thumb-height) / 2);
+
+ &:dir(ltr) {
+ right: auto;
+ left: calc(var(--position) - var(--thumb-width) / 2);
+ }
+
+ &:dir(rtl) {
+ right: calc(var(--position) - var(--thumb-width) / 2);
+ left: auto;
+ }
+}
+
+.vertical #thumb,
+.vertical #thumb-min,
+.vertical #thumb-max {
+ bottom: calc(var(--position) - var(--thumb-height) / 2);
+ left: calc(50% - var(--thumb-width) / 2);
+}
+
+/* Range-specific thumb styles */
+:host([range]) {
+ #thumb-min:focus-visible,
+ #thumb-max:focus-visible {
+ z-index: 4; /* Ensure focused thumb appears on top */
+ outline: var(--wa-focus-ring);
+ /* intentionally no offset due to border */
+ }
+}
+
+/* Markers */
+#markers {
pointer-events: none;
+}
- &::after {
- content: '';
- position: absolute;
- width: 0;
- height: 0;
- inset-inline-start: 50%;
- inset-block-start: 100%;
- translate: calc(-1 * var(--wa-tooltip-arrow-size));
- border-inline: var(--wa-tooltip-arrow-size) solid transparent;
- border-block-start: var(--border-block);
+.marker {
+ z-index: 2;
+ position: absolute;
+ width: var(--marker-width);
+ height: var(--marker-height);
+ border-radius: 50%;
+ background-color: var(--wa-color-surface-default);
+}
+
+.marker:first-of-type,
+.marker:last-of-type {
+ display: none;
+}
+
+.horizontal .marker {
+ top: calc(50% - var(--marker-height) / 2);
+ left: calc(var(--position) - var(--marker-width) / 2);
+}
+
+.vertical .marker {
+ top: calc(var(--position) - var(--marker-height) / 2);
+ left: calc(50% - var(--marker-width) / 2);
+}
+
+/* Marker labels */
+#references {
+ position: relative;
+
+ slot {
+ display: flex;
+ justify-content: space-between;
+ height: 100%;
}
- &:dir(rtl)::after {
- translate: var(--wa-tooltip-arrow-size);
+ ::slotted(*) {
+ color: var(--wa-color-text-quiet);
+ font-size: 0.875em;
+ line-height: 1;
+ }
+}
+
+.horizontal {
+ #references {
+ margin-block-start: 0.5em;
+ }
+}
+
+.vertical {
+ display: flex;
+ margin-inline: auto;
+
+ #track {
+ order: 1;
}
- &.visible {
- opacity: 1;
- }
+ #references {
+ order: 2;
+ width: min-content;
+ margin-inline-start: 0.75em;
- --inset-block: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
- --border-block: var(--wa-tooltip-arrow-size) solid var(--wa-tooltip-background-color);
-
- @media (forced-colors: active) {
- border: solid 1px transparent;
-
- &::after {
- display: none;
+ slot {
+ flex-direction: column;
}
}
}
-/* RTL tooltip positioning */
-:host(:dir(rtl)) .tooltip {
- inset-inline-start: auto;
- inset-inline-end: 0;
-}
-
-/* Tooltip on bottom */
-:host([tooltip='bottom']) .tooltip {
- inset-block-end: auto;
- inset-block-start: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
-
- &::after {
- border-block-end: var(--border-block);
- inset-block-start: auto;
- inset-block-end: 100%;
- }
-}
-
-/* Bottom tooltip RTL fix */
-:host([tooltip='bottom']:dir(rtl)) .tooltip {
- inset-inline-start: auto;
- inset-inline-end: 0;
+.vertical #references slot {
+ flex-direction: column;
}
diff --git a/packages/webawesome/src/components/slider/slider.test.ts b/packages/webawesome/src/components/slider/slider.test.ts
index 99c92c9c2..3594c26e3 100644
--- a/packages/webawesome/src/components/slider/slider.test.ts
+++ b/packages/webawesome/src/components/slider/slider.test.ts
@@ -21,9 +21,8 @@ describe('', () => {
it('default properties', async () => {
const el = await fixture(html` `);
- expect(el.name).to.equal('');
+ expect(el.name).to.equal(null);
expect(el.value).to.equal(0);
- expect(el.title).to.equal('');
expect(el.label).to.equal('');
expect(el.hint).to.equal('');
expect(el.disabled).to.be.false;
@@ -31,22 +30,16 @@ describe('', () => {
expect(el.min).to.equal(0);
expect(el.max).to.equal(100);
expect(el.step).to.equal(1);
- expect(el.tooltip).to.equal('top');
+ expect(el.tooltipPlacement).to.equal('top');
expect(el.defaultValue).to.equal(0);
});
- it('should have title if title attribute is set', async () => {
- const el = await fixture(html` `);
- const input = el.shadowRoot!.querySelector('input')!;
-
- expect(input.title).to.equal('Test');
- });
-
it('should be disabled with the disabled attribute', async () => {
const el = await fixture(html` `);
- const input = el.shadowRoot!.querySelector('.control')!;
+ const input = el.shadowRoot!.querySelector("[role='slider']")!;
- expect(input.disabled).to.be.true;
+ expect(el.matches(':disabled')).to.be.true;
+ expect(input.getAttribute('aria-disabled')).to.equal('true');
});
describe('when the value changes', () => {
diff --git a/packages/webawesome/src/components/slider/slider.ts b/packages/webawesome/src/components/slider/slider.ts
index 00a724fe2..b0cc5dc4b 100644
--- a/packages/webawesome/src/components/slider/slider.ts
+++ b/packages/webawesome/src/components/slider/slider.ts
@@ -1,24 +1,34 @@
+import type { PropertyValues } from 'lit';
import { html } from 'lit';
-import { customElement, eventOptions, property, query, state } from 'lit/decorators.js';
+import { customElement, property, query, queryAll, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
-import { ifDefined } from 'lit/directives/if-defined.js';
-import { live } from 'lit/directives/live.js';
+import { DraggableElement } from '../../internal/drag.js';
+import { clamp } from '../../internal/math.js';
import { HasSlotController } from '../../internal/slot.js';
-import { MirrorValidator } from '../../internal/validators/mirror-validator.js';
-import { watch } from '../../internal/watch.js';
+import { submitOnEnter } from '../../internal/submit-on-enter.js';
+import { SliderValidator } from '../../internal/validators/slider-validator.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import formControlStyles from '../../styles/component/form-control.css';
+import sizeStyles from '../../styles/utilities/size.css';
import { LocalizeController } from '../../utilities/localize.js';
+import '../tooltip/tooltip.js';
+import type WaTooltip from '../tooltip/tooltip.js';
import styles from './slider.css';
/**
+ *
+ *
* @summary Ranges allow the user to select a single value within a given range using a slider.
* @documentation https://backers.webawesome.com/docs/components/range
* @status stable
* @since 2.0
*
+ * @dependency wa-tooltip
+ *
* @slot label - The slider label. Alternatively, you can use the `label` attribute.
* @slot hint - Text that describes how to use the input. Alternatively, you can use the `hint` attribute.
+ * instead.
+ * @slot reference - One or more reference labels to show visually below the slider.
*
* @event blur - Emitted when the control loses focus.
* @event change - Emitted when an alteration to the control's value is committed by the user.
@@ -26,61 +36,107 @@ import styles from './slider.css';
* @event input - Emitted when the control receives input.
* @event wa-invalid - Emitted when the form control has been checked for validity and its constraints aren't satisfied.
*
- * @csspart form-control - The form control that wraps the label, input, and hint.
- * @csspart form-control-label - The input's label.
- * @csspart form-control-input - The input's wrapper.
- * @csspart hint - The hint's wrapper.
- * @csspart base - The internal `` element.
- * @csspart tooltip - The slider tooltip.
+ * @csspart label - The element that contains the sliders's label.
+ * @csspart hint - The element that contains the slider's description.
+ * @csspart slider - The focusable element with `role="slider"`. Contains the track and reference slot.
+ * @csspart track - The slider's track.
+ * @csspart indicator - The colored indicator that shows from the start of the slider to the current value.
+ * @csspart markers - The container that holds all the markers when `with-markers` is used.
+ * @csspart marker - The individual markers that are shown when `with-markers` is used.
+ * @csspart references - The container that holds references that get slotted in.
+ * @csspart thumb - The slider's thumb.
+ * @csspart thumb-min - The min value thumb in a range slider.
+ * @csspart thumb-max - The max value thumb in a range slider.
+ * @csspart tooltip - The tooltip, a `` element.
+ * @csspart tooltip__tooltip - The tooltip's `tooltip` part.
+ * @csspart tooltip__content - The tooltip's `content` part.
+ * @csspart tooltip__arrow - The tooltip's `arrow` part.
*
- * @cssproperty --thumb-color - The color of the thumb.
- * @cssproperty --thumb-gap - The visual gap between the edges of the thumb and the track.
- * @cssproperty --thumb-shadow - The shadow effects around the edges of the thumb.
- * @cssproperty --thumb-size - The size of the thumb.
- * @cssproperty --tooltip-offset - The vertical distance the tooltip is offset from the thumb.
- * @cssproperty --track-color-active - The color of the portion of the track that represents the current value.
- * @cssproperty --track-color-inactive - The of the portion of the track that represents the remaining value.
- * @cssproperty --track-height - The height of the track.
- * @cssproperty --track-active-offset - The point of origin of the active track.
+ * @cssstate disabled - Applied when the slider is disabled.
+ * @cssstate dragging - Applied when the slider is being dragged.
+ * @cssstate focused - Applied when the slider has focus.
+ * @cssstate user-valid - Applied when the slider is valid and the user has sufficiently interacted with it.
+ * @cssstate user-invalid - Applied when the slider is invalid and the user has sufficiently interacted with it.
+ *
+ * @cssproperty [--track-size=0.75em] - The height or width of the slider's track.
+ * @cssproperty [--marker-width=0.1875em] - The width of each individual marker.
+ * @cssproperty [--marker-height=0.1875em] - The height of each individual marker.
+ * @cssproperty [--thumb-width=1.25em] - The width of the thumb.
+ * @cssproperty [--thumb-height=1.25em] - The height of the thumb.
*/
@customElement('wa-slider')
export default class WaSlider extends WebAwesomeFormAssociatedElement {
- static css = [formControlStyles, styles];
+ static formAssociated = true;
+ static observeSlots = true;
+ static css = [sizeStyles, formControlStyles, styles];
static get validators() {
- return [...super.validators, MirrorValidator()];
+ return [...super.validators, SliderValidator()];
}
+ private draggableTrack: DraggableElement;
+ private draggableThumbMin: DraggableElement | null = null;
+ private draggableThumbMax: DraggableElement | null = null;
private readonly hasSlotController = new HasSlotController(this, 'hint', 'label');
private readonly localize = new LocalizeController(this);
- private resizeObserver: ResizeObserver;
+ private trackBoundingClientRect: DOMRect;
+ private valueWhenDraggingStarted: number | undefined | null;
+ private activeThumb: 'min' | 'max' | null = null;
+ private lastTrackPosition: number | null = null; // Track last position for direction detection
- @query('.control') input: HTMLInputElement;
- @query('.tooltip') output: HTMLOutputElement | null;
+ protected get focusableAnchor() {
+ return this.isRange ? this.thumbMin || this.slider : this.slider;
+ }
- @state() private hasTooltip = false;
- @property() title = ''; // make reactive to pass through
+ /** Override validation target to point to the focusable element */
+ get validationTarget() {
+ return this.focusableAnchor;
+ }
- /** The name of the slider, submitted as a name/value pair with form data. */
- @property() name: string = '';
+ @query('#slider') slider: HTMLElement;
+ @query('#thumb') thumb: HTMLElement;
+ @query('#thumb-min') thumbMin: HTMLElement;
+ @query('#thumb-max') thumbMax: HTMLElement;
+ @query('#track') track: HTMLElement;
+ @query('#tooltip') tooltip: WaTooltip;
+ @queryAll('wa-tooltip') tooltips: NodeListOf;
+
+ /**
+ * The slider's label. If you need to provide HTML in the label, use the `label` slot instead.
+ */
+ @property() label: string = '';
+
+ /** The slider hint. If you need to display HTML, use the hint slot instead. */
+ @property({ attribute: 'hint' }) hint = '';
+
+ /** The name of the slider. This will be submitted with the form as a name/value pair. */
+ @property({ reflect: true }) name: string;
+
+ /** The minimum value of a range selection. Used only when range attribute is set. */
+ @property({ type: Number, attribute: 'min-value' }) minValue = 0;
+
+ /** The maximum value of a range selection. Used only when range attribute is set. */
+ @property({ type: Number, attribute: 'max-value' }) maxValue = 50;
/** The default value of the form control. Primarily used for resetting the form control. */
- @property({ type: Number, attribute: 'value', reflect: true }) defaultValue: number =
- Number(this.getAttribute('value')) || 0;
+ @property({ attribute: 'value', reflect: true, type: Number }) defaultValue: number =
+ this.getAttribute('value') == null ? this.minValue : Number(this.getAttribute('value'));
- private _value: number | null = null;
+ private _value: number = this.defaultValue;
/** The current value of the slider, submitted as a name/value pair with form data. */
get value(): number {
if (this.valueHasChanged) {
- return this._value || 0;
+ return this._value;
}
- return this._value ?? (this.defaultValue || 0);
+ return this._value ?? this.defaultValue;
}
@state()
set value(val: number | null) {
+ val = Number(val) ?? this.minValue;
+
if (this._value === val) {
return;
}
@@ -89,234 +145,817 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
this._value = val;
}
- /** The slider label. If you need to display HTML, use the `label` slot instead. */
- @property() label = '';
+ /** Converts the slider to a range slider with two thumbs. */
+ @property({ type: Boolean, reflect: true }) range = false;
- /** The slider hint. If you need to display HTML, use the hint slot instead. */
- @property({ attribute: 'hint' }) hint = '';
+ /** Get if this is a range slider */
+ get isRange(): boolean {
+ return this.range;
+ }
/** Disables the slider. */
@property({ type: Boolean }) disabled = false;
- /** The minimum acceptable value of the slider. */
- @property({ type: Number }) min = 0;
+ /** Makes the slider a read-only field. */
+ @property({ type: Boolean, reflect: true }) readonly = false;
- /** The maximum acceptable value of the slider. */
- @property({ type: Number }) max = 100;
+ /** The orientation of the slider. */
+ @property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
- /** The interval at which the slider will increase and decrease. */
- @property({ type: Number }) step = 1;
+ /** The slider's size. */
+ @property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
- /** The preferred placement of the slider tooltip. */
- @property() tooltip: 'top' | 'bottom' | 'none' = 'top';
+ /** The starting value from which to draw the slider's fill, which is based on its current value. */
+ @property({ attribute: 'indicator-offset', type: Number }) indicatorOffset: number;
/**
- * A function used to format the tooltip's value. The slider value is passed as the first and only argument. The
- * function should return a string to display in the tooltip.
+ * The form to associate this control with. If omitted, the closest containing `