From 63eb0a6b8442af5de32cb8a37d2bc4d809f4f0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Tue, 7 Jul 2026 17:45:37 +0300 Subject: [PATCH] =?UTF-8?q?feat(=D0=BF=D0=BE=D1=80=D1=82=D0=B0=D0=BB-?= =?UTF-8?q?=D0=BC=D0=BE=D0=B1=D0=B8=D0=BB=D0=BA=D0=B0):=20composable=20use?= =?UTF-8?q?LayoutTier=20=E2=80=94=20=D1=8F=D1=80=D1=83=D1=81=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D1=84=D0=BE=D0=BD/=D0=BF=D0=BB=D0=B0=D0=BD?= =?UTF-8?q?=D1=88=D0=B5=D1=82/=D0=B4=D0=B5=D1=81=D0=BA=D1=82=D0=BE=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/resources/js/composables/useLayoutTier.ts | 31 +++++++++++++++++++ app/tests/Frontend/useLayoutTier.spec.ts | 27 ++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 app/resources/js/composables/useLayoutTier.ts create mode 100644 app/tests/Frontend/useLayoutTier.spec.ts diff --git a/app/resources/js/composables/useLayoutTier.ts b/app/resources/js/composables/useLayoutTier.ts new file mode 100644 index 00000000..f99c6efd --- /dev/null +++ b/app/resources/js/composables/useLayoutTier.ts @@ -0,0 +1,31 @@ +import { computed, type ComputedRef } from 'vue'; +import { useDisplay } from 'vuetify'; + +export type LayoutTier = 'phone' | 'tablet' | 'desktop'; + +/** + * Единый источник «яруса» экрана для адаптива портала. + * phone < 600px (нижнее меню-вкладки) + * tablet 600..1279px (узкая колонка-иконки / rail) + * desktop >= 1280px (полный сайдбар 232px, как сейчас) + */ +export function useLayoutTier(): { + tier: ComputedRef; + isPhone: ComputedRef; + isTablet: ComputedRef; + isDesktop: ComputedRef; +} { + const display = useDisplay(); + const tier = computed(() => { + const w = display.width.value; + if (w < 600) return 'phone'; + if (w < 1280) return 'tablet'; + return 'desktop'; + }); + return { + tier, + isPhone: computed(() => tier.value === 'phone'), + isTablet: computed(() => tier.value === 'tablet'), + isDesktop: computed(() => tier.value === 'desktop'), + }; +} diff --git a/app/tests/Frontend/useLayoutTier.spec.ts b/app/tests/Frontend/useLayoutTier.spec.ts new file mode 100644 index 00000000..e219dd17 --- /dev/null +++ b/app/tests/Frontend/useLayoutTier.spec.ts @@ -0,0 +1,27 @@ +import { describe, it, expect } from 'vitest'; +import { defineComponent } from 'vue'; +import { mount } from '@vue/test-utils'; +import { createVuetify } from 'vuetify'; +import { useLayoutTier } from '../../resources/js/composables/useLayoutTier'; + +function tierAtWidth(width: number): string { + Object.defineProperty(window, 'innerWidth', { configurable: true, value: width }); + window.dispatchEvent(new Event('resize')); + const vuetify = createVuetify(); + let captured = ''; + const Harness = defineComponent({ + setup() { + const { tier } = useLayoutTier(); + captured = tier.value; + return () => null; + }, + }); + mount(Harness, { global: { plugins: [vuetify] } }); + return captured; +} + +describe('useLayoutTier', () => { + it('телефон при ширине < 600', () => { expect(tierAtWidth(390)).toBe('phone'); }); + it('планшет при 600..1279', () => { expect(tierAtWidth(820)).toBe('tablet'); }); + it('десктоп при >= 1280', () => { expect(tierAtWidth(1440)).toBe('desktop'); }); +});