This commit is contained in:
Joe 2020-05-14 14:23:49 +08:00 committed by GitHub
parent b57ecc4a4d
commit cbb1c280f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 264 additions and 334 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 Joe
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -2,6 +2,4 @@
A theme component that allows you to build a header menu - with submenus - using plain text!
More information <a href="https://meta.discourse.org/t">https://meta.discourse.org/</a>
<a href="https://github.com/claviska/jquery-dropdown">jquery-dropdown</a> courtesy of Cory LaViska - MIT license
More information <a href="https://meta.discourse.org/t/header-submenus/94584">https://meta.discourse.org/t/header-submenus/94584</a>

View File

@ -1,183 +1,114 @@
@import "common/foundation/variables";
$menu-background: null !default;
$menu-item-color: null !default;
$menu-item-active-background: null !default;
$menu-item-active-color: null !default;
$submenu-background: null !default;
$submenu-item-color: null !default;
$submenu-item-hover-background: null !default;
$submenu-item-hover-color: null !default;
$divider-color: null !default;
@if $Menu_background != "" {
$menu-background: #{$Menu_background};
} @else {
$menu-background: $primary;
@function fallback($setting, $color) {
@if not $setting or $setting == "" {
@return $color;
} @else {
@return $setting;
}
}
@if $Menu_item_color != "" {
$menu-item-color: #{$Menu_item_color};
} @else {
$menu-item-color: $secondary;
}
@if $Menu_item-active_background != "" {
$menu-item-active-background: #{$Menu_item_active_background};
} @else {
$menu-item-active-background: $primary-low;
}
@if $Menu_item_active_color != "" {
$menu-item-active-color: #{$Menu_item_active_color};
} @else {
$menu-item-active-color: $primary;
}
@if $Submenu_background != "" {
$submenu-background: #{$Submenu_background};
} @else {
$submenu-background: $secondary;
}
@if $Submenu_item_color != "" {
$submenu-item-color: #{$Submenu_item_color};
} @else {
$submenu-item-color: $primary;
}
@if $Submenu_item_hover_background != "" {
$submenu-item-hover-background: #{$Submenu_item_hover_background};
} @else {
$submenu-item-hover-background: $tertiary-low;
}
@if $Submenu_item_hover_color != "" {
$submenu-item-hover-color: #{$Submenu_item_hover_color};
} @else {
$submenu-item-hover-color: $primary;
}
@if $Divider_color != "" {
$divider-color: #{$Divider_color};
} @else {
$divider-color: $primary-low;
}
$item-height: 40px;
$icon-opacity: 0.7;
.top-menu {
background: $menu-background;
background: fallback($Menu_background, $primary);
width: 100%;
.wrap {
.menu-content {
display: flex;
align-items: center;
@if $Invert_position == "true" {
justify-content: flex-end;
}
justify-content: if($Invert_position == "true", flex-end, flex-start);
}
.wrap > a {
color: $menu-item-color;
.menu-items {
display: flex;
height: $item-height;
}
.menu-item {
position: relative;
color: fallback($Menu_item_color, $secondary);
border: none;
padding: 10px 12px;
padding: 0 0.5em;
font-size: $font-up-1;
line-height: $line-height-medium;
transition: all 0.15s;
display: flex;
align-items: center;
white-space: nowrap;
&:first-of-type {
margin-left: -12px;
}
&:hover,
&:active,
&:focus {
background: fallback($Menu_item-active_background, $primary-low);
color: fallback($Menu-item-active-background, $primary);
cursor: default;
.d-icon-caret-right {
transform: rotate(90deg);
}
.d-header-dropdown {
display: block;
top: $item-height;
left: 0;
z-index: z("header") + 1;
.rtl & {
&:first-of-type {
margin-right: -12px;
.rtl & {
left: unset;
right: 0;
}
}
}
&.d-dropdown-open {
background: $menu-item-active-background;
color: $menu-item-active-color;
z-index: z("header") + 2;
.d-icon-caret-right {
margin-left: 0.25em;
transition: transform 0.15s ease-in-out;
.rtl & {
margin-left: 0em;
margin-right: 0.25em;
}
}
}
.d-icon {
margin-right: 5px;
opacity: 0.7;
min-width: 16px;
text-align: center;
&.fa-none {
min-width: unset;
}
.rtl & {
margin-right: 0;
margin-left: 5px;
}
.d-header-dropdown {
position: absolute;
display: none;
}
.d-icon-caret-down {
transform: rotate(-90deg);
transition: transform ease 0.15s;
.rtl & {
transform: rotate(90deg);
}
}
.d-dropdown-open .d-icon-caret-down {
transform: rotate(0);
}
}
.d-dropdown {
position: absolute;
z-index: z("header") + 1;
display: none;
.d-dropdown-menu {
min-width: 160px;
max-width: 360px;
list-style: none;
background: $submenu-background;
background: fallback($Submenu_background, $secondary);
box-shadow: shadow("menu-panel");
overflow: visible;
padding: 4px 0;
margin: 0;
}
.d-dropdown-menu li {
list-style: none;
padding: 0 0;
margin: 0;
line-height: 18px;
}
.d-dropdown-menu li > a {
.submenu-link {
display: flex;
align-items: center;
color: $submenu-item-color;
text-decoration: none;
line-height: 18px;
padding: 3px 15px;
margin: 0;
color: fallback($Submenu_item_color, $primary);
padding: 0.5em;
font-size: $font-down-1;
white-space: nowrap;
&:hover {
background-color: $submenu-item-hover-background;
color: inherit;
cursor: pointer;
color: fallback($Submenu_item_hover_color, $primary);
background-color: fallback($Submenu_item_hover_background, $tertiary-low);
}
}
.d-dropdown-menu .divider {
font-size: 1px;
border-top: solid 1px $divider-color;
padding: 0;
margin: 5px 0;
.divider {
border-top: solid 1px fallback($Divider_color, $primary-low);
margin: 0.25em auto;
}
.d-icon {
opacity: $icon-opacity;
min-width: 1em;
&:not(.d-icon-caret-right) {
margin-right: 0.5em;
.rtl & {
margin-right: 0;
margin-left: 0.5em;
}
}
}
}

View File

@ -1,126 +0,0 @@
<div id='top-menu' class='top-menu'>
<div class='wrap'></div>
</div>
<script type="text/discourse-plugin" version="0.8.23">
/* based on Cory LaViska's jQuery Dropdown:
* https://github.com/claviska/jquery-dropdown
*
* @license: MIT license: http://opensource.org/licenses/MIT
*/
jQuery&&function(n){function o(d,t){var o=d?n(this):t,r=n(o.attr("data-d-dropdown")),e=o.hasClass("d-dropdown-open");if(d){if(n(d.target).hasClass("d-dropdown-ignore"))return;d.preventDefault(),d.stopPropagation()}else if(o!==t.target&&n(t.target).hasClass("d-dropdown-ignore"))return;s(),e||o.hasClass("d-dropdown-disabled")||(o.addClass("d-dropdown-open"),r.data("d-dropdown-trigger",o).show(),a(),r.trigger("show",{dDropdown:r,trigger:o}))}function s(d){var t=d?n(d.target).parents().addBack():null;if(t&&t.is(".d-dropdown")){if(!t.is(".d-dropdown-menu"))return;if(!t.is("A"))return}n(document).find(".d-dropdown:visible").each(function(){var d=n(this);d.hide().removeData("d-dropdown-trigger").trigger("hide",{dDropdown:d})}),n(document).find(".d-dropdown-open").removeClass("d-dropdown-open")}function a(){var d=n(".d-dropdown:visible").eq(0),t=d.data("d-dropdown-trigger"),o=t?parseInt(t.attr("data-horizontal-offset")||0,10):null,r=t?parseInt(t.attr("data-vertical-offset")||0,10):null;0!==d.length&&t&&(d.hasClass("d-dropdown-relative")?d.css({left:d.hasClass("d-dropdown-anchor-right")?t.position().left-(d.outerWidth(!0)-t.outerWidth(!0))-parseInt(t.css("margin-right"),10)+o:t.position().left+parseInt(t.css("margin-left"),10)+o,top:t.position().top+t.outerHeight(!0)-parseInt(t.css("margin-top"),10)+r}):d.css({left:d.hasClass("d-dropdown-anchor-right")?t.offset().left-(d.outerWidth()-t.outerWidth())+o:t.offset().left+o,top:t.offset().top+t.outerHeight()+r}))}n.extend(n.fn,{dDropdown:function(d,t){switch(d){case"show":return o(null,n(this)),n(this);case"hide":return s(),n(this);case"attach":return n(this).attr("data-d-dropdown",t);case"detach":return s(),n(this).removeAttr("data-d-dropdown");case"disable":return n(this).addClass("d-dropdown-disabled");case"enable":return s(),n(this).removeClass("d-dropdown-disabled")}}}),n(document).on("click.d-dropdown","[data-d-dropdown]",o),n(document).on("click.d-dropdown",s),n(document).on("scroll",s),n('.top-menu .wrap').on("scroll",s),n(window).on("resize",a)}(jQuery);
</script>
<script type="text/discourse-plugin" version="0.8.23">
function cleanUp(item) {
return item.replace(/\s+/g, "-").toLowerCase();
}
function exists(item) {
return (
item &&
item.length &&
item !== "null" &&
item !== "none" &&
item !== "None" &&
item !== " " &&
item !== ""
);
}
const { iconHTML } = require("discourse-common/lib/icon-library"),
rtlClass = settings.RTL_support ? "d-dropdown-anchor-right" : "",
caret = settings.Show_caret
? iconHTML("caret-down", { class: "header-menu-caret" })
: "";
let sec = $.map(settings.Menu_items.split("|"), $.trim),
seg = $.map(settings.Submenu_items.split("|"), $.trim),
rawMain = "",
mainUl = "";
$.each(sec, function() {
const sec = $.map(this.split(","), $.trim),
text = sec[0],
trigger = cleanUp(sec[0]),
title = sec[2],
viewClass = sec[3],
icon = exists(sec[1])
? iconHTML(cleanUp(sec[1]), { class: "header-menu-icon" })
: "";
rawMain += `<a
data-d-dropdown="#${trigger}"
class="${viewClass} ${trigger}"
title="${title}">
${icon}
${text}
${caret}
</a>
<div id="${trigger}" class="d-dropdown ${rtlClass}">
<ul class="d-dropdown-menu"></ul>
</div>`;
});
$("#top-menu .wrap").html(rawMain);
$.each(sec, function() {
let sec = $.map(this.split(","), $.trim),
mainUl = "";
$.each(seg, function() {
const seg = $.map(this.split(","), $.trim),
parentMenu = cleanUp(sec[0]),
parentName = cleanUp(seg[0]),
text = seg[1],
itemClass =
seg[4] === "blank" ? `${cleanUp(seg[1])} blank` : cleanUp(seg[1]),
href = seg[3],
target = seg[4] === "blank" ? "_blank" : "_self",
title = exists(seg[5]) ? seg[5] : "",
icon = exists(seg[2])
? iconHTML(cleanUp(seg[2]), { class: "header-menu-icon" })
: "";
if (parentName === parentMenu) {
if (text === "divider") {
mainUl += `<li class='divider'></li>`;
return $(`#${parentMenu} ul`).html(mainUl);
}
mainUl += `<li class="submenu-item ${itemClass}">
<a
target="${target}"
title="${title}"
class="submenu-link"
href="${href}">
${icon}
${text}
</a>
</li>`;
$(`#${parentMenu} ul`).html(mainUl);
}
});
});
$(".d-dropdown-menu li:not(.blank) a").click(function() {
require("discourse/lib/url").default.routeTo(
$(event.currentTarget).attr("href")
);
$(document)
.find(".d-dropdown:visible")
.each(function() {
var dDropdown = $(this);
dDropdown
.hide()
.removeData("d-dropdown-trigger")
.trigger("hide", { dDropdown: dDropdown });
});
$(document)
.find(".d-dropdown-open")
.removeClass("d-dropdown-open");
return false;
});
</script>

View File

@ -1,20 +1,30 @@
$item-height: 40px;
@if $Fixed_mode == "true" {
.top-menu {
position: fixed;
top: 0;
z-index: z("header") + 1;
}
.d-header {
top: $item-height;
position: fixed;
}
#main-outlet {
padding-top: calc(5.8572em + #{$item-height});
}
} @else {
.d-header {
top: $item-height;
.docked & {
top: 0;
}
}
}
.vmo {
display: none;
}
@if $Fixed_mode == "true" {
.d-dropdown {
top: 38.5px !important;
}
.d-header {
margin-top: 38.5px;
}
.top-menu {
position: fixed;
width: 100%;
z-index: z("header") + 1;
}
#main-outlet {
padding-top: 8.65em;
}
}

View File

@ -0,0 +1,39 @@
<div id='top-menu' class='top-menu'>
<div class='menu-content wrap'>
<div class="menu-placeholder">
<div class="menu-item-container">
<div class="menu-items">
{{#each menuItems as |item|}}
<a class="menu-item {{item.view}} {{item.className}}" title="{{item.title}}">
{{#if item.icon}}
{{d-icon item.icon}}
{{/if}}
{{item.text}}
{{#if showCaret}}
{{d-icon "caret-right"}}
{{/if}}
<div class="d-header-dropdown">
<ul class="d-dropdown-menu">
{{#each item.childItems as |child|}}
{{#if child.divider}}
<li class='divider'></li>
{{else}}
<li class="submenu-item {{child.className}}">
<a target="{{child.target}}" title="{{child.title}}" class="submenu-link" href="{{child.href}}">
{{#if child.icon}}
{{d-icon child.icon}}
{{/if}}
{{child.text}}
</a>
</li>
{{/if}}
{{/each}}
</ul>
</div>
</a>
{{/each}}
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,90 @@
// Used instead of dasherize for backwards compatibility with stable
const getClassName = text => {
return text.toLowerCase().replace(/\s/g, "-");
};
export default {
setupComponent(args, component) {
try {
const splitMenuItems = settings.Menu_items.split("|").filter(Boolean);
const splitSubmenuItems = settings.Submenu_items.split("|").filter(
Boolean
);
const menuItemsArray = [];
const SubmenuItemsArray = [];
splitSubmenuItems.forEach(item => {
const fragments = item.split(",").map(fragment => fragment.trim());
const parent = fragments[0].toLowerCase();
const text = fragments[1];
if (text.toLowerCase() === "divider") {
const divider = {
parent,
divider: true
};
return SubmenuItemsArray.push(divider);
}
const className = getClassName(text);
const icon =
fragments[2].toLowerCase() === "none"
? ""
: fragments[2].toLowerCase();
const href = fragments[3];
const target = fragments[4] === "blank" ? "_blank" : "";
const title = fragments[5];
const submenItem = {
parent,
text,
className,
icon,
href,
target,
title
};
SubmenuItemsArray.push(submenItem);
});
splitMenuItems.forEach(item => {
const fragments = item.split(",").map(fragment => fragment.trim());
const parentFor = fragments[0].toLowerCase();
const text = fragments[0];
const className = getClassName(text);
const icon =
fragments[1].toLowerCase() === "none"
? ""
: fragments[1].toLowerCase();
const title = fragments[2];
const view = fragments[3];
const childItems = SubmenuItemsArray.filter(
link => link.parent === parentFor
);
const menuItem = {
text,
className,
icon,
title,
view,
childItems
};
menuItemsArray.push(menuItem);
});
const showCaret = settings.Show_caret;
this.setProperties({
menuItems: menuItemsArray,
showCaret
});
} catch (error) {
console.error(error);
console.error(
"There's an issue in the Header Submenus Component. Check if your settings are entered correctly"
);
}
}
};

View File

@ -1,37 +1,29 @@
@import "common/foundation/variables";
$item-height: 40px;
.top-menu {
.wrap > a {
font-size: $font-0;
padding: 10px 8px;
.menu-items {
display: flex;
width: 100%;
height: $item-height;
}
.d-header {
margin-top: $item-height;
.docked & {
margin-top: 0;
}
}
.top-menu .wrap > a:first-of-type {
margin-left: -10px;
.menu-item-container {
overflow-x: scroll;
height: 100vh;
}
.rtl & {
margin-right: -10px;
}
.menu-placeholder {
height: $item-height;
position: relative;
width: 100%;
}
.vdo {
display: none;
}
.top-menu .wrap {
overflow-x: scroll;
> a {
white-space: nowrap;
}
}
// handles last menu item dropdown position
.top-menu .wrap > div:last-of-type {
left: unset !important;
right: 0 !important;
.rtl & {
left: 0 !important;
right: unset !important;
}
}

View File

@ -2,67 +2,63 @@ Menu_items:
type: list
default: "Design, magic, Get inspired!, vdm|Code, far-keyboard, Learn new things!, vdm|Business, far-money-bill-alt, Start a new career!, vdm|Shop, shopping-cart, Buy cool stuff!, vdo|More, none, Even more cool things!, vdo"
description:
en: "Add menu items. One item per line in this order: Text, icon, title, view.<br><b>Text:</b> what appears on the menu.<br><b>Icon:</b> the icon displayed next to the item. If you do not want to use an icon, use `none`<br><b>Title:</b> the text that appears when the item is hovered.<br><b>View:</b> Choose what devices the item appears on. `vdm` appears on both desktop and mobile, `vdo` appears on desktops only, `vmo` appears on mobiles only. Due to lack of space, it is reccomended to not add more than 3-4 items on mobile."
en: "Add menu items. One item per line in this order: Text, icon, title, view.<br><br><b>Text:</b> what appears on the menu.<br><b>Icon:</b> the icon displayed next to the item. If you do not want to use an icon, use `none`<br><b>Title:</b> the text that appears when the item is hovered.<br><b>View:</b> Choose what devices the item appears on. `vdm` appears on both desktop and mobile, `vdo` appears on desktops only, `vmo` appears on mobiles only."
Submenu_items:
type: list
default: "Design, Galleries, th, #, blank, Cool galleries for you to look at.|Design, Design process, far-lightbulb, #, blank, Learn the basics.|Design, Blog design, columns, #, blank, What makes for a great blog?|Design, divider|Design, Freebies, gift, #, blank, Everyone likes freebies!|Design, Photoshop tutorials, book, #, blank, Photoshop for beginners.|Design, Design trends, far-chart-bar, #, blank, Stay on top of the current trends! |Code, Wordpress, wordpress, #, blank, Wordpress code examples|Code, Tools, wrench, #, blank, Tools that will make your life easier!|Code, Tutorials, fab-leanpub, #, blank, Just starting out? We'll guide you through the basics|Business, Blogging, none, #, blank, Why not start a blog?|Business, Social media, none, #, blank, Learn how to leverage Social media and make it work for your business |Business, Make money, none, #, blank, Everyone like to be paid!|Business, Marketing, none, #, blank, No business will survive without customers...Here's how to get'em!|Shop, Vectors, none, #, blank,|Shop, Textures, none, #, blank,|Shop, Brushes, none, #, blank,|Shop, divider|Shop, UI kits, none, #, blank,|Shop, Templates , none, #, blank,|Shop, PSDs, none, #, blank,|More, About us, none, /about, self, Meet the gang!|More, Contact us, none, #, blank, Let's get in touch|More, Terms of Service, none, /tos, self, |More, Privacy policy, none, /privacy, self"
description:
en: "Add submenu items. One item per line in this order: Parent, text, icon, URL, target, title.<br><b>Parent:</b> the name of the parent menu item which this submenu item show under. Use the `text` value from the list above<br><b>Text:</b> the text that shows for this submenu item.<br><b>Icon:</b> the icon for this submenu item, use `none` if no icon is needed<br><b>URL:</b> the path this menu item links to. You can use relative paths as well.<br><b>Target:</b> Choose whether this item will open in a new tab or in the same tab. Use `blank` to open the link in a new tab, or use `self` to open it in the same tab.<br><b>Title:</b> the text that shows when the item is hovered.<br><b>Dividers:</b> You can also add dividers between submenu items. To add a divider user `parent, divider`"
en: "Add submenu items. One item per line in this order: Parent, text, icon, URL, target, title.<br><br><b>Parent:</b> the name of the parent menu item which this submenu item show under. Use the `text` value from the list above<br><b>Text:</b> the text that shows for this submenu item.<br><b>Icon:</b> the icon for this submenu item, use `none` if no icon is needed<br><b>URL:</b> the path this menu item links to.<br>You can use relative paths as well.<br><b>Target:</b> Choose whether this item will open in a new tab or in the same tab. Use `blank` to open the link in a new tab, or use `self` to open it in the same tab.<br><b>Title:</b> the text that shows when the item is hovered.<br><b>Dividers:</b> You can also add dividers between submenu items. To add a divider user `parent, divider`"
Svg_icons:
type: "list"
list_type: "compact"
default: "fa-magic|far-keyboard|far-money-bill-alt|fa-shopping-cart|far-lightbulb|fa-columns|fa-gift|fa-book|far-chart-bar|fab-wordpress|fa-wrench|fab-leanpub"
default: "fa-th|fa-magic|far-keyboard|far-money-bill-alt|fa-shopping-cart|far-lightbulb|fa-columns|fa-gift|fa-book|far-chart-bar|fab-wordpress|fa-wrench|fab-leanpub"
description:
en: "Include FontAwesome icon classes for each icon used in the lists above."
Fixed_mode:
default: false
description:
en: "Force the menu to be visible on top regardless of scroll - Desktop only"
en: "Force the menu to be visible at the top of the screen regardless of scroll position - Desktop only"
Show_caret:
default: true
description:
en: "Show caret icon next to menu items"
en: "Show caret icons next to menu items"
Invert_position:
default: false
description:
en: "Display menu items on the opposite side of the screen"
RTL_support:
default: false
description:
en: "Enable RTL support"
Menu_background:
default: ""
description:
en: "Background color for the menu"
en: "Background color for the menu. <small>(hex, rgb, rgba or CSS color name)</small>"
Menu_item_color:
default: ""
description:
en: "Color for menu items"
en: "Color for menu items. <small>(hex, rgb, rgba or CSS color name)</small>"
Menu-item-active-background:
default: ""
description:
en: "Background color for menu items when active"
en: "Background color for menu items when active. <small>(hex, rgb, rgba or CSS color name)</small>"
Menu-item-active-color:
default: ""
description:
en: "Color for menu items when active"
en: "Color for menu items when active. <small>(hex, rgb, rgba or CSS color name)</small>"
Submenu_background:
default: ""
description:
en: "Background color for submenus"
en: "Background color for submenus. <small>(hex, rgb, rgba or CSS color name)</small>"
Submenu_item_color:
default: ""
description:
en: "Color for submenu items"
en: "Color for submenu items. <small>(hex, rgb, rgba or CSS color name)</small>"
Submenu_item_hover_background:
default: ""
description:
en: "Background color for submenus items when hovered"
en: "Background color for submenus items when hovered. <small>(hex, rgb, rgba or CSS color name)</small>"
Submenu_item_hover_color:
default: ""
description:
en: "Color for submenus items when hovered"
en: "Color for submenus items when hovered. <small>(hex, rgb, rgba or CSS color name)</small>"
Divider_color:
default: ""
description:
en: "Color for divider lines in submenus"
en: "Color for divider lines in submenus. <small>(hex, rgb, rgba or CSS color name)</small>"