commit
56ef1edcfe
@ -0,0 +1 @@
|
||||
node_modules
|
@ -0,0 +1,88 @@
|
||||
# Tiny Components - Validator
|
||||
|
||||
Created with [Riot.js](https://riot.js.org)
|
||||
|
||||
Validate Form or a Single Form-Field, Error Messages can be show just in time
|
||||
or after Submit entire Form.
|
||||
|
||||
For Validation this Component uses [Validate.js](https://validatejs.org/)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @tiny-components/validator --save
|
||||
```
|
||||
|
||||
## You can use it like this
|
||||
|
||||
```javascript
|
||||
<form class="form" onsubmit={ (event) => ( state.validator.submit(event) ) }>>
|
||||
<div class="field">
|
||||
<label>
|
||||
email
|
||||
<input type="email" name="email" onkeyup={ (event) => { state.validator.handle(event, 'email') }} />
|
||||
</label>
|
||||
<field-error errors={ state.validator.errors('email') } ></field-error>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>
|
||||
password
|
||||
<input type="password" name="email" onkeyup={ (event) => { state.validator.handle(event, 'password') }} />
|
||||
</label>
|
||||
<field-error errors={ state.validator.errors('password') } ></field-error>
|
||||
</div>
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
import Validator from './validator.js'
|
||||
|
||||
export default {
|
||||
|
||||
state: {
|
||||
validator: { }
|
||||
},
|
||||
|
||||
onBeforeMount() {
|
||||
// creating formValidator
|
||||
this.state.validator = new FormValidator(this.$('.form'), {
|
||||
'email': {
|
||||
'presence': true,
|
||||
'email': true
|
||||
},
|
||||
'password': {
|
||||
'presence': true
|
||||
}
|
||||
})
|
||||
|
||||
// adding on success
|
||||
this.state.validator.onSuccess((event, data) => {
|
||||
this.handleSuccess(event, data)
|
||||
})
|
||||
|
||||
// adding on error
|
||||
this.state.validator.onError((event, errors, data) => {
|
||||
this.handleError(event, errors, data)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
handleSuccess(event, data)
|
||||
{
|
||||
event.preventDefault()
|
||||
this.update()
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
handleError(event, errors, data)
|
||||
{
|
||||
this.update()
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
```
|
@ -0,0 +1,2 @@
|
||||
[install.scopes]
|
||||
"@tiny-components" = "https://gitea.node001.net/api/packages/tiny-components/npm/"
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Tiny Components | Field Upload</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<link href="/css/styles.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header class="header">
|
||||
<div class="bar">
|
||||
<div class="bar__start">
|
||||
<h1 class="m-top-4 m-bottom-4 h4">
|
||||
@tiny-components/upload
|
||||
</h1>
|
||||
</div>
|
||||
<div class="bar__main justify-end">
|
||||
<a class="button button--small m-left-sm-3 m-bottom-0" href="https://git.node001.net/tiny-components/upload" rel="noopener" target="_blank">
|
||||
Gitea
|
||||
<svg class="m-left-3 icon fill-text" aria-hidden="true">
|
||||
<use xlink:href="symbol-defs.svg#icon-gitea"></use>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="grid">
|
||||
<div class="col-12">
|
||||
|
||||
<h2>
|
||||
Upload
|
||||
</h2>
|
||||
|
||||
<form class="form-html" novalidate method="post">
|
||||
<div class="field-group">
|
||||
<tiny-field-upload label="Select File" name="file" multiple></tiny-field-upload>
|
||||
<field-error name="file"></field-error>
|
||||
</div>
|
||||
<button class="button" type="submit" disabled>
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<h2>
|
||||
Upload / Max
|
||||
</h2>
|
||||
|
||||
<form class="form-html" novalidate method="post">
|
||||
<div class="field-group">
|
||||
<tiny-field-upload label="Select File" name="file" max="2" multiple></tiny-field-upload>
|
||||
<field-error name="file"></field-error>
|
||||
</div>
|
||||
<button class="button" type="submit" disabled>
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
(self.webpackChunk_tiny_components_upload=self.webpackChunk_tiny_components_upload||[]).push([[654],{874:()=>{}}]);
|
After Width: | Height: | Size: 66 KiB |
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@tiny-components/upload",
|
||||
"version": "0.1.0",
|
||||
"description": "Upload Field for Riotjs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:node001-net/tiny-components/upload.git"
|
||||
},
|
||||
"author": "Björn Hase",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiny-components/plain-ui": "^0.6.0",
|
||||
"riot": "^9.4.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@riotjs/compiler": "^9.0.7",
|
||||
"@riotjs/webpack-loader": "^9.0.1",
|
||||
"@tiny-components/webpack": "^0.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode production --config webpack.config.js",
|
||||
"build-dev": "webpack --mode development --config webpack.config.js"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import * as riot from 'riot'
|
||||
|
||||
import FieldUpload from './fieldUpload.riot'
|
||||
|
||||
riot.register('tiny-field-upload', FieldUpload)
|
||||
riot.mount('tiny-field-upload')
|
@ -0,0 +1,3 @@
|
||||
@import
|
||||
'../node_modules/@tiny-components/plain-ui/src/scss/plain-ui',
|
||||
'fieldUpload.scss';
|
@ -0,0 +1,140 @@
|
||||
<tiny-field-upload>
|
||||
<div class="tiny-field-upload">
|
||||
<input type="file" class="tiny-field-upload__field" multiple={ props.multiple !== undefined } onchange={ (event) => { handleSelectFile(event) } } />
|
||||
<button class="button button--info" type="button" onclick={ (event) => { handleOpenFileManager(event) } } disabled={ state.disabled }>
|
||||
<svg class="icon fill-text-contrast" aria-hidden="true">
|
||||
<use xlink:href="/symbol-defs.svg#icon-upload"></use>
|
||||
</svg>
|
||||
{ props.label }
|
||||
</button>
|
||||
<div class="tiny-field-upload__files">
|
||||
<div class="tiny-field-upload__files-item" each={ file in state.files }>
|
||||
<div class="panel">
|
||||
<div class="bar">
|
||||
<div class="bar__start"></div>
|
||||
<div class="bar__main">{ file.name }</div>
|
||||
<div class="bar__end">
|
||||
<button type="button" class="button button--transparent tiny-field-upload__files-remove" onclick={ (event) => { handleRemoveFile(event, file) } }>
|
||||
<svg class="icon fill-text-contrast" aria-hidden="true">
|
||||
<use xlink:href="/symbol-defs.svg#icon-delete"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel__body">
|
||||
<input type="file" name="{ props.name }[]" />
|
||||
<img class="tiny-field-upload__files-media" src={ addSrc(file) } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
state:
|
||||
{
|
||||
files: [],
|
||||
field: undefined,
|
||||
disabled: false
|
||||
},
|
||||
|
||||
onMounted()
|
||||
{
|
||||
// getting field for getting files
|
||||
this.field = this.$('.tiny-field-upload__field')
|
||||
},
|
||||
|
||||
/**
|
||||
* after update
|
||||
*
|
||||
*/
|
||||
onUpdated(props, state) {
|
||||
|
||||
// getting elements
|
||||
const elements = this.$$('.tiny-field-upload__item input')
|
||||
|
||||
let index = 0
|
||||
|
||||
// adding for each element a file-input
|
||||
if (elements.length > 0) {
|
||||
for (const element of elements) {
|
||||
const dataTransfer = new DataTransfer()
|
||||
|
||||
// adding to each input field a file form files
|
||||
dataTransfer.items.add(state.files[index++]);
|
||||
element.files = dataTransfer.files
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* getting file and parse it for img-element
|
||||
*
|
||||
*/
|
||||
addSrc(file) {
|
||||
return URL.createObjectURL(file)
|
||||
},
|
||||
|
||||
/**
|
||||
* open file manager by button click on field-element
|
||||
*
|
||||
*/
|
||||
handleOpenFileManager(event)
|
||||
{
|
||||
this.field.click()
|
||||
},
|
||||
|
||||
/**
|
||||
* remove file from files
|
||||
*
|
||||
*/
|
||||
handleRemoveFile(event, file)
|
||||
{
|
||||
const index = this.state.files.indexOf(file)
|
||||
this.state.files.splice(index, 1)
|
||||
|
||||
this.validateMax()
|
||||
this.update()
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
handleSelectFile(event)
|
||||
{
|
||||
let index = 0
|
||||
|
||||
for (const file of event.target.files) {
|
||||
if (!this.props.max || (this.props.max && (((index++) + 1) <= this.props.max))) {
|
||||
this.state.files.push(file)
|
||||
}
|
||||
}
|
||||
|
||||
this.validateMax()
|
||||
this.update()
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
validateMax()
|
||||
{
|
||||
this.state.disabled = false
|
||||
|
||||
if (this.props.max && (this.state.files.length >= this.props.max)) {
|
||||
this.state.disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</tiny-field-upload>
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
.tiny-field-upload {
|
||||
|
||||
// hide all file inputs
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__files {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -0.5em 0;
|
||||
}
|
||||
|
||||
&__files-item {
|
||||
width: calc(25% - 1em);
|
||||
margin: 0 0.5em 1em;
|
||||
|
||||
img.tiny-field-upload__files-media {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.panel__body {
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
const tinyComponentsWebpack = require('@tiny-components/webpack')
|
||||
const riotRules = require('@tiny-components/webpack/rules/riot')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = tinyComponentsWebpack({
|
||||
index: [
|
||||
'./src/example.js'
|
||||
],
|
||||
styles: [
|
||||
'./src/example.scss'
|
||||
],
|
||||
}, {
|
||||
publicPath: '/example/',
|
||||
destination: path.resolve(process.cwd(), 'example'),
|
||||
rules: [ riotRules ],
|
||||
svg: {
|
||||
src: [
|
||||
'node_modules/@tiny-components/plain-ui/src/icons/mono-icons/svg/*.svg'
|
||||
]
|
||||
},
|
||||
purge: {
|
||||
src: path.join(__dirname, './**')
|
||||
}
|
||||
})
|
Loading…
Reference in new issue