@vue-lynx-example/todomvc 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/main.lynx.bundle +0 -0
- package/lynx.config.ts +11 -0
- package/package.json +33 -0
- package/src/TodoApp.vue +111 -0
- package/src/TodoFooter.vue +42 -0
- package/src/TodoHeader.vue +23 -0
- package/src/TodoItem.vue +56 -0
- package/src/index.ts +7 -0
- package/src/todomvc.css +245 -0
- package/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# TodoMVC
|
|
2
|
+
|
|
3
|
+
Classic [TodoMVC](https://todomvc.com) built with Vue 3 × Lynx using CSS Selector styling.
|
|
4
|
+
|
|
5
|
+
## Features Exercised
|
|
6
|
+
|
|
7
|
+
- `<script setup>` with `defineProps` / `defineEmits`
|
|
8
|
+
- `ref`, `computed` for state management
|
|
9
|
+
- `v-for` list rendering with `:key`
|
|
10
|
+
- `v-if` / `v-else` conditional rendering
|
|
11
|
+
- Dynamic `:class` binding
|
|
12
|
+
- `@tap`, `@longpress`, `@confirm`, `@blur` events
|
|
13
|
+
- CSS Selectors (`enableCSSSelector: true`)
|
|
14
|
+
- Multi-component composition (TodoApp, TodoHeader, TodoItem, TodoFooter)
|
|
Binary file
|
package/lynx.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vue-lynx-example/todomvc",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "TodoMVC built with Vue 3 × Lynx",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src",
|
|
10
|
+
"lynx.config.ts",
|
|
11
|
+
"tsconfig.json"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/Huxpro/vue-lynx",
|
|
16
|
+
"directory": "examples/todomvc"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "rspeedy build",
|
|
20
|
+
"dev": "rspeedy dev"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"vue-lynx": "workspace:*"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@lynx-js/rspeedy": "^0.13.5",
|
|
27
|
+
"@rsbuild/plugin-vue": "^1.2.6",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/TodoApp.vue
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
import TodoHeader from './TodoHeader.vue'
|
|
5
|
+
import TodoItem from './TodoItem.vue'
|
|
6
|
+
import TodoFooter from './TodoFooter.vue'
|
|
7
|
+
|
|
8
|
+
// ── State ──────────────────────────────────────────────────
|
|
9
|
+
const todos = ref([])
|
|
10
|
+
const filter = ref('all')
|
|
11
|
+
|
|
12
|
+
// ── Derived ────────────────────────────────────────────────
|
|
13
|
+
const activeTodos = computed(() => todos.value.filter(t => !t.completed))
|
|
14
|
+
const completedTodos = computed(() => todos.value.filter(t => t.completed))
|
|
15
|
+
const filteredTodos = computed(() => {
|
|
16
|
+
if (filter.value === 'active') return activeTodos.value
|
|
17
|
+
if (filter.value === 'completed') return completedTodos.value
|
|
18
|
+
return todos.value
|
|
19
|
+
})
|
|
20
|
+
const allCompleted = computed(() =>
|
|
21
|
+
todos.value.length > 0 && activeTodos.value.length === 0,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
25
|
+
let nextId = 0
|
|
26
|
+
function uuid() {
|
|
27
|
+
return `todo-${++nextId}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Actions ────────────────────────────────────────────────
|
|
31
|
+
function addTodo(title) {
|
|
32
|
+
if (!title.trim()) return
|
|
33
|
+
todos.value.push({ id: uuid(), title: title.trim(), completed: false })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function toggleTodo(todo) {
|
|
37
|
+
todo.completed = !todo.completed
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function deleteTodo(todo) {
|
|
41
|
+
todos.value = todos.value.filter(t => t.id !== todo.id)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function editTodo(todo, newTitle) {
|
|
45
|
+
if (!newTitle.trim()) {
|
|
46
|
+
deleteTodo(todo)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
todo.title = newTitle.trim()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toggleAll() {
|
|
53
|
+
const newVal = !allCompleted.value
|
|
54
|
+
todos.value.forEach(t => { t.completed = newVal })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function clearCompleted() {
|
|
58
|
+
todos.value = todos.value.filter(t => !t.completed)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function setFilter(f) {
|
|
62
|
+
filter.value = f
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<view class="todoapp">
|
|
68
|
+
<TodoHeader @add-todo="addTodo" />
|
|
69
|
+
|
|
70
|
+
<view class="main" v-if="todos.length > 0">
|
|
71
|
+
<!-- Toggle all -->
|
|
72
|
+
<view class="toggle-all-container">
|
|
73
|
+
<view
|
|
74
|
+
class="toggle-all-btn"
|
|
75
|
+
:class="{ 'all-completed': allCompleted }"
|
|
76
|
+
@tap="toggleAll"
|
|
77
|
+
>
|
|
78
|
+
<text class="toggle-all-icon">✓</text>
|
|
79
|
+
</view>
|
|
80
|
+
<text class="toggle-all-label">Mark all as complete</text>
|
|
81
|
+
</view>
|
|
82
|
+
|
|
83
|
+
<!-- Todo list -->
|
|
84
|
+
<view class="todo-list">
|
|
85
|
+
<TodoItem
|
|
86
|
+
v-for="todo in filteredTodos"
|
|
87
|
+
:key="todo.id"
|
|
88
|
+
:todo="todo"
|
|
89
|
+
@toggle="toggleTodo"
|
|
90
|
+
@delete="deleteTodo"
|
|
91
|
+
@edit="editTodo"
|
|
92
|
+
/>
|
|
93
|
+
</view>
|
|
94
|
+
</view>
|
|
95
|
+
|
|
96
|
+
<TodoFooter
|
|
97
|
+
v-if="todos.length > 0"
|
|
98
|
+
:active-count="activeTodos.length"
|
|
99
|
+
:completed-count="completedTodos.length"
|
|
100
|
+
:current-filter="filter"
|
|
101
|
+
@set-filter="setFilter"
|
|
102
|
+
@clear-completed="clearCompleted"
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<!-- Info -->
|
|
106
|
+
<view class="info">
|
|
107
|
+
<text class="info-text">Tap a todo circle to toggle</text>
|
|
108
|
+
<text class="info-text">Built with Vue 3 × Lynx</text>
|
|
109
|
+
</view>
|
|
110
|
+
</view>
|
|
111
|
+
</template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps(['activeCount', 'completedCount', 'currentFilter'])
|
|
3
|
+
const emit = defineEmits(['set-filter', 'clear-completed'])
|
|
4
|
+
|
|
5
|
+
function onAll() { emit('set-filter', 'all') }
|
|
6
|
+
function onActive() { emit('set-filter', 'active') }
|
|
7
|
+
function onCompleted() { emit('set-filter', 'completed') }
|
|
8
|
+
function onClear() { emit('clear-completed') }
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<view class="footer">
|
|
13
|
+
<view :style="{ flexDirection: 'row' }">
|
|
14
|
+
<text class="todo-count-number">{{ activeCount }}</text>
|
|
15
|
+
<text class="todo-count"> {{ activeCount === 1 ? 'item' : 'items' }} left</text>
|
|
16
|
+
</view>
|
|
17
|
+
|
|
18
|
+
<view class="filters">
|
|
19
|
+
<text
|
|
20
|
+
class="filter-btn"
|
|
21
|
+
:class="{ selected: currentFilter === 'all' }"
|
|
22
|
+
@tap="onAll"
|
|
23
|
+
>All</text>
|
|
24
|
+
<text
|
|
25
|
+
class="filter-btn"
|
|
26
|
+
:class="{ selected: currentFilter === 'active' }"
|
|
27
|
+
@tap="onActive"
|
|
28
|
+
>Active</text>
|
|
29
|
+
<text
|
|
30
|
+
class="filter-btn"
|
|
31
|
+
:class="{ selected: currentFilter === 'completed' }"
|
|
32
|
+
@tap="onCompleted"
|
|
33
|
+
>Completed</text>
|
|
34
|
+
</view>
|
|
35
|
+
|
|
36
|
+
<text
|
|
37
|
+
v-if="completedCount > 0"
|
|
38
|
+
class="clear-completed"
|
|
39
|
+
@tap="onClear"
|
|
40
|
+
>Clear completed</text>
|
|
41
|
+
</view>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const emit = defineEmits(['add-todo'])
|
|
3
|
+
|
|
4
|
+
function onConfirm(e) {
|
|
5
|
+
const value = e?.detail?.value ?? ''
|
|
6
|
+
if (value.trim()) {
|
|
7
|
+
emit('add-todo', value)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<view class="header">
|
|
14
|
+
<text class="title">todos</text>
|
|
15
|
+
<input
|
|
16
|
+
class="new-todo"
|
|
17
|
+
type="text"
|
|
18
|
+
placeholder="What needs to be done?"
|
|
19
|
+
confirm-type="done"
|
|
20
|
+
@confirm="onConfirm"
|
|
21
|
+
/>
|
|
22
|
+
</view>
|
|
23
|
+
</template>
|
package/src/TodoItem.vue
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps(['todo'])
|
|
5
|
+
const emit = defineEmits(['toggle', 'delete', 'edit'])
|
|
6
|
+
|
|
7
|
+
const editing = ref(false)
|
|
8
|
+
|
|
9
|
+
function onToggle() {
|
|
10
|
+
emit('toggle', props.todo)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function onDelete() {
|
|
14
|
+
emit('delete', props.todo)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function startEdit() {
|
|
18
|
+
editing.value = true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function onEditConfirm(e) {
|
|
22
|
+
const value = e?.detail?.value ?? ''
|
|
23
|
+
editing.value = false
|
|
24
|
+
emit('edit', props.todo, value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function cancelEdit() {
|
|
28
|
+
editing.value = false
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<!-- Normal view -->
|
|
34
|
+
<view
|
|
35
|
+
v-if="!editing"
|
|
36
|
+
class="todo-item"
|
|
37
|
+
:class="{ completed: todo.completed }"
|
|
38
|
+
>
|
|
39
|
+
<view class="todo-toggle" @tap="onToggle">
|
|
40
|
+
<text v-if="todo.completed" class="checkmark">✓</text>
|
|
41
|
+
</view>
|
|
42
|
+
<text class="todo-label" @longpress="startEdit">{{ todo.title }}</text>
|
|
43
|
+
<text class="destroy" @tap="onDelete">✕</text>
|
|
44
|
+
</view>
|
|
45
|
+
|
|
46
|
+
<!-- Edit view -->
|
|
47
|
+
<view v-else class="edit-container">
|
|
48
|
+
<input
|
|
49
|
+
class="edit-input"
|
|
50
|
+
type="text"
|
|
51
|
+
:value="todo.title"
|
|
52
|
+
@confirm="onEditConfirm"
|
|
53
|
+
@blur="cancelEdit"
|
|
54
|
+
/>
|
|
55
|
+
</view>
|
|
56
|
+
</template>
|
package/src/index.ts
ADDED
package/src/todomvc.css
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* TodoMVC CSS for Lynx
|
|
3
|
+
*
|
|
4
|
+
* Adapted from todomvc-app-css for Lynx constraints:
|
|
5
|
+
* - No pseudo-elements (::before, ::after)
|
|
6
|
+
* - No :hover, :focus, :checked pseudo-classes
|
|
7
|
+
* - All elements are <view>, <text>, <input>
|
|
8
|
+
* - Class selectors + descendant/child combinators only
|
|
9
|
+
* - Lynx defaults to border-box, linear layout
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ─── App Container ─────────────────────────────────────── */
|
|
13
|
+
|
|
14
|
+
.todoapp {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
background-color: #fff;
|
|
18
|
+
position: relative;
|
|
19
|
+
width: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* ─── Header ────────────────────────────────────────────── */
|
|
23
|
+
|
|
24
|
+
.header {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
align-items: center;
|
|
28
|
+
padding-top: 12px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.header .title {
|
|
32
|
+
font-size: 60px;
|
|
33
|
+
font-weight: 200;
|
|
34
|
+
color: rgba(175, 47, 47, 0.15);
|
|
35
|
+
text-align: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.new-todo {
|
|
39
|
+
width: 90%;
|
|
40
|
+
padding: 16px 16px 16px 16px;
|
|
41
|
+
font-size: 18px;
|
|
42
|
+
border: 1px solid #999;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
margin-top: 8px;
|
|
45
|
+
color: #4d4d4d;
|
|
46
|
+
background-color: #fff;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ─── Main Section ──────────────────────────────────────── */
|
|
50
|
+
|
|
51
|
+
.main {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
border-top: 1px solid #e6e6e6;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Toggle All */
|
|
58
|
+
.toggle-all-container {
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: row;
|
|
61
|
+
align-items: center;
|
|
62
|
+
padding: 8px 16px;
|
|
63
|
+
border-bottom: 1px solid #e6e6e6;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.toggle-all-btn {
|
|
67
|
+
width: 36px;
|
|
68
|
+
height: 36px;
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
border-radius: 18px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.toggle-all-btn.all-completed {
|
|
76
|
+
background-color: #737373;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.toggle-all-icon {
|
|
80
|
+
font-size: 20px;
|
|
81
|
+
color: #e6e6e6;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.toggle-all-btn.all-completed .toggle-all-icon {
|
|
85
|
+
color: #fff;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.toggle-all-label {
|
|
89
|
+
font-size: 14px;
|
|
90
|
+
color: #e6e6e6;
|
|
91
|
+
margin-left: 8px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ─── Todo List ─────────────────────────────────────────── */
|
|
95
|
+
|
|
96
|
+
.todo-list {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ─── Todo Item ─────────────────────────────────────────── */
|
|
102
|
+
|
|
103
|
+
.todo-item {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: row;
|
|
106
|
+
align-items: center;
|
|
107
|
+
padding: 12px 16px;
|
|
108
|
+
border-bottom: 1px solid #ededed;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Checkbox circle */
|
|
112
|
+
.todo-toggle {
|
|
113
|
+
width: 32px;
|
|
114
|
+
height: 32px;
|
|
115
|
+
border-radius: 16px;
|
|
116
|
+
border: 2px solid #bddad5;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
flex-shrink: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.todo-item.completed .todo-toggle {
|
|
124
|
+
border-color: #5dc2af;
|
|
125
|
+
background-color: #5dc2af;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.checkmark {
|
|
129
|
+
font-size: 18px;
|
|
130
|
+
color: #fff;
|
|
131
|
+
font-weight: 700;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Todo label */
|
|
135
|
+
.todo-label {
|
|
136
|
+
flex: 1;
|
|
137
|
+
font-size: 18px;
|
|
138
|
+
color: #4d4d4d;
|
|
139
|
+
margin-left: 12px;
|
|
140
|
+
margin-right: 12px;
|
|
141
|
+
line-height: 1.4;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.todo-item.completed .todo-label {
|
|
145
|
+
color: #d9d9d9;
|
|
146
|
+
text-decoration: line-through;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Destroy button */
|
|
150
|
+
.destroy {
|
|
151
|
+
width: 28px;
|
|
152
|
+
height: 28px;
|
|
153
|
+
font-size: 22px;
|
|
154
|
+
color: #cc9a9a;
|
|
155
|
+
text-align: center;
|
|
156
|
+
line-height: 28px;
|
|
157
|
+
flex-shrink: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ─── Edit mode ─────────────────────────────────────────── */
|
|
161
|
+
|
|
162
|
+
.edit-container {
|
|
163
|
+
display: flex;
|
|
164
|
+
flex-direction: row;
|
|
165
|
+
align-items: center;
|
|
166
|
+
padding: 4px 16px;
|
|
167
|
+
border-bottom: 1px solid #ededed;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.edit-input {
|
|
171
|
+
flex: 1;
|
|
172
|
+
font-size: 18px;
|
|
173
|
+
padding: 12px;
|
|
174
|
+
border: 1px solid #999;
|
|
175
|
+
border-radius: 4px;
|
|
176
|
+
color: #4d4d4d;
|
|
177
|
+
background-color: #fff;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ─── Footer ────────────────────────────────────────────── */
|
|
181
|
+
|
|
182
|
+
.footer {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-direction: row;
|
|
185
|
+
align-items: center;
|
|
186
|
+
padding: 10px 16px;
|
|
187
|
+
border-top: 1px solid #e6e6e6;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.todo-count {
|
|
191
|
+
font-size: 13px;
|
|
192
|
+
color: #777;
|
|
193
|
+
flex-shrink: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.todo-count-number {
|
|
197
|
+
font-weight: 700;
|
|
198
|
+
font-size: 13px;
|
|
199
|
+
color: #777;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Filter buttons */
|
|
203
|
+
.filters {
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: row;
|
|
206
|
+
flex: 1;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
gap: 4px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.filter-btn {
|
|
212
|
+
font-size: 13px;
|
|
213
|
+
color: #777;
|
|
214
|
+
padding: 3px 8px;
|
|
215
|
+
border: 1px solid transparent;
|
|
216
|
+
border-radius: 3px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.filter-btn.selected {
|
|
220
|
+
border-color: rgba(175, 47, 47, 0.2);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Clear completed */
|
|
224
|
+
.clear-completed {
|
|
225
|
+
font-size: 13px;
|
|
226
|
+
color: #777;
|
|
227
|
+
flex-shrink: 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ─── Info Footer ───────────────────────────────────────── */
|
|
231
|
+
|
|
232
|
+
.info {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
align-items: center;
|
|
236
|
+
margin-top: 24px;
|
|
237
|
+
padding: 0 16px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.info-text {
|
|
241
|
+
font-size: 11px;
|
|
242
|
+
color: #bfbfbf;
|
|
243
|
+
text-align: center;
|
|
244
|
+
margin-top: 4px;
|
|
245
|
+
}
|