This commit is contained in:
Dayowe 2025-06-08 21:45:16 +02:00
parent 9fa91da90e
commit 4eda60df2a

View File

@ -32,20 +32,93 @@
.dog-calc-section {
background: #fdfcfe;
border: 1px solid #e8e3ed;
border-radius: 8px;
border-radius: 8px 8px 0 0;
padding: 24px;
margin-bottom: 24px;
margin-bottom: 0;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
}
.dog-calc-section h2 {
margin-top: 0;
.dog-calc-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.dog-calc-section h2 {
margin: 0;
color: #6f3f6d;
font-size: 1.5rem;
font-weight: 600;
}
/* Unit Switch */
.dog-calc-unit-switch {
display: flex;
align-items: center;
gap: 12px;
}
.dog-calc-unit-label {
font-size: 0.9rem;
font-weight: 500;
color: #635870;
transition: color 0.2s ease;
}
.dog-calc-unit-label.active {
color: #6f3f6d;
font-weight: 600;
}
.dog-calc-switch {
position: relative;
display: inline-block;
width: 48px;
height: 24px;
}
.dog-calc-switch input {
opacity: 0;
width: 0;
height: 0;
}
.dog-calc-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #e8e3ed;
transition: 0.3s;
border-radius: 24px;
}
.dog-calc-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.3s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.dog-calc-switch input:checked + .dog-calc-slider {
background-color: #f19a5f;
}
.dog-calc-switch input:checked + .dog-calc-slider:before {
transform: translateX(24px);
}
.dog-calc-form-group {
margin-bottom: 20px;
}
@ -143,8 +216,8 @@
.dog-calc-collapsible {
background: #ffffff;
border: 1px solid #e8e3ed;
border-radius: 8px;
margin-bottom: 24px;
border-top: none;
margin-bottom: 0;
overflow: hidden;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
}
@ -152,17 +225,7 @@
.dog-calc-collapsible-header {
background: #f8f5fa;
padding: 20px 24px;
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e8e3ed;
transition: background-color 0.2s ease;
}
.dog-calc-collapsible-header:hover {
background: #f5f1f6;
}
.dog-calc-collapsible-header h3 {
@ -172,30 +235,57 @@
font-weight: 600;
}
.dog-calc-collapsible-arrow {
transition: transform 0.2s ease;
font-size: 1.2rem;
color: #9f5999;
}
.dog-calc-collapsible.active .dog-calc-collapsible-arrow {
transform: rotate(180deg);
}
.dog-calc-collapsible-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.dog-calc-collapsible.active .dog-calc-collapsible-content {
max-height: 1000px;
display: block;
}
.dog-calc-collapsible-inner {
padding: 24px;
}
/* Action Buttons */
.dog-calc-action-buttons {
display: flex;
justify-content: center;
gap: 16px;
padding: 20px;
background: #f8f5fa;
border-left: 1px solid #e8e3ed;
border-right: 1px solid #e8e3ed;
margin-top: -1px;
}
.dog-calc-btn {
padding: 8px 16px;
border: 1px solid #e8e3ed;
border-radius: 6px;
font-size: 0.9rem;
font-weight: 500;
font-family: inherit;
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 6px;
background: white;
color: #6f3f6d;
}
.dog-calc-btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.dog-calc-btn-share:hover {
border-color: #9f5999;
color: #9f5999;
}
.dog-calc-btn-embed:hover {
border-color: #7fa464;
color: #7fa464;
}
.dog-calc-input-group {
display: flex;
gap: 16px;
@ -224,9 +314,12 @@
.dog-calc-footer {
text-align: center;
padding: 16px 0;
border-top: 1px solid #e8e3ed;
margin-top: 24px;
padding: 20px;
background: #fdfcfe;
border: 1px solid #e8e3ed;
border-radius: 0 0 8px 8px;
border-top: none;
margin-top: -1px;
}
.dog-calc-footer a {
@ -280,6 +373,30 @@
.dog-calc-collapsible-header {
padding: 16px 20px;
}
.dog-calc-section-header {
flex-direction: column;
align-items: stretch;
gap: 12px;
}
.dog-calc-section h2 {
text-align: center;
}
.dog-calc-unit-switch {
justify-content: center;
}
.dog-calc-action-buttons {
flex-direction: column;
padding: 16px;
}
.dog-calc-btn {
width: 100%;
justify-content: center;
}
}
@media (prefers-color-scheme: dark) {
@ -346,8 +463,47 @@
}
.dog-calc-footer {
background: #24202d;
border-color: #433c4f;
}
.dog-calc-unit-label {
color: #b8b0c2;
}
.dog-calc-unit-label.active {
color: #f5f3f7;
}
.dog-calc-slider {
background-color: #433c4f;
}
.dog-calc-action-buttons {
background: #312b3b;
border-color: #433c4f;
}
.dog-calc-btn {
background: #433c4f;
border-color: #433c4f;
color: #f5f3f7;
}
.dog-calc-btn:hover {
background: #524a5f;
border-color: #524a5f;
}
.dog-calc-btn-share:hover {
border-color: #9f5999;
color: #f19a5f;
}
.dog-calc-btn-embed:hover {
border-color: #7fa464;
color: #7fa464;
}
}
`;
@ -355,7 +511,17 @@
const widgetTemplate = `
<div class="dog-calc-widget">
<div class="dog-calc-section">
<h2>Dog's Characteristics</h2>
<div class="dog-calc-section-header">
<h2>Dog's Characteristics</h2>
<div class="dog-calc-unit-switch">
<span class="dog-calc-unit-label active" id="metricLabel">Metric</span>
<label class="dog-calc-switch">
<input type="checkbox" id="unitToggle">
<span class="dog-calc-slider"></span>
</label>
<span class="dog-calc-unit-label" id="imperialLabel">Imperial</span>
</div>
</div>
<div class="dog-calc-form-group">
<label for="dogType">Dog Type / Activity Level:</label>
@ -376,7 +542,7 @@
</div>
<div class="dog-calc-form-group">
<label for="weight">Dog's Weight (kg):</label>
<label for="weight" id="weightLabel">Dog's Weight (kg):</label>
<input type="number" id="weight" min="0.1" step="0.1" placeholder="Enter weight in kg" aria-describedby="weightHelp">
<div id="weightError" class="dog-calc-error dog-calc-hidden">Please enter a valid weight (minimum 0.1 kg)</div>
</div>
@ -393,15 +559,14 @@
</div>
</div>
<div class="dog-calc-collapsible" id="foodCalculator">
<div class="dog-calc-collapsible active" id="foodCalculator">
<div class="dog-calc-collapsible-header">
<h3>How much food is that?</h3>
<span class="dog-calc-collapsible-arrow"></span>
<h3>How much should I feed?</h3>
</div>
<div class="dog-calc-collapsible-content">
<div class="dog-calc-collapsible-inner">
<div class="dog-calc-form-group">
<label for="foodEnergy">Food Energy Content (kcal/100g):</label>
<label for="foodEnergy" id="foodEnergyLabel">Food Energy Content (kcal/100g):</label>
<input type="number" id="foodEnergy" min="1" step="1" placeholder="Enter kcal per 100g" aria-describedby="foodEnergyHelp">
<div id="foodEnergyError" class="dog-calc-error dog-calc-hidden">Please enter a valid energy content (minimum 1 kcal/100g)</div>
</div>
@ -438,6 +603,15 @@
</div>
</div>
<div class="dog-calc-action-buttons">
<button class="dog-calc-btn dog-calc-btn-share" onclick="alert('Share functionality not available in widget mode')">
Share
</button>
<button class="dog-calc-btn dog-calc-btn-embed" onclick="alert('Embed functionality not available in widget mode')">
Embed
</button>
</div>
<div class="dog-calc-footer">
<a href="https://caninenutritionandwellness.com" target="_blank" rel="noopener noreferrer">
by caninenutritionandwellness.com
@ -451,6 +625,7 @@
constructor(container) {
this.container = container;
this.currentMER = 0;
this.isImperial = false;
this.init();
}
@ -458,6 +633,7 @@
this.injectStyles();
this.injectHTML();
this.bindEvents();
this.updateUnitLabels();
}
injectStyles() {
@ -479,7 +655,7 @@
const foodEnergyInput = this.container.querySelector('#foodEnergy');
const daysInput = this.container.querySelector('#days');
const unitSelect = this.container.querySelector('#unit');
const collapsibleHeader = this.container.querySelector('.dog-calc-collapsible-header');
const unitToggle = this.container.querySelector('#unitToggle');
if (weightInput) {
weightInput.addEventListener('input', () => this.updateCalorieCalculations());
@ -500,16 +676,122 @@
if (unitSelect) unitSelect.addEventListener('change', () => this.updateFoodCalculations());
if (collapsibleHeader) {
collapsibleHeader.addEventListener('click', () => this.toggleCollapsible());
if (unitToggle) unitToggle.addEventListener('change', () => this.toggleUnits());
}
toggleUnits() {
const toggle = this.container.querySelector('#unitToggle');
this.isImperial = toggle.checked;
this.updateUnitLabels();
this.convertExistingValues();
this.updateCalorieCalculations();
}
updateUnitLabels() {
const metricLabel = this.container.querySelector('#metricLabel');
const imperialLabel = this.container.querySelector('#imperialLabel');
const weightLabel = this.container.querySelector('#weightLabel');
const foodEnergyLabel = this.container.querySelector('#foodEnergyLabel');
const weightInput = this.container.querySelector('#weight');
const foodEnergyInput = this.container.querySelector('#foodEnergy');
const unitSelect = this.container.querySelector('#unit');
if (metricLabel && imperialLabel) {
metricLabel.classList.toggle('active', !this.isImperial);
imperialLabel.classList.toggle('active', this.isImperial);
}
if (this.isImperial) {
if (weightLabel) weightLabel.textContent = "Dog's Weight (lbs):";
if (weightInput) {
weightInput.placeholder = "Enter weight in lbs";
weightInput.min = "0.2";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/oz):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per oz";
foodEnergyInput.min = "0.03";
foodEnergyInput.step = "0.01";
}
if (unitSelect) {
unitSelect.innerHTML = `
<option value="oz">ounces (oz)</option>
<option value="lb">pounds (lb)</option>
<option value="g">grams (g)</option>
<option value="kg">kilograms (kg)</option>
`;
}
} else {
if (weightLabel) weightLabel.textContent = "Dog's Weight (kg):";
if (weightInput) {
weightInput.placeholder = "Enter weight in kg";
weightInput.min = "0.1";
weightInput.step = "0.1";
}
if (foodEnergyLabel) foodEnergyLabel.textContent = "Food Energy Content (kcal/100g):";
if (foodEnergyInput) {
foodEnergyInput.placeholder = "Enter kcal per 100g";
foodEnergyInput.min = "1";
foodEnergyInput.step = "1";
}
if (unitSelect) {
unitSelect.innerHTML = `
<option value="g">grams (g)</option>
<option value="kg">kilograms (kg)</option>
<option value="oz">ounces (oz)</option>
<option value="lb">pounds (lb)</option>
`;
}
}
}
toggleCollapsible() {
const collapsible = this.container.querySelector('.dog-calc-collapsible');
if (collapsible) {
collapsible.classList.toggle('active');
convertExistingValues() {
const weightInput = this.container.querySelector('#weight');
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (weightInput && weightInput.value) {
const currentWeight = parseFloat(weightInput.value);
if (!isNaN(currentWeight)) {
if (this.isImperial) {
weightInput.value = this.formatNumber(currentWeight * 2.20462, 1);
} else {
weightInput.value = this.formatNumber(currentWeight / 2.20462, 1);
}
}
}
if (foodEnergyInput && foodEnergyInput.value) {
const currentEnergy = parseFloat(foodEnergyInput.value);
if (!isNaN(currentEnergy)) {
if (this.isImperial) {
foodEnergyInput.value = this.formatNumber(currentEnergy / 3.52739, 2);
} else {
foodEnergyInput.value = this.formatNumber(currentEnergy * 3.52739, 0);
}
}
}
}
getWeightInKg() {
const weightInput = this.container.querySelector('#weight');
if (!weightInput || !weightInput.value) return null;
const weight = parseFloat(weightInput.value);
if (isNaN(weight)) return null;
return this.isImperial ? weight / 2.20462 : weight;
}
getFoodEnergyPer100g() {
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (!foodEnergyInput || !foodEnergyInput.value) return null;
const energy = parseFloat(foodEnergyInput.value);
if (isNaN(energy)) return null;
return this.isImperial ? energy * 3.52739 : energy;
}
calculateRER(weightKg) {
@ -579,23 +861,23 @@
}
updateCalorieCalculations() {
const weightInput = this.container.querySelector('#weight');
const dogTypeSelect = this.container.querySelector('#dogType');
const calorieResults = this.container.querySelector('#calorieResults');
const rerValue = this.container.querySelector('#rerValue');
const merValue = this.container.querySelector('#merValue');
if (!weightInput || !dogTypeSelect || !calorieResults || !rerValue || !merValue) {
if (!dogTypeSelect || !calorieResults || !rerValue || !merValue) {
return;
}
const weight = weightInput.value;
const weightKg = this.getWeightInKg();
const dogTypeFactor = dogTypeSelect.value;
this.showError('weightError', false);
if (!weight || !this.validateInput(weight, 0.1)) {
if (weight) this.showError('weightError', true);
if (!weightKg || weightKg < 0.1) {
const weightInput = this.container.querySelector('#weight');
if (weightInput && weightInput.value) this.showError('weightError', true);
calorieResults.style.display = 'none';
return;
}
@ -605,7 +887,6 @@
return;
}
const weightKg = parseFloat(weight);
const factor = parseFloat(dogTypeFactor);
const rer = this.calculateRER(weightKg);
@ -623,26 +904,26 @@
updateFoodCalculations() {
if (this.currentMER === 0) return;
const foodEnergyInput = this.container.querySelector('#foodEnergy');
const daysInput = this.container.querySelector('#days');
const unitSelect = this.container.querySelector('#unit');
const dailyFoodResults = this.container.querySelector('#dailyFoodResults');
const dailyFoodValue = this.container.querySelector('#dailyFoodValue');
const totalFoodDisplay = this.container.querySelector('#totalFoodDisplay');
if (!foodEnergyInput || !daysInput || !unitSelect || !dailyFoodResults || !dailyFoodValue || !totalFoodDisplay) {
if (!daysInput || !unitSelect || !dailyFoodResults || !dailyFoodValue || !totalFoodDisplay) {
return;
}
const foodEnergy = foodEnergyInput.value;
const energyPer100g = this.getFoodEnergyPer100g();
const days = daysInput.value;
const unit = unitSelect.value;
this.showError('foodEnergyError', false);
this.showError('daysError', false);
if (!foodEnergy || !this.validateInput(foodEnergy, 1)) {
if (foodEnergy) this.showError('foodEnergyError', true);
if (!energyPer100g || energyPer100g < 1) {
const foodEnergyInput = this.container.querySelector('#foodEnergy');
if (foodEnergyInput && foodEnergyInput.value) this.showError('foodEnergyError', true);
dailyFoodResults.style.display = 'none';
totalFoodDisplay.value = '';
return;
@ -654,7 +935,6 @@
return;
}
const energyPer100g = parseFloat(foodEnergy);
const numDays = parseInt(days);
const dailyFoodGrams = (this.currentMER / energyPer100g) * 100;