@ -0,0 +1,124 @@ | |||||
import validate from 'validate.js' | |||||
import serialize from 'form-serialize' | |||||
/** | |||||
* Form Validator with RiotJS Components | |||||
* | |||||
* | |||||
* | |||||
* | |||||
*/ | |||||
class FormValidator | |||||
{ | |||||
/** | |||||
* | |||||
* @param {[type]} formSelector [description] | |||||
* @param {[type]} constraits [description] | |||||
*/ | |||||
constructor(formSelector, constraits, onSuccess) | |||||
{ | |||||
// getting selector to find form-element | |||||
this.formSelector = formSelector | |||||
// constraits for validate.js | |||||
this.constraits = constraits | |||||
// get form and elements | |||||
this.form = document.querySelector(this.formSelector) | |||||
this.elements = this.form.querySelectorAll('field-error') | |||||
// adding submit event | |||||
this.form.addEventListener('submit', (event) => { | |||||
this.onSubmit(event) | |||||
}) | |||||
// adding event if a element is updated | |||||
this.form.addEventListener('field-update', (event) => { | |||||
this.onFieldUpdate(event) | |||||
}) | |||||
this.onSuccess = onSuccess | |||||
} | |||||
/** | |||||
* | |||||
* @param {[type]} event [description] | |||||
* @return {[type]} [description] | |||||
*/ | |||||
onSubmit(event) | |||||
{ | |||||
event.preventDefault() | |||||
let errors = validate(serialize(event.target, { | |||||
hash: true | |||||
}), this.constraits, { | |||||
fullMessages: false | |||||
}) | |||||
if (errors) { | |||||
// send each element a event | |||||
this.elements.forEach((element) => { | |||||
let elementErrors = false | |||||
// check for errors by name | |||||
if (errors[element.attributes.name.nodeValue]) { | |||||
elementErrors = errors[element.attributes.name.nodeValue] | |||||
} | |||||
this.dispatchCustomEvent(elementErrors, element) | |||||
}) | |||||
} else { | |||||
this.onSuccess(event, serialize(event.target, { | |||||
hash: true | |||||
})) | |||||
} | |||||
} | |||||
/** | |||||
* | |||||
* | |||||
* @param {Event} event | |||||
* | |||||
*/ | |||||
onFieldUpdate(event) | |||||
{ | |||||
// workaround, make sure that value for single is undefined if it is empty | |||||
if (event.detail.value == '') { | |||||
event.detail.value = undefined | |||||
} | |||||
let errors = validate.single(event.detail.value, this.constraits[event.detail.name]) | |||||
// search for element by name and dispatch event | |||||
this.elements.forEach((element) => { | |||||
if (element.attributes.name.nodeValue == event.detail.name) { | |||||
this.dispatchCustomEvent(errors, element) | |||||
} | |||||
}) | |||||
} | |||||
/** | |||||
* dispatch event to single element | |||||
* | |||||
* @param {Array} errors | |||||
* @param {Element} element | |||||
* | |||||
*/ | |||||
dispatchCustomEvent(errors, element) | |||||
{ | |||||
let detail = false | |||||
if (errors) { | |||||
detail = errors | |||||
} | |||||
const formValidationEvent = new CustomEvent('form-validation', { | |||||
'detail': detail | |||||
}) | |||||
element.dispatchEvent(formValidationEvent) | |||||
} | |||||
} | |||||
export default FormValidator |
@ -0,0 +1,6 @@ | |||||
import * as riot from 'riot' | |||||
import NoteForm from './components/note-form.riot' | |||||
riot.register('note-form', NoteForm) | |||||
riot.mount('note-form') |
@ -0,0 +1,123 @@ | |||||
<app-note-form> | |||||
<div class="note-form"> | |||||
<div class="panel"> | |||||
<div class="panel__body"> | |||||
<form id="form" novalidate> | |||||
<input type="hidden" if={ state.note && state.note._id } name="_id" value="{ state.note._id }" /> | |||||
<div class="field-group"> | |||||
<label class="field-label"> | |||||
title | |||||
<input type="text" class="field-text" name="title" /> | |||||
</label> | |||||
</div> | |||||
<div class="field-group"> | |||||
<label class="field-label"> | |||||
content | |||||
<textarea name="content" class="field-text"></textarea> | |||||
</label> | |||||
</div> | |||||
<div class=""> | |||||
<div class="tabs"> | |||||
</div> | |||||
</div> | |||||
<div> | |||||
<button class="button" if={ !state.note || (state.note && !state.note._id) }> | |||||
Create | |||||
</button> | |||||
<button class="button" type="submit" if={ state.note && state.note._id }> | |||||
Save | |||||
</button> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<script> | |||||
import axios from 'axios' | |||||
import * as riot from 'riot' | |||||
import FormValidator from './../FormValidator' | |||||
import FieldError from './field-error.riot' | |||||
riot.register('field-error', FieldError) | |||||
riot.mount('field-error') | |||||
/** | |||||
* | |||||
* | |||||
* | |||||
* @author Björn Hase | |||||
* | |||||
*/ | |||||
export default { | |||||
state: { | |||||
note: undefined | |||||
}, | |||||
onBeforeMount() { | |||||
// show error if props is missing | |||||
if (!this.props.bucketId) { | |||||
console.error('ID of Bucket is Missing!') | |||||
} | |||||
}, | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
onMounted(props, state) { | |||||
// create form validation | |||||
const formValidation = new FormValidator('form', { | |||||
'title': { | |||||
'length': { | |||||
'maximum': 255 | |||||
} | |||||
}, | |||||
'content': { | |||||
'length': { | |||||
'maximum': 10922 | |||||
} | |||||
} | |||||
}, (event, data) => { | |||||
this.handleSubmit(event, data) | |||||
}) | |||||
}, | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
handleSubmit(event, data) { | |||||
let method = 'post' | |||||
if (this.state.note && this.state.note._id) { | |||||
method = 'put' | |||||
} | |||||
axios({ | |||||
method: method, | |||||
url: '/api/note/' + this.props.bucketId, | |||||
data: data | |||||
}).then((response) => { | |||||
this.state.note = response.data.data | |||||
this.update() | |||||
}) | |||||
} | |||||
} | |||||
</script> | |||||
</app-note-form> |
@ -0,0 +1,49 @@ | |||||
<app-note> | |||||
<div class="note"> | |||||
<div class="panel"> | |||||
<div class="bar"> | |||||
<div class="bar__end w-100"> | |||||
<button class="button button--transparent" onclick={ (event) => { handleEdit(event, note) } }> | |||||
<svg class="icon fill-text-contrast" aria-hidden="true"> | |||||
<use xlink:href="/symbol-defs.svg#icon-edit"></use> | |||||
</svg> | |||||
</button> | |||||
<button class="button button--transparent" onclick={ (event) => { handleDelete(event, note) } }> | |||||
<svg class="icon fill-text-contrast" aria-hidden="true"> | |||||
<use xlink:href="/symbol-defs.svg#icon-delete"></use> | |||||
</svg> | |||||
</button> | |||||
</div> | |||||
</div> | |||||
<div class="note__title"> | |||||
<h3> | |||||
{ state.note.title } | |||||
</h3> | |||||
</div> | |||||
<div class="panel__body"> | |||||
{ state.note.content } | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<script> | |||||
import axios from 'axios' | |||||
import remove from 'lodash.remove' | |||||
/** | |||||
* | |||||
* | |||||
* | |||||
* @author Björn Hase | |||||
* | |||||
*/ | |||||
export default { | |||||
state: { | |||||
note: undefined | |||||
} | |||||
} | |||||
</script> | |||||
</app-note> |
@ -0,0 +1,36 @@ | |||||
.note-form { | |||||
position: fixed; | |||||
top: 0; | |||||
left: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
.panel { | |||||
position: relative; | |||||
z-index: 20; | |||||
max-width: 33%; | |||||
width: 100%; | |||||
height: 100%; | |||||
border-left: 0; | |||||
border-top: 0; | |||||
border-bottom: 0; | |||||
} | |||||
&:before { | |||||
position: fixed; | |||||
top: 0; | |||||
left: 0; | |||||
width: 100%; | |||||
height: 100%; | |||||
background-color: transparent; | |||||
transition: background-color .5s; | |||||
z-index: 19; | |||||
content: ""; | |||||
background: rgba(0,0,0,.87); | |||||
z-index: 0; | |||||
} | |||||
} |
@ -0,0 +1,11 @@ | |||||
<% layout('./layout.html') %> | |||||
<div class="container container--app"> | |||||
<div class="grid"> | |||||
<div class="col-12"> | |||||
<h1 class="highlight"> | |||||
Settings | |||||
</h1> | |||||
</div> | |||||
</div> | |||||
</div> |
@ -0,0 +1,17 @@ | |||||
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts' | |||||
const router = Router() | |||||
/** | |||||
* render template for settings | |||||
* | |||||
* @param request | |||||
* @param response | |||||
* @return | |||||
*/ | |||||
router.get('/', function(request, response, next) | |||||
{ | |||||
response.render('settings') | |||||
}) | |||||
export default router |
@ -1,43 +0,0 @@ | |||||
import { Drash } from 'https://deno.land/x/drash@v1.4.4/mod.ts' | |||||
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts' | |||||
import { Bucket as BucketSchema } from './src/schemas/bucket.ts' | |||||
import Schema, { Type, string, number, array } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts' | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
export class BucketResource extends Drash.Http.Resource | |||||
{ | |||||
// route | |||||
static paths = ['/bucket/[:id?]'] | |||||
/** | |||||
* [GET description] | |||||
* @param id [description] | |||||
* @return [description] | |||||
*/ | |||||
public GET(id) | |||||
{ | |||||
const db = new Database<BucketSchema>() | |||||
const buckets = await db.findOne({ | |||||
'_id': id | |||||
}) | |||||
this.response.body = bucket | |||||
return this.response | |||||
} | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
public POST() | |||||
{ | |||||
const db = new Database<BucketSchema>() | |||||
this.response.body = bucket | |||||
return this.response | |||||
} | |||||
} |
@ -1,24 +0,0 @@ | |||||
import { Drash } from 'https://deno.land/x/drash@v1.4.4/mod.ts' | |||||
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts' | |||||
import { Bucket as BucketSchema } from './src/schemas/bucket.ts' | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
export class BucketResource extends Drash.Http.Resource | |||||
{ | |||||
// route | |||||
static paths = ['/buckets'] | |||||
// | |||||
public GET() | |||||
{ | |||||
const db = new Database<BucketSchema>() | |||||
const buckets = await db.findMany() | |||||
this.response.body = buckets | |||||
return this.response | |||||
} | |||||
} |
@ -1,18 +0,0 @@ | |||||
import { Drash } from 'https://deno.land/x/drash@v1.4.4/mod.ts' | |||||
/** | |||||
* | |||||
* | |||||
*/ | |||||
export class IndexResource extends Drash.Http.Resource | |||||
{ | |||||
// route | |||||
static paths = ['/'] | |||||
// | |||||
public GET() | |||||
{ | |||||
this.response.body = 'Hallo' | |||||
return this.response | |||||
} | |||||
} |
@ -1,8 +1,9 @@ | |||||
export interface Note { | |||||
export interface NoteSchema { | |||||
_id: string; | _id: string; | ||||
title: string; | title: string; | ||||
type: string; | type: string; | ||||
content: string; | content: string; | ||||
attachment: array; | |||||
attachment: any[]; | |||||
authors: string[]; | |||||
tags: string[]; | tags: string[]; | ||||
} | } |