├── .gitignore
├── src
├── app
│ ├── index
│ │ ├── constants
│ │ │ ├── CategoryTypes.js
│ │ │ └── ActionTypes.js
│ │ ├── actions
│ │ │ ├── category.js
│ │ │ └── list.js
│ │ ├── reducers
│ │ │ ├── index.js
│ │ │ ├── filter.js
│ │ │ ├── category.js
│ │ │ └── list.js
│ │ ├── store
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── containers
│ │ │ ├── Root.js
│ │ │ └── App.js
│ │ └── components
│ │ │ ├── list
│ │ │ ├── Toggle.jsx
│ │ │ ├── AddTodo.jsx
│ │ │ ├── Todo.jsx
│ │ │ ├── List.jsx
│ │ │ └── index.jsx
│ │ │ ├── index.jsx
│ │ │ └── category
│ │ │ └── index.jsx
│ └── learn.jsx
├── index.html
└── css
│ └── base.css
├── README.md
├── webpack.config.js
├── package.json
└── app.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | src/dist
--------------------------------------------------------------------------------
/src/app/index/constants/CategoryTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_CATEGORY = 'ADD_CATEGORY';
--------------------------------------------------------------------------------
/src/app/index/actions/category.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/CategoryTypes';
2 |
3 | export function addCategory(name) {
4 | return {
5 | type: types.ADD_CATEGORY,
6 | name
7 | }
8 | }
--------------------------------------------------------------------------------
/src/app/index/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import category from './category';
3 | import list from './list';
4 | import filter from './filter';
5 |
6 | export default combineReducers({category, list, filter});
--------------------------------------------------------------------------------
/src/app/index/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 | import rootReducers from '../reducers';
3 |
4 | // 之所以不直接返回 store 而是返回 createStore 是为了让外界可以传递initialState。
5 | export default initialState => createStore(rootReducers, initialState);
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/app/index/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_ITEM = 'ADD_ITEM';
2 | export const UPDATE_ITEM = 'UPDATE_ITEM';
3 | export const DELETE_ITEM = 'DELETE_ITEM';
4 | export const UPDATE_ITEMS = 'UPDATE_ITEMS';
5 | export const DELETE_ITEMS = 'DELETE_ITEMS';
6 | export const FILTER_ITEMS = 'FILTER_ITEMS';
--------------------------------------------------------------------------------
/src/app/index/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import createStore from './store';
4 | import Root from './containers/Root';
5 |
6 | let store = createStore();
7 |
8 | ReactDOM.render(
9 | ,
10 | document.getElementById('app')
11 | );
--------------------------------------------------------------------------------
/src/app/index/reducers/filter.js:
--------------------------------------------------------------------------------
1 | import { FILTER_ITEMS } from '../constants/ActionTypes';
2 |
3 | export default (state = {active: true}, action) => {
4 | switch (action.type) {
5 | case FILTER_ITEMS:
6 | return Object.assign({}, state, {active: action.active})
7 |
8 | default:
9 | return state
10 | }
11 | }
--------------------------------------------------------------------------------
/src/app/index/containers/Root.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Provider } from 'react-redux';
3 | import App from './App';
4 |
5 | export default class Root extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # learn-react
2 |
3 | [](http://todos.berwin.me)
4 |
5 | ## install
6 |
7 | ```shell
8 | npm install
9 |
10 | npm run build
11 | ```
12 |
13 | ## run
14 |
15 | ```shell
16 | npm run start
17 | ```
18 |
19 | Run in the Browser
20 |
21 | ```shell
22 | http://127.0.0.1:3000
23 | ```
--------------------------------------------------------------------------------
/src/app/index/containers/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Router, Route, Link } from 'react-router';
3 | import { createHistory, useBasename } from 'history';
4 | import Index from '../components/index.jsx';
5 |
6 | export default class View extends Component {
7 | render () {
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/index/components/list/Toggle.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class Toggle extends Component {
4 |
5 | handleChange(e) {
6 | let { updateItems } = this.props;
7 |
8 | updateItems({status: e.currentTarget.checked});
9 | }
10 |
11 | render () {
12 | let { isAllCompleted } = this.props;
13 |
14 | return ()
15 | }
16 | }
--------------------------------------------------------------------------------
/src/app/index/components/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router';
3 | import Category from './category/index.jsx';
4 | import ListBox from './list/index.jsx';
5 |
6 | export default class Index extends Component {
7 | render () {
8 | return (
9 |
10 |
Todo-List
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/index/reducers/category.js:
--------------------------------------------------------------------------------
1 | import { ADD_CATEGORY } from '../constants/CategoryTypes';
2 |
3 |
4 | /**
5 | * 创建分类
6 | *
7 | *
8 | */
9 | let createCategory = name => {
10 | let time = Date.now();
11 |
12 | return {
13 | id: Math.random().toString(36).split('.').join(''),
14 | addTime: time,
15 | updateTime: time,
16 | name
17 | }
18 | }
19 |
20 | export default (state = [], action) => {
21 | switch (action.type) {
22 | case ADD_CATEGORY:
23 | return [createCategory(action.name)]
24 |
25 | default:
26 | return state
27 | }
28 | }
--------------------------------------------------------------------------------
/src/app/index/components/list/AddTodo.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class AddTodo extends Component {
4 |
5 | handleKeyUp(e) {
6 | if (e.keyCode === 13) {
7 | let text = e.target.value;
8 | if (!text) return;
9 |
10 | this.props.addItem(text);
11 | e.target.value = '';
12 | }
13 | }
14 |
15 | render () {
16 | return (
17 |
18 | );
19 | }
20 | }
21 |
22 | AddTodo.propTypes = {
23 | addItem: React.PropTypes.func.isRequired
24 | }
--------------------------------------------------------------------------------
/src/app/index/components/list/Todo.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class Item extends Component {
4 | toggleTodo(e) {
5 | this.props.updateItem({
6 | status: e.currentTarget.checked
7 | })
8 | }
9 |
10 | render () {
11 | let { deleteItem, status } = this.props;
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 |
3 | module.exports = {
4 | entry: './src/app/index/index.js',
5 | output: {
6 | path: __dirname + '/src/dist',
7 | filename: 'bundle.js'
8 | },
9 | module: {
10 | loaders: [
11 | {test: /(\.js|\.jsx)$/, loader: 'babel-loader'}
12 | ]
13 | },
14 |
15 | resolve: {
16 | root: [__dirname + '/node_modules']
17 | }
18 | };
19 |
20 | /*
21 | * 压缩代码配置项
22 | *
23 | plugins: [
24 | new webpack.optimize.UglifyJsPlugin({
25 | compress: {
26 | warnings: false
27 | }
28 | })
29 | ]
30 | */
--------------------------------------------------------------------------------
/src/app/index/components/category/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { connect } from 'react-redux';
4 | import * as actions from '../../actions/category.js';
5 |
6 | export default class Category extends Component {
7 | render () {
8 | return (
9 |
10 |
Default
11 |
12 | );
13 | }
14 | }
15 |
16 | let mapStateToProps = state => ({category: state.category});
17 | let mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
18 |
19 | export default connect(mapStateToProps, mapDispatchToProps)(Category);
--------------------------------------------------------------------------------
/src/app/index/components/list/List.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Todo from './Todo.jsx';
3 |
4 | export default class List extends Component {
5 | render () {
6 | let { deleteItem, updateItem, activeFilter, filter } = this.props;
7 |
8 | return (
9 | {
10 | this.props.list.map(todo =>
11 | filter.active && todo.status ? '' :
12 | deleteItem(todo.id) }
15 | updateItem={ data => updateItem({ id: todo.id, ...data }) }
16 | activeFilter={ activeFilter }
17 | {...todo} />
18 | )
19 | }
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/index/actions/list.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 |
3 | export function addItem(text) {
4 | return {
5 | type: types.ADD_ITEM,
6 | text
7 | }
8 | }
9 |
10 | export function updateItem(data) {
11 | return {
12 | type: types.UPDATE_ITEM,
13 | data
14 | }
15 | }
16 |
17 | export function deleteItem(id) {
18 | return {
19 | type: types.DELETE_ITEM,
20 | id
21 | }
22 | }
23 |
24 | export function updateItems(data) {
25 | return {
26 | type: types.UPDATE_ITEMS,
27 | data
28 | }
29 | }
30 |
31 | export function deleteItems(query) {
32 | return {
33 | type: types.DELETE_ITEMS,
34 | query
35 | }
36 | }
37 |
38 | export function activeFilter(action) {
39 | return {
40 | type: types.FILTER_ITEMS,
41 | active: action
42 | }
43 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-react",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "webpack -p",
9 | "watch": "webpack -w",
10 | "start": "node app.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/berwin/learn-react.git"
15 | },
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/berwin/learn-react/issues"
20 | },
21 | "homepage": "https://github.com/berwin/learn-react#readme",
22 | "dependencies": {
23 | "babel-core": "^5.8.25",
24 | "babel-loader": "^5.3.2",
25 | "history": "^1.13.0",
26 | "react": "^0.14.0",
27 | "react-dom": "^0.14.0",
28 | "react-redux": "^4.0.0",
29 | "react-router": "^1.0.0-rc3",
30 | "redux": "^3.0.3",
31 | "request": "^2.65.0"
32 | },
33 | "devDependencies": {
34 | "webpack": "^1.12.2"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var fs = require('fs');
3 | var request = require('request');
4 |
5 | function statPV() {
6 | var options = {
7 | url: 'http://stat.berwin.me/api/v1/content',
8 | json: true,
9 | method: 'POST',
10 | timeout: 30000,
11 | body: {
12 | groupID : '56372245135c3a806b54c2e1',
13 | sourceID : '563721f8135c3a806b54c2e0',
14 | token: '563721f8135c3a806b54c2df',
15 | data : {
16 | value: 'pv'
17 | }
18 | }
19 | };
20 |
21 | request(options, function(e, r, body) {});
22 | }
23 |
24 | var server = http.createServer(function (req, res) {
25 | if (req.url == '/favicon.ico') return res.end();
26 |
27 | var file = '';
28 |
29 | if (req.url === '/') {
30 | statPV();
31 | file = fs.readFileSync(__dirname + '/src/index.html');
32 | res.setHeader("Content-Type", "text/html");
33 | } else {
34 | file = fs.readFileSync(__dirname + '/src' + req.url);
35 | if (~req.url.indexOf('.css')) res.setHeader("Content-Type", "text/css");
36 | }
37 |
38 | res.end(file);
39 | });
40 |
41 | server.listen(3000, function () {
42 | console.log('\x1B[32m', 'Server started http://127.0.0.1:3000');
43 | });
--------------------------------------------------------------------------------
/src/app/index/reducers/list.js:
--------------------------------------------------------------------------------
1 | import { ADD_ITEM, UPDATE_ITEM, DELETE_ITEM, UPDATE_ITEMS, DELETE_ITEMS } from '../constants/ActionTypes';
2 |
3 | /**
4 | * 添加
5 | *
6 | * @param {String} 添加的文字
7 | *
8 | * @return {Object} 将要添加的数据
9 | */
10 | let createItem = text => {
11 | let time = Date.now();
12 |
13 | return {
14 | id: Math.random().toString(36).split('.').join(''),
15 | addTime: time,
16 | updateTime: time,
17 | status: false,
18 | text
19 | }
20 | }
21 |
22 | /**
23 | * 更新
24 | *
25 | * @param {String} 添加的文字
26 | *
27 | * @return {Array} 更新后的数据
28 | */
29 | let updateItem = ({id, ...other}, state) => {
30 | let time = Date.now();
31 |
32 | return state.map(item =>
33 | item.id === id ?
34 | Object.assign({}, item, other, {
35 | updateTime: time
36 | }) :
37 | item
38 | );
39 | }
40 |
41 | export default (state = [], action) => {
42 | switch (action.type) {
43 | case ADD_ITEM:
44 | return [createItem(action.text), ...state]
45 |
46 | case UPDATE_ITEM:
47 | return updateItem(action.data, state)
48 |
49 | case DELETE_ITEM:
50 | return state.filter(item => item.id !== action.id)
51 |
52 | case UPDATE_ITEMS:
53 | return state.map(item => Object.assign({}, item, action.data))
54 |
55 | case DELETE_ITEMS:
56 | return []
57 |
58 | default:
59 | return state
60 | }
61 | }
--------------------------------------------------------------------------------
/src/app/index/components/list/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { connect } from 'react-redux';
4 | import * as actions from '../../actions/list.js';
5 | import AddTodo from './AddTodo.jsx';
6 | import List from './List.jsx';
7 | import Toggle from './Toggle.jsx';
8 |
9 | export default class ListBox extends Component {
10 | toggleComplated() {
11 | let { filter, activeFilter } = this.props;
12 |
13 | activeFilter(!filter.active);
14 | }
15 |
16 | render () {
17 | let { filter, addItem, updateItems, isAllCompleted } = this.props;
18 |
19 | return (
20 |
30 | );
31 | }
32 | }
33 |
34 | let mapStateToProps = state => {
35 | let list = state.list;
36 |
37 | return {
38 | isAllCompleted: !!list.length && list.every(item => item.status),
39 | ...state
40 | }
41 | };
42 | let mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
43 |
44 | export default connect(mapStateToProps, mapDispatchToProps)(ListBox);
--------------------------------------------------------------------------------
/src/app/learn.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import {bindActionCreators, combineReducers, createStore} from 'redux';
4 | import {Provider, connect} from 'react-redux';
5 |
6 | // React Component
7 | class Counter extends React.Component {
8 | render () {
9 | const {value, onIncreaseClick} = this.props;
10 |
11 | console.log(this.props)
12 |
13 | return (
14 |
15 | {value}
16 |
17 |
18 | )
19 | }
20 | }
21 |
22 | Counter.propTypes = {
23 | value: PropTypes.number.isRequired,
24 | onIncreaseClick: PropTypes.func.isRequired
25 | };
26 |
27 | // Action
28 | const increaseAction = {type: 'increase'};
29 |
30 | // Reducer
31 | function count (state = 0, action) {
32 |
33 | switch (action.type) {
34 | case 'increase':
35 | return state + 1
36 | default:
37 | return state
38 | }
39 | }
40 |
41 | function count2 (state = 0, action) {
42 |
43 | switch (action.type) {
44 | case 'increase2':
45 | return state + 1
46 | default:
47 | return state
48 | }
49 | }
50 |
51 | let todoApp = combineReducers({
52 | count, count2
53 | });
54 |
55 | // Store
56 | let store = createStore(todoApp);
57 |
58 | function mapStateToProps (state) {
59 | return {value: state.count};
60 | }
61 |
62 | function mapDispatchToProps (dispatch) {
63 | store.getState()
64 | // return {onIncreaseClick: dispatch(increaseAction)}
65 | return {onIncreaseClick: () => dispatch(increaseAction)}
66 | // return bindActionCreators(increaseAction, dispatch);
67 | }
68 |
69 | let App = connect(mapStateToProps, mapDispatchToProps)(Counter);
70 |
71 | ReactDOM.render(
72 |
73 |
74 | ,
75 | document.getElementById('app')
76 | );
--------------------------------------------------------------------------------
/src/css/base.css:
--------------------------------------------------------------------------------
1 | html,body {margin: 0; padding: 0;}
2 | .clearfix:before,.clearfix:after{content:''; display:table;}.clearfix:after{clear:both;}.clearfix{*zoom:1;}/*清浮动用的*/
3 | li{list-style: none;}
4 | button {
5 | margin: 0;
6 | padding: 0;
7 | border: 0;
8 | background: none;
9 | font-size: 100%;
10 | vertical-align: baseline;
11 | font-family: inherit;
12 | font-weight: inherit;
13 | color: inherit;
14 | -webkit-appearance: none;
15 | appearance: none;
16 | -webkit-font-smoothing: antialiased;
17 | -moz-font-smoothing: antialiased;
18 | font-smoothing: antialiased;
19 | }
20 | a {
21 | color: rgba(42, 174, 245, 1);
22 | text-decoration:none;
23 | }
24 |
25 | button,
26 | input[type="checkbox"] {
27 | outline: none;
28 | }
29 |
30 | #app {
31 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
32 | color: #4d4d4d;
33 | min-width: 430px;
34 | max-width: 750px;
35 | margin: 0 auto;
36 | -webkit-font-smoothing: antialiased;
37 | -moz-font-smoothing: antialiased;
38 | font-smoothing: antialiased;
39 | font-weight: 300;
40 | }
41 | #app h1 {
42 | width: 100%;
43 | font-size: 80px;
44 | font-weight: 200;
45 | text-align: center;
46 | color: rgba(42, 174, 245, 1);
47 | -webkit-text-rendering: optimizeLegibility;
48 | -moz-text-rendering: optimizeLegibility;
49 | text-rendering: optimizeLegibility;
50 | }
51 |
52 | #app .box {
53 | border:1px solid #ccc;
54 | border-radius:5px;
55 | box-shadow:0 20px 50px rgba(0,0,0,.4);
56 | }
57 |
58 | .category, .list {
59 | float:left;
60 | box-sizing:border-box;
61 | }
62 |
63 | .category {
64 | padding:10px 0;
65 | width:40%;
66 | }
67 | .category li {
68 | padding:0 10px;
69 | line-height: 30px;
70 | }
71 | .category li.active {
72 | color:#fff;
73 | background: rgba(42, 174, 245, 1);
74 | /*background: rgba(206, 206, 206, 1);*/
75 | }
76 |
77 | label[for='toggle-all'] {
78 | display: none;
79 | }
80 |
81 | #app .list-header {
82 | position:relative;
83 | height:65px;
84 | }
85 |
86 | #toggle-all {
87 | position: absolute;
88 | top: 12px;
89 | left: -12px;
90 | width: 60px;
91 | height: 34px;
92 | text-align: center;
93 | border: none; /* Mobile Safari */
94 | }
95 |
96 | #toggle-all:before {
97 | content: '❯';
98 | font-size: 22px;
99 | color: #e6e6e6;
100 | padding: 10px 27px 10px 27px;
101 | }
102 |
103 | #toggle-all:checked:before {
104 | color: #737373;
105 | }
106 |
107 | #app .list {
108 | width:60%;
109 | border-left:1px solid #ededed;
110 | }
111 |
112 | #new-todo {
113 | padding: 16px 16px 16px 60px;
114 | border: none;
115 | background: rgba(0, 0, 0, 0.003);
116 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
117 |
118 | width: 100%;
119 | font-size: 24px;
120 | font-family: inherit;
121 | font-weight: inherit;
122 | line-height: 1.4em;
123 | outline: none;
124 | color: inherit;
125 | box-sizing: border-box;
126 | }
127 |
128 | #app .todo-list {
129 | padding:0;margin:0;
130 | }
131 |
132 | #app .list li {
133 | position: relative;
134 | font-size: 24px;
135 | border-bottom: 1px solid #ededed;
136 | }
137 |
138 |
139 | #app .list li .toggle {
140 | text-align: center;
141 | width: 40px;
142 | /* auto, since non-WebKit browsers doesn't support input styling */
143 | height: 40px;
144 | position: absolute;
145 | top: 0;
146 | bottom: 0;
147 | margin: auto 0;
148 | border: none; /* Mobile Safari */
149 | -webkit-appearance: none;
150 | appearance: none;
151 | }
152 |
153 | #app .list li .toggle:after {
154 | content: url('data:image/svg+xml;utf8,');
155 | }
156 |
157 | #app .list li .toggle:checked:after {
158 | content: url('data:image/svg+xml;utf8,');
159 | }
160 |
161 | #app .list li label {
162 | white-space: pre;
163 | word-break: break-word;
164 | padding: 15px 60px 15px 15px;
165 | margin-left: 45px;
166 | display: block;
167 | line-height: 1.2;
168 | transition: color 0.4s;
169 | }
170 |
171 | #app .list li.completed label {
172 | color: #d9d9d9;
173 | text-decoration: line-through;
174 | }
175 |
176 | #app .list li .destroy {
177 | display: none;
178 | position: absolute;
179 | top: 0;
180 | right: 10px;
181 | bottom: 0;
182 | width: 40px;
183 | height: 40px;
184 | margin: auto 0;
185 | font-size: 30px;
186 | /*color: #cc9a9a;*/
187 | color: rgba(42, 174, 245, .5);
188 | /*margin-bottom: 11px;*/
189 | transition: color 0.2s ease-out;
190 | font-family: monospace;
191 | }
192 |
193 | #app .list li .destroy:hover {
194 | color: rgba(42, 174, 245, 1);
195 | }
196 |
197 | #app .list li .destroy:after {
198 | content: '×';
199 | }
200 |
201 | #app .list li:hover .destroy {
202 | display: block;
203 | }
204 |
205 | .list-footer {
206 | color: #777;
207 | padding: 10px 15px;
208 | height: 20px;
209 | text-align: center;
210 | }
211 |
212 |
213 |
214 | /*
215 | Hack to remove background from Mobile Safari.
216 | Can't use it globally since it destroys checkboxes in Firefox
217 | */
218 | @media screen and (-webkit-min-device-pixel-ratio:0) {
219 | #toggle-all,
220 | #todo-list li .toggle {
221 | background: none;
222 | }
223 |
224 | #todo-list li .toggle {
225 | height: 40px;
226 | }
227 |
228 | #toggle-all {
229 | -webkit-transform: rotate(90deg);
230 | transform: rotate(90deg);
231 | -webkit-appearance: none;
232 | appearance: none;
233 | }
234 | }
235 |
--------------------------------------------------------------------------------