├── .nvmrc ├── src ├── demo-app │ ├── assets │ │ ├── .gitkeep │ │ └── logo.png │ ├── app │ │ ├── snippets │ │ │ ├── .gitignore │ │ │ ├── date-slider │ │ │ │ ├── date-slider.component.id-template.html │ │ │ │ ├── date-slider.component.title-template.html │ │ │ │ ├── date-slider.component.template.html │ │ │ │ └── date-slider.component.ts │ │ │ ├── range-slider │ │ │ │ ├── range-slider.component.title-template.html │ │ │ │ ├── range-slider.component.id-template.html │ │ │ │ ├── range-slider.component.template.html │ │ │ │ └── range-slider.component.ts │ │ │ ├── simple-slider │ │ │ │ ├── simple-slider.component.id-template.html │ │ │ │ ├── simple-slider.component.title-template.html │ │ │ │ ├── simple-slider.component.template.html │ │ │ │ └── simple-slider.component.ts │ │ │ ├── styled-slider │ │ │ │ ├── styled-slider.component.id-template.html │ │ │ │ ├── styled-slider.component.title-template.html │ │ │ │ ├── styled-slider.component.template.html │ │ │ │ ├── styled-slider.component.ts │ │ │ │ └── styled-slider.component.scss │ │ │ ├── ticks-slider │ │ │ │ ├── ticks-slider.component.id-template.html │ │ │ │ ├── ticks-slider.component.title-template.html │ │ │ │ ├── ticks-slider.component.template.html │ │ │ │ └── ticks-slider.component.ts │ │ │ ├── disabled-slider │ │ │ │ ├── disabled-slider.component.title-template.html │ │ │ │ ├── disabled-slider.component.id-template.html │ │ │ │ ├── disabled-slider.component.template.html │ │ │ │ └── disabled-slider.component.ts │ │ │ ├── limited-slider │ │ │ │ ├── limited-slider.component.id-template.html │ │ │ │ ├── limited-slider.component.title-template.html │ │ │ │ ├── limited-slider.component.template.html │ │ │ │ └── limited-slider.component.ts │ │ │ ├── stepped-slider │ │ │ │ ├── stepped-slider.component.id-template.html │ │ │ │ ├── stepped-slider.component.title-template.html │ │ │ │ ├── stepped-slider.component.template.html │ │ │ │ └── stepped-slider.component.ts │ │ │ ├── alphabet-slider │ │ │ │ ├── alphabet-slider.component.id-template.html │ │ │ │ ├── alphabet-slider.component.title-template.html │ │ │ │ ├── alphabet-slider.component.template.html │ │ │ │ └── alphabet-slider.component.ts │ │ │ ├── log-scale-slider │ │ │ │ ├── log-scale-slider.component.id-template.html │ │ │ │ ├── log-scale-slider.component.title-template.html │ │ │ │ ├── log-scale-slider.component.template.html │ │ │ │ └── log-scale-slider.component.ts │ │ │ ├── read-only-slider │ │ │ │ ├── read-only-slider.component.id-template.html │ │ │ │ ├── read-only-slider.component.title-template.html │ │ │ │ ├── read-only-slider.component.template.html │ │ │ │ └── read-only-slider.component.ts │ │ │ ├── vertical-sliders │ │ │ │ ├── vertical-sliders.component.id-template.html │ │ │ │ ├── vertical-sliders.component.title-template.html │ │ │ │ ├── vertical-sliders.component.template.html │ │ │ │ └── vertical-sliders.component.ts │ │ │ ├── push-range-slider │ │ │ │ ├── push-range-slider.component.id-template.html │ │ │ │ ├── push-range-slider.component.title-template.html │ │ │ │ ├── push-range-slider.component.template.html │ │ │ │ └── push-range-slider.component.ts │ │ │ ├── user-events-slider │ │ │ │ ├── user-events-slider.component.id-template.html │ │ │ │ ├── user-events-slider.component.title-template.html │ │ │ │ ├── user-events-slider.component.template.html │ │ │ │ └── user-events-slider.component.ts │ │ │ ├── custom-scale-slider │ │ │ │ ├── custom-scale-slider.component.id-template.html │ │ │ │ ├── custom-scale-slider.component.title-template.html │ │ │ │ ├── custom-scale-slider.component.template.html │ │ │ │ └── custom-scale-slider.component.ts │ │ │ ├── custom-ticks-slider │ │ │ │ ├── custom-ticks-slider.component.id-template.html │ │ │ │ ├── custom-ticks-slider.component.title-template.html │ │ │ │ ├── custom-ticks-slider.component.template.html │ │ │ │ └── custom-ticks-slider.component.ts │ │ │ ├── right-to-left-slider │ │ │ │ ├── right-to-left-slider.component.title-template.html │ │ │ │ ├── right-to-left-slider.component.id-template.html │ │ │ │ ├── right-to-left-slider.component.template.html │ │ │ │ └── right-to-left-slider.component.ts │ │ │ ├── ticks-values-slider │ │ │ │ ├── ticks-values-slider.component.id-template.html │ │ │ │ ├── ticks-values-slider.component.title-template.html │ │ │ │ ├── ticks-values-slider.component.template.html │ │ │ │ └── ticks-values-slider.component.ts │ │ │ ├── dynamic-options-slider │ │ │ │ ├── dynamic-options-slider.component.title-template.html │ │ │ │ ├── dynamic-options-slider.component.id-template.html │ │ │ │ ├── dynamic-options-slider.component.template.html │ │ │ │ └── dynamic-options-slider.component.ts │ │ │ ├── floating-point-slider │ │ │ │ ├── floating-point-slider.component.id-template.html │ │ │ │ ├── floating-point-slider.component.title-template.html │ │ │ │ ├── floating-point-slider.component.template.html │ │ │ │ └── floating-point-slider.component.ts │ │ │ ├── limited-range-slider │ │ │ │ ├── limited-range-slider.component.id-template.html │ │ │ │ ├── limited-range-slider.component.title-template.html │ │ │ │ ├── limited-range-slider.component.template.html │ │ │ │ └── limited-range-slider.component.ts │ │ │ ├── manual-refresh-slider │ │ │ │ ├── manual-refresh-slider.component.id-template.html │ │ │ │ ├── manual-refresh-slider.component.title-template.html │ │ │ │ ├── manual-refresh-slider.component.template.html │ │ │ │ └── manual-refresh-slider.component.ts │ │ │ ├── selection-bar-slider │ │ │ │ ├── selection-bar-slider.component.id-template.html │ │ │ │ ├── selection-bar-slider.component.title-template.html │ │ │ │ ├── selection-bar-slider.component.template.html │ │ │ │ └── selection-bar-slider.component.ts │ │ │ ├── ticks-tooltips-slider │ │ │ │ ├── ticks-tooltips-slider.component.id-template.html │ │ │ │ ├── ticks-tooltips-slider.component.title-template.html │ │ │ │ ├── ticks-tooltips-slider.component.template.html │ │ │ │ └── ticks-tooltips-slider.component.ts │ │ │ ├── trigger-focus-slider │ │ │ │ ├── trigger-focus-slider.component.id-template.html │ │ │ │ ├── trigger-focus-slider.component.title-template.html │ │ │ │ ├── trigger-focus-slider.component.template.html │ │ │ │ └── trigger-focus-slider.component.ts │ │ │ ├── draggable-range-slider │ │ │ │ ├── draggable-range-slider.component.id-template.html │ │ │ │ ├── draggable-range-slider.component.title-template.html │ │ │ │ ├── draggable-range-slider.component.template.html │ │ │ │ └── draggable-range-slider.component.ts │ │ │ ├── custom-ticks-legend-slider │ │ │ │ ├── custom-ticks-legend-slider.component.id-template.html │ │ │ │ ├── custom-ticks-legend-slider.component.title-template.html │ │ │ │ ├── custom-ticks-legend-slider.component.template.html │ │ │ │ └── custom-ticks-legend-slider.component.ts │ │ │ ├── dynamic-tick-color-slider │ │ │ │ ├── dynamic-tick-color-slider.component.id-template.html │ │ │ │ ├── dynamic-tick-color-slider.component.title-template.html │ │ │ │ ├── dynamic-tick-color-slider.component.template.html │ │ │ │ └── dynamic-tick-color-slider.component.ts │ │ │ ├── dynamically-created-sliders │ │ │ │ ├── dynamically-created-sliders.component.title-template.html │ │ │ │ ├── dynamically-created-sliders.component.id-template.html │ │ │ │ ├── dynamically-created-sliders.component.template.html │ │ │ │ └── dynamically-created-sliders.component.ts │ │ │ ├── intermediate-ticks-slider │ │ │ │ ├── intermediate-ticks-slider.component.id-template.html │ │ │ │ ├── intermediate-ticks-slider.component.title-template.html │ │ │ │ ├── intermediate-ticks-slider.component.template.html │ │ │ │ └── intermediate-ticks-slider.component.ts │ │ │ ├── no-switching-range-slider │ │ │ │ ├── no-switching-range-slider.component.id-template.html │ │ │ │ ├── no-switching-range-slider.component.title-template.html │ │ │ │ ├── no-switching-range-slider.component.template.html │ │ │ │ └── no-switching-range-slider.component.ts │ │ │ ├── reactive-form-range-slider │ │ │ │ ├── reactive-form-range-slider.component.id-template.html │ │ │ │ ├── reactive-form-range-slider.component.title-template.html │ │ │ │ ├── reactive-form-range-slider.component.template.html │ │ │ │ └── reactive-form-range-slider.component.ts │ │ │ ├── ticks-values-range-slider │ │ │ │ ├── ticks-values-range-slider.component.id-template.html │ │ │ │ ├── ticks-values-range-slider.component.title-template.html │ │ │ │ ├── ticks-values-range-slider.component.template.html │ │ │ │ └── ticks-values-range-slider.component.ts │ │ │ ├── draggable-range-only-slider │ │ │ │ ├── draggable-range-only-slider.component.id-template.html │ │ │ │ ├── draggable-range-only-slider.component.title-template.html │ │ │ │ ├── draggable-range-only-slider.component.template.html │ │ │ │ └── draggable-range-only-slider.component.ts │ │ │ ├── dynamic-pointer-color-slider │ │ │ │ ├── dynamic-pointer-color-slider.component.id-template.html │ │ │ │ ├── dynamic-pointer-color-slider.component.title-template.html │ │ │ │ ├── dynamic-pointer-color-slider.component.template.html │ │ │ │ └── dynamic-pointer-color-slider.component.ts │ │ │ ├── reactive-form-simple-slider │ │ │ │ ├── reactive-form-simple-slider.component.id-template.html │ │ │ │ ├── reactive-form-simple-slider.component.title-template.html │ │ │ │ ├── reactive-form-simple-slider.component.template.html │ │ │ │ └── reactive-form-simple-slider.component.ts │ │ │ ├── selection-bar-at-end-slider │ │ │ │ ├── selection-bar-at-end-slider.component.id-template.html │ │ │ │ ├── selection-bar-at-end-slider.component.title-template.html │ │ │ │ ├── selection-bar-at-end-slider.component.template.html │ │ │ │ └── selection-bar-at-end-slider.component.ts │ │ │ ├── ticks-custom-tooltips-slider │ │ │ │ ├── ticks-custom-tooltips-slider.component.id-template.html │ │ │ │ ├── ticks-custom-tooltips-slider.component.title-template.html │ │ │ │ ├── ticks-custom-tooltips-slider.component.template.html │ │ │ │ └── ticks-custom-tooltips-slider.component.ts │ │ │ ├── ticks-values-tooltips-slider │ │ │ │ ├── ticks-values-tooltips-slider.component.id-template.html │ │ │ │ ├── ticks-values-tooltips-slider.component.title-template.html │ │ │ │ ├── ticks-values-tooltips-slider.component.template.html │ │ │ │ └── ticks-values-tooltips-slider.component.ts │ │ │ ├── custom-legend-function-slider │ │ │ │ ├── custom-legend-function-slider.component.id-template.html │ │ │ │ ├── custom-legend-function-slider.component.title-template.html │ │ │ │ ├── custom-legend-function-slider.component.template.html │ │ │ │ └── custom-legend-function-slider.component.ts │ │ │ ├── disabled-normalisation-slider │ │ │ │ ├── disabled-normalisation-slider.component.id-template.html │ │ │ │ ├── disabled-normalisation-slider.component.title-template.html │ │ │ │ ├── disabled-normalisation-slider.component.template.html │ │ │ │ └── disabled-normalisation-slider.component.ts │ │ │ ├── selection-bar-gradient-slider │ │ │ │ ├── selection-bar-gradient-slider.component.id-template.html │ │ │ │ ├── selection-bar-gradient-slider.component.title-template.html │ │ │ │ ├── selection-bar-gradient-slider.component.template.html │ │ │ │ └── selection-bar-gradient-slider.component.ts │ │ │ ├── custom-display-function-slider │ │ │ │ ├── custom-display-function-slider.component.id-template.html │ │ │ │ ├── custom-display-function-slider.component.title-template.html │ │ │ │ ├── custom-display-function-slider.component.template.html │ │ │ │ └── custom-display-function-slider.component.ts │ │ │ ├── prevent-change-on-scroll-slider │ │ │ │ ├── prevent-change-on-scroll-slider.component.id-template.html │ │ │ │ ├── prevent-change-on-scroll-slider.component.title-template.html │ │ │ │ ├── prevent-change-on-scroll-slider.component.template.html │ │ │ │ └── prevent-change-on-scroll-slider.component.ts │ │ │ ├── selection-bar-from-value-slider │ │ │ │ ├── selection-bar-from-value-slider.component.id-template.html │ │ │ │ ├── selection-bar-from-value-slider.component.title-template.html │ │ │ │ ├── selection-bar-from-value-slider.component.template.html │ │ │ │ └── selection-bar-from-value-slider.component.ts │ │ │ ├── dynamic-color-selection-bar-slider │ │ │ │ ├── dynamic-color-selection-bar-slider.component.id-template.html │ │ │ │ ├── dynamic-color-selection-bar-slider.component.title-template.html │ │ │ │ ├── dynamic-color-selection-bar-slider.component.template.html │ │ │ │ └── dynamic-color-selection-bar-slider.component.ts │ │ │ ├── custom-html-display-function-slider │ │ │ │ ├── custom-html-display-function-slider.component.id-template.html │ │ │ │ ├── custom-html-display-function-slider.component.title-template.html │ │ │ │ ├── custom-html-display-function-slider.component.template.html │ │ │ │ └── custom-html-display-function-slider.component.ts │ │ │ ├── custom-combine-labels-function-slider │ │ │ │ ├── custom-combine-labels-function-slider.component.id-template.html │ │ │ │ ├── custom-combine-labels-function-slider.component.title-template.html │ │ │ │ ├── custom-combine-labels-function-slider.component.template.html │ │ │ │ └── custom-combine-labels-function-slider.component.ts │ │ │ ├── intermediate-ticks-values-range-slider │ │ │ │ ├── intermediate-ticks-values-range-slider.component.id-template.html │ │ │ │ ├── intermediate-ticks-values-range-slider.component.title-template.html │ │ │ │ ├── intermediate-ticks-values-range-slider.component.template.html │ │ │ │ └── intermediate-ticks-values-range-slider.component.ts │ │ │ └── index.ts │ │ ├── app.component.html │ │ ├── docs.component.html │ │ ├── header.component.scss │ │ ├── demos.component.ts │ │ ├── docs.component.ts │ │ ├── docs.component.scss │ │ ├── app.component.scss │ │ ├── header.component.ts │ │ ├── demos.component.scss │ │ ├── home.component.scss │ │ ├── home.component.ts │ │ ├── header.component.html │ │ ├── app-router.config.ts │ │ ├── demos.component.html │ │ ├── app.component.ts │ │ └── app.module.ts │ ├── favicon.ico │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── .gitignore │ ├── typings.d.ts │ ├── tsconfig.spec.json │ ├── test.ts │ ├── main.ts │ ├── tsconfig.app.json │ ├── index.html │ ├── styles.scss │ ├── karma.conf.js │ └── .eslintrc.json └── ngx-slider │ ├── lib │ ├── tooltip-wrapper.component.scss │ ├── main.scss │ ├── pointer-type.ts │ ├── change-context.ts │ ├── event-listener.ts │ ├── tooltip-wrapper.component.html │ ├── public_api.json │ ├── tooltip-wrapper.component.ts │ ├── math-helper.ts │ ├── slider.module.ts │ ├── slider.component.spec.ts │ ├── slider-label.directive.ts │ ├── slider-handle.directive.ts │ ├── variables.scss │ ├── value-helper.ts │ ├── math-helper.spec.ts │ ├── value-helper.spec.ts │ ├── event-listener-helper.ts │ └── slider.component.html │ ├── .gitignore │ ├── ng-package.json │ ├── package.json.template │ ├── LICENSE │ └── .eslintrc.json ├── .vscode └── settings.json ├── assets ├── logo.png ├── range-slider.png ├── ticks-slider.png ├── simple-slider.png ├── styled-slider.png ├── vertical-slider.png ├── customised-slider.png ├── slider-anatomy-bars.png ├── slider-anatomy-ticks.png ├── slider-not-refreshed.png ├── slider-anatomy-labels.png └── slider-anatomy-combined-label.png ├── e2e ├── utils │ ├── element-location.ts │ ├── element-size.ts │ ├── index.ts │ └── custom-interactions.ts ├── main-page.spec.ts ├── limited-slider.spec.ts ├── limited-range-slider.spec.ts └── no-switching-range-slider.spec.ts ├── .prettierrc ├── archive ├── ng5-slider-v1.2.6-site-archive.zip └── README.md ├── typedoc.json ├── .editorconfig ├── scripts ├── scss-bundle-workspace │ └── package.json ├── bundle-scss.js ├── publish-gh-pages.sh ├── copy-assets-from-node-modules.js ├── utils.js ├── generate-lib-files.js ├── esm │ └── generate-demo-app-docs.mjs └── generate-demo-app-snippets.js ├── .gitignore ├── tsconfig.json ├── LICENSE ├── TOOLTIPS.md ├── playwright.config.ts ├── UPGRADING.md ├── KNOWN_ISSUES.md ├── package.json ├── typedoc └── README.md └── angular.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.19.3 2 | -------------------------------------------------------------------------------- /src/demo-app/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2 3 | } -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/logo.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/.gitignore: -------------------------------------------------------------------------------- 1 | # HTML templates are auto-generated 2 | *.component.html -------------------------------------------------------------------------------- /src/demo-app/app/snippets/date-slider/date-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | date-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/range-slider/range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/date-slider/date-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with dates -------------------------------------------------------------------------------- /src/demo-app/app/snippets/range-slider/range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/simple-slider/simple-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | simple-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/simple-slider/simple-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Simple slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/styled-slider/styled-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | styled-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-slider/ticks-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-slider/ticks-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks -------------------------------------------------------------------------------- /e2e/utils/element-location.ts: -------------------------------------------------------------------------------- 1 | export interface ElementLocation { 2 | x: number; 3 | y: number; 4 | } -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-slider/disabled-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Disabled slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-slider/limited-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | limited-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/stepped-slider/stepped-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | stepped-slider 2 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/tooltip-wrapper.component.scss: -------------------------------------------------------------------------------- 1 | .ngx-slider-inner-tooltip { 2 | height: 100%; 3 | } -------------------------------------------------------------------------------- /assets/range-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/range-slider.png -------------------------------------------------------------------------------- /assets/ticks-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/ticks-slider.png -------------------------------------------------------------------------------- /e2e/utils/element-size.ts: -------------------------------------------------------------------------------- 1 | export interface ElementSize { 2 | width: number; 3 | height: number; 4 | } -------------------------------------------------------------------------------- /src/demo-app/app/snippets/alphabet-slider/alphabet-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | alphabet-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/alphabet-slider/alphabet-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with alphabet -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-slider/disabled-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | disabled-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/log-scale-slider/log-scale-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | log-scale-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/read-only-slider/read-only-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | read-only-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/read-only-slider/read-only-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Read-only slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/styled-slider/styled-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom style -------------------------------------------------------------------------------- /src/demo-app/app/snippets/vertical-sliders/vertical-sliders.component.id-template.html: -------------------------------------------------------------------------------- 1 | vertical-sliders 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/vertical-sliders/vertical-sliders.component.title-template.html: -------------------------------------------------------------------------------- 1 | Vertical sliders -------------------------------------------------------------------------------- /assets/simple-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/simple-slider.png -------------------------------------------------------------------------------- /assets/styled-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/styled-slider.png -------------------------------------------------------------------------------- /assets/vertical-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/vertical-slider.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/push-range-slider/push-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | push-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/user-events-slider/user-events-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | user-events-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/src/demo-app/favicon.ico -------------------------------------------------------------------------------- /src/ngx-slider/lib/main.scss: -------------------------------------------------------------------------------- 1 | @import "slider.component.scss"; 2 | @import "tooltip-wrapper.component.scss"; 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } 7 | -------------------------------------------------------------------------------- /assets/customised-slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/customised-slider.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-scale-slider/custom-scale-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-scale-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-slider/custom-ticks-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-ticks-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-slider/limited-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider limited to 10 through 90 -------------------------------------------------------------------------------- /src/demo-app/app/snippets/log-scale-slider/log-scale-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with logarithmic scale -------------------------------------------------------------------------------- /src/demo-app/app/snippets/right-to-left-slider/right-to-left-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Right to left slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/stepped-slider/stepped-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom step value -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-slider/ticks-values-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-values-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/user-events-slider/user-events-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | User events slider 2 | -------------------------------------------------------------------------------- /src/demo-app/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/src/demo-app/assets/logo.png -------------------------------------------------------------------------------- /assets/slider-anatomy-bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/slider-anatomy-bars.png -------------------------------------------------------------------------------- /assets/slider-anatomy-ticks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/slider-anatomy-ticks.png -------------------------------------------------------------------------------- /assets/slider-not-refreshed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/slider-not-refreshed.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-scale-slider/custom-scale-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom scale -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-options-slider/dynamic-options-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Dynamic options slider -------------------------------------------------------------------------------- /src/demo-app/app/snippets/floating-point-slider/floating-point-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | floating-point-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-range-slider/limited-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | limited-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/manual-refresh-slider/manual-refresh-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | manual-refresh-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/right-to-left-slider/right-to-left-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | right-to-left-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-slider/selection-bar-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | selection-bar-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-tooltips-slider/ticks-tooltips-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-tooltips-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-slider/ticks-values-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks and values -------------------------------------------------------------------------------- /src/demo-app/app/snippets/trigger-focus-slider/trigger-focus-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | trigger-focus-slider 2 | -------------------------------------------------------------------------------- /assets/slider-anatomy-labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/slider-anatomy-labels.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-slider/draggable-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | draggable-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-slider/draggable-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with draggable range -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-options-slider/dynamic-options-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | dynamic-options-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/manual-refresh-slider/manual-refresh-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with manual refresh -------------------------------------------------------------------------------- /src/demo-app/app/snippets/floating-point-slider/floating-point-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with floating point values -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-slider/selection-bar-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with visible selection bar -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-tooltips-slider/ticks-tooltips-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks and tooltips -------------------------------------------------------------------------------- /src/demo-app/app/snippets/trigger-focus-slider/trigger-focus-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with manual focus trigger -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-legend-slider/custom-ticks-legend-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-ticks-legend-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-slider/custom-ticks-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks at specific positions -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-tick-color-slider/dynamic-tick-color-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | dynamic-tick-color-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-tick-color-slider/dynamic-tick-color-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with dynamic tick color -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamically-created-sliders/dynamically-created-sliders.component.title-template.html: -------------------------------------------------------------------------------- 1 | Dynamically created sliders -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-slider/intermediate-ticks-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | intermediate-ticks-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/no-switching-range-slider/no-switching-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | no-switching-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-range-slider/reactive-form-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | reactive-form-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-range-slider/reactive-form-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider in reactive form -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-range-slider/ticks-values-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-values-range-slider 2 | -------------------------------------------------------------------------------- /assets/slider-anatomy-combined-label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/assets/slider-anatomy-combined-label.png -------------------------------------------------------------------------------- /src/demo-app/app/snippets/date-slider/date-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-only-slider/draggable-range-only-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | draggable-range-only-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-only-slider/draggable-range-only-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with draggable range only -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-pointer-color-slider/dynamic-pointer-color-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | dynamic-pointer-color-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamically-created-sliders/dynamically-created-sliders.component.id-template.html: -------------------------------------------------------------------------------- 1 | dynamically-created-sliders 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-slider/limited-slider.component.template.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/no-switching-range-slider/no-switching-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider with noSwitching=true -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-simple-slider/reactive-form-simple-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | reactive-form-simple-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-simple-slider/reactive-form-simple-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Simple slider in reactive form -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-at-end-slider/selection-bar-at-end-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | selection-bar-at-end-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-custom-tooltips-slider/ticks-custom-tooltips-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-custom-tooltips-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-slider/ticks-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-range-slider/ticks-values-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider with ticks and values -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-tooltips-slider/ticks-values-tooltips-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | ticks-values-tooltips-slider 2 | -------------------------------------------------------------------------------- /archive/ng5-slider-v1.2.6-site-archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular-slider/ngx-slider/HEAD/archive/ng5-slider-v1.2.6-site-archive.zip -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-legend-function-slider/custom-legend-function-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-legend-function-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-legend-slider/custom-ticks-legend-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks values and legend -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-normalisation-slider/disabled-normalisation-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | disabled-normalisation-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-pointer-color-slider/dynamic-pointer-color-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with dynamic pointer color -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-slider/intermediate-ticks-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks at intermediate values -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-gradient-slider/selection-bar-gradient-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | selection-bar-gradient-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/stepped-slider/stepped-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment: any = { 2 | production: true, 3 | enableExternalImages: true 4 | }; 5 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-display-function-slider/custom-display-function-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-display-function-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-display-function-slider/custom-display-function-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom display function -------------------------------------------------------------------------------- /src/demo-app/app/snippets/log-scale-slider/log-scale-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/prevent-change-on-scroll-slider/prevent-change-on-scroll-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | prevent-change-on-scroll-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-from-value-slider/selection-bar-from-value-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | selection-bar-from-value-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-gradient-slider/selection-bar-gradient-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with selection bar gradient -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-slider/ticks-values-slider.component.template.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-tooltips-slider/ticks-values-tooltips-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with tooltips for tick values -------------------------------------------------------------------------------- /src/demo-app/app/app.component.html: -------------------------------------------------------------------------------- 1 | @if (!testMode) { 2 | 3 | } 4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-scale-slider/custom-scale-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-slider/custom-ticks-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-normalisation-slider/disabled-normalisation-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with disabled input normalisation -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-range-slider/limited-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider with the range limited to 10 through 50 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-at-end-slider/selection-bar-at-end-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with visible selection bar at the end -------------------------------------------------------------------------------- /src/demo-app/app/docs.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-color-selection-bar-slider/dynamic-color-selection-bar-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | dynamic-color-selection-bar-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/floating-point-slider/floating-point-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/right-to-left-slider/right-to-left-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-slider/selection-bar-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-custom-tooltips-slider/ticks-custom-tooltips-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks and customised tooltips 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-tooltips-slider/ticks-tooltips-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-html-display-function-slider/custom-html-display-function-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-html-display-function-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-legend-function-slider/custom-legend-function-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with ticks values and custom legend function -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-color-selection-bar-slider/dynamic-color-selection-bar-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with dynamic selection bar color -------------------------------------------------------------------------------- /src/demo-app/app/snippets/push-range-slider/push-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider with minimum range of 10, maximum of 30 and pushRange option -------------------------------------------------------------------------------- /src/ngx-slider/lib/pointer-type.ts: -------------------------------------------------------------------------------- 1 | /** Pointer type */ 2 | export enum PointerType { 3 | /** Low pointer */ 4 | Min, 5 | /** High pointer */ 6 | Max 7 | } 8 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-combine-labels-function-slider/custom-combine-labels-function-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | custom-combine-labels-function-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-combine-labels-function-slider/custom-combine-labels-function-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom combine labels function -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-legend-slider/custom-ticks-legend-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-tick-color-slider/dynamic-tick-color-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-slider/intermediate-ticks-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-values-range-slider/intermediate-ticks-values-range-slider.component.id-template.html: -------------------------------------------------------------------------------- 1 | intermediate-ticks-values-range-slider 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/prevent-change-on-scroll-slider/prevent-change-on-scroll-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Prevent change on page scroll using touch gesture 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-at-end-slider/selection-bar-at-end-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-from-value-slider/selection-bar-from-value-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with visible selection bar from specified value -------------------------------------------------------------------------------- /src/demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | # The following assets are copied from node_modules/ packages 2 | assets/bootstrap-icons.svg 3 | assets/bootstrap.min.css 4 | assets/prism.min.css 5 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-legend-function-slider/custom-legend-function-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-pointer-color-slider/dynamic-pointer-color-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-tooltips-slider/ticks-values-tooltips-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-range-slider/limited-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/push-range-slider/push-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-from-value-slider/selection-bar-from-value-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-html-display-function-slider/custom-html-display-function-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Slider with custom display function using HTML formatting -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-color-selection-bar-slider/dynamic-color-selection-bar-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | // eslint-disable-next-line no-var 3 | declare var module: NodeModule; 4 | interface NodeModule { 5 | id: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-slider/draggable-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-values-range-slider/intermediate-ticks-values-range-slider.component.title-template.html: -------------------------------------------------------------------------------- 1 | Range slider with ticks and values at intermediate positions -------------------------------------------------------------------------------- /src/demo-app/app/snippets/alphabet-slider/alphabet-slider.component.template.html: -------------------------------------------------------------------------------- 1 | Current letter: {{indexToLetter(value)}} 2 | 3 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/no-switching-range-slider/no-switching-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-range-slider/ticks-values-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/header.component.scss: -------------------------------------------------------------------------------- 1 | .fork-me-ribbon { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | border: 0; 6 | } 7 | 8 | .navbar { 9 | background-color: #b6ddfa; 10 | } -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-only-slider/draggable-range-only-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-display-function-slider/custom-display-function-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-gradient-slider/selection-bar-gradient-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/simple-slider/simple-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

Value:

2 | 3 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/styled-slider/styled-slider.component.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /src/ngx-slider/lib/change-context.ts: -------------------------------------------------------------------------------- 1 | import { PointerType } from './pointer-type'; 2 | 3 | export class ChangeContext { 4 | value: number; 5 | highValue?: number; 6 | pointerType: PointerType; 7 | } 8 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-html-display-function-slider/custom-html-display-function-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /archive/README.md: -------------------------------------------------------------------------------- 1 | This directory serves as an archive for old content that may still be needed for reference. 2 | 3 | The intention is to keep it without changes or renames so that Github links will continue working. 4 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-combine-labels-function-slider/custom-combine-labels-function-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-values-range-slider/intermediate-ticks-values-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["**/e2e/**"], 3 | "compilerOptions": { 4 | "paths": { 5 | "@local/ngx-slider": [ 6 | "./src/ngx-slider/lib/public_api.ts" 7 | ] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /e2e/main-page.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test('main page has expected title', async ({ page }) => { 4 | await page.goto('/'); 5 | 6 | await expect(page).toHaveTitle(/ngx-slider/); 7 | }); 8 | -------------------------------------------------------------------------------- /e2e/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { expect } from './custom-matchers'; 2 | export { ElementLocation } from './element-location'; 3 | export { ElementSize } from './element-size'; 4 | export { mouseDragRelative, touchDragRelative } from './custom-interactions'; -------------------------------------------------------------------------------- /src/demo-app/app/snippets/read-only-slider/read-only-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamically-created-sliders/dynamically-created-sliders.component.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /src/demo-app/app/snippets/prevent-change-on-scroll-slider/prevent-change-on-scroll-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

Value:

2 | 3 | -------------------------------------------------------------------------------- /src/ngx-slider/.gitignore: -------------------------------------------------------------------------------- 1 | # package.json is auto-generated from package.json.template and main package.json 2 | package.json 3 | 4 | # public_api.ts is autogenerated from public_api.json 5 | public_api.ts 6 | 7 | # README.md is copied from main README.md 8 | README.md 9 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-slider/disabled-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/event-listener.ts: -------------------------------------------------------------------------------- 1 | import { Subject, Subscription } from 'rxjs'; 2 | 3 | export class EventListener { 4 | eventName: string = null; 5 | events: Subject = null; 6 | eventsSubscription: Subscription = null; 7 | teardownCallback: () => void = null; 8 | } 9 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-options-slider/dynamic-options-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | New ceil: 4 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/range-slider/range-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

Min value:

2 |

Max value:

3 | 4 | -------------------------------------------------------------------------------- /src/ngx-slider/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "lib/public_api.ts" 5 | }, 6 | "dest": "../../dist/ngx-slider", 7 | "allowedNonPeerDependencies": ["detect-passive-events", "rxjs", "rxjs-compat"] 8 | } 9 | -------------------------------------------------------------------------------- /src/demo-app/app/demos.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-demos', 5 | templateUrl: './demos.component.html', 6 | styleUrls: ['./demos.component.scss'], 7 | standalone: false 8 | }) 9 | export class DemosComponent { 10 | } 11 | -------------------------------------------------------------------------------- /src/demo-app/app/docs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-demos', 5 | templateUrl: './docs.component.html', 6 | styleUrls: ['./docs.component.scss'], 7 | standalone: false 8 | }) 9 | export class DocsComponent { 10 | } 11 | -------------------------------------------------------------------------------- /src/demo-app/app/docs.component.scss: -------------------------------------------------------------------------------- 1 | .docs-iframe-container { 2 | display: flex; 3 | height: 100%; 4 | flex-direction: column; 5 | overflow: hidden; 6 | } 7 | 8 | .docs-iframe-container iframe { 9 | width: 100%; 10 | height: 100%; 11 | border: none; 12 | margin: 0; 13 | padding: 0; 14 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-normalisation-slider/disabled-normalisation-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

Min value:

2 |

Max value:

3 | 4 | -------------------------------------------------------------------------------- /src/demo-app/app/app.component.scss: -------------------------------------------------------------------------------- 1 | ::ng-deep { 2 | main { 3 | flex-grow: 1; 4 | } 5 | 6 | .test-mode { 7 | /* Hide code snippets in test mode, so that we don't get the annoying scroll bar */ 8 | .snippet-code-tabset { 9 | display: none; 10 | } 11 | .snippet-code-content { 12 | display: none; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/demo-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts" 12 | ], 13 | "include": [ 14 | "../ngx-slider/**/*.spec.ts", 15 | "../ngx-slider/**/*.d.ts" 16 | ] 17 | } -------------------------------------------------------------------------------- /src/ngx-slider/lib/tooltip-wrapper.component.html: -------------------------------------------------------------------------------- 1 | @if (template) { 2 | 3 | } 4 | 5 | @if (!template) { 6 |
7 | {{content}} 8 |
9 | } -------------------------------------------------------------------------------- /src/demo-app/test.ts: -------------------------------------------------------------------------------- 1 | import "zone.js/testing"; 2 | 3 | import { getTestBed } from "@angular/core/testing"; 4 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; 5 | 6 | // First, initialize the Angular testing environment. 7 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-range-slider/reactive-form-range-slider.component.template.html: -------------------------------------------------------------------------------- 1 |
2 |

Low value: {{ sliderForm.value.sliderControl[0] }}

3 |

High value: {{ sliderForm.value.sliderControl[1] }}

4 |

5 | 6 |
7 | -------------------------------------------------------------------------------- /src/demo-app/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.log(err)); 14 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/simple-slider/simple-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-simple-slider', 6 | templateUrl: './simple-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SimpleSliderComponent { 10 | value: number = 100; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 250 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-custom-tooltips-slider/ticks-custom-tooltips-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
{{content}}
5 |
6 |
7 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-slider/ticks-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-slider', 6 | templateUrl: './ticks-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showTicks: true 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/stepped-slider/stepped-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-stepped-slider', 6 | templateUrl: './stepped-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SteppedSliderComponent { 10 | value: number = 12; 11 | options: Options = { 12 | floor: 10, 13 | ceil: 100, 14 | step: 5 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/range-slider/range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-range-slider', 6 | templateUrl: './range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class RangeSliderComponent { 10 | minValue: number = 50; 11 | maxValue: number = 200; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 250 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/trigger-focus-slider/trigger-focus-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/demo-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/demo-app", 5 | "baseUrl": ".", 6 | "types": [], 7 | "paths": { 8 | "@local/ngx-slider": [ 9 | "../ngx-slider/lib/public_api.ts" 10 | ] 11 | } 12 | }, 13 | "files": [ 14 | "main.ts" 15 | ], 16 | "include": [ 17 | "**/*.d.ts" 18 | ], 19 | "exclude": [ 20 | "test.ts", 21 | "**/*.spec.ts" 22 | ] 23 | } -------------------------------------------------------------------------------- /scripts/scss-bundle-workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scss-bundle-workspace", 3 | "version": "1.0.0", 4 | "description": "Separate workspace for scss-bundle which unfortunately clashes with other packages in the main package.json file", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Piotr Dziwinski", 10 | "license": "MIT", 11 | "dependencies": { 12 | "scss-bundle": "^3.1.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/demo-app/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment: any = { 7 | production: false, 8 | enableExternalImages: true 9 | }; 10 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/public_api.json: -------------------------------------------------------------------------------- 1 | { 2 | "exports": [ 3 | { 4 | "what": ["NgxSliderModule"], 5 | "file": "./slider.module" 6 | }, 7 | { 8 | "what": ["SliderComponent"], 9 | "file": "./slider.component" 10 | }, 11 | { 12 | "what": "*", 13 | "file": "./change-context" 14 | }, 15 | { 16 | "what": "*", 17 | "file": "./pointer-type" 18 | }, 19 | { 20 | "what": "*", 21 | "file": "./options" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/floating-point-slider/floating-point-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-floating-point-slider', 6 | templateUrl: './floating-point-slider.component.html', 7 | standalone: false 8 | }) 9 | export class FloatingPointSliderComponent { 10 | value: number = 0.5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 2, 14 | step: 0.1 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/log-scale-slider/log-scale-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-log-scale-slider', 6 | templateUrl: './log-scale-slider.component.html', 7 | standalone: false 8 | }) 9 | export class LogScaleSliderComponent { 10 | value: number = 1; 11 | options: Options = { 12 | floor: 1, 13 | ceil: 100, 14 | logScale: true, 15 | showTicks: true 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-slider/selection-bar-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-selection-bar-slider', 6 | templateUrl: './selection-bar-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SelectionBarSliderComponent { 10 | value: number = 10; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showSelectionBar: true 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-slider/limited-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-limited-slider', 6 | templateUrl: './limited-slider.component.html', 7 | standalone: false 8 | }) 9 | export class LimitedSliderComponent { 10 | value: number = 50; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 100, 14 | step: 1, 15 | minLimit: 10, 16 | maxLimit: 90 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-slider/custom-ticks-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-ticks-slider', 6 | templateUrl: './custom-ticks-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomTicksSliderComponent { 10 | value: number = 55; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 100, 14 | ticksArray: [0, 10, 25, 50, 100] 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/user-events-slider/user-events-slider.component.template.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Events: 10 | 13 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/right-to-left-slider/right-to-left-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-right-to-left-slider', 6 | templateUrl: './right-to-left-slider.component.html', 7 | standalone: false 8 | }) 9 | export class RightToLeftSliderComponent { 10 | value: number = 20; 11 | options: Options = { 12 | floor: 10, 13 | ceil: 100, 14 | step: 5, 15 | rightToLeft: true 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/ngx-slider/package.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "keysToCopyFromMainPackageJson": [ 3 | "name", 4 | "version", 5 | "description", 6 | "keywords", 7 | "author", 8 | "license", 9 | "repository", 10 | "bugs", 11 | "homepage" 12 | ], 13 | "dependenciesToCopyFromMainPackageJson": [ 14 | "detect-passive-events", 15 | "rxjs" 16 | ], 17 | "dependenciesToCopyAsPeerDependenciesFromMainPackageJson": [ 18 | "@angular/core", 19 | "@angular/common", 20 | "@angular/forms" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-simple-slider/reactive-form-simple-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

Value: {{ sliderControl.value }}

2 |

Dirty: {{ sliderControl.dirty }}

3 | 4 |

5 | 6 | 7 | 8 |

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-at-end-slider/selection-bar-at-end-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-selection-bar-at-end-slider', 6 | templateUrl: './selection-bar-at-end-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SelectionBarAtEndSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showSelectionBarEnd: true 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-slider/draggable-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-draggable-range-slider', 6 | templateUrl: './draggable-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DraggableRangeSliderComponent { 10 | minValue: number = 1; 11 | maxValue: number = 8; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 10, 15 | draggableRange: true 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-slider/ticks-values-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-values-slider', 6 | templateUrl: './ticks-values-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksValuesSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | step: 1, 15 | showTicks: true, 16 | showTicksValues: true 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-slider/intermediate-ticks-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-intermediate-ticks-slider', 6 | templateUrl: './intermediate-ticks-slider.component.html', 7 | standalone: false 8 | }) 9 | export class IntermediateTicksSliderComponent { 10 | value: number = 55; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 100, 14 | showTicks: true, 15 | tickStep: 10 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/tooltip-wrapper.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, TemplateRef } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'ngx-slider-tooltip-wrapper', 5 | templateUrl: './tooltip-wrapper.component.html', 6 | styleUrls: ['./tooltip-wrapper.component.scss'], 7 | standalone: false 8 | }) 9 | export class TooltipWrapperComponent { 10 | @Input() 11 | template: TemplateRef; 12 | 13 | @Input() 14 | tooltip: string; 15 | 16 | @Input() 17 | placement: string; 18 | 19 | @Input() 20 | content: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-range-slider/ticks-values-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-values-range-slider', 6 | templateUrl: './ticks-values-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksValuesRangeSliderComponent { 10 | minValue: number = 1; 11 | maxValue: number = 8; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 10, 15 | showTicksValues: true 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-from-value-slider/selection-bar-from-value-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-selection-bar-from-value-slider', 6 | templateUrl: './selection-bar-from-value-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SelectionBarFromValueSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: -10, 13 | ceil: 10, 14 | showSelectionBarFromValue: 0 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/limited-range-slider/limited-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-limited-range-slider', 6 | templateUrl: './limited-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class LimitedRangeSliderComponent { 10 | minValue: number = 40; 11 | maxValue: number = 60; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100, 15 | step: 1, 16 | minRange: 10, 17 | maxRange: 50 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/draggable-range-only-slider/draggable-range-only-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-draggable-range-only-slider', 6 | templateUrl: './draggable-range-only-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DraggableRangeOnlySliderComponent { 10 | minValue: number = 4; 11 | maxValue: number = 6; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 10, 15 | draggableRangeOnly: true 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/styled-slider/styled-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-styled-slider', 6 | templateUrl: './styled-slider.component.html', 7 | styleUrls: ['./styled-slider.component.scss'], 8 | standalone: false 9 | }) 10 | export class StyledSliderComponent { 11 | minValue: number = 10; 12 | maxValue: number = 90; 13 | options: Options = { 14 | floor: 0, 15 | ceil: 100, 16 | step: 10, 17 | showTicks: true 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/manual-refresh-slider/manual-refresh-slider.component.template.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 |

7 | 8 |
9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/no-switching-range-slider/no-switching-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-no-switching-range-slider', 6 | templateUrl: './no-switching-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class NoSwitchingRangeSliderComponent { 10 | minValue: number = 10; 11 | maxValue: number = 90; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100, 15 | step: 1, 16 | noSwitching: true 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/push-range-slider/push-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-push-range-slider', 6 | templateUrl: './push-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class PushRangeSliderComponent { 10 | minValue: number = 60; 11 | maxValue: number = 70; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100, 15 | step: 1, 16 | minRange: 10, 17 | maxRange: 30, 18 | pushRange: true 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-tooltips-slider/ticks-tooltips-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-tooltips-slider', 6 | templateUrl: './ticks-tooltips-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksTooltipsSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showTicks: true, 15 | ticksTooltip: (v: number): string => { 16 | return 'Tooltip for ' + v; 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-normalisation-slider/disabled-normalisation-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-disabled-normalisation-slider', 6 | templateUrl: './disabled-normalisation-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DisabledNormalisationSliderComponent { 10 | minValue: number = 50; 11 | maxValue: number = 200; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 250, 15 | step: 10, 16 | enforceStep: false, 17 | enforceRange: false, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-custom-tooltips-slider/ticks-custom-tooltips-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-custom-tooltips-slider', 6 | templateUrl: './ticks-custom-tooltips-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksCustomTooltipsSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showTicks: true, 15 | ticksTooltip: (v: number): string => { 16 | return 'Tooltip for ' + v; 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-legend-function-slider/custom-legend-function-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-legend-function-slider', 6 | templateUrl: './custom-legend-function-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomLegendFunctionSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showTicks: true, 15 | getLegend: (value: number): string => { 16 | return 'T' + value; 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-display-function-slider/custom-display-function-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-display-function-slider', 6 | templateUrl: './custom-display-function-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomDisplayFunctionSliderComponent { 10 | minValue: number = 100; 11 | maxValue: number = 400; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 500, 15 | translate: (value: number): string => { 16 | return '$' + value; 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/ticks-values-tooltips-slider/ticks-values-tooltips-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-ticks-values-tooltips-slider', 6 | templateUrl: './ticks-values-tooltips-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TicksValuesTooltipsSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 10, 14 | showTicksValues: true, 15 | ticksValuesTooltip: (v: number): string => { 16 | return 'Tooltip for ' + v; 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/selection-bar-gradient-slider/selection-bar-gradient-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-selection-bar-gradient-slider', 6 | templateUrl: './selection-bar-gradient-slider.component.html', 7 | standalone: false 8 | }) 9 | export class SelectionBarGradientSliderComponent { 10 | minValue: number = 0; 11 | maxValue: number = 80; 12 | options: Options = { 13 | ceil: 100, 14 | showSelectionBar: true, 15 | selectionBarGradient: { 16 | from: 'white', 17 | to: '#FC0' 18 | } 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/intermediate-ticks-values-range-slider/intermediate-ticks-values-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-intermediate-ticks-values-range-slider', 6 | templateUrl: './intermediate-ticks-values-range-slider.component.html', 7 | standalone: false 8 | }) 9 | export class IntermediateTicksValuesRangeSliderComponent { 10 | minValue: number = 15; 11 | maxValue: number = 85; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100, 15 | showTicksValues: true, 16 | tickStep: 10, 17 | tickValueStep: 10 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/read-only-slider/read-only-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-read-only-slider', 6 | templateUrl: './read-only-slider.component.html', 7 | standalone: false 8 | }) 9 | export class ReadOnlySliderComponent { 10 | readOnly: boolean = true; 11 | value: number = 50; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100, 15 | readOnly: true 16 | }; 17 | 18 | /* Due to the way Angular 2+ handles change detection, we have to create a new options object. */ 19 | onChangeReadOnly(): void { 20 | this.options = Object.assign({}, this.options, {readOnly: this.readOnly}); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/math-helper.ts: -------------------------------------------------------------------------------- 1 | /** Helper with mathematical functions */ 2 | export class MathHelper { 3 | /* Round numbers to a given number of significant digits */ 4 | static roundToPrecisionLimit(value: number, precisionLimit: number): number { 5 | return +( value.toPrecision(precisionLimit) ); 6 | } 7 | 8 | static isModuloWithinPrecisionLimit(value: number, modulo: number, precisionLimit: number): boolean { 9 | const limit: number = Math.pow(10, -precisionLimit); 10 | return Math.abs(value % modulo) <= limit || Math.abs(Math.abs(value % modulo) - modulo) <= limit; 11 | } 12 | 13 | static clampToRange(value: number, floor: number, ceil: number): number { 14 | return Math.min(Math.max(value, floor), ceil); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/prevent-change-on-scroll-slider/prevent-change-on-scroll-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostListener, EventEmitter } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-prevent-change-on-scroll-slider', 6 | templateUrl: './prevent-change-on-scroll-slider.component.html', 7 | standalone: false, 8 | }) 9 | export class PreventChangeOnScrollSliderComponent { 10 | value: number = 100; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 250 14 | }; 15 | emitOnScroll: EventEmitter = new EventEmitter; 16 | 17 | @HostListener('window:scroll', ['$event']) 18 | public onScroll(event: any): void { 19 | this.emitOnScroll.emit(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-options-slider/dynamic-options-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-dynamic-options-slider', 6 | templateUrl: './dynamic-options-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DynamicOptionsSliderComponent { 10 | value: number = 100; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 250 14 | }; 15 | 16 | setNewCeil(newCeil: number): void { 17 | // Due to change detection rules in Angular, we need to re-create the options object to apply the change 18 | const newOptions: Options = Object.assign({}, this.options); 19 | newOptions.ceil = newCeil; 20 | this.options = newOptions; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-range-slider/reactive-form-range-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'; 3 | import { Options } from '@local/ngx-slider'; 4 | 5 | @Component({ 6 | selector: 'app-reactive-form-range-slider', 7 | templateUrl: './reactive-form-range-slider.component.html', 8 | standalone: false 9 | }) 10 | export class ReactiveFormRangeSliderComponent { 11 | sliderForm: UntypedFormGroup = new UntypedFormGroup({ 12 | sliderControl: new UntypedFormControl([20, 80]), 13 | }); 14 | options: Options = { 15 | floor: 0, 16 | ceil: 100, 17 | step: 5, 18 | }; 19 | 20 | resetForm(): void { 21 | this.sliderForm.reset({ sliderControl: [20, 80] }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/trigger-focus-slider/trigger-focus-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter } from '@angular/core'; 2 | import { Options, PointerType } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-trigger-focus-slider', 6 | templateUrl: './trigger-focus-slider.component.html', 7 | standalone: false 8 | }) 9 | export class TriggerFocusSliderComponent { 10 | triggerFocus: EventEmitter = new EventEmitter(); 11 | minValue: number = 20; 12 | maxValue: number = 80; 13 | options: Options = { 14 | floor: 0, 15 | ceil: 100, 16 | step: 5 17 | }; 18 | 19 | PointerType: any = PointerType; // expose enum to the view 20 | 21 | focusSlider(pointerType: PointerType): void { 22 | this.triggerFocus.emit(pointerType); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-combine-labels-function-slider/custom-combine-labels-function-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-combine-labels-function-slider', 6 | templateUrl: './custom-combine-labels-function-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomCombineLabelsFunctionSliderComponent { 10 | minValue: number = 100; 11 | maxValue: number = 400; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 500, 15 | translate: (value: number): string => { 16 | return '$' + value; 17 | }, 18 | combineLabels: (minValue: string, maxValue: string): string => { 19 | return 'from ' + minValue + ' up to ' + maxValue; 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-ticks-legend-slider/custom-ticks-legend-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-ticks-legend-slider', 6 | templateUrl: './custom-ticks-legend-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomTicksLegendSliderComponent { 10 | value: number = 5; 11 | options: Options = { 12 | showTicksValues: true, 13 | stepsArray: [ 14 | {value: 1, legend: 'Very poor'}, 15 | {value: 2}, 16 | {value: 3, legend: 'Fair'}, 17 | {value: 4}, 18 | {value: 5, legend: 'Average'}, 19 | {value: 6}, 20 | {value: 7, legend: 'Good'}, 21 | {value: 8}, 22 | {value: 9, legend: 'Excellent'} 23 | ] 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/disabled-slider/disabled-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-disabled-slider', 6 | templateUrl: './disabled-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DisabledSliderComponent { 10 | disabled: boolean = true; 11 | minValue: number = 10; 12 | maxValue: number = 90; 13 | options: Options = { 14 | floor: 0, 15 | ceil: 100, 16 | step: 10, 17 | disabled: true, 18 | showTicks: true, 19 | draggableRange: true 20 | }; 21 | 22 | /* Due to the way Angular 2+ handles change detection, we have to create a new options object. */ 23 | onChangeDisabled(): void { 24 | this.options = Object.assign({}, this.options, {disabled: this.disabled}); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/reactive-form-simple-slider/reactive-form-simple-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { UntypedFormControl } from '@angular/forms'; 3 | import { Options } from '@local/ngx-slider'; 4 | 5 | @Component({ 6 | selector: 'app-reactive-form-simple-slider', 7 | templateUrl: './reactive-form-simple-slider.component.html', 8 | standalone: false 9 | }) 10 | export class ReactiveFormSimpleSliderComponent { 11 | sliderControl: UntypedFormControl = new UntypedFormControl(100); 12 | 13 | options: Options = { 14 | floor: 0, 15 | ceil: 250, 16 | }; 17 | 18 | disable(): void { 19 | this.sliderControl.disable(); 20 | } 21 | 22 | enable(): void { 23 | this.sliderControl.enable(); 24 | } 25 | 26 | resetForm(): void { 27 | this.sliderControl.reset(100); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-tick-color-slider/dynamic-tick-color-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-dynamic-tick-color-slider', 6 | templateUrl: './dynamic-tick-color-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DynamicTickColorSliderComponent { 10 | value: number = 0; 11 | options: Options = { 12 | ceil: 12, 13 | floor: 0, 14 | showSelectionBar: true, 15 | showTicks: true, 16 | getTickColor: (value: number): string => { 17 | if (value < 3) { 18 | return 'red'; 19 | } 20 | if (value < 6) { 21 | return 'orange'; 22 | } 23 | if (value < 9) { 24 | return 'yellow'; 25 | } 26 | return '#2AE02A'; 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-pointer-color-slider/dynamic-pointer-color-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-dynamic-pointer-color-slider', 6 | templateUrl: './dynamic-pointer-color-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DynamicPointerColorSliderComponent { 10 | value: number = 12; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 12, 14 | showSelectionBar: true, 15 | getPointerColor: (value: number): string => { 16 | if (value <= 3) { 17 | return 'red'; 18 | } 19 | if (value <= 6) { 20 | return 'orange'; 21 | } 22 | if (value <= 9) { 23 | return 'yellow'; 24 | } 25 | return '#2AE02A'; 26 | } 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamic-color-selection-bar-slider/dynamic-color-selection-bar-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-dynamic-color-selection-bar-slider', 6 | templateUrl: './dynamic-color-selection-bar-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DynamicColorSelectionBarSliderComponent { 10 | value: number = 12; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 12, 14 | showSelectionBar: true, 15 | getSelectionBarColor: (value: number): string => { 16 | if (value <= 3) { 17 | return 'red'; 18 | } 19 | if (value <= 6) { 20 | return 'orange'; 21 | } 22 | if (value <= 9) { 23 | return 'yellow'; 24 | } 25 | return '#2AE02A'; 26 | } 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/slider.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SliderComponent } from './slider.component'; 4 | import { SliderElementDirective } from './slider-element.directive'; 5 | import { SliderHandleDirective } from './slider-handle.directive'; 6 | import { SliderLabelDirective } from './slider-label.directive'; 7 | import { TooltipWrapperComponent } from './tooltip-wrapper.component'; 8 | 9 | /** 10 | * NgxSlider module 11 | * 12 | * The module exports the slider component 13 | */ 14 | @NgModule({ 15 | imports: [ 16 | CommonModule 17 | ], 18 | declarations: [ 19 | SliderComponent, 20 | SliderElementDirective, 21 | SliderHandleDirective, 22 | SliderLabelDirective, 23 | TooltipWrapperComponent 24 | ], 25 | exports: [ 26 | SliderComponent 27 | ] 28 | }) 29 | export class NgxSliderModule { } 30 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/date-slider/date-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options, LabelType } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-date-slider', 6 | templateUrl: './date-slider.component.html', 7 | standalone: false 8 | }) 9 | export class DateSliderComponent { 10 | dateRange: Date[] = this.createDateRange(); 11 | value: number = this.dateRange[0].getTime(); 12 | options: Options = { 13 | stepsArray: this.dateRange.map((date: Date) => { 14 | return { value: date.getTime() }; 15 | }), 16 | translate: (value: number, label: LabelType): string => { 17 | return new Date(value).toDateString(); 18 | } 19 | }; 20 | 21 | createDateRange(): Date[] { 22 | const dates: Date[] = []; 23 | for (let i: number = 1; i <= 31; i++) { 24 | dates.push(new Date(2018, 5, i)); 25 | } 26 | return dates; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/demo-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ngx-slider 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-html-display-function-slider/custom-html-display-function-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options, LabelType } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-html-display-function-slider', 6 | templateUrl: './custom-html-display-function-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomHtmlDisplayFunctionSliderComponent { 10 | minValue: number = 100; 11 | maxValue: number = 400; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 500, 15 | translate: (value: number, label: LabelType): string => { 16 | switch (label) { 17 | case LabelType.Low: 18 | return 'Min price: $' + value; 19 | case LabelType.High: 20 | return 'Max price: $' + value; 21 | default: 22 | return '$' + value; 23 | } 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /docs 7 | /tmp 8 | /out-tsc 9 | 10 | # dependencies 11 | /node_modules 12 | /scripts/scss-bundle-workspace/node_modules 13 | 14 | # github pages temporary directory 15 | /gh-pages 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | #!.vscode/tasks.json 30 | #!.vscode/launch.json 31 | !.vscode/extensions.json 32 | 33 | # misc 34 | /.angular/cache 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | 48 | # Playwright 49 | /test-results/ 50 | /playwright-report/ 51 | /blob-report/ 52 | /playwright/.cache/ 53 | -------------------------------------------------------------------------------- /src/demo-app/styles.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | font-family: "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "sans-serif"; 7 | } 8 | 9 | app-root { 10 | display: flex; 11 | flex-direction: column; 12 | height: 100%; 13 | } 14 | 15 | /* Override font in test mode to always ensure deterministic results no matter what fonts are installed */ 16 | .test-mode { 17 | font-family: "Helvetica" !important; 18 | 19 | /* Disable animations, too */ 20 | .ngx-slider { 21 | * { 22 | transition: none !important; 23 | } 24 | } 25 | } 26 | 27 | // override bootstrap classes for accessibility 28 | a { 29 | // used https://pinetools.com/darken-color 30 | // to make #007bff darker: #003d7f 31 | color: #003d7f; 32 | } 33 | 34 | .btn-info { 35 | // #17a2b8 > #0b515c 36 | background-color: #0b515c; 37 | color: white; 38 | } 39 | 40 | .btn-primary { 41 | // #007bff > #003d7f 42 | background-color: #003d7f; 43 | } 44 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/alphabet-slider/alphabet-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options, LabelType, CustomStepDefinition } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-alphabet-slider', 6 | templateUrl: './alphabet-slider.component.html', 7 | standalone: false 8 | }) 9 | export class AlphabetSliderComponent { 10 | alphabet: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 11 | value: number = this.letterToIndex('E'); 12 | options: Options = { 13 | stepsArray: this.alphabet.split('').map((letter: string): CustomStepDefinition => { 14 | return { value: this.letterToIndex(letter) }; 15 | }), 16 | translate: (value: number, label: LabelType): string => { 17 | return this.indexToLetter(value); 18 | } 19 | }; 20 | 21 | indexToLetter(index: number): string { 22 | return this.alphabet[index]; 23 | } 24 | 25 | letterToIndex(letter: string): number { 26 | return this.alphabet.indexOf(letter); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/bundle-scss.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const child_process = require('child_process'); 3 | 4 | function executeCommand(command, cwd) { 5 | console.log(`> ${command}`); 6 | const output = child_process.execSync(command, {cwd: cwd}); 7 | if (output instanceof Buffer) { 8 | console.log(output.toString('utf8')); 9 | } else { 10 | console.log(output); 11 | } 12 | } 13 | 14 | // scss-bundle has some nasty clashing dependencies 15 | // The only way I found for this to work is to use a separate workspace directory with only the scss-bundle in package.json 16 | const workspaceDir = path.resolve(__dirname, './scss-bundle-workspace'); 17 | const mainScss = path.resolve(__dirname, '../src/ngx-slider/lib/main.scss'); 18 | const distScss = path.resolve(__dirname, '../dist/ngx-slider/scss/ngx-slider.scss'); 19 | 20 | console.log('executing scss-bundle in separate workspace'); 21 | executeCommand('npm install', workspaceDir); 22 | executeCommand(`npx scss-bundle -e ${mainScss} -o ${distScss}`, workspaceDir); -------------------------------------------------------------------------------- /scripts/publish-gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Stop on error 4 | set -e 5 | 6 | # Get current revision 7 | rev=$(git rev-parse HEAD) 8 | # Get the current origin URL 9 | origin_url=$(git config remote.origin.url) 10 | 11 | # Build everything 12 | npm run build 13 | 14 | # Prepare a temporary clone of the local repository 15 | rm -rf gh-pages 16 | mkdir gh-pages 17 | pushd gh-pages &>/dev/null 18 | git clone -b gh-pages ../ ./ 19 | # Default origin will be set to the local repository, so we need to set the origin again under different name 20 | git remote add upstream ${origin_url} 21 | # Pull latest changes 22 | git pull --ff upstream gh-pages 23 | 24 | 25 | # Start fresh 26 | rm -rf * 27 | # Add demo app files 28 | cp -R ../dist/demo-app/browser/* ./ 29 | cp index.html 404.html 30 | # Workaround for files starting with underscore not showing up 31 | touch .nojekyll 32 | 33 | 34 | # All done - commit and push 35 | git add -A . 36 | git commit -m "Release github pages for ngx-slider ${rev}" 37 | git push upstream gh-pages 38 | 39 | popd &>/dev/null -------------------------------------------------------------------------------- /src/demo-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/demo-app/app/snippets/dynamically-created-sliders/dynamically-created-sliders.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | interface SliderDetails { 5 | value: number; 6 | highValue: number; 7 | floor: number; 8 | ceil: number; 9 | } 10 | 11 | @Component({ 12 | selector: 'app-dynamically-created-sliders', 13 | templateUrl: './dynamically-created-sliders.component.html', 14 | standalone: false 15 | }) 16 | export class DynamicallyCreatedSlidersComponent { 17 | sliders: SliderDetails[] = [ 18 | { 19 | value: -1, 20 | highValue: 2, 21 | floor: -5, 22 | ceil: 5 23 | }, 24 | { 25 | value: 1, 26 | highValue: 2, 27 | floor: 0, 28 | ceil: 5 29 | }, 30 | { 31 | value: 30, 32 | highValue: 60, 33 | floor: 0, 34 | ceil: 100 35 | } 36 | ]; 37 | 38 | sliderOptions(slider: SliderDetails): Options { 39 | return { 40 | floor: slider.floor, 41 | ceil: slider.ceil 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scripts/copy-assets-from-node-modules.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | function copyBootstrapCss() { 5 | const sourceFile = path.resolve(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'); 6 | const destinationFile = path.resolve(__dirname, '../src/demo-app/assets/bootstrap.min.css'); 7 | 8 | fs.copyFileSync(sourceFile, destinationFile); 9 | } 10 | 11 | function copyBootstrapIcons() { 12 | const sourceFile = path.resolve(__dirname, '../node_modules/bootstrap-icons/bootstrap-icons.svg'); 13 | const destinationFile = path.resolve(__dirname, '../src/demo-app/assets/bootstrap-icons.svg'); 14 | 15 | fs.copyFileSync(sourceFile, destinationFile); 16 | } 17 | 18 | function copyPrismjsCss() { 19 | const sourceFile = path.resolve(__dirname, '../node_modules/prismjs/themes/prism.min.css'); 20 | const destinationFile = path.resolve(__dirname, '../src/demo-app/assets/prism.min.css'); 21 | 22 | fs.copyFileSync(sourceFile, destinationFile); 23 | } 24 | 25 | copyBootstrapCss() 26 | copyBootstrapIcons() 27 | copyPrismjsCss() -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": false, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": false, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "esModuleInterop": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "bundler", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022", 20 | "useDefineForClassFields": false, 21 | "paths": { 22 | "@local/ngx-slider": [ 23 | "./src/ngx-slider/lib/public_api.ts" 24 | ] 25 | } 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/custom-scale-slider/custom-scale-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-custom-scale-slider', 6 | templateUrl: './custom-scale-slider.component.html', 7 | standalone: false 8 | }) 9 | export class CustomScaleSliderComponent { 10 | value: number = 50; 11 | options: Options = { 12 | floor: 0, 13 | ceil: 100, 14 | step: 10, 15 | showTicksValues: true, 16 | customValueToPosition: (val: number, minVal: number, maxVal: number): number => { 17 | val = Math.sqrt(val); 18 | minVal = Math.sqrt(minVal); 19 | maxVal = Math.sqrt(maxVal); 20 | const range: number = maxVal - minVal; 21 | return (val - minVal) / range; 22 | }, 23 | customPositionToValue: (percent: number, minVal: number, maxVal: number): number => { 24 | minVal = Math.sqrt(minVal); 25 | maxVal = Math.sqrt(maxVal); 26 | const value: number = percent * (maxVal - minVal) + minVal; 27 | return Math.pow(value, 2); 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/demo-app/app/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy, inject } from '@angular/core'; 2 | import { Router, NavigationEnd, Event } from '@angular/router'; 3 | 4 | import { environment } from '../environments/environment'; 5 | 6 | @Component({ 7 | selector: 'app-header', 8 | templateUrl: './header.component.html', 9 | styleUrls: ['./header.component.scss'], 10 | standalone: false 11 | }) 12 | export class HeaderComponent implements OnInit, OnDestroy { 13 | private router = inject(Router); 14 | 15 | navbarCollapsed: boolean = true; 16 | atRootUrl: boolean = false; 17 | atDocsUrl: boolean = false; 18 | urlSub: any; 19 | enableExternalImages: boolean = environment.enableExternalImages; 20 | 21 | ngOnInit(): void { 22 | this.urlSub = this.router.events.subscribe((event: Event) => { 23 | if (event instanceof NavigationEnd) { 24 | this.atRootUrl = event.url === '/' || event.url === '/home'; 25 | this.atDocsUrl = event.url.indexOf('/docs') === 0; 26 | } 27 | }); 28 | } 29 | 30 | ngOnDestroy(): void { 31 | this.urlSub.unsubscribe(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Rafal Zajac 4 | Copyright (c) 2018 Piotr Dziwinski 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/ngx-slider/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Rafal Zajac 4 | Copyright (c) 2018 Piotr Dziwinski 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/demo-app/app/demos.component.scss: -------------------------------------------------------------------------------- 1 | ::ng-deep { 2 | .snippet-card { 3 | margin-bottom: 3rem; 4 | 5 | .snippet-content { 6 | margin-bottom: 1.25rem; 7 | } 8 | 9 | .snippet-code-tabset { 10 | // Indent the tabs slightly 11 | margin: 0; 12 | padding: .5rem 1.25rem 0; 13 | // Remove the border to blend in with the code area 14 | border-bottom: none; 15 | 16 | .nav-item { 17 | .nav-link { 18 | // Style all tabs with border and background color, even the inactive ones 19 | border: 1px solid #dee2e6; 20 | background-color: #fdfdfd; 21 | color: #3e5bc5; 22 | 23 | &.active { 24 | // Make the active tab blend in with the code area 25 | background-color: #f5f2f0; 26 | border-bottom: 1px solid #f5f2f0; 27 | color: inherit; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .snippet-code-content { 34 | pre[class*=language-] { 35 | // Remove the gap between code area and the tab list 36 | margin-top: 0; 37 | // Surround with same border as the tab list 38 | border: 1px solid #dee2e6; 39 | border-radius: 4px; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/styled-slider/styled-slider.component.scss: -------------------------------------------------------------------------------- 1 | // We need to use ::ng-deep to overcome view encapsulation 2 | ::ng-deep { 3 | .custom-slider .ngx-slider .ngx-slider-bar { 4 | background: #ffe4d1; 5 | height: 2px; 6 | } 7 | .custom-slider .ngx-slider .ngx-slider-selection { 8 | background: orange; 9 | } 10 | 11 | .custom-slider .ngx-slider .ngx-slider-pointer { 12 | width: 8px; 13 | height: 16px; 14 | top: auto; /* to remove the default positioning */ 15 | bottom: 0; 16 | background-color: #333; 17 | border-top-left-radius: 3px; 18 | border-top-right-radius: 3px; 19 | } 20 | 21 | .custom-slider .ngx-slider .ngx-slider-pointer:after { 22 | display: none; 23 | } 24 | 25 | .custom-slider .ngx-slider .ngx-slider-bubble { 26 | bottom: 14px; 27 | } 28 | 29 | .custom-slider .ngx-slider .ngx-slider-bubble.ngx-slider-limit { 30 | font-weight: bold; 31 | color: orange; 32 | } 33 | 34 | .custom-slider .ngx-slider .ngx-slider-tick { 35 | width: 1px; 36 | height: 10px; 37 | margin-left: 4px; 38 | border-radius: 0; 39 | background: #ffe4d1; 40 | top: -1px; 41 | } 42 | 43 | .custom-slider .ngx-slider .ngx-slider-tick.ngx-slider-selected { 44 | background: orange; 45 | } 46 | } -------------------------------------------------------------------------------- /src/demo-app/app/snippets/vertical-sliders/vertical-sliders.component.template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 7 |
8 | 9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/user-events-slider/user-events-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options, ChangeContext, PointerType } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-user-events-slider', 6 | templateUrl: './user-events-slider.component.html', 7 | standalone: false 8 | }) 9 | export class UserEventsSliderComponent { 10 | minValue: number = 20; 11 | maxValue: number = 80; 12 | options: Options = { 13 | floor: 0, 14 | ceil: 100 15 | }; 16 | logText: string = ''; 17 | 18 | onUserChangeStart(changeContext: ChangeContext): void { 19 | this.logText += `onUserChangeStart(${this.getChangeContextString(changeContext)})\n`; 20 | } 21 | 22 | onUserChange(changeContext: ChangeContext): void { 23 | this.logText += `onUserChange(${this.getChangeContextString(changeContext)})\n`; 24 | } 25 | 26 | onUserChangeEnd(changeContext: ChangeContext): void { 27 | this.logText += `onUserChangeEnd(${this.getChangeContextString(changeContext)})\n`; 28 | } 29 | 30 | getChangeContextString(changeContext: ChangeContext): string { 31 | return `{pointerType: ${changeContext.pointerType === PointerType.Min ? 'Min' : 'Max'}, ` + 32 | `value: ${changeContext.value}, ` + 33 | `highValue: ${changeContext.highValue}}`; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/slider.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SliderComponent } from './slider.component'; 4 | import { SliderElementDirective } from './slider-element.directive'; 5 | import { SliderHandleDirective } from './slider-handle.directive'; 6 | import { SliderLabelDirective } from './slider-label.directive'; 7 | import { TooltipWrapperComponent } from './tooltip-wrapper.component'; 8 | 9 | describe('SliderComponent', () => { 10 | let component: SliderComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(waitForAsync(() => { 14 | TestBed.configureTestingModule({ 15 | declarations: [ 16 | SliderComponent, 17 | SliderElementDirective, 18 | SliderHandleDirective, 19 | SliderLabelDirective, 20 | TooltipWrapperComponent 21 | ] 22 | }) 23 | .compileComponents(); 24 | })); 25 | 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(SliderComponent); 28 | component = fixture.componentInstance; 29 | component.options = { 30 | floor: 0, 31 | ceil: 10 32 | }; 33 | component.value = 5; 34 | fixture.detectChanges(); 35 | }); 36 | 37 | it('should create', () => { 38 | expect(component).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /TOOLTIPS.md: -------------------------------------------------------------------------------- 1 | # Tooltips 2 | 3 | Prior to version 1.1, the library used to have a dependency on ng-bootstrap to support tooltips. 4 | 5 | As of version 1.1, this dependency has been removed, and tooltips are rendered by default by using the standard HTML `title` attribute (e.g. `
Some content
`), but it's also possible to customise this behaviour by specifying a custom template. 6 | 7 | When using a custom template, elements that would normally be rendered as `
` tags, are instead rendered using the specified template. Inside the custom template, the user is free to choose any way of rendering the tooltips, including, but of course not limited to, using ng-bootstrap. 8 | 9 | The syntax for specifying the custom template is the following: 10 | ```html 11 | 12 | 13 | 16 |
{{content}}
17 |
18 |
19 | ``` 20 | 21 | For more concrete examples, please refer to tooltip samples on [official site](https://angular-slider.github.io/ngx-slider/demos#ticks-custom-tooltips-slider). -------------------------------------------------------------------------------- /src/demo-app/app/snippets/manual-refresh-slider/manual-refresh-slider.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-manual-refresh-slider', 6 | templateUrl: './manual-refresh-slider.component.html', 7 | standalone: false 8 | }) 9 | export class ManualRefreshSliderComponent { 10 | // 2019-06-11 UPDATE: The use-case in this example is now resolved on newest browser version which support ResizeObserver API 11 | // (https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) - at this time it is Chrome >= 64 and Opera >= 51. 12 | // For other browsers, or older browser versions, the workaround with manualRefresh still applies. 13 | manualRefreshEnabled: boolean = true; 14 | manualRefresh: EventEmitter = new EventEmitter(); 15 | isCollapsed: boolean = true; 16 | minValue: number = 20; 17 | maxValue: number = 80; 18 | options: Options = { 19 | floor: 0, 20 | ceil: 100, 21 | step: 5, 22 | animate: false // animations don't play nicely with collapse 23 | }; 24 | 25 | toggleCollapsed(): void { 26 | this.isCollapsed = !this.isCollapsed; 27 | if (this.manualRefreshEnabled) { 28 | // Bootstrap uses display CSS property to effect the collapse, so we need this to manually trigger a refresh 29 | this.manualRefresh.emit(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/slider-label.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, inject } from '@angular/core'; 2 | import { SliderElementDirective } from './slider-element.directive'; 3 | import { ValueHelper } from './value-helper'; 4 | import { AllowUnsafeHtmlInSlider } from './options'; 5 | 6 | @Directive({ 7 | selector: '[ngxSliderLabel]', 8 | standalone: false, 9 | }) 10 | export class SliderLabelDirective extends SliderElementDirective { 11 | private allowUnsafeHtmlInSlider = inject(AllowUnsafeHtmlInSlider, { 12 | optional: true, 13 | }); 14 | 15 | private _value: string = null; 16 | get value(): string { 17 | return this._value; 18 | } 19 | 20 | setValue(value: string): void { 21 | let recalculateDimension: boolean = false; 22 | 23 | if ( 24 | !this.alwaysHide && 25 | (ValueHelper.isNullOrUndefined(this.value) || 26 | this.value.length !== value.length || 27 | (this.value.length > 0 && this.dimension === 0)) 28 | ) { 29 | recalculateDimension = true; 30 | } 31 | 32 | this._value = value; 33 | if (this.allowUnsafeHtmlInSlider === false) { 34 | this.elemRef.nativeElement.innerText = value; 35 | } else { 36 | this.elemRef.nativeElement.innerHTML = value; 37 | } 38 | 39 | // Update dimension only when length of the label have changed 40 | if (recalculateDimension) { 41 | this.calculateDimension(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/slider-handle.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostBinding, inject } from '@angular/core'; 2 | import { SliderElementDirective } from './slider-element.directive'; 3 | import { DOCUMENT } from '@angular/common'; 4 | 5 | @Directive({ 6 | selector: '[ngxSliderHandle]', 7 | standalone: false, 8 | }) 9 | export class SliderHandleDirective extends SliderElementDirective { 10 | @HostBinding('class.ngx-slider-active') 11 | active: boolean = false; 12 | 13 | @HostBinding('attr.role') 14 | role: string = ''; 15 | 16 | @HostBinding('attr.tabindex') 17 | tabindex: string = ''; 18 | 19 | @HostBinding('attr.aria-orientation') 20 | ariaOrientation: string = ''; 21 | 22 | @HostBinding('attr.aria-label') 23 | ariaLabel: string = ''; 24 | 25 | @HostBinding('attr.aria-labelledby') 26 | ariaLabelledBy: string = ''; 27 | 28 | @HostBinding('attr.aria-valuenow') 29 | ariaValueNow: string = ''; 30 | 31 | @HostBinding('attr.aria-valuetext') 32 | ariaValueText: string = ''; 33 | 34 | @HostBinding('attr.aria-valuemin') 35 | ariaValueMin: string = ''; 36 | 37 | @HostBinding('attr.aria-valuemax') 38 | ariaValueMax: string = ''; 39 | 40 | private document: Document = inject(DOCUMENT); 41 | 42 | focus(): void { 43 | this.elemRef.nativeElement.focus(); 44 | } 45 | 46 | focusIfNeeded(): void { 47 | if (this.document.activeElement !== this.elemRef.nativeElement) { 48 | this.elemRef.nativeElement.focus(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ngx-slider/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "extends": [ 12 | "eslint:recommended", 13 | "plugin:@typescript-eslint/recommended", 14 | "plugin:@angular-eslint/recommended", 15 | "plugin:@angular-eslint/template/process-inline-templates" 16 | ], 17 | "rules": { 18 | "@angular-eslint/directive-selector": [ 19 | "error", 20 | { 21 | "type": "attribute", 22 | "prefix": "ngx", 23 | "style": "camelCase" 24 | } 25 | ], 26 | "@angular-eslint/component-selector": [ 27 | "error", 28 | { 29 | "type": "element", 30 | "prefix": "ngx", 31 | "style": "kebab-case" 32 | } 33 | ], 34 | "@angular-eslint/prefer-standalone": "off", 35 | "@typescript-eslint/no-explicit-any": "off", 36 | "@typescript-eslint/no-unused-vars": [ 37 | "error", 38 | { 39 | "args": "none" 40 | } 41 | ] 42 | } 43 | }, 44 | { 45 | "files": [ 46 | "*.html" 47 | ], 48 | "extends": [ 49 | "plugin:@angular-eslint/template/recommended", 50 | "plugin:@angular-eslint/template/accessibility" 51 | ], 52 | "rules": {} 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** Get all files in directory recursively, synchronously */ 5 | function readdirRecursivelySync(dir) { 6 | let results = []; 7 | const list = fs.readdirSync(dir); 8 | for (let file of list) { 9 | file = path.resolve(dir, file); 10 | const stat = fs.statSync(file); 11 | if (stat && stat.isDirectory()) { 12 | results = results.concat(readdirRecursivelySync(file)); 13 | } else { 14 | results.push(file); 15 | } 16 | } 17 | return results; 18 | } 19 | 20 | /** Copy README.md from given location to the library directory */ 21 | function copyReadmeMd(sourceReadmeMd) { 22 | const libReadmeFile = path.resolve(__dirname, '../src/ngx-slider/README.md'); 23 | 24 | const sourceReadme = fs.readFileSync(sourceReadmeMd, { encoding: 'utf8'}); 25 | fs.writeFileSync(libReadmeFile, sourceReadme, {encoding: 'utf8'}); 26 | } 27 | 28 | /** Escape { and } or otherwise Angular will complain when we're not actually using them for bindings */ 29 | function escapeBracesForAngular(html) { 30 | return html.replace(/([{}])/g, "{{ '$1' }}"); 31 | } 32 | 33 | /** Escape at (@) character, which is also reserved in Angular templates now. */ 34 | function escapeAtForAngular(html) { 35 | return html.replace(/@/g, "@"); 36 | } 37 | 38 | 39 | exports.readdirRecursivelySync = readdirRecursivelySync; 40 | exports.copyReadmeMd = copyReadmeMd; 41 | exports.escapeBracesForAngular = escapeBracesForAngular; 42 | exports.escapeAtForAngular = escapeAtForAngular; -------------------------------------------------------------------------------- /src/demo-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "extends": [ 12 | "eslint:recommended", 13 | "plugin:@typescript-eslint/recommended", 14 | "plugin:@angular-eslint/recommended", 15 | "plugin:@angular-eslint/template/process-inline-templates" 16 | ], 17 | "rules": { 18 | "@angular-eslint/directive-selector": [ 19 | "error", 20 | { 21 | "type": "attribute", 22 | "prefix": "app", 23 | "style": "camelCase" 24 | } 25 | ], 26 | "@angular-eslint/component-selector": [ 27 | "error", 28 | { 29 | "type": "element", 30 | "prefix": "app", 31 | "style": "kebab-case" 32 | } 33 | ], 34 | "@angular-eslint/prefer-standalone": "off", 35 | "@typescript-eslint/no-explicit-any": "off", 36 | "@typescript-eslint/no-unused-vars": [ 37 | "error", 38 | { 39 | "args": "none" 40 | } 41 | ] 42 | } 43 | }, 44 | { 45 | "files": [ 46 | "*.html" 47 | ], 48 | "extends": [ 49 | "plugin:@angular-eslint/template/recommended", 50 | "plugin:@angular-eslint/template/accessibility" 51 | ], 52 | "rules": { 53 | "@angular-eslint/template/prefer-control-flow": "off" 54 | } 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/demo-app/app/home.component.scss: -------------------------------------------------------------------------------- 1 | .horizontal-slider-container { 2 | width: 80%; 3 | } 4 | 5 | .inline-with-vertical-slider-container { 6 | height: auto; 7 | } 8 | 9 | @media(min-width: 992px) { 10 | .inline-with-vertical-slider-container { 11 | height: 10rem; 12 | } 13 | } 14 | 15 | .vertical-slider-container { 16 | text-align: left; 17 | margin-left: calc(50% - 2rem); 18 | height: 10rem; 19 | } 20 | 21 | // We need to use ::ng-deep to overcome view encapsulation 22 | ::ng-deep { 23 | .styled-slider.ngx-slider .ngx-slider-bar { 24 | background: #ffe4d1; 25 | height: 2px; 26 | } 27 | .styled-slider.ngx-slider .ngx-slider-selection { 28 | background: orange; 29 | } 30 | 31 | .styled-slider.ngx-slider .ngx-slider-pointer { 32 | width: 8px; 33 | height: 16px; 34 | top: auto; /* to remove the default positioning */ 35 | bottom: 0; 36 | background-color: #333; 37 | border-top-left-radius: 3px; 38 | border-top-right-radius: 3px; 39 | } 40 | 41 | .styled-slider.ngx-slider .ngx-slider-pointer:after { 42 | display: none; 43 | } 44 | 45 | .styled-slider.ngx-slider .ngx-slider-bubble { 46 | bottom: 14px; 47 | } 48 | 49 | .styled-slider.ngx-slider .ngx-slider-limit { 50 | font-weight: bold; 51 | color: orange; 52 | } 53 | 54 | .styled-slider.ngx-slider .ngx-slider-tick { 55 | width: 1px; 56 | height: 10px; 57 | margin-left: 4px; 58 | border-radius: 0; 59 | background: #ffe4d1; 60 | top: -1px; 61 | } 62 | 63 | .styled-slider.ngx-slider .ngx-slider-tick.ngx-slider-selected { 64 | background: orange; 65 | } 66 | } -------------------------------------------------------------------------------- /src/ngx-slider/lib/variables.scss: -------------------------------------------------------------------------------- 1 | @mixin ng-deep($enableNgDeep: true) { 2 | @if $enableNgDeep { 3 | ::ng-deep { 4 | @content; 5 | } 6 | } @else { 7 | @content; 8 | } 9 | } 10 | 11 | @mixin rounded($radius: 2px) { 12 | -webkit-border-radius: $radius; 13 | -moz-border-radius: $radius; 14 | border-radius: $radius; 15 | } 16 | 17 | $enableNgDeep: true !default; 18 | 19 | $handleActiveColor: #451aff !default; 20 | $handleHoverColor: #fff !default; 21 | $labelTextColor: #55637d !default; 22 | $handleBgColor: #0db9f0 !default; 23 | $handleInnerColor: #fff !default; 24 | $handleDisabledColor: #d8e0f3 !default; 25 | $limitLabelTextColor: $labelTextColor !default; 26 | $barFillColor: $handleBgColor !default; 27 | $barDisabledFillColor: #8b91a2 !default; 28 | $barNormalColor: #d8e0f3 !default; 29 | $barLeftOutSelectionColor: #df002d !default; 30 | $barRightOutSelectionColor: #03a688 !default; 31 | 32 | $ticksColor: $barNormalColor !default; 33 | $selectedTicksColor: $barFillColor !default; 34 | $ticksWidth: 10px !default; 35 | $ticksHeight: 10px !default; 36 | $ticksValuePosition: -34px !default; 37 | $ticksLegendPosition: 24px !default; 38 | $ticksValuePositionOnVertical: 24px !default; 39 | $tickLegendMaxWidth: 50px !default; 40 | 41 | $handleSize: 32px !default; 42 | $handlePointerSize: 8px !default; 43 | $bubblePaddingVertical: 1px !default; 44 | $bubblePaddingHorizontal: 3px !default; 45 | $labelFontSize: 16px !default; 46 | $barDimension: 4px !default; 47 | 48 | $withLegendMargin: 40px !default; 49 | 50 | $sliderMargin: 15px !default; 51 | $sliderMarginWithLabel: 35px !default; 52 | $sliderVerticalMargin: 20px !default; 53 | 54 | $animationDuration: 0.3s !default; -------------------------------------------------------------------------------- /src/demo-app/app/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options, LabelType } from '@local/ngx-slider'; 3 | 4 | @Component({ 5 | selector: 'app-home', 6 | templateUrl: './home.component.html', 7 | styleUrls: ['./home.component.scss'], 8 | standalone: false 9 | }) 10 | export class HomeComponent { 11 | simpleSliderValue: number = 50; 12 | simpleSliderOptions: Options = { 13 | floor: 0, 14 | ceil: 100 15 | }; 16 | 17 | rangeSliderLowValue: number = 25; 18 | rangeSliderHighValue: number = 75; 19 | rangeSliderOptions: Options = { 20 | floor: 0, 21 | ceil: 100 22 | }; 23 | 24 | ticksSliderValue: number = 5; 25 | ticksSliderOptions: Options = { 26 | floor: 0, 27 | ceil: 10, 28 | step: 1, 29 | showTicks: true, 30 | showTicksValues: true 31 | }; 32 | 33 | customisedSliderLowValue: number = 150; 34 | customisedSliderHighValue: number = 350; 35 | customisedSliderOptions: Options = { 36 | floor: 0, 37 | ceil: 500, 38 | translate: (value: number, label: LabelType): string => { 39 | switch (label) { 40 | case LabelType.Low: 41 | return 'Min price: $' + value; 42 | case LabelType.High: 43 | return 'Max price: $' + value; 44 | default: 45 | return '$' + value; 46 | } 47 | } 48 | }; 49 | 50 | styledSliderLowValue: number = 30; 51 | styledSliderHighValue: number = 70; 52 | styledSliderOptions: Options = { 53 | floor: 0, 54 | ceil: 100, 55 | step: 10, 56 | showTicks: true 57 | }; 58 | 59 | verticalSliderValue: number = 5; 60 | verticalSliderOptions: Options = { 61 | floor: 0, 62 | ceil: 10, 63 | vertical: true 64 | }; 65 | 66 | constructor() { } 67 | } 68 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/vertical-sliders/vertical-sliders.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Options } from '@local/ngx-slider'; 3 | 4 | interface SimpleSliderModel { 5 | value: number; 6 | options: Options; 7 | } 8 | 9 | interface RangeSliderModel { 10 | minValue: number; 11 | maxValue: number; 12 | options: Options; 13 | } 14 | 15 | @Component({ 16 | selector: 'app-vertical-sliders', 17 | templateUrl: './vertical-sliders.component.html', 18 | standalone: false 19 | }) 20 | export class VerticalSlidersComponent { 21 | verticalSlider1: SimpleSliderModel = { 22 | value: 5, 23 | options: { 24 | floor: 0, 25 | ceil: 10, 26 | vertical: true 27 | } 28 | }; 29 | 30 | verticalSlider2: RangeSliderModel = { 31 | minValue: 20, 32 | maxValue: 80, 33 | options: { 34 | floor: 0, 35 | ceil: 100, 36 | vertical: true 37 | } 38 | }; 39 | 40 | verticalSlider3: SimpleSliderModel = { 41 | value: 5, 42 | options: { 43 | floor: 0, 44 | ceil: 10, 45 | vertical: true, 46 | showTicks: true 47 | } 48 | }; 49 | 50 | verticalSlider4: RangeSliderModel = { 51 | minValue: 1, 52 | maxValue: 5, 53 | options: { 54 | floor: 0, 55 | ceil: 6, 56 | vertical: true, 57 | showTicksValues: true 58 | } 59 | }; 60 | 61 | verticalSlider5: SimpleSliderModel = { 62 | value: 50, 63 | options: { 64 | floor: 0, 65 | ceil: 100, 66 | vertical: true, 67 | showSelectionBar: true 68 | } 69 | }; 70 | 71 | verticalSlider6: SimpleSliderModel = { 72 | value: 6, 73 | options: { 74 | floor: 0, 75 | ceil: 6, 76 | vertical: true, 77 | showSelectionBar: true, 78 | showTicksValues: true, 79 | ticksValuesTooltip: (v: number): string => { 80 | return 'Tooltip for ' + v; 81 | } 82 | } 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/value-helper.ts: -------------------------------------------------------------------------------- 1 | import { CustomStepDefinition } from './options'; 2 | 3 | /** 4 | * Collection of functions to handle conversions/lookups of values 5 | */ 6 | export class ValueHelper { 7 | static isNullOrUndefined(value: any): boolean { 8 | return value === undefined || value === null; 9 | } 10 | 11 | static areArraysEqual(array1: any[], array2: any[]): boolean { 12 | if (array1.length !== array2.length) { 13 | return false; 14 | } 15 | 16 | for (let i: number = 0; i < array1.length; ++i) { 17 | if (array1[i] !== array2[i]) { 18 | return false; 19 | } 20 | } 21 | 22 | return true; 23 | } 24 | 25 | static linearValueToPosition(val: number, minVal: number, maxVal: number): number { 26 | const range: number = maxVal - minVal; 27 | return (val - minVal) / range; 28 | } 29 | 30 | static logValueToPosition(val: number, minVal: number, maxVal: number): number { 31 | val = Math.log(val); 32 | minVal = Math.log(minVal); 33 | maxVal = Math.log(maxVal); 34 | const range: number = maxVal - minVal; 35 | return (val - minVal) / range; 36 | } 37 | 38 | static linearPositionToValue(percent: number, minVal: number, maxVal: number): number { 39 | return percent * (maxVal - minVal) + minVal; 40 | } 41 | 42 | static logPositionToValue(percent: number, minVal: number, maxVal: number): number { 43 | minVal = Math.log(minVal); 44 | maxVal = Math.log(maxVal); 45 | const value: number = percent * (maxVal - minVal) + minVal; 46 | return Math.exp(value); 47 | } 48 | 49 | static findStepIndex(modelValue: number, stepsArray: CustomStepDefinition[]): number { 50 | const differences: number[] = stepsArray.map((step: CustomStepDefinition): number => Math.abs(modelValue - step.value)); 51 | 52 | let minDifferenceIndex: number = 0; 53 | for (let index: number = 0; index < stepsArray.length; index++) { 54 | if (differences[index] !== differences[minDifferenceIndex] && differences[index] < differences[minDifferenceIndex]) { 55 | minDifferenceIndex = index; 56 | } 57 | } 58 | 59 | return minDifferenceIndex; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /e2e/utils/custom-interactions.ts: -------------------------------------------------------------------------------- 1 | import { Locator } from '@playwright/test'; 2 | 3 | export async function mouseDragRelative(locator: Locator, options: {offsetX: number, offsetY: number}) { 4 | const page = locator.page(); 5 | const box = await locator.boundingBox(); 6 | 7 | const centerX = box.x + box.width / 2; 8 | const centerY = box.y + box.height / 2; 9 | 10 | await page.mouse.move(centerX, centerY); 11 | await page.mouse.down({ button: 'left' }); 12 | await page.mouse.move(centerX + options.offsetX, centerY + options.offsetY); 13 | await page.mouse.up({ button: 'left' }); 14 | } 15 | 16 | export async function touchDragRelative(locator: Locator, options: {offsetX: number, offsetY: number}) { 17 | const page = locator.page(); 18 | const box = await locator.boundingBox(); 19 | 20 | const centerX = box.x + box.width / 2; 21 | const centerY = box.y + box.height / 2; 22 | 23 | // Playwright doesn't support touch gestures yet, so this is a crude workaround 24 | await page.evaluate( 25 | ([centerX, centerY, offsetX, offsetY]) => { 26 | const element = document.elementFromPoint(centerX, centerY); 27 | 28 | const touchStartEvent = new PointerEvent("pointerdown", { 29 | bubbles: true, 30 | cancelable: true, 31 | composed: true, 32 | isPrimary: true, 33 | pointerType: 'touch', 34 | clientX: centerX, 35 | clientY: centerY, 36 | }); 37 | element.dispatchEvent(touchStartEvent); 38 | 39 | const touchMoveEvent1 = new PointerEvent("pointermove", { 40 | bubbles: true, 41 | cancelable: true, 42 | composed: true, 43 | isPrimary: true, 44 | pointerType: 'touch', 45 | clientX: centerX + offsetX, 46 | clientY: centerY + offsetY, 47 | }); 48 | element.dispatchEvent(touchMoveEvent1); 49 | 50 | const touchEndEvent = new PointerEvent("pointerup", { 51 | bubbles: true, 52 | cancelable: true, 53 | composed: true, 54 | isPrimary: true, 55 | pointerType: 'touch', 56 | clientX: centerX + offsetX, 57 | clientY: centerY + offsetY 58 | }); 59 | element.dispatchEvent(touchEndEvent); 60 | }, 61 | [centerX, centerY, options.offsetX, options.offsetY] 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/math-helper.spec.ts: -------------------------------------------------------------------------------- 1 | import { MathHelper } from './math-helper'; 2 | 3 | describe('MathHelper', () => { 4 | describe('roundToPrecisionLimit', () => { 5 | it('gets rid of digits to the left of precision limit', () => { 6 | expect(MathHelper.roundToPrecisionLimit(123456789010 + 1, 11)).toEqual(123456789010); 7 | expect(MathHelper.roundToPrecisionLimit(123456789010 - 1, 11)).toEqual(123456789010); 8 | }); 9 | 10 | it('keeps digits to the right of precision limit', () => { 11 | expect(MathHelper.roundToPrecisionLimit(123456789010 + 10, 11)).toEqual(123456789010 + 10); 12 | expect(MathHelper.roundToPrecisionLimit(123456789010 - 10, 11)).toEqual(123456789010 - 10); 13 | }); 14 | 15 | it('helps with rounding common floating point operations', () => { 16 | expect(0.3 - 0.2).not.toEqual(0.1); // yeah, floating point 17 | expect(MathHelper.roundToPrecisionLimit(0.3 - 0.2, 12)).toEqual(0.1); // that's better 18 | }); 19 | }); 20 | 21 | describe('isModuloWithinPrecisionLimit', () => { 22 | it('works for decimal modulo', () => { 23 | expect(2 % 0.1 === 0).toBeFalsy(); 24 | expect(MathHelper.isModuloWithinPrecisionLimit(2, 0.1, 10)).toBeTruthy(); 25 | 26 | expect(MathHelper.isModuloWithinPrecisionLimit(2 - 1e-12, 0.1, 10)).toBeTruthy(); 27 | expect(MathHelper.isModuloWithinPrecisionLimit(2 + 1e-12, 0.1, 10)).toBeTruthy(); 28 | 29 | expect(MathHelper.isModuloWithinPrecisionLimit(2, 0.1 - 1e-12, 10)).toBeTruthy(); 30 | expect(MathHelper.isModuloWithinPrecisionLimit(2, 0.1 + 1e-12, 10)).toBeTruthy(); 31 | 32 | expect(MathHelper.isModuloWithinPrecisionLimit(2 + 1e-9, 0.1, 10)).toBeFalsy(); 33 | expect(MathHelper.isModuloWithinPrecisionLimit(2 - 1e-9, 0.1, 10)).toBeFalsy(); 34 | expect(MathHelper.isModuloWithinPrecisionLimit(2, 0.1 - 1e-9, 10)).toBeFalsy(); 35 | expect(MathHelper.isModuloWithinPrecisionLimit(2, 0.1 + 1e-9, 10)).toBeFalsy(); 36 | }); 37 | }); 38 | 39 | describe('clampToRange', () => { 40 | it('returns original value if in range', () => { 41 | expect(MathHelper.clampToRange(40, 0, 100)).toEqual(40); 42 | }); 43 | 44 | it('returns floor if value is below floor', () => { 45 | expect(MathHelper.clampToRange(-10, 0, 100)).toEqual(0); 46 | }); 47 | 48 | it('returns ceil if value is above ceil', () => { 49 | expect(MathHelper.clampToRange(110, 0, 100)).toEqual(100); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /src/demo-app/app/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | logo 7 |
8 | 9 |
10 |

ngx-slider

11 | 12 | @if (enableExternalImages) { 13 | Fork me on GitHub 18 | } 19 | 20 |

Self-contained, mobile-friendly slider component for Angular based on angularjs-slider

21 |

@if (enableExternalImages) { 22 | npm version 23 | }

24 |
25 |
26 |
27 |
28 | 29 | 53 |
54 | -------------------------------------------------------------------------------- /src/demo-app/app/app-router.config.ts: -------------------------------------------------------------------------------- 1 | import { ExtraOptions, Routes } from '@angular/router'; 2 | 3 | import { DemosComponent } from './demos.component'; 4 | import { DocsComponent } from './docs.component'; 5 | import { HomeComponent } from './home.component'; 6 | import { 7 | CustomTicksLegendSliderComponent, 8 | DraggableRangeOnlySliderComponent, 9 | DraggableRangeSliderComponent, 10 | LimitedRangeSliderComponent, 11 | LimitedSliderComponent, 12 | NoSwitchingRangeSliderComponent, 13 | PreventChangeOnScrollSliderComponent, 14 | PushRangeSliderComponent, 15 | RangeSliderComponent, 16 | ReactiveFormRangeSliderComponent, 17 | ReactiveFormSimpleSliderComponent, 18 | RightToLeftSliderComponent, 19 | SimpleSliderComponent, 20 | TicksValuesSliderComponent, 21 | VerticalSlidersComponent, 22 | } from './snippets'; 23 | 24 | export const routerConfig: Routes = [ 25 | { path: '', pathMatch: 'full', component: HomeComponent }, 26 | { path: 'home', component: HomeComponent }, 27 | { path: 'demos', component: DemosComponent }, 28 | { path: 'docs', component: DocsComponent }, 29 | { path: 'api', redirectTo: 'docs' }, 30 | 31 | // pages for e2e testing 32 | { path: 'custom-ticks-legend-slider', component: CustomTicksLegendSliderComponent }, 33 | { path: 'draggable-range-only-slider', component: DraggableRangeOnlySliderComponent }, 34 | { path: 'draggable-range-slider', component: DraggableRangeSliderComponent }, 35 | { path: 'limited-range-slider', component: LimitedRangeSliderComponent }, 36 | { path: 'limited-slider', component: LimitedSliderComponent }, 37 | { path: 'no-switching-range-slider', component: NoSwitchingRangeSliderComponent }, 38 | { path: 'prevent-change-on-scroll-slider', component: PreventChangeOnScrollSliderComponent }, 39 | { path: 'push-range-slider', component: PushRangeSliderComponent }, 40 | { path: 'range-slider', component: RangeSliderComponent }, 41 | { path: 'reactive-form-range-slider', component: ReactiveFormRangeSliderComponent }, 42 | { path: 'reactive-form-simple-slider', component: ReactiveFormSimpleSliderComponent }, 43 | { path: 'right-to-left-slider', component: RightToLeftSliderComponent }, 44 | { path: 'simple-slider', component: SimpleSliderComponent }, 45 | { path: 'ticks-values-slider', component: TicksValuesSliderComponent }, 46 | { path: 'vertical-sliders', component: VerticalSlidersComponent }, 47 | 48 | { path: '**', pathMatch: 'full', redirectTo: ''} 49 | ]; 50 | 51 | export const routerOptions: ExtraOptions = { 52 | useHash: false, 53 | anchorScrolling: 'enabled' 54 | }; 55 | -------------------------------------------------------------------------------- /e2e/limited-slider.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, Page, Locator } from '@playwright/test'; 2 | import { expect, mouseDragRelative, touchDragRelative } from './utils'; 3 | 4 | async function setUp(page: Page) { 5 | await page.setViewportSize({ width: 800, height: 600 }); 6 | await page.goto('/limited-slider?testMode=true'); 7 | } 8 | 9 | function getSlider(page: Page): Locator { 10 | return page.locator('ngx-slider'); 11 | } 12 | 13 | function getSliderFloorLabel(page: Page): Locator { 14 | return getSlider(page).locator('span.ngx-slider-floor'); 15 | } 16 | 17 | function getSliderCeilLabel(page: Page): Locator { 18 | return getSlider(page).locator('span.ngx-slider-ceil'); 19 | } 20 | 21 | function getSliderPointer(page: Page): Locator { 22 | return getSlider(page).locator('span.ngx-slider-pointer-min'); 23 | } 24 | 25 | function getSliderPointerLabel(page: Page): Locator { 26 | return getSlider(page).locator('span.ngx-slider-model-value'); 27 | } 28 | 29 | 30 | test('limited slider initial state displays starting values', async ({ page }) => { 31 | await setUp(page); 32 | 33 | await expect(getSliderFloorLabel(page)).toHaveText('0'); 34 | await expect(getSliderCeilLabel(page)).toHaveText('100'); 35 | await expect(getSliderPointerLabel(page)).toHaveText('50'); 36 | }); 37 | 38 | test('limited slider after dragging pointer to above the low limit moves the pointer to the new position', async ({ page }) => { 39 | await setUp(page); 40 | 41 | await mouseDragRelative(getSliderPointer(page), {offsetX: -286, offsetY: 0}); 42 | 43 | await expect(getSliderPointerLabel(page)).toHaveText('11'); 44 | }); 45 | 46 | test('limited slider after dragging pointer beyond the low limit stops the pointer at the low limit', async ({ page }) => { 47 | await setUp(page); 48 | 49 | await mouseDragRelative(getSliderPointer(page), {offsetX: -300, offsetY: 0}); 50 | 51 | await expect(getSliderPointerLabel(page)).toHaveText('10'); 52 | }); 53 | 54 | test('limited slider after dragging pointer to above the high limit moves the pointer to the new position', async ({ page }) => { 55 | await setUp(page); 56 | 57 | await mouseDragRelative(getSliderPointer(page), {offsetX: 286, offsetY: 0}); 58 | 59 | await expect(getSliderPointerLabel(page)).toHaveText('89'); 60 | }); 61 | 62 | test('limited slider after dragging pointer beyond the high limit stops the pointer at the high limit', async ({ page }) => { 63 | await setUp(page); 64 | 65 | await mouseDragRelative(getSliderPointer(page), {offsetX: 300, offsetY: 0}); 66 | 67 | await expect(getSliderPointerLabel(page)).toHaveText('90'); 68 | }); 69 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // require('dotenv').config(); 8 | 9 | /** 10 | * See https://playwright.dev/docs/test-configuration. 11 | */ 12 | export default defineConfig({ 13 | testDir: './e2e', 14 | /* Run tests in files in parallel */ 15 | fullyParallel: true, 16 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 17 | forbidOnly: !!process.env.CI, 18 | /* Retry on CI only */ 19 | retries: process.env.CI ? 2 : 0, 20 | /* Opt out of parallel tests on CI. */ 21 | workers: process.env.CI ? 1 : undefined, 22 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 23 | reporter: 'html', 24 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 25 | use: { 26 | /* Base URL to use in actions like `await page.goto('/')`. */ 27 | baseURL: 'http://localhost:4200', 28 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 29 | trace: 'on-first-retry', 30 | /* Capture screenshots to see what happened in each test */ 31 | screenshot: 'on', 32 | }, 33 | /* Set test timeout to 30 seconds */ 34 | timeout: 30 * 1000, 35 | 36 | /* Configure projects for major browsers */ 37 | projects: [ 38 | { 39 | name: 'chromium', 40 | use: { 41 | ...devices['Desktop Chrome'], 42 | /* We need touch events for some tests */ 43 | hasTouch: true, 44 | /* Uncomment for debugging */ 45 | //headless: false, 46 | }, 47 | }, 48 | 49 | // { 50 | // name: 'firefox', 51 | // use: { ...devices['Desktop Firefox'] }, 52 | // }, 53 | 54 | // { 55 | // name: 'webkit', 56 | // use: { ...devices['Desktop Safari'] }, 57 | // }, 58 | 59 | /* Test against mobile viewports. */ 60 | // { 61 | // name: 'Mobile Chrome', 62 | // use: { ...devices['Pixel 5'] }, 63 | // }, 64 | // { 65 | // name: 'Mobile Safari', 66 | // use: { ...devices['iPhone 12'] }, 67 | // }, 68 | 69 | /* Test against branded browsers. */ 70 | // { 71 | // name: 'Microsoft Edge', 72 | // use: { ...devices['Desktop Edge'], channel: 'msedge' }, 73 | // }, 74 | // { 75 | // name: 'Google Chrome', 76 | // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 77 | // }, 78 | ], 79 | 80 | /* Run Angular server before starting the tests */ 81 | webServer: { 82 | // only start "npx ng serve"; assume that "npm run prepare" has already been done 83 | command: 'npx ng serve', 84 | port: 4200, 85 | timeout: 120 * 1000, 86 | }, 87 | }); 88 | -------------------------------------------------------------------------------- /scripts/generate-lib-files.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const utils = require("./utils.js"); 5 | 6 | /** Generate package.json file based on package.json.template and main package.json */ 7 | function generatePackageJson() { 8 | const mainFile = path.resolve(__dirname, "../package.json"); 9 | const libTemplateFile = path.resolve( 10 | __dirname, 11 | "../src/ngx-slider/package.json.template" 12 | ); 13 | const libFile = path.resolve(__dirname, "../src/ngx-slider/package.json"); 14 | 15 | const libTemplateConfig = JSON.parse( 16 | fs.readFileSync(libTemplateFile, { encoding: "utf8" }) 17 | ); 18 | const mainConfig = JSON.parse( 19 | fs.readFileSync(mainFile, { encoding: "utf8" }) 20 | ); 21 | 22 | let libConfig = {}; 23 | 24 | for (let key of libTemplateConfig.keysToCopyFromMainPackageJson) { 25 | libConfig[key] = mainConfig[key]; 26 | } 27 | libConfig.dependencies = {}; 28 | for (let dependency of libTemplateConfig.dependenciesToCopyFromMainPackageJson) { 29 | libConfig.dependencies[dependency] = mainConfig.dependencies[dependency]; 30 | } 31 | libConfig.peerDependencies = {}; 32 | for (let dependency of libTemplateConfig.dependenciesToCopyAsPeerDependenciesFromMainPackageJson) { 33 | libConfig.peerDependencies[dependency] = 34 | mainConfig.dependencies[dependency]; 35 | } 36 | 37 | libConfig = Object.assign({}, libConfig, libTemplateConfig, libConfig); 38 | delete libConfig.keysToCopyFromMainPackageJson; 39 | delete libConfig.dependenciesToCopyFromMainPackageJson; 40 | delete libConfig.dependenciesToCopyAsPeerDependenciesFromMainPackageJson; 41 | 42 | const prettyPrintedLibConfig = JSON.stringify(libConfig, null, 2); 43 | 44 | fs.writeFileSync(libFile, prettyPrintedLibConfig, { encoding: "utf8" }); 45 | } 46 | 47 | /** Convert public_api.json to public_api.ts */ 48 | function generatePublicApiTs() { 49 | const configFile = path.resolve( 50 | __dirname, 51 | "../src/ngx-slider/lib/public_api.json" 52 | ); 53 | const tsFile = path.resolve(__dirname, "../src/ngx-slider/lib/public_api.ts"); 54 | 55 | const config = JSON.parse(fs.readFileSync(configFile, { encoding: "utf8" })); 56 | 57 | let tsFileContent = ""; 58 | 59 | for (let exportDef of config.exports) { 60 | if (exportDef.what instanceof Array) { 61 | const whats = exportDef.what.join(", "); 62 | tsFileContent += `export { ${whats} } from '${exportDef.file}';\n`; 63 | } else { 64 | tsFileContent += `export ${exportDef.what} from '${exportDef.file}';\n`; 65 | } 66 | } 67 | 68 | fs.writeFileSync(tsFile, tsFileContent, { encoding: "utf8" }); 69 | } 70 | 71 | generatePackageJson(); 72 | generatePublicApiTs(); 73 | 74 | const mainReadmeFile = path.resolve(__dirname, "../README.md"); 75 | utils.copyReadmeMd(mainReadmeFile); 76 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading from ng5-slider v1.x.x to ngx-slider v2.0.0 2 | 3 | v2.0.0 re-branded the slider component from ng5-slider to ngx-slider, putting it under a different NPM namespace. 4 | 5 | The upgrade process is relatively straightforward, with mostly cosmetic changes of finding "ng5" and replacing it with "ngx". 6 | 7 | This is a step-by-step guide to doing this: 8 | 9 | 1. Change the package name `ng5-slider` -> `@angular-slider/ngx-slider` in your `package.json`: 10 | 11 | Before: 12 | ```json 13 | "dependencies": { 14 | "ng5-slider": "^1.x.x", 15 | // ... 16 | } 17 | ``` 18 | 19 | After: 20 | ```json 21 | "dependencies": { 22 | "@angular-slider/ngx-slider": "^2.0.0", 23 | // ... 24 | } 25 | ``` 26 | 27 | 2. Run `npm install` to pull the new package. 28 | 29 | 3. Replace reference to slider module `Ng5SliderModule` -> `NgxSliderModule` in your `app.module.ts`: 30 | 31 | Before: 32 | ```typescript 33 | import { Ng5SliderModule } from 'ng5-slider'; 34 | 35 | ... 36 | 37 | @NgModule({ 38 | ... 39 | imports: [ 40 | ... 41 | Ng5SliderModule, 42 | ... 43 | ], 44 | ... 45 | }) 46 | export class AppModule {} 47 | ``` 48 | 49 | After: 50 | ```typescript 51 | import { NgxSliderModule } from '@angular-slider/ngx-slider'; 52 | 53 | ... 54 | 55 | @NgModule({ 56 | ... 57 | imports: [ 58 | ... 59 | NgxSliderModule, 60 | ... 61 | ], 62 | ... 63 | }) 64 | export class AppModule {} 65 | ``` 66 | 67 | 4. Rename all slider imports in your Typescript files `import ... from 'ng5-slider'` -> `import ... from '@angular-slider/ngx-slider'`: 68 | 69 | Before: 70 | ```typescript 71 | import { Options } from 'ng5-slider'; 72 | 73 | ... 74 | export class MyComponent { 75 | ... 76 | options: Options = { 77 | floor: 0, 78 | ceil: 100 79 | }; 80 | } 81 | ``` 82 | 83 | After: 84 | ```typescript 85 | import { Options } from '@angular-slider/ngx-slider'; 86 | 87 | ... 88 | export class MyComponent { 89 | ... 90 | options: Options = { 91 | floor: 0, 92 | ceil: 100 93 | }; 94 | } 95 | ``` 96 | 97 | 5. Rename all usages of slider directive `` -> `` in your HTML templates: 98 | 99 | Before: 100 | ```html 101 | 102 | ``` 103 | 104 | After: 105 | ```html 106 | 107 | ``` 108 | 109 | 6. If you use custom CSS styling for the slider, replace `ng5` -> `ngx` in the CSS class names. 110 | 111 | Before: 112 | ```scss 113 | .custom-slider { 114 | .ng5-slider { 115 | .ng5-slider-selection { 116 | background: rgb(255, 255, 0) !important; 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | After: 123 | ```scss 124 | .custom-slider { 125 | .ngx-slider { 126 | .ngx-slider-selection { 127 | background: rgb(255, 255, 0) !important; 128 | } 129 | } 130 | } 131 | ``` 132 | 133 | 7. Try building and running your app. If there are no errors, you should be all good to go! 134 | -------------------------------------------------------------------------------- /scripts/esm/generate-demo-app-docs.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | This script generates the API documentation pages in demo app using Typedoc. 3 | The files are then embedded as additional assets in the demo app. 4 | 5 | Annoyingly, typedoc v0.27 or later is only available as ESM module, 6 | therefore this script had to be rewritten to ESM format as well. 7 | This required renaming it to *.mjs to make Node pick it up as ESM 8 | as well as separating it from the other existing *.js scripts 9 | which still use CommonJS (mixing the two script types in one directory simply doesn't work). 10 | */ 11 | 12 | import * as path from 'path'; 13 | import * as mkdirp from 'mkdirp'; 14 | import * as fs from 'fs'; 15 | import * as rimraf from 'rimraf'; 16 | import { fileURLToPath } from 'url'; 17 | import * as typedoc from 'typedoc'; 18 | 19 | // Workaround for missing __dirname in ESM 20 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 21 | 22 | 23 | /** Copy README.md from given location to the library directory 24 | This is copy-pasted from scripts/utils.js as unfortunately utils.js 25 | is still in CommonJS format and cannot be imported here 26 | */ 27 | function copyReadmeMd(sourceReadmeMd) { 28 | const libReadmeFile = path.resolve(__dirname, '../../src/ngx-slider/README.md'); 29 | 30 | const sourceReadme = fs.readFileSync(sourceReadmeMd, { encoding: 'utf8'}); 31 | fs.writeFileSync(libReadmeFile, sourceReadme, {encoding: 'utf8'}); 32 | } 33 | 34 | 35 | /** Run typedoc over library public API files to generate HTML files with documentation. 36 | */ 37 | async function generateTypedocDocs(typedocDocsDir) { 38 | const publicApiConfigFile = path.resolve(__dirname, '../../src/ngx-slider/lib/public_api.json'); 39 | const publicApiConfig = JSON.parse(fs.readFileSync(publicApiConfigFile, { encoding: 'utf8' })); 40 | 41 | const files = publicApiConfig.exports 42 | .map(exportDef => path.resolve(__dirname, `../../src/ngx-slider/lib/${exportDef.file}.ts`)); 43 | 44 | // HACK: When Typedoc finds a README.md file, it uses it to generate content for the index page of documentation 45 | // This is not very helpful, as it repeats the same stuff that's already shown on Github and NPM 46 | // So instead, replace the README.md with our own file 47 | const apiDocsReadmeFile = path.resolve( 48 | __dirname, 49 | "../../typedoc/README.md" 50 | ); 51 | copyReadmeMd(apiDocsReadmeFile); 52 | 53 | const app = await typedoc.Application.bootstrap({ 54 | entryPoints: ["src/ngx-slider/lib/public_api.ts"], 55 | }); 56 | 57 | app.options.addReader(new typedoc.TSConfigReader()); 58 | app.options.addReader(new typedoc.TypeDocReader()); 59 | 60 | const project = await app.convert(); 61 | await app.generateDocs(project, typedocDocsDir); 62 | 63 | // HACK: restore the README.md to original 64 | const mainReadmeFile = path.resolve(__dirname, "../../README.md"); 65 | copyReadmeMd(mainReadmeFile); 66 | } 67 | 68 | const typedocDocsDir = path.resolve( __dirname, "../../docs"); 69 | rimraf.sync(typedocDocsDir); 70 | mkdirp.sync(typedocDocsDir); 71 | 72 | generateTypedocDocs(typedocDocsDir) 73 | .then(() => { 74 | console.log("Typedoc generation finished successfully.") 75 | }) 76 | .catch(console.error); 77 | -------------------------------------------------------------------------------- /src/demo-app/app/demos.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/value-helper.spec.ts: -------------------------------------------------------------------------------- 1 | import { ValueHelper } from './value-helper'; 2 | import { CustomStepDefinition } from './options'; 3 | 4 | const precision: number = 0.0001; 5 | 6 | describe('ValueHelper', () => { 7 | describe('linearValueToPosition', () => { 8 | it('converts using linear interpolation', () => { 9 | const number: number = 55; 10 | const minValue: number = 0; 11 | const maxValue: number = 100; 12 | expect(ValueHelper.linearValueToPosition(number, minValue, maxValue)).toBeCloseTo(0.55, precision); 13 | }); 14 | }); 15 | 16 | describe('linearPositionToValue', () => { 17 | it('converts using linear interpolation', () => { 18 | const percent: number = 0.55; 19 | const minValue: number = 0; 20 | const maxValue: number = 100; 21 | expect(ValueHelper.linearPositionToValue(percent, minValue, maxValue)).toBeCloseTo(55, precision); 22 | }); 23 | }); 24 | 25 | describe('logValueToPosition', () => { 26 | it('converts using log scale', () => { 27 | const number: number = 1e2; 28 | const minValue: number = 1e0; 29 | const maxValue: number = 1e10; 30 | expect(ValueHelper.logValueToPosition(number, minValue, maxValue)).toBeCloseTo(0.2, precision); 31 | }); 32 | }); 33 | 34 | describe('logPositionToValue', () => { 35 | it('converts using log scale', () => { 36 | const percent: number = 0.2; 37 | const minValue: number = 1e0; 38 | const maxValue: number = 1e10; 39 | expect(ValueHelper.logPositionToValue(percent, minValue, maxValue)).toBeCloseTo(1e2, precision); 40 | }); 41 | }); 42 | 43 | describe('findStepIndex', () => { 44 | it('finds the first equal index if value is equal', () => { 45 | const stepArray: CustomStepDefinition[] = [ 46 | { 47 | value: 1 48 | }, 49 | { 50 | value: 2 51 | }, 52 | { 53 | value: 3 54 | } 55 | ]; 56 | 57 | expect(ValueHelper.findStepIndex(1, stepArray)).toEqual(0); 58 | expect(ValueHelper.findStepIndex(2, stepArray)).toEqual(1); 59 | expect(ValueHelper.findStepIndex(3, stepArray)).toEqual(2); 60 | }); 61 | 62 | it('finds the closest index if value is not equal', () => { 63 | const stepArray: CustomStepDefinition[] = [ 64 | { 65 | value: 10 66 | }, 67 | { 68 | value: 20 69 | }, 70 | { 71 | value: 30 72 | } 73 | ]; 74 | 75 | expect(ValueHelper.findStepIndex(9, stepArray)).toEqual(0); 76 | expect(ValueHelper.findStepIndex(11, stepArray)).toEqual(0); 77 | expect(ValueHelper.findStepIndex(15, stepArray)).toEqual(0); 78 | 79 | expect(ValueHelper.findStepIndex(16, stepArray)).toEqual(1); 80 | expect(ValueHelper.findStepIndex(19, stepArray)).toEqual(1); 81 | expect(ValueHelper.findStepIndex(21, stepArray)).toEqual(1); 82 | expect(ValueHelper.findStepIndex(25, stepArray)).toEqual(1); 83 | 84 | expect(ValueHelper.findStepIndex(26, stepArray)).toEqual(2); 85 | expect(ValueHelper.findStepIndex(29, stepArray)).toEqual(2); 86 | expect(ValueHelper.findStepIndex(31, stepArray)).toEqual(2); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | # Known issues 2 | 3 | ## Slider is not refreshing when used in a dynamic component 4 | 5 | 2019-06-11 UPDATE: This issue is now resolved on newest browser versions which support [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). At this time it is Chrome >= 64 and Opera >= 51. For other browsers, or older browser versions, the workaround below still applies. 6 | 7 | If you use slider in a dynamic component (e.g. pop-up dialog shown on user action), then the slider may not be shown properly on first render. You may see the slider elements being stacked together like so: 8 | ![slider not refreshed](https://raw.githubusercontent.com/angular-slider/ngx-slider/master/assets/slider-not-refreshed.png) 9 | 10 | This is a known problem which comes from the way DOM updates are published (or more precisely not published) as changes visible to Angular components. 11 | 12 | The workaround is to use `manualRefresh` input to the slider, and trigger the manual refresh when the slider is shown for the first time. 13 | 14 | Please refer to [manual refresh demo](https://angular-slider.github.io/ngx-slider/demos#manual-refresh-slider) for an example of how to use it. 15 | 16 | ## Rendering performance 17 | 18 | When using multiple instances of slider on single page, the first page render will likely take a longer time to complete. 19 | 20 | This is caused by the slider initialisation code, which waits for the first render of its elements before it can determine their bounds and then re-position them as necessary. 21 | 22 | There is no easy way to solve this other than re-writing the rendering code. This is closely related to the problem with Angular Universal described below. However, if the use-case is simple enough, there is a workaround available which could help - see the discussion on [issue #45](https://github.com/angular-slider/ngx-slider/issues/45) for details. 23 | 24 | ## Compatibility with Angular Universal 25 | 26 | The slider component is currently strictly tied to browser environment, because it positions its elements based on current geometry of its elements in browser window. This makes it incompatible with Angular Universal. 27 | 28 | Unfortunately, there is no easy fix for this, other than re-writing all of the layouting code in the slider, and applying style changes through CSS. For more detailed explanation and discussion, please see [issue #66](https://github.com/angular-slider/ngx-slider/issues/66). 29 | 30 | Therefore, for the present and near future, Angular Universal will not be supported. 31 | 32 | ## Using CSS transform: scale makes slider render incorrectly 33 | 34 | Using `transform : scale(N)` in css will mess with the calculation of the slider. You can workaround this by setting the scale in ngx-slider options as the opposite value of the scale set in css. [See Issue.](https://github.com/angular-slider/ngx-slider/issues/367) 35 | 36 | ## Compatibility with Angular's recommended Content Security Policy (CSP) 37 | 38 | Ngx-Slider makes use of innerHTML which means that sites that make use of a Content Security Policy (CSP) throw an exceptions. To fix this you can set the value of the `AllowUnsafeHtmlInSlider` provider in your app.module to `false`. Note that this will prevent you from customizing labels using HTML. 39 | -------------------------------------------------------------------------------- /src/demo-app/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy, HostBinding, inject } from '@angular/core'; 2 | import { ActivatedRoute, Router, Event, NavigationEnd } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'], 8 | standalone: false 9 | }) 10 | export class AppComponent implements OnInit, OnDestroy { 11 | private route = inject(ActivatedRoute); 12 | private router = inject(Router); 13 | 14 | private static REDIRECT_IDS: string[] = [ 15 | 'simple-slider', 16 | 'range-slider', 17 | 'styled-slider', 18 | 'limited-slider', 19 | 'limited-range-slider', 20 | 'no-switching-range-slider', 21 | 'push-range-slider', 22 | 'selection-bar-slider', 23 | 'selection-bar-at-end-slider', 24 | 'selection-bar-from-value-slider', 25 | 'selection-bar-gradient-slider', 26 | 'dynamic-color-selection-bar-slider', 27 | 'dynamic-pointer-color-slider', 28 | 'stepped-slider', 29 | 'right-to-left-slider', 30 | 'floating-point-slider', 31 | 'custom-display-function-slider', 32 | 'custom-combine-labels-function-slider', 33 | 'custom-html-display-function-slider', 34 | 'alphabet-slider', 35 | 'date-slider', 36 | 'ticks-slider', 37 | 'intermediate-ticks-slider', 38 | 'custom-ticks-slider', 39 | 'custom-ticks-legend-slider', 40 | 'custom-legend-function-slider', 41 | 'ticks-tooltips-slider', 42 | 'ticks-custom-tooltips-slider', 43 | 'ticks-values-tooltips-slider', 44 | 'ticks-values-range-slider', 45 | 'intermediate-ticks-values-range-slider', 46 | 'dynamic-tick-color-slider', 47 | 'log-scale-slider', 48 | 'custom-scale-slider', 49 | 'draggable-range-slider', 50 | 'draggable-range-only-slider', 51 | 'disabled-slider', 52 | 'read-only-slider', 53 | 'vertical-sliders', 54 | 'user-events-slider', 55 | 'manual-refresh-slider' 56 | ]; 57 | 58 | @HostBinding('class.test-mode') 59 | testMode: boolean = false; 60 | 61 | private queryParamsSub: any; 62 | private fragmentSub: any; 63 | private eventsSub: any; 64 | 65 | ngOnInit(): void { 66 | this.queryParamsSub = this.route.queryParams.subscribe((params: any): void => { 67 | this.testMode = params['testMode'] === 'true'; 68 | }); 69 | 70 | // Provide redirects for old site links 71 | this.fragmentSub = this.route.fragment.subscribe((fragment: string) => { 72 | if (this.route.snapshot.url.length === 0 && AppComponent.REDIRECT_IDS.indexOf(fragment) !== -1) { 73 | this.router.navigateByUrl('/demos#' + fragment); 74 | } 75 | }); 76 | 77 | this.eventsSub = this.router.events.subscribe((event: Event) => { 78 | if (event instanceof NavigationEnd) { 79 | const { fragment } = this.router.parseUrl(this.router.url); 80 | if (fragment !== undefined && fragment !== null) { 81 | const element: Element = document.querySelector(`#${fragment}`); 82 | if (element !== undefined && element !== null) { 83 | element.scrollIntoView(); 84 | } 85 | } else { 86 | window.scrollTo(0, 0); 87 | } 88 | } 89 | }); 90 | } 91 | 92 | ngOnDestroy(): void { 93 | this.queryParamsSub.unsubscribe(); 94 | this.fragmentSub.unsubscribe(); 95 | this.eventsSub.unsubscribe(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/event-listener-helper.ts: -------------------------------------------------------------------------------- 1 | import { Renderer2 } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { throttleTime, tap } from 'rxjs/operators'; 4 | import { supportsPassiveEvents } from 'detect-passive-events'; 5 | 6 | import { EventListener } from './event-listener'; 7 | import { ValueHelper } from './value-helper'; 8 | 9 | /** 10 | * Helper class to attach event listeners to DOM elements with debounce support using rxjs 11 | */ 12 | export class EventListenerHelper { 13 | constructor(private renderer: Renderer2) { 14 | } 15 | 16 | public attachPassiveEventListener(nativeElement: any, eventName: string, callback: (event: any) => void, 17 | throttleInterval?: number): EventListener { 18 | // Only use passive event listeners if the browser supports it 19 | if (supportsPassiveEvents !== true) { 20 | return this.attachEventListener(nativeElement, eventName, callback, throttleInterval); 21 | } 22 | 23 | // Angular doesn't support passive event handlers (yet), so we need to roll our own code using native functions 24 | const listener: EventListener = new EventListener(); 25 | listener.eventName = eventName; 26 | listener.events = new Subject(); 27 | 28 | const observerCallback: (event: Event) => void = (event: Event): void => { 29 | listener.events.next(event); 30 | }; 31 | nativeElement.addEventListener(eventName, observerCallback, {passive: true, capture: false}); 32 | 33 | listener.teardownCallback = (): void => { 34 | nativeElement.removeEventListener(eventName, observerCallback, {passive: true, capture: false}); 35 | }; 36 | 37 | listener.eventsSubscription = listener.events 38 | .pipe((!ValueHelper.isNullOrUndefined(throttleInterval)) 39 | ? throttleTime(throttleInterval, undefined, { leading: true, trailing: true}) 40 | : tap(() => {}) // no-op 41 | ) 42 | .subscribe((event: Event) => { 43 | callback(event); 44 | }); 45 | 46 | return listener; 47 | } 48 | 49 | public detachEventListener(eventListener: EventListener): void { 50 | if (!ValueHelper.isNullOrUndefined(eventListener.eventsSubscription)) { 51 | eventListener.eventsSubscription.unsubscribe(); 52 | eventListener.eventsSubscription = null; 53 | } 54 | 55 | if (!ValueHelper.isNullOrUndefined(eventListener.events)) { 56 | eventListener.events.complete(); 57 | eventListener.events = null; 58 | } 59 | 60 | if (!ValueHelper.isNullOrUndefined(eventListener.teardownCallback)) { 61 | eventListener.teardownCallback(); 62 | eventListener.teardownCallback = null; 63 | } 64 | } 65 | 66 | public attachEventListener(nativeElement: any, eventName: string, callback: (event: any) => void, 67 | throttleInterval?: number): EventListener { 68 | const listener: EventListener = new EventListener(); 69 | listener.eventName = eventName; 70 | listener.events = new Subject(); 71 | 72 | const observerCallback: (event: Event) => void = (event: Event): void => { 73 | listener.events.next(event); 74 | }; 75 | 76 | listener.teardownCallback = this.renderer.listen(nativeElement, eventName, observerCallback); 77 | 78 | listener.eventsSubscription = listener.events 79 | .pipe((!ValueHelper.isNullOrUndefined(throttleInterval)) 80 | ? throttleTime(throttleInterval, undefined, { leading: true, trailing: true}) 81 | : tap(() => {}) // no-op 82 | ) 83 | .subscribe((event: Event) => { callback(event); }); 84 | 85 | return listener; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/ngx-slider/lib/slider.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @for (t of ticks; track t) { 34 | 35 | 36 | @if (t.value !== null && t.value !== undefined) { 37 | 39 | } 40 | @if (t.legend !== null && t.legend !== undefined && allowUnsafeHtmlInSlider === false) { 41 | 42 | } 43 | @if (t.legend !== null && t.legend !== undefined && (allowUnsafeHtmlInSlider === null || allowUnsafeHtmlInSlider === undefined || allowUnsafeHtmlInSlider)) { 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-slider/ngx-slider", 3 | "version": "21.0.0", 4 | "private": true, 5 | "description": "Self-contained, mobile friendly slider component for Angular based on angularjs-slider", 6 | "keywords": [ 7 | "slider", 8 | "ui", 9 | "component", 10 | "angular", 11 | "ng", 12 | "ngx" 13 | ], 14 | "author": "Piotr Dziwinski", 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/angular-slider/ngx-slider.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/angular-slider/ngx-slider/issues" 22 | }, 23 | "homepage": "https://github.com/angular-slider/ngx-slider#readme", 24 | "scripts": { 25 | "prepare": "npm run prepare:lib && npm run prepare:demo-app", 26 | "prepare:lib": "node scripts/generate-lib-files.js", 27 | "prepare:demo-app": "node scripts/generate-demo-app-snippets.js && node scripts/esm/generate-demo-app-docs.mjs && node scripts/copy-assets-from-node-modules.js", 28 | "start": "npm run prepare && ng serve", 29 | "build": "npm run build:lib && npm run build:demo-app", 30 | "build:lib": "npm run prepare:lib && rimraf dist/ngx-slider && ng-packagr -p src/ngx-slider/ng-package.json && node scripts/bundle-scss.js", 31 | "build:demo-app": "npm run prepare:demo-app && rimraf dist/demo-app && ng build --configuration production --base-href /ngx-slider/", 32 | "publish-gh": "scripts/publish-gh-pages.sh", 33 | "test": "npm run prepare && ng test", 34 | "test:once": "npm run prepare && ng test --watch=false", 35 | "lint": "npm run prepare && ng lint", 36 | "e2e": "npm run prepare && playwright test", 37 | "ci": "npm run test:once && playwright test && npm run lint && npm run build:lib && npm run build:demo-app" 38 | }, 39 | "dependencies": { 40 | "@angular/animations": "^21.0.0", 41 | "@angular/common": "^21.0.0", 42 | "@angular/compiler": "^21.0.0", 43 | "@angular/core": "^21.0.0", 44 | "@angular/forms": "^21.0.0", 45 | "@angular/platform-browser": "^21.0.0", 46 | "@angular/platform-browser-dynamic": "^21.0.0", 47 | "@angular/router": "^21.0.0", 48 | "@ng-bootstrap/ng-bootstrap": "^20.0.0-rc.0", 49 | "bootstrap": "^5.3.8", 50 | "bootstrap-icons": "^1.11.3", 51 | "detect-passive-events": "^2.0.3", 52 | "rxjs": "^7.8.2", 53 | "tslib": "^2.8.1", 54 | "zone.js": "~0.15.1" 55 | }, 56 | "devDependencies": { 57 | "@angular-eslint/builder": "21.0.0", 58 | "@angular-eslint/eslint-plugin": "21.0.0", 59 | "@angular-eslint/eslint-plugin-template": "21.0.0", 60 | "@angular-eslint/schematics": "21.0.0", 61 | "@angular-eslint/template-parser": "21.0.0", 62 | "@angular/build": "^21.0.0", 63 | "@angular/cli": "^21.0.0", 64 | "@angular/compiler-cli": "^21.0.0", 65 | "@playwright/test": "^1.41.2", 66 | "@types/jasmine": "~5.1.13", 67 | "@types/node": "^22.10.10", 68 | "@typescript-eslint/eslint-plugin": "^8.34.1", 69 | "@typescript-eslint/parser": "^8.34.1", 70 | "@typescript-eslint/utils": "^8.34.1", 71 | "escape-html": "^1.0.3", 72 | "eslint": "^8.56.0", 73 | "jasmine-core": "~5.5.0", 74 | "jasmine-spec-reporter": "~7.0.0", 75 | "karma": "~6.4.4", 76 | "karma-chrome-launcher": "~3.2.0", 77 | "karma-coverage-istanbul-reporter": "~3.0.3", 78 | "karma-jasmine": "~5.1.0", 79 | "karma-jasmine-html-reporter": "^2.1.0", 80 | "mkdirp": "^3.0.1", 81 | "ng-packagr": "^21.0.0", 82 | "prismjs": "^1.30.0", 83 | "rimraf": "^6.1.2", 84 | "ts-node": "~10.9.2", 85 | "typedoc": "~0.28.15", 86 | "typescript": "~5.9.3" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/demo-app/app/snippets/index.ts: -------------------------------------------------------------------------------- 1 | export * from './alphabet-slider/alphabet-slider.component'; 2 | export * from './custom-combine-labels-function-slider/custom-combine-labels-function-slider.component'; 3 | export * from './custom-display-function-slider/custom-display-function-slider.component'; 4 | export * from './custom-html-display-function-slider/custom-html-display-function-slider.component'; 5 | export * from './custom-legend-function-slider/custom-legend-function-slider.component'; 6 | export * from './custom-scale-slider/custom-scale-slider.component'; 7 | export * from './custom-ticks-legend-slider/custom-ticks-legend-slider.component'; 8 | export * from './custom-ticks-legend-slider/custom-ticks-legend-slider.component'; 9 | export * from './custom-ticks-slider/custom-ticks-slider.component'; 10 | export * from './date-slider/date-slider.component'; 11 | export * from './disabled-normalisation-slider/disabled-normalisation-slider.component'; 12 | export * from './disabled-slider/disabled-slider.component'; 13 | export * from './draggable-range-only-slider/draggable-range-only-slider.component'; 14 | export * from './draggable-range-slider/draggable-range-slider.component'; 15 | export * from './dynamic-color-selection-bar-slider/dynamic-color-selection-bar-slider.component'; 16 | export * from './dynamic-options-slider/dynamic-options-slider.component'; 17 | export * from './dynamic-pointer-color-slider/dynamic-pointer-color-slider.component'; 18 | export * from './dynamic-tick-color-slider/dynamic-tick-color-slider.component'; 19 | export * from './dynamically-created-sliders/dynamically-created-sliders.component'; 20 | export * from './floating-point-slider/floating-point-slider.component'; 21 | export * from './intermediate-ticks-slider/intermediate-ticks-slider.component'; 22 | export * from './intermediate-ticks-values-range-slider/intermediate-ticks-values-range-slider.component'; 23 | export * from './limited-range-slider/limited-range-slider.component'; 24 | export * from './limited-slider/limited-slider.component'; 25 | export * from './log-scale-slider/log-scale-slider.component'; 26 | export * from './manual-refresh-slider/manual-refresh-slider.component'; 27 | export * from './no-switching-range-slider/no-switching-range-slider.component'; 28 | export * from './prevent-change-on-scroll-slider/prevent-change-on-scroll-slider.component'; 29 | export * from './push-range-slider/push-range-slider.component'; 30 | export * from './range-slider/range-slider.component'; 31 | export * from './reactive-form-range-slider/reactive-form-range-slider.component'; 32 | export * from './reactive-form-simple-slider/reactive-form-simple-slider.component'; 33 | export * from './read-only-slider/read-only-slider.component'; 34 | export * from './right-to-left-slider/right-to-left-slider.component'; 35 | export * from './selection-bar-at-end-slider/selection-bar-at-end-slider.component'; 36 | export * from './selection-bar-from-value-slider/selection-bar-from-value-slider.component'; 37 | export * from './selection-bar-gradient-slider/selection-bar-gradient-slider.component'; 38 | export * from './selection-bar-slider/selection-bar-slider.component'; 39 | export * from './simple-slider/simple-slider.component'; 40 | export * from './stepped-slider/stepped-slider.component'; 41 | export * from './styled-slider/styled-slider.component'; 42 | export * from './ticks-custom-tooltips-slider/ticks-custom-tooltips-slider.component'; 43 | export * from './ticks-slider/ticks-slider.component'; 44 | export * from './ticks-tooltips-slider/ticks-tooltips-slider.component'; 45 | export * from './ticks-values-range-slider/ticks-values-range-slider.component'; 46 | export * from './ticks-values-slider/ticks-values-slider.component'; 47 | export * from './ticks-values-tooltips-slider/ticks-values-tooltips-slider.component'; 48 | export * from './trigger-focus-slider/trigger-focus-slider.component'; 49 | export * from './user-events-slider/user-events-slider.component'; 50 | export * from './vertical-sliders/vertical-sliders.component'; 51 | -------------------------------------------------------------------------------- /scripts/generate-demo-app-snippets.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script will go through all *.component.template.html (and *.component.title-template.html) 3 | files in snippets and generate the corresponding HTML template *.component.html, pasting the code 4 | examples in the HTML. 5 | */ 6 | 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | const escape = require('escape-html'); 10 | const prism = require('prismjs'); 11 | require('prismjs/components/prism-scss'); 12 | require('prismjs/components/prism-typescript'); 13 | 14 | const utils = require('./utils.js'); 15 | 16 | /** Generate template for a single file */ 17 | function generateTemplate(templateFile, snippetsDir) { 18 | const titleTemplateFile = templateFile.replace('.template.html', '.title-template.html'); 19 | const sectionIdTemplateFile = templateFile.replace('.template.html', '.id-template.html'); 20 | const outputTemplateFile = templateFile.replace('.template.html', '.html'); 21 | const codeFile = templateFile.replace('.template.html', '.ts'); 22 | const styleFile = templateFile.replace('.template.html', '.scss'); 23 | 24 | const titleTemplateFileContent = fs.readFileSync(path.resolve(snippetsDir, titleTemplateFile), { encoding: 'utf8' }).trim(); 25 | const sectionIdTemplateFileContent = fs.readFileSync(path.resolve(snippetsDir, sectionIdTemplateFile), { encoding: 'utf8' }).trim(); 26 | 27 | const templateFileContent = fs.readFileSync(path.resolve(snippetsDir, templateFile), { encoding: 'utf8' }); 28 | const templateNavHtml = navHtml(path.basename(outputTemplateFile), templateFileContent, 'html'); 29 | 30 | let codeFileContent = fs.readFileSync(path.resolve(snippetsDir, codeFile), { encoding: 'utf8' }); 31 | // The only modification to the source file is to remove the @local prefix from slider import 32 | codeFileContent = codeFileContent.replace(/@local\/ngx-slider/g, "@angular-slider/ngx-slider"); 33 | const codeNavHtml = navHtml(path.basename(codeFile), codeFileContent, 'typescript'); 34 | 35 | let styleNavHtml = ''; 36 | if (fs.existsSync(path.resolve(snippetsDir, styleFile))) { 37 | const styleFileContent = fs.readFileSync(path.resolve(snippetsDir, styleFile), { encoding: 'utf8' }); 38 | styleNavHtml = navHtml(path.basename(styleFile), styleFileContent, 'scss'); 39 | } 40 | const fileNameAndPath = process.platform === "win32" ? templateFile.split('\\') : templateFile.split('/'); 41 | const fileName = fileNameAndPath[fileNameAndPath.length -1]; 42 | const navName = fileName.replace(/-/g,'').replace('.component','').replace('.template.html', 'Nav'); 43 | 44 | const outputHtmlFileContent = ` 45 |

${titleTemplateFileContent} 46 | 47 |

48 |
49 |
50 |
51 | ${templateFileContent} 52 |
53 | 54 | 61 |
62 |
63 |
`; 64 | 65 | fs.writeFileSync(path.resolve(snippetsDir, outputTemplateFile), utils.escapeAtForAngular(outputHtmlFileContent), { encoding: 'utf8' }); 66 | } 67 | 68 | /** Generate highlighted source code using prism */ 69 | function highlight(code, lang) { 70 | return prism.highlight(code.trim(), prism.languages[lang]); 71 | } 72 | 73 | /** Common HTML template for tab */ 74 | function navHtml(tabTitle, codeContent, codeLang) { 75 | return `
  • 76 | ${escape(tabTitle)} 77 | 78 |
    ${utils.escapeBracesForAngular(highlight(codeContent, codeLang))}
    79 |
    80 |
  • `; 81 | } 82 | 83 | 84 | const snippetsDir = path.resolve(__dirname, '../src/demo-app/app/snippets'); 85 | 86 | const templateFiles = utils.readdirRecursivelySync(snippetsDir) 87 | .filter((file) => file.endsWith('component.template.html')); 88 | 89 | for (let templateFile of templateFiles) { 90 | generateTemplate(templateFile, snippetsDir) 91 | } 92 | -------------------------------------------------------------------------------- /typedoc/README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | These pages are generated from source code of ngx-slider using Typedoc. 4 | 5 | Below, you will find an overview of using the slider directive, and following the navigation links, you can explore the public API portion of the library. 6 | 7 | The public API is made up of all declarations made available when importing the `ngx-slider` package, for instance the `Options` class: 8 | ```ts 9 | import { Options } from '@angular-slider/ngx-slider'; 10 | ``` 11 | 12 | ## Slider directive 13 | 14 | The slider component takes the following inputs and outputs: 15 | ```html 16 | 29 | ``` 30 | 31 | ### Model bindings 32 | 33 | For single value slider, `value` specifies the model value of the slider. For range sliders, `value` is the minimum model value and `highValue` is the maximum model value. 34 | 35 | ### Alternative bindings using reactive forms 36 | 37 | Instead of binding to `value` and `highValue`, it is also possible to bind the values using Angular [reactive forms](https://angular.io/guide/reactive-forms) (`formControl` and `formControlName` directives). For an example of this, refer to relevant examples: [simple slider in reactive form](routerLink:///demos#reactive-form-simple-slider) and [range slider in reactive form](routerLink:///demos#reactive-form-range-slider). 38 | 39 | ### Options 40 | 41 | `options` is an object of options that configure the slider (e.g. minimum, maximum values, legend values, etc.). Available options are documented in [Options class](routerLink:///docs/classes/_options_.options.html). 42 | 43 | **Note**: Due to the way change detection works in Angular, runtime changes in nested values of options object will not be picked up automatically. To work around this, you need to re-create the options object every time you make a change: 44 | ```ts 45 | changeOptions() { 46 | const newOptions: Options = Object.assign({}, currentOptions); 47 | newOptions.ceil = 100; 48 | currentOptions = newOptions; 49 | } 50 | ``` 51 | For a complete example, see the [dynamic options slider demo](routerLink:///demos#dynamic-options-slider). 52 | 53 | ### Manual refresh 54 | 55 | `manualRefresh` input is provided to solve some cases where the slider is not being updated after CSS style changes. This is for example changing the `display` property to show/hide the slider (or any parent DOM element). Instead of observing the CSS changes, the slider provides this input to manually trigger a refresh. Refer to the [example demo](routerLink:///demos#manual-refresh-slider) to see how it can be used. 56 | 57 | ### Trigger focus 58 | 59 | `triggerFocus` input is provided to set the focus programmatically on a slider handle. The emitter takes a `PointerType` as argument, or if left `undefined`, will default to `PointerType.Min`. Refer to the [example demo](routerLink:///demos#trigger-focus-slider) to see how it works. 60 | 61 | ### Cancel user change 62 | 63 | `cancelUserChange` input ends current user intraction and restores value of a slider handle that was present before `userChangeStart` triggered. Refer to the [example demo](routerLink:///demos#prevent-change-on-scroll-slider) to see how it works. 64 | 65 | ### User change events 66 | 67 | `userChangeStart`, `userChange` and `userChangeEnd` provide output events that are triggered by user interaction (through keyboard, mouse or touchpad). The event handler also passes a `ChangeContext` object which contains details about the changes. Refer to the [example demo](routerLink:///demos#user-events-slider) to see how it works. 68 | 69 | ### Value change events 70 | 71 | `valueChange` and `highValueChange` outputs are emitted whenever the model values change (including programmatically). They are provided to support two-way binding of the model values but they can also be used to attach custom event handlers. 72 | 73 | ### Old documentation 74 | 75 | If you are looking for old documentation of v1.2.x releases under the old name of ng5-slider, they are [archived in the Github repository](https://raw.githubusercontent.com/angular-slider/ngx-slider/master/archive/ng5-slider-v1.2.6-site-archive.zip). -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-slider-demo-app": { 7 | "root": "", 8 | "sourceRoot": "src/demo-app", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular/build:application", 15 | "options": { 16 | "outputPath": { 17 | "base": "dist/demo-app" 18 | }, 19 | "index": "src/demo-app/index.html", 20 | "polyfills": [ 21 | "zone.js" 22 | ], 23 | "tsConfig": "src/demo-app/tsconfig.app.json", 24 | "assets": [ 25 | "src/demo-app/favicon.ico", 26 | "src/demo-app/assets", 27 | { 28 | "glob": "**/*", 29 | "input": "docs/", 30 | "output": "api-docs/" 31 | } 32 | ], 33 | "styles": [ 34 | "src/demo-app/styles.scss" 35 | ], 36 | "scripts": [], 37 | "browser": "src/demo-app/main.ts" 38 | }, 39 | "configurations": { 40 | "production": { 41 | "fileReplacements": [ 42 | { 43 | "replace": "src/demo-app/environments/environment.ts", 44 | "with": "src/demo-app/environments/environment.prod.ts" 45 | } 46 | ], 47 | "optimization": true, 48 | "outputHashing": "all", 49 | "sourceMap": false, 50 | "namedChunks": false, 51 | "aot": true, 52 | "extractLicenses": true 53 | } 54 | }, 55 | "defaultConfiguration": "" 56 | }, 57 | "serve": { 58 | "builder": "@angular/build:dev-server", 59 | "options": { 60 | "buildTarget": "ngx-slider-demo-app:build" 61 | }, 62 | "configurations": { 63 | "production": { 64 | "buildTarget": "ngx-slider-demo-app:build:production" 65 | } 66 | } 67 | }, 68 | "extract-i18n": { 69 | "builder": "@angular/build:extract-i18n", 70 | "options": { 71 | "buildTarget": "ngx-slider-demo-app:build" 72 | } 73 | }, 74 | "test": { 75 | "builder": "@angular/build:karma", 76 | "options": { 77 | "main": "src/demo-app/test.ts", 78 | "polyfills": [ 79 | "zone.js", 80 | "zone.js/testing" 81 | ], 82 | "tsConfig": "src/demo-app/tsconfig.spec.json", 83 | "karmaConfig": "src/demo-app/karma.conf.js", 84 | "include": [ 85 | "../ngx-slider/**/**.spec.ts", 86 | "../ngx-slider/**/**.d.ts" 87 | ], 88 | "styles": [ 89 | "src/demo-app/styles.scss" 90 | ], 91 | "scripts": [], 92 | "assets": [ 93 | "src/demo-app/favicon.ico", 94 | "src/demo-app/assets", 95 | { 96 | "glob": "**/*", 97 | "input": "docs/", 98 | "output": "api-docs/" 99 | } 100 | ] 101 | } 102 | }, 103 | "lint": { 104 | "builder": "@angular-eslint/builder:lint", 105 | "options": { 106 | "lintFilePatterns": [ 107 | "src/**/*.ts", 108 | "src/**/*.html" 109 | ] 110 | } 111 | } 112 | } 113 | } 114 | }, 115 | "cli": { 116 | "analytics": false, 117 | "schematicCollections": [ 118 | "@angular-eslint/schematics" 119 | ] 120 | }, 121 | "schematics": { 122 | "@schematics/angular:component": { 123 | "type": "component" 124 | }, 125 | "@schematics/angular:directive": { 126 | "type": "directive" 127 | }, 128 | "@schematics/angular:service": { 129 | "type": "service" 130 | }, 131 | "@schematics/angular:guard": { 132 | "typeSeparator": "." 133 | }, 134 | "@schematics/angular:interceptor": { 135 | "typeSeparator": "." 136 | }, 137 | "@schematics/angular:module": { 138 | "typeSeparator": "." 139 | }, 140 | "@schematics/angular:pipe": { 141 | "typeSeparator": "." 142 | }, 143 | "@schematics/angular:resolver": { 144 | "typeSeparator": "." 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /e2e/limited-range-slider.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, Page, Locator } from '@playwright/test'; 2 | import { expect, mouseDragRelative, touchDragRelative } from './utils'; 3 | 4 | async function setUp(page: Page) { 5 | await page.setViewportSize({ width: 800, height: 600 }); 6 | await page.goto('/limited-range-slider?testMode=true'); 7 | } 8 | 9 | function getSlider(page: Page): Locator { 10 | return page.locator('ngx-slider'); 11 | } 12 | 13 | function getSliderFloorLabel(page: Page): Locator { 14 | return getSlider(page).locator('span.ngx-slider-floor'); 15 | } 16 | 17 | function getSliderCeilLabel(page: Page): Locator { 18 | return getSlider(page).locator('span.ngx-slider-ceil'); 19 | } 20 | 21 | function getSliderLowPointer(page: Page): Locator { 22 | return getSlider(page).locator('span.ngx-slider-pointer-min'); 23 | } 24 | 25 | function getSliderHighPointer(page: Page): Locator { 26 | return getSlider(page).locator('span.ngx-slider-pointer-max'); 27 | } 28 | 29 | function getSliderLowPointerLabel(page: Page): Locator { 30 | return getSlider(page).locator('span.ngx-slider-model-value'); 31 | } 32 | 33 | function getSliderHighPointerLabel(page: Page): Locator { 34 | return getSlider(page).locator('span.ngx-slider-model-high'); 35 | } 36 | 37 | 38 | 39 | test('limited range slider initial state displays starting values', async ({ page }) => { 40 | await setUp(page); 41 | 42 | await expect(getSliderFloorLabel(page)).toHaveText('0'); 43 | await expect(getSliderCeilLabel(page)).toHaveText('100'); 44 | await expect(getSliderLowPointerLabel(page)).toHaveText('40'); 45 | await expect(getSliderHighPointerLabel(page)).toHaveText('60'); 46 | }); 47 | 48 | test('limited range slider dragging low pointer within the min range limit moves it to new position', async ({ page }) => { 49 | await setUp(page); 50 | 51 | await mouseDragRelative(getSliderLowPointer(page), {offsetX: 66, offsetY: 0}); 52 | 53 | await expect(getSliderLowPointerLabel(page)).toHaveText('49'); 54 | await expect(getSliderHighPointerLabel(page)).toHaveText('60'); 55 | }); 56 | 57 | test('limited range slider dragging low pointer exceeding the min range limit stops the movement at the limit', async ({ page }) => { 58 | await setUp(page); 59 | 60 | await touchDragRelative(getSliderLowPointer(page), {offsetX: 100, offsetY: 0}); 61 | 62 | await expect(getSliderLowPointerLabel(page)).toHaveText('50'); 63 | await expect(getSliderHighPointerLabel(page)).toHaveText('60'); 64 | }); 65 | 66 | test('limited range slider dragging low pointer within the max range limit moves it to new position', async ({ page }) => { 67 | await setUp(page); 68 | 69 | await mouseDragRelative(getSliderLowPointer(page), {offsetX: -212, offsetY: 0}); 70 | 71 | await expect(getSliderLowPointerLabel(page)).toHaveText('11'); 72 | await expect(getSliderHighPointerLabel(page)).toHaveText('60'); 73 | }); 74 | 75 | test('limited range slider dragging low pointer exceeding the max range limit stops the movement at the limit', async ({ page }) => { 76 | await setUp(page); 77 | 78 | await touchDragRelative(getSliderLowPointer(page), {offsetX: -300, offsetY: 0}); 79 | 80 | await expect(getSliderLowPointerLabel(page)).toHaveText('10'); 81 | await expect(getSliderHighPointerLabel(page)).toHaveText('60'); 82 | }); 83 | 84 | test('limited range slider dragging high pointer within the min range limit moves it to new position', async ({ page }) => { 85 | await setUp(page); 86 | 87 | await mouseDragRelative(getSliderHighPointer(page), {offsetX: -66, offsetY: 0}); 88 | 89 | await expect(getSliderLowPointerLabel(page)).toHaveText('40'); 90 | await expect(getSliderHighPointerLabel(page)).toHaveText('51'); 91 | }); 92 | 93 | test('limited range slider dragging high pointer exceeding the min range limit stops the movement at the limit', async ({ page }) => { 94 | await setUp(page); 95 | 96 | await touchDragRelative(getSliderHighPointer(page), {offsetX: -100, offsetY: 0}); 97 | 98 | await expect(getSliderLowPointerLabel(page)).toHaveText('40'); 99 | await expect(getSliderHighPointerLabel(page)).toHaveText('50'); 100 | }); 101 | 102 | test('limited range slider dragging high pointer within the max range limit moves it to new position', async ({ page }) => { 103 | await setUp(page); 104 | 105 | await mouseDragRelative(getSliderHighPointer(page), {offsetX: 212, offsetY: 0}); 106 | 107 | await expect(getSliderLowPointerLabel(page)).toHaveText('40'); 108 | await expect(getSliderHighPointerLabel(page)).toHaveText('89'); 109 | }); 110 | 111 | test('limited range slider dragging high pointer exceeding the max range limit stops the movement at the limit', async ({ page }) => { 112 | await setUp(page); 113 | 114 | await touchDragRelative(getSliderHighPointer(page), {offsetX: 300, offsetY: 0}); 115 | 116 | await expect(getSliderLowPointerLabel(page)).toHaveText('40'); 117 | await expect(getSliderHighPointerLabel(page)).toHaveText('90'); 118 | }); 119 | -------------------------------------------------------------------------------- /e2e/no-switching-range-slider.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, Page, Locator } from '@playwright/test'; 2 | import { expect, mouseDragRelative, touchDragRelative } from './utils'; 3 | 4 | async function setUp(page: Page) { 5 | await page.setViewportSize({ width: 800, height: 600 }); 6 | await page.goto('/no-switching-range-slider?testMode=true'); 7 | } 8 | 9 | function getSlider(page: Page): Locator { 10 | return page.locator('ngx-slider'); 11 | } 12 | 13 | function getSliderFloorLabel(page: Page): Locator { 14 | return getSlider(page).locator('span.ngx-slider-floor'); 15 | } 16 | 17 | function getSliderCeilLabel(page: Page): Locator { 18 | return getSlider(page).locator('span.ngx-slider-ceil'); 19 | } 20 | 21 | function getSliderLowPointer(page: Page): Locator { 22 | return getSlider(page).locator('span.ngx-slider-pointer-min'); 23 | } 24 | 25 | function getSliderHighPointer(page: Page): Locator { 26 | return getSlider(page).locator('span.ngx-slider-pointer-max'); 27 | } 28 | 29 | function getSliderLowPointerLabel(page: Page): Locator { 30 | return getSlider(page).locator('span.ngx-slider-model-value'); 31 | } 32 | 33 | function getSliderHighPointerLabel(page: Page): Locator { 34 | return getSlider(page).locator('span.ngx-slider-model-high'); 35 | } 36 | 37 | function getSliderCombinedLabel(page: Page): Locator { 38 | return getSlider(page).locator('span.ngx-slider-combined'); 39 | } 40 | 41 | 42 | test('no switching range slider initial state displays starting values', async ({ page }) => { 43 | await setUp(page); 44 | 45 | await expect(getSliderFloorLabel(page)).toHaveText('0'); 46 | await expect(getSliderCeilLabel(page)).toHaveText('100'); 47 | await expect(getSliderLowPointerLabel(page)).toHaveText('10'); 48 | await expect(getSliderHighPointerLabel(page)).toHaveText('90'); 49 | }); 50 | 51 | test('no switching range slider dragging low pointer below high pointer moves it to new position', async ({ page }) => { 52 | await setUp(page); 53 | 54 | await mouseDragRelative(getSliderLowPointer(page), {offsetX: 366, offsetY: 0}); 55 | 56 | // Slider elements are hidden by setting opacity to 0, hence this assertion 57 | // Regular assertion such as expect(...).toBeHidden() don't work based on opacity 58 | await expect(getSliderLowPointerLabel(page)).not.toHaveCSS('opacity', '0'); 59 | await expect(getSliderHighPointerLabel(page)).not.toHaveCSS('opacity', '0'); 60 | await expect(getSliderCombinedLabel(page)).toHaveCSS('opacity', '0'); 61 | 62 | await expect(getSliderLowPointerLabel(page)).toHaveText('60'); 63 | await expect(getSliderHighPointerLabel(page)).toHaveText('90'); 64 | }); 65 | 66 | test('no switching range slider dragging low pointer beyond high pointer stops the movement at the high pointer and combines labels', async ({ page }) => { 67 | await setUp(page); 68 | 69 | await touchDragRelative(getSliderLowPointer(page), {offsetX: 660, offsetY: 0}); 70 | 71 | // Slider elements are hidden by setting opacity to 0, hence this assertion 72 | // Regular assertion such as expect(...).toBeHidden() don't work based on opacity 73 | await expect(getSliderLowPointerLabel(page)).toHaveCSS('opacity', '0'); 74 | await expect(getSliderHighPointerLabel(page)).toHaveCSS('opacity', '0'); 75 | await expect(getSliderCombinedLabel(page)).not.toHaveCSS('opacity', '0'); 76 | 77 | await expect(getSliderCombinedLabel(page)).toHaveText('90 - 90'); 78 | }); 79 | 80 | test('no switching range slider dragging high pointer above low pointer moves it to new position', async ({ page }) => { 81 | await setUp(page); 82 | 83 | await mouseDragRelative(getSliderHighPointer(page), {offsetX: -366, offsetY: 0}); 84 | 85 | // Slider elements are hidden by setting opacity to 0, hence this assertion 86 | // Regular assertion such as expect(...).toBeHidden() don't work based on opacity 87 | await expect(getSliderLowPointerLabel(page)).not.toHaveCSS('opacity', '0'); 88 | await expect(getSliderHighPointerLabel(page)).not.toHaveCSS('opacity', '0'); 89 | await expect(getSliderCombinedLabel(page)).toHaveCSS('opacity', '0'); 90 | 91 | await expect(getSliderLowPointerLabel(page)).toHaveText('10'); 92 | await expect(getSliderHighPointerLabel(page)).toHaveText('40'); 93 | }); 94 | 95 | test('no switching range slider dragging high pointer beyond low pointer stops the movement at the low pointer and combines labels', async ({ page }) => { 96 | await setUp(page); 97 | 98 | await touchDragRelative(getSliderHighPointer(page), {offsetX: -660, offsetY: 0}); 99 | 100 | // Slider elements are hidden by setting opacity to 0, hence this assertion 101 | // Regular assertion such as expect(...).toBeHidden() don't work based on opacity 102 | await expect(getSliderLowPointerLabel(page)).toHaveCSS('opacity', '0'); 103 | await expect(getSliderHighPointerLabel(page)).toHaveCSS('opacity', '0'); 104 | await expect(getSliderCombinedLabel(page)).not.toHaveCSS('opacity', '0'); 105 | 106 | await expect(getSliderCombinedLabel(page)).toHaveText('10 - 10'); 107 | }); 108 | -------------------------------------------------------------------------------- /src/demo-app/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { RouterModule } from '@angular/router'; 5 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; 6 | 7 | import { NgxSliderModule } from '@local/ngx-slider'; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { HeaderComponent } from './header.component'; 11 | import { HomeComponent } from './home.component'; 12 | import { DemosComponent } from './demos.component'; 13 | import { DocsComponent } from './docs.component'; 14 | import { 15 | AlphabetSliderComponent, 16 | CustomCombineLabelsFunctionSliderComponent, 17 | CustomDisplayFunctionSliderComponent, 18 | CustomHtmlDisplayFunctionSliderComponent, 19 | CustomLegendFunctionSliderComponent, 20 | CustomScaleSliderComponent, 21 | CustomTicksLegendSliderComponent, 22 | CustomTicksSliderComponent, 23 | DateSliderComponent, 24 | DisabledNormalisationSliderComponent, 25 | DisabledSliderComponent, 26 | DraggableRangeOnlySliderComponent, 27 | DraggableRangeSliderComponent, 28 | DynamicColorSelectionBarSliderComponent, 29 | DynamicOptionsSliderComponent, 30 | DynamicPointerColorSliderComponent, 31 | DynamicTickColorSliderComponent, 32 | DynamicallyCreatedSlidersComponent, 33 | FloatingPointSliderComponent, 34 | IntermediateTicksSliderComponent, 35 | IntermediateTicksValuesRangeSliderComponent, 36 | LimitedRangeSliderComponent, 37 | LimitedSliderComponent, 38 | LogScaleSliderComponent, 39 | ManualRefreshSliderComponent, 40 | NoSwitchingRangeSliderComponent, 41 | PreventChangeOnScrollSliderComponent, 42 | PushRangeSliderComponent, 43 | RangeSliderComponent, 44 | ReactiveFormRangeSliderComponent, 45 | ReactiveFormSimpleSliderComponent, 46 | ReadOnlySliderComponent, 47 | RightToLeftSliderComponent, 48 | SelectionBarAtEndSliderComponent, 49 | SelectionBarFromValueSliderComponent, 50 | SelectionBarGradientSliderComponent, 51 | SelectionBarSliderComponent, 52 | SimpleSliderComponent, 53 | SteppedSliderComponent, 54 | StyledSliderComponent, 55 | TicksCustomTooltipsSliderComponent, 56 | TicksSliderComponent, 57 | TicksTooltipsSliderComponent, 58 | TicksValuesRangeSliderComponent, 59 | TicksValuesSliderComponent, 60 | TicksValuesTooltipsSliderComponent, 61 | TriggerFocusSliderComponent, 62 | UserEventsSliderComponent, 63 | VerticalSlidersComponent, 64 | } from './snippets'; 65 | import { routerConfig, routerOptions } from './app-router.config'; 66 | 67 | @NgModule({ 68 | declarations: [ 69 | AppComponent, 70 | HeaderComponent, 71 | HomeComponent, 72 | DemosComponent, 73 | DocsComponent, 74 | AlphabetSliderComponent, 75 | CustomCombineLabelsFunctionSliderComponent, 76 | CustomDisplayFunctionSliderComponent, 77 | CustomHtmlDisplayFunctionSliderComponent, 78 | CustomLegendFunctionSliderComponent, 79 | CustomScaleSliderComponent, 80 | CustomTicksLegendSliderComponent, 81 | CustomTicksSliderComponent, 82 | DateSliderComponent, 83 | DisabledNormalisationSliderComponent, 84 | DisabledSliderComponent, 85 | DraggableRangeOnlySliderComponent, 86 | DraggableRangeSliderComponent, 87 | DynamicColorSelectionBarSliderComponent, 88 | DynamicOptionsSliderComponent, 89 | DynamicPointerColorSliderComponent, 90 | DynamicTickColorSliderComponent, 91 | DynamicallyCreatedSlidersComponent, 92 | FloatingPointSliderComponent, 93 | IntermediateTicksSliderComponent, 94 | IntermediateTicksValuesRangeSliderComponent, 95 | LimitedRangeSliderComponent, 96 | LimitedSliderComponent, 97 | LogScaleSliderComponent, 98 | ManualRefreshSliderComponent, 99 | NoSwitchingRangeSliderComponent, 100 | PreventChangeOnScrollSliderComponent, 101 | PushRangeSliderComponent, 102 | RangeSliderComponent, 103 | ReactiveFormRangeSliderComponent, 104 | ReactiveFormSimpleSliderComponent, 105 | ReadOnlySliderComponent, 106 | RightToLeftSliderComponent, 107 | SelectionBarAtEndSliderComponent, 108 | SelectionBarFromValueSliderComponent, 109 | SelectionBarGradientSliderComponent, 110 | SelectionBarSliderComponent, 111 | SimpleSliderComponent, 112 | SteppedSliderComponent, 113 | StyledSliderComponent, 114 | TicksCustomTooltipsSliderComponent, 115 | TicksSliderComponent, 116 | TicksTooltipsSliderComponent, 117 | TicksValuesRangeSliderComponent, 118 | TicksValuesSliderComponent, 119 | TicksValuesTooltipsSliderComponent, 120 | TriggerFocusSliderComponent, 121 | UserEventsSliderComponent, 122 | VerticalSlidersComponent, 123 | ], 124 | imports: [ 125 | BrowserModule, 126 | FormsModule, 127 | ReactiveFormsModule, 128 | RouterModule.forRoot(routerConfig, routerOptions), 129 | NgbModule, 130 | NgxSliderModule, 131 | ], 132 | providers: [], 133 | bootstrap: [AppComponent], 134 | }) 135 | export class AppModule {} 136 | --------------------------------------------------------------------------------