Awesome Context Menu

Создание контекстного меню по клику на правую кнопку мыши.

Сначала структура.
Наше меню, по структуре, это обычный список <ul>.

<ul class="context"> <li class="context__header">Заголовок</li> <li class="context__item"><a href="#">Пункт меню</a></li> <li class="context__item context__item--nope"><a href="#">Неактивный пункт меню</a></li> <li class="context__item"><a href="#">Добавить в Pin</a></li> <li class="context__divider"></li> <li class="context__item context__item--email"><a href="#">Пункт меню с подменю</a><i class="context-right-open"></i> <ul class="context context--sub"> <li class="context__item"><a href="#">Пункт меню подменю</a></li> <li class="context__item"><a href="#">Пункт меню подменю</a></li> </ul> </li> </ul>

<li class="context__divider"></li> разделитель между пунктами меню.
<i class="context-right-open"></i> иконка, показывающая что меню содержит подменю.
Классом context__item--nope можно отметить временно неактивный пункт меню.


Теперь перейдём с стилям.
Разбирать каждый пункт не буду, просто приведу код целиком:

.context { font-size: 14px; color: #D0C7FF; list-style: none; margin: 0; padding: 0.05em 0.25em; border: 1px solid transparent; border-right-color: #5062c2; border-bottom-color: #5062c2; border-radius: 3px; position: absolute; min-width: 16em; z-index: 1; background: linear-gradient(145deg, #673AB7, #3F51B5); box-shadow: 0 4px 14px -5px #141321; will-change: transform, opacity; transition: transform, opacity, visibility; transition-duration: 0.35s, 0.2s, 0s; transition-delay: 0.08s, 0s, 0.4s; transition-timing-function: ease; transform: rotate3d(-1, -1, 0, 25deg) scale(1); transform-origin: 0 0; opacity: 0; visibility: hidden; } .context, .context * { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: default; } .context.is-visible { opacity: 1; transform: none; transition-delay: 0s, 0s, 0s; visibility: visible; } .context--sub { background: #4b4ab6; width: auto; min-width: 10em; left: 100%; top: -0.4em; transform: translateX(-0.7em); transition: transform, opacity, width, min-width, visibility; transition-timing-function: ease; transition-duration: 0.3s, 0.25s, 0.15s, 0.15s, 0.01s; transition-delay: 0.3s, 0.25s, 0.3s, 0.3s, 0.35s; overflow: hidden; } .context--sub.oppositeX { right: 100%; left: auto; transform: translateX(0.7em); } .context--sub.oppositeY { top: auto; bottom: -0.4em; } .context__header, .context__item { padding-left: 1.5em; padding-right: 1.5em; padding-top: 0.3em; padding-bottom: 0.35em; } .context__header, .context__divider { margin-top: 0.25em; margin-bottom: 0.25em; border-bottom: 1px solid rgba(208, 199, 255, 0.3); } .context__header { font-weight: 700; padding-bottom: 0.5em; } .context__item { border-radius: 3px; position: relative; } .context__item:not(.context__item--nope):hover { background-color: rgba(255, 255, 255, 0.09); color: white; } .context__item:not(.context__item--nope):hover .context--sub { opacity: 1; transform: translateX(0); transition-delay: 0.2s, 0.25s, 0.2s, 0.2s, 0s; border-radius: 0 3px 3px 3px; visibility: visible; } .context__item:last-child { margin-bottom: 0.25em; } .context__item:first-child { margin-top: 0.25em; } .context__item--nope { color: rgba(255, 255, 255, 0.3); pointer-events: none; } .context__item--active { -webkit-animation: flash 0.5s ease 1; animation: flash 0.5s ease 1; } .context a { cursor: default; color: inherit; text-decoration: none; display: block; } .context-right-open { background: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 15 15"%3E%3Cpath fill="rgba(255, 255, 255, 0.3)" d="M10.207 7.5L6 3.293v8.414L10.207 7.5Z"%2F%3E%3C%2Fsvg%3E') no-repeat 0 0%; position: absolute; width: 16px; height: 16px; transform: translateY(-55%); right: 0; top: 50%; } @-webkit-keyframes flash { 0% { background: rgba(255, 255, 255, 0); } 20% { background: rgba(255, 255, 255, 0.4); } } @keyframes flash { 0% { background: rgba(255, 255, 255, 0); } 20% { background: rgba(255, 255, 255, 0.4); } }


Теперь скрипт jquery:

var $doc = $(document), $context = $(".context:not(.context--sub)"); $doc.on("contextmenu", function (e) { var $window = $(window), $sub = $context.find(".context--sub"); $sub.removeClass("oppositeX oppositeY"); e.preventDefault(); var w = $context.width(); var h = $context.height(); var x = e.clientX; var y = e.clientY; var ww = $window.width(); var wh = $window.height(); var padx = 30; var pady = 20; var fx = x; var fy = y; var hitsRight = x + w >= ww - padx; var hitsBottom = y + h >= wh - pady; if (hitsRight) { fx = ww - w - padx; } if (hitsBottom) { fy = wh - h - pady; } $context. css({ left: fx - 1, top: fy - 1 }); var sw = $sub.width(); var sh = $sub.height(); var sx = $sub.offset().left; var sy = $sub.offset().top; var subHitsRight = sx + sw - padx >= ww - padx; var subHitsBottom = sy + sh - pady >= wh - pady; if (subHitsRight) { $sub.addClass("oppositeX"); } if (subHitsBottom) { $sub.addClass("oppositeY"); } $context.addClass("is-visible"); $doc.on("mousedown", function (e) { var $tar = $(e.target); if (!$tar.is($context) && !$tar.closest(".context").length) { $context.removeClass("is-visible"); $doc.off(e); } }); }); $context.on("mousedown touchstart", ".context__item:not(.context__item--nope)", function (e) { if (e.which === 1) { var $item = $(this); $item.removeClass("context__item--active"); setTimeout(function () { $item.addClass("context__item--active"); }, 10); } });
Добавить в закладки
Комментариев пока нет
Чтобы оставить комментарий нужно войти на сайт под своим именем.