mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-30 19:40:29 +00:00
Compare commits
22 Commits
dev
...
20250829.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c33618e05 | ||
![]() |
3837b3e630 | ||
![]() |
7c15633f6d | ||
![]() |
f7ec8650eb | ||
![]() |
7674eee0fb | ||
![]() |
f494a6453a | ||
![]() |
37f3682ffa | ||
![]() |
8055286a1f | ||
![]() |
0bdd213761 | ||
![]() |
810b43760e | ||
![]() |
424d71c55a | ||
![]() |
176924241c | ||
![]() |
da08aa7fb0 | ||
![]() |
6047227648 | ||
![]() |
fc71fd6bc3 | ||
![]() |
90a1b135e1 | ||
![]() |
e19413b6ca | ||
![]() |
0dfc10af5f | ||
![]() |
bbbc419bea | ||
![]() |
50ad5e376f | ||
![]() |
a9f2254bbc | ||
![]() |
a8836404d4 |
@@ -0,0 +1,76 @@
|
||||
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4744_40067)">
|
||||
<path d="M0 6C0 2.68629 2.68629 0 6 0H28C31.3137 0 34 2.68629 34 6C34 9.31371 31.3137 12 28 12H6C2.68629 12 0 9.31371 0 6Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M0 28C0 23.5817 3.58172 20 8 20H42.6667C47.0849 20 50.6667 23.5817 50.6667 28V36C50.6667 40.4183 47.0849 44 42.6667 44H8.00001C3.58173 44 0 40.4183 0 36V28Z" fill="#1C1C1C"/>
|
||||
<path d="M8 20.5H42.667C46.809 20.5002 50.167 23.858 50.167 28V36C50.167 40.142 46.809 43.4998 42.667 43.5H8C3.85787 43.5 0.5 40.1421 0.5 36V28C0.5 23.8579 3.85786 20.5 8 20.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<path d="M6 32C6 28.6863 8.68629 26 12 26C15.3137 26 18 28.6863 18 32C18 35.3137 15.3137 38 12 38C8.68629 38 6 35.3137 6 32Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M24 31C24 29.3431 25.3431 28 27 28H39.6667C41.3235 28 42.6667 29.3431 42.6667 31V33C42.6667 34.6569 41.3235 36 39.6667 36H27C25.3431 36 24 34.6569 24 33V31Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M54.6666 28C54.6666 23.5817 58.2483 20 62.6666 20H97.3333C101.752 20 105.333 23.5817 105.333 28V36C105.333 40.4183 101.752 44 97.3333 44H62.6666C58.2484 44 54.6666 40.4183 54.6666 36V28Z" fill="#1C1C1C"/>
|
||||
<path d="M62.6666 20.5H97.3336C101.476 20.5002 104.834 23.858 104.834 28V36C104.834 40.142 101.476 43.4998 97.3336 43.5H62.6666C58.5245 43.5 55.1666 40.1421 55.1666 36V28C55.1666 23.8579 58.5245 20.5 62.6666 20.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<path d="M60.6666 32C60.6666 28.6863 63.3529 26 66.6666 26C69.9803 26 72.6666 28.6863 72.6666 32C72.6666 35.3137 69.9803 38 66.6666 38C63.3529 38 60.6666 35.3137 60.6666 32Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M78.6666 31C78.6666 29.3431 80.0098 28 81.6666 28H94.3333C95.9901 28 97.3333 29.3431 97.3333 31V33C97.3333 34.6569 95.9901 36 94.3333 36H81.6666C80.0098 36 78.6666 34.6569 78.6666 33V31Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M109.333 28C109.333 23.5817 112.915 20 117.333 20H152C156.418 20 160 23.5817 160 28V36C160 40.4183 156.418 44 152 44H117.333C112.915 44 109.333 40.4183 109.333 36V28Z" fill="#1C1C1C"/>
|
||||
<path d="M117.333 20.5H152C156.142 20.5002 159.5 23.858 159.5 28V36C159.5 40.142 156.142 43.4998 152 43.5H117.333C113.191 43.5 109.833 40.1421 109.833 36V28C109.833 23.8579 113.191 20.5 117.333 20.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<path d="M115.333 32C115.333 28.6863 118.02 26 121.333 26C124.647 26 127.333 28.6863 127.333 32C127.333 35.3137 124.647 38 121.333 38C118.02 38 115.333 35.3137 115.333 32Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M133.333 31C133.333 29.3431 134.677 28 136.333 28H149C150.657 28 152 29.3431 152 31V33C152 34.6569 150.657 36 149 36H136.333C134.677 36 133.333 34.6569 133.333 33V31Z" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M0 56C0 53.7909 1.79086 52 4 52H29C31.2091 52 33 53.7909 33 56C33 58.2091 31.2091 60 29 60H4C1.79086 60 0 58.2091 0 56Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M0 72C0 67.5817 3.58172 64 8 64H29C33.4183 64 37 67.5817 37 72V96C37 100.418 33.4183 104 29 104H8C3.58172 104 0 100.418 0 96V72Z" fill="#1C1C1C"/>
|
||||
<path d="M8 64.5H29C33.1421 64.5 36.5 67.8579 36.5 72V96C36.5 100.142 33.1421 103.5 29 103.5H8C3.85786 103.5 0.5 100.142 0.5 96V72C0.5 67.8579 3.85786 64.5 8 64.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<mask id="mask0_4744_40067" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="6" y="72" width="25" height="24">
|
||||
<path d="M18.5 74C16.6435 74 14.863 74.7375 13.5503 76.0503C12.2375 77.363 11.5 79.1435 11.5 81C11.5 83.38 12.69 85.47 14.5 86.74V89C14.5 89.2652 14.6054 89.5196 14.7929 89.7071C14.9804 89.8946 15.2348 90 15.5 90H21.5C21.7652 90 22.0196 89.8946 22.2071 89.7071C22.3946 89.5196 22.5 89.2652 22.5 89V86.74C24.31 85.47 25.5 83.38 25.5 81C25.5 79.1435 24.7625 77.363 23.4497 76.0503C22.137 74.7375 20.3565 74 18.5 74ZM15.5 93C15.5 93.2652 15.6054 93.5196 15.7929 93.7071C15.9804 93.8946 16.2348 94 16.5 94H20.5C20.7652 94 21.0196 93.8946 21.2071 93.7071C21.3946 93.5196 21.5 93.2652 21.5 93V92H15.5V93Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4744_40067)">
|
||||
<rect x="6.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M41 72C41 67.5817 44.5817 64 49 64H70C74.4183 64 78 67.5817 78 72V96C78 100.418 74.4183 104 70 104H49C44.5817 104 41 100.418 41 96V72Z" fill="#1C1C1C"/>
|
||||
<path d="M49 64.5H70C74.1421 64.5 77.5 67.8579 77.5 72V96C77.5 100.142 74.1421 103.5 70 103.5H49C44.8579 103.5 41.5 100.142 41.5 96V72C41.5 67.8579 44.8579 64.5 49 64.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<mask id="mask1_4744_40067" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="47" y="72" width="25" height="24">
|
||||
<path d="M66.5 80C67.61 80 68.5 80.9 68.5 82V88.76C69.11 89.31 69.5 90.11 69.5 91C69.5 92.66 68.16 94 66.5 94C64.84 94 63.5 92.66 63.5 91C63.5 90.11 63.89 89.31 64.5 88.76V82C64.5 80.9 65.4 80 66.5 80ZM66.5 81C65.95 81 65.5 81.45 65.5 82V83H67.5V82C67.5 81.45 67.05 81 66.5 81ZM52.5 92V84H49.5L59.5 75L63.9 78.96C63.04 79.69 62.5 80.78 62.5 82V88C61.87 88.83 61.5 89.87 61.5 91L61.6 92H52.5Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_4744_40067)">
|
||||
<rect x="47.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M82 72C82 67.5817 85.5817 64 90 64H111C115.418 64 119 67.5817 119 72V96C119 100.418 115.418 104 111 104H90C85.5817 104 82 100.418 82 96V72Z" fill="#1C1C1C"/>
|
||||
<path d="M90 64.5H111C115.142 64.5 118.5 67.8579 118.5 72V96C118.5 100.142 115.142 103.5 111 103.5H90C85.8579 103.5 82.5 100.142 82.5 96V72C82.5 67.8579 85.8579 64.5 90 64.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<mask id="mask2_4744_40067" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="88" y="72" width="25" height="24">
|
||||
<path d="M100.5 84H107.5C106.97 88.11 104.22 91.78 100.5 92.92V84H93.5V78.3L100.5 75.19M100.5 73L91.5 77V83C91.5 88.55 95.34 93.73 100.5 95C105.66 93.73 109.5 88.55 109.5 83V77L100.5 73Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_4744_40067)">
|
||||
<rect x="88.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M123 72C123 67.5817 126.582 64 131 64H152C156.418 64 160 67.5817 160 72V96C160 100.418 156.418 104 152 104H131C126.582 104 123 100.418 123 96V72Z" fill="#1C1C1C"/>
|
||||
<path d="M131 64.5H152C156.142 64.5 159.5 67.8579 159.5 72V96C159.5 100.142 156.142 103.5 152 103.5H131C126.858 103.5 123.5 100.142 123.5 96V72C123.5 67.8579 126.858 64.5 131 64.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<mask id="mask3_4744_40067" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="129" y="72" width="25" height="24">
|
||||
<path d="M145.5 84C145.5 83.4696 145.711 82.9609 146.086 82.5858C146.461 82.2107 146.97 82 147.5 82C148.03 82 148.539 82.2107 148.914 82.5858C149.289 82.9609 149.5 83.4696 149.5 84C149.5 84.5304 149.289 85.0391 148.914 85.4142C148.539 85.7893 148.03 86 147.5 86C146.97 86 146.461 85.7893 146.086 85.4142C145.711 85.0391 145.5 84.5304 145.5 84ZM139.5 84C139.5 83.4696 139.711 82.9609 140.086 82.5858C140.461 82.2107 140.97 82 141.5 82C142.03 82 142.539 82.2107 142.914 82.5858C143.289 82.9609 143.5 83.4696 143.5 84C143.5 84.5304 143.289 85.0391 142.914 85.4142C142.539 85.7893 142.03 86 141.5 86C140.97 86 140.461 85.7893 140.086 85.4142C139.711 85.0391 139.5 84.5304 139.5 84ZM133.5 84C133.5 83.4696 133.711 82.9609 134.086 82.5858C134.461 82.2107 134.97 82 135.5 82C136.03 82 136.539 82.2107 136.914 82.5858C137.289 82.9609 137.5 83.4696 137.5 84C137.5 84.5304 137.289 85.0391 136.914 85.4142C136.539 85.7893 136.03 86 135.5 86C134.97 86 134.461 85.7893 134.086 85.4142C133.711 85.0391 133.5 84.5304 133.5 84Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask3_4744_40067)">
|
||||
<rect x="129.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M0 116C0 113.791 1.79086 112 4 112H29C31.2091 112 33 113.791 33 116C33 118.209 31.2091 120 29 120H4C1.79086 120 0 118.209 0 116Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M0 132C0 127.582 3.58172 124 8 124H70C74.4183 124 78 127.582 78 132V160H0V132Z" fill="url(#paint0_linear_4744_40067)"/>
|
||||
<path d="M8 124.5H70C74.1421 124.5 77.5 127.858 77.5 132V159.5H0.5V132C0.5 127.858 3.85786 124.5 8 124.5Z" stroke="url(#paint1_linear_4744_40067)" stroke-opacity="0.12"/>
|
||||
<path d="M82 132C82 127.582 85.5817 124 90 124H152C156.418 124 160 127.582 160 132V160H82V132Z" fill="url(#paint2_linear_4744_40067)"/>
|
||||
<path d="M90 124.5H152C156.142 124.5 159.5 127.858 159.5 132V159.5H82.5V132C82.5 127.858 85.8579 124.5 90 124.5Z" stroke="url(#paint3_linear_4744_40067)" stroke-opacity="0.12"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4744_40067" x1="39" y1="124" x2="39" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="#1C1C1C"/>
|
||||
<stop offset="1" stop-color="#1C1C1C" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_4744_40067" x1="39" y1="124" x2="39" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="white" stop-opacity="0.24"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_4744_40067" x1="121" y1="124" x2="121" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="#1C1C1C"/>
|
||||
<stop offset="1" stop-color="#1C1C1C" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_4744_40067" x1="121" y1="124" x2="121" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="white" stop-opacity="0.24"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_4744_40067">
|
||||
<rect width="160" height="160" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,76 @@
|
||||
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4744_39984)">
|
||||
<path d="M0 6C0 2.68629 2.68629 0 6 0H28C31.3137 0 34 2.68629 34 6C34 9.31371 31.3137 12 28 12H6C2.68629 12 0 9.31371 0 6Z" fill="black" fill-opacity="0.32"/>
|
||||
<path d="M0 28C0 23.5817 3.58172 20 8 20H42.6667C47.0849 20 50.6667 23.5817 50.6667 28V36C50.6667 40.4183 47.0849 44 42.6667 44H8.00001C3.58173 44 0 40.4183 0 36V28Z" fill="white"/>
|
||||
<path d="M8 20.5H42.667C46.809 20.5002 50.167 23.858 50.167 28V36C50.167 40.142 46.809 43.4998 42.667 43.5H8C3.85787 43.5 0.5 40.1421 0.5 36V28C0.5 23.8579 3.85786 20.5 8 20.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="6" y="26" width="12" height="12" rx="6" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M24 31C24 29.3431 25.3431 28 27 28H39.6667C41.3235 28 42.6667 29.3431 42.6667 31V33C42.6667 34.6569 41.3235 36 39.6667 36H27C25.3431 36 24 34.6569 24 33V31Z" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M54.6667 28C54.6667 23.5817 58.2484 20 62.6667 20H97.3333C101.752 20 105.333 23.5817 105.333 28V36C105.333 40.4183 101.752 44 97.3334 44H62.6667C58.2484 44 54.6667 40.4183 54.6667 36V28Z" fill="white"/>
|
||||
<path d="M62.6667 20.5H97.3337C101.476 20.5002 104.834 23.858 104.834 28V36C104.834 40.142 101.476 43.4998 97.3337 43.5H62.6667C58.5246 43.5 55.1667 40.1421 55.1667 36V28C55.1667 23.8579 58.5246 20.5 62.6667 20.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="60.6667" y="26" width="12" height="12" rx="6" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M78.6667 31C78.6667 29.3431 80.0098 28 81.6667 28H94.3334C95.9902 28 97.3334 29.3431 97.3334 31V33C97.3334 34.6569 95.9902 36 94.3334 36H81.6667C80.0098 36 78.6667 34.6569 78.6667 33V31Z" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M109.333 28C109.333 23.5817 112.915 20 117.333 20H152C156.418 20 160 23.5817 160 28V36C160 40.4183 156.418 44 152 44H117.333C112.915 44 109.333 40.4183 109.333 36V28Z" fill="white"/>
|
||||
<path d="M117.333 20.5H152C156.142 20.5002 159.5 23.858 159.5 28V36C159.5 40.142 156.142 43.4998 152 43.5H117.333C113.191 43.5 109.833 40.1421 109.833 36V28C109.833 23.8579 113.191 20.5 117.333 20.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="115.333" y="26" width="12" height="12" rx="6" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M133.333 31C133.333 29.3431 134.676 28 136.333 28H149C150.657 28 152 29.3431 152 31V33C152 34.6569 150.657 36 149 36H136.333C134.676 36 133.333 34.6569 133.333 33V31Z" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M0 56C0 53.7909 1.79086 52 4 52H29C31.2091 52 33 53.7909 33 56C33 58.2091 31.2091 60 29 60H4C1.79086 60 0 58.2091 0 56Z" fill="black" fill-opacity="0.32"/>
|
||||
<path d="M0 72C0 67.5817 3.58172 64 8 64H29C33.4183 64 37 67.5817 37 72V96C37 100.418 33.4183 104 29 104H8C3.58172 104 0 100.418 0 96V72Z" fill="white"/>
|
||||
<path d="M8 64.5H29C33.1421 64.5 36.5 67.8579 36.5 72V96C36.5 100.142 33.1421 103.5 29 103.5H8C3.85786 103.5 0.5 100.142 0.5 96V72C0.5 67.8579 3.85786 64.5 8 64.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<mask id="mask0_4744_39984" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="6" y="72" width="25" height="24">
|
||||
<path d="M18.5 74C16.6435 74 14.863 74.7375 13.5503 76.0503C12.2375 77.363 11.5 79.1435 11.5 81C11.5 83.38 12.69 85.47 14.5 86.74V89C14.5 89.2652 14.6054 89.5196 14.7929 89.7071C14.9804 89.8946 15.2348 90 15.5 90H21.5C21.7652 90 22.0196 89.8946 22.2071 89.7071C22.3946 89.5196 22.5 89.2652 22.5 89V86.74C24.31 85.47 25.5 83.38 25.5 81C25.5 79.1435 24.7625 77.363 23.4497 76.0503C22.137 74.7375 20.3565 74 18.5 74ZM15.5 93C15.5 93.2652 15.6054 93.5196 15.7929 93.7071C15.9804 93.8946 16.2348 94 16.5 94H20.5C20.7652 94 21.0196 93.8946 21.2071 93.7071C21.3946 93.5196 21.5 93.2652 21.5 93V92H15.5V93Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4744_39984)">
|
||||
<rect x="6.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M41 72C41 67.5817 44.5817 64 49 64H70C74.4183 64 78 67.5817 78 72V96C78 100.418 74.4183 104 70 104H49C44.5817 104 41 100.418 41 96V72Z" fill="white"/>
|
||||
<path d="M49 64.5H70C74.1421 64.5 77.5 67.8579 77.5 72V96C77.5 100.142 74.1421 103.5 70 103.5H49C44.8579 103.5 41.5 100.142 41.5 96V72C41.5 67.8579 44.8579 64.5 49 64.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<mask id="mask1_4744_39984" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="47" y="72" width="25" height="24">
|
||||
<path d="M66.5 80C67.61 80 68.5 80.9 68.5 82V88.76C69.11 89.31 69.5 90.11 69.5 91C69.5 92.66 68.16 94 66.5 94C64.84 94 63.5 92.66 63.5 91C63.5 90.11 63.89 89.31 64.5 88.76V82C64.5 80.9 65.4 80 66.5 80ZM66.5 81C65.95 81 65.5 81.45 65.5 82V83H67.5V82C67.5 81.45 67.05 81 66.5 81ZM52.5 92V84H49.5L59.5 75L63.9 78.96C63.04 79.69 62.5 80.78 62.5 82V88C61.87 88.83 61.5 89.87 61.5 91L61.6 92H52.5Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_4744_39984)">
|
||||
<rect x="47.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M82 72C82 67.5817 85.5817 64 90 64H111C115.418 64 119 67.5817 119 72V96C119 100.418 115.418 104 111 104H90C85.5817 104 82 100.418 82 96V72Z" fill="white"/>
|
||||
<path d="M90 64.5H111C115.142 64.5 118.5 67.8579 118.5 72V96C118.5 100.142 115.142 103.5 111 103.5H90C85.8579 103.5 82.5 100.142 82.5 96V72C82.5 67.8579 85.8579 64.5 90 64.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<mask id="mask2_4744_39984" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="88" y="72" width="25" height="24">
|
||||
<path d="M100.5 84H107.5C106.97 88.11 104.22 91.78 100.5 92.92V84H93.5V78.3L100.5 75.19M100.5 73L91.5 77V83C91.5 88.55 95.34 93.73 100.5 95C105.66 93.73 109.5 88.55 109.5 83V77L100.5 73Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_4744_39984)">
|
||||
<rect x="88.5" y="72" width="24" height="24" fill="#03A9F4"/>
|
||||
</g>
|
||||
<path d="M123 72C123 67.5817 126.582 64 131 64H152C156.418 64 160 67.5817 160 72V96C160 100.418 156.418 104 152 104H131C126.582 104 123 100.418 123 96V72Z" fill="white"/>
|
||||
<path d="M131 64.5H152C156.142 64.5 159.5 67.8579 159.5 72V96C159.5 100.142 156.142 103.5 152 103.5H131C126.858 103.5 123.5 100.142 123.5 96V72C123.5 67.8579 126.858 64.5 131 64.5Z" stroke="black" stroke-opacity="0.12"/>
|
||||
<mask id="mask3_4744_39984" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="129" y="72" width="25" height="24">
|
||||
<path d="M145.5 84C145.5 83.4696 145.711 82.9609 146.086 82.5858C146.461 82.2107 146.97 82 147.5 82C148.03 82 148.539 82.2107 148.914 82.5858C149.289 82.9609 149.5 83.4696 149.5 84C149.5 84.5304 149.289 85.0391 148.914 85.4142C148.539 85.7893 148.03 86 147.5 86C146.97 86 146.461 85.7893 146.086 85.4142C145.711 85.0391 145.5 84.5304 145.5 84ZM139.5 84C139.5 83.4696 139.711 82.9609 140.086 82.5858C140.461 82.2107 140.97 82 141.5 82C142.03 82 142.539 82.2107 142.914 82.5858C143.289 82.9609 143.5 83.4696 143.5 84C143.5 84.5304 143.289 85.0391 142.914 85.4142C142.539 85.7893 142.03 86 141.5 86C140.97 86 140.461 85.7893 140.086 85.4142C139.711 85.0391 139.5 84.5304 139.5 84ZM133.5 84C133.5 83.4696 133.711 82.9609 134.086 82.5858C134.461 82.2107 134.97 82 135.5 82C136.03 82 136.539 82.2107 136.914 82.5858C137.289 82.9609 137.5 83.4696 137.5 84C137.5 84.5304 137.289 85.0391 136.914 85.4142C136.539 85.7893 136.03 86 135.5 86C134.97 86 134.461 85.7893 134.086 85.4142C133.711 85.0391 133.5 84.5304 133.5 84Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask3_4744_39984)">
|
||||
<rect x="129.5" y="72" width="24" height="24" fill="#18BCF2"/>
|
||||
</g>
|
||||
<path d="M0 116C0 113.791 1.79086 112 4 112H29C31.2091 112 33 113.791 33 116C33 118.209 31.2091 120 29 120H4C1.79086 120 0 118.209 0 116Z" fill="black" fill-opacity="0.32"/>
|
||||
<path d="M0 132C0 127.582 3.58172 124 8 124H70C74.4183 124 78 127.582 78 132V160H0V132Z" fill="url(#paint0_linear_4744_39984)"/>
|
||||
<path d="M8 124.5H70C74.1421 124.5 77.5 127.858 77.5 132V159.5H0.5V132C0.5 127.858 3.85786 124.5 8 124.5Z" stroke="url(#paint1_linear_4744_39984)" stroke-opacity="0.12"/>
|
||||
<path d="M82 132C82 127.582 85.5817 124 90 124H152C156.418 124 160 127.582 160 132V160H82V132Z" fill="url(#paint2_linear_4744_39984)"/>
|
||||
<path d="M90 124.5H152C156.142 124.5 159.5 127.858 159.5 132V159.5H82.5V132C82.5 127.858 85.8579 124.5 90 124.5Z" stroke="url(#paint3_linear_4744_39984)" stroke-opacity="0.12"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4744_39984" x1="39" y1="124" x2="39" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_4744_39984" x1="39" y1="124" x2="39" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-opacity="0.12"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_4744_39984" x1="121" y1="124" x2="121" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_4744_39984" x1="121" y1="124" x2="121" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.5" stop-opacity="0.12"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_4744_39984">
|
||||
<rect width="160" height="160" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 8.9 KiB |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20250827.0"
|
||||
version = "20250829.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
@@ -119,10 +119,11 @@ class HaAlert extends LitElement {
|
||||
.main-content {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
line-height: normal;
|
||||
margin-left: 8px;
|
||||
margin-right: 0;
|
||||
margin-inline-start: 8px;
|
||||
margin-inline-end: 0;
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
.title {
|
||||
margin-top: 2px;
|
||||
|
@@ -3,11 +3,15 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
||||
import { STATE_ATTRIBUTES } from "../data/entity_attributes";
|
||||
import {
|
||||
STATE_ATTRIBUTES,
|
||||
STATE_ATTRIBUTES_DOMAIN_CLASS,
|
||||
} from "../data/entity_attributes";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-attribute-value";
|
||||
import "./ha-expansion-panel";
|
||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||
|
||||
@customElement("ha-attributes")
|
||||
class HaAttributes extends LitElement {
|
||||
@@ -22,7 +26,12 @@ class HaAttributes extends LitElement {
|
||||
private get _filteredAttributes() {
|
||||
return this._computeDisplayAttributes(
|
||||
STATE_ATTRIBUTES.concat(
|
||||
this.extraFilters ? this.extraFilters.split(",") : []
|
||||
this.extraFilters ? this.extraFilters.split(",") : [],
|
||||
(this.stateObj &&
|
||||
STATE_ATTRIBUTES_DOMAIN_CLASS[computeStateDomain(this.stateObj)]?.[
|
||||
this.stateObj.attributes?.device_class
|
||||
]) ||
|
||||
[]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { mdiChevronUp } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "./ha-icon-button";
|
||||
|
||||
@@ -16,12 +16,18 @@ export class HaAutomationRow extends LitElement {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public selected = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: "sort-selected" })
|
||||
public sortSelected = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public disabled = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: "building-block" })
|
||||
public buildingBlock = false;
|
||||
|
||||
@query(".row")
|
||||
private _rowElement?: HTMLDivElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div
|
||||
@@ -66,15 +72,42 @@ export class HaAutomationRow extends LitElement {
|
||||
if (ev.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
if (ev.key !== "Enter" && ev.key !== " ") {
|
||||
|
||||
if (
|
||||
ev.key !== "Enter" &&
|
||||
ev.key !== " " &&
|
||||
!(
|
||||
(this.sortSelected || ev.altKey) &&
|
||||
(ev.key === "ArrowUp" || ev.key === "ArrowDown")
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (ev.key === "ArrowUp" || ev.key === "ArrowDown") {
|
||||
if (ev.key === "ArrowUp") {
|
||||
fireEvent(this, "move-up");
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "move-down");
|
||||
return;
|
||||
}
|
||||
if (this.sortSelected && (ev.key === "Enter" || ev.key === " ")) {
|
||||
fireEvent(this, "stop-sort-selection");
|
||||
return;
|
||||
}
|
||||
|
||||
this.click();
|
||||
}
|
||||
|
||||
public focus() {
|
||||
requestAnimationFrame(() => {
|
||||
this._rowElement?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
@@ -134,6 +167,11 @@ export class HaAutomationRow extends LitElement {
|
||||
overflow-wrap: anywhere;
|
||||
margin: 0 12px;
|
||||
}
|
||||
:host([sort-selected]) .row {
|
||||
box-shadow:
|
||||
0px 0px 8px 4px rgba(var(--rgb-accent-color), 0.8),
|
||||
inset 0px 2px 8px 4px rgba(var(--rgb-accent-color), 0.4);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -144,5 +182,6 @@ declare global {
|
||||
|
||||
interface HASSDomEvents {
|
||||
"toggle-collapsed": undefined;
|
||||
"stop-sort-selection": undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -197,6 +197,7 @@ export class HaBottomSheet extends LitElement {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 7;
|
||||
padding-bottom: 76px;
|
||||
}
|
||||
.handle-wrapper .handle::after {
|
||||
content: "";
|
||||
|
@@ -16,9 +16,23 @@ export class HaMdButtonMenu extends LitElement {
|
||||
|
||||
@property() public positioning?: "fixed" | "absolute" | "popover";
|
||||
|
||||
@property({ attribute: "anchor-corner" }) public anchorCorner:
|
||||
| "start-start"
|
||||
| "start-end"
|
||||
| "end-start"
|
||||
| "end-end" = "end-start";
|
||||
|
||||
@property({ attribute: "menu-corner" }) public menuCorner:
|
||||
| "start-start"
|
||||
| "start-end"
|
||||
| "end-start"
|
||||
| "end-end" = "start-start";
|
||||
|
||||
@property({ type: Boolean, attribute: "has-overflow" }) public hasOverflow =
|
||||
false;
|
||||
|
||||
@property({ type: Boolean }) public quick = false;
|
||||
|
||||
@query("ha-md-menu", true) private _menu!: HaMdMenu;
|
||||
|
||||
public get items() {
|
||||
@@ -39,8 +53,11 @@ export class HaMdButtonMenu extends LitElement {
|
||||
<slot name="trigger" @slotchange=${this._setTriggerAria}></slot>
|
||||
</div>
|
||||
<ha-md-menu
|
||||
.quick=${this.quick}
|
||||
.positioning=${this.positioning}
|
||||
.hasOverflow=${this.hasOverflow}
|
||||
.anchorCorner=${this.anchorCorner}
|
||||
.menuCorner=${this.menuCorner}
|
||||
@opening=${this._handleOpening}
|
||||
@closing=${this._handleClosing}
|
||||
>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiStar } from "@mdi/js";
|
||||
import { mdiInformationOutline, mdiStar } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -71,6 +71,17 @@ export class HaNetwork extends LitElement {
|
||||
<span slot="description" data-for="auto_configure">
|
||||
${this.hass.localize("ui.panel.config.network.adapter.detected")}:
|
||||
${format_auto_detected_interfaces(this.networkConfig.adapters)}
|
||||
${!configured_adapters.length
|
||||
? html`<div class="info-text">
|
||||
<ha-svg-icon
|
||||
.path=${mdiInformationOutline}
|
||||
class="info-icon"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.network.adapter.auto_configure_manual_hint"
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
</span>
|
||||
</ha-settings-row>
|
||||
${configured_adapters.length || this._expanded
|
||||
@@ -171,6 +182,21 @@ export class HaNetwork extends LitElement {
|
||||
span[slot="description"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--info-color, var(--primary-color));
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -53,9 +53,15 @@ export class HaMediaSelector extends LitElement {
|
||||
|
||||
private _contextEntities: string[] | undefined;
|
||||
|
||||
private get _hasAccept(): boolean {
|
||||
return !!this.selector?.media?.accept?.length;
|
||||
}
|
||||
|
||||
willUpdate(changedProps: PropertyValues<this>) {
|
||||
if (changedProps.has("context")) {
|
||||
this._contextEntities = ensureArray(this.context?.filter_entity);
|
||||
if (!this._hasAccept) {
|
||||
this._contextEntities = ensureArray(this.context?.filter_entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProps.has("value")) {
|
||||
@@ -99,10 +105,8 @@ export class HaMediaSelector extends LitElement {
|
||||
(stateObj &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));
|
||||
|
||||
const hasAccept = this.selector?.media?.accept?.length;
|
||||
|
||||
return html`
|
||||
${hasAccept ||
|
||||
${this._hasAccept ||
|
||||
(this._contextEntities && this._contextEntities.length <= 1)
|
||||
? nothing
|
||||
: html`
|
||||
@@ -148,7 +152,7 @@ export class HaMediaSelector extends LitElement {
|
||||
: this.value.metadata?.title || this.value.media_content_id}
|
||||
@click=${this._pickMedia}
|
||||
@keydown=${this._handleKeyDown}
|
||||
class=${this.disabled || (!entityId && !hasAccept)
|
||||
class=${this.disabled || (!entityId && !this._hasAccept)
|
||||
? "disabled"
|
||||
: ""}
|
||||
>
|
||||
@@ -215,7 +219,7 @@ export class HaMediaSelector extends LitElement {
|
||||
|
||||
private _entityChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (this.context?.filter_entity) {
|
||||
if (!this._hasAccept && this.context?.filter_entity) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
media_content_id: "",
|
||||
@@ -257,7 +261,7 @@ export class HaMediaSelector extends LitElement {
|
||||
media_content_type: id.media_content_type,
|
||||
media_content_id: id.media_content_id,
|
||||
})),
|
||||
...(this.context?.filter_entity
|
||||
...(!this._hasAccept && this.context?.filter_entity
|
||||
? { browse_entity_id: this._getActiveEntityId() }
|
||||
: {}),
|
||||
},
|
||||
|
@@ -21,9 +21,15 @@ declare global {
|
||||
};
|
||||
"drag-start": undefined;
|
||||
"drag-end": undefined;
|
||||
"item-cloned": HaSortableClonedEventData;
|
||||
}
|
||||
}
|
||||
|
||||
export interface HaSortableClonedEventData {
|
||||
item: any;
|
||||
clone: any;
|
||||
}
|
||||
|
||||
export type HaSortableOptions = Omit<
|
||||
SortableInstance.SortableOptions,
|
||||
"onStart" | "onChoose" | "onEnd" | "onUpdate" | "onAdd" | "onRemove"
|
||||
@@ -148,6 +154,7 @@ export class HaSortable extends LitElement {
|
||||
onUpdate: this._handleUpdate,
|
||||
onAdd: this._handleAdd,
|
||||
onRemove: this._handleRemove,
|
||||
onClone: this._handleClone,
|
||||
};
|
||||
|
||||
if (this.draggableSelector) {
|
||||
@@ -187,6 +194,10 @@ export class HaSortable extends LitElement {
|
||||
fireEvent(this, "item-removed", { index: evt.oldIndex });
|
||||
};
|
||||
|
||||
private _handleClone = (evt) => {
|
||||
fireEvent(this, "item-cloned", evt);
|
||||
};
|
||||
|
||||
private _handleEnd = async (evt) => {
|
||||
fireEvent(this, "drag-end");
|
||||
// put back in original location
|
||||
|
@@ -558,11 +558,11 @@ export interface AutomationClipboard {
|
||||
export interface BaseSidebarConfig {
|
||||
toggleYamlMode: () => boolean;
|
||||
delete: () => void;
|
||||
close: (focus?: boolean) => void;
|
||||
}
|
||||
|
||||
export interface TriggerSidebarConfig extends BaseSidebarConfig {
|
||||
save: (value: Trigger) => void;
|
||||
close: () => void;
|
||||
rename: () => void;
|
||||
disable: () => void;
|
||||
duplicate: () => void;
|
||||
@@ -575,7 +575,6 @@ export interface TriggerSidebarConfig extends BaseSidebarConfig {
|
||||
|
||||
export interface ConditionSidebarConfig extends BaseSidebarConfig {
|
||||
save: (value: Condition) => void;
|
||||
close: () => void;
|
||||
rename: () => void;
|
||||
disable: () => void;
|
||||
test: () => void;
|
||||
@@ -589,7 +588,6 @@ export interface ConditionSidebarConfig extends BaseSidebarConfig {
|
||||
|
||||
export interface ActionSidebarConfig extends BaseSidebarConfig {
|
||||
save: (value: Action) => void;
|
||||
close: () => void;
|
||||
rename: () => void;
|
||||
disable: () => void;
|
||||
duplicate: () => void;
|
||||
@@ -604,7 +602,6 @@ export interface ActionSidebarConfig extends BaseSidebarConfig {
|
||||
}
|
||||
|
||||
export interface OptionSidebarConfig extends BaseSidebarConfig {
|
||||
close: () => void;
|
||||
rename: () => void;
|
||||
duplicate: () => void;
|
||||
defaultOption?: boolean;
|
||||
@@ -612,7 +609,6 @@ export interface OptionSidebarConfig extends BaseSidebarConfig {
|
||||
|
||||
export interface ScriptFieldSidebarConfig extends BaseSidebarConfig {
|
||||
save: (value: Field) => void;
|
||||
close: () => void;
|
||||
config: {
|
||||
field: Field;
|
||||
selector: boolean;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { formatDurationDigital } from "../common/datetime/format_duration";
|
||||
import type { FrontendLocaleData } from "./translation";
|
||||
|
||||
// These attributes are hidden from the more-info window for all entities.
|
||||
export const STATE_ATTRIBUTES = [
|
||||
"entity_id",
|
||||
"assumed_state",
|
||||
@@ -26,6 +27,14 @@ export const STATE_ATTRIBUTES = [
|
||||
"available_tones",
|
||||
];
|
||||
|
||||
// These attributes are hidden from the more-info window for entities of the
|
||||
// matching domain and device_class.
|
||||
export const STATE_ATTRIBUTES_DOMAIN_CLASS = {
|
||||
sensor: {
|
||||
enum: ["options"],
|
||||
},
|
||||
};
|
||||
|
||||
export const TEMPERATURE_ATTRIBUTES = new Set([
|
||||
"temperature",
|
||||
"current_temperature",
|
||||
|
@@ -195,6 +195,7 @@ export const fetchHostDisksUsage = async (hass: HomeAssistant) => {
|
||||
type: "supervisor/api",
|
||||
endpoint: "/host/disks/default/usage",
|
||||
method: "get",
|
||||
timeout: 3600, // seconds. This can take a while
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -53,6 +53,7 @@ export default class HaAutomationActionEditor extends LitElement {
|
||||
this.disabled || (this.action.enabled === false && !this.yamlMode),
|
||||
yaml: yamlMode,
|
||||
indent: this.indent,
|
||||
card: !this.inSidebar,
|
||||
})}
|
||||
>
|
||||
${yamlMode
|
||||
|
@@ -26,6 +26,7 @@ import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -153,6 +154,9 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
@property({ type: Boolean, attribute: "sidebar" })
|
||||
public optionsInSidebar = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sort-selected" })
|
||||
public sortSelected = false;
|
||||
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
state: false,
|
||||
@@ -186,6 +190,9 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
@query("ha-automation-action-editor")
|
||||
private _actionEditor?: HaAutomationActionEditor;
|
||||
|
||||
@query("ha-automation-row")
|
||||
private _automationRowElement?: HaAutomationRow;
|
||||
|
||||
protected firstUpdated(changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(changedProperties);
|
||||
|
||||
@@ -254,138 +261,136 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
<ha-svg-icon .path=${mdiAlertCircleCheck}></ha-svg-icon>
|
||||
</ha-tooltip>`
|
||||
: nothing}
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-button-menu
|
||||
quick
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
anchor-corner="end-end"
|
||||
menu-corner="start-end"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
<ha-md-button-menu
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-md-menu-item .clickAction=${this._runAction}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.run"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._renameAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-menu-item .clickAction=${this._runAction}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.run"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._renameAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutAction}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>`
|
||||
: nothing}
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || !!this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || !!this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || !!this.last}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || !!this.last}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${!this._uiModeAvailable || !!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${!this._uiModeAvailable || !!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiPlaylistEdit}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.action.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${this.action.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.action.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${this.action.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
class="warning"
|
||||
.clickAction=${this._onDelete}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
.clickAction=${this._onDelete}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>`
|
||||
: nothing}
|
||||
</ha-md-button-menu>
|
||||
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
</ha-md-button-menu>`
|
||||
: nothing}
|
||||
${!this.optionsInSidebar
|
||||
? html`${this._warnings
|
||||
? html`<ha-automation-editor-warning
|
||||
@@ -447,6 +452,7 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
...ACTION_BUILDING_BLOCKS,
|
||||
...ACTION_COMBINED_BLOCKS,
|
||||
].includes(blockType!)}
|
||||
.sortSelected=${this.sortSelected}
|
||||
>${this._renderRow()}</ha-automation-row
|
||||
>`
|
||||
: html`
|
||||
@@ -670,9 +676,12 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
save: (value) => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
},
|
||||
close: () => {
|
||||
close: (focus?: boolean) => {
|
||||
this._selected = false;
|
||||
fireEvent(this, "close-sidebar");
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
rename: () => {
|
||||
this._renameAction();
|
||||
@@ -697,9 +706,11 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
this._collapsed = false;
|
||||
|
||||
if (this.narrow) {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -747,6 +758,14 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
this._collapsed = !this._collapsed;
|
||||
}
|
||||
|
||||
public isSelected() {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._automationRowElement?.focus();
|
||||
}
|
||||
|
||||
static styles = rowStyles;
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, queryAll, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
@@ -10,6 +10,7 @@ import { listenMediaQuery } from "../../../../common/dom/media_query";
|
||||
import { nextRender } from "../../../../common/util/render-status";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-sortable";
|
||||
import type { HaSortableClonedEventData } from "../../../../components/ha-sortable";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
ACTION_BUILDING_BLOCKS,
|
||||
@@ -47,6 +48,8 @@ export default class HaAutomationAction extends LitElement {
|
||||
|
||||
@state() private _showReorder = false;
|
||||
|
||||
@state() private _rowSortSelected?: number;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
@@ -61,6 +64,8 @@ export default class HaAutomationAction extends LitElement {
|
||||
|
||||
private _focusLastActionOnChange = false;
|
||||
|
||||
private _focusActionIndexOnChange?: number;
|
||||
|
||||
private _actionKeys = new WeakMap<Action, string>();
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
@@ -89,8 +94,9 @@ export default class HaAutomationAction extends LitElement {
|
||||
@item-moved=${this._actionMoved}
|
||||
@item-added=${this._actionAdded}
|
||||
@item-removed=${this._actionRemoved}
|
||||
@item-cloned=${this._actionCloned}
|
||||
>
|
||||
<div class="rows">
|
||||
<div class="rows ${!this.optionsInSidebar ? "no-sidebar" : ""}">
|
||||
${repeat(
|
||||
this.actions,
|
||||
(action) => this._getKey(action),
|
||||
@@ -111,10 +117,20 @@ export default class HaAutomationAction extends LitElement {
|
||||
.hass=${this.hass}
|
||||
?highlight=${this.highlightedActions?.includes(action)}
|
||||
.optionsInSidebar=${this.optionsInSidebar}
|
||||
.sortSelected=${this._rowSortSelected === idx}
|
||||
@stop-sort-selection=${this._stopSortSelection}
|
||||
>
|
||||
${this._showReorder && !this.disabled
|
||||
? html`
|
||||
<div class="handle" slot="icons">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="handle ${this._rowSortSelected === idx
|
||||
? "active"
|
||||
: ""}"
|
||||
slot="icons"
|
||||
@keydown=${this._handleDragKeydown}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
@@ -154,19 +170,27 @@ export default class HaAutomationAction extends LitElement {
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("actions") && this._focusLastActionOnChange) {
|
||||
this._focusLastActionOnChange = false;
|
||||
if (
|
||||
changedProps.has("actions") &&
|
||||
(this._focusLastActionOnChange ||
|
||||
this._focusActionIndexOnChange !== undefined)
|
||||
) {
|
||||
const mode = this._focusLastActionOnChange ? "new" : "moved";
|
||||
|
||||
const row = this.shadowRoot!.querySelector<HaAutomationActionRow>(
|
||||
"ha-automation-action-row:last-of-type"
|
||||
`ha-automation-action-row:${mode === "new" ? "last-of-type" : `nth-of-type(${this._focusActionIndexOnChange! + 1})`}`
|
||||
)!;
|
||||
|
||||
this._focusLastActionOnChange = false;
|
||||
this._focusActionIndexOnChange = undefined;
|
||||
|
||||
row.updateComplete.then(() => {
|
||||
// on new condition open the settings in the sidebar, except for building blocks
|
||||
const type = getAutomationActionType(row.action);
|
||||
if (
|
||||
type &&
|
||||
this.optionsInSidebar &&
|
||||
!ACTION_BUILDING_BLOCKS.includes(type)
|
||||
(!ACTION_BUILDING_BLOCKS.includes(type) || mode === "moved")
|
||||
) {
|
||||
row.openSidebar();
|
||||
if (this.narrow) {
|
||||
@@ -176,8 +200,14 @@ export default class HaAutomationAction extends LitElement {
|
||||
});
|
||||
}
|
||||
}
|
||||
row.expand();
|
||||
row.focus();
|
||||
|
||||
if (mode === "new") {
|
||||
row.expand();
|
||||
}
|
||||
|
||||
if (!this.optionsInSidebar) {
|
||||
row.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -246,18 +276,30 @@ export default class HaAutomationAction extends LitElement {
|
||||
return this._actionKeys.get(action)!;
|
||||
}
|
||||
|
||||
private _moveUp(ev) {
|
||||
private async _moveUp(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationActionRow).first) {
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _moveDown(ev) {
|
||||
private async _moveDown(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationActionRow).last) {
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _move(oldIndex: number, newIndex: number) {
|
||||
@@ -277,6 +319,12 @@ export default class HaAutomationAction extends LitElement {
|
||||
private async _actionAdded(ev: CustomEvent): Promise<void> {
|
||||
ev.stopPropagation();
|
||||
const { index, data } = ev.detail;
|
||||
let selected = false;
|
||||
if (data?.["ha-automation-row-selected"]) {
|
||||
selected = true;
|
||||
delete data["ha-automation-row-selected"];
|
||||
}
|
||||
|
||||
let actions = [
|
||||
...this.actions.slice(0, index),
|
||||
data,
|
||||
@@ -284,6 +332,9 @@ export default class HaAutomationAction extends LitElement {
|
||||
];
|
||||
// Add action locally to avoid UI jump
|
||||
this.actions = actions;
|
||||
if (selected) {
|
||||
this._focusActionIndexOnChange = actions.length === 1 ? 0 : index;
|
||||
}
|
||||
await nextRender();
|
||||
if (this.actions !== actions) {
|
||||
// Ensure action is added even after update
|
||||
@@ -292,6 +343,9 @@ export default class HaAutomationAction extends LitElement {
|
||||
data,
|
||||
...this.actions.slice(index),
|
||||
];
|
||||
if (selected) {
|
||||
this._focusActionIndexOnChange = actions.length === 1 ? 0 : index;
|
||||
}
|
||||
}
|
||||
fireEvent(this, "value-changed", { value: actions });
|
||||
}
|
||||
@@ -327,6 +381,12 @@ export default class HaAutomationAction extends LitElement {
|
||||
fireEvent(this, "value-changed", { value: actions });
|
||||
}
|
||||
|
||||
private _actionCloned(ev: CustomEvent<HaSortableClonedEventData>) {
|
||||
if (ev.detail.item.action && ev.detail.item.isSelected()) {
|
||||
ev.detail.item.action["ha-automation-row-selected"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _duplicateAction(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
@@ -335,14 +395,21 @@ export default class HaAutomationAction extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
static styles = [
|
||||
automationRowsStyles,
|
||||
css`
|
||||
:host([root]) .rows {
|
||||
padding-right: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
private _handleDragKeydown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter" || ev.key === " ") {
|
||||
ev.stopPropagation();
|
||||
this._rowSortSelected =
|
||||
this._rowSortSelected === undefined
|
||||
? (ev.target as any).index
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopSortSelection() {
|
||||
this._rowSortSelected = undefined;
|
||||
}
|
||||
|
||||
static styles = automationRowsStyles;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -28,6 +28,8 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public selected = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "supported" }) public uiSupported =
|
||||
@@ -55,6 +57,7 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
(this.condition.enabled === false && !this.yamlMode),
|
||||
yaml: yamlMode,
|
||||
indent: this.indent,
|
||||
card: !this.inSidebar,
|
||||
})}
|
||||
>
|
||||
${yamlMode
|
||||
|
@@ -26,6 +26,7 @@ import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -115,6 +116,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sort-selected" })
|
||||
public sortSelected = false;
|
||||
|
||||
@state() private _collapsed = true;
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
@@ -145,6 +149,9 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
@query("ha-automation-condition-editor")
|
||||
public conditionEditor?: HaAutomationConditionEditor;
|
||||
|
||||
@query("ha-automation-row")
|
||||
private _automationRowElement?: HaAutomationRow;
|
||||
|
||||
private _renderRow() {
|
||||
return html`
|
||||
<ha-svg-icon
|
||||
@@ -160,143 +167,140 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
|
||||
<slot name="icons" slot="icons"></slot>
|
||||
|
||||
<ha-md-button-menu
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
>
|
||||
</ha-icon-button>
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-button-menu
|
||||
quick
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
anchor-corner="end-end"
|
||||
menu-corner="start-end"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
>
|
||||
</ha-icon-button>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`
|
||||
<ha-md-menu-item .clickAction=${this._testCondition}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.test"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._renameCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item .clickAction=${this._testCondition}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.test"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiFlask}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._renameCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
`
|
||||
: nothing}
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutCondition}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || this.last}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || this.last}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${this._uiSupported(this.condition.condition) ||
|
||||
!!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiPlaylistEdit}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${this._uiSupported(this.condition.condition) ||
|
||||
!!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.condition.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${this.condition.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.condition.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${this.condition.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
class="warning"
|
||||
.clickAction=${this._onDelete}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
.clickAction=${this._onDelete}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>`
|
||||
: nothing}
|
||||
</ha-md-button-menu>
|
||||
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
</ha-md-button-menu>`
|
||||
: nothing}
|
||||
${!this.optionsInSidebar
|
||||
? html`${this._warnings
|
||||
? html`<ha-automation-editor-warning
|
||||
@@ -356,6 +360,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
.buildingBlock=${CONDITION_BUILDING_BLOCKS.includes(
|
||||
this.condition.condition
|
||||
)}
|
||||
.sortSelected=${this.sortSelected}
|
||||
>${this._renderRow()}</ha-automation-row
|
||||
>`
|
||||
: html`
|
||||
@@ -643,9 +648,12 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
save: (value) => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
},
|
||||
close: () => {
|
||||
close: (focus?: boolean) => {
|
||||
this._selected = false;
|
||||
fireEvent(this, "close-sidebar");
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
rename: () => {
|
||||
this._renameCondition();
|
||||
@@ -668,9 +676,11 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
this._collapsed = false;
|
||||
|
||||
if (this.narrow) {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -684,6 +694,14 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
this._collapsed = !this._collapsed;
|
||||
}
|
||||
|
||||
public isSelected() {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._automationRowElement?.focus();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
rowStyles,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, queryAll, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
@@ -11,6 +11,7 @@ import { nextRender } from "../../../../common/util/render-status";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-sortable";
|
||||
import type { HaSortableClonedEventData } from "../../../../components/ha-sortable";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type {
|
||||
AutomationClipboard,
|
||||
@@ -45,6 +46,8 @@ export default class HaAutomationCondition extends LitElement {
|
||||
|
||||
@state() private _showReorder = false;
|
||||
|
||||
@state() private _rowSortSelected?: number;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
@@ -59,6 +62,8 @@ export default class HaAutomationCondition extends LitElement {
|
||||
|
||||
private _focusLastConditionOnChange = false;
|
||||
|
||||
private _focusConditionIndexOnChange?: number;
|
||||
|
||||
private _conditionKeys = new WeakMap<Condition, string>();
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
@@ -100,16 +105,25 @@ export default class HaAutomationCondition extends LitElement {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: updatedConditions,
|
||||
});
|
||||
} else if (this._focusLastConditionOnChange) {
|
||||
this._focusLastConditionOnChange = false;
|
||||
} else if (
|
||||
this._focusLastConditionOnChange ||
|
||||
this._focusConditionIndexOnChange !== undefined
|
||||
) {
|
||||
const mode = this._focusLastConditionOnChange ? "new" : "moved";
|
||||
|
||||
const row = this.shadowRoot!.querySelector<HaAutomationConditionRow>(
|
||||
"ha-automation-condition-row:last-of-type"
|
||||
`ha-automation-condition-row:${mode === "new" ? "last-of-type" : `nth-of-type(${this._focusConditionIndexOnChange! + 1})`}`
|
||||
)!;
|
||||
|
||||
this._focusLastConditionOnChange = false;
|
||||
this._focusConditionIndexOnChange = undefined;
|
||||
|
||||
row.updateComplete.then(() => {
|
||||
// on new condition open the settings in the sidebar, except for building blocks
|
||||
if (
|
||||
this.optionsInSidebar &&
|
||||
!CONDITION_BUILDING_BLOCKS.includes(row.condition.condition)
|
||||
(!CONDITION_BUILDING_BLOCKS.includes(row.condition.condition) ||
|
||||
mode === "moved")
|
||||
) {
|
||||
row.openSidebar();
|
||||
if (this.narrow) {
|
||||
@@ -119,8 +133,14 @@ export default class HaAutomationCondition extends LitElement {
|
||||
});
|
||||
}
|
||||
}
|
||||
row.expand();
|
||||
row.focus();
|
||||
|
||||
if (mode === "new") {
|
||||
row.expand();
|
||||
}
|
||||
|
||||
if (!this.optionsInSidebar) {
|
||||
row.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -151,8 +171,9 @@ export default class HaAutomationCondition extends LitElement {
|
||||
@item-moved=${this._conditionMoved}
|
||||
@item-added=${this._conditionAdded}
|
||||
@item-removed=${this._conditionRemoved}
|
||||
@item-cloned=${this._conditionCloned}
|
||||
>
|
||||
<div class="rows">
|
||||
<div class="rows ${!this.optionsInSidebar ? "no-sidebar" : ""}">
|
||||
${repeat(
|
||||
this.conditions.filter((c) => typeof c === "object"),
|
||||
(condition) => this._getKey(condition),
|
||||
@@ -174,10 +195,20 @@ export default class HaAutomationCondition extends LitElement {
|
||||
.hass=${this.hass}
|
||||
?highlight=${this.highlightedConditions?.includes(cond)}
|
||||
.optionsInSidebar=${this.optionsInSidebar}
|
||||
.sortSelected=${this._rowSortSelected === idx}
|
||||
@stop-sort-selection=${this._stopSortSelection}
|
||||
>
|
||||
${this._showReorder && !this.disabled
|
||||
? html`
|
||||
<div class="handle" slot="icons">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="handle ${this._rowSortSelected === idx
|
||||
? "active"
|
||||
: ""}"
|
||||
slot="icons"
|
||||
@keydown=${this._handleDragKeydown}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
@@ -266,15 +297,27 @@ export default class HaAutomationCondition extends LitElement {
|
||||
private _moveUp(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationConditionRow).first) {
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _moveDown(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationConditionRow).last) {
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _move(oldIndex: number, newIndex: number) {
|
||||
@@ -294,6 +337,11 @@ export default class HaAutomationCondition extends LitElement {
|
||||
private async _conditionAdded(ev: CustomEvent): Promise<void> {
|
||||
ev.stopPropagation();
|
||||
const { index, data } = ev.detail;
|
||||
let selected = false;
|
||||
if (data?.["ha-automation-row-selected"]) {
|
||||
selected = true;
|
||||
delete data["ha-automation-row-selected"];
|
||||
}
|
||||
let conditions = [
|
||||
...this.conditions.slice(0, index),
|
||||
data,
|
||||
@@ -301,6 +349,9 @@ export default class HaAutomationCondition extends LitElement {
|
||||
];
|
||||
// Add condition locally to avoid UI jump
|
||||
this.conditions = conditions;
|
||||
if (selected) {
|
||||
this._focusConditionIndexOnChange = conditions.length === 1 ? 0 : index;
|
||||
}
|
||||
await nextRender();
|
||||
if (this.conditions !== conditions) {
|
||||
// Ensure condition is added even after update
|
||||
@@ -309,6 +360,9 @@ export default class HaAutomationCondition extends LitElement {
|
||||
data,
|
||||
...this.conditions.slice(index),
|
||||
];
|
||||
if (selected) {
|
||||
this._focusConditionIndexOnChange = conditions.length === 1 ? 0 : index;
|
||||
}
|
||||
}
|
||||
fireEvent(this, "value-changed", { value: conditions });
|
||||
}
|
||||
@@ -325,6 +379,12 @@ export default class HaAutomationCondition extends LitElement {
|
||||
fireEvent(this, "value-changed", { value: conditions });
|
||||
}
|
||||
|
||||
private _conditionCloned(ev: CustomEvent<HaSortableClonedEventData>) {
|
||||
if (ev.detail.item.isSelected()) {
|
||||
ev.detail.item.condition["ha-automation-row-selected"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const conditions = [...this.conditions];
|
||||
@@ -354,14 +414,21 @@ export default class HaAutomationCondition extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
static styles = [
|
||||
automationRowsStyles,
|
||||
css`
|
||||
:host([root]) .rows {
|
||||
padding-right: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
private _handleDragKeydown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter" || ev.key === " ") {
|
||||
ev.stopPropagation();
|
||||
this._rowSortSelected =
|
||||
this._rowSortSelected === undefined
|
||||
? (ev.target as any).index
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopSortSelection() {
|
||||
this._rowSortSelected = undefined;
|
||||
}
|
||||
|
||||
static styles = automationRowsStyles;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -432,62 +432,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
class=${this._mode === "yaml" ? "yaml-mode" : ""}
|
||||
@subscribe-automation-config=${this._subscribeAutomationConfig}
|
||||
>
|
||||
<div class="error-wrapper">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.unavailable"
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiRobotConfused}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: ""}
|
||||
${this._blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.confirm_take_control"
|
||||
)}
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
>${this.hass.localize("ui.common.yes")}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
>${this.hass.localize("ui.common.no")}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
? html`<ha-alert alert-type="warning" dismissable
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.read_only"
|
||||
)}
|
||||
<ha-button
|
||||
appearance="filled"
|
||||
size="small"
|
||||
variant="warning"
|
||||
slot="action"
|
||||
@click=${this._duplicate}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.migrate"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
</div>
|
||||
${this._mode === "gui"
|
||||
? html`
|
||||
<div>
|
||||
@@ -499,7 +443,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.isWide=${this.isWide}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
.disabled=${Boolean(this._readOnly)}
|
||||
.disabled=${this._readOnly}
|
||||
.saving=${this._saving}
|
||||
.dirty=${this._dirty}
|
||||
@value-changed=${this._valueChanged}
|
||||
@@ -513,13 +457,94 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.isWide=${this.isWide}
|
||||
.stateObj=${stateObj}
|
||||
.config=${this._config}
|
||||
.disabled=${Boolean(this._readOnly)}
|
||||
.disabled=${this._readOnly}
|
||||
.dirty=${this._dirty}
|
||||
.saving=${this._saving}
|
||||
@value-changed=${this._valueChanged}
|
||||
@save-automation=${this._handleSaveAutomation}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
></manual-automation-editor>
|
||||
>
|
||||
<div class="alert-wrapper" slot="alerts">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.unavailable"
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiRobotConfused}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${this._blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.confirm_take_control"
|
||||
)}
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
>${this.hass.localize(
|
||||
"ui.common.yes"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
>${this.hass.localize(
|
||||
"ui.common.no"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
dismissable
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.read_only"
|
||||
)}
|
||||
<ha-button
|
||||
appearance="filled"
|
||||
size="small"
|
||||
variant="warning"
|
||||
slot="action"
|
||||
@click=${this._duplicate}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.migrate"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${stateObj?.state === "off"
|
||||
? html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.disabled"
|
||||
)}
|
||||
<ha-button
|
||||
size="small"
|
||||
slot="action"
|
||||
@click=${this._toggle}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.enable"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</manual-automation-editor>
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
@@ -542,7 +567,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
</ha-button>
|
||||
</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
: nothing}
|
||||
<ha-yaml-editor
|
||||
copy-clipboard
|
||||
.hass=${this.hass}
|
||||
@@ -1156,22 +1181,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
display: block;
|
||||
}
|
||||
|
||||
:not(.yaml-mode) > .error-wrapper {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
z-index: 3;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:not(.yaml-mode) > .error-wrapper ha-alert {
|
||||
background-color: var(--card-background-color);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
}
|
||||
|
||||
manual-automation-editor {
|
||||
max-width: 1540px;
|
||||
padding: 0 12px;
|
||||
|
@@ -193,7 +193,7 @@ export default class HaAutomationSidebar extends LitElement {
|
||||
}
|
||||
|
||||
private _closeSidebar() {
|
||||
this.config?.close();
|
||||
this.config?.close(true);
|
||||
}
|
||||
|
||||
private _toggleYamlMode = () => {
|
||||
|
@@ -23,7 +23,6 @@ import {
|
||||
extractSearchParam,
|
||||
removeSearchParam,
|
||||
} from "../../../common/url/search-params";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
@@ -93,9 +92,6 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
|
||||
@state() private _sidebarConfig?: SidebarConfig;
|
||||
|
||||
@query(".content")
|
||||
private _contentElement?: HTMLDivElement;
|
||||
|
||||
@query("ha-automation-sidebar") private _sidebarElement?: HaAutomationSidebar;
|
||||
|
||||
private _previousConfig?: ManualAutomationConfig;
|
||||
@@ -112,20 +108,6 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
|
||||
private _renderContent() {
|
||||
return html`
|
||||
${this.stateObj?.state === "off"
|
||||
? html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.disabled"
|
||||
)}
|
||||
<ha-button size="small" slot="action" @click=${this._enable}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.enable"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>
|
||||
`
|
||||
: nothing}
|
||||
${this.config.description
|
||||
? html`<ha-markdown
|
||||
class="description"
|
||||
@@ -274,37 +256,39 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"split-view": true,
|
||||
"sidebar-hidden": !this._sidebarConfig,
|
||||
"has-sidebar": this._sidebarConfig && !this.narrow,
|
||||
})}
|
||||
>
|
||||
<div class="content-wrapper">
|
||||
<div class="content">${this._renderContent()}</div>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._saveAutomation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<div class="content">
|
||||
<slot name="alerts"></slot>
|
||||
${this._renderContent()}
|
||||
</div>
|
||||
<div class="fab-positioner">
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._saveAutomation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-positioner">
|
||||
<ha-automation-sidebar
|
||||
tabindex="-1"
|
||||
class=${classMap({ hidden: !this._sidebarConfig })}
|
||||
.isWide=${this.isWide}
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.config=${this._sidebarConfig}
|
||||
@value-changed=${this._sidebarConfigChanged}
|
||||
.disabled=${this.disabled}
|
||||
></ha-automation-sidebar>
|
||||
</div>
|
||||
<ha-automation-sidebar
|
||||
tabindex="-1"
|
||||
class=${classMap({
|
||||
sidebar: true,
|
||||
overlay: !this.isWide && !this.narrow,
|
||||
rtl: computeRTL(this.hass),
|
||||
})}
|
||||
.isWide=${this.isWide}
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.config=${this._sidebarConfig}
|
||||
@value-changed=${this._sidebarConfigChanged}
|
||||
.disabled=${this.disabled}
|
||||
></ha-automation-sidebar>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -355,8 +339,6 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
|
||||
private _handleCloseSidebar() {
|
||||
this._sidebarConfig = undefined;
|
||||
// fix content shift when bottom rows are scrolled into view
|
||||
this._contentElement?.scrollIntoView();
|
||||
}
|
||||
|
||||
private _triggerChanged(ev: CustomEvent): void {
|
||||
@@ -386,15 +368,6 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private async _enable(): Promise<void> {
|
||||
if (!this.hass || !this.stateObj) {
|
||||
return;
|
||||
}
|
||||
await this.hass.callService("automation", "turn_on", {
|
||||
entity_id: this.stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _saveAutomation() {
|
||||
this._closeSidebar();
|
||||
fireEvent(this, "save-automation");
|
||||
@@ -656,9 +629,8 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
.description {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -17,6 +17,7 @@ import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_de
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||
import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -63,6 +64,9 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
@property({ type: Boolean, attribute: "sidebar" })
|
||||
public optionsInSidebar = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sort-selected" })
|
||||
public sortSelected = false;
|
||||
|
||||
@state() private _expanded = false;
|
||||
|
||||
@state() private _selected = false;
|
||||
@@ -79,6 +83,9 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
@query("ha-automation-action")
|
||||
private _actionElement?: HaAutomationAction;
|
||||
|
||||
@query("ha-automation-row")
|
||||
private _automationRowElement?: HaAutomationRow;
|
||||
|
||||
private _expandedChanged(ev) {
|
||||
if (ev.currentTarget.id !== "option") {
|
||||
return;
|
||||
@@ -123,14 +130,17 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
|
||||
<slot name="icons" slot="icons"></slot>
|
||||
|
||||
${this.option
|
||||
${this.option && !this.optionsInSidebar
|
||||
? html`
|
||||
<ha-md-button-menu
|
||||
quick
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
positioning="fixed"
|
||||
anchor-corner="end-end"
|
||||
menu-corner="start-end"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
@@ -138,35 +148,28 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`
|
||||
<ha-md-menu-item
|
||||
@click=${this._renameOption}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.rename"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiRenameBox}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
@click=${this._renameOption}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
@click=${this._duplicateOption}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
`
|
||||
: nothing}
|
||||
<ha-md-menu-item
|
||||
@click=${this._duplicateOption}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
@click=${this._moveUp}
|
||||
@@ -175,7 +178,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_up"
|
||||
)}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon>
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
@@ -185,25 +188,23 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon>
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-menu-item
|
||||
@click=${this._removeOption}
|
||||
class="warning"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
slot="graphic"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>`
|
||||
: nothing}
|
||||
<ha-md-menu-item
|
||||
@click=${this._removeOption}
|
||||
class="warning"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.choose.remove_option"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
</ha-md-button-menu>
|
||||
`
|
||||
: nothing}
|
||||
@@ -215,6 +216,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
return html`<div
|
||||
class=${classMap({
|
||||
"card-content": true,
|
||||
card: !this.optionsInSidebar,
|
||||
indent: this.optionsInSidebar,
|
||||
selected: this._selected,
|
||||
hidden: this.optionsInSidebar && this._collapsed,
|
||||
@@ -271,6 +273,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
.selected=${this._selected}
|
||||
@click=${this._toggleSidebar}
|
||||
@toggle-collapsed=${this._toggleCollapse}
|
||||
.sortSelected=${this.sortSelected}
|
||||
>${this._renderRow()}</ha-automation-row
|
||||
>`
|
||||
: html`
|
||||
@@ -382,9 +385,12 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
|
||||
public openSidebar(): void {
|
||||
fireEvent(this, "open-sidebar", {
|
||||
close: () => {
|
||||
close: (focus?: boolean) => {
|
||||
this._selected = false;
|
||||
fireEvent(this, "close-sidebar");
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
rename: () => {
|
||||
this._renameOption();
|
||||
@@ -398,9 +404,11 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
this._collapsed = false;
|
||||
|
||||
if (this.narrow) {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -438,6 +446,14 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
this._collapsed = !this._collapsed;
|
||||
}
|
||||
|
||||
public isSelected() {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._automationRowElement?.focus();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
rowStyles,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, queryAll, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
@@ -10,6 +10,7 @@ import { listenMediaQuery } from "../../../../common/dom/media_query";
|
||||
import { nextRender } from "../../../../common/util/render-status";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-sortable";
|
||||
import type { HaSortableClonedEventData } from "../../../../components/ha-sortable";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { AutomationClipboard } from "../../../../data/automation";
|
||||
import type { Option } from "../../../../data/script";
|
||||
@@ -36,6 +37,8 @@ export default class HaAutomationOption extends LitElement {
|
||||
|
||||
@state() private _showReorder = false;
|
||||
|
||||
@state() private _rowSortSelected?: number;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
@@ -50,6 +53,8 @@ export default class HaAutomationOption extends LitElement {
|
||||
|
||||
private _focusLastOptionOnChange = false;
|
||||
|
||||
private _focusOptionIndexOnChange?: number;
|
||||
|
||||
private _optionsKeys = new WeakMap<Option, string>();
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
@@ -78,8 +83,9 @@ export default class HaAutomationOption extends LitElement {
|
||||
@item-moved=${this._optionMoved}
|
||||
@item-added=${this._optionAdded}
|
||||
@item-removed=${this._optionRemoved}
|
||||
@item-cloned=${this._optionCloned}
|
||||
>
|
||||
<div class="rows">
|
||||
<div class="rows ${!this.optionsInSidebar ? "no-sidebar" : ""}">
|
||||
${repeat(
|
||||
this.options,
|
||||
(option) => this._getKey(option),
|
||||
@@ -98,10 +104,20 @@ export default class HaAutomationOption extends LitElement {
|
||||
@value-changed=${this._optionChanged}
|
||||
.hass=${this.hass}
|
||||
.optionsInSidebar=${this.optionsInSidebar}
|
||||
.sortSelected=${this._rowSortSelected === idx}
|
||||
@stop-sort-selection=${this._stopSortSelection}
|
||||
>
|
||||
${this._showReorder && !this.disabled
|
||||
? html`
|
||||
<div class="handle" slot="icons">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="handle ${this._rowSortSelected === idx
|
||||
? "active"
|
||||
: ""}"
|
||||
slot="icons"
|
||||
@keydown=${this._handleDragKeydown}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
@@ -143,12 +159,20 @@ export default class HaAutomationOption extends LitElement {
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("options") && this._focusLastOptionOnChange) {
|
||||
this._focusLastOptionOnChange = false;
|
||||
if (
|
||||
changedProps.has("options") &&
|
||||
(this._focusLastOptionOnChange ||
|
||||
this._focusOptionIndexOnChange !== undefined)
|
||||
) {
|
||||
const mode = this._focusLastOptionOnChange ? "new" : "moved";
|
||||
|
||||
const row = this.shadowRoot!.querySelector<HaAutomationOptionRow>(
|
||||
"ha-automation-option-row:last-of-type"
|
||||
`ha-automation-option-row:${mode === "new" ? "last-of-type" : `nth-of-type(${this._focusOptionIndexOnChange! + 1})`}`
|
||||
)!;
|
||||
|
||||
this._focusLastOptionOnChange = false;
|
||||
this._focusOptionIndexOnChange = undefined;
|
||||
|
||||
row.updateComplete.then(() => {
|
||||
if (this.narrow) {
|
||||
row.scrollIntoView({
|
||||
@@ -156,8 +180,16 @@ export default class HaAutomationOption extends LitElement {
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
row.expand();
|
||||
row.focus();
|
||||
|
||||
if (mode === "new") {
|
||||
row.expand();
|
||||
}
|
||||
|
||||
if (this.optionsInSidebar) {
|
||||
row.openSidebar();
|
||||
} else {
|
||||
row.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -187,15 +219,27 @@ export default class HaAutomationOption extends LitElement {
|
||||
private _moveUp(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationOptionRow).first) {
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _moveDown(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationOptionRow).last) {
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _move(oldIndex: number, newIndex: number) {
|
||||
@@ -215,6 +259,12 @@ export default class HaAutomationOption extends LitElement {
|
||||
private async _optionAdded(ev: CustomEvent): Promise<void> {
|
||||
ev.stopPropagation();
|
||||
const { index, data } = ev.detail;
|
||||
let selected = false;
|
||||
if (data?.["ha-automation-row-selected"]) {
|
||||
selected = true;
|
||||
delete data["ha-automation-row-selected"];
|
||||
}
|
||||
|
||||
const options = [
|
||||
...this.options.slice(0, index),
|
||||
data,
|
||||
@@ -222,6 +272,9 @@ export default class HaAutomationOption extends LitElement {
|
||||
];
|
||||
// Add option locally to avoid UI jump
|
||||
this.options = options;
|
||||
if (selected) {
|
||||
this._focusOptionIndexOnChange = options.length === 1 ? 0 : index;
|
||||
}
|
||||
await nextRender();
|
||||
fireEvent(this, "value-changed", { value: this.options });
|
||||
}
|
||||
@@ -238,6 +291,12 @@ export default class HaAutomationOption extends LitElement {
|
||||
fireEvent(this, "value-changed", { value: options });
|
||||
}
|
||||
|
||||
private _optionCloned(ev: CustomEvent<HaSortableClonedEventData>) {
|
||||
if (ev.detail.item.isSelected()) {
|
||||
ev.detail.item.option["ha-automation-row-selected"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _optionChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const options = [...this.options];
|
||||
@@ -269,7 +328,28 @@ export default class HaAutomationOption extends LitElement {
|
||||
fireEvent(this, "show-default-actions");
|
||||
};
|
||||
|
||||
static styles = automationRowsStyles;
|
||||
private _handleDragKeydown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter" || ev.key === " ") {
|
||||
ev.stopPropagation();
|
||||
this._rowSortSelected =
|
||||
this._rowSortSelected === undefined
|
||||
? (ev.target as any).index
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopSortSelection() {
|
||||
this._rowSortSelected = undefined;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
automationRowsStyles,
|
||||
css`
|
||||
:host([root]) .rows {
|
||||
padding-right: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -67,10 +67,13 @@ export default class HaAutomationSidebarCard extends LitElement {
|
||||
<slot slot="subtitle" name="subtitle"></slot>
|
||||
<slot name="overflow-menu" slot="actionItems">
|
||||
<ha-md-button-menu
|
||||
quick
|
||||
@click=${this._openOverflowMenu}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
.positioning=${this.narrow ? "absolute" : "fixed"}
|
||||
anchor-corner="end-end"
|
||||
menu-corner="start-end"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
|
@@ -182,6 +182,7 @@ export default class HaAutomationSidebarCondition extends LitElement {
|
||||
@value-changed=${this._valueChangedSidebar}
|
||||
.disabled=${this.disabled}
|
||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||
sidebar
|
||||
></ha-automation-condition-editor> `}
|
||||
</ha-automation-sidebar-card>`;
|
||||
}
|
||||
|
@@ -67,7 +67,7 @@ export default class HaAutomationSidebarOption extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
@@ -193,6 +193,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
|
||||
.yamlMode=${this.yamlMode}
|
||||
.disabled=${this.disabled}
|
||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||
sidebar
|
||||
></ha-automation-trigger-editor>
|
||||
</ha-automation-sidebar-card>
|
||||
`;
|
||||
|
@@ -54,7 +54,7 @@ export const editorStyles = css`
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
.card-content.card {
|
||||
padding: 16px;
|
||||
}
|
||||
.card-content.yaml {
|
||||
@@ -69,7 +69,7 @@ export const indentStyle = css`
|
||||
.selector-row,
|
||||
:host([indent]) ha-form {
|
||||
margin-left: 12px;
|
||||
padding: 12px 24px 16px 16px;
|
||||
padding: 12px 0 16px 16px;
|
||||
border-left: 2px solid var(--ha-color-border-neutral-quiet);
|
||||
border-bottom: 2px solid var(--ha-color-border-neutral-quiet);
|
||||
border-radius: 0;
|
||||
@@ -108,78 +108,59 @@ export const saveFabStyles = css`
|
||||
export const manualEditorStyles = css`
|
||||
:host {
|
||||
display: block;
|
||||
--sidebar-width: 0;
|
||||
--sidebar-gap: 0;
|
||||
}
|
||||
|
||||
.split-view {
|
||||
.has-sidebar {
|
||||
--sidebar-width: min(35vw, 500px);
|
||||
--sidebar-gap: 16px;
|
||||
}
|
||||
|
||||
.fab-positioner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
gap: 16px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.split-view.sidebar-hidden {
|
||||
gap: 0;
|
||||
.fab-positioner ha-fab {
|
||||
position: fixed;
|
||||
right: unset;
|
||||
left: unset;
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
.fab-positioner ha-fab.dirty {
|
||||
bottom: 16px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
position: relative;
|
||||
flex: 6;
|
||||
padding-right: calc(var(--sidebar-width) + var(--sidebar-gap));
|
||||
padding-inline-end: calc(var(--sidebar-width) + var(--sidebar-gap));
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 32px 16px 64px 0;
|
||||
height: calc(100vh - 153px);
|
||||
height: calc(100dvh - 153px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 72px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 12px 0;
|
||||
flex: 4;
|
||||
height: calc(100vh - 81px);
|
||||
height: calc(100dvh - 81px);
|
||||
width: 40%;
|
||||
}
|
||||
.split-view.sidebar-hidden .sidebar {
|
||||
border-color: transparent;
|
||||
border-width: 0;
|
||||
overflow: hidden;
|
||||
flex: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.sidebar.overlay {
|
||||
ha-automation-sidebar {
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
height: calc(100% - 70px);
|
||||
padding: 0;
|
||||
z-index: 5;
|
||||
box-shadow: -8px 0 16px rgba(0, 0, 0, 0.2);
|
||||
top: calc(var(--header-height) + 16px);
|
||||
height: calc(-81px + 100dvh);
|
||||
width: var(--sidebar-width);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar.overlay.rtl {
|
||||
right: unset;
|
||||
left: 8px;
|
||||
ha-automation-sidebar.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media all and (max-width: 870px) {
|
||||
.split-view {
|
||||
gap: 0;
|
||||
margin-right: -8px;
|
||||
}
|
||||
.sidebar {
|
||||
height: 0;
|
||||
width: 0;
|
||||
flex: 0;
|
||||
}
|
||||
.sidebar-positioner {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.split-view.sidebar-hidden .sidebar.overlay {
|
||||
width: 0;
|
||||
}
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -190,13 +171,13 @@ export const manualEditorStyles = css`
|
||||
|
||||
export const automationRowsStyles = css`
|
||||
.rows {
|
||||
padding: 16px 0 16px 16px;
|
||||
margin: -16px;
|
||||
margin-right: -20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
.rows.no-sidebar {
|
||||
margin-right: 0;
|
||||
}
|
||||
.sortable-ghost {
|
||||
background: none;
|
||||
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||
@@ -209,9 +190,19 @@ export const automationRowsStyles = css`
|
||||
scroll-margin-top: 48px;
|
||||
}
|
||||
.handle {
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
border-radius: var(--ha-border-radius-pill);
|
||||
}
|
||||
.handle:focus {
|
||||
outline: var(--wa-focus-ring);
|
||||
background: var(--ha-color-fill-neutral-quiet-resting);
|
||||
}
|
||||
.handle.active {
|
||||
outline: var(--wa-focus-ring);
|
||||
background: var(--ha-color-fill-neutral-normal-active);
|
||||
}
|
||||
.handle ha-svg-icon {
|
||||
pointer-events: none;
|
||||
|
@@ -29,6 +29,8 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
|
||||
@property({ type: Boolean, attribute: "show-id" }) public showId = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
|
||||
|
||||
@query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
|
||||
|
||||
protected render() {
|
||||
@@ -47,6 +49,7 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
this.trigger.enabled === false &&
|
||||
!this.yamlMode),
|
||||
yaml: yamlMode,
|
||||
card: !this.inSidebar,
|
||||
})}
|
||||
>
|
||||
${yamlMode
|
||||
|
@@ -28,6 +28,7 @@ import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import { debounce } from "../../../../common/util/debounce";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -115,6 +116,9 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
@property({ type: Boolean, attribute: "sidebar" })
|
||||
public optionsInSidebar = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "sort-selected" })
|
||||
public sortSelected = false;
|
||||
|
||||
@state() private _yamlMode = false;
|
||||
|
||||
@state() private _triggered?: Record<string, unknown>;
|
||||
@@ -132,6 +136,9 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
@query("ha-automation-trigger-editor")
|
||||
public triggerEditor?: HaAutomationTriggerEditor;
|
||||
|
||||
@query("ha-automation-row")
|
||||
private _automationRowElement?: HaAutomationRow;
|
||||
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
state: false,
|
||||
@@ -165,145 +172,142 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
|
||||
<slot name="icons" slot="icons"></slot>
|
||||
|
||||
<ha-md-button-menu
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-button-menu
|
||||
quick
|
||||
slot="icons"
|
||||
@click=${preventDefaultStopPropagation}
|
||||
@keydown=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
positioning="fixed"
|
||||
anchor-corner="end-end"
|
||||
menu-corner="start-end"
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._renameTrigger}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`<ha-md-menu-item
|
||||
.clickAction=${this._renameTrigger}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.rename"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._showTriggerId}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._showTriggerId}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.edit_id"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._duplicateTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentDuplicate}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._copyTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.copy"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._cutTrigger}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.cut"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCut}></ha-svg-icon>
|
||||
</ha-md-menu-item>`
|
||||
: nothing}
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveUp}
|
||||
.disabled=${this.disabled || this.first}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowUp}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || this.last}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._moveDown}
|
||||
.disabled=${this.disabled || this.last}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.move_down")}
|
||||
<ha-svg-icon slot="start" .path=${mdiArrowDown}></ha-svg-icon
|
||||
></ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${!supported || !!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon slot="start" .path=${mdiPlaylistEdit}></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
|
||||
${!this.optionsInSidebar
|
||||
? html`
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._toggleYamlMode}
|
||||
.disabled=${!supported || !!this._warnings}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiPlaylistEdit}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
|
||||
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${"enabled" in this.trigger && this.trigger.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${"enabled" in this.trigger &&
|
||||
this.trigger.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDelete}
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDisable}
|
||||
.disabled=${this.disabled || type === "list"}
|
||||
>
|
||||
${"enabled" in this.trigger && this.trigger.enabled === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.enable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.disable"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${"enabled" in this.trigger &&
|
||||
this.trigger.enabled === false
|
||||
? mdiPlayCircleOutline
|
||||
: mdiStopCircleOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
<ha-md-menu-item
|
||||
.clickAction=${this._onDelete}
|
||||
class="warning"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
class="warning"
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
`
|
||||
: nothing}
|
||||
</ha-md-button-menu>
|
||||
slot="start"
|
||||
.path=${mdiDelete}
|
||||
></ha-svg-icon>
|
||||
</ha-md-menu-item>
|
||||
</ha-md-button-menu>`
|
||||
: nothing}
|
||||
${!this.optionsInSidebar
|
||||
? html`${this._warnings
|
||||
? html`<ha-automation-editor-warning
|
||||
@@ -345,6 +349,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
this.trigger.enabled === false}
|
||||
@click=${this._toggleSidebar}
|
||||
.selected=${this._selected}
|
||||
.sortSelected=${this.sortSelected}
|
||||
>${this._selected
|
||||
? "selected"
|
||||
: nothing}${this._renderRow()}</ha-automation-row
|
||||
@@ -477,9 +482,12 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
save: (value) => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
},
|
||||
close: () => {
|
||||
close: (focus?: boolean) => {
|
||||
this._selected = false;
|
||||
fireEvent(this, "close-sidebar");
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
rename: () => {
|
||||
this._renameTrigger();
|
||||
@@ -500,9 +508,11 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
this._selected = true;
|
||||
|
||||
if (this.narrow) {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -666,6 +676,14 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
customElements.get(`ha-automation-trigger-${type}`) !== undefined
|
||||
);
|
||||
|
||||
public isSelected() {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._automationRowElement?.focus();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
rowStyles,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
@@ -11,6 +11,7 @@ import { nextRender } from "../../../../common/util/render-status";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-sortable";
|
||||
import type { HaSortableClonedEventData } from "../../../../components/ha-sortable";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type {
|
||||
AutomationClipboard,
|
||||
@@ -46,6 +47,8 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
|
||||
@state() private _showReorder = false;
|
||||
|
||||
@state() private _rowSortSelected?: number;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "automationClipboard",
|
||||
@@ -57,6 +60,8 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
|
||||
private _focusLastTriggerOnChange = false;
|
||||
|
||||
private _focusTriggerIndexOnChange?: number;
|
||||
|
||||
private _triggerKeys = new WeakMap<Trigger, string>();
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
@@ -85,8 +90,9 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
@item-moved=${this._triggerMoved}
|
||||
@item-added=${this._triggerAdded}
|
||||
@item-removed=${this._triggerRemoved}
|
||||
@item-cloned=${this._triggerCloned}
|
||||
>
|
||||
<div class="rows">
|
||||
<div class="rows ${!this.optionsInSidebar ? "no-sidebar" : ""}">
|
||||
${repeat(
|
||||
this.triggers,
|
||||
(trigger) => this._getKey(trigger),
|
||||
@@ -106,10 +112,20 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
?highlight=${this.highlightedTriggers?.includes(trg)}
|
||||
.optionsInSidebar=${this.optionsInSidebar}
|
||||
.sortSelected=${this._rowSortSelected === idx}
|
||||
@stop-sort-selection=${this._stopSortSelection}
|
||||
>
|
||||
${this._showReorder && !this.disabled
|
||||
? html`
|
||||
<div class="handle" slot="icons">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="handle ${this._rowSortSelected === idx
|
||||
? "active"
|
||||
: ""}"
|
||||
slot="icons"
|
||||
@keydown=${this._handleDragKeydown}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
@@ -172,12 +188,18 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("triggers") && this._focusLastTriggerOnChange) {
|
||||
this._focusLastTriggerOnChange = false;
|
||||
|
||||
if (
|
||||
changedProps.has("triggers") &&
|
||||
(this._focusLastTriggerOnChange ||
|
||||
this._focusTriggerIndexOnChange !== undefined)
|
||||
) {
|
||||
const row = this.shadowRoot!.querySelector<HaAutomationTriggerRow>(
|
||||
"ha-automation-trigger-row:last-of-type"
|
||||
`ha-automation-trigger-row:${this._focusLastTriggerOnChange ? "last-of-type" : `nth-of-type(${this._focusTriggerIndexOnChange! + 1})`}`
|
||||
)!;
|
||||
|
||||
this._focusLastTriggerOnChange = false;
|
||||
this._focusTriggerIndexOnChange = undefined;
|
||||
|
||||
row.updateComplete.then(() => {
|
||||
if (this.optionsInSidebar) {
|
||||
row.openSidebar();
|
||||
@@ -189,8 +211,8 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
}
|
||||
} else {
|
||||
row.expand();
|
||||
row.focus();
|
||||
}
|
||||
row.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -216,15 +238,27 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
private _moveUp(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationTriggerRow).first) {
|
||||
const newIndex = index - 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _moveDown(ev) {
|
||||
ev.stopPropagation();
|
||||
const index = (ev.target as any).index;
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (!(ev.target as HaAutomationTriggerRow).last) {
|
||||
const newIndex = index + 1;
|
||||
this._move(index, newIndex);
|
||||
if (this._rowSortSelected === index) {
|
||||
this._rowSortSelected = newIndex;
|
||||
}
|
||||
ev.target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private _move(oldIndex: number, newIndex: number) {
|
||||
@@ -244,6 +278,12 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
private async _triggerAdded(ev: CustomEvent): Promise<void> {
|
||||
ev.stopPropagation();
|
||||
const { index, data } = ev.detail;
|
||||
let selected = false;
|
||||
if (data?.["ha-automation-row-selected"]) {
|
||||
selected = true;
|
||||
delete data["ha-automation-row-selected"];
|
||||
}
|
||||
|
||||
let triggers = [
|
||||
...this.triggers.slice(0, index),
|
||||
data,
|
||||
@@ -251,6 +291,9 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
];
|
||||
// Add trigger locally to avoid UI jump
|
||||
this.triggers = triggers;
|
||||
if (selected) {
|
||||
this._focusTriggerIndexOnChange = triggers.length === 1 ? 0 : index;
|
||||
}
|
||||
await nextRender();
|
||||
if (this.triggers !== triggers) {
|
||||
// Ensure trigger is added even after update
|
||||
@@ -259,6 +302,9 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
data,
|
||||
...this.triggers.slice(index),
|
||||
];
|
||||
if (selected) {
|
||||
this._focusTriggerIndexOnChange = triggers.length === 1 ? 0 : index;
|
||||
}
|
||||
}
|
||||
fireEvent(this, "value-changed", { value: triggers });
|
||||
}
|
||||
@@ -275,6 +321,12 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
fireEvent(this, "value-changed", { value: triggers });
|
||||
}
|
||||
|
||||
private _triggerCloned(ev: CustomEvent<HaSortableClonedEventData>) {
|
||||
if (ev.detail.item.isSelected()) {
|
||||
ev.detail.item.trigger["ha-automation-row-selected"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _triggerChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const triggers = [...this.triggers];
|
||||
@@ -302,14 +354,21 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
static styles = [
|
||||
automationRowsStyles,
|
||||
css`
|
||||
:host([root]) .rows {
|
||||
padding-right: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
private _handleDragKeydown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Enter" || ev.key === " ") {
|
||||
ev.stopPropagation();
|
||||
this._rowSortSelected =
|
||||
this._rowSortSelected === undefined
|
||||
? (ev.target as any).index
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopSortSelection() {
|
||||
this._rowSortSelected = undefined;
|
||||
}
|
||||
|
||||
static styles = automationRowsStyles;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -49,8 +49,8 @@ const STRATEGIES = [
|
||||
{
|
||||
type: "home",
|
||||
images: {
|
||||
light: "/static/images/dashboard-options/light/icon-dashboard-areas.svg",
|
||||
dark: "/static/images/dashboard-options/dark/icon-dashboard-areas.svg",
|
||||
light: "/static/images/dashboard-options/light/icon-dashboard-home.svg",
|
||||
dark: "/static/images/dashboard-options/dark/icon-dashboard-home.svg",
|
||||
},
|
||||
name: "ui.panel.config.lovelace.dashboards.dialog_new.strategy.home.title",
|
||||
description:
|
||||
|
@@ -23,10 +23,7 @@ class ConfigNetwork extends LitElement {
|
||||
@state() private _error?: { code: string; message: string };
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this.hass.userData?.showAdvanced ||
|
||||
!isComponentLoaded(this.hass, "network")
|
||||
) {
|
||||
if (!isComponentLoaded(this.hass, "network")) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
|
@@ -384,60 +384,6 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
</ha-list-item>
|
||||
</ha-button-menu>
|
||||
<div class=${this._mode === "yaml" ? "yaml-mode" : ""}>
|
||||
<div class="error-wrapper">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.script.editor.unavailable"
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiRobotConfused}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${this._blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.confirm_take_control"
|
||||
)}
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
>${this.hass.localize("ui.common.yes")}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
>${this.hass.localize("ui.common.no")}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
? html`<ha-alert alert-type="warning" dismissable
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.script.editor.read_only"
|
||||
)}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="action"
|
||||
@click=${this._duplicate}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.migrate"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
</div>
|
||||
${this._mode === "gui"
|
||||
? html`
|
||||
<div>
|
||||
@@ -467,7 +413,68 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
@value-changed=${this._valueChanged}
|
||||
@editor-save=${this._handleSaveScript}
|
||||
@save-script=${this._handleSaveScript}
|
||||
></manual-script-editor>
|
||||
>
|
||||
<div class="alert-wrapper" slot="alerts">
|
||||
${this._errors || stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.title=${stateObj?.state === UNAVAILABLE
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.script.editor.unavailable"
|
||||
)
|
||||
: undefined}
|
||||
>
|
||||
${this._errors || this._validationErrors}
|
||||
${stateObj?.state === UNAVAILABLE
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiRobotConfused}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${this._blueprintConfig
|
||||
? html`<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.confirm_take_control"
|
||||
)}
|
||||
<div slot="action" style="display: flex;">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._takeControlSave}
|
||||
>${this.hass.localize(
|
||||
"ui.common.yes"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._revertBlueprint}
|
||||
>${this.hass.localize(
|
||||
"ui.common.no"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-alert>`
|
||||
: this._readOnly
|
||||
? html`<ha-alert
|
||||
alert-type="warning"
|
||||
dismissable
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.script.editor.read_only"
|
||||
)}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="action"
|
||||
@click=${this._duplicate}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.migrate"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
</div>
|
||||
</manual-script-editor>
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
@@ -1092,6 +1099,28 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
}
|
||||
|
||||
.alert-wrapper {
|
||||
position: sticky;
|
||||
top: -24px;
|
||||
margin-top: -24px;
|
||||
margin-bottom: 8px;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.alert-wrapper ha-alert {
|
||||
background-color: var(--card-background-color);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
margin-bottom: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
manual-script-editor {
|
||||
max-width: 1540px;
|
||||
padding: 0 12px;
|
||||
|
@@ -5,10 +5,8 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../../../common/translations/localize";
|
||||
import "../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../components/ha-automation-row";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-md-button-menu";
|
||||
import "../../../components/ha-md-menu-item";
|
||||
import type { ScriptFieldSidebarConfig } from "../../../data/automation";
|
||||
import type { Field } from "../../../data/script";
|
||||
import { SELECTOR_SELECTOR_BUILDING_BLOCKS } from "../../../data/selector/selector_selector";
|
||||
@@ -47,6 +45,12 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
@query("ha-script-field-selector-editor")
|
||||
private _selectorEditor?: HaScriptFieldSelectorEditor;
|
||||
|
||||
@query("ha-automation-row:first-of-type")
|
||||
private _fieldRowElement?: HaAutomationRow;
|
||||
|
||||
@query(".selector-row ha-automation-row")
|
||||
private _selectorRowElement?: HaAutomationRow;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-card outlined>
|
||||
@@ -198,11 +202,17 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
save: (value) => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
},
|
||||
close: () => {
|
||||
close: (focus?: boolean) => {
|
||||
if (selectorEditor) {
|
||||
this._selectorRowSelected = false;
|
||||
if (focus) {
|
||||
this.focusSelector();
|
||||
}
|
||||
} else {
|
||||
this._selected = false;
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
fireEvent(this, "close-sidebar");
|
||||
},
|
||||
@@ -221,9 +231,11 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
} satisfies ScriptFieldSidebarConfig);
|
||||
|
||||
if (this.narrow) {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -252,15 +264,19 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
});
|
||||
};
|
||||
|
||||
public focus() {
|
||||
this._fieldRowElement?.focus();
|
||||
}
|
||||
|
||||
public focusSelector() {
|
||||
this._selectorRowElement?.focus();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
indentStyle,
|
||||
css`
|
||||
ha-button-menu,
|
||||
ha-icon-button {
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
@@ -306,9 +322,6 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
ha-md-menu-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
.warning ul {
|
||||
margin: 4px 0;
|
||||
}
|
||||
@@ -324,6 +337,10 @@ export default class HaScriptFieldRow extends LitElement {
|
||||
border-color: var(--state-inactive-color);
|
||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||
}
|
||||
|
||||
.selector-row {
|
||||
padding: 12px 0 16px 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -22,7 +22,6 @@ import {
|
||||
extractSearchParam,
|
||||
removeSearchParam,
|
||||
} from "../../../common/url/search-params";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-markdown";
|
||||
import type { SidebarConfig } from "../../../data/automation";
|
||||
@@ -38,6 +37,7 @@ import { showToast } from "../../../util/toast";
|
||||
import "../automation/action/ha-automation-action";
|
||||
import type HaAutomationAction from "../automation/action/ha-automation-action";
|
||||
import "../automation/ha-automation-sidebar";
|
||||
import type HaAutomationSidebar from "../automation/ha-automation-sidebar";
|
||||
import { showPasteReplaceDialog } from "../automation/paste-replace-dialog/show-dialog-paste-replace";
|
||||
import { manualEditorStyles, saveFabStyles } from "../automation/styles";
|
||||
import "./ha-script-fields";
|
||||
@@ -69,17 +69,19 @@ export class HaManualScriptEditor extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public dirty = false;
|
||||
|
||||
@query("ha-script-fields")
|
||||
private _scriptFields?: HaScriptFields;
|
||||
|
||||
private _openFields = false;
|
||||
|
||||
@state() private _pastedConfig?: ScriptConfig;
|
||||
|
||||
@state() private _sidebarConfig?: SidebarConfig;
|
||||
|
||||
@query("ha-script-fields")
|
||||
private _scriptFields?: HaScriptFields;
|
||||
|
||||
@query("ha-automation-sidebar") private _sidebarElement?: HaAutomationSidebar;
|
||||
|
||||
private _previousConfig?: ScriptConfig;
|
||||
|
||||
private _openFields = false;
|
||||
|
||||
public addFields() {
|
||||
this._openFields = true;
|
||||
fireEvent(this, "value-changed", {
|
||||
@@ -196,36 +198,41 @@ export class HaManualScriptEditor extends LitElement {
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"split-view": true,
|
||||
"sidebar-hidden": !this._sidebarConfig,
|
||||
"has-sidebar": this._sidebarConfig && !this.narrow,
|
||||
})}
|
||||
>
|
||||
<div class="content-wrapper">
|
||||
<div class="content">${this._renderContent()}</div>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._saveScript}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<div class="content">
|
||||
<slot name="alerts"></slot>
|
||||
${this._renderContent()}
|
||||
</div>
|
||||
<div class="fab-positioner">
|
||||
<div class="fab-positioner">
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._saveScript}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-positioner">
|
||||
<ha-automation-sidebar
|
||||
tabindex="-1"
|
||||
class=${classMap({ hidden: !this._sidebarConfig })}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.hass=${this.hass}
|
||||
.config=${this._sidebarConfig}
|
||||
@value-changed=${this._sidebarConfigChanged}
|
||||
.disabled=${this.disabled}
|
||||
></ha-automation-sidebar>
|
||||
</div>
|
||||
<ha-automation-sidebar
|
||||
class=${classMap({
|
||||
sidebar: true,
|
||||
overlay: !this.isWide,
|
||||
rtl: computeRTL(this.hass),
|
||||
})}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.hass=${this.hass}
|
||||
.config=${this._sidebarConfig}
|
||||
@value-changed=${this._sidebarConfigChanged}
|
||||
.disabled=${this.disabled}
|
||||
></ha-automation-sidebar>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -452,10 +459,13 @@ export class HaManualScriptEditor extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _openSidebar(ev: CustomEvent<SidebarConfig>) {
|
||||
private async _openSidebar(ev: CustomEvent<SidebarConfig>) {
|
||||
// deselect previous selected row
|
||||
this._sidebarConfig?.close?.();
|
||||
this._sidebarConfig = ev.detail;
|
||||
|
||||
await this._sidebarElement?.updateComplete;
|
||||
this._sidebarElement?.focus();
|
||||
}
|
||||
|
||||
private _sidebarConfigChanged(ev: CustomEvent<{ value: SidebarConfig }>) {
|
||||
@@ -522,6 +532,10 @@ export class HaManualScriptEditor extends LitElement {
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -327,8 +327,7 @@ class HaConfigSectionStorage extends LitElement {
|
||||
>${roundWithOneDecimal(freeSpaceGB)} GB</span
|
||||
>`,
|
||||
});
|
||||
const chart = html`
|
||||
<ha-segmented-bar
|
||||
return html`<ha-segmented-bar
|
||||
.heading=${this.hass.localize("ui.panel.config.storage.used_space")}
|
||||
.description=${this.hass.localize(
|
||||
"ui.panel.config.storage.detailed_description",
|
||||
@@ -339,17 +338,15 @@ class HaConfigSectionStorage extends LitElement {
|
||||
)}
|
||||
.segments=${segments}
|
||||
></ha-segmented-bar>
|
||||
`;
|
||||
return storageInfo || storageInfo === null
|
||||
? chart
|
||||
: html`
|
||||
<div class="loading-container">
|
||||
${chart}
|
||||
<div class="loading-overlay">
|
||||
<ha-spinner></ha-spinner>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
${!storageInfo || storageInfo === null
|
||||
? html`<ha-alert alert-type="info">
|
||||
<ha-spinner slot="icon"></ha-spinner>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.storage.loading_detailed"
|
||||
)}</ha-alert
|
||||
>`
|
||||
: nothing}`;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -522,6 +519,10 @@ class HaConfigSectionStorage extends LitElement {
|
||||
ha-icon-next {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
ha-alert ha-spinner {
|
||||
--ha-spinner-size: 24px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -239,6 +239,7 @@ export type LovelaceCardFeatureConfig =
|
||||
| FanOscillateCardFeatureConfig
|
||||
| FanPresetModesCardFeatureConfig
|
||||
| FanSpeedCardFeatureConfig
|
||||
| HistoryChartCardFeatureConfig
|
||||
| HumidifierToggleCardFeatureConfig
|
||||
| HumidifierModesCardFeatureConfig
|
||||
| LawnMowerCommandsCardFeatureConfig
|
||||
|
@@ -15,6 +15,7 @@ import type { HomeAssistant } from "../../../../../types";
|
||||
import { supportsAlarmModesCardFeature } from "../../../card-features/hui-alarm-modes-card-feature";
|
||||
import { supportsCoverOpenCloseCardFeature } from "../../../card-features/hui-cover-open-close-card-feature";
|
||||
import { supportsFanSpeedCardFeature } from "../../../card-features/hui-fan-speed-card-feature";
|
||||
import { supportsHistoryChartCardFeature } from "../../../card-features/hui-history-chart-card-feature";
|
||||
import { supportsLightBrightnessCardFeature } from "../../../card-features/hui-light-brightness-card-feature";
|
||||
import { supportsLockCommandsCardFeature } from "../../../card-features/hui-lock-commands-card-feature";
|
||||
import { supportsTargetTemperatureCardFeature } from "../../../card-features/hui-target-temperature-card-feature";
|
||||
@@ -237,7 +238,12 @@ export const computeAreaTileCardConfig =
|
||||
|
||||
let feature: LovelaceCardFeatureConfig | undefined;
|
||||
if (includeFeature) {
|
||||
if (supportsLightBrightnessCardFeature(hass, context)) {
|
||||
if (supportsHistoryChartCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "history-chart",
|
||||
hours_to_show: 24,
|
||||
};
|
||||
} else if (supportsLightBrightnessCardFeature(hass, context)) {
|
||||
feature = {
|
||||
type: "light-brightness",
|
||||
};
|
||||
|
@@ -29,8 +29,12 @@ export class HuiHomeDashboardStrategyEditor
|
||||
<ha-entities-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._config.favorite_entities || []}
|
||||
label="Favorite entities"
|
||||
placeholder="Add favorite entity"
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.strategy.home.favorite_entities"
|
||||
)}
|
||||
placeholder=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.strategy.home.add_favorite_entity"
|
||||
)}
|
||||
reorder
|
||||
allow-custom-entity
|
||||
@value-changed=${this._valueChanged}
|
||||
|
@@ -2,6 +2,7 @@ import type {
|
||||
EntityFilter,
|
||||
EntityFilterFunc,
|
||||
} from "../../../../../common/entity/entity_filter";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
|
||||
export const HOME_SUMMARIES = [
|
||||
"lights",
|
||||
@@ -10,16 +11,16 @@ export const HOME_SUMMARIES = [
|
||||
"media_players",
|
||||
] as const;
|
||||
|
||||
export type HomeSummaries = (typeof HOME_SUMMARIES)[number];
|
||||
export type HomeSummary = (typeof HOME_SUMMARIES)[number];
|
||||
|
||||
export const HOME_SUMMARIES_ICONS: Record<HomeSummaries, string> = {
|
||||
export const HOME_SUMMARIES_ICONS: Record<HomeSummary, string> = {
|
||||
lights: "mdi:lamps",
|
||||
climate: "mdi:home-thermometer",
|
||||
security: "mdi:security",
|
||||
media_players: "mdi:multimedia",
|
||||
};
|
||||
|
||||
export const HOME_SUMMARIES_FILTERS: Record<HomeSummaries, EntityFilter[]> = {
|
||||
export const HOME_SUMMARIES_FILTERS: Record<HomeSummary, EntityFilter[]> = {
|
||||
lights: [{ domain: "light", entity_category: "none" }],
|
||||
climate: [
|
||||
{ domain: "climate", entity_category: "none" },
|
||||
@@ -65,7 +66,7 @@ export const HOME_SUMMARIES_FILTERS: Record<HomeSummaries, EntityFilter[]> = {
|
||||
},
|
||||
{
|
||||
domain: "binary_sensor",
|
||||
device_class: ["door", "garage_door"],
|
||||
device_class: ["door", "garage_door", "motion"],
|
||||
entity_category: "none",
|
||||
},
|
||||
],
|
||||
@@ -90,3 +91,6 @@ export const findEntities = (
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const getSummaryLabel = (localize: LocalizeFunc, summary: HomeSummary) =>
|
||||
localize(`ui.panel.lovelace.strategy.home.summary_list.${summary}`);
|
||||
|
@@ -14,10 +14,11 @@ import type { HeadingCardConfig } from "../../cards/types";
|
||||
import { computeAreaTileCardConfig } from "../areas/helpers/areas-strategy-helper";
|
||||
import {
|
||||
findEntities,
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES,
|
||||
HOME_SUMMARIES_FILTERS,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
type HomeSummaries,
|
||||
type HomeSummary,
|
||||
} from "./helpers/home-summaries";
|
||||
|
||||
export interface HomeAreaViewStrategyConfig {
|
||||
@@ -96,7 +97,7 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
acc[summary] = findEntities(areaEntities, filterFunctions);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<HomeSummaries, string[]>
|
||||
{} as Record<HomeSummary, string[]>
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -110,7 +111,11 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
sections.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
computeHeadingCard("Lights", HOME_SUMMARIES_ICONS.lights, "lights"),
|
||||
computeHeadingCard(
|
||||
getSummaryLabel(hass.localize, "lights"),
|
||||
HOME_SUMMARIES_ICONS.lights,
|
||||
"lights"
|
||||
),
|
||||
...lights.map(computeTileCard),
|
||||
],
|
||||
});
|
||||
@@ -121,7 +126,7 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
type: "grid",
|
||||
cards: [
|
||||
computeHeadingCard(
|
||||
"Climate",
|
||||
getSummaryLabel(hass.localize, "climate"),
|
||||
HOME_SUMMARIES_ICONS.climate,
|
||||
"climate"
|
||||
),
|
||||
@@ -135,7 +140,7 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
type: "grid",
|
||||
cards: [
|
||||
computeHeadingCard(
|
||||
"Security",
|
||||
getSummaryLabel(hass.localize, "security"),
|
||||
HOME_SUMMARIES_ICONS.security,
|
||||
"security"
|
||||
),
|
||||
@@ -149,7 +154,7 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
type: "grid",
|
||||
cards: [
|
||||
computeHeadingCard(
|
||||
"Media players",
|
||||
getSummaryLabel(hass.localize, "media_players"),
|
||||
HOME_SUMMARIES_ICONS.media_players,
|
||||
"media-players"
|
||||
),
|
||||
@@ -229,9 +234,11 @@ export class HomeAreaViewStrategy extends ReactiveElement {
|
||||
const device = hass.devices[deviceId];
|
||||
let heading = "";
|
||||
if (device) {
|
||||
heading = computeDeviceName(device) || "Unnamed device";
|
||||
heading =
|
||||
computeDeviceName(device) ||
|
||||
hass.localize("ui.panel.lovelace.strategy.home.unamed_device");
|
||||
} else {
|
||||
heading = "Others";
|
||||
heading = hass.localize("ui.panel.lovelace.strategy.home.others");
|
||||
}
|
||||
|
||||
deviceSections.push({
|
||||
|
@@ -23,6 +23,7 @@ const processAreasForClimate = (
|
||||
entities: string[]
|
||||
): LovelaceCardConfig[] => {
|
||||
const cards: LovelaceCardConfig[] = [];
|
||||
const computeTileCard = computeAreaTileCardConfig(hass, "", true);
|
||||
|
||||
for (const areaId of areaIds) {
|
||||
const area = hass.areas[areaId];
|
||||
@@ -33,8 +34,6 @@ const processAreasForClimate = (
|
||||
});
|
||||
const areaEntities = entities.filter(areaFilter);
|
||||
|
||||
const computeTileCard = computeAreaTileCardConfig(hass, "", true);
|
||||
|
||||
if (areaEntities.length > 0) {
|
||||
cards.push({
|
||||
heading_style: "subtitle",
|
||||
@@ -46,6 +45,13 @@ const processAreasForClimate = (
|
||||
},
|
||||
});
|
||||
|
||||
if (hass.areas[areaId].temperature_entity_id) {
|
||||
cards.push(computeTileCard(hass.areas[areaId].temperature_entity_id));
|
||||
}
|
||||
if (hass.areas[areaId].humidity_entity_id) {
|
||||
cards.push(computeTileCard(hass.areas[areaId].humidity_entity_id));
|
||||
}
|
||||
|
||||
for (const entityId of areaEntities) {
|
||||
cards.push(computeTileCard(entityId));
|
||||
}
|
||||
@@ -89,7 +95,10 @@ export class HomeClimateViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? floor.name : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? floor.name
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -110,7 +119,10 @@ export class HomeClimateViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? "Other areas" : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? hass.localize("ui.panel.lovelace.strategy.home.other_areas")
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -6,7 +6,10 @@ import type { LovelaceViewRawConfig } from "../../../../data/lovelace/config/vie
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { getAreas } from "../areas/helpers/areas-strategy-helper";
|
||||
import type { LovelaceStrategyEditor } from "../types";
|
||||
import { HOME_SUMMARIES_ICONS } from "./helpers/home-summaries";
|
||||
import {
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
} from "./helpers/home-summaries";
|
||||
import type { HomeAreaViewStrategyConfig } from "./home-area-view-strategy";
|
||||
import type { HomeMainViewStrategyConfig } from "./home-main-view-strategy";
|
||||
|
||||
@@ -60,7 +63,7 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
});
|
||||
|
||||
const lightView = {
|
||||
title: "Lights",
|
||||
title: getSummaryLabel(hass.localize, "lights"),
|
||||
path: "lights",
|
||||
subview: true,
|
||||
strategy: {
|
||||
@@ -70,7 +73,7 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
} satisfies LovelaceViewRawConfig;
|
||||
|
||||
const climateView = {
|
||||
title: "Climate",
|
||||
title: getSummaryLabel(hass.localize, "climate"),
|
||||
path: "climate",
|
||||
subview: true,
|
||||
strategy: {
|
||||
@@ -80,7 +83,7 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
} satisfies LovelaceViewRawConfig;
|
||||
|
||||
const securityView = {
|
||||
title: "Security",
|
||||
title: getSummaryLabel(hass.localize, "security"),
|
||||
path: "security",
|
||||
subview: true,
|
||||
strategy: {
|
||||
@@ -90,7 +93,7 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
} satisfies LovelaceViewRawConfig;
|
||||
|
||||
const mediaPlayersView = {
|
||||
title: "Media players",
|
||||
title: getSummaryLabel(hass.localize, "media_players"),
|
||||
path: "media-players",
|
||||
subview: true,
|
||||
strategy: {
|
||||
|
@@ -89,7 +89,10 @@ export class HomeLightsViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? floor.name : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? floor.name
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -110,7 +113,10 @@ export class HomeLightsViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? "Other areas" : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? hass.localize("ui.panel.lovelace.strategy.home.other_areas")
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -15,7 +15,10 @@ import type {
|
||||
WeatherForecastCardConfig,
|
||||
} from "../../cards/types";
|
||||
import { getAreas } from "../areas/helpers/areas-strategy-helper";
|
||||
import { HOME_SUMMARIES_ICONS } from "./helpers/home-summaries";
|
||||
import {
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
} from "./helpers/home-summaries";
|
||||
|
||||
export interface HomeMainViewStrategyConfig {
|
||||
type: "home-main";
|
||||
@@ -63,7 +66,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
{
|
||||
type: "heading",
|
||||
heading_style: "title",
|
||||
heading: "Areas",
|
||||
heading: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
...areas.map<AreaCardConfig>((area) =>
|
||||
computeAreaCard(area.area_id, hass)
|
||||
@@ -96,6 +99,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
({
|
||||
type: "tile",
|
||||
entity: entityId,
|
||||
show_entity_picture: true,
|
||||
}) as TileCardConfig
|
||||
)
|
||||
);
|
||||
@@ -107,12 +111,12 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: "Summaries",
|
||||
heading: hass.localize("ui.panel.lovelace.strategy.home.summaries"),
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.lights,
|
||||
name: "Lights",
|
||||
name: getSummaryLabel(hass.localize, "lights"),
|
||||
icon_height: "24px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
@@ -126,7 +130,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.climate,
|
||||
name: "Climate",
|
||||
name: getSummaryLabel(hass.localize, "climate"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
@@ -140,7 +144,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.security,
|
||||
name: "Security",
|
||||
name: getSummaryLabel(hass.localize, "security"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
@@ -154,7 +158,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.media_players,
|
||||
name: "Media Players",
|
||||
name: getSummaryLabel(hass.localize, "media_players"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
@@ -231,7 +235,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
card: {
|
||||
type: "markdown",
|
||||
text_only: true,
|
||||
content: "## Welcome {{user}}",
|
||||
content: `## ${hass.localize("ui.panel.lovelace.strategy.home.welcome_user", { user: "{{ user }}" })}`,
|
||||
} satisfies MarkdownCardConfig,
|
||||
},
|
||||
};
|
||||
|
@@ -87,7 +87,10 @@ export class HomeMMediaPlayersViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? floor.name : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? floor.name
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -108,7 +111,10 @@ export class HomeMMediaPlayersViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? "Other areas" : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? hass.localize("ui.panel.lovelace.strategy.home.other_areas")
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
} from "../areas/helpers/areas-strategy-helper";
|
||||
import { getHomeStructure } from "./helpers/home-structure";
|
||||
import { findEntities, HOME_SUMMARIES_FILTERS } from "./helpers/home-summaries";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
|
||||
export interface HomeSecurityViewStrategyConfig {
|
||||
type: "home-security";
|
||||
@@ -23,6 +24,8 @@ const processAreasForSecurity = (
|
||||
entities: string[]
|
||||
): LovelaceCardConfig[] => {
|
||||
const cards: LovelaceCardConfig[] = [];
|
||||
const computeTileCard = computeAreaTileCardConfig(hass, "", false);
|
||||
const computeTileCardWithFeature = computeAreaTileCardConfig(hass, "", true);
|
||||
|
||||
for (const areaId of areaIds) {
|
||||
const area = hass.areas[areaId];
|
||||
@@ -33,8 +36,6 @@ const processAreasForSecurity = (
|
||||
});
|
||||
const areaEntities = entities.filter(areaFilter);
|
||||
|
||||
const computeTileCard = computeAreaTileCardConfig(hass, "", false);
|
||||
|
||||
if (areaEntities.length > 0) {
|
||||
cards.push({
|
||||
heading_style: "subtitle",
|
||||
@@ -47,7 +48,12 @@ const processAreasForSecurity = (
|
||||
});
|
||||
|
||||
for (const entityId of areaEntities) {
|
||||
cards.push(computeTileCard(entityId));
|
||||
cards.push(
|
||||
computeDomain(entityId) === "binary_sensor" &&
|
||||
hass.states[entityId]?.attributes.device_class === "motion"
|
||||
? computeTileCardWithFeature(entityId)
|
||||
: computeTileCard(entityId)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +95,10 @@ export class HomeSecurityViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? floor.name : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? floor.name
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -110,7 +119,10 @@ export class HomeSecurityViewStrategy extends ReactiveElement {
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: floorCount > 1 ? "Other areas" : "Areas",
|
||||
heading:
|
||||
floorCount > 1
|
||||
? hass.localize("ui.panel.lovelace.strategy.home.other_areas")
|
||||
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -453,7 +453,7 @@
|
||||
"media_content_id": "Media content ID",
|
||||
"media_content_type": "Media content type",
|
||||
"media_content_id_detail": "The ID of the content to play. Platform dependent.",
|
||||
"media_content_type_detail": "The type of the content to play, such as image, music, tv show, video, episode, channel, or playlist."
|
||||
"media_content_type_detail": "The type of the content to play, such as image, music, TV show, video, episode, channel, or playlist."
|
||||
},
|
||||
"file": {
|
||||
"upload_failed": "Upload failed",
|
||||
@@ -3850,7 +3850,7 @@
|
||||
"triggers": {
|
||||
"name": "Triggers",
|
||||
"header": "When",
|
||||
"description": "A trigger is a specific event happening in or around your home, for example: ''When the sun sets''. Any trigger listed here will start your automation.",
|
||||
"description": "A trigger is a specific event happening in or around your home, for example: ''When the sun sets''. Any trigger added here will start your automation.",
|
||||
"learn_more": "Learn more about triggers",
|
||||
"triggered": "Triggered",
|
||||
"add": "Add trigger",
|
||||
@@ -4110,7 +4110,7 @@
|
||||
"conditions": {
|
||||
"name": "Conditions",
|
||||
"header": "And if",
|
||||
"description": "This list of conditions needs to be satisfied for the automation to run. A condition can be satisfied or not at any given time, for example: ''If {user} is home''. You can use building blocks to create more complex conditions.",
|
||||
"description": "All conditions added here need to be satisfied for the automation to run. A condition can be satisfied or not at any given time, for example: ''If {user} is home''. You can use building blocks to create more complex conditions.",
|
||||
"learn_more": "Learn more about conditions",
|
||||
"add": "Add condition",
|
||||
"search": "Search condition",
|
||||
@@ -4277,7 +4277,7 @@
|
||||
"actions": {
|
||||
"name": "Actions",
|
||||
"header": "Then do",
|
||||
"description": "This list of actions will be performed in sequence when the automation runs. An action usually controls one of your areas, devices, or entities, for example: 'Turn on the lights'. You can use building blocks to create more complex sequences of actions.",
|
||||
"description": "All actions added here will be performed in sequence when the automation runs. An action usually controls one of your areas, devices, or entities, for example: 'Turn on the lights'. You can use building blocks to create more complex sequences of actions.",
|
||||
"learn_more": "Learn more about actions",
|
||||
"add": "Add action",
|
||||
"search": "Search action",
|
||||
@@ -5447,8 +5447,8 @@
|
||||
"config_entry": {
|
||||
"application_credentials": {
|
||||
"delete_title": "Application credentials",
|
||||
"delete_prompt": "Would you like to also delete Application Credentials for this integration?",
|
||||
"delete_detail": "If you delete them, you will need to enter credentials when setting up the integration again. If you keep them, they will be used automatically when setting up the integration again or may be accessed from the Application Credentials menu.",
|
||||
"delete_prompt": "Would you like to also delete application credentials for this integration?",
|
||||
"delete_detail": "If you delete them, you will need to enter credentials when setting up the integration again. If you keep them, they will be used automatically when setting up the integration again or may be accessed from the Application credentials menu.",
|
||||
"delete_error_title": "Deleting application credentials failed",
|
||||
"dismiss": "Keep",
|
||||
"learn_more": "Learn more about application credentials"
|
||||
@@ -5647,10 +5647,10 @@
|
||||
},
|
||||
"application_credentials": {
|
||||
"caption": "Application credentials",
|
||||
"description": "Manage the OAuth application credentials used by Integrations",
|
||||
"description": "Manage the OAuth application credentials used by integrations",
|
||||
"editor": {
|
||||
"caption": "Add credentials",
|
||||
"description": "OAuth is used to grant Home Assistant access to information on other websites without giving a password. This mechanism is used by companies such as Spotify, Google, Withings, Microsoft, and Twitter.",
|
||||
"caption": "Add application credentials",
|
||||
"description": "OAuth is used to grant Home Assistant secure delegated access to information on other websites without revealing your personal credentials.",
|
||||
"missing_credentials": "Setting up {integration} requires configuring application credentials.",
|
||||
"missing_credentials_domain_link": "View {integration} documentation",
|
||||
"view_documentation": "View application credentials documentation",
|
||||
@@ -6571,7 +6571,7 @@
|
||||
"in_progress": "We're communicating with the device. This may take some time.",
|
||||
"failed": "The command failed. Additional information may be available in the logs.",
|
||||
"success": "Your device is ready to be added to another Matter platform.",
|
||||
"scan_code": "With their app, scan the QR code or enter the sharing code below to finish set up.",
|
||||
"scan_code": "With their app, scan the QR code or enter the sharing code below to finish setup.",
|
||||
"copy_code": "Copy code"
|
||||
}
|
||||
},
|
||||
@@ -6664,6 +6664,7 @@
|
||||
"ip_information": "IP information",
|
||||
"adapter": {
|
||||
"auto_configure": "Autoconfigure",
|
||||
"auto_configure_manual_hint": "Uncheck to manually select network adapters",
|
||||
"detected": "Detected",
|
||||
"adapter": "Adapter"
|
||||
}
|
||||
@@ -6673,6 +6674,7 @@
|
||||
"description": "{percent_used} used - {free_space} free",
|
||||
"used_space": "Storage",
|
||||
"detailed_description": "{used} of {total} used",
|
||||
"loading_detailed": "Loading detailed storage information...",
|
||||
"segments": {
|
||||
"used": "Used space",
|
||||
"free": "Free space",
|
||||
@@ -6815,6 +6817,20 @@
|
||||
},
|
||||
"other_areas": "Other areas",
|
||||
"areas": "Areas"
|
||||
},
|
||||
"home": {
|
||||
"summary_list": {
|
||||
"climate": "Climate",
|
||||
"lights": "Lights",
|
||||
"security": "Security",
|
||||
"media_players": "Media players"
|
||||
},
|
||||
"welcome_user": "Welcome {user}",
|
||||
"summaries": "Summaries",
|
||||
"areas": "Areas",
|
||||
"other_areas": "Other areas",
|
||||
"unamed_device": "Unnamed device",
|
||||
"others": "Others"
|
||||
}
|
||||
},
|
||||
"cards": {
|
||||
@@ -8174,6 +8190,10 @@
|
||||
"no_entities": "No entities in this group, the section will not be displayed",
|
||||
"use_compact_card": "Use compact card",
|
||||
"use_large_card": "Use large card"
|
||||
},
|
||||
"home": {
|
||||
"favorite_entities": "Favorite entities",
|
||||
"add_favorite_entity": "Add favorite entity"
|
||||
}
|
||||
},
|
||||
"view": {
|
||||
|
Reference in New Issue
Block a user