From 5213332a30c375246f4a4847a085c1261a7be01d Mon Sep 17 00:00:00 2001 From: bozokopic Date: Fri, 13 Apr 2018 15:44:53 +0200 Subject: web frontend --- src_js/opcut/common.js | 15 ++++- src_js/opcut/grid.js | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ src_js/opcut/main.js | 4 +- src_js/opcut/states.js | 26 ++++++++ src_js/opcut/vt.js | 90 +++++++++++++++++++++++-- 5 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 src_js/opcut/grid.js create mode 100644 src_js/opcut/states.js (limited to 'src_js') diff --git a/src_js/opcut/common.js b/src_js/opcut/common.js index 7e63d19..3f39115 100644 --- a/src_js/opcut/common.js +++ b/src_js/opcut/common.js @@ -1,3 +1,16 @@ -export const defaultState = {}; + +export function submit() { + +} + + +export function addPanel() { + +} + + +export function addItem() { + +} diff --git a/src_js/opcut/grid.js b/src_js/opcut/grid.js new file mode 100644 index 0000000..b72e0b0 --- /dev/null +++ b/src_js/opcut/grid.js @@ -0,0 +1,175 @@ +import r from 'opcut/renderer'; +import * as u from 'opcut/util'; +import * as ev from 'opcut/ev'; + + +export const state = { + items: [], + selectedItem: null +}; + + +export function clearState(state) { + return u.set('selectedItem', null, state); +} + + +export function tbody(gridPath, columns, validators) { + const gridState = r.get(gridPath); + return ['tbody', gridState.items.map((row, rowIndex) => + ['tr', u.zip(columns || [], validators || []).map(([column, validator]) => { + let content = ''; + if (typeof column == 'function') { + content = column(row, rowIndex); + } else { + const selected = u.equals(gridState.selectedItem, [rowIndex, column]); + content = u.get(column, row); + if (typeof content == 'boolean') { + content = checkboxColumn(gridPath, column)(row, rowIndex); + } else if (selected) { + content = ['input.grid-input', { + type: 'text', + 'ev-change': (evt) => r.set([gridPath, 'items', rowIndex, column], + evt.target.value), + 'ev-blur': _ => { + if (u.equals(gridState.selectedItem, [rowIndex, column])) + r.set([gridPath, 'selectedItem'], null); + }, + 'ev-keyup': (evt) => { + switch (evt.key) { + case 'Enter': + evt.target.blur(); + break; + case 'Escape': + evt.target.value = u.get(column, row); + evt.target.blur(); + break; + } + }, + value: content + }]; + } + } + const title = (validator ? validator(u.get(column, row), row) : null); + return ['td' + (title ? '.invalid' : ''), { + title: (title ? title : ''), + 'ev-click': evt => { + if (u.equals(gridState.selectedItem, [rowIndex, column])) + return; + ev.one(r, 'render', () => { + if (evt.target.firstChild && evt.target.firstChild.focus) + evt.target.firstChild.focus(); + }); + r.set([gridPath, 'selectedItem'], [rowIndex, column]); + }}, + content]; + })] + )]; +} + + +export function tfoot(gridPath, colspan, newItem) { + const itemsPath = [gridPath, 'items']; + return ['tfoot', + ['tr', + ['td', { + colSpan: colspan}, + ['div', + ['button', { + 'ev-click': () => r.change(itemsPath, u.append(newItem))}, + ['span.fa.fa-plus'], + ' Add' + ], + ['span.spacer'], + ['button', { + 'ev-click': () => r.set(itemsPath, [])}, + ['span.fa.fa-trash-o'], + ' Remove all' + ] + ] + ] + ] + ]; +} + + +export function deleteColumn(gridPath, showUpDown, onDeleteCb) { + const itemsPath = [gridPath, 'items']; + return (i, index) => [ + (!showUpDown ? [] : [ + ['button', { + 'ev-click': () => { + const gridState = r.get(gridPath); + if (index < 1) + return; + r.change(itemsPath, u.pipe( + u.set(index, gridState.items[index-1]), + u.set(index-1, i) + )); + }}, + ['span.fa.fa-arrow-up'] + ], + ['button', { + 'ev-click': () => { + const gridState = r.get(gridPath); + if (index > gridState.items.length - 2) + return; + r.change(itemsPath, u.pipe( + u.set(index, gridState.items[index+1]), + u.set(index+1, i) + )); + }}, + ['span.fa.fa-arrow-down'] + ] + ]), + ['button', { + 'ev-click': () => { + const item = r.get(itemsPath, index); + r.change(itemsPath, u.omit(index)); + if (onDeleteCb) + onDeleteCb(item); + }}, + ['span.fa.fa-minus'] + ] + ]; +} + + +export function checkboxColumn(gridPath, column) { + return (i, index) => { + const columnPath = [gridPath, 'items', index, column]; + return ['div', { + style: 'text-align: center'}, + ['input', { + type: 'checkbox', + 'ev-change': (evt) => r.set(columnPath, evt.target.checked), + checked: r.get(columnPath)} + ] + ]; + }; +} + + +export function selectColumn(gridPath, column, values) { + return (i, index) => { + const columnPath = [gridPath, 'items', index, column]; + const selectedValue = r.get(columnPath); + const invalid = values.find( + i => u.equals(selectedValue, (u.isArray(i) ? i[0] : i))) === undefined; + const allValues = (invalid ? u.append(selectedValue, values) : values); + return ['select' + (invalid ? '.invalid' : ''), { + title: (invalid ? 'invalid value' : ''), + style: 'width: 100%', + 'ev-change': (evt) => r.set(columnPath, evt.target.value)}, + allValues.map(i => { + const value = (u.isArray(i) ? i[0] : i); + const label = (u.isArray(i) ? i[1] : i); + return ['option', { + selected: selectedValue == value, + value: value}, + label + ]; + }) + ]; + }; +} diff --git a/src_js/opcut/main.js b/src_js/opcut/main.js index 06ca4a0..8d00c56 100644 --- a/src_js/opcut/main.js +++ b/src_js/opcut/main.js @@ -1,7 +1,7 @@ import r from 'opcut/renderer'; import * as u from 'opcut/util'; import * as ev from 'opcut/ev'; -import * as common from 'opcut/common'; +import * as states from 'opcut/states'; import * as vt from 'opcut/vt'; import 'style/main.scss'; @@ -9,7 +9,7 @@ import 'style/main.scss'; function main() { let root = document.body.appendChild(document.createElement('div')); - r.init(root, common.defaultState, vt.main); + r.init(root, states.main, vt.main); } diff --git a/src_js/opcut/states.js b/src_js/opcut/states.js new file mode 100644 index 0000000..12c3f8a --- /dev/null +++ b/src_js/opcut/states.js @@ -0,0 +1,26 @@ +import * as grid from 'opcut/grid'; + + +export const main = { + form: { + method: 'FORWARD_GREEDY', + cut_width: '1', + panels: grid.state, + items: grid.state + } +}; + + +export const panelsItem = { + name: 'Panel', + width: '100', + height: '100' +}; + + +export const itemsItem = { + name: 'Item', + width: '10', + height: '10', + can_rotate: true +}; diff --git a/src_js/opcut/vt.js b/src_js/opcut/vt.js index f67b23f..39f6188 100644 --- a/src_js/opcut/vt.js +++ b/src_js/opcut/vt.js @@ -1,13 +1,91 @@ +import r from 'opcut/renderer'; + +import * as common from 'opcut/common'; export function main() { - return ['div', - 'application in development - ', - ['a', { - props: { - href: 'https://github.com/bozokopic/opcut' + return ['div.window', + leftPanel(), + ['div.center-panel'], + ['div.right-panel'] + ]; +} + + +function leftPanel() { + return ['div.left-panel', + ['div.header', + ['div.title', 'OPCUT'], + ['a', { + props: { + href: 'https://github.com/bozokopic/opcut' + }}, + ['span.fa.fa-github'] + ] + ], + ['div', + ['label', 'Method'], + ['select', + ['FORWARD_GREEDY', 'GREEDY'].map(method => + ['option', { + props: { + value: method, + selected: r.get('form', 'method') == method + }}, + method + ]) + ] + ], + ['div', + ['label', 'Cut width'], + ['input', { + props: { + value: r.get('form', 'cut_width') + }, + on: { + change: evt => r.set(['form', 'cut_width'], evt.target.value) + }} + ] + ], + ['div.list', + ['label', 'Panels'], + ['div.content', + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'] + ], + ['button.add', { + on: { + click: common.addPanel + }}, + ['span.fa.fa-plus'], + ' Add panel' + ] + ], + ['div.list', + ['label', 'Items'], + ['div.content', + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'], + 'sdfsdfssfd', ['br'] + ], + ['button.add', { + on: { + click: common.addItem + }}, + ['span.fa.fa-plus'], + ' Add item' + ] + ], + ['button.submit', { + on: { + click: common.submit }}, - 'Github page' + 'Calculate' ] ]; } -- cgit v1.2.3-70-g09d2