@ -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; | |||
title: string; | |||
type: string; | |||
content: string; | |||
attachment: array; | |||
attachment: any[]; | |||
authors: string[]; | |||
tags: string[]; | |||
} |