Compare commits
2 Commits
39f94a6b2e
...
89bd555dc1
| Author | SHA1 | Date | |
|---|---|---|---|
| 89bd555dc1 | |||
| b9ae7d707d |
@@ -167,6 +167,14 @@
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/* ===== Step Counter ===== */
|
||||
.progress-counter {
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
color: var(--umzug-text-secondary);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* ===== Running Totals Bar ===== */
|
||||
.running-totals {
|
||||
position: sticky;
|
||||
@@ -181,11 +189,14 @@
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
||||
display: none;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.running-totals.visible {
|
||||
display: block;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.running-totals-label {
|
||||
@@ -219,6 +230,24 @@
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slideInForward {
|
||||
from { opacity: 0; transform: translateX(30px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes slideInBackward {
|
||||
from { opacity: 0; transform: translateX(-30px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
.wizard-step.active.forward {
|
||||
animation: slideInForward 0.3s ease;
|
||||
}
|
||||
|
||||
.wizard-step.active.backward {
|
||||
animation: slideInBackward 0.3s ease;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
@@ -394,27 +423,78 @@
|
||||
gap: 12px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
opacity: 0.55;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.furniture-item.has-quantity,
|
||||
.furniture-item:hover,
|
||||
.furniture-item:focus-within {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.furniture-item .montage-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.furniture-item.has-quantity .montage-toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.furniture-item:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.quantity-input {
|
||||
width: 56px;
|
||||
height: 36px;
|
||||
padding: 0 8px;
|
||||
.quantity-stepper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--umzug-border);
|
||||
border-radius: var(--umzug-radius-sm);
|
||||
border-radius: var(--umzug-radius);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.qty-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
background: var(--umzug-bg);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background var(--umzug-transition);
|
||||
color: var(--umzug-text);
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.qty-btn:hover {
|
||||
background: var(--umzug-border);
|
||||
}
|
||||
|
||||
.qty-btn:active {
|
||||
background: var(--umzug-primary-light);
|
||||
}
|
||||
|
||||
.quantity-stepper .quantity-input {
|
||||
border: none;
|
||||
border-left: 1px solid var(--umzug-border);
|
||||
border-right: 1px solid var(--umzug-border);
|
||||
border-radius: 0;
|
||||
width: 48px;
|
||||
height: 36px;
|
||||
padding: 0 4px;
|
||||
font-size: 0.95rem;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
color: var(--umzug-text);
|
||||
background: var(--umzug-surface);
|
||||
transition: border-color var(--umzug-transition), box-shadow var(--umzug-transition);
|
||||
}
|
||||
|
||||
.quantity-input:focus {
|
||||
.quantity-stepper .quantity-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--umzug-primary);
|
||||
box-shadow: 0 0 0 3px var(--umzug-primary-light);
|
||||
@@ -630,6 +710,19 @@
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.summary-edit {
|
||||
float: right;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--umzug-primary);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.summary-edit:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#wizard-summary .summary-grand-total {
|
||||
background: var(--umzug-primary);
|
||||
color: #fff;
|
||||
@@ -730,7 +823,8 @@
|
||||
flex-basis: calc(100% - 80px);
|
||||
}
|
||||
|
||||
.quantity-input {
|
||||
.quantity-input,
|
||||
.quantity-stepper {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
@@ -769,18 +863,34 @@
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.progress-dot {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dot-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.progress-dot.active .dot-label {
|
||||
display: block;
|
||||
font-size: 0.6rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.dot-number {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.progress-track {
|
||||
top: 14px;
|
||||
top: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
var l10n = typeof umzugslisteL10n !== 'undefined' ? umzugslisteL10n : {};
|
||||
var TOTAL_STEPS = 9;
|
||||
var currentStep = 1;
|
||||
var highestStep = 1;
|
||||
|
||||
// ===== Utility Helpers =====
|
||||
|
||||
@@ -47,16 +48,23 @@
|
||||
function showStep(n) {
|
||||
if (n < 1 || n > TOTAL_STEPS) return;
|
||||
|
||||
// Hide all steps
|
||||
// Determine direction
|
||||
var direction = n > currentStep ? 'forward' : 'backward';
|
||||
|
||||
// Hide all steps and remove direction classes
|
||||
qsa('.wizard-step').forEach(function(el) {
|
||||
el.classList.remove('active');
|
||||
el.classList.remove('active', 'forward', 'backward');
|
||||
});
|
||||
|
||||
// Show target step
|
||||
// Show target step with direction
|
||||
var target = qs('.wizard-step[data-step="' + n + '"]');
|
||||
if (target) target.classList.add('active');
|
||||
if (target) {
|
||||
target.classList.add(direction);
|
||||
target.classList.add('active');
|
||||
}
|
||||
|
||||
currentStep = n;
|
||||
if (n > highestStep) highestStep = n;
|
||||
updateProgressBar();
|
||||
updateNavButtons();
|
||||
updateRunningTotalsVisibility();
|
||||
@@ -90,7 +98,7 @@
|
||||
dot.classList.remove('active', 'completed');
|
||||
if (step === currentStep) {
|
||||
dot.classList.add('active');
|
||||
} else if (step < currentStep) {
|
||||
} else if (step <= highestStep) {
|
||||
dot.classList.add('completed');
|
||||
}
|
||||
});
|
||||
@@ -98,9 +106,15 @@
|
||||
// Update progress fill
|
||||
var fill = qs('#progress-fill');
|
||||
if (fill) {
|
||||
var pct = ((currentStep - 1) / (TOTAL_STEPS - 1)) * 100;
|
||||
var pct = ((highestStep - 1) / (TOTAL_STEPS - 1)) * 100;
|
||||
fill.style.width = pct + '%';
|
||||
}
|
||||
|
||||
// Update step counter
|
||||
var counter = qs('#progress-counter');
|
||||
if (counter) {
|
||||
counter.textContent = (l10n.stepLabel || 'Step') + ' ' + currentStep + ' ' + (l10n.stepOf || 'of') + ' ' + TOTAL_STEPS;
|
||||
}
|
||||
}
|
||||
|
||||
function updateNavButtons() {
|
||||
@@ -269,6 +283,11 @@
|
||||
|
||||
// ===== Summary Generation =====
|
||||
|
||||
function summaryHeading(text, gotoStep) {
|
||||
var editLabel = escHtml(l10n.summaryEdit || 'Edit');
|
||||
return '<h3>' + escHtml(text) + ' <a class="summary-edit" data-goto="' + gotoStep + '" role="button">' + editLabel + '</a></h3>';
|
||||
}
|
||||
|
||||
function generateSummary() {
|
||||
var container = qs('#wizard-summary');
|
||||
if (!container) return;
|
||||
@@ -277,7 +296,7 @@
|
||||
|
||||
// Customer info
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(l10n.summaryMovingDate || 'Moving Date') + '</h3>';
|
||||
html += summaryHeading(l10n.summaryMovingDate || 'Moving Date', 1);
|
||||
var day = getFieldVal('day');
|
||||
var month = getFieldVal('month');
|
||||
var year = getFieldVal('year');
|
||||
@@ -286,7 +305,7 @@
|
||||
|
||||
// Loading address
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(l10n.summaryLoading || 'Loading Address') + '</h3>';
|
||||
html += summaryHeading(l10n.summaryLoading || 'Loading Address', 1);
|
||||
html += summaryRow('Name', getFieldVal('bName'));
|
||||
html += summaryRow('Street', getFieldVal('bStrasse'));
|
||||
html += summaryRow('ZIP/City', getFieldVal('bort'));
|
||||
@@ -303,7 +322,7 @@
|
||||
|
||||
// Unloading address
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(l10n.summaryUnloading || 'Unloading Address') + '</h3>';
|
||||
html += summaryHeading(l10n.summaryUnloading || 'Unloading Address', 1);
|
||||
html += summaryRow('Name', getFieldVal('eName'));
|
||||
html += summaryRow('Street', getFieldVal('eStrasse'));
|
||||
html += summaryRow('ZIP/City', getFieldVal('eort'));
|
||||
@@ -320,13 +339,13 @@
|
||||
|
||||
// Room summaries
|
||||
var roomMap = [
|
||||
{ key: 'wohnzimmer', name: 'Wohnzimmer' },
|
||||
{ key: 'schlafzimmer', name: 'Schlafzimmer' },
|
||||
{ key: 'arbeitszimmer', name: 'Arbeitszimmer' },
|
||||
{ key: 'bad', name: 'Bad' },
|
||||
{ key: 'kueche_esszimmer', name: 'Kueche_Esszimmer' },
|
||||
{ key: 'kinderzimmer', name: 'Kinderzimmer' },
|
||||
{ key: 'keller', name: 'Keller' }
|
||||
{ key: 'wohnzimmer', name: 'Wohnzimmer', step: 2 },
|
||||
{ key: 'schlafzimmer', name: 'Schlafzimmer', step: 3 },
|
||||
{ key: 'arbeitszimmer', name: 'Arbeitszimmer', step: 4 },
|
||||
{ key: 'bad', name: 'Bad', step: 5 },
|
||||
{ key: 'kueche_esszimmer', name: 'Kueche_Esszimmer', step: 5 },
|
||||
{ key: 'kinderzimmer', name: 'Kinderzimmer', step: 6 },
|
||||
{ key: 'keller', name: 'Keller', step: 7 }
|
||||
];
|
||||
|
||||
roomMap.forEach(function(room) {
|
||||
@@ -335,7 +354,7 @@
|
||||
|
||||
var total = calculateRoomTotal(room.key);
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(getRoomDisplayName(room.key)) + '</h3>';
|
||||
html += summaryHeading(getRoomDisplayName(room.key), room.step);
|
||||
roomItems.forEach(function(item) {
|
||||
html += '<div class="summary-item">';
|
||||
html += '<span class="summary-item-name">' + escHtml(item.name) + '</span>';
|
||||
@@ -366,7 +385,7 @@
|
||||
var additionalHtml = getAdditionalWorkSummary();
|
||||
if (additionalHtml) {
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(l10n.summaryAdditional || 'Additional Work') + '</h3>';
|
||||
html += summaryHeading(l10n.summaryAdditional || 'Additional Work', 8);
|
||||
html += additionalHtml;
|
||||
html += '</div>';
|
||||
}
|
||||
@@ -375,7 +394,7 @@
|
||||
var sonstiges = getFieldVal('sonstiges');
|
||||
if (sonstiges) {
|
||||
html += '<div class="summary-section">';
|
||||
html += '<h3>' + escHtml(l10n.summaryOther || 'Other') + '</h3>';
|
||||
html += summaryHeading(l10n.summaryOther || 'Other', 8);
|
||||
html += '<p>' + escHtml(sonstiges) + '</p>';
|
||||
html += '</div>';
|
||||
}
|
||||
@@ -510,11 +529,11 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Progress dot click (backward navigation only)
|
||||
// Progress dot click (navigate to any visited step)
|
||||
qsa('.progress-dot').forEach(function(dot) {
|
||||
dot.addEventListener('click', function() {
|
||||
var step = parseInt(this.getAttribute('data-step'), 10);
|
||||
if (step < currentStep) {
|
||||
if (step <= highestStep && step !== currentStep) {
|
||||
showStep(step);
|
||||
}
|
||||
});
|
||||
@@ -524,15 +543,38 @@
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target.classList.contains('quantity-input')) {
|
||||
handleQuantityChange();
|
||||
var hasQty = parseGermanDecimal(e.target.value) > 0;
|
||||
// Toggle has-value class
|
||||
if (parseGermanDecimal(e.target.value) > 0) {
|
||||
if (hasQty) {
|
||||
e.target.classList.add('has-value');
|
||||
} else {
|
||||
e.target.classList.remove('has-value');
|
||||
}
|
||||
// Toggle has-quantity on parent row for dimming/montage visibility
|
||||
var row = e.target.closest('.furniture-item');
|
||||
if (row) row.classList.toggle('has-quantity', hasQty);
|
||||
}
|
||||
});
|
||||
|
||||
// Stepper button click handlers
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.qty-btn');
|
||||
if (!btn) return;
|
||||
var input = btn.parentNode.querySelector('.quantity-input');
|
||||
if (!input) return;
|
||||
var val = parseGermanDecimal(input.value);
|
||||
if (btn.classList.contains('qty-plus')) val++;
|
||||
else if (btn.classList.contains('qty-minus') && val > 0) val--;
|
||||
input.value = val > 0 ? val : '';
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
});
|
||||
|
||||
// Summary edit link click handler
|
||||
document.addEventListener('click', function(e) {
|
||||
var el = e.target.closest('.summary-edit');
|
||||
if (el) showStep(parseInt(el.dataset.goto, 10));
|
||||
});
|
||||
|
||||
// Clear field errors on input
|
||||
document.addEventListener('input', function(e) {
|
||||
if (e.target.classList.contains('field-error')) {
|
||||
|
||||
@@ -134,6 +134,7 @@ class Umzugsliste_Form_Renderer {
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-counter" id="progress-counter"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -391,7 +392,11 @@ class Umzugsliste_Form_Renderer {
|
||||
$montage_name = $room_name . '[m' . $item_name . ']';
|
||||
?>
|
||||
<div class="furniture-item" data-room="<?php echo esc_attr( $room_key ); ?>" data-cbm="<?php echo esc_attr( $cbm ); ?>">
|
||||
<input type="text" name="<?php echo esc_attr( $quantity_name ); ?>" class="quantity-input" inputmode="decimal" placeholder="0" maxlength="3">
|
||||
<div class="quantity-stepper">
|
||||
<button type="button" class="qty-btn qty-minus" aria-label="<?php echo esc_attr__( 'Decrease', 'siegel-umzugsliste' ); ?>">-</button>
|
||||
<input type="text" name="<?php echo esc_attr( $quantity_name ); ?>" class="quantity-input" inputmode="numeric" placeholder="0" maxlength="3">
|
||||
<button type="button" class="qty-btn qty-plus" aria-label="<?php echo esc_attr__( 'Increase', 'siegel-umzugsliste' ); ?>">+</button>
|
||||
</div>
|
||||
<span class="item-name"><?php echo esc_html( $item_name ); ?></span>
|
||||
<span class="item-cbm"><?php echo esc_html( str_replace( '.', ',', (string) $cbm ) ); ?></span>
|
||||
<input type="hidden" name="<?php echo esc_attr( $cbm_name ); ?>" value="<?php echo esc_attr( $cbm ); ?>">
|
||||
|
||||
@@ -51,11 +51,26 @@ class Umzugsliste_Shortcode {
|
||||
* @return string Form HTML
|
||||
*/
|
||||
public function render_form( $atts ) {
|
||||
// Ensure assets are enqueued
|
||||
$this->enqueue_assets();
|
||||
$atts = shortcode_atts( array( 'lang' => '' ), $atts, 'umzugsliste' );
|
||||
$switched = false;
|
||||
|
||||
// Render the form
|
||||
return Umzugsliste_Form_Renderer::render();
|
||||
if ( ! empty( $atts['lang'] ) ) {
|
||||
$locale_map = array( 'de' => 'de_DE', 'en' => 'en_US' );
|
||||
$locale = isset( $locale_map[ $atts['lang'] ] ) ? $locale_map[ $atts['lang'] ] : '';
|
||||
if ( $locale && $locale !== get_locale() ) {
|
||||
switch_to_locale( $locale );
|
||||
$switched = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->enqueue_assets();
|
||||
$html = Umzugsliste_Form_Renderer::render();
|
||||
|
||||
if ( $switched ) {
|
||||
restore_previous_locale();
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,6 +123,9 @@ class Umzugsliste_Shortcode {
|
||||
'grandTotalLabel' => __( 'Grand Total', 'siegel-umzugsliste' ),
|
||||
'quantityLabel' => __( 'Qty', 'siegel-umzugsliste' ),
|
||||
'cbmLabel' => __( 'cbm', 'siegel-umzugsliste' ),
|
||||
'summaryEdit' => __( 'Edit', 'siegel-umzugsliste' ),
|
||||
'stepLabel' => __( 'Step', 'siegel-umzugsliste' ),
|
||||
'stepOf' => __( 'of', 'siegel-umzugsliste' ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ $l10n_data = array(
|
||||
'grandTotalLabel' => __( 'Grand Total', 'siegel-umzugsliste' ),
|
||||
'quantityLabel' => __( 'Qty', 'siegel-umzugsliste' ),
|
||||
'cbmLabel' => __( 'cbm', 'siegel-umzugsliste' ),
|
||||
'summaryEdit' => __( 'Edit', 'siegel-umzugsliste' ),
|
||||
'stepLabel' => __( 'Step', 'siegel-umzugsliste' ),
|
||||
'stepOf' => __( 'of', 'siegel-umzugsliste' ),
|
||||
'nonce' => wp_create_nonce( 'umzugsliste_submit' ),
|
||||
);
|
||||
?>
|
||||
|
||||
@@ -126,6 +126,15 @@ class Umzugsliste {
|
||||
}
|
||||
|
||||
if ( $use_standalone ) {
|
||||
// Extract lang from shortcode if present and switch locale before template loads
|
||||
$post = get_queried_object();
|
||||
if ( $post && isset( $post->post_content ) && preg_match( '/\[umzugsliste[^\]]*lang=["\'](\w+)["\']/', $post->post_content, $m ) ) {
|
||||
$locale_map = array( 'de' => 'de_DE', 'en' => 'en_US' );
|
||||
if ( isset( $locale_map[ $m[1] ] ) && $locale_map[ $m[1] ] !== get_locale() ) {
|
||||
switch_to_locale( $locale_map[ $m[1] ] );
|
||||
}
|
||||
}
|
||||
|
||||
$custom_template = UMZUGSLISTE_PLUGIN_DIR . 'templates/form-page.php';
|
||||
if ( file_exists( $custom_template ) ) {
|
||||
return $custom_template;
|
||||
|
||||
Reference in New Issue
Block a user