mirror of
https://github.com/robonen/lorem-blog.git
synced 2026-03-20 02:44:39 +00:00
Merge pull request #5 from robonen/chore/update-deps
chore: update deps and use useAsyncData composable in blog context
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import antfu from '@antfu/eslint-config';
|
import antfu from '@antfu/eslint-config';
|
||||||
|
|
||||||
export default antfu({
|
export default antfu(
|
||||||
|
{
|
||||||
stylistic: {
|
stylistic: {
|
||||||
indent: 2,
|
indent: 2,
|
||||||
semi: true,
|
semi: true,
|
||||||
@@ -9,12 +10,19 @@ export default antfu({
|
|||||||
'style/comma-dangle': ['error', 'always-multiline'],
|
'style/comma-dangle': ['error', 'always-multiline'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
vue: true,
|
||||||
|
typescript: true,
|
||||||
rules: {
|
rules: {
|
||||||
'unused-imports/no-unused-imports': 'error',
|
'unused-imports/no-unused-imports': 'error',
|
||||||
'regexp/no-obscure-range': ['error', {
|
'regexp/no-obscure-range': ['error', {
|
||||||
allowed: 'all',
|
allowed: 'all',
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
vue: true,
|
},
|
||||||
typescript: true,
|
{
|
||||||
});
|
files: ['**/*.vue'],
|
||||||
|
rules: {
|
||||||
|
'import/first': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
28
package.json
28
package.json
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.12.1",
|
"packageManager": "pnpm@10.13.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
@@ -15,24 +15,24 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@robonen/stdlib": "^0.0.7",
|
"@robonen/stdlib": "^0.0.7",
|
||||||
"@robonen/vue": "^0.0.7",
|
"@robonen/vue": "^0.0.9",
|
||||||
"@tailwindcss/vite": "^4.1.8",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"tailwindcss": "^4.1.8",
|
"tailwindcss": "^4.1.11",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.17",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.14.1",
|
"@antfu/eslint-config": "^4.16.2",
|
||||||
"@robonen/tsconfig": "^0.0.2",
|
"@robonen/tsconfig": "^0.0.2",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.16.3",
|
||||||
"@vitejs/plugin-vue": "^5.2.3",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.31.0",
|
||||||
"eslint-plugin-vuejs-accessibility": "^2.4.1",
|
"eslint-plugin-vuejs-accessibility": "^2.4.1",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.1.0",
|
||||||
"vite": "^6.2.4",
|
"vite": "^7.0.4",
|
||||||
"vitest": "^3.1.1",
|
"vitest": "^3.2.4",
|
||||||
"vue-tsc": "^2.2.8"
|
"vue-tsc": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1036
pnpm-lock.yaml
generated
1036
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { BlogHeader, useProvidingPosts } from '@/widgets/Blog';
|
import { BlogHeader, BlogList, useProvidingPosts } from '@/widgets/Blog';
|
||||||
import { BlogList } from '@/widgets/Blog';
|
|
||||||
|
|
||||||
useProvidingPosts(() => fetch('/posts.json').then((response) => {
|
useProvidingPosts(() => fetch('/posts.json').then((response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { type MaybeRefOrGetter, onScopeDispose, toValue, watchEffect } from 'vue';
|
import type { MaybeRefOrGetter } from 'vue';
|
||||||
|
import { onScopeDispose, toValue, watchEffect } from 'vue';
|
||||||
|
|
||||||
export function useBodyScrollLock(isLocked: MaybeRefOrGetter<boolean>) {
|
export function useBodyScrollLock(isLocked: MaybeRefOrGetter<boolean>) {
|
||||||
let originalOverflow: string | null = null;
|
let originalOverflow: string | null = null;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import type { TemplateRef } from 'vue';
|
||||||
import { clamp } from '@robonen/stdlib';
|
import { clamp } from '@robonen/stdlib';
|
||||||
import { nextTick, onMounted, onUnmounted, ref, type TemplateRef, watch } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
export function useTextAreaAutosize(textareaRef: TemplateRef<HTMLTextAreaElement | null>) {
|
export function useTextAreaAutosize(textareaRef: TemplateRef<HTMLTextAreaElement | null>) {
|
||||||
const minHeight = ref(0);
|
const minHeight = ref(0);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type BadgeVariant, badgeVariants } from './types';
|
import type { BadgeVariant } from './types';
|
||||||
|
import { badgeVariants } from './types';
|
||||||
|
|
||||||
export interface BadgeProps {
|
export interface BadgeProps {
|
||||||
variant?: BadgeVariant;
|
variant?: BadgeVariant;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { ButtonProps } from './types';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { type ButtonProps, buttonSizes, buttonVariants } from './types';
|
import { buttonSizes, buttonVariants } from './types';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||||
variant: 'primary',
|
variant: 'primary',
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ describe('blog Context', () => {
|
|||||||
const context = useProvidingPosts(mockLoader);
|
const context = useProvidingPosts(mockLoader);
|
||||||
|
|
||||||
expect(context.loading.value).toBe(true);
|
expect(context.loading.value).toBe(true);
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(mockLoader).toHaveBeenCalled();
|
expect(mockLoader).toHaveBeenCalled();
|
||||||
@@ -115,6 +117,8 @@ describe('blog Context', () => {
|
|||||||
|
|
||||||
it('should return available tags from all posts', async () => {
|
it('should return available tags from all posts', async () => {
|
||||||
const context = useProvidingPosts(mockLoader);
|
const context = useProvidingPosts(mockLoader);
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
const expectedTags = ['vue', 'javascript', 'frontend', 'typescript', 'react', 'comparison'];
|
const expectedTags = ['vue', 'javascript', 'frontend', 'typescript', 'react', 'comparison'];
|
||||||
@@ -158,6 +162,7 @@ describe('blog Context', () => {
|
|||||||
|
|
||||||
const context = useProvidingPosts(errorLoader);
|
const context = useProvidingPosts(errorLoader);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to load posts:', expect.any(Error));
|
expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to load posts:', expect.any(Error));
|
||||||
expect(context.loading.value).toBe(false);
|
expect(context.loading.value).toBe(false);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { PostComment } from '@/entities/Comment';
|
import type { PostComment } from '@/entities/Comment';
|
||||||
import type { Post } from '@/entities/Post';
|
import type { Post } from '@/entities/Post';
|
||||||
import { useInjectionStore } from '@robonen/vue';
|
import { useAsyncState, useInjectionStore } from '@robonen/vue';
|
||||||
import { computed, reactive, ref, shallowRef } from 'vue';
|
import { computed, reactive, shallowRef } from 'vue';
|
||||||
import { kmpSearch } from '@/shared/utils';
|
import { kmpSearch } from '@/shared/utils';
|
||||||
|
|
||||||
type PostItem = Post & {
|
type PostItem = Post & {
|
||||||
@@ -16,8 +16,22 @@ export const {
|
|||||||
useProvidingState: useProvidingPosts,
|
useProvidingState: useProvidingPosts,
|
||||||
useInjectedState: useInjectedPosts,
|
useInjectedState: useInjectedPosts,
|
||||||
} = useInjectionStore((loader: () => Promise<any>) => {
|
} = useInjectionStore((loader: () => Promise<any>) => {
|
||||||
const posts = shallowRef<PostItem[]>([]);
|
const {
|
||||||
const loading = ref(false);
|
state: posts,
|
||||||
|
isLoading: loading,
|
||||||
|
executeImmediately: loadPosts,
|
||||||
|
} = useAsyncState<PostItem[]>(
|
||||||
|
async () => {
|
||||||
|
const response = await loader();
|
||||||
|
return response.data as PostItem[];
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
onError(error) {
|
||||||
|
console.error('Failed to load posts:', error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const selectedPost = shallowRef<PostItem | null>(null);
|
const selectedPost = shallowRef<PostItem | null>(null);
|
||||||
|
|
||||||
@@ -54,21 +68,6 @@ export const {
|
|||||||
return Array.from(tagsSet);
|
return Array.from(tagsSet);
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadPosts = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await loader();
|
|
||||||
posts.value = data!.data as PostItem[];
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('Failed to load posts:', error);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function toggleTag(tag: string) {
|
function toggleTag(tag: string) {
|
||||||
if (filters.tags.has(tag))
|
if (filters.tags.has(tag))
|
||||||
filters.tags.delete(tag);
|
filters.tags.delete(tag);
|
||||||
@@ -85,9 +84,6 @@ export const {
|
|||||||
filters.tags.clear();
|
filters.tags.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial load
|
|
||||||
loadPosts();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
posts,
|
posts,
|
||||||
loading,
|
loading,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { NavigationMenuItem } from '../NavigationMenu';
|
||||||
import { Logo } from '@/shared/icons';
|
import { Logo } from '@/shared/icons';
|
||||||
import { NavigationMenu, type NavigationMenuItem } from '../NavigationMenu';
|
import { NavigationMenu } from '../NavigationMenu';
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ name: 'Главная', path: '/' },
|
{ name: 'Главная', path: '/' },
|
||||||
|
|||||||
Reference in New Issue
Block a user