feat: Add option to disable mousewheel carousel scrolling (#2263)
- Closes: #2220
This commit is contained in:
@@ -41,6 +41,7 @@ live:
|
|||||||
| `next_previous` | | Configures how the "Next & Previous" controls are shown on the `live` view. See below. |
|
| `next_previous` | | Configures how the "Next & Previous" controls are shown on the `live` view. See below. |
|
||||||
| `thumbnails` | | Configures how thumbnails are shown on the `live` view. See below. |
|
| `thumbnails` | | Configures how thumbnails are shown on the `live` view. See below. |
|
||||||
| `timeline` | | Configures how the mini-timeline is shown on the `live` view. See below. |
|
| `timeline` | | Configures how the mini-timeline is shown on the `live` view. See below. |
|
||||||
|
| `wheel` | `true` | Whether to allow mouse wheel to scroll through the carousel. |
|
||||||
|
|
||||||
### `next_previous`
|
### `next_previous`
|
||||||
|
|
||||||
@@ -206,6 +207,7 @@ live:
|
|||||||
next_previous:
|
next_previous:
|
||||||
style: chevrons
|
style: chevrons
|
||||||
size: 48
|
size: 48
|
||||||
|
wheel: true
|
||||||
ptz:
|
ptz:
|
||||||
mode: auto
|
mode: auto
|
||||||
position: bottom-right
|
position: bottom-right
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ media_viewer:
|
|||||||
| `next_previous` | | Configures how the "Next & Previous" controls are shown on the media viewer. See below. |
|
| `next_previous` | | Configures how the "Next & Previous" controls are shown on the media viewer. See below. |
|
||||||
| `thumbnails` | | Configures how thumbnails are shown on the media viewer. See below. |
|
| `thumbnails` | | Configures how thumbnails are shown on the media viewer. See below. |
|
||||||
| `timeline` | | Configures how the mini-timeline is shown on the media viewer. See below. |
|
| `timeline` | | Configures how the mini-timeline is shown on the media viewer. See below. |
|
||||||
|
| `wheel` | `true` | Whether to allow mouse wheel to scroll through the carousel. |
|
||||||
|
|
||||||
### `next_previous`
|
### `next_previous`
|
||||||
|
|
||||||
@@ -183,6 +184,7 @@ media_viewer:
|
|||||||
next_previous:
|
next_previous:
|
||||||
size: 48
|
size: 48
|
||||||
style: thumbnails
|
style: thumbnails
|
||||||
|
wheel: true
|
||||||
ptz:
|
ptz:
|
||||||
mode: off
|
mode: off
|
||||||
position: bottom-right
|
position: bottom-right
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ export class AdvancedCameraCardCarousel extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public plugins?: EmblaCarouselPlugins;
|
public plugins?: EmblaCarouselPlugins;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public wheelScrolling?: boolean;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public selected = 0;
|
public selected = 0;
|
||||||
|
|
||||||
@@ -121,6 +124,7 @@ export class AdvancedCameraCardCarousel extends LitElement {
|
|||||||
loop: this.loop,
|
loop: this.loop,
|
||||||
plugins: this.plugins,
|
plugins: this.plugins,
|
||||||
textDirection: getTextDirection(this),
|
textDirection: getTextDirection(this),
|
||||||
|
wheelScrolling: this.wheelScrolling,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (changedProps.has('selected')) {
|
} else if (changedProps.has('selected')) {
|
||||||
|
|||||||
@@ -339,6 +339,7 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
|
|||||||
this._getPlugins.bind(this),
|
this._getPlugins.bind(this),
|
||||||
)}
|
)}
|
||||||
.selected=${this._getSelectedCameraIndex()}
|
.selected=${this._getSelectedCameraIndex()}
|
||||||
|
.wheelScrolling=${this.liveConfig?.controls.wheel}
|
||||||
transitionEffect=${this._getTransitionEffect()}
|
transitionEffect=${this._getTransitionEffect()}
|
||||||
@advanced-camera-card:carousel:select=${this._setViewHandler.bind(this)}
|
@advanced-camera-card:carousel:select=${this._setViewHandler.bind(this)}
|
||||||
@advanced-camera-card:media:loaded=${() => {
|
@advanced-camera-card:media:loaded=${() => {
|
||||||
|
|||||||
@@ -339,6 +339,7 @@ export class AdvancedCameraCardViewerCarousel extends LitElement {
|
|||||||
.dragEnabled=${this.viewerConfig?.draggable ?? true}
|
.dragEnabled=${this.viewerConfig?.draggable ?? true}
|
||||||
.plugins=${guard([this.viewerConfig, this._media], this._getPlugins.bind(this))}
|
.plugins=${guard([this.viewerConfig, this._media], this._getPlugins.bind(this))}
|
||||||
.selected=${this._selected}
|
.selected=${this._selected}
|
||||||
|
.wheelScrolling=${this.viewerConfig?.controls.wheel}
|
||||||
transitionEffect=${this._getTransitionEffect()}
|
transitionEffect=${this._getTransitionEffect()}
|
||||||
@advanced-camera-card:carousel:select=${(ev: CustomEvent<CarouselSelected>) => {
|
@advanced-camera-card:carousel:select=${(ev: CustomEvent<CarouselSelected>) => {
|
||||||
this._setViewSelectedIndex(ev.detail.index);
|
this._setViewSelectedIndex(ev.detail.index);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export const liveConfigDefault = {
|
|||||||
ptz: ptzControlsDefaults,
|
ptz: ptzControlsDefaults,
|
||||||
thumbnails: liveThumbnailControlsDefaults,
|
thumbnails: liveThumbnailControlsDefaults,
|
||||||
timeline: miniTimelineConfigDefault,
|
timeline: miniTimelineConfigDefault,
|
||||||
|
wheel: true,
|
||||||
},
|
},
|
||||||
microphone: {
|
microphone: {
|
||||||
...microphoneConfigDefault,
|
...microphoneConfigDefault,
|
||||||
@@ -121,6 +122,7 @@ export const liveConfigSchema = z
|
|||||||
liveConfigDefault.controls.thumbnails,
|
liveConfigDefault.controls.thumbnails,
|
||||||
),
|
),
|
||||||
timeline: miniTimelineConfigSchema.default(liveConfigDefault.controls.timeline),
|
timeline: miniTimelineConfigSchema.default(liveConfigDefault.controls.timeline),
|
||||||
|
wheel: z.boolean().default(liveConfigDefault.controls.wheel),
|
||||||
})
|
})
|
||||||
.default(liveConfigDefault.controls),
|
.default(liveConfigDefault.controls),
|
||||||
display: viewDisplaySchema,
|
display: viewDisplaySchema,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const viewerConfigDefault = {
|
|||||||
...ptzControlsDefaults,
|
...ptzControlsDefaults,
|
||||||
mode: 'off' as const,
|
mode: 'off' as const,
|
||||||
},
|
},
|
||||||
|
wheel: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ export const viewerConfigSchema = z
|
|||||||
timeline: miniTimelineConfigSchema.default(
|
timeline: miniTimelineConfigSchema.default(
|
||||||
viewerConfigDefault.controls.timeline,
|
viewerConfigDefault.controls.timeline,
|
||||||
),
|
),
|
||||||
|
wheel: z.boolean().default(viewerConfigDefault.controls.wheel),
|
||||||
})
|
})
|
||||||
.default(viewerConfigDefault.controls),
|
.default(viewerConfigDefault.controls),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -248,6 +248,8 @@ export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE =
|
|||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.style` as const;
|
`${CONF_MEDIA_VIEWER}.controls.timeline.style` as const;
|
||||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.window_seconds` as const;
|
`${CONF_MEDIA_VIEWER}.controls.timeline.window_seconds` as const;
|
||||||
|
export const CONF_MEDIA_VIEWER_CONTROLS_WHEEL =
|
||||||
|
`${CONF_MEDIA_VIEWER}.controls.wheel` as const;
|
||||||
export const CONF_MEDIA_VIEWER_ZOOMABLE = `${CONF_MEDIA_VIEWER}.zoomable` as const;
|
export const CONF_MEDIA_VIEWER_ZOOMABLE = `${CONF_MEDIA_VIEWER}.zoomable` as const;
|
||||||
|
|
||||||
const CONF_LIVE = 'live' as const;
|
const CONF_LIVE = 'live' as const;
|
||||||
@@ -271,6 +273,7 @@ export const CONF_LIVE_CONTROLS_PTZ_ORIENTATION =
|
|||||||
`${CONF_LIVE}.controls.ptz.orientation` as const;
|
`${CONF_LIVE}.controls.ptz.orientation` as const;
|
||||||
export const CONF_LIVE_CONTROLS_PTZ_POSITION =
|
export const CONF_LIVE_CONTROLS_PTZ_POSITION =
|
||||||
`${CONF_LIVE}.controls.ptz.position` as const;
|
`${CONF_LIVE}.controls.ptz.position` as const;
|
||||||
|
export const CONF_LIVE_CONTROLS_WHEEL = `${CONF_LIVE}.controls.wheel` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE =
|
||||||
`${CONF_LIVE}.controls.thumbnails.media_type` as const;
|
`${CONF_LIVE}.controls.thumbnails.media_type` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE =
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ import {
|
|||||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||||
|
CONF_LIVE_CONTROLS_WHEEL,
|
||||||
CONF_LIVE_DISPLAY_GRID_COLUMNS,
|
CONF_LIVE_DISPLAY_GRID_COLUMNS,
|
||||||
CONF_LIVE_DISPLAY_GRID_MAX_COLUMNS,
|
CONF_LIVE_DISPLAY_GRID_MAX_COLUMNS,
|
||||||
CONF_LIVE_DISPLAY_GRID_SELECTED_POSITION,
|
CONF_LIVE_DISPLAY_GRID_SELECTED_POSITION,
|
||||||
@@ -175,6 +176,7 @@ import {
|
|||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||||
|
CONF_MEDIA_VIEWER_CONTROLS_WHEEL,
|
||||||
CONF_MEDIA_VIEWER_DISPLAY_GRID_COLUMNS,
|
CONF_MEDIA_VIEWER_DISPLAY_GRID_COLUMNS,
|
||||||
CONF_MEDIA_VIEWER_DISPLAY_GRID_MAX_COLUMNS,
|
CONF_MEDIA_VIEWER_DISPLAY_GRID_MAX_COLUMNS,
|
||||||
CONF_MEDIA_VIEWER_DISPLAY_GRID_SELECTED_POSITION,
|
CONF_MEDIA_VIEWER_DISPLAY_GRID_SELECTED_POSITION,
|
||||||
@@ -2911,6 +2913,13 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
|||||||
label: localize('config.common.controls.builtin'),
|
label: localize('config.common.controls.builtin'),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
${this._renderSwitch(
|
||||||
|
CONF_LIVE_CONTROLS_WHEEL,
|
||||||
|
this._defaults.live.controls.wheel,
|
||||||
|
{
|
||||||
|
label: localize('config.common.controls.wheel'),
|
||||||
|
},
|
||||||
|
)}
|
||||||
${this._renderNextPreviousControls(
|
${this._renderNextPreviousControls(
|
||||||
MENU_LIVE_CONTROLS_NEXT_PREVIOUS,
|
MENU_LIVE_CONTROLS_NEXT_PREVIOUS,
|
||||||
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
|
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||||
@@ -3106,6 +3115,13 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
|||||||
label: localize('config.common.controls.builtin'),
|
label: localize('config.common.controls.builtin'),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
${this._renderSwitch(
|
||||||
|
CONF_MEDIA_VIEWER_CONTROLS_WHEEL,
|
||||||
|
this._defaults.media_viewer.controls.wheel,
|
||||||
|
{
|
||||||
|
label: localize('config.common.controls.wheel'),
|
||||||
|
},
|
||||||
|
)}
|
||||||
${this._renderNextPreviousControls(
|
${this._renderNextPreviousControls(
|
||||||
MENU_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS,
|
MENU_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
CONF_MEDIA_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "Cerca d'escombratge només a la càmera seleccionada",
|
"seek-in-camera": "Cerca d'escombratge només a la càmera seleccionada",
|
||||||
"seek-in-media": "Cerca d'escombratge només dins de l'element multimèdia seleccionat"
|
"seek-in-media": "Cerca d'escombratge només dins de l'element multimèdia seleccionat"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": ""
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "Visualització",
|
"editor_label": "Visualització",
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "Pan seeks within selected camera only",
|
"seek-in-camera": "Pan seeks within selected camera only",
|
||||||
"seek-in-media": "Pan seeks within selected media item only"
|
"seek-in-media": "Pan seeks within selected media item only"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": "Scroll wheel scrolls media carousel"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "Display",
|
"editor_label": "Display",
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "Pan ne cherche que parmi la caméra sélectionnée",
|
"seek-in-camera": "Pan ne cherche que parmi la caméra sélectionnée",
|
||||||
"seek-in-media": "Pan ne cherche que parmi les médias sélectionnés"
|
"seek-in-media": "Pan ne cherche que parmi les médias sélectionnés"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": ""
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "Affichage",
|
"editor_label": "Affichage",
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "",
|
"seek-in-camera": "",
|
||||||
"seek-in-media": ""
|
"seek-in-media": ""
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": ""
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "",
|
"editor_label": "",
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "",
|
"seek-in-camera": "",
|
||||||
"seek-in-media": ""
|
"seek-in-media": ""
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": ""
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "",
|
"editor_label": "",
|
||||||
|
|||||||
@@ -263,7 +263,8 @@
|
|||||||
"seek-in-camera": "",
|
"seek-in-camera": "",
|
||||||
"seek-in-media": ""
|
"seek-in-media": ""
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"wheel": ""
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"editor_label": "",
|
"editor_label": "",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export class CarouselController {
|
|||||||
protected _dragFree: boolean;
|
protected _dragFree: boolean;
|
||||||
protected _draggable: boolean;
|
protected _draggable: boolean;
|
||||||
protected _textDirection: TextDirection;
|
protected _textDirection: TextDirection;
|
||||||
|
protected _wheelScrolling: boolean;
|
||||||
|
|
||||||
protected _plugins: EmblaCarouselPlugins;
|
protected _plugins: EmblaCarouselPlugins;
|
||||||
protected _carousel: EmblaCarouselType;
|
protected _carousel: EmblaCarouselType;
|
||||||
@@ -48,6 +49,7 @@ export class CarouselController {
|
|||||||
dragFree?: boolean;
|
dragFree?: boolean;
|
||||||
plugins?: EmblaCarouselPlugins;
|
plugins?: EmblaCarouselPlugins;
|
||||||
textDirection?: TextDirection;
|
textDirection?: TextDirection;
|
||||||
|
wheelScrolling?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
this._root = root;
|
this._root = root;
|
||||||
@@ -60,6 +62,7 @@ export class CarouselController {
|
|||||||
this._draggable = options?.dragEnabled ?? true;
|
this._draggable = options?.dragEnabled ?? true;
|
||||||
this._plugins = options?.plugins ?? [];
|
this._plugins = options?.plugins ?? [];
|
||||||
this._textDirection = options?.textDirection ?? 'ltr';
|
this._textDirection = options?.textDirection ?? 'ltr';
|
||||||
|
this._wheelScrolling = options?.wheelScrolling ?? true;
|
||||||
|
|
||||||
this._carousel = this._createCarousel(getChildrenFromElement(this._parent));
|
this._carousel = this._createCarousel(getChildrenFromElement(this._parent));
|
||||||
|
|
||||||
@@ -144,7 +147,7 @@ export class CarouselController {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
...this._plugins,
|
...this._plugins,
|
||||||
...(slides.length > 1
|
...(slides.length > 1 && this._wheelScrolling
|
||||||
? [
|
? [
|
||||||
WheelGesturesPlugin({
|
WheelGesturesPlugin({
|
||||||
// Whether the carousel is vertical or horizontal, interpret y-axis wheel
|
// Whether the carousel is vertical or horizontal, interpret y-axis wheel
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ describe('config defaults', () => {
|
|||||||
style: 'ribbon',
|
style: 'ribbon',
|
||||||
window_seconds: 3600,
|
window_seconds: 3600,
|
||||||
},
|
},
|
||||||
|
wheel: true,
|
||||||
},
|
},
|
||||||
draggable: true,
|
draggable: true,
|
||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
@@ -180,6 +181,7 @@ describe('config defaults', () => {
|
|||||||
style: 'ribbon',
|
style: 'ribbon',
|
||||||
window_seconds: 3600,
|
window_seconds: 3600,
|
||||||
},
|
},
|
||||||
|
wheel: true,
|
||||||
},
|
},
|
||||||
draggable: true,
|
draggable: true,
|
||||||
lazy_load: true,
|
lazy_load: true,
|
||||||
|
|||||||
@@ -239,6 +239,24 @@ describe('CarouselController', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not include wheel plugin when wheelScrolling is false', () => {
|
||||||
|
const children = createTestSlideNodes();
|
||||||
|
const root = createRoot();
|
||||||
|
const parent = createParent({ children: children });
|
||||||
|
new CarouselController(root, parent, { wheelScrolling: false });
|
||||||
|
|
||||||
|
// Verify WheelGesturesPlugin is NOT present
|
||||||
|
expect(EmblaCarousel).toBeCalledWith(
|
||||||
|
root,
|
||||||
|
expect.anything(),
|
||||||
|
expect.not.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'wheelGestures',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should recreate carousel when children are added', () => {
|
it('should recreate carousel when children are added', () => {
|
||||||
const children = createTestSlideNodes();
|
const children = createTestSlideNodes();
|
||||||
const root = createRoot();
|
const root = createRoot();
|
||||||
|
|||||||
Reference in New Issue
Block a user