Ремонт: техника — авто-группировка карточек по типу + перенос строк в названии
All checks were successful
Deploy Remont Site / deploy (push) Successful in 19s
All checks were successful
Deploy Remont Site / deploy (push) Successful in 19s
- inline() поддерживает <br> в тексте заметки (было видно литеральным) - карточки: «Модель<br>описание» → модель жирным + приглушённое описание - колонка «Тип/Категория/Техника» → карточки группируются по типу с подзаголовками (пустая ячейка типа наследует значение сверху) - таблица «Варианты техники» переведена в формат Тип|Название|Цена|Ссылка|Фото - README: описан формат таблицы техники Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KjyzuPoH9T5k3oCbKEEazy
This commit is contained in:
@@ -280,6 +280,7 @@ a.rooms-link:hover .room-name { color: var(--sage-deep); }
|
||||
.opt__body { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
|
||||
.opt__no { font-size: 12px; letter-spacing: .12em; color: var(--sage-deep); font-weight: 600; }
|
||||
.opt__name { font-family: var(--ff-display); font-size: 20px; line-height: 1.15; }
|
||||
.opt__desc { font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); margin-top: 2px; }
|
||||
.opt__price { font-family: var(--ff-display); font-size: 28px; margin-top: 8px; }
|
||||
.opt__price span { font-size: 15px; color: var(--ink-soft); }
|
||||
.opt__link {
|
||||
@@ -288,6 +289,15 @@ a.rooms-link:hover .room-name { color: var(--sage-deep); }
|
||||
}
|
||||
.opt__link svg { width: 15px; height: 15px; transition: transform .2s; }
|
||||
.opt__link:hover svg { transform: translate(2px,-2px); }
|
||||
/* группа карточек по типу (техника) */
|
||||
.opt-group { margin-top: 34px; }
|
||||
.opt-group:first-child { margin-top: 0; }
|
||||
.opt-group__title {
|
||||
display: flex; align-items: baseline; gap: 12px; margin: 0 0 16px;
|
||||
font-family: var(--ff-display); font-size: 21px; letter-spacing: -.01em;
|
||||
}
|
||||
.opt-group__title span { font-size: 12px; letter-spacing: .06em; color: var(--ink-soft); font-weight: 400; }
|
||||
.opt-group__title::after { content: ""; flex: 1; height: 1px; background: var(--line); align-self: center; }
|
||||
@media (max-width: 860px) { .options { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 560px) { .options { grid-template-columns: 1fr; } }
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ function inline(t) {
|
||||
t = esc(t);
|
||||
t = t.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
|
||||
t = t.replace(/<br\s*\/?>/gi, '<br>'); // разрешаем перенос строки <br> в тексте заметки
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -179,28 +180,54 @@ function renderCards(tbl) {
|
||||
const iPrice = colIndex(H, /цена|price|₽|руб/i);
|
||||
const iLink = colIndex(H, /ссылк|link|url/i);
|
||||
const iPhoto = colIndex(H, /фото|photo|картин|image/i);
|
||||
const iName = H.findIndex((h, idx) => idx !== iNo && idx !== iPrice && idx !== iLink && idx !== iPhoto && h);
|
||||
const iCat = colIndex(H, /^(тип|категор|группа|техника|раздел)$/i);
|
||||
const iName = H.findIndex((h, idx) => idx !== iNo && idx !== iPrice && idx !== iLink && idx !== iPhoto && idx !== iCat && h);
|
||||
|
||||
const cards = tbl.rows.map((r, idx) => {
|
||||
const no = iNo >= 0 && r[iNo] ? r[iNo] : String(idx + 1);
|
||||
const name = iName >= 0 ? r[iName] : '';
|
||||
// одна карточка. no — подпись «Вариант N» (в пределах группы)
|
||||
const card = (r, no) => {
|
||||
const nameRaw = iName >= 0 ? (r[iName] || '') : '';
|
||||
// «Модель<br>описание» → жирная модель + приглушённое описание
|
||||
const [head, ...rest] = nameRaw.split(/<br\s*\/?>/i);
|
||||
const desc = rest.join('<br>').trim();
|
||||
const priceRaw = iPrice >= 0 ? r[iPrice] : '';
|
||||
const priceNum = (priceRaw.match(/[\d\s .]+/) || [''])[0].trim();
|
||||
const unit = /₽|руб/i.test(priceRaw) ? '₽' : '';
|
||||
const photo = iPhoto >= 0 ? parseWiki(r[iPhoto]) : null;
|
||||
const url = iLink >= 0 ? extractUrl(r[iLink]) : '';
|
||||
const media = photo
|
||||
? `<div class="opt__media" data-zoom-wrap><img src="${imgSrc(photo)}" alt="${esc(name)}" data-zoom loading="lazy"></div>` : '';
|
||||
? `<div class="opt__media" data-zoom-wrap><img src="${imgSrc(photo)}" alt="${esc(head)}" data-zoom loading="lazy"></div>` : '';
|
||||
const price = priceNum
|
||||
? `<span class="opt__price">${esc(priceNum)}${unit ? ` <span>${unit}</span>` : ''}</span>` : '';
|
||||
const link = url
|
||||
? `<a class="opt__link" href="${esc(url)}" target="_blank" rel="noopener">Открыть на ${esc(domainLabel(url))} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M7 17 17 7M8 7h9v9"/></svg></a>` : '';
|
||||
return `<article class="opt reveal">${media}<div class="opt__body">
|
||||
<span class="opt__no">Вариант ${esc(no)}</span>
|
||||
<span class="opt__name">${inline(name)}</span>
|
||||
<span class="opt__name">${inline(head)}</span>
|
||||
${desc ? `<span class="opt__desc">${inline(desc)}</span>` : ''}
|
||||
${price}${link}</div></article>`;
|
||||
};
|
||||
|
||||
// без колонки типа — простая сетка карточек
|
||||
if (iCat < 0) {
|
||||
const cards = tbl.rows.map((r, idx) => card(r, iNo >= 0 && r[iNo] ? r[iNo] : idx + 1)).join('\n');
|
||||
return `<div class="options">${cards}</div>`;
|
||||
}
|
||||
|
||||
// с колонкой типа — группируем по типу (пустая ячейка наследует тип сверху)
|
||||
const groups = [];
|
||||
let cur = null, lastCat = '';
|
||||
tbl.rows.forEach((r) => {
|
||||
const cat = (r[iCat] && r[iCat].trim()) || lastCat;
|
||||
lastCat = cat;
|
||||
if (!cur || cur.cat !== cat) { cur = { cat, rows: [] }; groups.push(cur); }
|
||||
cur.rows.push(r);
|
||||
});
|
||||
return groups.map((g) => {
|
||||
const cards = g.rows.map((r, i) => card(r, i + 1)).join('\n');
|
||||
return `<div class="opt-group reveal">
|
||||
<h4 class="opt-group__title">${inline(g.cat)}<span>${g.rows.length} ${plural(g.rows.length, 'вариант', 'варианта', 'вариантов')}</span></h4>
|
||||
<div class="options">${cards}</div></div>`;
|
||||
}).join('\n');
|
||||
return `<div class="options">${cards}</div>`;
|
||||
}
|
||||
|
||||
function tableCell(c) {
|
||||
|
||||
Reference in New Issue
Block a user