├── .gitignore ├── example01 └── index.html ├── example02 └── index.html ├── example03 ├── index.html └── src │ ├── app.js │ ├── components │ └── Items.js │ └── core │ └── Component.js ├── example04 ├── index.html └── src │ ├── app.js │ ├── components │ └── Items.js │ └── core │ └── Component.js ├── example05 ├── index.html └── src │ ├── app.js │ ├── components │ └── Items.js │ └── core │ └── Component.js ├── example06 ├── index.html └── src │ ├── app.js │ ├── components │ └── Items.js │ └── core │ └── Component.js ├── example07 ├── index.html └── src │ ├── app.js │ ├── components │ └── Items.js │ └── core │ └── Component.js └── example08 ├── index.html └── src ├── App.js ├── components ├── ItemAppender.js ├── ItemFilter.js └── Items.js ├── core └── Component.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | *.iml -------------------------------------------------------------------------------- /example01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 1 6 | 7 | 8 |

Example #1

9 |
10 | 37 | 38 | -------------------------------------------------------------------------------- /example02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 2 6 | 7 | 8 |

Example #2

9 |
10 | 56 | 57 | -------------------------------------------------------------------------------- /example03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 3 6 | 7 | 8 |

Example #3

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example03/src/app.js: -------------------------------------------------------------------------------- 1 | import Items from "./components/Items.js"; 2 | 3 | class App { 4 | constructor() { 5 | const $app = document.querySelector('#app'); 6 | new Items($app); 7 | } 8 | } 9 | 10 | new App(); 11 | -------------------------------------------------------------------------------- /example03/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | setup () { 5 | this.$state = { items: ['item1', 'item2'] }; 6 | } 7 | template () { 8 | const { items } = this.$state; 9 | return ` 10 | 13 | 14 | ` 15 | } 16 | 17 | setEvent () { 18 | this.$target.querySelector('button').addEventListener('click', () => { 19 | const { items } = this.$state; 20 | this.setState({ items: [ ...items, `item${items.length + 1}` ] }); 21 | }); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /example03/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $state; 4 | constructor ($target) { 5 | this.$target = $target; 6 | this.setup(); 7 | this.render(); 8 | } 9 | setup () {}; 10 | template () { return ''; } 11 | render () { 12 | this.$target.innerHTML = this.template(); 13 | this.setEvent(); 14 | } 15 | setEvent () {} 16 | setState (newState) { 17 | this.$state = { ...this.$state, ...newState }; 18 | this.render(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example04/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 4 6 | 7 | 8 |

Example #4

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example04/src/app.js: -------------------------------------------------------------------------------- 1 | import Items from "./components/Items.js"; 2 | 3 | class App { 4 | constructor() { 5 | const $app = document.querySelector('#app'); 6 | new Items($app); 7 | } 8 | } 9 | 10 | new App(); 11 | -------------------------------------------------------------------------------- /example04/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | setup () { 5 | this.$state = { items: ['item1', 'item2'] }; 6 | } 7 | template () { 8 | const { items } = this.$state; 9 | return ` 10 | 18 | 19 | ` 20 | } 21 | 22 | setEvent () { 23 | this.$target.querySelector('.addBtn').addEventListener('click', () => { 24 | const { items } = this.$state; 25 | this.setState({ items: [ ...items, `item${items.length + 1}` ] }); 26 | }); 27 | 28 | this.$target.querySelectorAll('.deleteBtn').forEach(deleteBtn => 29 | deleteBtn.addEventListener('click', ({ target }) => { 30 | const items = [ ...this.$state.items ]; 31 | items.splice(target.dataset.index, 1); 32 | this.setState({ items }); 33 | })) 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /example04/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $state; 4 | constructor ($target) { 5 | this.$target = $target; 6 | this.setup(); 7 | this.render(); 8 | } 9 | setup () {}; 10 | template () { return ''; } 11 | render () { 12 | this.$target.innerHTML = this.template(); 13 | this.setEvent(); 14 | } 15 | setEvent () {} 16 | setState (newState) { 17 | this.$state = { ...this.$state, ...newState }; 18 | this.render(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example05/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 5 6 | 7 | 8 |

Example #5

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example05/src/app.js: -------------------------------------------------------------------------------- 1 | import Items from "./components/Items.js"; 2 | 3 | class App { 4 | constructor() { 5 | const $app = document.querySelector('#app'); 6 | new Items($app); 7 | } 8 | } 9 | 10 | new App(); 11 | -------------------------------------------------------------------------------- /example05/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | setup () { 5 | this.$state = { items: ['item1', 'item2'] }; 6 | } 7 | template () { 8 | const { items } = this.$state; 9 | return ` 10 | 18 | 19 | ` 20 | } 21 | 22 | setEvent () { 23 | this.$target.addEventListener('click', ({ target }) => { 24 | const items = [ ...this.$state.items ]; 25 | 26 | if (target.classList.contains('addBtn')) { 27 | this.setState({ items: [ ...items, `item${items.length + 1}` ] }); 28 | } 29 | 30 | if (target.classList.contains('deleteBtn')) { 31 | items.splice(target.dataset.index, 1); 32 | this.setState({ items }); 33 | } 34 | 35 | }); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /example05/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $state; 4 | constructor ($target) { 5 | this.$target = $target; 6 | this.setup(); 7 | this.setEvent(); 8 | this.render(); 9 | } 10 | setup () {}; 11 | template () { return ''; } 12 | render () { 13 | this.$target.innerHTML = this.template(); 14 | } 15 | setEvent () {} 16 | setState (newState) { 17 | this.$state = { ...this.$state, ...newState }; 18 | this.render(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example06/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 6 6 | 7 | 8 |

Example #6

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example06/src/app.js: -------------------------------------------------------------------------------- 1 | import Items from "./components/Items.js"; 2 | 3 | class App { 4 | constructor() { 5 | const $app = document.querySelector('#app'); 6 | new Items($app); 7 | } 8 | } 9 | 10 | new App(); 11 | -------------------------------------------------------------------------------- /example06/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | setup () { 5 | this.$state = { items: ['item1', 'item2'] }; 6 | } 7 | template () { 8 | const { items } = this.$state; 9 | return ` 10 | 18 | 19 | ` 20 | } 21 | 22 | setEvent () { 23 | this.addEvent('click', '.addBtn', ({ target }) => { 24 | const { items } = this.$state; 25 | this.setState({ items: [ ...items, `item${items.length + 1}` ] }); 26 | }); 27 | 28 | this.addEvent('click', '.deleteBtn', ({ target }) => { 29 | const items = [ ...this.$state.items ]; 30 | items.splice(target.dataset.index, 1); 31 | this.setState({ items }); 32 | }); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /example06/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $state; 4 | constructor ($target) { 5 | this.$target = $target; 6 | this.setup(); 7 | this.setEvent(); 8 | this.render(); 9 | } 10 | setup () {}; 11 | template () { return ''; } 12 | render () { 13 | this.$target.innerHTML = this.template(); 14 | } 15 | setEvent () {} 16 | setState (newState) { 17 | this.$state = { ...this.$state, ...newState }; 18 | this.render(); 19 | } 20 | addEvent (eventType, selector, callback) { 21 | this.$target.addEventListener(eventType, event => { 22 | if (!event.target.closest(selector)) return false; 23 | callback(event); 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example07/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 7 6 | 7 | 8 |

Example #7

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example07/src/app.js: -------------------------------------------------------------------------------- 1 | import Items from "./components/Items.js"; 2 | 3 | class App { 4 | constructor() { 5 | const $app = document.querySelector('#app'); 6 | new Items($app); 7 | } 8 | } 9 | 10 | new App(); 11 | -------------------------------------------------------------------------------- /example07/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | get filteredItems () { 5 | const { isFilter, items } = this.$state; 6 | return items.filter(({ active }) => (isFilter === 1 && active) || 7 | (isFilter === 2 && !active) || 8 | isFilter === 0); 9 | } 10 | 11 | setup() { 12 | this.$state = { 13 | isFilter: 0, 14 | items: [ 15 | { 16 | seq: 1, 17 | contents: 'item1', 18 | active: false, 19 | }, 20 | { 21 | seq: 2, 22 | contents: 'item2', 23 | active: true, 24 | } 25 | ] 26 | }; 27 | } 28 | 29 | template() { 30 | return ` 31 |
32 | 33 |
34 |
35 | 46 |
47 | 52 | ` 53 | } 54 | 55 | setEvent() { 56 | this.addEvent('keyup', '.appender', ({ key, target }) => { 57 | if (key !== 'Enter') return; 58 | const {items} = this.$state; 59 | const seq = Math.max(0, ...items.map(v => v.seq)) + 1; 60 | const contents = target.value; 61 | const active = false; 62 | this.setState({ 63 | items: [ 64 | ...items, 65 | {seq, contents, active} 66 | ] 67 | }); 68 | }); 69 | 70 | this.addEvent('click', '.deleteBtn', ({target}) => { 71 | const items = [ ...this.$state.items ];; 72 | const seq = Number(target.closest('[data-seq]').dataset.seq); 73 | items.splice(items.findIndex(v => v.seq === seq), 1); 74 | this.setState({items}); 75 | }); 76 | 77 | this.addEvent('click', '.toggleBtn', ({target}) => { 78 | const items = [ ...this.$state.items ]; 79 | const seq = Number(target.closest('[data-seq]').dataset.seq); 80 | const index = items.findIndex(v => v.seq === seq); 81 | items[index].active = !items[index].active; 82 | this.setState({items}); 83 | }); 84 | 85 | this.addEvent('click', '.filterBtn', ({ target }) => { 86 | this.setState({ isFilter: Number(target.dataset.isFilter) }); 87 | }); 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /example07/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $state; 4 | constructor ($target) { 5 | this.$target = $target; 6 | this.setup(); 7 | this.setEvent(); 8 | this.render(); 9 | } 10 | setup () {}; 11 | template () { return ''; } 12 | render () { 13 | this.$target.innerHTML = this.template(); 14 | } 15 | setEvent () {} 16 | setState (newState) { 17 | this.$state = { ...this.$state, ...newState }; 18 | this.render(); 19 | } 20 | addEvent (eventType, selector, callback) { 21 | this.$target.addEventListener(eventType, event => { 22 | if (!event.target.closest(selector)) return false; 23 | callback(event); 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example08/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple Component 8 6 | 7 | 8 |

Example #8

9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example08/src/App.js: -------------------------------------------------------------------------------- 1 | import Component from "./core/Component.js"; 2 | import Items from "./components/Items.js"; 3 | import ItemAppender from "./components/ItemAppender.js"; 4 | import ItemFilter from "./components/ItemFilter.js"; 5 | 6 | export default class App extends Component { 7 | 8 | setup () { 9 | this.$state = { 10 | isFilter: 0, 11 | items: [ 12 | { 13 | seq: 1, 14 | contents: 'item1', 15 | active: false, 16 | }, 17 | { 18 | seq: 2, 19 | contents: 'item2', 20 | active: true, 21 | } 22 | ] 23 | }; 24 | } 25 | 26 | template () { 27 | return ` 28 |
29 |
30 | 31 | `; 32 | } 33 | 34 | mounted () { 35 | const { filteredItems, addItem, deleteItem, toggleItem, filterItem } = this; 36 | const $itemAppender = this.$target.querySelector('[data-component="item-appender"]'); 37 | const $items = this.$target.querySelector('[data-component="items"]'); 38 | const $itemFilter = this.$target.querySelector('[data-component="item-filter"]'); 39 | 40 | new ItemAppender($itemAppender, { 41 | addItem: addItem.bind(this) 42 | }); 43 | new Items($items, { 44 | filteredItems, 45 | deleteItem: deleteItem.bind(this), 46 | toggleItem: toggleItem.bind(this), 47 | }); 48 | new ItemFilter($itemFilter, { 49 | filterItem: filterItem.bind(this) 50 | }); 51 | } 52 | 53 | get filteredItems () { 54 | const { isFilter, items } = this.$state; 55 | return items.filter(({ active }) => (isFilter === 1 && active) || 56 | (isFilter === 2 && !active) || 57 | isFilter === 0); 58 | } 59 | 60 | addItem (contents) { 61 | const {items} = this.$state; 62 | const seq = Math.max(0, ...items.map(v => v.seq)) + 1; 63 | const active = false; 64 | this.setState({ 65 | items: [ 66 | ...items, 67 | {seq, contents, active} 68 | ] 69 | }); 70 | } 71 | 72 | deleteItem (seq) { 73 | const items = [ ...this.$state.items ]; 74 | items.splice(items.findIndex(v => v.seq === seq), 1); 75 | this.setState({items}); 76 | } 77 | 78 | toggleItem (seq) { 79 | const items = [ ...this.$state.items ]; 80 | const index = items.findIndex(v => v.seq === seq); 81 | items[index].active = !items[index].active; 82 | this.setState({items}); 83 | } 84 | 85 | filterItem (isFilter) { 86 | this.setState({ isFilter }); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /example08/src/components/ItemAppender.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class ItemAppender extends Component { 4 | 5 | template() { 6 | return ``; 7 | } 8 | 9 | setEvent() { 10 | const { addItem } = this.$props; 11 | this.addEvent('keyup', '.appender', ({ key, target }) => { 12 | if (key !== 'Enter') return; 13 | addItem(target.value); 14 | }); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /example08/src/components/ItemFilter.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class ItemFilter extends Component { 4 | 5 | template() { 6 | return ` 7 | 8 | 9 | 10 | ` 11 | } 12 | 13 | setEvent() { 14 | const { filterItem } = this.$props; 15 | this.addEvent('click', '.filterBtn', ({ target }) => { 16 | filterItem(Number(target.dataset.isFilter)); 17 | }); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /example08/src/components/Items.js: -------------------------------------------------------------------------------- 1 | import Component from "../core/Component.js"; 2 | 3 | export default class Items extends Component { 4 | 5 | template() { 6 | const { filteredItems } = this.$props; 7 | return ` 8 | 19 | ` 20 | } 21 | 22 | setEvent() { 23 | const { deleteItem, toggleItem } = this.$props; 24 | 25 | this.addEvent('click', '.deleteBtn', ({target}) => { 26 | deleteItem(Number(target.closest('[data-seq]').dataset.seq)); 27 | }); 28 | 29 | this.addEvent('click', '.toggleBtn', ({target}) => { 30 | toggleItem(Number(target.closest('[data-seq]').dataset.seq)); 31 | }); 32 | 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /example08/src/core/Component.js: -------------------------------------------------------------------------------- 1 | export default class Component { 2 | $target; 3 | $props; 4 | $state; 5 | constructor ($target, $props) { 6 | this.$target = $target; 7 | this.$props = $props; 8 | this.setup(); 9 | this.setEvent(); 10 | this.render(); 11 | } 12 | setup () {}; 13 | mounted () {}; 14 | template () { return ''; } 15 | render () { 16 | this.$target.innerHTML = this.template(); 17 | this.mounted(); 18 | } 19 | setEvent () {} 20 | setState (newState) { 21 | this.$state = { ...this.$state, ...newState }; 22 | this.render(); 23 | } 24 | addEvent (eventType, selector, callback) { 25 | this.$target.addEventListener(eventType, event => { 26 | if (!event.target.closest(selector)) return false; 27 | callback(event); 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example08/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.js'; 2 | 3 | new App(document.querySelector('#app')); 4 | --------------------------------------------------------------------------------