fix(spa): visual fixes for SPA (#106)
* fix(spa): prevent more-link button from wrapping in block header Long block titles squeezed the "Смотреть всё" link in BaseBlock, causing its text to wrap onto two lines. Allow the title column to shrink and keep the link in a single line. * fix(spa): enable typography styles for product description Add @tailwindcss/typography plugin so the `prose` classes used in the product description actually apply: paragraph spacing, headings, hr, links and tables. Also make wide tables inside `.prose` horizontally scrollable instead of overflowing the viewport. * fix(spa): open description links via Telegram WebApp API Links inside the v-html product description used to navigate the Mini App's WebView away from the app on Telegram mobile. Intercept clicks on anchors in the description and route them through Telegram.WebApp.openTelegramLink for t.me/tg:// targets and Telegram.WebApp.openLink for everything else, falling back to window.open when running outside Telegram. * fix(spa): refine categories list layout Tighten category item typography (text-base, leading-snug) and slightly enlarge the icon for a better balance with long titles. Drop the duplicate "Назад к ..." link and replace the parent-category CTA with a daisyUI soft block button "Показать все товары категории" so the primary action is visually distinct from the list.
This commit is contained in:
48
frontend/spa/package-lock.json
generated
48
frontend/spa/package-lock.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/vue": "^8.1.0",
|
"@testing-library/vue": "^8.1.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
@@ -1588,6 +1589,19 @@
|
|||||||
"node": ">= 20"
|
"node": ">= 20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/typography": {
|
||||||
|
"version": "0.5.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
|
||||||
|
"integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/vite": {
|
"node_modules/@tailwindcss/vite": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz",
|
||||||
@@ -2520,6 +2534,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cssesc": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"cssesc": "bin/cssesc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssstyle": {
|
"node_modules/cssstyle": {
|
||||||
"version": "5.3.7",
|
"version": "5.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
|
||||||
@@ -4276,6 +4303,20 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss-value-parser": {
|
"node_modules/postcss-value-parser": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
@@ -5051,6 +5092,13 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.3.3",
|
"version": "7.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/vue": "^8.1.0",
|
"@testing-library/vue": "^8.1.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
:key="category.id"
|
:key="category.id"
|
||||||
class="flex items-center"
|
class="flex items-center gap-3"
|
||||||
@click.prevent="$emit('onSelect', category)"
|
@click.prevent="$emit('onSelect', category)"
|
||||||
>
|
>
|
||||||
<div class="avatar">
|
<div class="avatar shrink-0">
|
||||||
<div class="w-8 h-8 rounded">
|
<div class="w-10 h-10 rounded">
|
||||||
<img :src="category.image" :alt="category.name" loading="lazy" width="30" height="30" class="bg-base-400"/>
|
<img :src="category.image" :alt="category.name" loading="lazy" width="40" height="40" class="bg-base-400"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="ml-4 text-lg line-clamp-2">{{ category.name }}</h3>
|
<h3 class="text-base font-medium leading-snug line-clamp-2">{{ category.name }}</h3>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="px-4">
|
<section class="px-4">
|
||||||
<header class="flex justify-between items-end mb-4">
|
<header class="flex justify-between items-end mb-4 gap-2">
|
||||||
<div>
|
<div class="min-w-0">
|
||||||
<div v-if="title" class="font-bold uppercase">{{ title }}</div>
|
<div v-if="title" class="font-bold uppercase">{{ title }}</div>
|
||||||
<div v-if="description" class="text-sm text-base-content/50">{{ description }}</div>
|
<div v-if="description" class="text-sm text-base-content/50">{{ description }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="moreLink">
|
<div v-if="moreLink" class="shrink-0">
|
||||||
<RouterLink :to="moreLink" class="btn btn-soft btn-xs" @click="haptic.selectionChanged">
|
<RouterLink :to="moreLink" class="btn btn-soft btn-xs whitespace-nowrap" @click="haptic.selectionChanged">
|
||||||
{{ moreText || 'Смотреть всё' }}
|
{{ moreText || 'Смотреть всё' }}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||||
|
|||||||
@@ -2,10 +2,19 @@
|
|||||||
|
|
||||||
@custom-variant dark (&:where(.dark, .dark *));
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
@plugin "daisyui" {
|
@plugin "daisyui" {
|
||||||
themes: all;
|
themes: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose :where(table):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
--color-base-100 - DaisyUI background
|
--color-base-100 - DaisyUI background
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,31 +9,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button v-if="parentId && parentCategory" class="py-1 px-4 flex items-center mb-3 cursor-pointer" @click="goBack">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 min-w-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<span class="ml-2 line-clamp-2">Назад к "{{ parentCategory.name }}"</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="parentCategory && parentCategory.children?.length && settings.show_category_products_button"
|
v-if="parentCategory && parentCategory.children?.length && settings.show_category_products_button"
|
||||||
class="py-2 px-4 flex items-center mb-3 cursor-pointer border-b w-full pb-2 border-base-200"
|
class="btn btn-soft btn-block btn-sm mb-3 justify-start"
|
||||||
@click.prevent="showProductsInParentCategory"
|
@click.prevent="showProductsInParentCategory"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 shrink-0">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
<span>Показать все товары категории</span>
|
||||||
<span class="ml-2">Показать товары из "{{ parentCategory.name }}"</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<TransitionGroup
|
<TransitionGroup
|
||||||
name="stagger"
|
name="stagger"
|
||||||
tag="ul"
|
tag="ul"
|
||||||
appear
|
appear
|
||||||
class="space-y-4"
|
class="space-y-3"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="(category, i) in categories"
|
v-for="(category, i) in categories"
|
||||||
@@ -99,10 +90,6 @@ function onSelect(category) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goBack() {
|
|
||||||
router.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showProductsInParentCategory() {
|
function showProductsInParentCategory() {
|
||||||
if (parentId.value != null) {
|
if (parentId.value != null) {
|
||||||
router.push({name: "product.categories.show", params: {category_id: parentId.value}});
|
router.push({name: "product.categories.show", params: {category_id: parentId.value}});
|
||||||
|
|||||||
@@ -87,7 +87,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Описание
|
Описание
|
||||||
</h3>
|
</h3>
|
||||||
<div class="prose prose-sm max-w-none text-base-content/80" v-html="product.description"></div>
|
<div
|
||||||
|
class="prose prose-sm max-w-none text-base-content/80"
|
||||||
|
v-html="product.description"
|
||||||
|
@click="handleDescriptionClick"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Attributes -->
|
<!-- Attributes -->
|
||||||
@@ -306,6 +310,29 @@ async function onCartBtnClick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDescriptionClick(event) {
|
||||||
|
const link = event.target.closest('a');
|
||||||
|
if (!link) return;
|
||||||
|
|
||||||
|
const href = link.getAttribute('href');
|
||||||
|
if (!href || href.startsWith('#') || href.startsWith('javascript:')) return;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const telegramWebApp = window.Telegram?.WebApp;
|
||||||
|
if (!telegramWebApp) {
|
||||||
|
window.open(href, '_blank', 'noopener,noreferrer');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^https?:\/\/(t|telegram)\.me\//i.test(href) || href.startsWith('tg://')) {
|
||||||
|
telegramWebApp.openTelegramLink(href);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramWebApp.openLink(href, {try_instant_view: false});
|
||||||
|
}
|
||||||
|
|
||||||
function openProductInMarketplace() {
|
function openProductInMarketplace() {
|
||||||
if (!product.value.share) {
|
if (!product.value.share) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user