]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(playground): todo mvc
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Thu, 25 Jan 2024 08:49:34 +0000 (16:49 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Thu, 25 Jan 2024 09:00:19 +0000 (17:00 +0800)
playground/package.json
playground/src/todo-mvc.vue
playground/vite.config.ts
pnpm-lock.yaml

index b6758d09618e2e65c71719d862bf97e13d718bcf..15545037d5f8288ef5015342ad3260aa4f4b654d 100644 (file)
@@ -7,6 +7,7 @@
     "build": "node ./setup/vite.js build"
   },
   "dependencies": {
+    "@vueuse/core": "^10.7.2",
     "vue": "workspace:*"
   },
   "devDependencies": {
index 75fcf27b837dbb89b8b10d883c3271e6d76ffa8a..4de671f3ed037108b022d08c93d434fea5e0739a 100644 (file)
@@ -1,12 +1,17 @@
 <script setup lang="ts">
-import { ref } from 'vue/vapor'
-
+import { computed } from 'vue/vapor'
+import { useLocalStorage } from '@vueuse/core'
 interface Task {
   title: string
   completed: boolean
 }
-const tasks = ref<Task[]>([])
-const value = ref('hello')
+
+const tasks = useLocalStorage<Task[]>('tasks', [])
+const value = useLocalStorage('value', '')
+
+const remaining = computed(() => {
+  return tasks.value.filter(task => !task.completed).length
+})
 
 function handleAdd() {
   tasks.value.push({
@@ -16,31 +21,76 @@ function handleAdd() {
   // TODO: clear input
   value.value = ''
 }
+
+function handleComplete(index: number, evt: Event) {
+  tasks.value[index].completed = (evt.target as HTMLInputElement).checked
+}
+
+function handleClearComplete() {
+  tasks.value = tasks.value.filter(task => !task.completed)
+}
+
+function handleClearAll() {
+  tasks.value = []
+}
 </script>
 
 <template>
+  <h1>todos</h1>
   <ul>
     <!-- TODO: v-for -->
-    <li>
-      <!-- TODO checked=false -->
-      <input type="checkbox" :checked="tasks[0]?.completed" />
+    <li v-show="tasks[0]" :class="{ del: tasks[0]?.completed }">
+      <input
+        type="checkbox"
+        :checked="tasks[0]?.completed"
+        @change="handleComplete(0, $event)"
+      />
       {{ tasks[0]?.title }}
     </li>
-    <li>
-      <input type="checkbox" :checked="tasks[1]?.completed" />
+    <li v-show="tasks[1]" :class="{ del: tasks[1]?.completed }">
+      <input
+        type="checkbox"
+        :checked="tasks[1]?.completed"
+        @change="handleComplete(1, $event)"
+      />
       {{ tasks[1]?.title }}
     </li>
-    <li>
-      <input type="checkbox" :checked="tasks[2]?.completed" />
+    <li v-show="tasks[2]" :class="{ del: tasks[2]?.completed }">
+      <input
+        type="checkbox"
+        :checked="tasks[2]?.completed"
+        @change="handleComplete(2, $event)"
+      />
       {{ tasks[2]?.title }}
     </li>
-    <li>
-      <input type="checkbox" :checked="tasks[3]?.completed" />
+    <li v-show="tasks[3]" :class="{ del: tasks[3]?.completed }">
+      <input
+        type="checkbox"
+        :checked="tasks[3]?.completed"
+        @change="handleComplete(3, $event)"
+      />
       {{ tasks[3]?.title }}
     </li>
-    <li>
-      <input type="text" v-model="value" />
-      <button @click="handleAdd">Add</button>
-    </li>
   </ul>
+  <p>
+    {{ remaining }} item{{ remaining !== 1 ? 's' : '' }} left /
+    {{ tasks.length }} item{{ tasks.length !== 1 ? 's' : '' }} in total
+  </p>
+  <div style="display: flex; gap: 8px">
+    <input
+      type="text"
+      v-model="value"
+      @keydown.enter="handleAdd"
+      placeholder="What need to be done?"
+    />
+    <button @click="handleAdd">Add</button>
+    <button @click="handleClearComplete">Clear completed</button>
+    <button @click="handleClearAll">Clear all</button>
+  </div>
 </template>
+
+<style>
+.del {
+  text-decoration: line-through;
+}
+</style>
index 7e6ebb5430d9e7713aed84ed1d1d4f134afa19c9..f10e5ee4090441b7c77590fc595662ee685e1a4c 100644 (file)
@@ -6,6 +6,9 @@ import * as CompilerVapor from '@vue/compiler-vapor'
 import * as CompilerSFC from '@vue/compiler-sfc'
 
 export default defineConfig({
+  resolve: {
+    alias: [{ find: /^vue$/, replacement: 'vue/vapor' }],
+  },
   build: {
     target: 'esnext',
   },
@@ -20,4 +23,7 @@ export default defineConfig({
     DevPlugin(),
     Inspect(),
   ],
+  optimizeDeps: {
+    exclude: ['@vueuse/core'],
+  },
 })
index c7b9fdd9f2ce8fe5308eed29749a3b6dd6db4845..32d4f53a411a4afcdaabb85426e628bc89b16c15 100644 (file)
@@ -454,6 +454,9 @@ importers:
 
   playground:
     dependencies:
+      '@vueuse/core':
+        specifier: ^10.7.2
+        version: 10.7.2(vue@packages+vue)
       vue:
         specifier: workspace:*
         version: link:../packages/vue
@@ -1346,6 +1349,10 @@ packages:
     resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
     dev: true
 
+  /@types/web-bluetooth@0.0.20:
+    resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
+    dev: false
+
   /@types/yauzl@2.10.2:
     resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==}
     requiresBuild: true
@@ -1641,6 +1648,31 @@ packages:
     resolution: {integrity: sha512-BK9D7AgpYAWVrtd7Kkc3CotU/ox8l+mPjsLgK16ZP+Ldj8jXPrJtzYQ2rTQNRJOxVSVx5acftDTLDLENFhQdDw==}
     dev: false
 
+  /@vueuse/core@10.7.2(vue@packages+vue):
+    resolution: {integrity: sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==}
+    dependencies:
+      '@types/web-bluetooth': 0.0.20
+      '@vueuse/metadata': 10.7.2
+      '@vueuse/shared': 10.7.2(vue@packages+vue)
+      vue-demi: 0.14.6(vue@packages+vue)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
+  /@vueuse/metadata@10.7.2:
+    resolution: {integrity: sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==}
+    dev: false
+
+  /@vueuse/shared@10.7.2(vue@packages+vue):
+    resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==}
+    dependencies:
+      vue-demi: 0.14.6(vue@packages+vue)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
   /@zeit/schemas@2.29.0:
     resolution: {integrity: sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==}
     dev: true
@@ -6284,6 +6316,21 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /vue-demi@0.14.6(vue@packages+vue):
+    resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: link:packages/vue
+    dev: false
+
   /w3c-xmlserializer@5.0.0:
     resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
     engines: {node: '>=18'}