Web card configuration visual modernization
This commit is contained in:
parent
ff244641e8
commit
07ec766383
1 changed files with 240 additions and 153 deletions
|
|
@ -527,7 +527,7 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
const zonesHtml = Object.entries(this._config.zones)
|
const zonesHtml = Object.entries(this._config.zones)
|
||||||
.map(([state, config], idx) => `
|
.map(([state, config], idx) => `
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<div class="input-wrapper" style="flex: 1;">
|
<div class="input-wrapper">
|
||||||
<label>State Name</label>
|
<label>State Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -538,9 +538,9 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
data-zone-field="state"
|
data-zone-field="state"
|
||||||
placeholder="home">
|
placeholder="home">
|
||||||
</div>
|
</div>
|
||||||
<div class="input-wrapper" style="width: 80px;">
|
<div class="input-wrapper" style="flex: 0 0 100px;">
|
||||||
<label>Color</label>
|
<label>Color</label>
|
||||||
<input type="color" value="${config.color}" data-zone-idx="${idx}" data-zone-field="color" style="width: 100%; height: 40px; border-radius: 4px; border: 1px solid var(--divider-color); cursor: pointer;">
|
<input type="color" value="${config.color}" data-zone-idx="${idx}" data-zone-field="color" class="entity-input" style="height: 40px; padding: 4px;">
|
||||||
</div>
|
</div>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
data-zone-delete="${idx}">
|
data-zone-delete="${idx}">
|
||||||
|
|
@ -636,7 +636,7 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
const entitiesHtml = this._config.entities
|
const entitiesHtml = this._config.entities
|
||||||
.map((entity, idx) => `
|
.map((entity, idx) => `
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<div class="input-wrapper" style="flex: 1;">
|
<div class="input-wrapper">
|
||||||
<label>Person Entity</label>
|
<label>Person Entity</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -648,7 +648,7 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
placeholder="person.example"
|
placeholder="person.example"
|
||||||
list="person-entities-list">
|
list="person-entities-list">
|
||||||
</div>
|
</div>
|
||||||
<div class="input-wrapper" style="flex: 1;">
|
<div class="input-wrapper">
|
||||||
<label>Activity Sensor</label>
|
<label>Activity Sensor</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -670,58 +670,75 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
this.innerHTML = `
|
this.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
.config-container {
|
.config-container {
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
max-width: 800px;
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
.config-header {
|
.config-header {
|
||||||
font-size: 20px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 32px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
padding-bottom: 12px;
|
padding-bottom: 16px;
|
||||||
border-bottom: 2px solid var(--divider-color);
|
border-bottom: 3px solid var(--primary-color);
|
||||||
}
|
}
|
||||||
.config-row {
|
.config-row {
|
||||||
margin: 20px 0;
|
margin: 24px 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
.config-row ha-textfield,
|
.config-row ha-textfield,
|
||||||
.config-row ha-select {
|
.config-row ha-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
--mdc-theme-primary: var(--primary-color);
|
||||||
}
|
}
|
||||||
.config-note {
|
.config-note {
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
margin-top: 6px;
|
margin-top: 8px;
|
||||||
font-style: italic;
|
padding: 8px 12px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid var(--primary-color);
|
||||||
}
|
}
|
||||||
.config-section {
|
.config-section {
|
||||||
margin: 32px 0;
|
margin: 32px 0;
|
||||||
padding: 20px;
|
padding: 24px;
|
||||||
background: var(--card-background-color);
|
background: var(--card-background-color);
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
border: 1px solid var(--divider-color);
|
border: 2px solid var(--divider-color);
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.config-section:hover {
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||||
}
|
}
|
||||||
.config-section-header {
|
.config-section-header {
|
||||||
font-size: 16px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 24px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
display: flex;
|
padding-bottom: 12px;
|
||||||
align-items: center;
|
border-bottom: 2px solid var(--divider-color);
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.config-section-header ha-icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
.config-item {
|
.config-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--secondary-background-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
.config-item:hover {
|
||||||
|
background: var(--divider-color);
|
||||||
}
|
}
|
||||||
.add-button {
|
.add-button {
|
||||||
margin-top: 12px;
|
margin-top: 16px;
|
||||||
|
width: 100%;
|
||||||
|
--mdc-theme-primary: var(--primary-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
ha-icon-button[data-entity-delete],
|
ha-icon-button[data-entity-delete],
|
||||||
ha-icon-button[data-zone-delete],
|
ha-icon-button[data-zone-delete],
|
||||||
|
|
@ -733,73 +750,119 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
.input-wrapper label {
|
.input-wrapper label {
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
margin-bottom: 4px;
|
margin-bottom: 6px;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
.entity-input {
|
.entity-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 12px;
|
max-width: 100%;
|
||||||
border: 1px solid var(--divider-color);
|
box-sizing: border-box;
|
||||||
border-radius: 4px;
|
padding: 10px 14px;
|
||||||
|
border: 2px solid var(--divider-color);
|
||||||
|
border-radius: 8px;
|
||||||
background: var(--card-background-color);
|
background: var(--card-background-color);
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: border-color 0.2s;
|
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
}
|
}
|
||||||
.entity-input:focus {
|
.entity-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(var(--rgb-primary-color), 0.1);
|
||||||
|
}
|
||||||
|
.entity-input:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.entity-input::placeholder {
|
.entity-input::placeholder {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
opacity: 0.5;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.radius-slider {
|
.radius-slider {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
height: 6px;
|
width: 100%;
|
||||||
border-radius: 3px;
|
height: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
background: var(--divider-color);
|
background: var(--divider-color);
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: background 0.2s;
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
.radius-slider:hover {
|
.radius-slider::-webkit-slider-runnable-track {
|
||||||
background: var(--secondary-text-color);
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--divider-color);
|
||||||
|
}
|
||||||
|
.radius-slider::-moz-range-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--divider-color);
|
||||||
}
|
}
|
||||||
.radius-slider::-webkit-slider-thumb {
|
.radius-slider::-webkit-slider-thumb {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 18px;
|
width: 20px;
|
||||||
height: 18px;
|
height: 20px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
cursor: pointer;
|
cursor: grab;
|
||||||
transition: transform 0.2s;
|
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
||||||
|
margin-top: -6px;
|
||||||
}
|
}
|
||||||
.radius-slider::-webkit-slider-thumb:hover {
|
.radius-slider::-webkit-slider-thumb:active {
|
||||||
transform: scale(1.2);
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
.radius-slider::-moz-range-thumb {
|
.radius-slider::-moz-range-thumb {
|
||||||
width: 18px;
|
width: 20px;
|
||||||
height: 18px;
|
height: 20px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
cursor: pointer;
|
cursor: grab;
|
||||||
border: none;
|
border: none;
|
||||||
transition: transform 0.2s;
|
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
.radius-slider::-moz-range-thumb:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern toggle button for API key */
|
||||||
|
#toggle-api-key-visibility {
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
#toggle-api-key-visibility:hover {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid layout improvements */
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
.config-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
.radius-slider::-moz-range-thumb:hover {
|
|
||||||
transform: scale(1.2);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="config-container">
|
<div class="config-container">
|
||||||
<div class="config-header">Map Badge Card Configuration</div>
|
<div class="config-header">
|
||||||
|
Map Badge Card Configuration
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<div class="config-section-header">
|
||||||
|
Map Provider Settings
|
||||||
|
</div>
|
||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<ha-select
|
<ha-select
|
||||||
id="map_provider"
|
id="map_provider"
|
||||||
|
|
@ -812,12 +875,20 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-row" id="google-api-key-row" style="display: ${this._config.map_provider === 'google' ? 'block' : 'none'}">
|
<div class="config-row" id="google-api-key-row" style="display: ${this._config.map_provider === 'google' ? 'block' : 'none'}">
|
||||||
|
<div style="position: relative;">
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
id="google_api_key"
|
id="google_api_key"
|
||||||
label="Google API Key"
|
label="Google API Key"
|
||||||
value="${this._config.google_api_key || ''}"
|
value="${this._config.google_api_key || ''}"
|
||||||
placeholder="AIzaSy...">
|
placeholder="AIzaSy..."
|
||||||
|
type="password">
|
||||||
</ha-textfield>
|
</ha-textfield>
|
||||||
|
<ha-icon-button
|
||||||
|
id="toggle-api-key-visibility"
|
||||||
|
style="position: absolute; right: 0; top: 8px;">
|
||||||
|
<ha-icon icon="mdi:eye"></ha-icon>
|
||||||
|
</ha-icon-button>
|
||||||
|
</div>
|
||||||
<div class="config-note">Required only for Google Maps - Get your API key from Google Cloud Console</div>
|
<div class="config-note">Required only for Google Maps - Get your API key from Google Cloud Console</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -856,21 +927,25 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
</ha-textfield>
|
</ha-textfield>
|
||||||
<div class="config-note">How often to refresh location data</div>
|
<div class="config-note">How often to refresh location data</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<div class="config-section-header">
|
||||||
|
Appearance Settings
|
||||||
|
</div>
|
||||||
<div class="config-row">
|
<div class="config-row">
|
||||||
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--primary-text-color);">
|
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--primary-text-color);">
|
||||||
Marker Border Radius
|
Marker Border Radius
|
||||||
</label>
|
</label>
|
||||||
<div style="display: flex; align-items: center; gap: 12px;">
|
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
id="marker_border_radius"
|
id="marker_border_radius"
|
||||||
min="0"
|
min="0"
|
||||||
max="50"
|
max="50"
|
||||||
value="${parseInt(this._config.marker_border_radius) || 50}"
|
value="${parseInt(this._config.marker_border_radius) || 50}"
|
||||||
class="radius-slider"
|
class="radius-slider">
|
||||||
style="flex: 1;">
|
<span id="marker-radius-value" style="min-width: 50px; text-align: right; font-weight: 600;">${this._config.marker_border_radius || '50%'}</span>
|
||||||
<span id="marker-radius-value" style="min-width: 40px; text-align: right; font-weight: 600;">${this._config.marker_border_radius || '50%'}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="config-note">0% for square, 50% for circle</div>
|
<div class="config-note">0% for square, 50% for circle</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -879,49 +954,44 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--primary-text-color);">
|
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--primary-text-color);">
|
||||||
Badge Border Radius
|
Badge Border Radius
|
||||||
</label>
|
</label>
|
||||||
<div style="display: flex; align-items: center; gap: 12px;">
|
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
id="badge_border_radius"
|
id="badge_border_radius"
|
||||||
min="0"
|
min="0"
|
||||||
max="50"
|
max="50"
|
||||||
value="${parseInt(this._config.badge_border_radius) || 50}"
|
value="${parseInt(this._config.badge_border_radius) || 50}"
|
||||||
class="radius-slider"
|
class="radius-slider">
|
||||||
style="flex: 1;">
|
<span id="badge-radius-value" style="min-width: 50px; text-align: right; font-weight: 600;">${this._config.badge_border_radius || '50%'}</span>
|
||||||
<span id="badge-radius-value" style="min-width: 40px; text-align: right; font-weight: 600;">${this._config.badge_border_radius || '50%'}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="config-note">0% for square, 50% for circle</div>
|
<div class="config-note">0% for square, 50% for circle</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="config-section-header">
|
<div class="config-section-header">
|
||||||
<ha-icon icon="mdi:account-multiple"></ha-icon>
|
|
||||||
Entities
|
Entities
|
||||||
</div>
|
</div>
|
||||||
<div id="entities-container">${entitiesHtml}</div>
|
<div id="entities-container">${entitiesHtml}</div>
|
||||||
${personDatalist}
|
${personDatalist}
|
||||||
${sensorDatalist}
|
${sensorDatalist}
|
||||||
<ha-button class="add-button" id="add-entity">
|
<ha-button class="add-button" id="add-entity">
|
||||||
<ha-icon icon="mdi:plus" slot="icon"></ha-icon>
|
|
||||||
Add Entity
|
Add Entity
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="config-section-header">
|
<div class="config-section-header">
|
||||||
<ha-icon icon="mdi:map-marker"></ha-icon>
|
|
||||||
Zone Configuration
|
Zone Configuration
|
||||||
</div>
|
</div>
|
||||||
<div id="zones-container">${zonesHtml}</div>
|
<div id="zones-container">${zonesHtml}</div>
|
||||||
<ha-button class="add-button" id="add-zone">
|
<ha-button class="add-button" id="add-zone">
|
||||||
<ha-icon icon="mdi:plus" slot="icon"></ha-icon>
|
|
||||||
Add Zone
|
Add Zone
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<div class="config-section-header">
|
<div class="config-section-header">
|
||||||
<ha-icon icon="mdi:walk"></ha-icon>
|
|
||||||
Activity Configuration
|
Activity Configuration
|
||||||
</div>
|
</div>
|
||||||
<div id="activities-container">${activitiesHtml}</div>
|
<div id="activities-container">${activitiesHtml}</div>
|
||||||
|
|
@ -956,6 +1026,17 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
this._fireChanged();
|
this._fireChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle API key visibility
|
||||||
|
this.querySelector('#toggle-api-key-visibility')?.addEventListener('click', (e) => {
|
||||||
|
const apiKeyField = this.querySelector('#google_api_key');
|
||||||
|
const toggleIcon = e.currentTarget.querySelector('ha-icon');
|
||||||
|
if (apiKeyField) {
|
||||||
|
const isPassword = apiKeyField.getAttribute('type') === 'password';
|
||||||
|
apiKeyField.setAttribute('type', isPassword ? 'text' : 'password');
|
||||||
|
toggleIcon.setAttribute('icon', isPassword ? 'mdi:eye-off' : 'mdi:eye');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Basic config - using 'change' event for ha-textfield components
|
// Basic config - using 'change' event for ha-textfield components
|
||||||
this.querySelector('#google_api_key')?.addEventListener('change', (e) => {
|
this.querySelector('#google_api_key')?.addEventListener('change', (e) => {
|
||||||
this._config.google_api_key = e.target.value;
|
this._config.google_api_key = e.target.value;
|
||||||
|
|
@ -980,6 +1061,9 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
markerSlider?.addEventListener('input', (e) => {
|
markerSlider?.addEventListener('input', (e) => {
|
||||||
const value = e.target.value + '%';
|
const value = e.target.value + '%';
|
||||||
markerValueDisplay.textContent = value;
|
markerValueDisplay.textContent = value;
|
||||||
|
});
|
||||||
|
markerSlider?.addEventListener('change', (e) => {
|
||||||
|
const value = e.target.value + '%';
|
||||||
this._config.marker_border_radius = value;
|
this._config.marker_border_radius = value;
|
||||||
this._fireChanged();
|
this._fireChanged();
|
||||||
});
|
});
|
||||||
|
|
@ -990,6 +1074,9 @@ class MapBadgeCardEditor extends HTMLElement {
|
||||||
badgeSlider?.addEventListener('input', (e) => {
|
badgeSlider?.addEventListener('input', (e) => {
|
||||||
const value = e.target.value + '%';
|
const value = e.target.value + '%';
|
||||||
badgeValueDisplay.textContent = value;
|
badgeValueDisplay.textContent = value;
|
||||||
|
});
|
||||||
|
badgeSlider?.addEventListener('change', (e) => {
|
||||||
|
const value = e.target.value + '%';
|
||||||
this._config.badge_border_radius = value;
|
this._config.badge_border_radius = value;
|
||||||
this._fireChanged();
|
this._fireChanged();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue