diff --git a/src/components/calcite-slider/calcite-slider.e2e.ts b/src/components/calcite-slider/calcite-slider.e2e.ts
index e2c099d2109..12491f577f7 100644
--- a/src/components/calcite-slider/calcite-slider.e2e.ts
+++ b/src/components/calcite-slider/calcite-slider.e2e.ts
@@ -48,8 +48,8 @@ describe("calcite-slider", () => {
>
`);
- const maxButton = await page.find("calcite-slider >>> .thumb--max");
- const minButton = await page.find("calcite-slider >>> .thumb--min");
+ const maxButton = await page.find("calcite-slider >>> .thumb--value");
+ const minButton = await page.find("calcite-slider >>> .thumb--minValue");
expect(minButton).toEqualAttribute("role", "slider");
expect(maxButton).toEqualAttribute("role", "slider");
expect(minButton).toEqualAttribute("aria-label", "Min Label");
@@ -112,7 +112,7 @@ describe("calcite-slider", () => {
`);
const slider = await page.find("calcite-slider");
- const handle = await page.find("calcite-slider >>> .thumb--max");
+ const handle = await page.find("calcite-slider >>> .thumb--value");
await page.waitForChanges();
let value = await slider.getProperty("value");
expect(value).toBe(20);
diff --git a/src/components/calcite-slider/calcite-slider.scss b/src/components/calcite-slider/calcite-slider.scss
index 0aa119fd9cb..b2a95dc4de0 100644
--- a/src/components/calcite-slider/calcite-slider.scss
+++ b/src/components/calcite-slider/calcite-slider.scss
@@ -9,6 +9,15 @@ $thumb-padding: ($thumb-size - $handle-size) / 2;
$track-height: 2px;
$tick-height: 4px;
+@mixin histogramEndcaps() {
+ .tick__label--min,
+ .tick__label--max {
+ margin: 6px -3px;
+ font-weight: 300;
+ color: var(--calcite-ui-text-3);
+ }
+}
+
:host {
display: block;
padding: $handle-size / 2 0;
@@ -26,76 +35,189 @@ $tick-height: 4px;
* with text elements to prevent overlap
*/
:host([label-handles]),
-:host([precise]) {
+:host([precise]:not([precise="false"])) {
margin-top: $handle-size + $thumb-padding;
}
:host([label-ticks]),
-:host([precise][is-range]) {
+:host([precise]:not([precise="false"])[is-range]) {
margin-bottom: $handle-size + $thumb-padding;
}
-:host([precise][label-handles]) {
+:host([precise]:not([precise="false"])[label-handles]) {
margin-top: $thumb-size + $thumb-padding;
}
-:host([precise][label-handles][is-range]) {
+:host([precise]:not([precise="false"])[label-handles][is-range]) {
margin-bottom: $thumb-size + $thumb-padding;
}
-// focus styles
-.thumb {
- @include focus-style-base();
- &:focus {
- @include focus-style-outset();
- }
-}
-
.thumb {
position: absolute;
- height: $thumb-size;
- width: $thumb-size;
- margin: -15px;
- box-sizing: border-box;
border: none;
background: transparent;
cursor: pointer;
font-family: inherit;
z-index: 2;
-}
+ outline: none;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ transform: translate(7px, -8px);
-.handle {
- position: absolute;
- top: 0;
- left: 0;
- height: $handle-size;
- width: $handle-size;
- margin: $thumb-padding;
- box-sizing: border-box;
- border-radius: 100%;
- background-color: var(--calcite-ui-foreground-1);
- border: 2px solid var(--calcite-ui-text-3);
- transition: border 0.25s ease, background-color 0.25s ease,
- box-shadow 0.25s ease;
-}
-
-.handle__label {
- position: absolute;
- left: 0;
- bottom: $thumb-size;
- width: $thumb-size;
- height: 0.75em;
- @include font-size(-3);
- font-weight: 500;
- line-height: 1;
- color: var(--calcite-ui-text-3);
- text-align: center;
-}
+ .handle__label {
+ transition: transform 150ms;
+ @include font-size(-4);
+ font-weight: 500;
+ line-height: 1;
+ color: var(--calcite-ui-text-2);
+ margin-bottom: 5px;
+ &.static,
+ &.transformed {
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ }
+ &--minValue.hyphen::after {
+ content: "\2014";
+ display: inline-block;
+ width: 1em;
+ }
+ &--value.hyphen::before {
+ content: "\2014";
+ display: inline-block;
+ width: 1em;
+ }
+ }
+
+ .handle {
+ @include focus-style-base();
+ height: $handle-size;
+ width: $handle-size;
+ box-sizing: border-box;
+ border-radius: 100%;
+ background-color: var(--calcite-ui-foreground-1);
+ box-shadow: 0 0 0 2px var(--calcite-ui-text-3) inset;
+ transition: border 0.25s ease, background-color 0.25s ease,
+ box-shadow 0.25s ease;
+ }
+
+ .handle-extension {
+ width: 2px;
+ height: $thumb-padding;
+ background-color: var(--calcite-ui-text-3);
+ }
-.thumb:hover .handle {
- border-width: 3px;
- border-color: var(--calcite-ui-blue-1);
- @include shadow(1, "hover");
+ &:hover {
+ .handle {
+ box-shadow: 0 0 0 3px var(--calcite-ui-blue-1) inset;
+ }
+ .handle-extension {
+ background-color: var(--calcite-ui-blue-1);
+ }
+ }
+
+ &:focus {
+ .handle {
+ @include focus-style-outset();
+ outline-offset: 2px;
+ }
+ .handle-extension {
+ background-color: var(--calcite-ui-blue-1);
+ }
+ }
+}
+.thumb--minValue {
+ transform: translate(-7px, -8px);
+}
+:host([label-handles]) {
+ .thumb {
+ transform: translate(50%, -25px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -25px);
+ }
+}
+:host([has-histogram][label-handles]) {
+ .thumb {
+ transform: translate(50%, -8px);
+ .handle__label {
+ margin-bottom: unset;
+ margin-top: 5px;
+ }
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -8px);
+ }
+}
+:host([precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(7px, -21px);
+ }
+ .thumb--minValue {
+ transform: translate(-7px, -2px);
+ .handle__label {
+ margin-bottom: unset;
+ margin-top: 5px;
+ }
+ }
+}
+:host([has-histogram][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(7px, -2px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -2px);
+ }
+}
+:host([ticks][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(7px, -20px);
+ }
+ .thumb--minValue {
+ transform: translate(-7px, -3px);
+ }
+}
+:host([has-histogram][ticks][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(7px, -3px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -3px);
+ }
+}
+:host([label-handles][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(50%, -38px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -2px);
+ }
+}
+:host([has-histogram][label-handles][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(50%, -2px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -2px);
+ }
+}
+:host([ticks][label-handles][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(50%, -37px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -3px);
+ }
+}
+:host([has-histogram][ticks][label-handles][precise]:not([precise="false"])) {
+ .thumb {
+ transform: translate(50%, -3px);
+ }
+ .thumb--minValue {
+ transform: translate(-50%, -3px);
+ }
}
.thumb:focus,
@@ -103,48 +225,16 @@ $tick-height: 4px;
z-index: 3;
.handle {
background-color: var(--calcite-ui-blue-1);
- border-color: var(--calcite-ui-blue-1);
@include shadow(1, "press");
}
}
-.thumb--precise {
- margin-top: -$thumb-size;
-}
-
-.thumb--precise:after {
- content: "";
- display: block;
- position: absolute;
- top: $handle-size;
- left: 50%;
- width: 2px;
- height: $thumb-padding;
- background-color: var(--calcite-ui-text-3);
- margin-left: -1px;
- margin-top: $thumb-padding;
- z-index: 1;
-}
-
.thumb:hover.thumb--precise:after,
.thumb:focus.thumb--precise:after,
.thumb--active.thumb--precise:after {
background-color: var(--calcite-ui-blue-1);
}
-.thumb--precise.thumb--min {
- margin-top: -$track-height;
- .handle__label {
- bottom: unset;
- top: $thumb-size;
- }
-}
-
-.thumb--precise.thumb--min:after {
- top: 0;
- margin-top: 0;
-}
-
.track {
height: $track-height;
border-radius: 0;
@@ -181,10 +271,8 @@ $tick-height: 4px;
width: 2px;
height: $tick-height;
left: var(--calcite-ui-border-1-offset);
- margin-left: -3px;
+ margin-left: -2px;
border: 1px solid var(--calcite-ui-foreground-1);
- border-right-width: 2px;
- border-left-width: 2px;
background-color: var(--calcite-ui-border-1);
}
@@ -194,9 +282,9 @@ $tick-height: 4px;
.tick__label {
position: absolute;
- @include font-size(-3);
+ @include font-size(-4);
font-weight: 500;
- color: var(--calcite-ui-text-3);
+ color: var(--calcite-ui-text-2);
width: 4em;
margin: $thumb-size / 2 -2em;
text-align: center;
@@ -208,6 +296,7 @@ $tick-height: 4px;
left: 0;
margin: $thumb-size / 2 -3px;
text-align: left;
+ transition: opacity 150ms;
}
.tick__label--max {
@@ -215,6 +304,14 @@ $tick-height: 4px;
right: 0;
margin: $thumb-size / 2 -3px;
text-align: right;
+ transition: opacity 50ms;
+}
+
+:host([has-histogram][label-handles]) {
+ @include histogramEndcaps();
+}
+:host([has-histogram][precise]:not([precise="false"])) {
+ @include histogramEndcaps();
}
.graph {
diff --git a/src/components/calcite-slider/calcite-slider.tsx b/src/components/calcite-slider/calcite-slider.tsx
index 3096ef4a1aa..cbf071b39b4 100644
--- a/src/components/calcite-slider/calcite-slider.tsx
+++ b/src/components/calcite-slider/calcite-slider.tsx
@@ -10,12 +10,14 @@ import {
h,
State,
VNode,
+ Watch,
} from "@stencil/core";
import { guid } from "../../utils/guid";
import { getKey } from "../../utils/key";
-type activeSliderProperty = "minValue" | "maxValue" | "value" | "minMaxValue";
import { DataSeries } from "../../interfaces/Graph";
+type activeSliderProperty = "minValue" | "maxValue" | "value" | "minMaxValue";
+
@Component({
tag: "calcite-slider",
styleUrl: "calcite-slider.scss",
@@ -68,6 +70,11 @@ export class CalciteSlider {
@Prop() precise?: boolean;
/** Display a histogram above the slider */
@Prop() histogram?: DataSeries;
+ @Watch("histogram") histogramWatcher(newHistogram) {
+ this.hasHistogram = newHistogram ? true : false;
+ }
+ /** Indicates if a histogram is present */
+ @Prop({ reflect: true, mutable: true }) hasHistogram: boolean = false;
//--------------------------------------------------------------------------
//
// Lifecycle
@@ -80,17 +87,456 @@ export class CalciteSlider {
if (this.snap) {
this.value = this.getClosestStep(this.value);
}
+ if (this.histogram) {
+ this.hasHistogram = true;
+ }
this.calciteSliderUpdate.emit();
}
+ componentDidRender() {
+ const valueOffset = this.adjustObscuredEndcapLabel("value");
+ if (this.isRange && this.labelHandles) {
+ const minValueOffset = this.adjustObscuredEndcapLabel("minValue");
+ if (!(this.precise && this.isRange && !this.hasHistogram)) {
+ this.hyphenateCollidingRangeHandleLabels(valueOffset, minValueOffset);
+ }
+ }
+ this.hideObscuredBoundingTickLabels();
+ }
+
render() {
const id = this.el.id || this.guid;
const min = this.minValue || this.min;
const max = this.maxValue || this.value;
const maxProp = this.isRange ? "maxValue" : "value";
+ const value = this[maxProp];
const left = `${this.getUnitInterval(min) * 100}%`;
const right = `${100 - this.getUnitInterval(max) * 100}%`;
+ const handle = (
+
+ );
+
+ const labeledHandle = (
+
+ );
+
+ const histogramLabeledHandle = (
+
+ );
+
+ const preciseHandle = (
+
+ );
+
+ const histogramPreciseHandle = (
+
+ );
+
+ const labeledPreciseHandle = (
+
+ );
+
+ const histogramLabeledPreciseHandle = (
+
+ );
+
+ const minHandle = (
+
+ );
+
+ const minLabeledHandle = (
+
+ );
+
+ const minHistogramLabeledHandle = (
+
+ );
+
+ const minPreciseHandle = (
+
+ );
+
+ const minLabeledPreciseHandle = (
+
+ );
+
return (
{this.renderGraph()}
@@ -102,98 +548,63 @@ export class CalciteSlider {
style={{ left, right }}
/>
- {this.tickValues.map((number) => (
+ {this.tickValues.map((tick) => (
= min && number <= max,
+ "tick--active": tick >= min && tick <= max,
}}
style={{
- left: `${this.getUnitInterval(number) * 100}%`,
+ left: `${this.getUnitInterval(tick) * 100}%`,
}}
>
- {this.labelTicks ? (
-
- {number}
-
- ) : (
- ""
- )}
+ {this.renderTickLabel(tick)}
))}
- {this.isRange ? (
-
- ) : (
- ""
- )}
-
+ {!this.precise && !this.labelHandles && this.isRange && minHandle}
+ {!this.hasHistogram &&
+ !this.precise &&
+ this.labelHandles &&
+ this.isRange &&
+ minLabeledHandle}
+ {this.precise && !this.labelHandles && this.isRange && minPreciseHandle}
+ {this.precise &&
+ this.labelHandles &&
+ this.isRange &&
+ minLabeledPreciseHandle}
+ {this.hasHistogram &&
+ !this.precise &&
+ this.labelHandles &&
+ this.isRange &&
+ minHistogramLabeledHandle}
+
+ {!this.precise && !this.labelHandles && handle}
+ {!this.hasHistogram &&
+ !this.precise &&
+ this.labelHandles &&
+ labeledHandle}
+ {!this.hasHistogram &&
+ this.precise &&
+ !this.labelHandles &&
+ preciseHandle}
+ {this.hasHistogram &&
+ this.precise &&
+ !this.labelHandles &&
+ histogramPreciseHandle}
+ {!this.hasHistogram &&
+ this.precise &&
+ this.labelHandles &&
+ labeledPreciseHandle}
+ {this.hasHistogram &&
+ !this.precise &&
+ this.labelHandles &&
+ histogramLabeledHandle}
+ {this.hasHistogram &&
+ this.precise &&
+ this.labelHandles &&
+ histogramLabeledPreciseHandle}
);
}
@@ -205,12 +616,95 @@ export class CalciteSlider {
width={300}
height={48}
data={this.histogram}
- highlightMin={this.isRange ? this.minValue : null}
- highlightMax={this.isRange ? this.maxValue : null}
+ highlightMin={this.isRange ? this.minValue : this.min}
+ highlightMax={this.isRange ? this.maxValue : this.value}
/>
) : null;
}
+
+ private renderTickLabel(tick: number): VNode {
+ const isMinTickLabel = tick === this.min;
+ const isMaxTickLabel = tick === this.max;
+ const tickLabel = (
+
+ {tick.toLocaleString()}
+
+ );
+ if (this.labelTicks && !this.hasHistogram && !this.isRange) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ !this.hasHistogram &&
+ this.isRange &&
+ !this.precise &&
+ !this.labelHandles
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ !this.hasHistogram &&
+ this.isRange &&
+ !this.precise &&
+ this.labelHandles
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ !this.hasHistogram &&
+ this.isRange &&
+ this.precise &&
+ (isMinTickLabel || isMaxTickLabel)
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ this.hasHistogram &&
+ !this.precise &&
+ !this.labelHandles
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ this.hasHistogram &&
+ this.precise &&
+ !this.labelHandles &&
+ (isMinTickLabel || isMaxTickLabel)
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ this.hasHistogram &&
+ !this.precise &&
+ this.labelHandles &&
+ (isMinTickLabel || isMaxTickLabel)
+ ) {
+ return tickLabel;
+ }
+ if (
+ this.labelTicks &&
+ this.hasHistogram &&
+ this.precise &&
+ this.labelHandles &&
+ (isMinTickLabel || isMaxTickLabel)
+ ) {
+ return tickLabel;
+ }
+ return null;
+ }
+
//--------------------------------------------------------------------------
//
// Event Listeners
@@ -462,6 +956,14 @@ export class CalciteSlider {
}
return num;
}
+ private getFontSizeForElement(element: HTMLElement) {
+ return Number(
+ window
+ .getComputedStyle(element)
+ .getPropertyValue("font-size")
+ .match(/\d+/)[0]
+ );
+ }
/**
* Get position of value along range as fractional value
* @return {number} number in the unit interval [0,1]
@@ -472,4 +974,295 @@ export class CalciteSlider {
const range = this.max - this.min;
return (num - this.min) / range;
}
+ private adjustObscuredEndcapLabel(name: "value" | "minValue"): number {
+ const handle: HTMLButtonElement | null = this.el.shadowRoot.querySelector(
+ `.thumb--${name}`
+ );
+ const label: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--${name}`
+ );
+ const labelStatic: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--${name}.static`
+ );
+ const labelTransformed: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--${name}.transformed`
+ );
+ if (handle && label && labelStatic && labelTransformed) {
+ const labelOffset = this.getLabelOffset(handle, labelStatic);
+ if (labelOffset) {
+ (label as HTMLSpanElement).style.transform = `translateX(${labelOffset}px)`;
+ (labelTransformed as HTMLSpanElement).style.transform = `translateX(${labelOffset}px)`;
+ } else {
+ (label as HTMLSpanElement).style.transform = "";
+ (labelTransformed as HTMLSpanElement).style.transform = "";
+ }
+ return Math.abs(labelOffset);
+ }
+ return 0;
+ }
+ private hyphenateCollidingRangeHandleLabels(
+ valueLabelOffset: number,
+ minValueLabelOffset: number
+ ) {
+ const minValueLabel: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--minValue`
+ );
+ const minValueLabelStatic: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--minValue.static`
+ );
+ const minValueLabelTransformed: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--minValue.transformed`
+ );
+ const valueLabel: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--value`
+ );
+ const valueLabelStatic: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--value.static`
+ );
+ const valueLabelTransformed: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ `.handle__label--value.transformed`
+ );
+ const labelFontSize = this.getFontSizeForElement(minValueLabel);
+ if (
+ minValueLabel &&
+ valueLabel &&
+ minValueLabelStatic &&
+ valueLabelStatic &&
+ minValueLabelTransformed &&
+ valueLabelTransformed
+ ) {
+ if (
+ valueLabelOffset > 0 ||
+ valueLabel.getBoundingClientRect().right >
+ this.el.getBoundingClientRect().right
+ ) {
+ const valueLabelTransformedOverlap = this.getRangeLabelOverlap(
+ minValueLabelStatic,
+ valueLabelTransformed
+ );
+ if (valueLabelTransformedOverlap > 0) {
+ minValueLabel.classList.add("hyphen");
+ minValueLabel.classList.add("max-offset");
+ minValueLabel.style.marginRight = `${
+ valueLabelTransformedOverlap * 2 - labelFontSize
+ }px`;
+ } else {
+ minValueLabel.classList.remove("hyphen");
+ minValueLabel.classList.remove("max-offset");
+ minValueLabel.style.marginRight = "0px";
+ }
+ valueLabel.style.marginLeft = "0px";
+ } else if (
+ minValueLabelOffset > 0 ||
+ minValueLabel.getBoundingClientRect().left <
+ this.el.getBoundingClientRect().left
+ ) {
+ const minValueLabelTransformedOverlap = this.getRangeLabelOverlap(
+ minValueLabelTransformed,
+ valueLabelStatic
+ );
+ if (minValueLabelTransformedOverlap > 0) {
+ valueLabel.classList.add("hyphen");
+ valueLabel.classList.add("min-offset");
+ valueLabel.style.marginLeft = `${
+ minValueLabelTransformedOverlap * 2 - labelFontSize
+ }px`;
+ } else {
+ valueLabel.classList.remove("hyphen");
+ valueLabel.classList.remove("min-offset");
+ valueLabel.style.marginLeft = "0px";
+ }
+ minValueLabel.style.marginRight = "0px";
+ } else {
+ const labelStaticOverlap = this.getRangeLabelOverlap(
+ minValueLabelStatic,
+ valueLabelStatic
+ );
+ if (labelStaticOverlap > 0) {
+ minValueLabel.classList.add("hyphen");
+ valueLabel.classList.add("hyphen");
+ minValueLabel.style.marginRight = `${
+ labelStaticOverlap - labelFontSize
+ }px`;
+ valueLabel.style.marginLeft = `${
+ labelStaticOverlap - labelFontSize
+ }px`;
+ } else {
+ minValueLabel.classList.remove("hyphen");
+ valueLabel.classList.remove("hyphen");
+ minValueLabel.style.marginRight = "0px";
+ valueLabel.style.marginLeft = "0px";
+ }
+ }
+ }
+ }
+ /**
+ * Hides bounding tick labels that are obscured by either handle.
+ */
+ private hideObscuredBoundingTickLabels() {
+ if (
+ !this.hasHistogram &&
+ !this.isRange &&
+ !this.labelHandles &&
+ !this.precise
+ ) {
+ return;
+ }
+ if (
+ !this.hasHistogram &&
+ !this.isRange &&
+ this.labelHandles &&
+ !this.precise
+ ) {
+ return;
+ }
+ if (
+ !this.hasHistogram &&
+ !this.isRange &&
+ !this.labelHandles &&
+ this.precise
+ ) {
+ return;
+ }
+ if (
+ !this.hasHistogram &&
+ !this.isRange &&
+ this.labelHandles &&
+ this.precise
+ ) {
+ return;
+ }
+ if (!this.hasHistogram && this.isRange && !this.precise) {
+ return;
+ }
+ if (this.hasHistogram && !this.precise && !this.labelHandles) {
+ return;
+ }
+
+ const minHandle: HTMLButtonElement | null = this.el.shadowRoot.querySelector(
+ ".thumb--minValue"
+ );
+ const maxHandle: HTMLButtonElement | null = this.el.shadowRoot.querySelector(
+ ".thumb--value"
+ );
+
+ const minTickLabel: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ ".tick__label--min"
+ );
+ const maxTickLabel: HTMLSpanElement | null = this.el.shadowRoot.querySelector(
+ ".tick__label--max"
+ );
+
+ if (!minHandle && maxHandle && minTickLabel && maxTickLabel) {
+ if (this.isMinTickLabelObscured(minTickLabel, maxHandle)) {
+ minTickLabel.style.opacity = "0";
+ } else {
+ minTickLabel.style.opacity = "1";
+ }
+ if (this.isMaxTickLabelObscured(maxTickLabel, maxHandle)) {
+ maxTickLabel.style.opacity = "0";
+ } else {
+ maxTickLabel.style.opacity = "1";
+ }
+ }
+
+ if (minHandle && maxHandle && minTickLabel && maxTickLabel) {
+ if (
+ this.isMinTickLabelObscured(minTickLabel, minHandle) ||
+ this.isMinTickLabelObscured(minTickLabel, maxHandle)
+ ) {
+ minTickLabel.style.opacity = "0";
+ } else {
+ minTickLabel.style.opacity = "1";
+ }
+ if (
+ this.isMaxTickLabelObscured(maxTickLabel, minHandle) ||
+ (this.isMaxTickLabelObscured(maxTickLabel, maxHandle) &&
+ this.hasHistogram)
+ ) {
+ maxTickLabel.style.opacity = "0";
+ } else {
+ maxTickLabel.style.opacity = "1";
+ }
+ }
+ }
+ /**
+ * Returns an integer representing the number of pixels to offset handle labels based on desired position behavior.
+ * @internal
+ */
+ private getLabelOffset(
+ handle: HTMLButtonElement,
+ label: HTMLSpanElement
+ ): number {
+ const handleBounds = handle.getBoundingClientRect();
+ const labelBounds = label.getBoundingClientRect();
+ const hostBounds = this.el.getBoundingClientRect();
+ if (
+ handleBounds.left < labelBounds.left ||
+ handleBounds.right > labelBounds.right
+ ) {
+ return 0;
+ }
+ if (labelBounds.left < hostBounds.left) {
+ const offset = Math.floor(hostBounds.left - labelBounds.left - 7);
+ return offset;
+ }
+ if (
+ labelBounds.right > hostBounds.right ||
+ labelBounds.right > handleBounds.right
+ ) {
+ const offset = Math.floor(-(labelBounds.right - hostBounds.right) + 7);
+ return offset;
+ }
+ return 0;
+ }
+ /**
+ * Returns an integer representing the number of pixels that the two given span elements are overlapping, taking into account
+ * a space in between the two spans equal to the font-size set on them to account for the space needed to render a hyphen.
+ * @param minValueLabel
+ * @param valueLabel
+ */
+ private getRangeLabelOverlap(
+ minValueLabel: HTMLSpanElement,
+ valueLabel: HTMLSpanElement
+ ): number {
+ const minValueLabelBounds = minValueLabel.getBoundingClientRect();
+ const valueLabelBounds = valueLabel.getBoundingClientRect();
+ const minValueLabelFontSize = this.getFontSizeForElement(minValueLabel);
+ const rangeLabelOverlap =
+ minValueLabelBounds.right + minValueLabelFontSize - valueLabelBounds.left;
+ return rangeLabelOverlap > 0 ? rangeLabelOverlap : 0;
+ }
+ /**
+ * Returns a boolean value representing if the minLabel span element is obscured (being overlapped) by the given handle button element.
+ * @param minLabel
+ * @param handle
+ */
+ private isMinTickLabelObscured(
+ minLabel: HTMLSpanElement,
+ handle: HTMLButtonElement
+ ): boolean {
+ const minLabelBounds = minLabel.getBoundingClientRect();
+ const handleBounds = handle.getBoundingClientRect();
+ if (handleBounds.left < minLabelBounds.right) {
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Returns a boolean value representing if the maxLabel span element is obscured (being overlapped) by the given handle button element.
+ * @param maxLabel
+ * @param handle
+ */
+ private isMaxTickLabelObscured(
+ maxLabel: HTMLSpanElement,
+ handle: HTMLButtonElement
+ ): boolean {
+ const maxLabelBounds = maxLabel.getBoundingClientRect();
+ const handleBounds = handle.getBoundingClientRect();
+ if (handleBounds.right > maxLabelBounds.left) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/demos/calcite-radio-button.html b/src/demos/calcite-radio-button.html
index 3f9a5d0f4ae..9503d87c70d 100644
--- a/src/demos/calcite-radio-button.html
+++ b/src/demos/calcite-radio-button.html
@@ -65,10 +65,13 @@
Scale s
- Stencil
- React
+ Stencil
+
+ React
+
Ember
- Angular
+ Angular
+
Scale m (default)
Stencil
@@ -91,10 +94,13 @@ Scale l
- Stencil
- React
+ Stencil
+
+ React
+
Ember
- Angular
+ Angular
+
Uses value as label if missing
@@ -121,8 +127,10 @@ Only one checked value (first wins, first two checked)
-
-
+
+
+
+
Only one checked value (first wins, second two checked)
@@ -131,34 +139,44 @@ Only one checked value (first wins, second two checked)
-
-
+
+
+
+
Only one checked value (first wins, first and last checked)
-
-
-
+
+
+
+
+
+
Only one checked value (first wins, all checked)
-
-
-
+
+
+
+
+
+
-
-
+
+
Scale s
- Stencil
+ Stencil
+
React
Ember
- Angular
+ Angular
+
@@ -166,100 +184,142 @@ Scale s
Stencil
React
- Ember
- Angular
+ Ember
+
+ Angular
+
Scale m (default)
- Stencil
+ Stencil
+
React
Ember
Angular
- Stencil
- React
- Ember
- Angular
+ Stencil
+
+ React
+
+ Ember
+
+ Angular
+
Scale l
- Stencil
- React
- Ember
- Angular
+ Stencil
+
+ React
+ Ember
+ Angular
+
-
+
- Stencil
- React
- Ember
- Angular
+
+ Stencil
+
+ React
+ Ember
+
+ Angular
+
Uses value as label if missing
-
-
-
+
+
+
-
+
-
-
-
+
+
+
+
+
+
None checked
-
-
-
+
+
+
-
+
-
-
-
+
+
+
+
+
+
Only one checked value (first wins, first two checked)
-
-
-
+
+
+
+
+
-
+
-
-
-
+
+
+
+
+
Only one checked value (first wins, second two checked)
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
+
+
Only one checked value (first wins, first and last checked)
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
Only one checked value (first wins, all checked)
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
+
+
diff --git a/src/demos/calcite-slider.html b/src/demos/calcite-slider.html
index 2ff9fb90187..e85c0e5fa4e 100644
--- a/src/demos/calcite-slider.html
+++ b/src/demos/calcite-slider.html
@@ -7,11 +7,24 @@
Calcite Slider