Ремонт: техника — авто-группировка карточек по типу + перенос строк в названии
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__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__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__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 { font-family: var(--ff-display); font-size: 28px; margin-top: 8px; }
|
||||||
.opt__price span { font-size: 15px; color: var(--ink-soft); }
|
.opt__price span { font-size: 15px; color: var(--ink-soft); }
|
||||||
.opt__link {
|
.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 svg { width: 15px; height: 15px; transition: transform .2s; }
|
||||||
.opt__link:hover svg { transform: translate(2px,-2px); }
|
.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: 860px) { .options { grid-template-columns: 1fr 1fr; } }
|
||||||
@media (max-width: 560px) { .options { grid-template-columns: 1fr; } }
|
@media (max-width: 560px) { .options { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ function inline(t) {
|
|||||||
t = esc(t);
|
t = esc(t);
|
||||||
t = t.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
t = t.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||||
t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
|
t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
|
||||||
|
t = t.replace(/<br\s*\/?>/gi, '<br>'); // разрешаем перенос строки <br> в тексте заметки
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,28 +180,54 @@ function renderCards(tbl) {
|
|||||||
const iPrice = colIndex(H, /цена|price|₽|руб/i);
|
const iPrice = colIndex(H, /цена|price|₽|руб/i);
|
||||||
const iLink = colIndex(H, /ссылк|link|url/i);
|
const iLink = colIndex(H, /ссылк|link|url/i);
|
||||||
const iPhoto = colIndex(H, /фото|photo|картин|image/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) => {
|
// одна карточка. no — подпись «Вариант N» (в пределах группы)
|
||||||
const no = iNo >= 0 && r[iNo] ? r[iNo] : String(idx + 1);
|
const card = (r, no) => {
|
||||||
const name = iName >= 0 ? r[iName] : '';
|
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 priceRaw = iPrice >= 0 ? r[iPrice] : '';
|
||||||
const priceNum = (priceRaw.match(/[\d\s .]+/) || [''])[0].trim();
|
const priceNum = (priceRaw.match(/[\d\s .]+/) || [''])[0].trim();
|
||||||
const unit = /₽|руб/i.test(priceRaw) ? '₽' : '';
|
const unit = /₽|руб/i.test(priceRaw) ? '₽' : '';
|
||||||
const photo = iPhoto >= 0 ? parseWiki(r[iPhoto]) : null;
|
const photo = iPhoto >= 0 ? parseWiki(r[iPhoto]) : null;
|
||||||
const url = iLink >= 0 ? extractUrl(r[iLink]) : '';
|
const url = iLink >= 0 ? extractUrl(r[iLink]) : '';
|
||||||
const media = photo
|
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
|
const price = priceNum
|
||||||
? `<span class="opt__price">${esc(priceNum)}${unit ? ` <span>${unit}</span>` : ''}</span>` : '';
|
? `<span class="opt__price">${esc(priceNum)}${unit ? ` <span>${unit}</span>` : ''}</span>` : '';
|
||||||
const link = url
|
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>` : '';
|
? `<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">
|
return `<article class="opt reveal">${media}<div class="opt__body">
|
||||||
<span class="opt__no">Вариант ${esc(no)}</span>
|
<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>`;
|
${price}${link}</div></article>`;
|
||||||
}).join('\n');
|
};
|
||||||
|
|
||||||
|
// без колонки типа — простая сетка карточек
|
||||||
|
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>`;
|
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
function tableCell(c) {
|
function tableCell(c) {
|
||||||
|
|||||||
@@ -26,6 +26,12 @@
|
|||||||
- Подряд идущие картинки `![[файл.png]]` → **галерея рендеров** (1 крупно / 2 / 3 / 4 в сетке).
|
- Подряд идущие картинки `![[файл.png]]` → **галерея рендеров** (1 крупно / 2 / 3 / 4 в сетке).
|
||||||
- **Таблица с колонкой «Фото» и без «Комментарий»** → красивые **карточки** (диваны, столы).
|
- **Таблица с колонкой «Фото» и без «Комментарий»** → красивые **карточки** (диваны, столы).
|
||||||
Колонки: `№ | Название | Цена | Ссылка | Фото`. Ссылка — просто URL, кнопка подпишется сама.
|
Колонки: `№ | Название | Цена | Ссылка | Фото`. Ссылка — просто URL, кнопка подпишется сама.
|
||||||
|
- В «Названии» можно писать модель + описание, разделяя переносом `<br>`:
|
||||||
|
`Haier C4F640<br>400 л, инвертор` → модель жирным, описание — приглушённой строкой под ней.
|
||||||
|
- Добавь колонку **«Тип»** (или «Категория», «Техника») — карточки **автоматически сгруппируются**
|
||||||
|
по типу с подзаголовками (Холодильник, Духовка…). Тип пиши в первой строке группы,
|
||||||
|
в остальных строках этой группы ячейку «Тип» **оставляй пустой** — они наследуют тип сверху.
|
||||||
|
Пример для техники: `Тип | Модель | Цена | Ссылка | Фото`.
|
||||||
- **Таблица с колонкой «Комментарий»** → аккуратная **сравнительная таблица** (техника).
|
- **Таблица с колонкой «Комментарий»** → аккуратная **сравнительная таблица** (техника).
|
||||||
Если добавить колонку «Фото» — появятся миниатюры прямо в таблице.
|
Если добавить колонку «Фото» — появятся миниатюры прямо в таблице.
|
||||||
- Картинка сразу перед нумерованным списком → блок **«вариант отделки»** (фото слева, список справа).
|
- Картинка сразу перед нумерованным списком → блок **«вариант отделки»** (фото слева, список справа).
|
||||||
|
|||||||
@@ -35,14 +35,14 @@ swatches: Шалфей #7C8A66, Дуб #C7A579, Кремовый #EDE8DE, Гра
|
|||||||
|
|
||||||
### Варианты техники
|
### Варианты техники
|
||||||
|
|
||||||
| Название | Цена | Ссылка | Фото |
|
| Тип | Название | Цена | Ссылка | Фото |
|
||||||
| ------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------- | ----------------------------------------- |
|
| --- | --- | --- | --- | --- |
|
||||||
| Варочная панель Haier | 28 000 ₽ | https://haieronline.ru/catalog/built-in/glass-ceramic-cove/hhy-y53nvb/ | ![[Pasted image 20260716000501.png\|154]] |
|
| Варочная панель | Haier | 28 000 ₽ | https://haieronline.ru/catalog/built-in/glass-ceramic-cove/hhy-y53nvb/ | ![[Pasted image 20260716000501.png\|154]] |
|
||||||
| Духовка Haier HOD-PM08TGB<br>70 л, 8 режимов, 2 стекла | 29 000 ₽ | https://haieronline.ru/catalog/built-in/ovens/hodpm08tgb/ | ![[Pasted image 20260716000638.png\|176]] |
|
| Духовка | Haier HOD-PM08TGB<br>70 л, 8 режимов, 2 стекла | 29 000 ₽ | https://haieronline.ru/catalog/built-in/ovens/hodpm08tgb/ | ![[Pasted image 20260716000638.png\|176]] |
|
||||||
| Духовка Haier HOX-P06HGBX<br>76 л, 6 режимов, 3 стекла | 30 000 ₽ | https://haieronline.ru/catalog/built-in/ovens/hoxp06hgbx/ | ![[Pasted image 20260716000625.png\|177]] |
|
| | Haier HOX-P06HGBX<br>76 л, 6 режимов, 3 стекла | 30 000 ₽ | https://haieronline.ru/catalog/built-in/ovens/hoxp06hgbx/ | ![[Pasted image 20260716000625.png\|177]] |
|
||||||
| Холодильник LG GA-B509CLSL<br>419 л, инвентор, <br>3 ящика морозилки,<br> нулевой зазор | 50 000 ₽ | https://www.lg.com/ru/refrigerators/lg-GA-B509CLSL# | ![[Pasted image 20260716125354.png\|159]] |
|
| Холодильник | LG GA-B509CLSL<br>419 л, инвентор, <br>3 ящика морозилки,<br> нулевой зазор | 50 000 ₽ | https://www.lg.com/ru/refrigerators/lg-GA-B509CLSL# | ![[Pasted image 20260716125354.png\|159]] |
|
||||||
| Холодильник HAIER C4F640CFMRGU1<br>400 л, инвентор, <br>4 ящика морозилки, нулевая зона свежести | 59 000 ₽ | https://haieronline.ru/catalog/appliances/fridges/kholodilnik-haier-c4f640cfmrgu1/ | ![[Pasted image 20260716125554.png\|155]] |
|
| | HAIER C4F640CFMRGU1<br>400 л, инвентор, <br>4 ящика морозилки, нулевая зона свежести | 59 000 ₽ | https://haieronline.ru/catalog/appliances/fridges/kholodilnik-haier-c4f640cfmrgu1/ | ![[Pasted image 20260716125554.png\|155]] |
|
||||||
| | | | |
|
| | | | | |
|
||||||
|
|
||||||
| Техника | Ссылка | Цена | Комментарий | Фото |
|
| Техника | Ссылка | Цена | Комментарий | Фото |
|
||||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---- |
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---- |
|
||||||
|
|||||||
Reference in New Issue
Block a user