diff --git a/packages/angular-test-app/src/app/app-routing.module.ts b/packages/angular-test-app/src/app/app-routing.module.ts index 4aa62880f9e..b41a3582484 100644 --- a/packages/angular-test-app/src/app/app-routing.module.ts +++ b/packages/angular-test-app/src/app/app-routing.module.ts @@ -86,6 +86,10 @@ import SelectEditable from 'src/preview-examples/select-editable'; import SelectMultiple from 'src/preview-examples/select-multiple'; import SelectNgModel from 'src/preview-examples/select-ng-model'; import Settings from 'src/preview-examples/settings'; +import Slider from 'src/preview-examples/slider'; +import SliderError from 'src/preview-examples/slider-error'; +import SliderMarker from 'src/preview-examples/slider-marker'; +import SliderTrace from 'src/preview-examples/slider-trace'; import Spinner from 'src/preview-examples/spinner'; import SpinnerLarge from 'src/preview-examples/spinner-large'; import SplitButton from 'src/preview-examples/split-button'; @@ -362,6 +366,10 @@ const routes: Routes = [ { path: 'key-value-list-with-icon', component: KeyValueListWithIcon }, { path: 'key-value-list-striped', component: KeyValueListStriped }, { path: 'menu-category', component: MenuCategory }, + { path: 'slider', component: Slider }, + { path: 'slider-trace', component: SliderTrace }, + { path: 'slider-marker', component: SliderMarker }, + { path: 'slider-error', component: SliderError }, { path: 'grid', component: Grid }, { path: 'grid-size', component: GridSize }, { path: 'grid-padding', component: GridPadding }, diff --git a/packages/angular-test-app/src/app/app.module.ts b/packages/angular-test-app/src/app/app.module.ts index 13374930e36..6afd1850bd7 100644 --- a/packages/angular-test-app/src/app/app.module.ts +++ b/packages/angular-test-app/src/app/app.module.ts @@ -94,6 +94,10 @@ import SelectEditable from 'src/preview-examples/select-editable'; import SelectMultiple from 'src/preview-examples/select-multiple'; import SelectNgModel from 'src/preview-examples/select-ng-model'; import Settings from 'src/preview-examples/settings'; +import Slider from 'src/preview-examples/slider'; +import SliderError from 'src/preview-examples/slider-error'; +import SliderMarker from 'src/preview-examples/slider-marker'; +import SliderTrace from 'src/preview-examples/slider-trace'; import Spinner from 'src/preview-examples/spinner'; import SpinnerLarge from 'src/preview-examples/spinner-large'; import SplitButton from 'src/preview-examples/split-button'; @@ -241,6 +245,10 @@ import { NavigationTestComponent } from './components/navigation-test.component' ContentHeader, ContentHeaderNoBack, MenuCategory, + Slider, + SliderTrace, + SliderMarker, + SliderError, Grid, GridSize, GridPadding, diff --git a/packages/angular-test-app/src/preview-examples/slider-error.html b/packages/angular-test-app/src/preview-examples/slider-error.html new file mode 100644 index 00000000000..149b2403662 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-error.html @@ -0,0 +1,18 @@ + + 0 + 100 + + + + 0 + 100 + diff --git a/packages/angular-test-app/src/preview-examples/slider-error.ts b/packages/angular-test-app/src/preview-examples/slider-error.ts new file mode 100644 index 00000000000..868c28be623 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-error.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-example', + templateUrl: './slider-error.html', +}) +export default class SliderError {} diff --git a/packages/angular-test-app/src/preview-examples/slider-marker.html b/packages/angular-test-app/src/preview-examples/slider-marker.html new file mode 100644 index 00000000000..7a7e3413ae3 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-marker.html @@ -0,0 +1,15 @@ + + 0 + 100 + + + + 0 + 100 + diff --git a/packages/angular-test-app/src/preview-examples/slider-marker.ts b/packages/angular-test-app/src/preview-examples/slider-marker.ts new file mode 100644 index 00000000000..eb0f12a8f39 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-marker.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-example', + templateUrl: './slider-marker.html', +}) +export default class SliderMarker {} diff --git a/packages/angular-test-app/src/preview-examples/slider-trace.html b/packages/angular-test-app/src/preview-examples/slider-trace.html new file mode 100644 index 00000000000..ed772e71b32 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-trace.html @@ -0,0 +1,22 @@ + + 0 + 100 + + + + 0 + 100 + diff --git a/packages/angular-test-app/src/preview-examples/slider-trace.ts b/packages/angular-test-app/src/preview-examples/slider-trace.ts new file mode 100644 index 00000000000..f044dad41a5 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider-trace.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-example', + templateUrl: './slider-trace.html', +}) +export default class SliderTrace {} diff --git a/packages/angular-test-app/src/preview-examples/slider.html b/packages/angular-test-app/src/preview-examples/slider.html new file mode 100644 index 00000000000..980795bbd45 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider.html @@ -0,0 +1,9 @@ + + 0 + 100 + + + + 0 + 100 + diff --git a/packages/angular-test-app/src/preview-examples/slider.ts b/packages/angular-test-app/src/preview-examples/slider.ts new file mode 100644 index 00000000000..a482e3d1873 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/slider.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-example', + templateUrl: './slider.html', +}) +export default class Slider {} diff --git a/packages/angular/src/components.ts b/packages/angular/src/components.ts index 43973b1cdd3..275f87a4d4d 100644 --- a/packages/angular/src/components.ts +++ b/packages/angular/src/components.ts @@ -1841,6 +1841,32 @@ export declare interface IxSelectItem extends Components.IxSelectItem { } +@ProxyCmp({ + inputs: ['disabled', 'error', 'marker', 'max', 'min', 'step', 'trace', 'traceReference', 'value'] +}) +@Component({ + selector: 'ix-slider', + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['disabled', 'error', 'marker', 'max', 'min', 'step', 'trace', 'traceReference', 'value'], +}) +export class IxSlider { + protected el: HTMLElement; + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + c.detach(); + this.el = r.nativeElement; + proxyOutputs(this, this.el, ['valueChange']); + } +} + + +export declare interface IxSlider extends Components.IxSlider { + + valueChange: EventEmitter>; +} + + @ProxyCmp({ inputs: ['size', 'variant'] }) diff --git a/packages/angular/src/declare-components.ts b/packages/angular/src/declare-components.ts index d33e1794932..3ff4b7cdec7 100644 --- a/packages/angular/src/declare-components.ts +++ b/packages/angular/src/declare-components.ts @@ -72,6 +72,7 @@ export const DIRECTIVES = [ d.IxRow, d.IxSelect, d.IxSelectItem, + d.IxSlider, d.IxSpinner, d.IxSplitButton, d.IxSplitButtonItem, diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json index a9718c445cd..b731882d240 100644 --- a/packages/core/component-doc.json +++ b/packages/core/component-doc.json @@ -9311,6 +9311,237 @@ "parts": [], "listeners": [] }, + { + "dirPath": "./src/components/slider", + "filePath": "./src/components/slider/slider.tsx", + "fileName": "slider.tsx", + "readmePath": "./src/components/slider/readme.md", + "usagesDir": "./src/components/slider/usage", + "tag": "ix-slider", + "overview": "", + "usage": {}, + "docs": "", + "docsTags": [ + { + "name": "since", + "text": "2.0.0" + }, + { + "name": "slot", + "text": "label-start - Element will be displayed at the start of the slider" + }, + { + "name": "slot", + "text": "label-end - Element will be displayed at the end of the slider" + } + ], + "encapsulation": "shadow", + "dependents": [], + "dependencies": [ + "ix-tooltip", + "ix-typography" + ], + "dependencyGraph": { + "ix-slider": [ + "ix-tooltip", + "ix-typography" + ], + "ix-tooltip": [ + "ix-typography" + ] + }, + "props": [ + { + "name": "disabled", + "type": "boolean", + "mutable": false, + "attr": "disabled", + "reflectToAttr": false, + "docs": "Show control as disabled", + "docsTags": [], + "default": "false", + "values": [ + { + "type": "boolean" + } + ], + "optional": false, + "required": false + }, + { + "name": "error", + "type": "boolean | string", + "mutable": false, + "attr": "error", + "reflectToAttr": false, + "docs": "Show error state and message", + "docsTags": [], + "values": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ], + "optional": false, + "required": false + }, + { + "name": "marker", + "type": "number[]", + "mutable": false, + "reflectToAttr": false, + "docs": "Define tick marker on the slider. Marker has to be within slider min/max", + "docsTags": [], + "values": [ + { + "type": "number[]" + } + ], + "optional": false, + "required": false + }, + { + "name": "max", + "type": "number", + "mutable": false, + "attr": "max", + "reflectToAttr": false, + "docs": "Maximum slider value", + "docsTags": [], + "default": "100", + "values": [ + { + "type": "number" + } + ], + "optional": false, + "required": false + }, + { + "name": "min", + "type": "number", + "mutable": false, + "attr": "min", + "reflectToAttr": false, + "docs": "Minimum slider value", + "docsTags": [], + "default": "0", + "values": [ + { + "type": "number" + } + ], + "optional": false, + "required": false + }, + { + "name": "step", + "type": "number", + "mutable": false, + "attr": "step", + "reflectToAttr": false, + "docs": "Legal number intervals", + "docsTags": [ + { + "name": "see", + "text": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#step" + } + ], + "values": [ + { + "type": "number" + } + ], + "optional": false, + "required": false + }, + { + "name": "trace", + "type": "boolean", + "mutable": false, + "attr": "trace", + "reflectToAttr": false, + "docs": "Show a trace line", + "docsTags": [], + "default": "false", + "values": [ + { + "type": "boolean" + } + ], + "optional": false, + "required": false + }, + { + "name": "traceReference", + "type": "number", + "mutable": false, + "attr": "trace-reference", + "reflectToAttr": false, + "docs": "Define the start point of the trace line", + "docsTags": [], + "default": "0", + "values": [ + { + "type": "number" + } + ], + "optional": false, + "required": false + }, + { + "name": "value", + "type": "number", + "mutable": false, + "attr": "value", + "reflectToAttr": false, + "docs": "Current value of the slider", + "docsTags": [], + "default": "0", + "values": [ + { + "type": "number" + } + ], + "optional": false, + "required": false + } + ], + "methods": [], + "events": [ + { + "event": "valueChange", + "detail": "number", + "bubbles": true, + "cancelable": true, + "composed": true, + "docs": "", + "docsTags": [] + } + ], + "styles": [], + "slots": [ + { + "name": "label-end", + "docs": "Element will be displayed at the end of the slider" + }, + { + "name": "label-start", + "docs": "Element will be displayed at the start of the slider" + } + ], + "parts": [], + "listeners": [ + { + "event": "pointerup", + "target": "window", + "capture": false, + "passive": true + } + ] + }, { "dirPath": "./src/components/spinner", "filePath": "./src/components/spinner/spinner.tsx", @@ -11046,13 +11277,18 @@ } ], "encapsulation": "shadow", - "dependents": [], + "dependents": [ + "ix-slider" + ], "dependencies": [ "ix-typography" ], "dependencyGraph": { "ix-tooltip": [ "ix-typography" + ], + "ix-slider": [ + "ix-tooltip" ] }, "props": [ @@ -11429,6 +11665,7 @@ "ix-modal-header", "ix-pagination", "ix-push-card", + "ix-slider", "ix-time-picker", "ix-tooltip" ], @@ -11467,6 +11704,9 @@ "ix-push-card": [ "ix-typography" ], + "ix-slider": [ + "ix-typography" + ], "ix-time-picker": [ "ix-typography" ], diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index e3190405d37..6af7f1e2c56 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -30,6 +30,7 @@ import { ButtonVariant as ButtonVariant1 } from "./components/button/button"; import { KeyValueLabelPosition } from "./components/key-value/key-value"; import { IxModalSize } from "./components/modal/modal"; import { PushCardVariant } from "./components/push-card/push-card"; +import { SliderMarker } from "./components/slider/slider"; import { SplitButtonVariant } from "./components/split-button/split-button"; import { TabClickDetail } from "./components/tab-item/tab-item"; import { TimePickerCorners } from "./components/time-picker/time-picker"; @@ -63,6 +64,7 @@ export { ButtonVariant as ButtonVariant1 } from "./components/button/button"; export { KeyValueLabelPosition } from "./components/key-value/key-value"; export { IxModalSize } from "./components/modal/modal"; export { PushCardVariant } from "./components/push-card/push-card"; +export { SliderMarker } from "./components/slider/slider"; export { SplitButtonVariant } from "./components/split-button/split-button"; export { TabClickDetail } from "./components/tab-item/tab-item"; export { TimePickerCorners } from "./components/time-picker/time-picker"; @@ -1630,6 +1632,48 @@ export namespace Components { */ "value": any; } + /** + * @since 2.0.0 + */ + interface IxSlider { + /** + * Show control as disabled + */ + "disabled": boolean; + /** + * Show error state and message + */ + "error": boolean | string; + /** + * Define tick marker on the slider. Marker has to be within slider min/max + */ + "marker": SliderMarker; + /** + * Maximum slider value + */ + "max": number; + /** + * Minimum slider value + */ + "min": number; + /** + * Legal number intervals + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#step + */ + "step": number; + /** + * Show a trace line + */ + "trace": boolean; + /** + * Define the start point of the trace line + */ + "traceReference": number; + /** + * Current value of the slider + */ + "value": number; + } interface IxSpinner { "hideTrack": boolean; /** @@ -1900,10 +1944,12 @@ export namespace Components { * @since 1.4.0 */ interface IxTooltip { + "animationFrame": boolean; /** * CSS selector for hover trigger element e.g. `for="[data-my-custom-select]"` */ "for": string; + "hideTooltip": () => Promise; /** * Define if the user can access the tooltip via mouse. */ @@ -1913,6 +1959,7 @@ export namespace Components { * @since 1.5.0 */ "placement": 'top' | 'right' | 'bottom' | 'left'; + "showTooltip": (anchorElement: any) => Promise; /** * Title of the tooltip */ @@ -2227,6 +2274,10 @@ export interface IxSelectItemCustomEvent extends CustomEvent { detail: T; target: HTMLIxSelectItemElement; } +export interface IxSliderCustomEvent extends CustomEvent { + detail: T; + target: HTMLIxSliderElement; +} export interface IxSplitButtonCustomEvent extends CustomEvent { detail: T; target: HTMLIxSplitButtonElement; @@ -2817,6 +2868,15 @@ declare global { prototype: HTMLIxSelectItemElement; new (): HTMLIxSelectItemElement; }; + /** + * @since 2.0.0 + */ + interface HTMLIxSliderElement extends Components.IxSlider, HTMLStencilElement { + } + var HTMLIxSliderElement: { + prototype: HTMLIxSliderElement; + new (): HTMLIxSliderElement; + }; interface HTMLIxSpinnerElement extends Components.IxSpinner, HTMLStencilElement { } var HTMLIxSpinnerElement: { @@ -3024,6 +3084,7 @@ declare global { "ix-row": HTMLIxRowElement; "ix-select": HTMLIxSelectElement; "ix-select-item": HTMLIxSelectItemElement; + "ix-slider": HTMLIxSliderElement; "ix-spinner": HTMLIxSpinnerElement; "ix-split-button": HTMLIxSplitButtonElement; "ix-split-button-item": HTMLIxSplitButtonItemElement; @@ -4776,6 +4837,49 @@ declare namespace LocalJSX { */ "value": any; } + /** + * @since 2.0.0 + */ + interface IxSlider { + /** + * Show control as disabled + */ + "disabled"?: boolean; + /** + * Show error state and message + */ + "error"?: boolean | string; + /** + * Define tick marker on the slider. Marker has to be within slider min/max + */ + "marker"?: SliderMarker; + /** + * Maximum slider value + */ + "max"?: number; + /** + * Minimum slider value + */ + "min"?: number; + "onValueChange"?: (event: IxSliderCustomEvent) => void; + /** + * Legal number intervals + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#step + */ + "step"?: number; + /** + * Show a trace line + */ + "trace"?: boolean; + /** + * Define the start point of the trace line + */ + "traceReference"?: number; + /** + * Current value of the slider + */ + "value"?: number; + } interface IxSpinner { "hideTrack"?: boolean; /** @@ -5075,6 +5179,7 @@ declare namespace LocalJSX { * @since 1.4.0 */ interface IxTooltip { + "animationFrame"?: boolean; /** * CSS selector for hover trigger element e.g. `for="[data-my-custom-select]"` */ @@ -5373,6 +5478,7 @@ declare namespace LocalJSX { "ix-row": IxRow; "ix-select": IxSelect; "ix-select-item": IxSelectItem; + "ix-slider": IxSlider; "ix-spinner": IxSpinner; "ix-split-button": IxSplitButton; "ix-split-button-item": IxSplitButtonItem; @@ -5551,6 +5657,10 @@ declare module "@stencil/core" { "ix-row": LocalJSX.IxRow & JSXBase.HTMLAttributes; "ix-select": LocalJSX.IxSelect & JSXBase.HTMLAttributes; "ix-select-item": LocalJSX.IxSelectItem & JSXBase.HTMLAttributes; + /** + * @since 2.0.0 + */ + "ix-slider": LocalJSX.IxSlider & JSXBase.HTMLAttributes; "ix-spinner": LocalJSX.IxSpinner & JSXBase.HTMLAttributes; "ix-split-button": LocalJSX.IxSplitButton & JSXBase.HTMLAttributes; "ix-split-button-item": LocalJSX.IxSplitButtonItem & JSXBase.HTMLAttributes; diff --git a/packages/core/src/components/slider/slider.scss b/packages/core/src/components/slider/slider.scss new file mode 100644 index 00000000000..51bf94ab5b8 --- /dev/null +++ b/packages/core/src/components/slider/slider.scss @@ -0,0 +1,293 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +@import 'mixins/shadow-dom/component'; + +@mixin track { + // Chrome, Safari, Opera, and Edge Chromium + input[type='range']::-webkit-slider-runnable-track { + @content; + } + + // Firefox + input[type='range']::-moz-range-track { + @content; + } +} + +@mixin clear-browser-thumb { + // Chrome, Safari, Opera, and Edge Chromium + input[type='range' i]::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + margin-top: -6px; + } + + // Firefox + input[type='range' i]::-moz-range-thumb { + border: none; + border-radius: 0; + } +} + +@mixin thumb { + // Chrome, Safari, Opera, and Edge Chromium + input[type='range']::-webkit-slider-thumb { + @content; + } + + // Firefox + input[type='range']::-moz-range-thumb { + @content; + } +} + +@mixin thumb-hover { + // Chrome, Safari, Opera, and Edge Chromium + input[type='range']:hover::-webkit-slider-thumb { + @content; + } + + // Firefox + input[type='range']:hover::-moz-range-thumb { + @content; + } +} + +@mixin thumb-active { + // Chrome, Safari, Opera, and Edge Chromium + input[type='range']:active::-webkit-slider-thumb { + @content; + } + + // Firefox + input[type='range']:active::-moz-range-thumb { + @content; + } +} + +@mixin thumb-focus { + input[type='range']:focus { + outline: none; + } + + // Chrome, Safari, Opera, and Edge Chromium + input[type='range']:focus-visible::-webkit-slider-thumb { + @content; + } + + // Firefox + input[type='range']:focus-visible::-moz-range-thumb { + @content; + } +} + +:host { + @include ix-component; + display: flex; + flex-direction: column; + min-height: 2rem; + --thumb-size: 1rem; + --value: 0; + --trace-start: 0; + --trace-end: 0; + --trace-reference: 0; + --trace-reference-color: var(--theme-color-8); + --trace-color: var(--theme-color-dynamic); + --tick-color: var(--theme-color-8); + --tick-color--active: var(--theme-color-dynamic); + --track-color: var(--theme-color-component-4); + + input[type='range'] { + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 0; + appearance: none; + background: transparent; + cursor: pointer; + width: 100%; + height: 1rem; + margin: 0; + } + + // Trace line + input[type='range'].trace::before { + content: ''; + position: absolute; + display: block; + z-index: -1; + + width: calc( + calc(7px + calc(100% * var(--trace-end)) - calc(16px * var(--trace-end))) - + calc( + 7px + calc(100% * var(--trace-start)) - + calc(16px * var(--trace-start)) + ) + ); + + left: calc( + 7px + calc(100% * var(--trace-start)) - calc(16px * var(--trace-start)) + ); + + height: 4px; + background-color: var(--trace-color); + top: 50%; + transform: translateY(-50%); + } + + // Trace reference + input[type='range'].trace::after { + content: ''; + position: absolute; + display: block; + width: 2px; + height: 16px; + background-color: var(--trace-reference-color); + top: 50%; + transform: translateY(-50%); + left: calc( + 7px + calc(100% * var(--trace-reference)) - + calc(16px * var(--trace-reference)) + ); + } + + @include track() { + background: transparent; + height: 0.25rem; + } + + @include clear-browser-thumb(); + + @include thumb() { + border-radius: 100px; + background-color: var(--theme-color-dynamic); + height: var(--thumb-size); + width: var(--thumb-size); + transition: all var(--theme-default-time) ease-in-out; + z-index: 10; + } + + @include thumb-hover() { + transform: scale(1.2); + background-color: var(--theme-color-dynamic); + } + + @include thumb-active() { + transform: scale(1.2); + + background-color: var(--theme-color-dynamic); + } + + @include thumb-focus() { + outline: 1px solid var(--theme-color-focus-bdr); + outline-offset: 0.125rem; + } + + /* style datalist */ + .ticks { + display: flex; + position: relative; + top: 50%; + transform: translateY(-50%); + } + .ticks .tick { + display: block; + position: absolute; + width: 8px; + height: 8px; + background-color: var(--tick-color); + border-radius: 100px; + top: 50%; + transform: translateY(-50%); + left: calc(var(--tick-value) * 100% - 4px); + } + + .ticks .tick.tick-active { + background-color: var(--tick-color--active); + } + + .slider { + position: relative; + display: block; + width: 100%; + height: 1.5rem; + } + + .track { + position: absolute; + background-color: var(--track-color); + height: 4px; + width: calc(100% - 1rem); + margin-left: 0.5rem; + top: 50%; + transform: translateY(-50%); + left: 0px; + } + + .thumb { + display: block; + position: absolute; + background-color: transparent; + height: 1rem; + width: 1rem; + border-radius: 100px; + left: 0px; + top: 50%; + transform: translateY(-50%); + } + + .hide-tooltip { + display: none; + } + + .label { + display: flex; + position: relative; + align-items: center; + justify-content: space-between; + width: 100%; + margin-top: 0.5rem; + + min-height: 0px; + } + + .label-start { + margin-left: 0.5rem; + } + + .label-end { + margin-right: 0.5rem; + } + + .label-error { + margin-left: 0.5rem; + } +} + +:host(.error) { + --trace-color: var(--theme-color-alarm-40); + --tick-color--active: var(--theme-color-alarm); + + @include thumb() { + background-color: var(--theme-color-alarm); + } +} + +:host(.disabled) { + pointer-events: none; + + --track-color: var(--theme-color-component-3); + --trace-color: var(--theme-color-3); + --tick-color: var(--theme-color-7); + --tick-color--active: var(--theme-color-7); + + @include thumb() { + background-color: var(--theme-color-7); + } +} diff --git a/packages/core/src/components/slider/slider.tsx b/packages/core/src/components/slider/slider.tsx new file mode 100644 index 00000000000..a2934f8d883 --- /dev/null +++ b/packages/core/src/components/slider/slider.tsx @@ -0,0 +1,305 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { + Component, + Element, + Event, + EventEmitter, + h, + Host, + Listen, + Prop, + State, + Watch, +} from '@stencil/core'; +import { A11yAttributes, a11yHostAttributes } from '../utils/a11y'; + +export type SliderMarker = Array; + +function between(min: number, value: number, max: number) { + if (value < min) { + return min; + } else if (value > max) { + return max; + } else { + return value; + } +} + +/** + * @since 2.0.0 + * + * @slot label-start - Element will be displayed at the start of the slider + * @slot label-end - Element will be displayed at the end of the slider + */ +@Component({ + tag: 'ix-slider', + styleUrl: 'slider.scss', + shadow: true, +}) +export class IxSlider { + @Element() hostElement!: HTMLIxSliderElement; + + /** + * Legal number intervals + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#step + */ + @Prop() step: number; + + /** + * Minimum slider value + */ + @Prop() min = 0; + + /** + * Maximum slider value + */ + @Prop() max = 100; + + /** + * Current value of the slider + */ + @Prop() value = 0; + + /** + * Define tick marker on the slider. Marker has to be within slider min/max + */ + @Prop() marker: SliderMarker; + + /** + * Show a trace line + */ + @Prop() trace = false; + + /** + * Define the start point of the trace line + */ + @Prop() traceReference = 0; + + /** + * Show control as disabled + */ + @Prop() disabled = false; + + /** + * Show error state and message + */ + @Prop() error: boolean | string; + + /** + * + */ + @Event() valueChange: EventEmitter; + + @State() rangeInput = 0; + @State() rangeMin = 0; + @State() rangeMax = 100; + @State() rangeTraceReference = 0; + @State() showTooltip = false; + + private a11yAttributes: A11yAttributes; + + get tooltip() { + return this.hostElement.shadowRoot.querySelector('ix-tooltip'); + } + + get pseudoThumb() { + return this.hostElement.shadowRoot.querySelector('.thumb') as HTMLElement; + } + + get slider() { + return this.hostElement.shadowRoot.getElementById( + 'slider' + ) as HTMLInputElement; + } + + @Watch('showTooltip') + onShowTooltipChange() { + if (this.showTooltip) { + this.tooltip.showTooltip(this.pseudoThumb); + return; + } + + this.tooltip.hideTooltip(); + } + + componentWillLoad() { + this.a11yAttributes = a11yHostAttributes(this.hostElement, [ + 'role', + 'aria-valuemin', + 'aria-valuemax', + 'aria-valuenow', + ]); + this.updateRangeVariables(); + } + + @Watch('value') + @Watch('max') + @Watch('min') + @Watch('traceReference') + private updateRangeVariables() { + this.rangeInput = between(this.min, this.value, this.max); + this.rangeTraceReference = between(this.min, this.traceReference, this.max); + this.rangeMin = Math.min(this.min, this.max); + this.rangeMax = Math.max(this.min, this.max); + } + + private onInput(event: InputEvent) { + event.stopPropagation(); + const value = parseInt(this.slider.value); + + if (!isNaN(value)) { + this.rangeInput = value; + this.emitInputEvent(); + } + } + + private emitInputEvent() { + this.valueChange.emit(this.rangeInput); + } + + private isMarkerActive(markerValue: number) { + const start = Math.min(this.traceReference, this.rangeInput); + const end = Math.max(this.traceReference, this.rangeInput); + const value = markerValue; + + return value >= start && value <= end; + } + + // Listen globally on window because sometimes the event listener + // of the DOM element input itself is not called if the release + // click is not inside the element anymore + @Listen('pointerup', { + target: 'window', + }) + onPointerUp() { + this.showTooltip = false; + } + + render() { + const range = this.rangeMax - this.rangeMin; + + let traceReferenceInPercentage = + (this.rangeTraceReference - this.rangeMin) / range; + + let valueInPercentage = (this.rangeInput - this.rangeMin) / range; + + const distance = valueInPercentage - traceReferenceInPercentage; + + let traceStart = traceReferenceInPercentage; + let traceEnd = valueInPercentage; + + if (distance <= 0) { + traceStart = valueInPercentage; + traceEnd = traceReferenceInPercentage; + } + + return ( + setTimeout(() => (this.showTooltip = true))} + > +
+
+
+
+ {this.marker + ? this.marker.map((markerValue) => { + if (markerValue > this.max || markerValue < this.min) { + return; + } + + let left = (markerValue - this.rangeMin) / range; + + return ( +
+ ); + }) + : null} +
+
+ + this.onInput(event)} + style={{ + '--value': `${valueInPercentage}`, + '--trace-reference': `${traceReferenceInPercentage}`, + '--trace-start': `${traceStart}`, + '--trace-end': `${traceEnd}`, + }} + class={{ + trace: + this.trace && traceReferenceInPercentage !== valueInPercentage, + }} + onFocus={() => { + this.showTooltip = true; + }} + onBlur={() => { + this.showTooltip = false; + }} + role="slider" + aria-valuenow={this.rangeInput} + aria-valuemin={this.min} + aria-valuemax={this.max} + {...this.a11yAttributes} + /> + + + {this.rangeInput} + +
+
+
+ +
+
+ +
+
+ {this.error ? ( + + {this.error} + + ) : null} +
+ ); + } +} diff --git a/packages/core/src/components/slider/test/slider.ct.ts b/packages/core/src/components/slider/test/slider.ct.ts new file mode 100644 index 00000000000..21a2a6822fe --- /dev/null +++ b/packages/core/src/components/slider/test/slider.ct.ts @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { expect } from '@playwright/test'; +import { test } from '@utils/test'; + +test('renders', async ({ page, mount }) => { + await mount(``); + + const slider = page.locator('ix-slider'); + await expect(slider).toHaveClass(/hydrated/); +}); + +test('should show tooltip by focus', async ({ page, mount }) => { + await mount(``); + + const slider = page.locator('ix-slider'); + await expect(slider).toHaveClass(/hydrated/); + + const input = slider.locator('input'); + + const tooltip = slider.locator('ix-tooltip'); + await input.focus(); + await expect(tooltip).toBeVisible(); + const left = (await tooltip.boundingBox()).x; + + await input.press('ArrowRight'); + await input.press('ArrowRight'); + + await page.waitForTimeout(500); + await expect(tooltip).toBeVisible(); + const leftAfterKeyboardNavigation = (await tooltip.boundingBox()).x; + + expect(leftAfterKeyboardNavigation).toBeGreaterThan(left); + + await input.blur(); + await page.waitForTimeout(500); + await expect(tooltip).not.toBeVisible(); +}); diff --git a/packages/core/src/components/tooltip/tooltip.tsx b/packages/core/src/components/tooltip/tooltip.tsx index 52875478518..b238c240d0d 100644 --- a/packages/core/src/components/tooltip/tooltip.tsx +++ b/packages/core/src/components/tooltip/tooltip.tsx @@ -15,7 +15,15 @@ import { offset, shift, } from '@floating-ui/dom'; -import { Component, Element, h, Host, Prop, State } from '@stencil/core'; +import { + Component, + Element, + h, + Host, + Method, + Prop, + State, +} from '@stencil/core'; type ArrowPosition = { top?: string; @@ -60,14 +68,17 @@ export class Tooltip { */ @Prop() placement: 'top' | 'right' | 'bottom' | 'left' = 'top'; + /** @internal */ + @Prop() animationFrame = false; + @State() visible = false; @Element() hostElement: HTMLIxTooltipElement; private observer: MutationObserver; private hideTooltipTimeout: NodeJS.Timeout; - private onMouseEnterBind = this.showTooltip.bind(this); - private onMouseLeaveBind = this.hideTooltip.bind(this); + private onMouseEnterBind = this.onTooltipShow.bind(this); + private onMouseLeaveBind = this.onTooltipHide.bind(this); private disposeAutoUpdate?: () => void; private tooltipCloseTimeInMS = 50; @@ -81,13 +92,25 @@ export class Tooltip { } } - private showTooltip(e: any) { + private onTooltipShow(e: Event) { + this.showTooltip(e.target as Element); + } + + private onTooltipHide() { + this.hideTooltip(); + } + + /** @internal */ + @Method() + async showTooltip(anchorElement: any) { clearTimeout(this.hideTooltipTimeout); this.visible = true; - this.computeTooltipPosition(e.target); + this.computeTooltipPosition(anchorElement); } - private hideTooltip() { + /** @internal */ + @Method() + async hideTooltip() { this.hideTooltipTimeout = setTimeout(() => { this.visible = false; }, this.tooltipCloseTimeInMS); @@ -129,7 +152,10 @@ export class Tooltip { } } - private async computeTooltipPosition(target: HTMLElement) { + private async computeTooltipPosition(target: Element) { + if (!target) { + return; + } this.disposeAutoUpdate = autoUpdate( target, this.hostElement, @@ -171,6 +197,7 @@ export class Tooltip { ancestorResize: true, ancestorScroll: true, elementResize: true, + animationFrame: this.animationFrame, } ); } @@ -194,7 +221,7 @@ export class Tooltip { } }); this.hostElement.addEventListener('mouseleave', () => { - this.hideTooltip(); + this.onTooltipHide(); }); } diff --git a/packages/core/src/components/utils/a11y.ts b/packages/core/src/components/utils/a11y.ts index 4f5d63eada5..cd6afe55f5b 100644 --- a/packages/core/src/components/utils/a11y.ts +++ b/packages/core/src/components/utils/a11y.ts @@ -9,13 +9,14 @@ export const a11yBoolean = (value: boolean) => (value ? 'true' : 'false'); export const a11yHostAttributes = ( - hostElement: HTMLElement + hostElement: HTMLElement, + ignoreAttributes: A11yAttributeName[] = [] ): Record => { const attributeObject: Record = {}; a11yAttributes.forEach((attr) => { if (hostElement.hasAttribute(attr)) { const value = hostElement.getAttribute(attr); - if (value !== null) { + if (value !== null && !ignoreAttributes.includes(attr)) { attributeObject[attr] = hostElement.getAttribute(attr); } hostElement.removeAttribute(attr); diff --git a/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-dark-linux.png index aa9a7684590..fe4ba974a7d 100644 Binary files a/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-dark-linux.png and b/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-light-linux.png index a74447d8619..cfeb7dee5ec 100644 Binary files a/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-light-linux.png and b/packages/core/src/tests/basic-navigation/basic-navigation.e2e.ts-snapshots/basic-navigation-mobile-mobile-expanded-overlay-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts b/packages/core/src/tests/slider/slider.e2e.ts new file mode 100644 index 00000000000..954d43090ff --- /dev/null +++ b/packages/core/src/tests/slider/slider.e2e.ts @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { expect } from '@playwright/test'; +import { test } from '@utils/test'; + +test('should show reference value', async ({ page, mount }) => { + await mount(` + + + + + + + + `); + + const slider = page.locator('ix-slider').nth(0); + await expect(slider).toHaveClass(/hydrated/); + + await expect(slider).toBeVisible(); + + expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(); +}); + +test('should render marker', async ({ page, mount }) => { + await mount(` + + `); + + const slider = page.locator('ix-slider'); + await expect(slider).toHaveClass(/hydrated/); + + await slider.evaluate( + (elm: HTMLIxSliderElement) => (elm.marker = [10, 20, 70, 80, 100]) + ); + + expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(); +}); + +test('should render with min-max changes', async ({ page, mount }) => { + await mount(` + + + + `); + + const slider1 = page.locator('ix-slider').nth(0); + const slider2 = page.locator('ix-slider').nth(1); + + await expect(slider1).toHaveClass(/hydrated/); + await expect(slider2).toHaveClass(/hydrated/); + + await slider1.evaluate((elm: HTMLIxSliderElement) => { + elm.marker = [700, 800, 900]; + }); + + await slider2.evaluate((elm: HTMLIxSliderElement) => { + elm.marker = [-50, 25]; + }); + + expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(); +}); + +test('should render with label', async ({ page, mount }) => { + await mount(` + + 500 + 1000 + + + + 500 + 1000 + + `); + + const slider1 = page.locator('ix-slider').nth(0); + const slider2 = page.locator('ix-slider').nth(1); + + await expect(slider1).toHaveClass(/hydrated/); + await expect(slider2).toHaveClass(/hydrated/); + + expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(); +}); + +test('should render with error', async ({ page, mount }) => { + await mount(` + + + + + 500 + 1000 + + `); + + const slider1 = page.locator('ix-slider').nth(0); + const slider2 = page.locator('ix-slider').nth(1); + + await expect(slider1).toHaveClass(/hydrated/); + await expect(slider2).toHaveClass(/hydrated/); + + expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(); +}); diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..5281b0277ab Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..267bbb76666 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-marker-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..45968950562 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..5b87263aed6 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-error-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..41faa567808 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..0929df930e8 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-label-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..d5703da18fe Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..53a1b649610 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-render-with-min-max-changes-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-dark-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-dark-linux.png new file mode 100644 index 00000000000..409baf01863 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-dark-linux.png differ diff --git a/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-light-linux.png b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-light-linux.png new file mode 100644 index 00000000000..09fc3f41aa4 Binary files /dev/null and b/packages/core/src/tests/slider/slider.e2e.ts-snapshots/should-show-reference-value-1-chromium---theme-classic-light-linux.png differ diff --git a/packages/documentation/docs/controls/layout-grid.md b/packages/documentation/docs/controls/layout-grid.md index 28a940c6f64..1dcac2dbf7b 100644 --- a/packages/documentation/docs/controls/layout-grid.md +++ b/packages/documentation/docs/controls/layout-grid.md @@ -1,18 +1,23 @@ import Playground from '@site/src/components/Playground'; +import Tags from './../auto-generated/ix-layout-grid/tags.md'; import Props from './../auto-generated/ix-layout-grid/props.md'; import Events from './../auto-generated/ix-layout-grid/events.md'; + import ColProps from './../auto-generated/ix-col/props.md'; import ColEvents from './../auto-generated/ix-col/events.md'; # Layout grid + + ## Usage + examplesByName +> ### Size diff --git a/packages/documentation/docs/controls/slider.md b/packages/documentation/docs/controls/slider.md new file mode 100644 index 00000000000..d412c43b5df --- /dev/null +++ b/packages/documentation/docs/controls/slider.md @@ -0,0 +1,55 @@ +import Playground from '@site/src/components/Playground'; +import Tags from './../auto-generated/ix-slider/tags.md'; +import Props from './../auto-generated/ix-slider/props.md'; +import Events from './../auto-generated/ix-slider/events.md'; +import Slots from './../auto-generated/ix-slider/slots.md'; + +# Slider + + + +## Usage + + + + +### Marker + + + + +### Trace + + + + +### Error + + + + +## Properties + +### Props + + + +### Events + + + +### Slots + + diff --git a/packages/html-test-app/src/preview-examples/slider-error.html b/packages/html-test-app/src/preview-examples/slider-error.html new file mode 100644 index 00000000000..e3c569dfc44 --- /dev/null +++ b/packages/html-test-app/src/preview-examples/slider-error.html @@ -0,0 +1,45 @@ + + + + + + Blind example + + + + + 0 + 100 + + + + 0 + 100 + + + + + + diff --git a/packages/html-test-app/src/preview-examples/slider-marker.html b/packages/html-test-app/src/preview-examples/slider-marker.html new file mode 100644 index 00000000000..2536ef0c768 --- /dev/null +++ b/packages/html-test-app/src/preview-examples/slider-marker.html @@ -0,0 +1,37 @@ + + + + + + Blind example + + + + + 0 + 100 + + + + 0 + 100 + + + + + + diff --git a/packages/html-test-app/src/preview-examples/slider-trace.html b/packages/html-test-app/src/preview-examples/slider-trace.html new file mode 100644 index 00000000000..d38aebdecca --- /dev/null +++ b/packages/html-test-app/src/preview-examples/slider-trace.html @@ -0,0 +1,34 @@ + + + + + + + + 0 + 100 + + + + 0 + 100 + + + + + + diff --git a/packages/html-test-app/src/preview-examples/slider.html b/packages/html-test-app/src/preview-examples/slider.html new file mode 100644 index 00000000000..3f9901ffdd3 --- /dev/null +++ b/packages/html-test-app/src/preview-examples/slider.html @@ -0,0 +1,26 @@ + + + + + + Blind example + + + + + 0 + 100 + + + + 0 + 100 + + + + + diff --git a/packages/react-test-app/src/main.tsx b/packages/react-test-app/src/main.tsx index 106617113d3..35fcab92a6a 100644 --- a/packages/react-test-app/src/main.tsx +++ b/packages/react-test-app/src/main.tsx @@ -74,6 +74,10 @@ import Select from './preview-examples/select'; import SelectEditable from './preview-examples/select-editable'; import SelectMultiple from './preview-examples/select-multiple'; import Settings from './preview-examples/settings'; +import Slider from './preview-examples/slider'; +import SliderError from './preview-examples/slider-error'; +import SliderMarker from './preview-examples/slider-marker'; +import SliderTrace from './preview-examples/slider-trace'; import Spinner from './preview-examples/spinner'; import SpinnerLarge from './preview-examples/spinner-large'; import Splitbutton from './preview-examples/split-button'; @@ -270,6 +274,10 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( } /> } /> + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/packages/react-test-app/src/preview-examples/slider-error.tsx b/packages/react-test-app/src/preview-examples/slider-error.tsx new file mode 100644 index 00000000000..26c3ad03ce1 --- /dev/null +++ b/packages/react-test-app/src/preview-examples/slider-error.tsx @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { IxSlider } from '@siemens/ix-react'; +import React from 'react'; + +export default () => { + return ( + <> + + 0 + 100 + + + + 0 + 100 + + + ); +}; diff --git a/packages/react-test-app/src/preview-examples/slider-marker.tsx b/packages/react-test-app/src/preview-examples/slider-marker.tsx new file mode 100644 index 00000000000..a53c59112bc --- /dev/null +++ b/packages/react-test-app/src/preview-examples/slider-marker.tsx @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { IxSlider } from '@siemens/ix-react'; +import React from 'react'; + +export default () => { + return ( + <> + + 0 + 100 + + + + 0 + 100 + + + ); +}; diff --git a/packages/react-test-app/src/preview-examples/slider-trace.tsx b/packages/react-test-app/src/preview-examples/slider-trace.tsx new file mode 100644 index 00000000000..efb1999e989 --- /dev/null +++ b/packages/react-test-app/src/preview-examples/slider-trace.tsx @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { IxSlider } from '@siemens/ix-react'; +import React from 'react'; + +export default () => { + return ( + <> + + 0 + 100 + + + + 0 + 100 + + + ); +}; diff --git a/packages/react-test-app/src/preview-examples/slider.tsx b/packages/react-test-app/src/preview-examples/slider.tsx new file mode 100644 index 00000000000..1f4ce6a3e24 --- /dev/null +++ b/packages/react-test-app/src/preview-examples/slider.tsx @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { IxSlider } from '@siemens/ix-react'; +import React from 'react'; + +export default () => { + return ( + <> + + 0 + 100 + + + + 0 + 100 + + + ); +}; diff --git a/packages/react/src/components.ts b/packages/react/src/components.ts index 3735478aed0..ff540f463e9 100644 --- a/packages/react/src/components.ts +++ b/packages/react/src/components.ts @@ -78,6 +78,7 @@ export const IxPushCard = /*@__PURE__*/createReactComponent('ix-row'); export const IxSelect = /*@__PURE__*/createReactComponent('ix-select'); export const IxSelectItem = /*@__PURE__*/createReactComponent('ix-select-item'); +export const IxSlider = /*@__PURE__*/createReactComponent('ix-slider'); export const IxSpinner = /*@__PURE__*/createReactComponent('ix-spinner'); export const IxSplitButton = /*@__PURE__*/createReactComponent('ix-split-button'); export const IxSplitButtonItem = /*@__PURE__*/createReactComponent('ix-split-button-item'); diff --git a/packages/vue-test-app/src/Root.vue b/packages/vue-test-app/src/Root.vue index ae8c36f9798..8844691f703 100644 --- a/packages/vue-test-app/src/Root.vue +++ b/packages/vue-test-app/src/Root.vue @@ -27,6 +27,10 @@ import CheckboxIndeterminate from './preview-examples/checkbox-indeterminate.vue import Checkbox from './preview-examples/checkbox.vue'; import MenuCategory from './preview-examples/menu-category.vue'; import Chip from './preview-examples/chip.vue'; +import Slider from './preview-examples/slider.vue'; +import SliderTrace from './preview-examples/slider-trace.vue'; +import SliderMarker from './preview-examples/slider-marker.vue'; +import SliderError from './preview-examples/slider-error.vue'; import ContentHeaderNoBack from './preview-examples/content-header-no-back.vue'; import ContentHeader from './preview-examples/content-header.vue'; import DatepickerRange from './preview-examples/datepicker-range.vue'; @@ -191,6 +195,10 @@ const routes: any = { '/testing/navigation/link1': NavigationTest, '/testing/navigation/link2': NavigationTest, '/preview/menu-category': MenuCategory, + '/preview/slider': Slider, + '/preview/slider-trace': SliderTrace, + '/preview/slider-marker': SliderMarker, + '/preview/slider-error': SliderError, '/preview/grid': Grid, '/preview/grid-size': GridSize, '/preview/grid-padding': GridPadding, diff --git a/packages/vue-test-app/src/preview-examples/slider-error.vue b/packages/vue-test-app/src/preview-examples/slider-error.vue new file mode 100644 index 00000000000..b3278d02339 --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/slider-error.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/packages/vue-test-app/src/preview-examples/slider-marker.vue b/packages/vue-test-app/src/preview-examples/slider-marker.vue new file mode 100644 index 00000000000..20561619557 --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/slider-marker.vue @@ -0,0 +1,26 @@ + + + + + + + diff --git a/packages/vue-test-app/src/preview-examples/slider-trace.vue b/packages/vue-test-app/src/preview-examples/slider-trace.vue new file mode 100644 index 00000000000..3293a9fa2f7 --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/slider-trace.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/packages/vue-test-app/src/preview-examples/slider.vue b/packages/vue-test-app/src/preview-examples/slider.vue new file mode 100644 index 00000000000..b1ff9a0457a --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/slider.vue @@ -0,0 +1,25 @@ + + + + + + diff --git a/packages/vue/src/components.ts b/packages/vue/src/components.ts index a0453f517df..8dd31336cac 100644 --- a/packages/vue/src/components.ts +++ b/packages/vue/src/components.ts @@ -643,6 +643,20 @@ export const IxSelectItem = /*@__PURE__*/ defineContainer('ix- ]); +export const IxSlider = /*@__PURE__*/ defineContainer('ix-slider', undefined, [ + 'step', + 'min', + 'max', + 'value', + 'marker', + 'trace', + 'traceReference', + 'disabled', + 'error', + 'valueChange' +]); + + export const IxSpinner = /*@__PURE__*/ defineContainer('ix-spinner', undefined, [ 'variant', 'size', @@ -760,7 +774,8 @@ export const IxTooltip = /*@__PURE__*/ defineContainer('ix-toolti 'for', 'titleContent', 'interactive', - 'placement' + 'placement', + 'animationFrame' ]);