├── 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 |
--------------------------------------------------------------------------------