Initial commit
commit
bef2601391
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"presets": [ "env", "stage-1", "react" ],
|
||||||
|
"retainLines": true
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
module.exports = {
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-control-regex": [
|
||||||
|
"off"
|
||||||
|
],
|
||||||
|
"no-empty": [
|
||||||
|
"off"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,137 @@
|
||||||
|
// "Generic dropdown component"
|
||||||
|
// Renders something and then when that "something" is focused renders a popup layer next to it
|
||||||
|
// For example, a text input with a popup selection list
|
||||||
|
// ...Or maybe a button with a popup menu
|
||||||
|
// (c) Vitaliy Filippov 2019+
|
||||||
|
// Version 2019-08-27
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export class Picker extends React.Component
|
||||||
|
{
|
||||||
|
static propTypes = {
|
||||||
|
direction: PropTypes.string,
|
||||||
|
clearOnClick: PropTypes.bool,
|
||||||
|
minWidth: PropTypes.number,
|
||||||
|
style: PropTypes.object,
|
||||||
|
renderInput: PropTypes.func.isRequired,
|
||||||
|
renderPicker: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
focused: false,
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
focus = () =>
|
||||||
|
{
|
||||||
|
this.setState({ focused: true, height: 0 });
|
||||||
|
this.calculateDirection();
|
||||||
|
if (this.props.clearOnClick)
|
||||||
|
{
|
||||||
|
document.body.addEventListener('click', this.blurExt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blur = () =>
|
||||||
|
{
|
||||||
|
this.setState({ focused: false });
|
||||||
|
if (this.props.clearOnClick)
|
||||||
|
{
|
||||||
|
document.body.removeEventListener('click', this.blurExt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blurExt = (ev) =>
|
||||||
|
{
|
||||||
|
let n = this.input ? ReactDOM.findDOMNode(this.input) : null;
|
||||||
|
let e = ev.target||ev.srcElement;
|
||||||
|
while (e)
|
||||||
|
{
|
||||||
|
// calendar-box is calendar.js's class
|
||||||
|
if (e == this.picker || e == n || /\bcalendar-box\b/.exec(e.className||''))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e = e.parentNode;
|
||||||
|
}
|
||||||
|
this.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInput = (e) =>
|
||||||
|
{
|
||||||
|
this.input = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPicker = (e) =>
|
||||||
|
{
|
||||||
|
this.picker = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
return (<div style={this.props.style}>
|
||||||
|
{this.props.renderInput({
|
||||||
|
onFocus: this.focus,
|
||||||
|
onBlur: this.blur,
|
||||||
|
focused: this.state.focused,
|
||||||
|
ref: this.setInput,
|
||||||
|
})}
|
||||||
|
{this.state.focused
|
||||||
|
? <div style={{
|
||||||
|
position: 'fixed',
|
||||||
|
background: 'white',
|
||||||
|
top: this.state.top,
|
||||||
|
width: this.state.width||'auto',
|
||||||
|
left: this.state.left,
|
||||||
|
zIndex: 100,
|
||||||
|
}} ref={this.setPicker}>
|
||||||
|
{this.props.renderPicker()}
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate()
|
||||||
|
{
|
||||||
|
if (this.state.focused && !this.state.height)
|
||||||
|
{
|
||||||
|
this.calculateDirection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateDirection()
|
||||||
|
{
|
||||||
|
if (!this.input || !this.picker)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const picker_height = ReactDOM.findDOMNode(this.picker).getBoundingClientRect().height;
|
||||||
|
const client = ReactDOM.findDOMNode(this.input).getBoundingClientRect();
|
||||||
|
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
||||||
|
let direction = this.props.direction;
|
||||||
|
if (!direction || direction === 'auto')
|
||||||
|
{
|
||||||
|
const down = client.top + picker_height < screen_height;
|
||||||
|
direction = down ? 'down' : 'up';
|
||||||
|
}
|
||||||
|
let top = client.top
|
||||||
|
+ (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop)
|
||||||
|
- (document.documentElement.clientTop || document.body.clientTop || 0);
|
||||||
|
top = direction == 'down' ? (top + client.height) + 'px' : (top - picker_height) + 'px';
|
||||||
|
const left = (client.left
|
||||||
|
+ (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft)
|
||||||
|
- (document.documentElement.clientLeft || document.body.clientLeft || 0)) + 'px';
|
||||||
|
const width = (this.props.minWidth && client.width < this.props.minWidth ? this.props.minWidth : client.width)+'px';
|
||||||
|
if (this.state.top !== top || this.state.left !== left ||
|
||||||
|
this.state.width !== width || this.state.height !== picker_height)
|
||||||
|
{
|
||||||
|
this.setState({ top, left, width, height: picker_height });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
// Simple Dropdown/Autocomplete with single/multiple selection and easy customisation via CSS modules
|
||||||
|
// Version 2019-08-27
|
||||||
|
// (c) Vitaliy Filippov 2019+
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import autocomplete_css from './autocomplete.css';
|
||||||
|
import input_css from './input.css';
|
||||||
|
import { Picker } from './Picker.js';
|
||||||
|
|
||||||
|
export default class Selectbox extends React.PureComponent
|
||||||
|
{
|
||||||
|
state = {
|
||||||
|
active: null,
|
||||||
|
query: null,
|
||||||
|
inputWidth: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuery = (ev) =>
|
||||||
|
{
|
||||||
|
this.setState({ query: ev.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown = (ev) =>
|
||||||
|
{
|
||||||
|
if ((ev.which == 40 || ev.which == 38) && this.filtered_items.length)
|
||||||
|
{
|
||||||
|
// up / down
|
||||||
|
this.setState({
|
||||||
|
active: this.state.active == null ? 0 : (
|
||||||
|
(this.state.active + (event.which === 40 ? 1 : this.filtered_items.length-1)) % this.filtered_items.length
|
||||||
|
),
|
||||||
|
});
|
||||||
|
if (!this.picker.state.focused)
|
||||||
|
{
|
||||||
|
this.picker.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((ev.which == 10 || ev.which == 13) && this.state.active != null &&
|
||||||
|
this.state.active < this.filtered_items.length)
|
||||||
|
{
|
||||||
|
// enter
|
||||||
|
this.onMouseDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseDown = () =>
|
||||||
|
{
|
||||||
|
this.setState({ query: null });
|
||||||
|
this.picker.blur();
|
||||||
|
const sel = this.filtered_items[this.state.active][this.props.valueKey||'id'];
|
||||||
|
let value = sel;
|
||||||
|
if (this.props.multiple)
|
||||||
|
{
|
||||||
|
const already = (this.props.value||[]).indexOf(sel);
|
||||||
|
if (already < 0)
|
||||||
|
{
|
||||||
|
// add
|
||||||
|
value = [ ...(this.props.value||[]), sel ];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// remove
|
||||||
|
value = [ ...this.props.value ];
|
||||||
|
value.splice(already, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.onChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOver = (ev) =>
|
||||||
|
{
|
||||||
|
let e = ev.target;
|
||||||
|
while (e && e != ev.currentTarget && !e.id)
|
||||||
|
{
|
||||||
|
e = e.parentNode;
|
||||||
|
}
|
||||||
|
if (e && e.id)
|
||||||
|
{
|
||||||
|
this.setState({ active: e.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focusInput = () =>
|
||||||
|
{
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInput = (p) =>
|
||||||
|
{
|
||||||
|
const value = this.state.query == null ? (this.props.multiple ? '' : this.item_hash[this.props.value]||'') : this.state.query;
|
||||||
|
const input = <input
|
||||||
|
readOnly={this.props.readOnly}
|
||||||
|
ref={this.setInput}
|
||||||
|
className={autocomplete_css.inputInputElement}
|
||||||
|
style={this.props.multiple ? { height: '29px', padding: 0, display: 'inline-block', width: this.state.inputWidth+'px' } : undefined}
|
||||||
|
onFocus={p.onFocus}
|
||||||
|
value={value}
|
||||||
|
onChange={this.setQuery}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
/>;
|
||||||
|
return (<div ref={p.ref} className={autocomplete_css.input}
|
||||||
|
style={this.props.multiple ? { fontSize: '13px', display: 'flex', overflow: 'hidden', cursor: 'text', height: 'auto' } : undefined}
|
||||||
|
onClick={this.props.multiple ? this.focusInput : undefined}>
|
||||||
|
{this.props.multiple
|
||||||
|
? <div style={{overflow: 'hidden', flex: 1, margin: '0 2em 0 .5em'}}>
|
||||||
|
<span className={autocomplete_css.inputInputElement} ref={this.setSizer}
|
||||||
|
style={{display: 'inline-block', width: 'auto', position: 'absolute', padding: '0 2px', top: '-100px', whiteSpace: 'pre', visibility: 'hidden'}}>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
{(this.props.value||[]).map(id => <span style={{cursor: 'pointer', display: 'inline-block', borderRadius: '2px', background: '#e0f0ff', border: '1px solid #c0e0ff', padding: '3px', margin: '2px 5px 2px 0'}}>{this.item_hash[id]}</span>)}
|
||||||
|
{input}
|
||||||
|
</div>
|
||||||
|
: input}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuggestions = () =>
|
||||||
|
{
|
||||||
|
return (<div className={autocomplete_css.suggestions} onMouseDown={this.onMouseDown} onMouseOver={this.onMouseOver}>
|
||||||
|
{this.filtered_items.map((e, i) => (<div key={i} id={i}
|
||||||
|
className={autocomplete_css.suggestion+(this.state.active == i ? ' '+autocomplete_css.active : '')}>
|
||||||
|
{e[this.props.labelKey||'name']}
|
||||||
|
</div>))}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSizer = (e) =>
|
||||||
|
{
|
||||||
|
this.sizer = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPicker = (e) =>
|
||||||
|
{
|
||||||
|
this.picker = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
setInput = (e) =>
|
||||||
|
{
|
||||||
|
this.input = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
if (!this.prevProps || this.props.source != this.prevProps.source)
|
||||||
|
{
|
||||||
|
if (!this.props.source)
|
||||||
|
{
|
||||||
|
this.items = [];
|
||||||
|
this.item_hash = {};
|
||||||
|
}
|
||||||
|
else if (this.props.source instanceof Array)
|
||||||
|
{
|
||||||
|
this.items = this.props.source;
|
||||||
|
this.item_hash = this.items.reduce((a, c) => { a[c[this.props.valueKey||'id']] = c[this.props.labelKey||'name']; return a; }, {});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.items = Object.keys(this.props.source).map(id => ({ id, name: this.props.source[id] }));
|
||||||
|
this.item_hash = this.props.source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.prevProps || this.props.suggestionMatch != this.prevProps.suggestionMatch ||
|
||||||
|
this.state.query != this.prevState.query)
|
||||||
|
{
|
||||||
|
if (!this.props.suggestionMatch || this.props.suggestionMatch == 'disabled' || !this.state.query)
|
||||||
|
{
|
||||||
|
this.filtered_items = this.items;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const n = this.props.labelKey||'name';
|
||||||
|
const q = this.state.query.toLowerCase();
|
||||||
|
this.filtered_items = this.items.filter(e => e[n].toLowerCase().indexOf(q) >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.prevProps = this.props;
|
||||||
|
this.prevState = this.state;
|
||||||
|
return (<Picker
|
||||||
|
ref={this.setPicker}
|
||||||
|
minWidth={this.props.minWidth}
|
||||||
|
clearOnClick={true}
|
||||||
|
renderInput={this.renderInput}
|
||||||
|
renderPicker={this.renderSuggestions}
|
||||||
|
style={this.props.style}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate()
|
||||||
|
{
|
||||||
|
if (this.sizer)
|
||||||
|
{
|
||||||
|
this.setState({ inputWidth: this.sizer.offsetWidth });
|
||||||
|
if (this.prevHeight && this.picker.input.offsetHeight != this.prevHeight)
|
||||||
|
{
|
||||||
|
this.picker.calculateDirection();
|
||||||
|
}
|
||||||
|
this.prevHeight = this.picker.input.offsetHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount()
|
||||||
|
{
|
||||||
|
this.componentDidUpdate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
.autocomplete
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input
|
||||||
|
{
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #d8d8d8;
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0;
|
||||||
|
height: 28px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:after
|
||||||
|
{
|
||||||
|
border: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-top: -0.375em;
|
||||||
|
font-family: FontAwesome;
|
||||||
|
content: "\f107";
|
||||||
|
color: #c0c0c0;
|
||||||
|
height: 0.75em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions
|
||||||
|
{
|
||||||
|
margin-top: 0;
|
||||||
|
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24);
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0;
|
||||||
|
transition-duration: 0.35s;
|
||||||
|
transition-property: max-height;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion
|
||||||
|
{
|
||||||
|
font-family: helvetica, arial, verdana, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 7px;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion.active
|
||||||
|
{
|
||||||
|
background: #d6e8f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear
|
||||||
|
{
|
||||||
|
top: 4px;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.values
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputInputElement
|
||||||
|
{
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: 200%;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 2em 0 .5em;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputInputElement::-ms-clear
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputBar
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputInputElement:focus + .inputBar
|
||||||
|
{
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #4196d4;
|
||||||
|
border-radius: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputBar:after, .inputBar:before
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.withclear input
|
||||||
|
{
|
||||||
|
text-indent: 0;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear
|
||||||
|
{
|
||||||
|
color: #aaa;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
left: auto;
|
||||||
|
top: 4px;
|
||||||
|
right: 28px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
|
||||||
|
<title>Selectbox</title>
|
||||||
|
<style>
|
||||||
|
html { margin: 0; height: 100%; font-family: Roboto, sans-serif; font-size: 15px; }
|
||||||
|
body { margin: 0; height: 100%; line-height: 1.2em; background: white; color: black; }
|
||||||
|
input, select, textarea, button { font-size: 100%; font-family: Roboto, sans-serif; }
|
||||||
|
table { border-collapse: collapse; font-size: 100%; }
|
||||||
|
form table td, form table th { vertical-align: top; }
|
||||||
|
table.left th { text-align: left; padding-right: .5em; }
|
||||||
|
td, th { padding: 0; }
|
||||||
|
img { border: 0; }
|
||||||
|
ul, p, form { margin: 0; }
|
||||||
|
input { margin: 1px; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,49 @@
|
||||||
|
.input
|
||||||
|
{
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #d8d8d8;
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0;
|
||||||
|
color: rgb(33, 33, 33);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputElement
|
||||||
|
{
|
||||||
|
font-size: inherit;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 2em 0 .5em;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputElement:focus
|
||||||
|
{
|
||||||
|
box-shadow: inset 0 0 0 1px #4196d4;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.inputElement
|
||||||
|
{
|
||||||
|
line-height: 200%;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputElement::-ms-clear
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar:after, .bar:before
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import Selectbox from './Selectbox.js';
|
||||||
|
|
||||||
|
const OPTIONS = {
|
||||||
|
day: 'День',
|
||||||
|
week: 'Неделя',
|
||||||
|
month: 'Месяц',
|
||||||
|
kak: 'Полный период детализации',
|
||||||
|
};
|
||||||
|
|
||||||
|
class Test extends React.PureComponent
|
||||||
|
{
|
||||||
|
state = {
|
||||||
|
value: [ 'day' ],
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange = (v) =>
|
||||||
|
{
|
||||||
|
this.setState({ value: v });
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
return <div style={{padding: '20px', width: '300px', background: '#e0e8ff'}}>
|
||||||
|
<Selectbox
|
||||||
|
source={OPTIONS}
|
||||||
|
multiple={true}
|
||||||
|
value={this.state.value}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Test />, document.getElementById('app')
|
||||||
|
);
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "selectbox",
|
||||||
|
"author": {
|
||||||
|
"name": "Vitaliy Filippov",
|
||||||
|
"email": "vitalif@yourcmc.ru",
|
||||||
|
"url": "http://yourcmc.ru/wiki/"
|
||||||
|
},
|
||||||
|
"description": "Simple React Dropdown/Autocomplete",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.26.3",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"babel-loader": "^7.1.5",
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"babel-preset-env": "^1.7.0",
|
||||||
|
"babel-preset-react": "^6.24.1",
|
||||||
|
"babel-preset-stage-1": "^6.24.1",
|
||||||
|
"css-loader": "^1.0.0",
|
||||||
|
"eslint": "^5.1.0",
|
||||||
|
"eslint-plugin-react": "^7.10.0",
|
||||||
|
"postcss": "^6.0.23",
|
||||||
|
"postcss-cssnext": "^3.1.0",
|
||||||
|
"postcss-each": "^0.10.0",
|
||||||
|
"postcss-import": "^11.1.0",
|
||||||
|
"postcss-loader": "^2.1.5",
|
||||||
|
"postcss-mixins": "^6.2.0",
|
||||||
|
"react": "^16.8.6",
|
||||||
|
"react-dom": "^16.8.6",
|
||||||
|
"style-loader": "^0.21.0",
|
||||||
|
"webpack": "^4.20.2",
|
||||||
|
"webpack-bundle-analyzer": "^2.13.1",
|
||||||
|
"webpack-cli": "^3.1.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"compile": "webpack --mode=production --optimize-minimize; nodejs copy-css.js",
|
||||||
|
"stats": "NODE_ENV=production webpack --mode=production --optimize-minimize --profile --json > stats.json; webpack-bundle-analyzer stats.json -h 0.0.0.0",
|
||||||
|
"watch-dev": "NODE_ENV=development webpack --mode=development -w",
|
||||||
|
"watch": "webpack --mode=production -w --optimize-minimize"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-import': {
|
||||||
|
root: __dirname,
|
||||||
|
},
|
||||||
|
'postcss-mixins': {},
|
||||||
|
'postcss-each': {},
|
||||||
|
'postcss-cssnext': {
|
||||||
|
features: {
|
||||||
|
customProperties: {
|
||||||
|
variables: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,55 @@
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
main: [ "babel-polyfill", './main.js' ]
|
||||||
|
},
|
||||||
|
context: __dirname,
|
||||||
|
output: {
|
||||||
|
path: __dirname+'/dist',
|
||||||
|
filename: '[name].js'
|
||||||
|
},
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /.jsx?$/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
exclude: /node_modules(?!\/react-toolbox\/components|\/dynamic-virtual-scroll)/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "style-loader",
|
||||||
|
options: {
|
||||||
|
singleton: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "css-loader",
|
||||||
|
options: {
|
||||||
|
modules: true, // default is false
|
||||||
|
sourceMap: true,
|
||||||
|
importLoaders: 1,
|
||||||
|
localIdentName: "[name]--[local]--[hash:base64:8]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postcss-loader"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
"process.env": {
|
||||||
|
NODE_ENV: JSON.stringify(process.env.NODE_ENV || "production")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
performance: {
|
||||||
|
maxEntrypointSize: 5000000,
|
||||||
|
maxAssetSize: 5000000
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue