15 changed files with 1430 additions and 29 deletions
@ -0,0 +1,105 @@ |
|||||
|
:root { |
||||
|
--bg-color: hsl(0.0, 0.0%, 90.2%); |
||||
|
--primary: hsl(82.3, 54.4%, 44.7%); |
||||
|
--secondary: hsl(194.5, 100.0%, 26.9%); |
||||
|
--gray: hsl(37.5, 25.0%, 93.7%); |
||||
|
--container: hsl(37.5, 50.0%, 96.9%); |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
background: var(--bg-color) !important; |
||||
|
} |
||||
|
|
||||
|
.stepper { |
||||
|
font-family: sans-serif; |
||||
|
background: var(--bg-color); |
||||
|
& > div { |
||||
|
h2 { |
||||
|
font-size: 2.3rem; |
||||
|
color: var(--secondary); |
||||
|
text-align: center; |
||||
|
} |
||||
|
.radio-grid { |
||||
|
display: grid; |
||||
|
grid-gap: 2rem; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
margin-top: 1rem; |
||||
|
label { |
||||
|
height: 20rem; |
||||
|
position: relative; |
||||
|
input { |
||||
|
visibility: hidden; |
||||
|
z-index: -99; |
||||
|
position: absolute; |
||||
|
} |
||||
|
input + span { |
||||
|
border: 1px var(--primary) solid; |
||||
|
border-radius: 2rem; |
||||
|
overflow: hidden; |
||||
|
cursor: pointer; |
||||
|
background: white; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
span:first-child { |
||||
|
display: flex; |
||||
|
flex: 1 0 0; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
span:nth-child(2) { |
||||
|
flex: 0 0 4rem; |
||||
|
background: var(--gray); |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
} |
||||
|
input:checked + span { |
||||
|
border-color: var(--secondary); |
||||
|
} |
||||
|
svg { |
||||
|
color: var(--secondary); |
||||
|
fill: currentColor; |
||||
|
width: 8rem; |
||||
|
height: 8rem; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.form-container { |
||||
|
background: var(--container); |
||||
|
border-radius: 2rem; |
||||
|
padding: 3rem; |
||||
|
display: grid; |
||||
|
grid-gap: 1rem; |
||||
|
.separator { |
||||
|
background: white; |
||||
|
height: 5px; |
||||
|
} |
||||
|
.form-grid { |
||||
|
display: grid; |
||||
|
grid-gap: 0.5rem; |
||||
|
grid-template-columns: 2fr 3fr 30px; |
||||
|
align-items: center; |
||||
|
input[type="text"] { |
||||
|
font-weight: bold; |
||||
|
border: var(--primary) 1px solid; |
||||
|
text-align: right; |
||||
|
} |
||||
|
.unit { |
||||
|
text-align: right; |
||||
|
} |
||||
|
.comment { |
||||
|
grid-column: 1 / -1; |
||||
|
font-size: 1.5rem; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
import 'alpinejs'; |
||||
|
import wNumb from 'wnumb'; |
||||
|
|
||||
|
var units = { |
||||
|
currency: wNumb({ |
||||
|
mark: ',', |
||||
|
thousand: '.', |
||||
|
prefix: '', |
||||
|
suffix: '', |
||||
|
decimals: 2 |
||||
|
}), |
||||
|
percent: wNumb({ |
||||
|
mark: ',', |
||||
|
thousand: '', |
||||
|
prefix: '', |
||||
|
suffix: ' %', |
||||
|
decimals: 2 |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
window.stepper = { |
||||
|
parts: { |
||||
|
notar: 0.02, |
||||
|
grundsteuer: 0.065, |
||||
|
makler: 0.0357 |
||||
|
}, |
||||
|
units: units, |
||||
|
step: 2, |
||||
|
greetings: [ |
||||
|
{ value: 'frau', label: 'Frau' }, |
||||
|
{ value: 'herr', label: 'Herr' }, |
||||
|
{ value: 'divers', label: 'Divers' }, |
||||
|
], |
||||
|
jobs: [ |
||||
|
{ value: 'angestellt', label: 'Angestellte*r' }, |
||||
|
], |
||||
|
titles: [ |
||||
|
{ value: 'prof', label: 'Prof' }, |
||||
|
{ value: 'dr', label: 'Dr' }, |
||||
|
], |
||||
|
value: { |
||||
|
kind: 'kauf', |
||||
|
kauf: { |
||||
|
kaufpreis: 300000, |
||||
|
modernisierung: 0, |
||||
|
baukosten: 0, |
||||
|
eigenkapital: 0, |
||||
|
}, |
||||
|
bau: { |
||||
|
grundstueckspreis: 300000, |
||||
|
bezahlt: '0', |
||||
|
baukosten: 0, |
||||
|
eigenkapital: 0, |
||||
|
}, |
||||
|
anschluss: { |
||||
|
objektwert: 300000, |
||||
|
umschuldung: 50000, |
||||
|
zuskap: 50000 |
||||
|
}, |
||||
|
wert: 300000, |
||||
|
umschuldung: 0, |
||||
|
kapital_zus: 0, |
||||
|
greeting: null, |
||||
|
title: null, |
||||
|
firstname: '', |
||||
|
lastname: '', |
||||
|
zip: '', |
||||
|
location: '', |
||||
|
phone: '', |
||||
|
email: '', |
||||
|
job: '', |
||||
|
haushalt: '', |
||||
|
einnahme: '' |
||||
|
}, |
||||
|
kinds: [ |
||||
|
{label: 'Kauf einer Immobilie', value: 'kauf', icon: 'home'}, |
||||
|
{label: 'Eigenes Bauvorhaben', value: 'bau', icon: 'home'}, |
||||
|
{label: 'Anschlussfinanzierung', value: 'anschluss', icon: 'home'} |
||||
|
], |
||||
|
|
||||
|
/* Methods */ |
||||
|
svg(icon) { |
||||
|
return `<svg><use xlink:href="${this.sprite}#${icon}"></use></svg>`; |
||||
|
} |
||||
|
}; |
||||
|
After Width: | Height: | Size: 962 B |
@ -1,12 +1,19 @@ |
|||||
{ |
{ |
||||
"name": "philipp/stepper", |
"name": "philipp/stepper", |
||||
"require": { |
"require": { |
||||
"twig/twig": "^3.1" |
|
||||
|
"twig/twig": "^3.1", |
||||
|
"symfony/var-dumper": "^5.1", |
||||
|
"illuminate/support": "^8.12" |
||||
}, |
}, |
||||
"authors": [ |
"authors": [ |
||||
{ |
{ |
||||
"name": "Philipp Lang", |
"name": "Philipp Lang", |
||||
"email": "philipp@zoomyboy.de" |
"email": "philipp@zoomyboy.de" |
||||
} |
} |
||||
] |
|
||||
|
], |
||||
|
"autoload": { |
||||
|
"psr-4": { |
||||
|
"Zoomyboy\\Stepper\\": "./src" |
||||
|
} |
||||
|
} |
||||
} |
} |
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,53 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace Zoomyboy\Stepper; |
||||
|
|
||||
|
use Twig\Loader\FilesystemLoader; |
||||
|
use Twig\Environment; |
||||
|
use Twig\TwigFilter as Filter; |
||||
|
use Twig\TwigFunction as Func; |
||||
|
|
||||
|
class Stepper { |
||||
|
|
||||
|
private $twig; |
||||
|
public $url; |
||||
|
|
||||
|
public function __construct() { |
||||
|
$loader = new FilesystemLoader(__DIR__.'/../views'); |
||||
|
|
||||
|
$this->twig = new Environment($loader); |
||||
|
|
||||
|
$this->twig->addFilter(new Filter('svg', [$this, 'svgTag'])); |
||||
|
$this->twig->addFilter(new Filter('prop', [$this, 'alpineProp'])); |
||||
|
$this->twig->addFunction(new Func('cprop', function ($aprop, $modifier) { |
||||
|
return $this->alpineProp($aprop, $modifier); |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
public function init() { |
||||
|
add_action('wp_enqueue_scripts', [ $this, 'enqueue' ]); |
||||
|
add_shortcode('stepper', [ $this, 'handle' ]); |
||||
|
} |
||||
|
|
||||
|
public function enqueue() { |
||||
|
wp_enqueue_script('stepper-js', $this->url.'assets/public/app.js'); |
||||
|
wp_enqueue_style('stepper-css', $this->url.'assets/public/app.css'); |
||||
|
} |
||||
|
|
||||
|
public function handle() { |
||||
|
echo $this->twig->render('stepper.twig.htm', [ |
||||
|
'sprite' => $this->url.'assets/public/sprite.svg' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
public function svgTag($e, $class = '') { |
||||
|
return '<svg class="'.$class.'"><use xlink:href="'.$sprite.'#'.$e.'"></use></svg>'; |
||||
|
} |
||||
|
|
||||
|
public function alpineProp($prop, $modifier) { |
||||
|
return ':value="units.'.$modifier.'.to('.$prop.')" |
||||
|
@focus="$event.target.value = '.$prop.'" |
||||
|
@change="'.$prop.' = units.'.$modifier.'.from($event.target.value)"'; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
<div x-show="step == 2"> |
||||
|
<h2 class="gradient">Geben Sie Ihre persönlichen Daten ein</h2> |
||||
|
|
||||
|
<div class="form-container"> |
||||
|
<div class="field-grid"> |
||||
|
|
||||
|
<label for="greeting"> |
||||
|
<span>Anrede</span> |
||||
|
<select x-model="value.greeting" id="greeting"> |
||||
|
<template x-for="greeting in greetings"> |
||||
|
<option :value="greeting.value" x-text="greeting.label"></option> |
||||
|
</template> |
||||
|
</select> |
||||
|
</label> |
||||
|
|
||||
|
<label for="title"> |
||||
|
<span>Titel</span> |
||||
|
<select x-model="value.title" id="title"> |
||||
|
<template x-for="title in titles"> |
||||
|
<option :value="title.value" x-text="title.label"></option> |
||||
|
</template> |
||||
|
</select> |
||||
|
</label> |
||||
|
|
||||
|
<label for="firstname"> |
||||
|
<span>Vorname</span> |
||||
|
<input type="text" x-model="value.firstname" id="firstname"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="lastname"> |
||||
|
<span>Nachname</span> |
||||
|
<input type="text" x-model="value.lastname" id="lastname"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="zip"> |
||||
|
<span>PLZ</span> |
||||
|
<input type="text" x-model="value.zip" id="zip"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="location"> |
||||
|
<span>Ort</span> |
||||
|
<input type="text" x-model="value.location" id="location"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="phone"> |
||||
|
<span>Telefon</span> |
||||
|
<input type="tel" x-model="value.phone" id="phone"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="email"> |
||||
|
<span>E-Mail-Adresse</span> |
||||
|
<input type="email" x-model="value.email" id="email"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="job"> |
||||
|
<span>Beruf des Hauptverdieners</span> |
||||
|
<select x-model="value.job" id="job"> |
||||
|
<template x-for="job in jobs"> |
||||
|
<option :value="job.value" x-text="job.label"></option> |
||||
|
</template> |
||||
|
</select> |
||||
|
</label> |
||||
|
|
||||
|
<label for="haushalt"> |
||||
|
<span>Haushaltsnetto monatlich</span> |
||||
|
<input type="text" x-model="value.haushalt" id="haushalt"> |
||||
|
</label> |
||||
|
|
||||
|
<label for="einnahme"> |
||||
|
<span>Netto-Mieteinnahme monatlich</span> |
||||
|
<input type="text" x-model="value.einnahme" id="einnahme"> |
||||
|
</label> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,68 @@ |
|||||
|
<form> |
||||
|
<div class="stepper" x-data="{ ...stepper, sprite: '{{sprite|raw}}' }"> |
||||
|
<div x-show="step == 0"> |
||||
|
<h2>Was möchten Sie finanzieren</h2> |
||||
|
<div class="radio-grid"> |
||||
|
<template x-for="ckind in kinds"> |
||||
|
<label> |
||||
|
<input @change="step++" type="radio" name="kind" :value="ckind.value" x-model="value.kind"> |
||||
|
<span> |
||||
|
<span x-html="svg(ckind.icon)"></span> |
||||
|
<span x-text="ckind.label"></span> |
||||
|
</span> |
||||
|
</label> |
||||
|
</template> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{% include 'vorhaben.twig.htm' with { |
||||
|
'kind': "bau", |
||||
|
bezahlt: true, |
||||
|
second: {label: 'Baukosten', value: 'baukosten'}, |
||||
|
main: {label: 'Grundstückspreis', value: 'grundstueckspreis'} |
||||
|
} %} |
||||
|
|
||||
|
{% include 'vorhaben.twig.htm' with { |
||||
|
'kind': "kauf", |
||||
|
bezahlt: false, |
||||
|
second: {label: 'Modernisierung', value: 'modernisierung'}, |
||||
|
main: {label: 'Kaufpreis', value: 'kaufpreis'} |
||||
|
} %} |
||||
|
|
||||
|
{% include 'daten.twig.htm' %} |
||||
|
|
||||
|
<div x-show="step == 1 && kind == 'anschluss'"> |
||||
|
<h2 class="gradient">Das Vorhaben in Zahlen</h2> |
||||
|
|
||||
|
<div class="form-container"> |
||||
|
<div class="form-grid"> |
||||
|
<label>Objektwert</label> |
||||
|
<input type="text" {{cprop('value.anschluss.objektwert', 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Umschuldung</label> |
||||
|
<input type="text" {{cprop('value.anschluss.umschuldung', 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
<span class="comment">Bitte geben Sie hier die Summe der gleichzeitig umzuschuldenden Darlehen an.</span> |
||||
|
</div> |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Zusätzl. Kapital</label> |
||||
|
<input type="text" {{cprop('value.anschluss.zuskap', 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator primary"></div> |
||||
|
<div> |
||||
|
<div>Darlehensbetrag</div> |
||||
|
<div x-text="units.currency.to(value.anschluss.objektwert + value.anschluss.umschuldung + value.anschluss.zuskap)"></div> |
||||
|
<span class="comment">Darlehensbeträge werden auf volle 1.000 Euro gerundet.</span> |
||||
|
</div> |
||||
|
<div class="separator gray"></div> |
||||
|
<button type="button" value="Werte übernehmen und weiter zum letzten Schritt"></button> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,65 @@ |
|||||
|
<div x-show="step == 1 && value.kind == '{{kind}}'"> |
||||
|
<h2 class="gradient">Ihr Bauvorhaben</h2> |
||||
|
|
||||
|
<div class="form-container"> |
||||
|
<div class="form-grid"> |
||||
|
<label>{{main.label}}</label> |
||||
|
<input type="text" {{cprop('value.' ~ kind ~ '.' ~ main.value, 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
{% if bezahlt %} |
||||
|
<div class="form-grid"> |
||||
|
<label>Bereits bezahlt?</label> |
||||
|
<select name="bezahlt" x-model="value.{{kind}}.bezahlt"> |
||||
|
<option value="1">Ja</option> |
||||
|
<option value="0">Nein</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
{% endif %} |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>{{second.label}}</label> |
||||
|
<input type="text" {{cprop('value.' ~ kind ~ '.' ~ second.value, 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Notar- & Gerichtskosten</label> |
||||
|
<span> |
||||
|
<span class="result-label" x-text="units.percent.to(parts.notar * 100)"></span> |
||||
|
<span class="result" x-text="units.currency.to(value.{{kind}}.{{main.value}} * parts.notar)"></span> |
||||
|
</span> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator primary"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Grunderwerbsteuer</label> |
||||
|
<span> |
||||
|
<span class="result-label" x-text="units.percent.to(parts.grundsteuer * 100)"></span> |
||||
|
<span class="result" x-text="units.currency.to(value.{{kind}}.{{main.value}} * parts.grundsteuer)"></span> |
||||
|
</span> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Makler <span class="result-label" x-text="units.percent.to(parts.makler * 100)"></span></label> |
||||
|
<input type="text" :value="units.currency.to(value.{{kind}}.{{main.value}} * parts.makler)"> |
||||
|
<span class="unit">€</span> |
||||
|
<span class="comment">Die vorgeschlagene Maklercourtage gentspricht der regional üblichen Höhe. Sie können diese jedoch bei Bedarf verändern oder auch komplett entfallen lassen.</span> |
||||
|
</div> |
||||
|
<div class="separator"></div> |
||||
|
<div class="form-grid"> |
||||
|
<label>Eigenkapital</label> |
||||
|
<input type="text" {{cprop('value.' ~ kind ~ '.eigenkapital', 'currency') | raw}}> |
||||
|
<span class="unit">€</span> |
||||
|
</div> |
||||
|
<div class="separator primary"></div> |
||||
|
<div> |
||||
|
<div><span>=</span>Darlehensbetrag</div> |
||||
|
<div x-text="units.currency.to(value.{{kind}}.{{main.value}} * (1+parts.notar+parts.grundsteuer+parts.makler) + value.{{kind}}.{{second.value}} - value.{{kind}}.eigenkapital)"></div> |
||||
|
<span class="comment">Darlehensbeträge werden auf volle 1.000 Euro gerundet.</span> |
||||
|
</div> |
||||
|
<div class="separator gray"></div> |
||||
|
<button @click="step++" type="button">Werte übernehmen und weiter zum letzten Schritt</button> |
||||
|
</div> |
||||
|
</div> |
||||
Loading…
Reference in new issue