♻️ Interactive v2 #1
| @@ -16,6 +16,8 @@ | |||||||
|     "@fontsource/roboto": "^5.0.8", |     "@fontsource/roboto": "^5.0.8", | ||||||
|     "@mdi/font": "^7.4.47", |     "@mdi/font": "^7.4.47", | ||||||
|     "@unocss/reset": "^0.58.5", |     "@unocss/reset": "^0.58.5", | ||||||
|  |     "dompurify": "^3.0.9", | ||||||
|  |     "marked": "^12.0.0", | ||||||
|     "pinia": "^2.1.7", |     "pinia": "^2.1.7", | ||||||
|     "universal-cookie": "^7.1.0", |     "universal-cookie": "^7.1.0", | ||||||
|     "unocss": "^0.58.5", |     "unocss": "^0.58.5", | ||||||
| @@ -26,7 +28,9 @@ | |||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@rushstack/eslint-patch": "^1.3.3", |     "@rushstack/eslint-patch": "^1.3.3", | ||||||
|     "@tsconfig/node20": "^20.1.2", |     "@tsconfig/node20": "^20.1.2", | ||||||
|  |     "@types/dompurify": "^3.0.5", | ||||||
|     "@types/node": "^20.11.10", |     "@types/node": "^20.11.10", | ||||||
|  |     "@unocss/preset-typography": "^0.58.5", | ||||||
|     "@vitejs/plugin-vue": "^5.0.3", |     "@vitejs/plugin-vue": "^5.0.3", | ||||||
|     "@vitejs/plugin-vue-jsx": "^3.1.0", |     "@vitejs/plugin-vue-jsx": "^3.1.0", | ||||||
|     "@vue/eslint-config-prettier": "^8.0.0", |     "@vue/eslint-config-prettier": "^8.0.0", | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ | |||||||
|  |  | ||||||
|         <div> |         <div> | ||||||
|           <div class="font-bold">{{ props.item?.author.nick }}</div> |           <div class="font-bold">{{ props.item?.author.nick }}</div> | ||||||
|           {{ props.item?.content }} |           <div class="prose prose-post" v-html="parseContent(props.item.content)"></div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </template> |     </template> | ||||||
| @@ -21,7 +21,14 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|  | import dompurify from "dompurify"; | ||||||
|  | import { parse } from "marked"; | ||||||
|  |  | ||||||
| const props = defineProps<{ item: any }>(); | const props = defineProps<{ item: any }>(); | ||||||
|  |  | ||||||
|  | function parseContent(src: string): string { | ||||||
|  |   return dompurify().sanitize(parse(src) as string); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style scoped> | <style scoped> | ||||||
| @@ -29,3 +36,9 @@ const props = defineProps<{ item: any }>(); | |||||||
|   border-radius: 8px; |   border-radius: 8px; | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | <style> | ||||||
|  | .prose.prose-post, p { | ||||||
|  |   margin: 0 !important; | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @@ -5,7 +5,7 @@ | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <v-infinite-scroll :items="props.posts" :onLoad="props.loader"> |     <v-infinite-scroll :items="props.posts" :onLoad="props.loader"> | ||||||
|       <template v-for="(item, index) in props.posts" :key="item"> |       <template v-for="item in props.posts" :key="item"> | ||||||
|         <div class="mb-3 px-1"> |         <div class="mb-3 px-1"> | ||||||
|           <post-item :item="item" /> |           <post-item :item="item" /> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								pkg/views/src/components/publish/PostEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								pkg/views/src/components/publish/PostEditor.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | <template> | ||||||
|  |   <v-dialog v-model="editor.show" class="max-w-[540px]"> | ||||||
|  |     <v-card title="New post"> | ||||||
|  |       <v-form> | ||||||
|  |         <v-card-text> | ||||||
|  |           <v-textarea | ||||||
|  |             required | ||||||
|  |             hide-details | ||||||
|  |             variant="outlined" | ||||||
|  |             label="What's happened?!" | ||||||
|  |           /> | ||||||
|  |  | ||||||
|  |           <div class="flex mt-1"> | ||||||
|  |             <v-tooltip text="Planned publish" location="start"> | ||||||
|  |               <template #activator="{ props }"> | ||||||
|  |                 <v-btn v-bind="props" type="button" variant="text" icon="mdi-calendar" size="small" /> | ||||||
|  |               </template> | ||||||
|  |             </v-tooltip> | ||||||
|  |             <v-tooltip text="Categories" location="start"> | ||||||
|  |               <template #activator="{ props }"> | ||||||
|  |                 <v-btn v-bind="props" type="button" variant="text" icon="mdi-shape" size="small" /> | ||||||
|  |               </template> | ||||||
|  |             </v-tooltip> | ||||||
|  |             <v-tooltip text="Media" location="start"> | ||||||
|  |               <template #activator="{ props }"> | ||||||
|  |                 <v-btn v-bind="props" type="button" variant="text" icon="mdi-camera" size="small" /> | ||||||
|  |               </template> | ||||||
|  |             </v-tooltip> | ||||||
|  |           </div> | ||||||
|  |         </v-card-text> | ||||||
|  |  | ||||||
|  |         <v-card-actions> | ||||||
|  |           <v-spacer></v-spacer> | ||||||
|  |  | ||||||
|  |           <v-btn type="reset" color="grey" @click="editor.show = false">Cancel</v-btn> | ||||||
|  |           <v-btn type="submit" @click.prevent>Publish</v-btn> | ||||||
|  |         </v-card-actions> | ||||||
|  |       </v-form> | ||||||
|  |     </v-card> | ||||||
|  |   </v-dialog> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useEditor } from "@/stores/editor"; | ||||||
|  |  | ||||||
|  | const editor = useEditor(); | ||||||
|  | </script> | ||||||
| @@ -25,11 +25,25 @@ | |||||||
|   <v-main> |   <v-main> | ||||||
|     <router-view /> |     <router-view /> | ||||||
|   </v-main> |   </v-main> | ||||||
|  |  | ||||||
|  |   <v-fab | ||||||
|  |     class="editor-fab" | ||||||
|  |     icon="mdi-pencil" | ||||||
|  |     color="primary" | ||||||
|  |     size="64" | ||||||
|  |     appear | ||||||
|  |     @click="editor.show = true" | ||||||
|  |   /> | ||||||
|  |  | ||||||
|  |   <post-editor /> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref } from "vue"; | import { ref } from "vue"; | ||||||
|  | import { useEditor } from "@/stores/editor"; | ||||||
|  | import PostEditor from "@/components/publish/PostEditor.vue"; | ||||||
|  |  | ||||||
|  | const editor = useEditor() | ||||||
| const navigationMenu = [ | const navigationMenu = [ | ||||||
|   { name: "Explore", icon: "mdi-compass", to: "explore" } |   { name: "Explore", icon: "mdi-compass", to: "explore" } | ||||||
| ]; | ]; | ||||||
| @@ -40,3 +54,11 @@ function toggleDrawer() { | |||||||
|   drawerOpen.value = !drawerOpen.value; |   drawerOpen.value = !drawerOpen.value; | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .editor-fab { | ||||||
|  |   position: fixed !important; | ||||||
|  |   bottom: 16px; | ||||||
|  |   right: 20px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import "vuetify/styles"; | |||||||
| import { createVuetify } from "vuetify"; | import { createVuetify } from "vuetify"; | ||||||
| import { md3 } from "vuetify/blueprints"; | import { md3 } from "vuetify/blueprints"; | ||||||
| import * as components from "vuetify/components"; | import * as components from "vuetify/components"; | ||||||
|  | import * as labsComponents from 'vuetify/labs/components' | ||||||
| import * as directives from "vuetify/directives"; | import * as directives from "vuetify/directives"; | ||||||
|  |  | ||||||
| import "@mdi/font/css/materialdesignicons.min.css"; | import "@mdi/font/css/materialdesignicons.min.css"; | ||||||
| @@ -22,8 +23,11 @@ const app = createApp(index); | |||||||
|  |  | ||||||
| app.use( | app.use( | ||||||
|   createVuetify({ |   createVuetify({ | ||||||
|     components, |  | ||||||
|     directives, |     directives, | ||||||
|  |     components: { | ||||||
|  |       ...components, | ||||||
|  |       ...labsComponents, | ||||||
|  |     }, | ||||||
|     blueprint: md3, |     blueprint: md3, | ||||||
|     theme: { |     theme: { | ||||||
|       defaultTheme: "original", |       defaultTheme: "original", | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								pkg/views/src/stores/editor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/views/src/stores/editor.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import { defineStore } from "pinia"; | ||||||
|  | import { ref } from "vue"; | ||||||
|  |  | ||||||
|  | export const useEditor = defineStore("editor", () => { | ||||||
|  |   const show = ref(false); | ||||||
|  |  | ||||||
|  |   return { show }; | ||||||
|  | }); | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <v-container class="flex max-md:flex-col gap-3 overflow-auto max-h-[calc(100vh-72px)] no-scrollbar"> |   <v-container class="flex max-md:flex-col gap-3 overflow-auto max-h-[calc(100vh-64px)] no-scrollbar"> | ||||||
|     <div class="timeline flex-grow-1 mt-[-16px]"> |     <div class="timeline flex-grow-1 mt-[-16px]"> | ||||||
|       <post-list :loading="loading" :posts="posts" :loader="readMore" /> |       <post-list :loading="loading" :posts="posts" :loader="readMore" /> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { defineConfig, presetUno } from "unocss" | import { defineConfig, presetAttributify, presetTypography, presetUno } from "unocss"; | ||||||
|  |  | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|   presets: [presetUno({ preflight: false })] |   presets: [presetAttributify(), presetTypography(), presetUno({ preflight: false })] | ||||||
| }) | }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user