Comparing version 3.0.0-alpha.13 to 3.0.0-alpha.14
# Changelog for osjs-gui | ||
## 3.0.0-alpha.14 | ||
* Separated Input into separate Fields | ||
* Changed how padding/margin on flexboxes work | ||
* Toolbar now supports both orientations | ||
* New ListView context creation | ||
## 3.0.0-alpha.13 | ||
@@ -4,0 +11,0 @@ |
19
index.js
@@ -34,3 +34,2 @@ /*! | ||
import Button from './src/components/Button'; | ||
import Input from './src/components/Input'; | ||
import Progressbar from './src/components/Progressbar'; | ||
@@ -42,3 +41,3 @@ import Menu from './src/components/Menu'; | ||
import Panes from './src/components/Panes'; | ||
import ListView from './src/components/ListView'; | ||
import {ListView, listView} from './src/components/ListView'; | ||
import Image from './src/components/Image'; | ||
@@ -48,6 +47,13 @@ import Video from './src/components/Video'; | ||
import Iframe from './src/components/Iframe'; | ||
import * as adapters from './src/adapters'; | ||
import TextField from './src/components/TextField'; | ||
import TextareaField from './src/components/TextareaField'; | ||
import SelectField from './src/components/SelectField'; | ||
import ToggleField from './src/components/ToggleField'; | ||
import RangeField from './src/components/RangeField'; | ||
import GUIServiceProvider from './src/provider'; | ||
export { | ||
listView, | ||
GUIServiceProvider, | ||
@@ -57,3 +63,2 @@ Box, | ||
Button, | ||
Input, | ||
Progressbar, | ||
@@ -70,3 +75,7 @@ Menu, | ||
Iframe, | ||
adapters | ||
TextField, | ||
TextareaField, | ||
SelectField, | ||
ToggleField, | ||
RangeField | ||
}; |
{ | ||
"name": "@osjs/gui", | ||
"version": "3.0.0-alpha.13", | ||
"version": "3.0.0-alpha.14", | ||
"description": "OS.js v3 GUI", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -32,38 +32,26 @@ /* | ||
import {h} from 'hyperapp'; | ||
import {className, icon} from '../utils'; | ||
import {fieldWrapper, icon} from '../utils'; | ||
const createProps = (props, defaults = {}, ignore = []) => Object.keys(props) | ||
.filter(k => ['class', 'style', 'label', 'inputStyle', ...ignore].indexOf(k) === -1) | ||
.reduce((o, k) => Object.assign(o, {[k]: props[k]}), defaults); | ||
const createInner = props => { | ||
const children = []; | ||
if (props.icon) { | ||
children.push(icon(props.icon)); | ||
} | ||
if (typeof props.label === 'string') { | ||
children.push(h('span', [], props.label)); | ||
} | ||
return children; | ||
}; | ||
/** | ||
* A button | ||
* @param {Object} props Properties | ||
* @param {String} [props.class] Append this className | ||
* @param {Object} [props.style] CSS Style object | ||
* @param {Boolean} [props.disabled] Disabled attribute | ||
* @param {Object} [props.inputStyle] CSS Style object for input | ||
* @param {Function} [props.onclick] Click event | ||
*/ | ||
const Button = props => h('div', createProps(props, { | ||
class: className('osjs-gui-button', props), | ||
style: props.style | ||
}, ['onclick']), [ | ||
h('button', createProps(props, { | ||
style: props.inputStyle | ||
}), createInner(props)) | ||
]); | ||
const Button = (props = {}, children = []) => | ||
fieldWrapper('button', props, { | ||
}, (fieldProps) => { | ||
const inner = []; | ||
if (props.icon) { | ||
inner.push(icon(props.icon)); | ||
} | ||
if (typeof props.label === 'string') { | ||
inner.push(h('span', [], props.label)); | ||
} | ||
return h('button', fieldProps, [ | ||
...inner, | ||
...children | ||
]); | ||
}); | ||
export default Button; |
@@ -32,3 +32,3 @@ /* | ||
import {h} from 'hyperapp'; | ||
import {className} from '../utils'; | ||
import {filteredProps, boxProps} from '../utils'; | ||
@@ -42,9 +42,10 @@ /** | ||
*/ | ||
const Iframe = (props, children) => | ||
h('iframe', Object.assign({ | ||
frameborder: 0 | ||
}, props, { | ||
class: className('osjs-gui-iframe', props) | ||
}), children); | ||
const Iframe = (props, children = []) => | ||
h('div', boxProps('osjs-gui-iframe', props.box), [ | ||
h('iframe', Object.assign({ | ||
frameborder: 0 | ||
}, filteredProps(props, ['box']))), | ||
...children | ||
]); | ||
export default Iframe; |
@@ -32,93 +32,89 @@ /* | ||
import {h} from 'hyperapp'; | ||
import {className, icon} from '../utils'; | ||
import {className, boxProps, filteredProps, icon} from '../utils'; | ||
const defaultRow = { | ||
active: false, | ||
data: null, | ||
columns: [] | ||
}; | ||
const createView = props => { | ||
const getColumn = iter => typeof iter === 'string' || !iter | ||
? {label: iter} | ||
: Object.assign({}, iter); // FIXME | ||
const cols = (paneIndex) => (row, rowIndex) => { | ||
const col = row.columns[paneIndex] || {}; | ||
const selected = props.selectedIndex === rowIndex; | ||
const colIcon = col.icon ? icon(col.icon) : null; | ||
const children = [h('span', {}, [typeof col === 'object' ? col.label : col])]; | ||
const convertRows = rows => rows.map(row => { | ||
if (row instanceof Array) { | ||
row = Object.assign({}, defaultRow, { | ||
data: row, | ||
columns: row | ||
}); | ||
} else { | ||
row = Object.assign({}, defaultRow, row); | ||
} | ||
if (colIcon) { | ||
children.unshift(colIcon); | ||
} | ||
row.columns = row.columns.map(getColumn); | ||
return row; | ||
}); | ||
return h('div', { | ||
'data-has-icon': col.icon ? true : undefined, | ||
class: 'cell' + (selected ? ' active' : ''), | ||
ondblclick: (ev) => props.onactivate({data: row.data, index: rowIndex, ev}), | ||
onclick: (ev) => props.onselect({data: row.data, index: rowIndex, ev}), | ||
oncontextmenu: (ev) => props.oncontextmenu({data: row.data, index: rowIndex, ev}) | ||
}, children); | ||
}; | ||
const cols = (paneIndex, props) => (row, rowIndex) => { | ||
const col = row.columns[paneIndex] || {}; | ||
const selected = props.selectedIndex === rowIndex; | ||
const colIcon = col.icon ? icon(col.icon) : null; | ||
const children = [h('span', {}, [col.label])]; | ||
const pane = (index, col) => h('div', {class: 'pane'}, [ | ||
h('div', { | ||
class: 'header', | ||
style: { | ||
display: props.hideColumns ? 'none' : undefined | ||
} | ||
}, typeof col === 'object' ? col.label : col), | ||
h('div', {class: 'rows'}, props.rows.map(cols(index))) | ||
]); | ||
if (colIcon) { | ||
children.unshift(colIcon); | ||
} | ||
return h('div', { | ||
'data-has-icon': col.icon ? true : undefined, | ||
class: 'cell' + (selected ? ' active' : ''), | ||
ondblclick: (ev) => { | ||
props.onactivate(row.data, rowIndex, ev); | ||
}, | ||
onclick: (ev) => { | ||
props.onselect(row.data, rowIndex, ev); | ||
}, | ||
oncontextmenu: (ev) => { | ||
props.onselect(row.data, rowIndex, ev); | ||
props.oncontextmenu(row.data, rowIndex, ev); | ||
class: 'osjs-gui-list-view-wrapper', | ||
oncreate: el => (el.scrollTop = props.scrollTop), | ||
onupdate: el => { | ||
if (props.selectedIndex < 0) { | ||
el.scrollTop = props.scrollTop; | ||
} | ||
} | ||
}, children); | ||
}, props.columns.map((c, i) => pane(i, c))); | ||
}; | ||
const pane = (index, col, props) => h('div', {class: 'pane'}, [ | ||
h('div', { | ||
class: 'header', | ||
style: { | ||
display: props.hideColumns ? 'none' : undefined | ||
} | ||
}, getColumn(col).label), | ||
h('div', {class: 'rows'}, props.rows.map(cols(index, props))) | ||
]); | ||
export const ListView = props => h( | ||
'div', | ||
boxProps('osjs-gui-list-view', props.box || {}, 'vertical'), | ||
createView(filteredProps(props, ['box'])) | ||
); | ||
const view = props => h('div', { | ||
class: 'osjs-gui-list-view-wrapper', | ||
oncreate: el => (el.scrollTop = props.scrollTop), | ||
onupdate: el => { | ||
if (props.selectedIndex < 0) { | ||
el.scrollTop = props.scrollTop; | ||
} | ||
} | ||
}, props.columns.map((c, i) => pane(i, c, props))); | ||
export const listView = ({ | ||
component: (state, actions) => { | ||
const newProps = Object.assign({ | ||
columns: [], | ||
rows: [], | ||
onselect: ({data, index, ev}) => { | ||
actions.select({data, index, ev}); | ||
actions.setSelectedIndex(index); | ||
}, | ||
onactivate: ({data, index, ev}) => { | ||
actions.activate({data, index, ev}); | ||
actions.setSelectedIndex(-1); | ||
}, | ||
oncontextmenu: ({data, index, ev}) => { | ||
actions.select({data, index, ev}); | ||
actions.contextmenu({data, index, ev}); | ||
actions.setSelectedIndex(index); | ||
} | ||
}, state); | ||
const ListView = props => { | ||
const newProps = Object.assign({ | ||
hideColumns: false, | ||
return (props = {}) => ListView(Object.assign(newProps, props)); | ||
}, | ||
state: state => Object.assign({ | ||
selectedIndex: -1, | ||
scrollTop: 0, | ||
columns: [], | ||
rows: [], | ||
onactivate: function() {}, | ||
onselect: function() {}, | ||
oncontextmenu: function() {} | ||
}, props); | ||
scrollTop: 0 | ||
}, state), | ||
newProps.rows = convertRows(newProps.rows); | ||
return h('div', { | ||
class: className('osjs-gui-list-view', props), | ||
}, view(newProps)); | ||
}; | ||
export default ListView; | ||
actions: actions => Object.assign({ | ||
select: () => () => ({}), | ||
activate: () => () => ({}), | ||
contextmenu: () => () => ({}), | ||
setRows: rows => ({rows}), | ||
setColumns: columns => ({columns}), | ||
setScrollTop: scrollTop => state => ({scrollTop}), | ||
setSelectedIndex: selectedIndex => state => ({selectedIndex}) | ||
}, actions || {}) | ||
}); |
@@ -108,2 +108,4 @@ /* | ||
className: 'osjs-gui osjs-gui-menu', | ||
oncreate: props.oncreate, | ||
onupdate: props.onupdate, | ||
style: { | ||
@@ -110,0 +112,0 @@ display: props.visible !== false ? 'block' : 'none', |
@@ -32,2 +32,3 @@ /* | ||
import {h} from 'hyperapp'; | ||
import {boxProps} from '../utils'; | ||
@@ -39,20 +40,26 @@ /** | ||
*/ | ||
const Progressbar = (props, children) => h('div', { | ||
className: 'osjs-gui osjs-gui-progressbar' | ||
}, [ | ||
h('div', { | ||
className: 'value', | ||
style: { | ||
width: String(props.value || 0) + '%' | ||
} | ||
}), | ||
h('div', { | ||
className: 'label', | ||
}, [ | ||
h('span', {}, [ | ||
String(props.value || 0) + '%' | ||
const Progressbar = (props, children) => { | ||
let value = typeof props.value === 'number' | ||
? Math.max(0, Math.abs(props.value)) % 100 | ||
: 0; | ||
const c = h('div', {class: 'osjs-gui-progressbar-wrapper'}, [ | ||
h('div', { | ||
className: 'value', | ||
style: { | ||
width: String(value) + '%' | ||
} | ||
}), | ||
h('div', { | ||
className: 'label', | ||
}, [ | ||
h('span', {}, [ | ||
String(value) + '%' | ||
]) | ||
]) | ||
]) | ||
]); | ||
]); | ||
return h('div', boxProps('osjs-gui-progressbar', props.box || {}), c); | ||
}; | ||
export default Progressbar; |
@@ -32,2 +32,3 @@ /* | ||
import {h} from 'hyperapp'; | ||
import {className} from '../utils'; | ||
@@ -44,5 +45,7 @@ /** | ||
const Toolbar = (props, children) => h('div', { | ||
className: 'osjs-gui osjs-gui-toolbar', | ||
className: className('osjs-gui-toolbar', props, 'osjs-gui-' + (props.orientation || 'horizontal')), | ||
style: { | ||
flexDirection: (props.orientation === 'horizontal') ? 'column' : 'row', | ||
flexDirection: props.orientation === 'vertical' | ||
? 'column' | ||
: 'row', | ||
alignItems: props.align, | ||
@@ -49,0 +52,0 @@ justifyContent: props.justify |
@@ -34,3 +34,3 @@ /* | ||
const clampPosition = (root, ev) => { | ||
const clampSubMenu = (root, ev) => { | ||
let ul = ev.target.querySelector('ul'); | ||
@@ -58,2 +58,22 @@ if (!ul) { | ||
const clampMenu = (root, el, currentPosition) => { | ||
const result = {}; | ||
const bottom = currentPosition.top + el.offsetHeight; | ||
const right = currentPosition.left + el.offsetWidth; | ||
const offY = root.offsetHeight - currentPosition.top; | ||
const offX = root.offsetWidth - currentPosition.left; | ||
const overflowRight = right > root.offsetWidth; | ||
const overflowBottom = bottom > root.offsetHeight; | ||
if (overflowBottom) { | ||
result.top = root.offsetHeight - el.offsetHeight - offY; | ||
} | ||
if (overflowRight) { | ||
result.left = root.offsetWidth - el.offsetHeight - offX; | ||
} | ||
return (overflowBottom || overflowRight) ? result : null; | ||
}; | ||
/* | ||
@@ -67,3 +87,5 @@ * Context Menu view | ||
onclick: callback, | ||
onshow: actions.onshow | ||
onshow: actions.onshow, | ||
onupdate: el => actions.clamp(el), | ||
oncreate: el => actions.clamp(el) | ||
}); | ||
@@ -95,5 +117,14 @@ | ||
}, { | ||
clamp: (el) => props => { | ||
const root = this.core.$root; | ||
clearTimeout(clampTimeout); | ||
const newPosition = clampMenu(root, el, props.position); | ||
if (newPosition) { | ||
return {position: newPosition}; | ||
} | ||
}, | ||
onshow: (ev) => props => { | ||
clearTimeout(clampTimeout); | ||
clampTimeout = setTimeout(() => clampPosition(this.core.$root, ev), 1); | ||
clampTimeout = setTimeout(() => clampSubMenu(this.core.$root, ev), 1); | ||
}, | ||
@@ -100,0 +131,0 @@ show: (options) => props => { |
@@ -50,3 +50,3 @@ /* | ||
justifyContent: props.justify, | ||
padding: typeof props.padding === 'undefined' || props.padding === true ? undefined : '0' | ||
margin: typeof props.padding === 'undefined' || props.padding === true ? undefined : '0' | ||
}, props.style || {}); | ||
@@ -74,2 +74,38 @@ | ||
export {className, boxProps, boxStyles, icon}; | ||
const filteredProps = (props, filterKeys) => { | ||
const keys = Object.keys(props); | ||
const filter = k => filterKeys.indexOf(k) === -1; | ||
return keys | ||
.filter(filter) | ||
.reduce((result, k) => Object.assign({ | ||
[k]: props[k] | ||
}, result), {}); | ||
}; | ||
const fieldWrapper = (name, props, defaultProps, cb, cbInput) => { | ||
const oninput = props.oninput || function() {}; | ||
const onchange = props.onchange || function() {}; | ||
const onkeydown = props.onkeydown || function() {}; | ||
const getValue = cbInput || (ev => [ev.target.value]); | ||
const wrapperProps = boxProps('osjs-gui-field', props.box || {}, name); | ||
const fieldProps = Object.assign( | ||
{ | ||
oninput: ev => oninput(ev, ...getValue(ev)), | ||
onchange: ev => onchange(ev, ...getValue(ev)), | ||
onkeydown: ev => { | ||
if (ev.keyCode === 13 && props.onenter) { | ||
props.onenter(ev, ...getValue(ev)); | ||
} | ||
onkeydown(ev); | ||
} | ||
}, | ||
defaultProps, | ||
filteredProps(props, ['choices', 'label', 'box', 'oninput', 'onchange']) | ||
); | ||
return h('div', wrapperProps, cb(fieldProps)); | ||
}; | ||
export {className, boxProps, boxStyles, filteredProps, fieldWrapper, icon}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
96002
40
1534