Пользователь
- Регистрация
- 1 Дек 2025
- Сообщения
- 48
- Автор темы
- #1
Зарегистрировавшись у нас, вы сможете обсуждать, делиться и отправлять личные сообщения другим участникам нашего сообщества.
Зарегистрироваться!<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Меню быстрого доступа — CRMP ONLINE</title>
<style>
:root{
--bg:#0f1724; --panel:#0b1220; --accent:#0ea5a4; --muted:#94a3b8; --glass:rgba(255,255,255,0.03);
--radius:12px; --shadow:0 6px 18px rgba(2,6,23,0.6);
--width:320px;
color-scheme: dark;
}
body{font-family:Inter, Roboto, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial; margin:0;}
/* Wrapper */
.quick-access{position:fixed; right:20px; top:20px; width:var(--width); z-index:9999}
.qa-toggle{appearance:none;border:none;background:linear-gradient(180deg,var(--accent),#06b6b4);color:#022; width:56px;height:56px;border-radius:50%;box-shadow:var(--shadow);display:flex;align-items:center;justify-content:center;font-weight:700;cursor:pointer}
.qa-panel{margin-top:10px;background:var(--panel);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);border:1px solid rgba(255,255,255,0.03)}
.qa-header{padding:12px 14px;font-weight:600;border-bottom:1px solid rgba(255,255,255,0.03);display:flex;align-items:center;justify-content:space-between}
.qa-header .title{font-size:14px}
.qa-list{list-style:none;margin:0;padding:8px;display:flex;flex-direction:column;gap:6px}
.qa-list a{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;text-decoration:none;color:inherit;background:transparent}
.qa-list a:hover{background:linear-gradient(90deg,rgba(255,255,255,0.02),var(--glass));}
.qa-list svg{width:20px;height:20px;flex:0 0 20px;opacity:0.95}
.qa-label{flex:1;font-size:14px}
.qa-short{font-size:12px;color:var(--muted);padding:4px 8px;border-radius:8px;background:rgba(255,255,255,0.02)}
.qa-footer{display:flex;gap:6px;padding:8px;border-top:1px solid rgba(255,255,255,0.02);}
.qa-btn{flex:1;background:transparent;border:1px solid rgba(255,255,255,0.04);padding:8px;border-radius:8px;color:var(--muted);cursor:pointer}
.qa-btn.primary{background:var(--accent);color:#022;border:none}
.small{font-size:12px;color:var(--muted);padding:8px}
/* Responsive */
@media (max-width:420px){.quick-access{left:12px;right:12px;width:auto} .qa-toggle{position:fixed;right:14px;top:14px}}
</style>
</head>
<body>
<nav class="quick-access" aria-label="Меню быстрого доступа CRMP ONLINE">
<button class="qa-toggle" aria-expanded="false" aria-controls="qa-panel" title="Быстрый доступ">+</button>
<div id="qa-panel" class="qa-panel" hidden>
<div class="qa-header">
<div class="title">Быстрый доступ</div>
<div class="small">Alt+[1-9] — перейти</div>
</div>
<ul class="qa-list" id="qa-list" role="list">
<!-- Ссылки рендерятся скриптом -->
</ul>
<div class="qa-footer">
<button id="qa-add" class="qa-btn">Добавить</button>
<button id="qa-edit" class="qa-btn">Изменить</button>
<button id="qa-reset" class="qa-btn">Сброс</button>
</div>
</div>
</nav>
<script>
(function(){
// --------- Конфигурация и ресурсы
const STORAGE_KEY = 'crmp_qa_items_v1';
const DEFAULT_ITEMS = [
{id:'forum', label:'Форум', href:'/forum', shortcut:'1', svg:iconMessage()},
{id:'news', label:'Новости', href:'/news', shortcut:'2', svg:iconNews()},
{id:'trade', label:'Торговля', href:'/store', shortcut:'3', svg:iconShop()},
{id:'create', label:'Создать тему', href:'/forum/new', shortcut:'4', svg:iconPlus()},
{id:'pm', label:'Личные сообщения', href:'/messages', shortcut:'5', svg:iconInbox()},
{id:'rules', label:'Правила', href:'/rules', shortcut:'6', svg:iconShield()}
];
// ---------- Элементы DOM
const toggle = document.querySelector('.qa-toggle');
const panel = document.getElementById('qa-panel');
const listNode = document.getElementById('qa-list');
const btnAdd = document.getElementById('qa-add');
const btnEdit = document.getElementById('qa-edit');
const btnReset = document.getElementById('qa-reset');
// ---------- Утилиты
const load = ()=>{ try{ const raw = localStorage.getItem(STORAGE_KEY); return raw ? JSON.parse(raw) : DEFAULT_ITEMS; }catch(e){return DEFAULT_ITEMS} };
const save = (items)=>localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
// ---------- Рендеринг
function render(){
const items = load();
listNode.innerHTML = '';
items.forEach(it=>{
const li = document.createElement('li');
const a = document.createElement('a');
a.href = it.href || '#';
a.setAttribute('data-id', it.id);
if(it.shortcut) a.setAttribute('data-shortcut', it.shortcut);
a.innerHTML = `
<span class="icon">${it.svg}</span>
<span class="qa-label">${escapeHtml(it.label)}</span>
<span class="qa-short">${it.shortcut ? 'Alt+'+escapeHtml(it.shortcut) : ''}</span>
`;
a.addEventListener('click', (e)=>{ /* при необходимости можно добавить аналитику */ });
li.appendChild(a);
listNode.appendChild(li);
});
}
// ---------- Интерфейс редактирования (упрощённый)
function editFlow(){
const items = load();
const choice = prompt('Введите действие: edit / add / remove / reorder / cancel (пример: edit)', 'edit');
if(!choice) return;
if(choice === 'cancel') return;
if(choice === 'add'){
const label = prompt('Название ссылки (например: Форум)');
const href = prompt('URL (например: /forum)');
const shortcut = prompt('Клавиша (цифра 1-9) или пусто');
if(label && href){
items.push({id:Date.now().toString(), label, href, shortcut, svg:iconLink()});
save(items); render();
}
return;
}
if(choice === 'edit'){
const id = prompt('Введите id ссылки для редактирования (посмотрите id в localStorage или порядок от 1 до n). Или введите индекс (1..n):');
if(!id) return;
let idx = parseInt(id,10);
let item = null;
if(!isNaN(idx)) item = items[idx-1];
if(!item){ item = items.find(x=>x.id === id); }
if(!item) return alert('Не найдено');
const label = prompt('Название', item.label) || item.label;
const href = prompt('URL', item.href) || item.href;
const shortcut = prompt('Клавиша (цифра 1-9)', item.shortcut) || item.shortcut;
item.label = label; item.href = href; item.shortcut = shortcut;
save(items); render();
return;
}
if(choice === 'remove'){
const idx = prompt('Введите индекс для удаления (1..n)');
const i = parseInt(idx,10);
if(isNaN(i)) return; items.splice(i-1,1); save(items); render(); return;
}
if(choice === 'reorder'){
const order = prompt('Введите новую последовательность индексов через запятую, например: 3,1,2,4');
if(!order) return; const arr = order.split(',').map(x=>parseInt(x.trim(),10)-1).filter(i=>!isNaN(i));
const newItems = [];
arr.forEach(i=>{ if(items[I]) newItems.push(items[I]); });
// добавляем оставшиеся
items.forEach(it=>{ if(!newItems.includes(it)) newItems.push(it); });
save(newItems); render(); return;
}
alert('Операция не распознана');
}
// ---------- Сброс
function resetToDefault(){ if(confirm('Сбросить меню к умолчанию?')){ localStorage.removeItem(STORAGE_KEY); render(); }}
// ---------- Горячие клавиши
window.addEventListener('keydown', (e)=>{
if((e.altKey || e.metaKey) && !e.ctrlKey){
const k = e.key;
if(/^[0-9]$/.test(k)){
const el = document.querySelector('.qa-list a[data-shortcut="'+k+'"]');
if(el){ window.location.href = el.href; e.preventDefault(); }
}
}
// Toggle panel — `/` или `\`
if(e.key === '/' && !e.metaKey && !e.ctrlKey){
togglePanel(); e.preventDefault();
}
// Esc закрывает
if(e.key === 'Escape'){
closePanel();
}
});
// ---------- Toggle
function togglePanel(){ const hidden = panel.hidden; panel.hidden = !hidden; toggle.setAttribute('aria-expanded', String(!hidden)); if(!hidden){ toggle.focus(); }}
function closePanel(){ panel.hidden = true; toggle.setAttribute('aria-expanded','false'); }
toggle.addEventListener('click', ()=>{ togglePanel(); });
btnAdd.addEventListener('click', ()=>{ editFlow('add'); });
btnEdit.addEventListener('click', ()=>{ editFlow(); });
btnReset.addEventListener('click', resetToDefault);
// ---------- Escape для клика вне панели
document.addEventListener('click', (e)=>{
if(!panel.contains(e.target) && !toggle.contains(e.target)) closePanel();
});
// ---------- Инициализация
render();
// ---------- Вспомогательные функции и иконки
function escapeHtml(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
function iconMessage(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M4 5h16v10H6l-2 2V5z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconNews(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.2"/><path d="M7 8h10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>'; }
function iconShop(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M3 7h18" stroke="currentColor" stroke-width="1.2"/><path d="M7 7v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconPlus(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconInbox(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M3 12h6l2 3h2l2-3h6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconShield(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M12 3l7 3v5c0 5-3.5 9-7 10-3.5-1-7-5-7-10V6l7-3z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconLink(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="[URL]http://www.w3.org/2000/svg[/URL]"><path d="M10 14a3.5 3.5 0 004.95 0l2-2a3.5 3.5 0 00-4.95-4.95l-1 .99" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
})();
</script>
</body>
</html>
вот код <!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Меню быстрого доступа — CRMP ONLINE</title>
<style>
:root{
--bg:#0f1724; --panel:#0b1220; --accent:#0ea5a4; --muted:#94a3b8; --glass:rgba(255,255,255,0.03);
--radius:12px; --shadow:0 6px 18px rgba(2,6,23,0.6);
--width:320px;
color-scheme: dark;
}
body{font-family:Inter, Roboto, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial; margin:0;}
/* Wrapper */
.quick-access{position:fixed; right:20px; top:20px; width:var(--width); z-index:9999}
.qa-toggle{appearance:none;border:none;background:linear-gradient(180deg,var(--accent),#06b6b4);color:#022; width:56px;height:56px;border-radius:50%;box-shadow:var(--shadow);display:flex;align-items:center;justify-content:center;font-weight:700;cursorointer}
.qa-panel{margin-top:10px;background:var(--panel);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);border:1px solid rgba(255,255,255,0.03)}
.qa-header{padding:12px 14px;font-weight:600;border-bottom:1px solid rgba(255,255,255,0.03);display:flex;align-items:center;justify-content:space-between}
.qa-header .title{font-size:14px}
.qa-list{list-style:none;margin:0;padding:8px;display:flex;flex-direction:column;gap:6px}
.qa-list a{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;text-decoration:none;color:inherit;background:transparent}
.qa-list a:hover{background:linear-gradient(90deg,rgba(255,255,255,0.02),var(--glass));}
.qa-list svg{width:20px;height:20px;flex:0 0 20px;opacity:0.95}
.qa-label{flex:1;font-size:14px}
.qa-short{font-size:12px;color:var(--muted);padding:4px 8px;border-radius:8px;background:rgba(255,255,255,0.02)}
.qa-footer{display:flex;gap:6px;padding:8px;border-top:1px solid rgba(255,255,255,0.02);}
.qa-btn{flex:1;background:transparent;border:1px solid rgba(255,255,255,0.04);padding:8px;border-radius:8px;color:var(--muted);cursorointer}
.qa-btn.primary{background:var(--accent);color:#022;border:none}
.small{font-size:12px;color:var(--muted);padding:8px}
/* Responsive */
@media (max-width:420px){.quick-access{left:12px;right:12px;width:auto} .qa-toggle{position:fixed;right:14px;top:14px}}
</style>
</head>
<body>
<nav class="quick-access" aria-label="Меню быстрого доступа CRMP ONLINE">
<button class="qa-toggle" aria-expanded="false" aria-controls="qa-panel" title="Быстрый доступ">+</button>
<div id="qa-panel" class="qa-panel" hidden>
<div class="qa-header">
<div class="title">Быстрый доступ</div>
<div class="small">Alt+[1-9] — перейти</div>
</div>
<ul class="qa-list" id="qa-list" role="list">
<!-- Ссылки рендерятся скриптом -->
</ul>
<div class="qa-footer">
<button id="qa-add" class="qa-btn">Добавить</button>
<button id="qa-edit" class="qa-btn">Изменить</button>
<button id="qa-reset" class="qa-btn">Сброс</button>
</div>
</div>
</nav>
<script>
(function(){
// --------- Конфигурация и ресурсы
const STORAGE_KEY = 'crmp_qa_items_v1';
const DEFAULT_ITEMS = [
{id:'forum', label:'Форум', href:'/forum', shortcut:'1', svg:iconMessage()},
{id:'news', label:'Новости', href:'/news', shortcut:'2', svg:iconNews()},
{id:'trade', label:'Торговля', href:'/store', shortcut:'3', svg:iconShop()},
{id:'create', label:'Создать тему', href:'/forum/new', shortcut:'4', svg:iconPlus()},
{id:'pm', label:'Личные сообщения', href:'/messages', shortcut:'5', svg:iconInbox()},
{id:'rules', label:'Правила', href:'/rules', shortcut:'6', svg:iconShield()}
];
// ---------- Элементы DOM
const toggle = document.querySelector('.qa-toggle');
const panel = document.getElementById('qa-panel');
const listNode = document.getElementById('qa-list');
const btnAdd = document.getElementById('qa-add');
const btnEdit = document.getElementById('qa-edit');
const btnReset = document.getElementById('qa-reset');
// ---------- Утилиты
const load = ()=>{ try{ const raw = localStorage.getItem(STORAGE_KEY); return raw ? JSON.parse(raw) : DEFAULT_ITEMS; }catch(e){return DEFAULT_ITEMS} };
const save = (items)=>localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
// ---------- Рендеринг
function render(){
const items = load();
listNode.innerHTML = '';
items.forEach(it=>{
const li = document.createElement('li');
const a = document.createElement('a');
a.href = it.href || '#';
a.setAttribute('data-id', it.id);
if(it.shortcut) a.setAttribute('data-shortcut', it.shortcut);
a.innerHTML = `
<span class="icon">${it.svg}</span>
<span class="qa-label">${escapeHtml(it.label)}</span>
<span class="qa-short">${it.shortcut ? 'Alt+'+escapeHtml(it.shortcut) : ''}</span>
`;
a.addEventListener('click', (e)=>{ /* при необходимости можно добавить аналитику */ });
li.appendChild(a);
listNode.appendChild(li);
});
}
// ---------- Интерфейс редактирования (упрощённый)
function editFlow(){
const items = load();
const choice = prompt('Введите действие: edit / add / remove / reorder / cancel (пример: edit)', 'edit');
if(!choice) return;
if(choice === 'cancel') return;
if(choice === 'add'){
const label = prompt('Название ссылки (например: Форум)');
const href = prompt('URL (например: /forum)');
const shortcut = prompt('Клавиша (цифра 1-9) или пусто');
if(label && href){
items.push({idate.now().toString(), label, href, shortcut, svg:iconLink()});
save(items); render();
}
return;
}
if(choice === 'edit'){
const id = prompt('Введите id ссылки для редактирования (посмотрите id в localStorage или порядок от 1 до n). Или введите индекс (1..n):');
if(!id) return;
let idx = parseInt(id,10);
let item = null;
if(!isNaN(idx)) item = items[idx-1];
if(!item){ item = items.find(x=>x.id === id); }
if(!item) return alert('Не найдено');
const label = prompt('Название', item.label) || item.label;
const href = prompt('URL', item.href) || item.href;
const shortcut = prompt('Клавиша (цифра 1-9)', item.shortcut) || item.shortcut;
item.label = label; item.href = href; item.shortcut = shortcut;
save(items); render();
return;
}
if(choice === 'remove'){
const idx = prompt('Введите индекс для удаления (1..n)');
const i = parseInt(idx,10);
if(isNaN(i)) return; items.splice(i-1,1); save(items); render(); return;
}
if(choice === 'reorder'){
const order = prompt('Введите новую последовательность индексов через запятую, например: 3,1,2,4');
if(!order) return; const arr = order.split(',').map(x=>parseInt(x.trim(),10)-1).filter(i=>!isNaN(i));
const newItems = [];
arr.forEach(i=>{ if(items) newItems.push(items); });
// добавляем оставшиеся
items.forEach(it=>{ if(!newItems.includes(it)) newItems.push(it); });
save(newItems); render(); return;
}
alert('Операция не распознана');
}
// ---------- Сброс
function resetToDefault(){ if(confirm('Сбросить меню к умолчанию?')){ localStorage.removeItem(STORAGE_KEY); render(); }}
// ---------- Горячие клавиши
window.addEventListener('keydown', (e)=>{
if((e.altKey || e.metaKey) && !e.ctrlKey){
const k = e.key;
if(/^[0-9]$/.test(k)){
const el = document.querySelector('.qa-list a[data-shortcut="'+k+'"]');
if(el){ window.location.href = el.href; e.preventDefault(); }
}
}
// Toggle panel — `/` или `\`
if(e.key === '/' && !e.metaKey && !e.ctrlKey){
togglePanel(); e.preventDefault();
}
// Esc закрывает
if(e.key === 'Escape'){
closePanel();
}
});
// ---------- Toggle
function togglePanel(){ const hidden = panel.hidden; panel.hidden = !hidden; toggle.setAttribute('aria-expanded', String(!hidden)); if(!hidden){ toggle.focus(); }}
function closePanel(){ panel.hidden = true; toggle.setAttribute('aria-expanded','false'); }
toggle.addEventListener('click', ()=>{ togglePanel(); });
btnAdd.addEventListener('click', ()=>{ editFlow('add'); });
btnEdit.addEventListener('click', ()=>{ editFlow(); });
btnReset.addEventListener('click', resetToDefault);
// ---------- Escape для клика вне панели
document.addEventListener('click', (e)=>{
if(!panel.contains(e.target) && !toggle.contains(e.target)) closePanel();
});
// ---------- Инициализация
render();
// ---------- Вспомогательные функции и иконки
function escapeHtml(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
function iconMessage(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 5h16v10H6l-2 2V5z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconNews(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.2"/><path d="M7 8h10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>'; }
function iconShop(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 7h18" stroke="currentColor" stroke-width="1.2"/><path d="M7 7v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconPlus(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconInbox(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 12h6l2 3h2l2-3h6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconShield(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3l7 3v5c0 5-3.5 9-7 10-3.5-1-7-5-7-10V6l7-3z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
function iconLink(){ return '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 14a3.5 3.5 0 004.95 0l2-2a3.5 3.5 0 00-4.95-4.95l-1 .99" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; }
})();
</script>
</body>
</html>