diff --git a/frontend/my-materials.html b/frontend/my-materials.html index 7412ba4..ceb3c4c 100644 --- a/frontend/my-materials.html +++ b/frontend/my-materials.html @@ -17,16 +17,29 @@ .mm-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; } .mm-search { flex: 1; min-width: 180px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 9px; font: inherit; background: var(--surface); color: var(--text); } .mm-kind { padding: 8px 10px; border: 1px solid var(--border); border-radius: 9px; font: inherit; background: var(--surface); color: var(--text); } - .mm-cols { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; } - .mm-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid var(--border); border-radius: 99px; background: var(--surface); cursor: pointer; font-size: .8rem; font-weight: 600; color: var(--text-2); } - .mm-chip:hover { border-color: var(--violet); color: var(--violet); } - .mm-chip.active { background: var(--violet); border-color: var(--violet); color: #fff; } - .mm-chip-count { font-size: .7rem; opacity: .7; } - .mm-chip-edit { display: inline-flex; opacity: .65; margin-left: 2px; } - .mm-chip-edit svg { width: 12px; height: 12px; } - .mm-chip-edit:hover { opacity: 1; } - .mm-chip-add { border-style: dashed; } + /* Двухколоночная раскладка: рейл папок слева + контент справа */ + .mm-body { display: flex; gap: 20px; align-items: flex-start; } + .mm-rail { width: 212px; flex-shrink: 0; position: sticky; top: 14px; display: flex; flex-direction: column; gap: 8px; } + .mm-rail-title { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); padding: 0 10px; } + .mm-rail-list { display: flex; flex-direction: column; gap: 3px; } + .mm-rail-item { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 9px; cursor: pointer; font-size: .84rem; font-weight: 600; color: var(--text-2); border: 1px solid transparent; transition: background .12s, color .12s, border-color .12s; } + .mm-rail-item:hover { background: rgba(155,93,229,0.07); color: var(--violet); } + .mm-rail-item.active { background: var(--violet); color: #fff; } + .mm-rail-item.drop-hover { border-color: var(--violet); border-style: dashed; background: rgba(155,93,229,0.14); color: var(--violet); } + .mm-rail-item svg { width: 15px; height: 15px; flex-shrink: 0; } + .mm-rail-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .mm-rail-count { font-size: .72rem; opacity: .65; } + .mm-rail-item.active .mm-rail-count { opacity: .85; } + .mm-rail-edit { display: inline-flex; opacity: 0; transition: opacity .12s; } + .mm-rail-item:hover .mm-rail-edit { opacity: .55; } + .mm-rail-edit:hover { opacity: 1 !important; } + .mm-rail-edit svg { width: 13px; height: 13px; } + .mm-rail-add { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 10px; border: 1px dashed var(--border); border-radius: 9px; background: transparent; cursor: pointer; font-size: .8rem; font-weight: 600; color: var(--text-2); transition: border-color .12s, color .12s; } + .mm-rail-add:hover { border-color: var(--violet); color: var(--violet); } + .mm-rail-add svg { width: 14px; height: 14px; } + .mm-content { flex: 1; min-width: 0; } .mm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; } + .mm-card.mm-dragging { opacity: .45; } .mm-card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; display: flex; flex-direction: column; position: relative; transition: border-color .14s, box-shadow .14s, transform .14s; } .mm-card:hover { border-color: rgba(155,93,229,0.3); box-shadow: 0 8px 24px rgba(15,23,42,0.08); transform: translateY(-2px); } .mm-card-media { background: #f1f5f9; aspect-ratio: 16/10; display: flex; align-items: center; justify-content: center; overflow: hidden; } @@ -57,6 +70,15 @@ .mm-viewer-note { white-space: pre-wrap; word-break: break-word; line-height: 1.6; font-size: 0.9rem; color: var(--text-2); } .mm-empty { padding: 60px 20px; text-align: center; color: var(--text-3); } .mm-empty svg { width: 38px; height: 38px; opacity: 0.4; margin-bottom: 12px; } + @media (max-width: 768px) { + .mm-body { flex-direction: column; } + .mm-rail { width: auto; position: static; flex-direction: row; overflow-x: auto; gap: 6px; padding-bottom: 4px; } + .mm-rail-title { display: none; } + .mm-rail-list { flex-direction: row; gap: 6px; } + .mm-rail-item { flex: 0 0 auto; } + .mm-rail-label { flex: 0 1 auto; max-width: 120px; } + .mm-rail-add { flex: 0 0 auto; white-space: nowrap; } + } @media (max-width: 640px) { .mm-grid { grid-template-columns: 1fr 1fr; } .mm-main { padding: 18px 14px; } } @@ -71,18 +93,26 @@
Сохранённые с уроков: страницы доски, заметки и вложения. Хранятся у вас и не пропадают, даже если урок удалят.
-
- - +
+ +
+
+ + +
+
Загрузка…
+
-
-
Загрузка…
@@ -166,8 +196,8 @@ const mv = moveSelect(m); if (m.kind === 'board' || m.kind === 'image') { - return `
- + return `
+
${chip}
${esc(m.title || kind)}
@@ -183,7 +213,7 @@ } if (m.kind === 'link') { - return `
+ return `
@@ -205,7 +235,7 @@ } // note - return `
+ return `
${mathHtml(m.body || '')}
${chip} @@ -216,22 +246,59 @@
`; } - /* ── Collections bar ── */ - function chip(key, label, count, editId) { + /* ── Folder rail (вертикальный список папок слева) ── */ + function railItem(key, label, count, editId, droppable) { const active = _filter.col === key ? ' active' : ''; - const ed = editId ? `${PENCIL}` : ''; - return ``; + const ed = editId + ? `${PENCIL}` + : ''; + const ic = key === 'all' ? 'inbox' : (key === 'none' ? 'folder-minus' : 'folder'); + const drop = droppable + ? ` ondragover="mmDragOver(event,this)" ondragleave="mmDragLeave(this)" ondrop="mmDrop(event,'${key}')"` + : ''; + return `
+ + ${esc(label)} + ${count}${ed} +
`; } function renderCols() { const bar = document.getElementById('mm-cols'); const noneCount = _mats.filter(m => !m.collection_id).length; - let html = chip('all', 'Все', _mats.length); - _cols.forEach(c => { html += chip(String(c.id), c.name, c.count, c.id); }); - if (noneCount) html += chip('none', 'Без папки', noneCount); - html += ``; + let html = railItem('all', 'Все', _mats.length, null, false); + _cols.forEach(c => { html += railItem(String(c.id), c.name, c.count, c.id, true); }); + html += railItem('none', 'Без папки', noneCount, null, true); bar.innerHTML = html; } + /* ── Drag-and-drop: перетащить карточку на папку, чтобы переместить ── */ + let _dragId = null; + function mmDragStart(e, id) { + _dragId = id; + try { e.dataTransfer.setData('text/plain', String(id)); e.dataTransfer.effectAllowed = 'move'; } catch (_) {} + if (e.currentTarget) e.currentTarget.classList.add('mm-dragging'); + } + function mmDragEnd(e) { + _dragId = null; + if (e.currentTarget) e.currentTarget.classList.remove('mm-dragging'); + document.querySelectorAll('.mm-rail-item.drop-hover').forEach(el => el.classList.remove('drop-hover')); + } + function mmDragOver(e, el) { e.preventDefault(); try { e.dataTransfer.dropEffect = 'move'; } catch (_) {} el.classList.add('drop-hover'); } + function mmDragLeave(el) { el.classList.remove('drop-hover'); } + function mmDrop(e, key) { + e.preventDefault(); + if (e.currentTarget) e.currentTarget.classList.remove('drop-hover'); + let id = _dragId; + if (id == null && e.dataTransfer) { const d = e.dataTransfer.getData('text/plain'); id = d ? Number(d) : null; } + if (id == null) return; + const cid = (key === 'none' || key === 'all') ? null : key; + const mt = _mats.find(x => x.id === id); + if (mt && String(mt.collection_id || '') === String(cid || '')) return; // уже в этой папке + moveMaterial(id, cid); + } + window.mmDragStart = mmDragStart; window.mmDragEnd = mmDragEnd; + window.mmDragOver = mmDragOver; window.mmDragLeave = mmDragLeave; window.mmDrop = mmDrop; + function filtered() { return _mats.filter(m => { if (_filter.col === 'none' && m.collection_id) return false;