Form: success notifier on save + configurable button text

This commit is contained in:
Michael Shanks 2020-07-06 16:24:44 +01:00
parent 8bc5cf564b
commit a55865e34c
4 changed files with 122 additions and 14 deletions

View File

@ -350,6 +350,11 @@ export default {
key: "title", key: "title",
control: Input, control: Input,
}, },
{
label: "Button Text",
key: "buttonText",
control: Input,
},
], ],
}, },
template: { template: {
@ -375,6 +380,11 @@ export default {
key: "title", key: "title",
control: Input, control: Input,
}, },
{
label: "Button Text",
key: "buttonText",
control: Input,
},
], ],
}, },
}, },

View File

@ -208,7 +208,8 @@
"data": true, "data": true,
"props": { "props": {
"model": "models", "model": "models",
"title": "string" "title": "string",
"buttonText": "string"
} }
}, },
"dataformwide": { "dataformwide": {
@ -216,7 +217,8 @@
"data": true, "data": true,
"props": { "props": {
"model": "models", "model": "models",
"title": "string" "title": "string",
"buttonText": "string"
} }
}, },
"datalist": { "datalist": {

View File

@ -1,9 +1,11 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { fade } from "svelte/transition"
export let _bb export let _bb
export let model export let model
export let title export let title
export let buttonText
const TYPE_MAP = { const TYPE_MAP = {
string: "text", string: "text",
@ -17,6 +19,10 @@
let store = _bb.store let store = _bb.store
let schema = {} let schema = {}
let modelDef = {} let modelDef = {}
let saved = false
let saving = false
let inputElements = {}
$: if (model && model.length !== 0) { $: if (model && model.length !== 0) {
fetchModel() fetchModel()
@ -32,14 +38,39 @@
} }
async function save() { async function save() {
// prevent double clicking firing multiple requests
if (saving) return
saving = true
const SAVE_RECORD_URL = `/api/${model}/records` const SAVE_RECORD_URL = `/api/${model}/records`
const response = await _bb.api.post(SAVE_RECORD_URL, newModel) const response = await _bb.api.post(SAVE_RECORD_URL, newModel)
const json = await response.json() const json = await response.json()
if (response.status === 200) {
store.update(state => { store.update(state => {
state[model] = state[model] ? [...state[model], json] : [json] state[model] = state[model] ? [...state[model], json] : [json]
return state return state
}) })
resetForm()
saved = true
setTimeout(() => {
saved = false
}, 1000)
}
saving = false
}
const resetForm = () => {
for (let el of Object.values(inputElements)) {
el.value = ""
if (el.checked) {
el.checked = false
}
}
newModel = {
modelId: model
}
} }
const handleInput = field => event => { const handleInput = field => event => {
@ -72,13 +103,14 @@
<div class="form-item"> <div class="form-item">
<label class="form-label" for="form-stacked-text">{field}</label> <label class="form-label" for="form-stacked-text">{field}</label>
{#if schema[field].type === 'string' && schema[field].constraints.inclusion} {#if schema[field].type === 'string' && schema[field].constraints.inclusion}
<select on:blur={handleInput(field)}> <select on:blur={handleInput(field)} bind:this={inputElements[field]}>
{#each schema[field].constraints.inclusion as opt} {#each schema[field].constraints.inclusion as opt}
<option>{opt}</option> <option>{opt}</option>
{/each} {/each}
</select> </select>
{:else} {:else}
<input <input
bind:this={inputElements[field]}
class="input" class="input"
type={TYPE_MAP[schema[field].type]} type={TYPE_MAP[schema[field].type]}
on:change={handleInput(field)} /> on:change={handleInput(field)} />
@ -87,7 +119,17 @@
<hr /> <hr />
{/each} {/each}
<div class="button-block"> <div class="button-block">
<button on:click={save}>Submit Form</button> <button on:click={save} class:saved>
{#if saved}
<div in:fade>
<span class:saved style="margin-right: 5px">🎉</span>Success<span class:saved style="margin-left: 5px">🎉</span>
</div>
{:else}
<div>
{buttonText || "Submit Form"}
</div>
{/if}
</button>
</div> </div>
</div> </div>
</form> </form>
@ -157,6 +199,10 @@
text-align: center; text-align: center;
} }
button.saved {
background-color: green;
}
button:hover { button:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05); 0 4px 6px -2px rgba(0, 0, 0, 0.05);

View File

@ -1,8 +1,11 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { fade } from "svelte/transition"
export let _bb export let _bb
export let model export let model
export let title export let title
export let buttonText
const TYPE_MAP = { const TYPE_MAP = {
string: "text", string: "text",
@ -16,6 +19,11 @@
let store = _bb.store let store = _bb.store
let schema = {} let schema = {}
let modelDef = {} let modelDef = {}
let saved = false
let saving = false
let inputElements = {}
$: if (model && model.length !== 0) { $: if (model && model.length !== 0) {
fetchModel() fetchModel()
} }
@ -27,14 +35,41 @@
schema = modelDef.schema schema = modelDef.schema
} }
async function save() { async function save() {
// prevent double clicking firing multiple requests
if (saving) return
saving = true
const SAVE_RECORD_URL = `/api/${model}/records` const SAVE_RECORD_URL = `/api/${model}/records`
const response = await _bb.api.post(SAVE_RECORD_URL, newModel) const response = await _bb.api.post(SAVE_RECORD_URL, newModel)
const json = await response.json() const json = await response.json()
if (response.status === 200) {
store.update(state => { store.update(state => {
state[model] = state[model] ? [...state[model], json] : [json] state[model] = state[model] ? [...state[model], json] : [json]
return state return state
}) })
resetForm()
saved = true
setTimeout(() => {
saved = false
}, 1000)
} }
saving = false
}
const resetForm = () => {
for (let el of Object.values(inputElements)) {
el.value = ""
if (el.checked) {
el.checked = false
}
}
newModel = {
modelId: model
}
}
const handleInput = field => event => { const handleInput = field => event => {
let value let value
if (event.target.type === "checkbox") { if (event.target.type === "checkbox") {
@ -62,13 +97,14 @@
<div class="form-item"> <div class="form-item">
<label class="form-label" for="form-stacked-text">{field}</label> <label class="form-label" for="form-stacked-text">{field}</label>
{#if schema[field].type === 'string' && schema[field].constraints.inclusion} {#if schema[field].type === 'string' && schema[field].constraints.inclusion}
<select on:blur={handleInput(field)}> <select on:blur={handleInput(field)} bind:this={inputElements[field]}>
{#each schema[field].constraints.inclusion as opt} {#each schema[field].constraints.inclusion as opt}
<option>{opt}</option> <option>{opt}</option>
{/each} {/each}
</select> </select>
{:else} {:else}
<input <input
bind:this={inputElements[field]}
class="input" class="input"
type={TYPE_MAP[schema[field].type]} type={TYPE_MAP[schema[field].type]}
on:change={handleInput(field)} /> on:change={handleInput(field)} />
@ -77,7 +113,17 @@
<hr /> <hr />
{/each} {/each}
<div class="button-block"> <div class="button-block">
<button on:click={save}>Submit Form</button> <button on:click={save} class:saved>
{#if saved}
<div in:fade>
<span class:saved style="margin-right: 5px">🎉</span>Success<span class:saved style="margin-left: 5px">🎉</span>
</div>
{:else}
<div>
{buttonText || "Submit Form"}
</div>
{/if}
</button>
</div> </div>
</div> </div>
</form> </form>
@ -138,6 +184,10 @@
text-align: center; text-align: center;
} }
button.saved {
background-color: green;
}
button:hover { button:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05); 0 4px 6px -2px rgba(0, 0, 0, 0.05);