diff --git a/assets/app.css b/assets/app.css index 74d54ab..25bfd91 100755 --- a/assets/app.css +++ b/assets/app.css @@ -587,6 +587,8 @@ textarea { background: linear-gradient(180deg, rgba(29, 33, 41, 0.96), rgba(18, 20, 27, 0.94)); box-shadow: var(--shadow); min-height: 120px; + transition: transform 120ms ease, box-shadow 180ms ease, border-color 180ms ease, background 180ms ease, filter 180ms ease; + will-change: transform; } .grid-card.is-hidden { @@ -865,6 +867,26 @@ textarea { touch-action: manipulation; } +.grid-card--tap:active, +.grid-card.is-pressed, +.mushroom-button:active, +.mushroom-button.is-pressed, +.round-button:active, +.round-button.is-pressed, +.icon-button:active, +.icon-button.is-pressed, +.main-quick-action:active, +.main-quick-action.is-pressed, +.entity-chip:active, +.entity-chip.is-pressed, +.temperature-sensor-modal__option:active, +.temperature-sensor-modal__option.is-pressed, +.room-item:active, +.room-item.is-pressed { + transform: translateY(1px) scale(0.985); + filter: brightness(1.06); +} + .grid-card--tap:focus-visible, .room-item:focus-visible, .icon-button:focus-visible, @@ -942,6 +964,7 @@ textarea { justify-content: center; gap: 10px; transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .mushroom-button:hover { @@ -1125,6 +1148,8 @@ textarea { align-items: center; justify-content: center; cursor: pointer; + transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .cover-card { @@ -1432,6 +1457,7 @@ body.is-mobile-ui #camera-modal { cursor: pointer; text-align: left; transition: transform 180ms ease, background 180ms ease, border-color 180ms ease; + will-change: transform; } .temperature-sensor-modal__option:hover { @@ -1718,6 +1744,8 @@ body.is-mobile-ui #camera-modal { cursor: pointer; text-align: center; line-height: 1.05; + transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .entity-chip.is-active { @@ -1906,6 +1934,7 @@ body.is-mobile-ui #camera-modal { transition: transform 180ms ease, box-shadow 180ms ease, filter 180ms ease; position: relative; overflow: hidden; + will-change: transform; } .main-quick-action:hover { diff --git a/assets/app.js b/assets/app.js index 7538504..226b552 100755 --- a/assets/app.js +++ b/assets/app.js @@ -58,6 +58,54 @@ return Array.from(root.querySelectorAll(sel)); } + const PRESSABLE_SELECTOR = [ + '.grid-card--tap', + '.mushroom-button', + '.round-button', + '.icon-button', + '.main-quick-action', + '.entity-chip', + '.temperature-sensor-modal__option', + '.room-item', + ].join(', '); + + function bindPressFeedback() { + let pressedEl = null; + let releaseTimer = null; + + const clearPressed = () => { + if (releaseTimer !== null) { + window.clearTimeout(releaseTimer); + releaseTimer = null; + } + if (pressedEl) { + pressedEl.classList.remove('is-pressed'); + pressedEl = null; + } + }; + + const setPressed = (el) => { + if (!el || el.classList.contains('is-disabled')) { + return; + } + clearPressed(); + pressedEl = el; + el.classList.add('is-pressed'); + releaseTimer = window.setTimeout(clearPressed, 160); + }; + + document.addEventListener('pointerdown', (event) => { + if (event.button !== undefined && event.button !== 0) return; + const target = event.target instanceof Element ? event.target.closest(PRESSABLE_SELECTOR) : null; + if (!target) return; + setPressed(target); + }, { passive: true }); + + document.addEventListener('pointerup', clearPressed, { passive: true }); + document.addEventListener('pointercancel', clearPressed, { passive: true }); + window.addEventListener('blur', clearPressed); + } + function mobileViewportQuery() { return window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`); } @@ -4524,6 +4572,7 @@ state.embedMode = detectEmbeddedContext(); syncLayoutState(); syncViewportState(); + bindPressFeedback(); updateClock(); clearInterval(state.clockTimer); state.clockTimer = setInterval(updateClock, 1000); diff --git a/wall_panel/assets/app.css b/wall_panel/assets/app.css index 74d54ab..25bfd91 100755 --- a/wall_panel/assets/app.css +++ b/wall_panel/assets/app.css @@ -587,6 +587,8 @@ textarea { background: linear-gradient(180deg, rgba(29, 33, 41, 0.96), rgba(18, 20, 27, 0.94)); box-shadow: var(--shadow); min-height: 120px; + transition: transform 120ms ease, box-shadow 180ms ease, border-color 180ms ease, background 180ms ease, filter 180ms ease; + will-change: transform; } .grid-card.is-hidden { @@ -865,6 +867,26 @@ textarea { touch-action: manipulation; } +.grid-card--tap:active, +.grid-card.is-pressed, +.mushroom-button:active, +.mushroom-button.is-pressed, +.round-button:active, +.round-button.is-pressed, +.icon-button:active, +.icon-button.is-pressed, +.main-quick-action:active, +.main-quick-action.is-pressed, +.entity-chip:active, +.entity-chip.is-pressed, +.temperature-sensor-modal__option:active, +.temperature-sensor-modal__option.is-pressed, +.room-item:active, +.room-item.is-pressed { + transform: translateY(1px) scale(0.985); + filter: brightness(1.06); +} + .grid-card--tap:focus-visible, .room-item:focus-visible, .icon-button:focus-visible, @@ -942,6 +964,7 @@ textarea { justify-content: center; gap: 10px; transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .mushroom-button:hover { @@ -1125,6 +1148,8 @@ textarea { align-items: center; justify-content: center; cursor: pointer; + transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .cover-card { @@ -1432,6 +1457,7 @@ body.is-mobile-ui #camera-modal { cursor: pointer; text-align: left; transition: transform 180ms ease, background 180ms ease, border-color 180ms ease; + will-change: transform; } .temperature-sensor-modal__option:hover { @@ -1718,6 +1744,8 @@ body.is-mobile-ui #camera-modal { cursor: pointer; text-align: center; line-height: 1.05; + transition: transform 180ms ease, background 180ms ease, border-color 180ms ease, color 180ms ease; + will-change: transform; } .entity-chip.is-active { @@ -1906,6 +1934,7 @@ body.is-mobile-ui #camera-modal { transition: transform 180ms ease, box-shadow 180ms ease, filter 180ms ease; position: relative; overflow: hidden; + will-change: transform; } .main-quick-action:hover { diff --git a/wall_panel/assets/app.js b/wall_panel/assets/app.js index 7538504..226b552 100755 --- a/wall_panel/assets/app.js +++ b/wall_panel/assets/app.js @@ -58,6 +58,54 @@ return Array.from(root.querySelectorAll(sel)); } + const PRESSABLE_SELECTOR = [ + '.grid-card--tap', + '.mushroom-button', + '.round-button', + '.icon-button', + '.main-quick-action', + '.entity-chip', + '.temperature-sensor-modal__option', + '.room-item', + ].join(', '); + + function bindPressFeedback() { + let pressedEl = null; + let releaseTimer = null; + + const clearPressed = () => { + if (releaseTimer !== null) { + window.clearTimeout(releaseTimer); + releaseTimer = null; + } + if (pressedEl) { + pressedEl.classList.remove('is-pressed'); + pressedEl = null; + } + }; + + const setPressed = (el) => { + if (!el || el.classList.contains('is-disabled')) { + return; + } + clearPressed(); + pressedEl = el; + el.classList.add('is-pressed'); + releaseTimer = window.setTimeout(clearPressed, 160); + }; + + document.addEventListener('pointerdown', (event) => { + if (event.button !== undefined && event.button !== 0) return; + const target = event.target instanceof Element ? event.target.closest(PRESSABLE_SELECTOR) : null; + if (!target) return; + setPressed(target); + }, { passive: true }); + + document.addEventListener('pointerup', clearPressed, { passive: true }); + document.addEventListener('pointercancel', clearPressed, { passive: true }); + window.addEventListener('blur', clearPressed); + } + function mobileViewportQuery() { return window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`); } @@ -4524,6 +4572,7 @@ state.embedMode = detectEmbeddedContext(); syncLayoutState(); syncViewportState(); + bindPressFeedback(); updateClock(); clearInterval(state.clockTimer); state.clockTimer = setInterval(updateClock, 1000); diff --git a/wall_panel/config.yaml b/wall_panel/config.yaml index 468e028..02bfa07 100755 --- a/wall_panel/config.yaml +++ b/wall_panel/config.yaml @@ -1,6 +1,6 @@ name: Wall Panel description: Wall Panel PHP interface as a Home Assistant add-on -version: "1.0.7" +version: "1.0.8" slug: wall_panel url: https://git.striker72rus.ru/PHP/wallpanell.git init: false