├── .gitignore
├── .gitattributes
├── extraParams.hxml
├── bin
├── index.html
└── controls.html
├── submit.sh
├── src
└── sui
│ ├── controls
│ ├── TextControl.hx
│ ├── PasswordControl.hx
│ ├── DateSelectControl.hx
│ ├── TextSelectControl.hx
│ ├── NumberSelectControl.hx
│ ├── TelControl.hx
│ ├── SearchControl.hx
│ ├── DateControl.hx
│ ├── DateTimeControl.hx
│ ├── BoolControl.hx
│ ├── UrlControl.hx
│ ├── EmailControl.hx
│ ├── IntControl.hx
│ ├── FloatControl.hx
│ ├── ControlValues.hx
│ ├── ControlStreams.hx
│ ├── IControl.hx
│ ├── IntRangeControl.hx
│ ├── FloatRangeControl.hx
│ ├── DataList.hx
│ ├── ColorControl.hx
│ ├── BaseTextControl.hx
│ ├── NumberControl.hx
│ ├── LabelControl.hx
│ ├── NumberRangeControl.hx
│ ├── MultiControl.hx
│ ├── TimeControl.hx
│ ├── ChoiceControl.hx
│ ├── Options.hx
│ ├── TriggerControl.hx
│ ├── BaseDateControl.hx
│ ├── SingleInputControl.hx
│ ├── SelectControl.hx
│ ├── DoubleInputControl.hx
│ ├── ArrayControl.hx
│ └── MapControl.hx
│ ├── macro
│ └── Embed.hx
│ ├── components
│ └── Grid.hx
│ └── Sui.hx
├── haxelib.json
├── package.json
├── style
├── icons
│ ├── remove.svg
│ ├── add.svg
│ ├── down.svg
│ ├── expand.svg
│ ├── up.svg
│ └── collapse.svg
└── sui.styl
├── demo
├── DemoObject.hx
└── DemoControls.hx
├── LICENSE
├── gulpfile.js
├── README.md
└── css
└── sui.css
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-*
2 | node_modules
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | bin/* linguist-vendored
2 | css/* linguist-vendored
3 |
--------------------------------------------------------------------------------
/extraParams.hxml:
--------------------------------------------------------------------------------
1 | -lib dots
2 | -lib thx.stream
3 | -lib thx.stream.dom
4 | -D sui_embed_css=1
--------------------------------------------------------------------------------
/bin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SUI - Simple User Interface
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/submit.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | rm -f sui.zip
4 | zip -r sui.zip src test css/sui.css extraParams.hxml haxelib.json LICENSE README.md -x "*/\.*"
5 | haxelib submit sui.zip -x
6 |
--------------------------------------------------------------------------------
/bin/controls.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SUI - Simple User Interface
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/sui/controls/TextControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class TextControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | super(value, "text", "text", options);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/sui/controls/PasswordControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class PasswordControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | super(value, "text", "password", options);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/sui/controls/DateSelectControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class DateSelectControl extends SelectControl {
6 | public function new(defaultValue : Date, options : OptionsSelect) {
7 | super(defaultValue, "select-date", options);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/sui/controls/TextSelectControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class TextSelectControl extends SelectControl {
6 | public function new(defaultValue : String, options : OptionsSelect) {
7 | super(defaultValue, "select-text", options);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/sui/controls/NumberSelectControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class NumberSelectControl extends SelectControl {
6 | public function new(defaultValue : T, options : OptionsSelect) {
7 | super(defaultValue, "select-number", options);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/sui/controls/TelControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class TelControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | if(null == options)
8 | options = {};
9 | super(value, "tel", "tel", options);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/sui/controls/SearchControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class SearchControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | if(null == options)
8 | options = {};
9 | super(value, "search", "search", options);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/sui/controls/DateControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using StringTools;
4 | import sui.controls.Options;
5 | using thx.Nulls;
6 | using thx.Strings;
7 |
8 | class DateControl extends BaseDateControl {
9 | public function new(value : Date, ?options : OptionsDate) {
10 | super(value, "date", "date", BaseDateControl.toRFCDate, options);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/sui/controls/DateTimeControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using StringTools;
4 | import sui.controls.Options;
5 | using thx.Nulls;
6 | using thx.Strings;
7 |
8 | class DateTimeControl extends BaseDateControl {
9 | public function new(value : Date, ?options : OptionsDate) {
10 | super(value, "date-time", "datetime-local", BaseDateControl.toRFCDateTimeNoSeconds, options);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/sui/controls/BoolControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | class BoolControl extends SingleInputControl {
4 | public function new(value : Bool, ?options : Options) {
5 | super(value, "change", "bool", "checkbox", options);
6 | }
7 |
8 | override function setInput(v : Bool)
9 | input.checked = v;
10 |
11 | override function getInput() : Bool
12 | return input.checked;
13 | }
--------------------------------------------------------------------------------
/src/sui/controls/UrlControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class UrlControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | if(null == options)
8 | options = {};
9 | if(null == options.placeholder)
10 | options.placeholder = "http://example.com";
11 | super(value, "url", "url", options);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/sui/controls/EmailControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class EmailControl extends BaseTextControl {
6 | public function new(value : String, ?options : OptionsText) {
7 | if(null == options)
8 | options = {};
9 | if(null == options.placeholder)
10 | options.placeholder = "name@example.com";
11 | super(value, "email", "email", options);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/sui/controls/IntControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class IntControl extends NumberControl {
6 | public function new(value : Int, ?options : OptionsNumber) {
7 | super(value, "int", options);
8 | }
9 |
10 | override function setInput(v : Int)
11 | input.value = '$v';
12 |
13 | override function getInput() : Int
14 | return Std.parseInt(input.value);
15 | }
--------------------------------------------------------------------------------
/src/sui/controls/FloatControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class FloatControl extends NumberControl {
6 | public function new(value : Float, ?options : OptionsNumber) {
7 | super(value, "float", options);
8 | }
9 |
10 | override function setInput(v : Float)
11 | input.value = '$v';
12 |
13 | override function getInput() : Float
14 | return Std.parseFloat(input.value);
15 | }
--------------------------------------------------------------------------------
/src/sui/controls/ControlValues.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using thx.stream.Value;
4 |
5 | class ControlValues {
6 | public var value(default, null) : Value;
7 | public var focused(default, null) : Value;
8 | public var enabled(default, null) : Value;
9 |
10 | public function new(defaultValue : T) {
11 | value = new Value(defaultValue);
12 | focused = new Value(false);
13 | enabled = new Value(true);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/sui/macro/Embed.hx:
--------------------------------------------------------------------------------
1 | package sui.macro;
2 |
3 | import haxe.macro.Context;
4 | import thx.macro.Macros;
5 |
6 | class Embed {
7 | macro public static function file(path : String) {
8 | var suiPath = Macros.getModulePath("sui.Sui").split("/").slice(0, -2).join("/")+"/../";
9 | path = suiPath + path;
10 | Context.registerModuleDependency("sui.Sui", path);
11 | var content = sys.io.File.getContent(path);
12 | return macro $v{content};
13 | }
14 | }
--------------------------------------------------------------------------------
/src/sui/controls/ControlStreams.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using thx.stream.Emitter;
4 |
5 | class ControlStreams {
6 | public var value(default, null) : Emitter;
7 | public var focused(default, null) : Emitter;
8 | public var enabled(default, null) : Emitter;
9 | public function new(value : Emitter, focused : Emitter, enabled : Emitter) {
10 | this.value = value;
11 | this.focused = focused;
12 | this.enabled = enabled;
13 | }
14 | }
--------------------------------------------------------------------------------
/haxelib.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sui",
3 | "url": "https://github.com/fponticelli/sui",
4 | "license": "MIT",
5 | "classPath": "src",
6 | "tags": ["ui", "js"],
7 | "description": "Fixes and updated package for thx.core",
8 | "releasenote": "Fixed compatibility issues with thx.core and some improvements.",
9 | "version": "0.5.0",
10 | "contributors": ["fponticelli"],
11 | "dependencies": {
12 | "thx.core": "",
13 | "thx.promise": "",
14 | "thx.stream": "",
15 | "thx.stream.dom": "",
16 | "dots": ""
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/sui/controls/IControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.DOMElement as Element;
4 |
5 | interface IControl {
6 | public var el(default, null) : Element;
7 | public var defaultValue(default, null) : T;
8 | public var streams(default, null) : ControlStreams;
9 |
10 | public function set(v : T) : Void;
11 | public function get() : T;
12 |
13 | public function isEnabled() : Bool;
14 | public function isFocused() : Bool;
15 |
16 | public function disable() : Void;
17 | public function enable() : Void;
18 |
19 | public function focus() : Void;
20 | public function blur() : Void;
21 |
22 | public function reset() : Void;
23 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sui",
3 | "version": "0.0.1",
4 | "description": "Simple User Interface",
5 | "main": "gulpfile.js",
6 | "bin": {
7 | "sui": "sui.js"
8 | },
9 | "directories": {
10 | "test": "test"
11 | },
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "author": "Franco Ponticelli",
16 | "license": "MIT",
17 | "dependencies": {
18 | "canvas": "^1.2.2",
19 | "stylus": "^0.51.1"
20 | },
21 | "devDependencies": {
22 | "gulp": "^3.8.11",
23 | "gulp-connect": "^2.2.0",
24 | "gulp-stylus": "^2.0.1",
25 | "nib": "^1.1.0",
26 | "run-sequence": "^1.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/sui/controls/IntRangeControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 | using thx.Ints;
5 |
6 | class IntRangeControl extends NumberRangeControl {
7 | public function new(value : Int, ?options : OptionsNumber) {
8 | if(null == options)
9 | options = {};
10 |
11 | if(null == options.min)
12 | options.min = Ints.min(value, 0);
13 | if(null == options.min) {
14 | var s = null != options.step ? options.step : 100;
15 | options.max = Ints.max(value, s);
16 | }
17 | super(value, options);
18 | }
19 |
20 | override function getInput1() : Int
21 | return input1.value.canParse() ? input1.value.parse() : null;
22 |
23 | override function getInput2() : Int
24 | return input2.value.canParse() ? input2.value.parse() : null;
25 | }
--------------------------------------------------------------------------------
/src/sui/controls/FloatRangeControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 | using thx.Floats;
5 |
6 | class FloatRangeControl extends NumberRangeControl {
7 | public function new(value : Float, ?options : OptionsNumber) {
8 | if(null == options)
9 | options = {};
10 |
11 | if(null == options.min)
12 | options.min = Math.min(value, 0);
13 | if(null == options.min) {
14 | var s = null != options.step ? options.step : 1;
15 | options.max = Math.max(value, s);
16 | }
17 | super(value, options);
18 | }
19 |
20 | override function getInput1() : Float
21 | return input1.value.canParse() ? input1.value.parse() : null;
22 |
23 | override function getInput2() : Float
24 | return input2.value.canParse() ? input2.value.parse() : null;
25 | }
--------------------------------------------------------------------------------
/style/icons/remove.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
--------------------------------------------------------------------------------
/src/sui/controls/DataList.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.DOMElement as Element;
4 | import dots.Html;
5 |
6 | class DataList {
7 | public static var nid = 0;
8 | public var id : String;
9 |
10 | public static function fromArray(container : Element, values : Array)
11 | return new DataList(container, values.map(function(v) return { value : v, label : v }));
12 |
13 | public function new(container : Element, values : Array<{ label : String, value : String }>) {
14 | id = 'sui-dl-${++nid}';
15 | var datalist = Html.parse('');
16 | container.appendChild(datalist);
17 | }
18 |
19 | static function toOption(o : { label : String, value : String })
20 | return '';
21 |
22 | public function applyTo(el : Element) {
23 | el.setAttribute("list", id);
24 | return this;
25 | }
26 | }
--------------------------------------------------------------------------------
/style/icons/add.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
--------------------------------------------------------------------------------
/style/icons/down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
--------------------------------------------------------------------------------
/style/icons/expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
--------------------------------------------------------------------------------
/style/icons/up.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
--------------------------------------------------------------------------------
/style/icons/collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
--------------------------------------------------------------------------------
/demo/DemoObject.hx:
--------------------------------------------------------------------------------
1 | import haxe.ds.Option;
2 | import sui.Sui;
3 |
4 | class DemoObject {
5 | public static function main() {
6 | var demoObject = new DemoObject(),
7 | sui = new Sui();
8 |
9 | sui.bind(demoObject);
10 | sui.attach();
11 | }
12 | /*
13 | public var scores : Array;
14 | public var name : String;
15 | public var startedOn : Date;
16 | public var info : Map;
17 | */
18 | public var option : Option;
19 | // @:isVar public var enabled(get, set) : Bool;
20 |
21 | public function new() {
22 | /*
23 | this.scores = [1, 5, 10];
24 | this.name = "Sui";
25 | this.startedOn = Date.fromString("2014-11-09");
26 | this.info = ["a" => "b"];
27 | */
28 | this.option = None;
29 | }
30 |
31 | public function traceMe()
32 | trace(Reflect.fields(this)
33 | .map(function(field) return '$field: ${Reflect.field(this, field)}')
34 | .join(", "));
35 | /*
36 | function get_enabled()
37 | return enabled;
38 |
39 | function set_enabled(v : Bool) {
40 | trace('enabled set to $v');
41 | return enabled = v;
42 | }
43 | */
44 | }
--------------------------------------------------------------------------------
/src/sui/controls/ColorControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class ColorControl extends DoubleInputControl {
6 | static var PATTERN = ~/^[#][0-9a-f]{6}$/i;
7 | public function new(value : String, ?options : OptionsColor) {
8 | if(null == options)
9 | options = {};
10 |
11 | super(value, "color", "input", "color", "input", "text", PATTERN.match, options);
12 |
13 | if(null != options.autocomplete)
14 | input2.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
15 |
16 | if(null != options.list)
17 | new DataList(el, options.list).applyTo(input1).applyTo(input2);
18 | else if(null != options.values)
19 | DataList.fromArray(el, options.values).applyTo(input1).applyTo(input2);
20 |
21 | setInputs(value);
22 | }
23 |
24 | override function setInput1(v : String)
25 | input1.value = v;
26 |
27 | override function setInput2(v : String)
28 | input2.value = v;
29 |
30 | override function getInput1() : String
31 | return input1.value;
32 |
33 | override function getInput2() : String
34 | return input2.value;
35 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Franco Ponticelli
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/sui/controls/BaseTextControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 |
5 | class BaseTextControl extends SingleInputControl {
6 | public function new(value : String, name : String, type : String, ?options : OptionsText) {
7 | if(null == options)
8 | options = {};
9 | super(value, "input", name, type, options);
10 |
11 | if(null != options.maxlength)
12 | input.setAttribute('maxlength', '${options.maxlength}');
13 | if(null != options.autocomplete)
14 | input.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
15 | if(null != options.pattern)
16 | input.setAttribute('pattern', '${options.pattern}');
17 | if(null != options.placeholder)
18 | input.setAttribute('placeholder', '${options.placeholder}');
19 | if(null != options.list)
20 | new DataList(el, options.list).applyTo(input);
21 | else if(null != options.values)
22 | DataList.fromArray(el, options.values).applyTo(input);
23 | }
24 |
25 | override function setInput(v : String)
26 | input.value = v;
27 |
28 | override function getInput() : String
29 | return input.value;
30 | }
--------------------------------------------------------------------------------
/src/sui/controls/NumberControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using StringTools;
4 | import sui.controls.Options;
5 |
6 | class NumberControl extends SingleInputControl {
7 | public function new(value : T, name : String, ?options : OptionsNumber) {
8 | if(null == options)
9 | options = {};
10 | super(value, "input", name, "number", options);
11 |
12 | if(null != options.autocomplete)
13 | input.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
14 |
15 | if(null != options.min)
16 | input.setAttribute('min', '${options.min}');
17 | if(null != options.max)
18 | input.setAttribute('max', '${options.max}');
19 | if(null != options.step)
20 | input.setAttribute('step', '${options.step}');
21 | if(null != options.placeholder)
22 | input.setAttribute('placeholder', '${options.placeholder}');
23 |
24 | if(null != options.list)
25 | new DataList(el, options.list.map(function(o) {
26 | return {
27 | label : o.label,
28 | value : '${o.value}'
29 | };
30 | })).applyTo(input);
31 | else if(null != options.values)
32 | new DataList(el, options.values.map(function(o) {
33 | return {
34 | label : '${o}',
35 | value : '${o}'
36 | };
37 | })).applyTo(input);
38 | }
39 | }
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | seq = require('run-sequence'),
3 | connect = require('gulp-connect');
4 | exec = require('child_process').exec,
5 | url = require('stylus').url,
6 | stylus = require('gulp-stylus'),
7 | nib = require('nib');
8 |
9 | gulp.task('connect', function () {
10 | connect.server({
11 | root: ['bin'],
12 | port: 8000,
13 | livereload: true
14 | });
15 | });
16 |
17 | gulp.task('html', function () {
18 | gulp.src('./bin/*.html')
19 | .pipe(connect.reload());
20 | });
21 |
22 | gulp.task('js', function () {
23 | gulp.src('./bin/*.js')
24 | .pipe(connect.reload());
25 | });
26 |
27 | gulp.task('build', function(cb) {
28 | seq("stylus", "haxe", cb);
29 | });
30 |
31 | gulp.task('haxe', function(cb) {
32 | exec("haxe build.hxml", function(err, stdout, stderr) {
33 | cb(err);
34 | });
35 | });
36 |
37 | gulp.task('stylus', function() {
38 | return gulp.src('./style/sui.styl')
39 | .pipe(stylus({
40 | use: [nib()],
41 | compress: true,
42 | url: 'embedurl'
43 | }))
44 | .pipe(gulp.dest('./css'));
45 | });
46 |
47 | gulp.task('watch', function () {
48 | gulp.watch(['./style/icons/*.svg'], ['build'] );
49 | gulp.watch(['./bin/*.html'], ['html']);
50 | gulp.watch(['./bin/controls.js'], ['js']);
51 | gulp.watch(['./style/*.styl'], ['build']);
52 | });
53 |
54 | gulp.task('default', function (cb) {
55 | seq(['connect'], ['build'], ['watch'], cb);
56 | });
57 |
--------------------------------------------------------------------------------
/src/sui/components/Grid.hx:
--------------------------------------------------------------------------------
1 | package sui.components;
2 |
3 | import dots.Html;
4 | import dots.Query;
5 | import js.html.DOMElement;
6 | import sui.controls.IControl;
7 | using thx.Arrays;
8 | using thx.Functions;
9 |
10 | class Grid {
11 | public var el(default, null) : DOMElement;
12 |
13 | public function new() {
14 | el = Html.parse('');
15 | }
16 |
17 | public function add(cell : CellContent) switch cell {
18 | case Single(control):
19 | var container = Html.parse(' |
');
20 | Query.first("td", container).appendChild(control.el);
21 | el.appendChild(container);
22 | case HorizontalPair(left, right):
23 | var container = Html.parse(' | |
');
24 | Query.first(".sui-left", container).appendChild(left.el);
25 | Query.first(".sui-right", container).appendChild(right.el);
26 | el.appendChild(container);
27 | case VerticalPair(top, bottom):
28 | var containers = Html.parseArray(' |
|
');
29 | Query.first("td", containers[0]).appendChild(top.el);
30 | Query.first("td", containers[1]).appendChild(bottom.el);
31 | containers.map.fn(el.appendChild(_));
32 | }
33 | }
34 |
35 | typedef WithElement = {
36 | public var el(default, null) : DOMElement;
37 | }
38 |
39 | enum CellContent {
40 | Single(control : WithElement);
41 | VerticalPair(top : WithElement, bottom : WithElement);
42 | HorizontalPair(left : WithElement, right : WithElement);
43 | }
44 |
--------------------------------------------------------------------------------
/src/sui/controls/LabelControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import dots.Html;
4 | import dots.Query;
5 | import js.html.DOMElement as Element;
6 |
7 | class LabelControl implements IControl {
8 | public var el(default, null) : Element;
9 | public var output(default, null) : Element;
10 | public var defaultValue(default, null) : String;
11 | public var streams(default, null) : ControlStreams;
12 |
13 | var values : ControlValues;
14 |
15 | public function new(defaultValue : String) {
16 | var template = '';
17 |
18 | this.defaultValue = defaultValue;
19 |
20 | values = new ControlValues(defaultValue);
21 | streams = new ControlStreams(values.value, values.focused, values.enabled);
22 |
23 | el = Html.parse(template);
24 | output = Query.first("output", el);
25 |
26 | values.enabled.subscribe(function(v) if(v) {
27 | el.classList.add("sui-disabled");
28 | } else {
29 | el.classList.remove("sui-disabled");
30 | });
31 | }
32 |
33 | public function set(v : String) {
34 | output.innerHTML = v;
35 | values.value.set(v);
36 | }
37 |
38 | public function get() : String
39 | return values.value.get();
40 |
41 | public function isEnabled()
42 | return values.enabled.get();
43 |
44 | public function isFocused()
45 | return values.focused.get();
46 |
47 | public function disable()
48 | values.enabled.set(false);
49 |
50 | public function enable()
51 | values.enabled.set(true);
52 |
53 | public function focus() {}
54 |
55 | public function blur() {}
56 |
57 | public function reset()
58 | set(defaultValue);
59 | }
--------------------------------------------------------------------------------
/src/sui/controls/NumberRangeControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import sui.controls.Options;
4 | using thx.Floats;
5 |
6 | class NumberRangeControl extends DoubleInputControl {
7 | public function new(value : T, ?options : OptionsNumber) {
8 | super(value, "float-range", "input", "range", "input", "number", function(v) return v != null, options);
9 |
10 | if(null != options.autocomplete) {
11 | input1.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
12 | input2.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
13 | }
14 |
15 | if(null != options.min) {
16 | input1.setAttribute('min', '${options.min}');
17 | input2.setAttribute('min', '${options.min}');
18 | }
19 | if(null != options.max) {
20 | input1.setAttribute('max', '${options.max}');
21 | input2.setAttribute('max', '${options.max}');
22 | }
23 | if(null != options.step) {
24 | input1.setAttribute('step', '${options.step}');
25 | input2.setAttribute('step', '${options.step}');
26 | }
27 | if(null != options.placeholder)
28 | input2.setAttribute('placeholder', '${options.placeholder}');
29 |
30 | if(null != options.list)
31 | new DataList(el, options.list.map(function(o) {
32 | return {
33 | label : o.label,
34 | value : '${o.value}'
35 | };
36 | })).applyTo(input1).applyTo(input2);
37 | else if(null != options.values)
38 | new DataList(el, options.values.map(function(o) {
39 | return {
40 | label : '${o}',
41 | value : '${o}'
42 | };
43 | })).applyTo(input1).applyTo(input2);
44 |
45 | setInputs(value);
46 | }
47 |
48 | override function setInput1(v : T)
49 | input1.value = '$v';
50 |
51 | override function setInput2(v : T)
52 | input2.value = '$v';
53 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SUI
2 | ## Simple User Interface: Html + Haxe
3 |
4 | Example:
5 |
6 | ```haxe
7 | var ui = new sui.Sui();
8 | ui.bool("boolean", function(v) trace('bool: $v'));
9 | ui.color("color", {
10 | list : [
11 | { value : "#FF0000", label : "red" },
12 | { value : "#00FF00", label : "blue" },
13 | { value : "#0000FF", label : "green" }]
14 | }, function(v) trace('color: $v'));
15 | ui.int("int range", 20, {
16 | min : 10,
17 | max : 30
18 | }, function(v) trace('int range: $v'));
19 | ui.trigger("trigger", function() trace("triggered"));
20 | ui.attach();
21 | ```
22 |
23 | [A live sample of some of the available controls](https://rawgit.com/fponticelli/sui/master/bin/controls.html).
24 | ## Installation
25 | SUI is available on haxelib (http://lib.haxe.org/p/sui/)
26 |
27 | To install, run:
28 | ```haxelib install sui```
29 |
30 | ##### Dependencies
31 | SUI dependents on _[thx.core](https://github.com/fponticelli/thx.core), [thx.promise](https://github.com/fponticelli/thx.promise), [thx.stream](https://github.com/fponticelli/thx.stream), [thx.stream.dom](https://github.com/fponticelli/thx.stream.dom), [dots](https://github.com/fponticelli/dots)_
32 |
33 | ## TODO
34 |
35 | ### API
36 | * Sui.add(): macro automap field/variable to control
37 | * append to container (with position and close controls)
38 | * Sui.hide()/Sui.show() (with default keyboard control H)
39 | * Sui.open()/Sui.close()
40 | * presets? save/restore?
41 | * listen?
42 |
43 | ### Controls
44 |
45 | * folder (with open/collapse)
46 | * select string (options)
47 | * select float
48 | * select int
49 | * select date
50 | * text area
51 | * objects and nested objects
52 | * arrays
53 | * unstructured objects (create field together with values)
54 |
55 |
56 | ### Inspiration
57 |
58 | [dat-gui](http://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage)
59 |
--------------------------------------------------------------------------------
/src/sui/controls/MultiControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.DOMElement as Element;
4 | using thx.stream.Emitter;
5 | using thx.Arrays;
6 | using thx.Nulls;
7 | using thx.Functions;
8 |
9 | class MultiControl implements IControl {
10 | public var el(default, null) : Element;
11 | public var defaultValue(default, null) : T;
12 | public var streams(default, null) : ControlStreams;
13 |
14 | var controls : Array>;
15 | var enabled : Bool;
16 | var values : ControlValues;
17 | public function new(defaultValue : T, element : Element, controls : Array>) {
18 | this.el = element;
19 | this.defaultValue = defaultValue;
20 | this.controls = controls;
21 | values = new ControlValues(defaultValue);
22 | streams = new ControlStreams(values.value, values.focused, values.enabled);
23 | controls.map.fn(_.streams.focused.feed(values.focused));
24 |
25 | streams.enabled.subscribe(function(value) {
26 | controls.map.fn(value ? _.enable() : _.disable());
27 | });
28 | }
29 |
30 | public function set(v : T) : Void
31 | values.value.set(v);
32 |
33 | public function get() : T
34 | return values.value.get();
35 |
36 | public function isEnabled() : Bool
37 | return values.enabled.get();
38 |
39 | public function isFocused() : Bool
40 | return values.focused.get();
41 |
42 | public function disable() : Void
43 | values.enabled.set(false);
44 |
45 | public function enable() : Void
46 | values.enabled.set(true);
47 |
48 | public function focus() : Void
49 | controls.first().with(_.focus());
50 |
51 | public function blur() : Void {
52 | var el = js.Browser.document.activeElement;
53 | controls
54 | // TODO el could just contain _.el
55 | .filter.fn(_.el == el)
56 | .first().with(el.blur());
57 | }
58 |
59 | public function reset() : Void
60 | set(defaultValue);
61 | }
62 |
--------------------------------------------------------------------------------
/src/sui/controls/TimeControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using StringTools;
4 | import sui.controls.Options;
5 |
6 | class TimeControl extends SingleInputControl {
7 | public function new(value : Float, ?options : OptionsTime) {
8 | if(null == options)
9 | options = {};
10 | super(value, "input", "time", "time", options);
11 |
12 | if(null != options.autocomplete)
13 | input.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
14 |
15 | if(null != options.min)
16 | input.setAttribute('min', timeToString(options.min));
17 | if(null != options.max)
18 | input.setAttribute('max', timeToString(options.max));
19 |
20 | if(null != options.list)
21 | new DataList(el, options.list.map(function(o) {
22 | return {
23 | label : o.label,
24 | value : timeToString(o.value)
25 | };
26 | })).applyTo(input);
27 | else if(null != options.values)
28 | new DataList(el, options.values.map(function(o) {
29 | return {
30 | label : timeToString(o),
31 | value : timeToString(o)
32 | };
33 | })).applyTo(input);
34 | }
35 |
36 | public static function timeToString(t : Float) {
37 | var h = Math.floor(t / 3600000);
38 | t -= h * 3600000;
39 | var m = Math.floor(t / 60000);
40 | t -= m * 60000;
41 | var s = t / 1000,
42 | hh = '$h'.lpad("0", 2),
43 | mm = '$m'.lpad("0", 2),
44 | ss = (s >= 10 ? '' : '0') + s;
45 | return '$hh:$mm:$ss';
46 | }
47 |
48 | public static function stringToTime(t : String) : Float {
49 | var p = t.split(":"),
50 | h = Std.parseInt(p[0]),
51 | m = Std.parseInt(p[1]),
52 | s = Std.parseFloat(p[2]);
53 | return s * 1000 + m * 60000 + h * 3600000;
54 | }
55 |
56 | override function setInput(v : Float)
57 | input.value = timeToString(v);
58 |
59 | override function getInput() : Float
60 | return stringToTime(input.value);
61 | }
--------------------------------------------------------------------------------
/src/sui/controls/ChoiceControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.DOMElement as Element;
4 | using thx.stream.Emitter;
5 | using thx.Arrays;
6 | using thx.Functions;
7 | using thx.Nulls;
8 |
9 | class ChoiceControl implements IControl {
10 | public var el(default, null) : Element;
11 | public var defaultValue(default, null) : T;
12 | public var streams(default, null) : ControlStreams;
13 |
14 | var controls : Array>;
15 | var enabled : Bool;
16 | var values : ControlValues;
17 | public function new(defaultValue : T, getSelectValue T -> TSelect, createSelectControl : TSelect -> IControl, createControls : TSelect -> ) {
18 | this.el = element;
19 | this.defaultValue = defaultValue;
20 | this.controls = controls;
21 | values = new ControlValues(defaultValue);
22 | streams = new ControlStreams(values.value, values.focused, values.enabled);
23 | controls.map.fn(_.streams.focused.feed(values.focused));
24 |
25 | streams.enabled.subscribe(function(value) {
26 | controls.map.fn(value ? _.enable() : _.disable());
27 | });
28 | }
29 |
30 | public function set(v : T) : Void
31 | values.value.set(v);
32 |
33 | public function get() : T
34 | return values.value.get();
35 |
36 | public function isEnabled() : Bool
37 | return values.enabled.get();
38 |
39 | public function isFocused() : Bool
40 | return values.focused.get();
41 |
42 | public function disable() : Void
43 | values.enabled.set(false);
44 |
45 | public function enable() : Void
46 | values.enabled.set(true);
47 |
48 | public function focus() : Void
49 | controls.first().with(_.focus());
50 |
51 | public function blur() : Void {
52 | var el = js.Browser.document.activeElement;
53 | controls
54 | // TODO el could just contain _.el
55 | .filter.fn(_.el == el)
56 | .first().with(el.blur());
57 | }
58 |
59 | public function reset() : Void
60 | set(defaultValue);
61 | }
62 |
--------------------------------------------------------------------------------
/src/sui/controls/Options.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | typedef Options = {
4 | ?disabled : Bool,
5 | ?autofocus : Bool,
6 | ?allownull : Bool
7 | }
8 |
9 | typedef OptionsList = {> Options,
10 | ?list : Array<{ label : String, value : T }>,
11 | ?values : Array
12 | }
13 |
14 | typedef OptionsSelect = {> OptionsList,
15 | ?labelfornull : String
16 | }
17 |
18 | typedef OptionsColor = {> OptionsList,
19 | ?autocomplete : Bool
20 | }
21 |
22 | typedef OptionsDate = {> OptionsList,
23 | ?min : Date,
24 | ?max : Date,
25 | ?autocomplete : Bool
26 | }
27 |
28 | typedef OptionsKindDate = {> OptionsDate,
29 | ?kind : DateKind,
30 | ?labelfornull : String,
31 | ?listonly : Bool
32 | }
33 |
34 | enum DateKind {
35 | DateOnly;
36 | DateTime;
37 | }
38 |
39 | typedef OptionsNumber = {> OptionsList,
40 | ?min : T,
41 | ?max : T,
42 | ?step : T,
43 | ?autocomplete : Bool,
44 | ?placeholder : String
45 | }
46 |
47 | typedef OptionsKindFloat = {> OptionsNumber,
48 | ?kind : FloatKind,
49 | ?labelfornull : String,
50 | ?listonly : Bool
51 | }
52 |
53 | enum FloatKind {
54 | FloatNumber;
55 | FloatTime;
56 | }
57 |
58 | typedef OptionsKindInt = {> OptionsNumber,
59 | ?labelfornull : String,
60 | ?listonly : Bool
61 | }
62 |
63 | typedef OptionsText = {> OptionsList,
64 | ?maxlength : Int,
65 | ?autocomplete : Bool,
66 | ?pattern : String,
67 | ?placeholder : String
68 | }
69 |
70 | typedef OptionsKindText = {> OptionsText,
71 | ?kind : TextKind,
72 | ?labelfornull : String,
73 | ?listonly : Bool
74 | }
75 |
76 | enum TextKind {
77 | TextEmail;
78 | TextPassword;
79 | TextSearch;
80 | TextTel;
81 | PlainText;
82 | TextUrl;
83 | }
84 |
85 | typedef OptionsTime = {> Options,
86 | ?min : Float,
87 | ?max : Float,
88 | ?autocomplete : Bool,
89 | ?list : Array<{ label : String, value : Float }>,
90 | ?values : Array
91 | }
92 |
93 | typedef OptionsFolder = {
94 | ?collapsible : Bool,
95 | ?collapsed : Bool
96 | }
--------------------------------------------------------------------------------
/src/sui/controls/TriggerControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import dots.Html;
4 | import dots.Query;
5 | import js.html.DOMElement as Element;
6 | import js.html.ButtonElement;
7 | import thx.Nil;
8 | using thx.stream.dom.Dom;
9 |
10 | class TriggerControl implements IControl {
11 | public var el(default, null) : Element;
12 | public var button(default, null) : ButtonElement;
13 | public var defaultValue(default, null) : Nil;
14 | public var streams(default, null) : ControlStreams;
15 |
16 | var values : ControlValues;
17 |
18 | public function new(label : String, ?options : Options) {
19 | var template = '';
20 |
21 | if(null == options)
22 | options = {};
23 |
24 | defaultValue = nil;
25 |
26 | el = Html.parse(template);
27 | button = (cast Query.first('button', el) : ButtonElement);
28 |
29 | values = new ControlValues(nil);
30 | var emitter = button.streamClick().toNil();
31 | streams = new ControlStreams(emitter, values.focused, values.enabled);
32 |
33 | values.enabled.subscribe(function(v) if(v) {
34 | el.classList.add("sui-disabled");
35 | button.removeAttribute("disabled");
36 | } else {
37 | el.classList.remove("sui-disabled");
38 | button.setAttribute("disabled", "disabled");
39 | });
40 |
41 | values.focused.subscribe(function(v) if(v) {
42 | el.classList.add("sui-focused");
43 | } else {
44 | el.classList.remove("sui-focused");
45 | });
46 |
47 | button.streamFocus().feed(values.focused);
48 |
49 | if(options.autofocus)
50 | focus();
51 | if(options.disabled)
52 | disable();
53 | }
54 |
55 | public function set(v : Nil)
56 | button.click();
57 |
58 | public function get() : Nil
59 | return nil;
60 |
61 | public function isEnabled()
62 | return values.enabled.get();
63 |
64 | public function isFocused()
65 | return values.focused.get();
66 |
67 | public function disable()
68 | values.enabled.set(false);
69 |
70 | public function enable()
71 | values.enabled.set(true);
72 |
73 | public function focus()
74 | button.focus();
75 |
76 | public function blur()
77 | button.blur();
78 |
79 | public function reset()
80 | set(defaultValue);
81 | }
--------------------------------------------------------------------------------
/src/sui/controls/BaseDateControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | using StringTools;
4 | import sui.controls.Options;
5 | using thx.Nulls;
6 | using thx.Strings;
7 |
8 | class BaseDateControl extends SingleInputControl {
9 | var dateToString : Date -> String;
10 | public function new(value : Date, name : String, type : String, dateToString : Date -> String, ?options : OptionsDate) {
11 | if(null == options)
12 | options = {};
13 |
14 | this.dateToString = dateToString;
15 |
16 | super(value, "input", name, type, options);
17 |
18 | if(null != options.autocomplete)
19 | input.setAttribute('autocomplete', options.autocomplete ? 'on' : 'off');
20 |
21 | if(null != options.min)
22 | input.setAttribute('min', dateToString(options.min));
23 | if(null != options.max)
24 | input.setAttribute('max', dateToString(options.max));
25 |
26 | if(null != options.list)
27 | new DataList(el, options.list.map(function(o) {
28 | return {
29 | label : o.label,
30 | value : dateToString(o.value)
31 | };
32 | })).applyTo(input);
33 | else if(null != options.values)
34 | new DataList(el, options.values.map(function(o) {
35 | return {
36 | label : o.toString(),
37 | value : dateToString(o)
38 | };
39 | })).applyTo(input);
40 | }
41 |
42 | override function setInput(v : Date)
43 | input.value = dateToString(v);
44 |
45 | override function getInput() : Date
46 | return input.value.isEmpty() ? null : fromRFC(input.value);
47 |
48 | public static function toRFCDate(date : Date) {
49 | var y = date.getFullYear(),
50 | m = '${date.getMonth()+1}'.lpad('0', 2),
51 | d = '${date.getDate()}'.lpad('0', 2);
52 | return '$y-$m-$d';
53 | }
54 |
55 | public static function toRFCDateTime(date : Date) {
56 | var d = toRFCDate(date),
57 | hh = '${date.getHours()}'.lpad('0', 2),
58 | mm = '${date.getMinutes()}'.lpad('0', 2),
59 | ss = '${date.getSeconds()}'.lpad('0', 2);
60 | return '${d}T$hh:$mm:$ss';
61 | }
62 |
63 | public static function toRFCDateTimeNoSeconds(date : Date) {
64 | var d = toRFCDate(date),
65 | hh = '${date.getHours()}'.lpad('0', 2),
66 | mm = '${date.getMinutes()}'.lpad('0', 2);
67 | return '${d}T$hh:$mm:00';
68 | }
69 |
70 | public static function fromRFC(date : String) {
71 | var dp = date.split("T")[0],
72 | dt = (date.split("T")[1]).or("00:00:00"),
73 | p = dp.split("-"),
74 | y = Std.parseInt(p[0]),
75 | m = Std.parseInt(p[1])-1,
76 | d = Std.parseInt(p[2]),
77 | t = dt.split(":"),
78 | hh = Std.parseInt(t[0]),
79 | mm = Std.parseInt(t[1]),
80 | ss = Std.parseInt(t[2]);
81 | return new Date(y, m, d, hh, mm, ss);
82 | }
83 | }
--------------------------------------------------------------------------------
/src/sui/controls/SingleInputControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import dots.Html;
4 | import dots.Query;
5 | import js.html.DOMElement as Element;
6 | import js.html.InputElement;
7 | import thx.error.AbstractMethod;
8 | using thx.stream.dom.Dom;
9 |
10 | class SingleInputControl implements IControl {
11 | public var el(default, null) : Element;
12 | public var input(default, null) : InputElement;
13 | public var defaultValue(default, null) : T;
14 | public var streams(default, null) : ControlStreams;
15 |
16 | var values : ControlValues;
17 |
18 | public function new(defaultValue : T, event : String, name : String, type : String, options : Options) {
19 | var template = '';
20 |
21 | if(null == options)
22 | options = {};
23 | if(null == options.allownull)
24 | options.allownull = true;
25 |
26 | this.defaultValue = defaultValue;
27 |
28 | values = new ControlValues(defaultValue);
29 | streams = new ControlStreams(values.value, values.focused, values.enabled);
30 |
31 | el = Html.parse(template);
32 | input = (cast Query.first('input', el) : InputElement);
33 |
34 | values.enabled.subscribe(function(v) if(v) {
35 | el.classList.add("sui-disabled");
36 | input.removeAttribute("disabled");
37 | } else {
38 | el.classList.remove("sui-disabled");
39 | input.setAttribute("disabled", "disabled");
40 | });
41 |
42 | values.focused.subscribe(function(v) if(v) {
43 | el.classList.add("sui-focused");
44 | } else {
45 | el.classList.remove("sui-focused");
46 | });
47 |
48 | setInput(defaultValue);
49 |
50 | input.streamFocus().feed(values.focused);
51 | input.streamEvent(event)
52 | .map(function(_) return getInput())
53 | .feed(values.value);
54 |
55 | if(!options.allownull)
56 | input.setAttribute("required", "required");
57 | if(options.autofocus)
58 | focus();
59 | if(options.disabled)
60 | disable();
61 | }
62 |
63 | function setInput(v : T)
64 | throw new AbstractMethod();
65 |
66 | function getInput() : T
67 | return throw new AbstractMethod();
68 |
69 | public function set(v : T) {
70 | setInput(v);
71 | values.value.set(v);
72 | }
73 |
74 | public function get() : T
75 | return values.value.get();
76 |
77 | public function isEnabled()
78 | return values.enabled.get();
79 |
80 | public function isFocused()
81 | return values.focused.get();
82 |
83 | public function disable()
84 | values.enabled.set(false);
85 |
86 | public function enable()
87 | values.enabled.set(true);
88 |
89 | public function focus()
90 | input.focus();
91 |
92 | public function blur()
93 | input.blur();
94 |
95 | public function reset()
96 | set(defaultValue);
97 | }
98 |
--------------------------------------------------------------------------------
/src/sui/controls/SelectControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.DOMElement as Element;
4 | import js.html.SelectElement;
5 | import dots.Html;
6 | import dots.Query;
7 | import sui.controls.Options;
8 | using thx.Arrays;
9 | using thx.Nulls;
10 | using thx.stream.dom.Dom;
11 | using thx.Functions;
12 |
13 | class SelectControl implements IControl {
14 | public var el(default, null) : Element;
15 | public var select(default, null) : SelectElement;
16 | public var defaultValue(default, null) : T;
17 | public var streams(default, null) : ControlStreams;
18 |
19 | var options : Array;
20 | var values : ControlValues;
21 | var count : Int = 0;
22 |
23 | function new(defaultValue : T, name : String, options : OptionsSelect) {
24 | var template = '';
25 | if(null == options)
26 | throw " A select control requires an option object with values or list set";
27 | if(null == options.values && null == options.list)
28 | throw " A select control requires either the values or list option";
29 | if(null == options.allownull)
30 | options.allownull = false;
31 | this.defaultValue = defaultValue;
32 |
33 | values = new ControlValues(defaultValue);
34 | streams = new ControlStreams(values.value, values.focused, values.enabled);
35 |
36 | el = Html.parse(template);
37 | select = (cast Query.first('select', el) : SelectElement);
38 |
39 | values.enabled.subscribe(function(v) if(v) {
40 | el.classList.add("sui-disabled");
41 | select.removeAttribute("disabled");
42 | } else {
43 | el.classList.remove("sui-disabled");
44 | select.setAttribute("disabled", "disabled");
45 | });
46 |
47 | values.focused.subscribe(function(v) if(v) {
48 | el.classList.add("sui-focused");
49 | } else {
50 | el.classList.remove("sui-focused");
51 | });
52 |
53 | this.options = [];
54 |
55 | (options.allownull ? [{ label : (options.labelfornull).or("- none -"), value : null }] : []).concat(
56 | (options.list)
57 | .or(options.values.map.fn({ value : _, label : Std.string(_) })))
58 | .map.fn(addOption(_.label, _.value));
59 |
60 | setInput(defaultValue);
61 |
62 | select.streamFocus().feed(values.focused);
63 | select.streamEvent("change")
64 | .map(function(_) return getInput())
65 | .feed(values.value);
66 |
67 | if(options.autofocus)
68 | focus();
69 | if(options.disabled)
70 | disable();
71 | }
72 |
73 | public function addOption(label : String, value : T) {
74 | var index = count++,
75 | option = Html.parse('');
76 | options[index] = value;
77 | select.appendChild(option);
78 | return option;
79 | }
80 |
81 | function setInput(v : T) {
82 | var index = options.indexOf(v);
83 | if(index < 0) throw 'value "$v" is not included in this select control';
84 | select.selectedIndex = index;
85 | }
86 |
87 | function getInput() {
88 | return options[select.selectedIndex];
89 | }
90 |
91 | public function set(v : T) {
92 | setInput(v);
93 | values.value.set(v);
94 | }
95 |
96 | public function get() : T
97 | return values.value.get();
98 |
99 | public function isEnabled()
100 | return values.enabled.get();
101 |
102 | public function isFocused()
103 | return values.focused.get();
104 |
105 | public function disable()
106 | values.enabled.set(false);
107 |
108 | public function enable()
109 | values.enabled.set(true);
110 |
111 | public function focus()
112 | select.focus();
113 |
114 | public function blur()
115 | select.blur();
116 |
117 | public function reset()
118 | set(defaultValue);
119 | }
120 |
--------------------------------------------------------------------------------
/src/sui/controls/DoubleInputControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import dots.Html;
4 | import dots.Query;
5 | import js.html.DOMElement as Element;
6 | import js.html.InputElement;
7 | import thx.error.AbstractMethod;
8 | using thx.stream.dom.Dom;
9 | import dots.Detect;
10 |
11 | class DoubleInputControl implements IControl {
12 | public var el(default, null) : Element;
13 | public var input1(default, null) : InputElement;
14 | public var input2(default, null) : InputElement;
15 | public var defaultValue(default, null) : T;
16 | public var streams(default, null) : ControlStreams;
17 |
18 | var values : ControlValues;
19 |
20 | public function new(defaultValue : T, name : String, event1 : String, type1 : String, event2 : String, type2 : String, filter : T -> Bool, options : Options) {
21 | var template = '';
22 |
23 | if(null == options)
24 | options = {};
25 | if(null == options.allownull)
26 | options.allownull = true;
27 |
28 | this.defaultValue = defaultValue;
29 |
30 | values = new ControlValues(defaultValue);
31 | streams = new ControlStreams(values.value, values.focused, values.enabled);
32 |
33 | el = Html.parse(template);
34 | input1 = (cast Query.first('.input1', el) : InputElement);
35 | input2 = (cast Query.first('.input2', el) : InputElement);
36 |
37 | values.enabled.subscribe(function(v) if(v) {
38 | el.classList.add("sui-disabled");
39 | input1.removeAttribute("disabled");
40 | input2.removeAttribute("disabled");
41 | } else {
42 | el.classList.remove("sui-disabled");
43 | input1.setAttribute("disabled", "disabled");
44 | input2.setAttribute("disabled", "disabled");
45 | });
46 |
47 | values.focused.subscribe(function(v) if(v) {
48 | el.classList.add("sui-focused");
49 | } else {
50 | el.classList.remove("sui-focused");
51 | });
52 |
53 | input1.streamFocus()
54 | .merge(input2.streamFocus())
55 | .feed(values.focused);
56 | input1.streamEvent(event1)
57 | .map(function(_) return getInput1())
58 | .subscribe(function(v) {
59 | setInput2(v);
60 | values.value.set(v);
61 | });
62 | input2.streamEvent(event2)
63 | .map(function(_) return getInput2())
64 | .filter(filter)
65 | .subscribe(function(v) {
66 | setInput1(v);
67 | values.value.set(v);
68 | });
69 |
70 | if(!options.allownull) {
71 | input1.setAttribute("required", "required");
72 | input2.setAttribute("required", "required");
73 | }
74 | if(options.autofocus)
75 | focus();
76 | if(options.disabled)
77 | disable();
78 |
79 | if(!Detect.supportsInput(type1))
80 | input1.style.display = "none";
81 | }
82 |
83 | function setInputs(v : T) {
84 | setInput1(v);
85 | setInput2(v);
86 | }
87 |
88 | function setInput1(v : T)
89 | throw new AbstractMethod();
90 |
91 | function setInput2(v : T)
92 | throw new AbstractMethod();
93 |
94 | function getInput1() : T
95 | return throw new AbstractMethod();
96 |
97 | function getInput2() : T
98 | return throw new AbstractMethod();
99 |
100 | public function set(v : T) {
101 | setInputs(v);
102 | values.value.set(v);
103 | }
104 |
105 | public function get() : T
106 | return values.value.get();
107 |
108 | public function isEnabled()
109 | return values.enabled.get();
110 |
111 | public function isFocused()
112 | return values.focused.get();
113 |
114 | public function disable()
115 | values.enabled.set(false);
116 |
117 | public function enable()
118 | values.enabled.set(true);
119 |
120 | public function focus()
121 | input2.focus();
122 |
123 | public function blur() {
124 | var el = js.Browser.document.activeElement;
125 | if(el == input1 || el == input2)
126 | el.blur();
127 | }
128 |
129 | public function reset()
130 | set(defaultValue);
131 | }
132 |
--------------------------------------------------------------------------------
/src/sui/controls/ArrayControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.ButtonElement;
4 | import js.html.DOMElement as Element;
5 | import sui.controls.Options;
6 | import dots.Html;
7 | import dots.Query;
8 | using thx.stream.dom.Dom;
9 | using thx.stream.Emitter;
10 | using thx.Arrays;
11 | using thx.Functions;
12 | using thx.Nulls;
13 |
14 | class ArrayControl implements IControl> {
15 | public var el(default, null) : Element;
16 | public var ul(default, null) : Element;
17 | public var addButton(default, null) : Element;
18 | public var defaultValue(default, null) : Array;
19 | public var defaultElementValue(default, null) : T;
20 | public var streams(default, null) : ControlStreams>;
21 | public var createElementControl(default, null) : T -> IControl;
22 | public var length(default, null) : Int;
23 |
24 | var values : ControlValues>;
25 | var elements : Array<{
26 | control : IControl,
27 | el : Element,
28 | index : Int
29 | }>;
30 |
31 | public function new(defaultValue : Array, defaultElementValue : T, createElementControl : T -> IControl, ?options : Options) {
32 | var template = '';
36 | options = (options).or({});
37 | this.defaultValue = defaultValue;
38 | this.defaultElementValue = defaultElementValue;
39 | this.createElementControl = createElementControl;
40 | this.elements = [];
41 | length = 0;
42 |
43 | values = new ControlValues(defaultValue);
44 | streams = new ControlStreams(values.value, values.focused.debounce(0), values.enabled);
45 |
46 | el = Html.parse(template);
47 | ul = Query.first('ul', el);
48 | addButton = Query.first('.sui-icon-add', el);
49 |
50 | addButton.streamClick().subscribe(function(_) addControl(defaultElementValue));
51 |
52 | values.enabled.subscribe(function(v) if(v) {
53 | el.classList.add("sui-disabled");
54 | } else {
55 | el.classList.remove("sui-disabled");
56 | });
57 |
58 | values.focused.subscribe(function(v) if(v) {
59 | el.classList.add("sui-focused");
60 | } else {
61 | el.classList.remove("sui-focused");
62 | });
63 |
64 | values.enabled
65 | .negate()
66 | .subscribe(el.subscribeToggleClass("sui-disabled"));
67 |
68 | values.enabled.subscribe(function(v) {
69 | elements.map.fn(v ? _.control.enable() : _.control.disable());
70 | });
71 |
72 | setValue(defaultValue);
73 |
74 | reset();
75 |
76 | if(options.autofocus)
77 | focus();
78 | if(options.disabled)
79 | disable();
80 | }
81 |
82 | function addControl(value : T) {
83 | var o = {
84 | control : createElementControl(value),
85 | el : Html.parse('
86 |
87 |
88 |
89 | '),
90 | index : length++
91 | };
92 |
93 | ul.appendChild(o.el);
94 |
95 | var removeElement = Query.first(".sui-icon-remove", o.el),
96 | upElement = Query.first(".sui-icon-up", o.el),
97 | downElement = Query.first(".sui-icon-down", o.el),
98 | controlContainer = Query.first(".sui-control-container", o.el);
99 |
100 | controlContainer.appendChild(o.control.el);
101 |
102 | removeElement.streamClick().subscribe(function(_) {
103 | ul.removeChild(o.el);
104 | elements.splice(o.index, 1);
105 | for(i in o.index...elements.length)
106 | elements[i].index--;
107 | length--;
108 | updateValue();
109 | });
110 |
111 | elements.push(o);
112 | o.control.streams.value.subscribe(function(_) updateValue());
113 | o.control.streams
114 | .focused
115 | .subscribe(o.el.subscribeToggleClass("sui-focus"));
116 |
117 | o.control.streams
118 | .focused
119 | .feed(values.focused);
120 |
121 | upElement.streamClick()
122 | .subscribe(function(_) {
123 | var pos = o.index,
124 | prev = elements[pos-1];
125 | elements[pos] = prev;
126 | elements[pos-1] = o;
127 | prev.index = pos;
128 | o.index = pos - 1;
129 | ul.insertBefore(o.el, prev.el);
130 | updateValue();
131 | });
132 |
133 | downElement.streamClick()
134 | .subscribe(function(_) {
135 | var pos = o.index,
136 | next = elements[pos+1];
137 | elements[pos] = next;
138 | elements[pos+1] = o;
139 | next.index = pos;
140 | o.index = pos + 1;
141 | ul.insertBefore(next.el, o.el);
142 | updateValue();
143 | });
144 | }
145 |
146 | function setValue(v : Array)
147 | v.map.fn(addControl(_));
148 |
149 | function getValue()
150 | return elements.map.fn(_.control.get());
151 |
152 | function updateValue()
153 | values.value.set(getValue());
154 |
155 | public function set(v : Array) {
156 | clear();
157 | setValue(v);
158 | values.value.set(v);
159 | }
160 |
161 | public function get() : Array
162 | return values.value.get();
163 |
164 | public function isEnabled()
165 | return values.enabled.get();
166 |
167 | public function isFocused()
168 | return values.focused.get();
169 |
170 | public function disable()
171 | values.enabled.set(false);
172 |
173 | public function enable()
174 | values.enabled.set(true);
175 |
176 | public function focus()
177 | if(elements.length > 0)
178 | elements.last().control.focus();
179 |
180 | public function blur() {
181 | var el = js.Browser.document.activeElement;
182 | elements
183 | // TODO el could just contain _.control.el
184 | .filter.fn(_.control.el == el)
185 | .first().with(el.blur());
186 | }
187 |
188 | public function reset()
189 | set(defaultValue);
190 |
191 | function clear() {
192 | length = 0;
193 | elements.map(function(item) {
194 | //control.destroy();
195 | ul.removeChild(item.el);
196 | });
197 | elements = [];
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/sui/controls/MapControl.hx:
--------------------------------------------------------------------------------
1 | package sui.controls;
2 |
3 | import js.html.ButtonElement;
4 | import js.html.DOMElement as Element;
5 | import sui.controls.Options;
6 | import dots.Html;
7 | import dots.Query;
8 | using thx.stream.dom.Dom;
9 | using thx.stream.Emitter;
10 | using thx.Arrays;
11 | using thx.Iterators;
12 | using thx.Nulls;
13 | using thx.Functions;
14 |
15 | class MapControl implements IControl