├── CHANGELOG.md ├── web ├── favicon.ico ├── main.dart ├── src │ ├── todo_model.dart │ ├── todo_view.dart │ ├── todos_collection_view.dart │ ├── todos_add_view.dart │ └── app.dart ├── styles.css ├── lib │ ├── model.dart │ ├── collection.dart │ ├── collection_view.dart │ └── view.dart └── index.html ├── README.md ├── .gitignore ├── analysis_options.yaml └── pubspec.yaml /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | - Initial version, created by Stagehand 4 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/DartBone/master/web/favicon.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DartBone 2 | 3 | Proof-of-concept of Backbone/Marionette in Dart. 4 | 5 | Not appropriate for any kind of use. 6 | -------------------------------------------------------------------------------- /web/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'src/app.dart'; 3 | 4 | void main() { 5 | var app = new App(parent: document.body); 6 | 7 | app.render(); 8 | } 9 | -------------------------------------------------------------------------------- /web/src/todo_model.dart: -------------------------------------------------------------------------------- 1 | import '../lib/model.dart'; 2 | 3 | class TodoModel extends Model { 4 | var attributes = {'task': ''}; 5 | 6 | TodoModel({this.attributes}) : super(attributes: attributes); 7 | } 8 | -------------------------------------------------------------------------------- /web/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Roboto); 2 | 3 | html, body { 4 | width: 100%; 5 | height: 100%; 6 | margin: 0; 7 | padding: 0; 8 | font-family: 'Roboto', sans-serif; 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | .pub/ 5 | build/ 6 | # Remove the following pattern if you wish to check in your lock file 7 | pubspec.lock 8 | 9 | # Directory created by dartdoc 10 | doc/api/ 11 | -------------------------------------------------------------------------------- /web/src/todo_view.dart: -------------------------------------------------------------------------------- 1 | import '../lib/view.dart'; 2 | 3 | class TodoView extends View { 4 | TodoView({model, parent}) : super(model: model, parent: parent); 5 | 6 | String template() { 7 | return """ 8 |
  • ${model.attributes['task']}
  • 9 | """; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /web/lib/model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | abstract class Model { 4 | Map attributes; 5 | final events = new StreamController.broadcast(); 6 | 7 | Model({this.attributes}); 8 | 9 | void set(key, value) { 10 | attributes[key] = value; 11 | trigger('change'); 12 | } 13 | 14 | trigger(String eventName) { 15 | events.add(eventName); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: true 3 | # exclude: 4 | # - path/to/excluded/files/** 5 | 6 | # Lint rules and documentation, see http://dart-lang.github.io/linter/lints 7 | linter: 8 | rules: 9 | - cancel_subscriptions 10 | - hash_and_equals 11 | - iterable_contains_unrelated_type 12 | - list_remove_unrelated_type 13 | - test_types_in_equals 14 | - unrelated_type_equality_checks 15 | - valid_regexps 16 | -------------------------------------------------------------------------------- /web/src/todos_collection_view.dart: -------------------------------------------------------------------------------- 1 | import '../lib/collection_view.dart'; 2 | import 'todo_view.dart'; 3 | 4 | class TodosCollectionView extends CollectionView { 5 | TodosCollectionView({collection, containerTag, parent}) 6 | : super( 7 | collection: collection, 8 | containerTag: containerTag, 9 | parent: parent, 10 | ); 11 | 12 | renderItem(model, parent) { 13 | return new TodoView(model: model, parent: parent); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: mari 2 | description: An absolute bare-bones web app. 3 | version: 0.0.1 4 | #homepage: https://www.example.com 5 | #author: stephengrider 6 | 7 | environment: 8 | sdk: '>=1.20.1 <2.0.0' 9 | 10 | #dependencies: 11 | # path: ^1.4.1 12 | 13 | dev_dependencies: 14 | browser: ^0.10.0 15 | dart_to_js_script_rewriter: ^1.0.1 16 | 17 | transformers: 18 | - dart_to_js_script_rewriter 19 | 20 | # Uncomment the following in sdk 1.24+ to make pub serve 21 | # use dartdevc (webdev.dartlang.org/tools/dartdevc). 22 | #web: 23 | # compiler: 24 | # debug: dartdevc 25 | -------------------------------------------------------------------------------- /web/lib/collection.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:async'; 3 | import 'model.dart'; 4 | 5 | class Collection extends ListBase { 6 | final List models = []; 7 | final events = new StreamController.broadcast(); 8 | 9 | int get length => models.length; 10 | Model operator [](int index) => models[index]; 11 | 12 | void operator []=(int index, Model model) { 13 | models[index] = model; 14 | trigger('change'); 15 | } 16 | 17 | void set length(int newLength) { 18 | models.length = newLength; 19 | } 20 | 21 | trigger(String eventName) { 22 | events.add(eventName); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | mari 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /web/src/todos_add_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import '../lib/collection.dart'; 3 | import '../lib/view.dart'; 4 | import 'todo_model.dart'; 5 | 6 | class TodosAddView extends View { 7 | Collection collection; 8 | 9 | TodosAddView({this.collection, model, parent}) 10 | : super(model: model, parent: parent); 11 | 12 | String template() { 13 | return """ 14 |
    15 | Enter new todo: 16 | 17 | 18 |
    19 | """; 20 | } 21 | 22 | Map events() { 23 | return {'click:button': onButtonClick}; 24 | } 25 | 26 | onButtonClick(event) { 27 | var input = find('input') as InputElement; 28 | 29 | collection.add( 30 | new TodoModel(attributes: {'task': input.value}), 31 | ); 32 | 33 | input.value = ''; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /web/lib/collection_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import '../lib/view.dart'; 3 | import '../lib/model.dart'; 4 | import '../lib/collection.dart'; 5 | 6 | abstract class CollectionView { 7 | String containerTag; 8 | Collection collection; 9 | Element parent; 10 | 11 | CollectionView({this.collection, this.containerTag, this.parent}) { 12 | bindCollection(); 13 | } 14 | 15 | void bindCollection() { 16 | collection.events.stream.listen((event) { 17 | switch (event) { 18 | case 'change': 19 | render(); 20 | break; 21 | default: 22 | } 23 | }); 24 | } 25 | 26 | View renderItem(Model model, Element parent); 27 | 28 | render() { 29 | var containerElement = new Element.tag(containerTag); 30 | 31 | collection.forEach((model) { 32 | renderItem(model, containerElement).render(); 33 | }); 34 | 35 | parent.innerHtml = ''; 36 | parent.append(containerElement); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web/src/app.dart: -------------------------------------------------------------------------------- 1 | import '../lib/view.dart'; 2 | import '../lib/collection.dart'; 3 | import 'todo_model.dart'; 4 | import 'todos_collection_view.dart'; 5 | import 'todos_add_view.dart'; 6 | 7 | class App extends View { 8 | App({model, parent}) : super(model: model, parent: parent); 9 | 10 | String template() { 11 | return """ 12 |
    13 | Hi, I'm the app! 14 |
    15 |
    16 |
    17 | """; 18 | } 19 | 20 | regionMap() => { 21 | 'todos': '.todos-region', 22 | 'addTodos': '.add-todos-region', 23 | }; 24 | 25 | onRender() { 26 | var collection = new Collection(); 27 | collection.addAll([ 28 | new TodoModel(attributes: {'task': 'Take out trash'}), 29 | new TodoModel(attributes: {'task': 'Do the dishes'}), 30 | new TodoModel(attributes: {'task': 'Play with dog'}), 31 | ]); 32 | 33 | new TodosCollectionView( 34 | collection: collection, 35 | containerTag: 'ul', 36 | parent: regions['todos'], 37 | )..render(); 38 | 39 | new TodosAddView( 40 | collection: collection, 41 | parent: regions['addTodos'], 42 | )..render(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/lib/view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'model.dart'; 3 | 4 | abstract class View { 5 | Model model; 6 | DocumentFragment el; 7 | Element parent; 8 | Map regions = {}; 9 | 10 | View({this.model, this.parent}) { 11 | bindModel(); 12 | } 13 | 14 | String template(); 15 | 16 | void onRender() {} 17 | Map events() => {}; 18 | Map regionMap() => {}; 19 | 20 | void bindModel() { 21 | if (model == null) return; 22 | 23 | model.events.stream.listen((String event) { 24 | switch (event) { 25 | case 'change': 26 | render(); 27 | break; 28 | default: 29 | } 30 | }); 31 | } 32 | 33 | Element find(String selector) { 34 | return parent.querySelector(selector); 35 | } 36 | 37 | void render() { 38 | el = new DocumentFragment.html(template()); 39 | 40 | mapRegions(); 41 | onRender(); 42 | bindEvents(el); 43 | 44 | parent.append(el); 45 | } 46 | 47 | void mapRegions() { 48 | regionMap().forEach((regionName, selector) { 49 | regions[regionName] = el.querySelector(selector); 50 | }); 51 | } 52 | 53 | void bindEvents(DocumentFragment el) { 54 | events().forEach((config, callback) { 55 | var configList = config.split(':'); 56 | var eventName = configList[0]; 57 | var selector = configList[1]; 58 | 59 | el.querySelectorAll(selector).forEach((element) { 60 | element.addEventListener(eventName, callback); 61 | }); 62 | }); 63 | } 64 | } 65 | --------------------------------------------------------------------------------