refactor modules code a bit; still working on menu component

This commit is contained in:
Sacha Greif 2015-10-08 11:13:48 +09:00
parent 963d4ef701
commit 2a3981253e
23 changed files with 342 additions and 332 deletions

View file

@ -20,7 +20,7 @@ wrapperId (String) [optional]
--> -->
<template name="modules"> <template name="modules">
<div class="{{getClass}}" data-zone="{{getZone}}" id="{{getId}}"> <div class="{{getClass}}" data-zone="{{zone}}" id="{{getId}}">
{{#each getModules}} {{#each getModules}}
{{#if showModule}} {{#if showModule}}
{{> Template.dynamic template=template data=moduleData}} {{> Template.dynamic template=template data=moduleData}}

View file

@ -2,9 +2,6 @@ Template.modules.helpers({
isDebug: function () { isDebug: function () {
return Session.get('debug'); return Session.get('debug');
}, },
getZone: function () {
return this.zone || this.toString();
},
getClass: function () { getClass: function () {
var zoneClass = "zone-wrapper "; var zoneClass = "zone-wrapper ";
if (this.zoneClass) { if (this.zoneClass) {
@ -18,9 +15,12 @@ Template.modules.helpers({
return this.wrapperId; return this.wrapperId;
}, },
getModules: function () { getModules: function () {
// look for the zone name in either the zone variable, or the data context itself var modules = this;
var zone = this.zone || this.toString(); var modules = Telescope.modules.get(modules.zone).map(function (module) {
return Telescope.modules.get(zone); module.modules = modules;
return module;
});
return modules;
}, },
showModule: function () { showModule: function () {
var module = this; var module = this;
@ -38,8 +38,10 @@ Template.modules.helpers({
return true; return true;
}, },
moduleData: function () { moduleData: function () {
var zoneData = Template.parentData(1) || {}; // might not always have data context var data = _.extend({
var moduleData = zoneData.moduleData || {}; zone: this.modules.zone,
return moduleData; moduleClass: this.modules.moduleClass
}, this.modules.moduleData);
return data;
} }
}); });

View file

@ -1,5 +1,5 @@
<template name="submit_button"> <template name="submit_button">
<div class="submit-button {{moduleClass}}"> <div class="submit-button {{moduleClass}}">
<a id="submit" class="submit btn btn-primary" href="{{pathFor 'postSubmit'}}">{{_ "post"}}</a> <a id="submit" class="submit btn btn-primary" href="{{pathFor 'postSubmit'}}">{{_ "post"}}</a>
</div> </div>
</template> </template>

View file

@ -2,21 +2,21 @@
Telescope.modules.add("secondaryNav", [ Telescope.modules.add("secondaryNav", [
{ {
template: 'submit_button', template: "submit_button",
order: 30 order: 30
} }
]); ]);
Telescope.modules.add("mobileNav", [ Telescope.modules.add("mobileNav", [
{ {
template: 'submit_button', template: "submit_button",
order: 30 order: 30
} }
]); ]);
Telescope.modules.add("footer", [ Telescope.modules.add("footer", [
{ {
template: 'footer_code', template: "footer_code",
order: 10 order: 10
} }
]); ]);

View file

@ -4,7 +4,13 @@ This is a component for generating flexible, nestable menus. It can generates ne
### Usage ### Usage
Just include the `menuComponent` template while passing the necessary arguments: Install with:
```
meteor add telescope:menu
```
Then just include the `menuComponent` template while passing the necessary arguments:
``` ```
{{> menuComponent menuItems=menuItems menuLabel="My Menu" menuClass="my-menu-class"}} {{> menuComponent menuItems=menuItems menuLabel="My Menu" menuClass="my-menu-class"}}

View file

@ -1,268 +0,0 @@
@function black($opacity){
@return rgba(0,0,0,$opacity);
}
@function white($opacity){
@return rgba(255,255,255,$opacity);
}
// $medium-text: #a4a9ab;
// $red: #DD3416;
// $text: #444444;
// ------------------------------------ Common ------------------------------------ //
.menu-list, .menu-dropdown, .menu-collapsible{
// .menu-item{
// margin: 10px 0;
// a{
// font-size:14px;
// }
// &:first-child{
// margin-top: 0px;
// }
// &:last-child{
// margin-bottom: 0px;
// }
// }
// .menu-items-toggle{
// display: none;
// }
// .menu-item-wrapper{
// // padding: 5px 0px;
// }
// .menu-label{
// display: block;
// font-weight: bold;
// cursor: pointer;
// }
// .menu-description{
// display: block;
// font-weight: normal;
// font-size: 12px;
// margin-top: 2px;
// // color: $medium-text;
// }
// .item-active{
// .dark-bg &, .medium-dark-bg &{
// background: white(0.1);
// }
// .light-bg &, .medium-light-bg &, white-bg &{
// background: black(0.1);
// }
// }
// .item-active.item-never-active{
// background: none;
// }
// .menu-top-label{
// cursor: pointer;
// }
// .menu-no-items{
// .menu-label{
// pointer-events: none;
// .menu-items-toggle{
// display: none;
// }
// }
// }
// active item
.item-active{
.menu-item-label-text{
&:before{
content: "> ";
}
}
}
// toggle icons
.menu-items-toggle{
display: none;
border-radius: 100%;
border: 1px solid white(0.3);
&:hover{
background: white(0.1);
}
}
.menu-icon{
height: 20px;
width: 20px;
display: flex;
justify-content: center;
align-items: center;
svg{
display: block;
width: 14px;
height: 14px;
fill: currentColor;
}
}
}
// ------------------------------------ Collapsible ------------------------------------ //
.menu-collapsible{
// menu
.menu-label{
display: flex;
justify-content: space-between;
}
.menu-label-text, .menu-items-toggle{
display: block;
}
.menu-items{
// hide menu items by default
display: none;
}
.menu-items-toggle{
display: block;
}
.menu-items, .menu-child-items{
padding-left: 10px;
border-left: 5px solid rgba(0,0,0,0.1);
margin: 10px 0 10px 6px;
.medium-dark-bg &, .dark-bg & {
border-color: rgba(255,255,255,0.1);
}
.light-bg &, .menu-dropdown & {
border-color: rgba(0,0,0,0.1);
}
}
// expanded menu
&.menu-component.js-expanded{
&>.menu-label-wrapper .menu-icon-expand{
display: none;
}
.menu-items{
display: block;
}
}
// collapsed menu
&.menu-component.js-collapsed{
&>.menu-label-wrapper .menu-icon-collapse{
display: none;
}
.menu-items{
display: none;
}
}
// menu item
.menu-item-label-wrapper{
display: flex;
justify-content: space-between;
}
.menu-item-label-text, .menu-items-toggle{
display: block;
}
.menu-child-items{
display: none;
}
// expanded menu item
.menu-item.js-expanded{
&>.menu-item-label-wrapper .menu-icon-expand{
display: none;
}
.menu-child-items{
display: block;
}
}
// collapsed menu item
.menu-item.js-collapsed{
&>.menu-item-label-wrapper .menu-icon-collapse{
display: none;
}
.menu-child-items{
display: none;
}
}
}
// ------------------------------------ Dropdown ------------------------------------ //
.menu-dropdown{
text-align: left;
position:relative;
&.menu-has-items{
.menu-label-wrapper{
cursor: pointer;
.menu-label{
font-weight: normal;
}
&:after{
display:inline-block;
position:relative;
top:-1px;
margin-left:4px;
content:"";
font-size:8px;
}
}
}
// menu items
.menu-items{
display:none;
top:30px;
left:10px;
position:absolute;
// padding-top:20px;
z-index: 10000;
background:white;
padding: 10px;
overflow: hidden; // prevent margin collapsing
min-width:180px;
border-radius: 3px;
box-shadow: 0 1px 3px black(0.35);
list-style-type: none;
}
&:hover{
// show menu items
.menu-items{
display:block;
}
// add special pseudo-element to preserve hover continuity between menu label and menu items
.menu-label{
&:after{
content: " ";
display: block;
position: absolute;
top: 10px;
width: 100%;
height: 20px;
}
}
}
}

View file

@ -118,27 +118,33 @@ Template.menuItem.helpers({
// generate item's CSS class // generate item's CSS class
itemClass: function () { itemClass: function () {
var itemClass = ""; var classes = [];
var currentPath = getCurrentPath(); var currentPath = getCurrentPath();
if (this.item.route && (getRoute(this.item) === currentPath || getRoute(this.item) === Meteor.absoluteUrl() + currentPath.substr(1))) { if (this.item.route && (getRoute(this.item) === currentPath || getRoute(this.item) === Meteor.absoluteUrl() + currentPath.substr(1))) {
// substr(1) is to avoid having two "/" in the URL // substr(1) is to avoid having two "/" in the URL
itemClass += " item-active"; classes.push("item-active");
}
if (getChildMenuItems(this).length) {
classes.push("menu-item-has-children");
} else {
classes.push("menu-item-no-children");
} }
if (Template.instance().expanded) { if (Template.instance().expanded) {
itemClass += " js-expanded"; classes.push("js-expanded");
} else { } else {
itemClass += " js-collapsed"; classes.push("js-collapsed");
} }
if (this.item.itemClass) { if (this.item.itemClass) {
itemClass += " "+this.item.itemClass; classes.push(this.item.itemClass);
} }
itemClass += " menu-level-" + this.level; classes.push("menu-level-" + this.level);
return itemClass; return _.unique(classes).join(" ");;
}, },
// generate array of child menu items // generate array of child menu items

View file

@ -0,0 +1,83 @@
.menu-collapsible{
// menu
.menu-label{
display: flex;
justify-content: space-between;
align-items: center;
}
.menu-label-text, .menu-items-toggle{
display: block;
}
.menu-items{
// hide menu items by default
display: none;
}
.menu-items-toggle{
display: block;
}
.menu-items, .menu-child-items{
padding-left: 10px;
border-left: 5px solid rgba(0,0,0,0.1);
margin: 10px 0 10px 6px;
.medium-dark-bg &, .dark-bg & {
border-color: rgba(255,255,255,0.1);
}
.light-bg &, .menu-dropdown & {
border-color: rgba(0,0,0,0.1);
}
}
// expanded menu
&.menu-component.js-expanded{
&>.menu-label-wrapper .menu-icon-expand{
display: none;
}
.menu-items{
display: block;
}
}
// collapsed menu
&.menu-component.js-collapsed{
&>.menu-label-wrapper .menu-icon-collapse{
display: none;
}
.menu-items{
display: none;
}
}
.menu-child-items{
display: none;
}
// expanded menu item
.menu-item.js-expanded{
&>.menu-item-label-wrapper .menu-icon-expand{
display: none;
}
.menu-child-items{
display: block;
}
}
// collapsed menu item
.menu-item.js-collapsed{
&>.menu-item-label-wrapper .menu-icon-collapse{
display: none;
}
.menu-child-items{
display: none;
}
}
}

View file

@ -0,0 +1,59 @@
@function black($opacity){
@return rgba(0,0,0,$opacity);
}
@function white($opacity){
@return rgba(255,255,255,$opacity);
}
// ------------------------------------ Common ------------------------------------ //
.menu-list, .menu-dropdown, .menu-collapsible{
// active item
.item-active{
.menu-item-label-text{
&:before{
content: "> ";
}
}
}
// menu item
.menu-item-label-wrapper{
display: flex;
justify-content: space-between;
align-items: center;
}
.menu-item-label-text, .menu-items-toggle{
display: block;
}
// toggle icons
.menu-items-toggle{
display: none;
border-radius: 100%;
border: 1px solid white(0.3);
&:hover{
background: white(0.1);
}
}
.menu-icon{
height: 20px;
width: 20px;
display: flex;
justify-content: center;
align-items: center;
svg{
display: block;
width: 14px;
height: 14px;
fill: currentColor;
}
}
}

View file

@ -0,0 +1,109 @@
.menu-dropdown{
// menu
text-align: left;
position:relative;
// indicator for menu with items
&.menu-has-items{
.menu-label-wrapper{
cursor: pointer;
.menu-label{
font-weight: normal;
}
&:after{
display:inline-block;
position:relative;
top:-1px;
margin-left:4px;
content:"";
font-size:8px;
}
}
}
// hover state
&:hover{
// show menu items
.menu-items{
display:block;
}
// add special pseudo-element to preserve hover continuity between menu label and menu items
.menu-label{
&:after{
content: " ";
display: block;
position: absolute;
top: 10px;
width: 100%;
height: 20px;
}
}
}
// dropdown style
.menu-items, .menu-child-items{
display:none; // hidden until user hovers
top:30px;
left:0px;
position:absolute;
z-index: 10000;
background:white;
min-width:180px;
border-radius: 3px;
box-shadow: 0 1px 3px black(0.35);
list-style-type: none;
padding: 5px 0;
}
// menu item
.menu-item{
position: relative;
margin-bottom: 0px;
padding: 5px 10px;
// hover
&:hover{
background: black(0.05);
&>.menu-child-items{
display: block;
}
}
// indicator for menu items with children
&.menu-item-has-children{
.menu-items-toggle{
display: block;
}
.menu-icon-collapse{
display: none;
}
.menu-icon-expand{
position: relative;
right: -5px;
opacity: 0.5;
}
}
}
.menu-item-label{
display: block;
width: 100%;
}
.menu-child-items{
top: -5px;
left: 100%;
}
}

View file

@ -0,0 +1,3 @@
@import "common";
@import "collapsible";
@import "dropdown";

View file

@ -16,10 +16,15 @@ Package.onUse(function(api) {
]); ]);
api.addFiles([ api.addFiles([
"lib/menu.scss",
"lib/helpers.js", "lib/helpers.js",
"lib/menu_component.html", "lib/menu_component.html",
"lib/menu_component.js", "lib/menu_component.js",
"lib/stylesheets/menu.scss",
"lib/stylesheets/_common.scss",
"lib/stylesheets/_collapsible.scss",
"lib/stylesheets/_dropdown.scss"
], 'client'); ], 'client');
}); });

View file

@ -40,7 +40,7 @@ Template.notifications_menu.helpers({
return menuItems; return menuItems;
}, },
menuClass: function () { menuClass: function () {
if (!!this.mobile) { if (this.zone === "mobileNav") {
return 'menu-collapsible'; return 'menu-collapsible';
} else if (Settings.get('navLayout', 'top-nav') === 'top-nav') { } else if (Settings.get('navLayout', 'top-nav') === 'top-nav') {
return 'menu-dropdown'; return 'menu-dropdown';

View file

@ -1,5 +1,5 @@
<template name="post_author"> <template name="post_author">
<div class="{{moduleClass}}"> <div class="{{moduleClass}}">
<a class="post-author" href="{{getProfileUrl userId}}">{{displayName}}</a> <a class="post-author" href="{{getProfileUrl item.userId}}">{{displayName}}</a>
</div> </div>
</template> </template>

View file

@ -1,3 +1,3 @@
<template name="post_item"> <template name="post_item">
{{> modules zone="postComponents" zoneClass=postClass moduleClass="post-module" wrapperId=_id moduleData=this}} {{> modules zone="postComponents" zoneClass=postClass moduleClass="post-module" wrapperId=_id moduleData=post}}
</template> </template>

View file

@ -32,7 +32,7 @@ showFilters (Boolean)
<div class="posts list {{postsLayout}}" aria-live="polite"> <div class="posts list {{postsLayout}}" aria-live="polite">
{{#each postsCursor}} {{#each postsCursor}}
{{> before_post_item}} {{> before_post_item}}
{{> post_item}} {{> post_item post=this}}
{{> after_post_item}} {{> after_post_item}}
{{/each}} {{/each}}
</div> </div>

View file

@ -1,7 +1,7 @@
<template name="categories_menu"> <template name="categories_menu">
<div class="categories-menu-wrapper {{moduleClass}}"> <div class="categories-menu-wrapper {{moduleClass}}">
{{#if hasCategories}} {{#if hasCategories}}
{{> menuComponent menuName="categories" menuLabel=menuLabel itemTemplate="categories_menu_item" menuItems=menuItems menuClass=menuClass startPosition="expanded"}} {{> menuComponent menuName="categories" menuLabel=menuLabel itemTemplate="categories_menu_item" menuItems=menuItems menuClass=menuClass startPosition=startPosition}}
{{/if}} {{/if}}
</div> </div>
</template> </template>

View file

@ -57,14 +57,20 @@ Meteor.startup(function () {
return defaultItem.concat(menuItems); return defaultItem.concat(menuItems);
}, },
startPosition: function () {
if (this.zone === "mobileNav") {
return "collapsed";
} else {
return "expanded";
}
},
menuClass: function () { menuClass: function () {
// go back up 4 levels to get the zone that's including the menu if (this.zone === "mobileNav") {
if (Template.parentData(4).zone === "mobileNav") {
return 'menu-collapsible'; return 'menu-collapsible';
} else if (Settings.get('navLayout', 'top-nav') === 'top-nav') { } else if (Settings.get('navLayout', 'top-nav') === 'top-nav') {
return 'menu-dropdown'; return 'menu-dropdown';
} else { } else {
return 'menu-collapsible menu-always-expanded'; return 'menu-collapsible';
} }
} }
}); });

View file

@ -20,6 +20,7 @@
@import "specific/avatars"; @import "specific/avatars";
@import "specific/banners"; @import "specific/banners";
@import "specific/errors"; @import "specific/errors";
@import "specific/menus";
@import "specific/nav"; @import "specific/nav";
@import "specific/layout"; @import "specific/layout";
@import "specific/loading"; @import "specific/loading";

View file

@ -0,0 +1,5 @@
.menu-dropdown{
.menu-items{
color: #333;
}
}

View file

@ -112,49 +112,41 @@ $mobile-nav-width: 200px;
margin-bottom: 0; margin-bottom: 0;
border-top:1px white(0.2) solid; border-top:1px white(0.2) solid;
padding: 0; padding: 0;
.menu-item-wrapper{
padding: 10px;
}
} }
.submit{ .menu-label, .menu-item-label-wrapper{
margin: 10px; padding: 10px;
} }
.menu-wrapper{ .menu-items, .menu-child-items{
border: 0;
padding-left: 0;
margin: 0; margin: 0;
}
.menu-item-wrapper{
border-left: 15px solid rgba(255,255,255, 0.1); border-left: 15px solid rgba(255,255,255, 0.1);
padding-left: 0; padding-left: 0;
} }
.menu-child-items{ // .menu-child-items{
margin: 0; // .menu-item-wrapper{
padding: 0; // border-width: 30px;
border: none; // }
.menu-item-wrapper{ // .menu-child-items .menu-item-wrapper{
border-width: 30px; // border-width: 45px;
} // }
.menu-child-items .menu-item-wrapper{ // .menu-child-items .menu-child-items .menu-item-wrapper{
border-width: 45px; // border-width: 60px;
} // }
.menu-child-items .menu-child-items .menu-item-wrapper{ // .menu-child-items .menu-child-items .menu-child-items .menu-item-wrapper{
border-width: 60px; // border-width: 75px;
} // }
.menu-child-items .menu-child-items .menu-child-items .menu-item-wrapper{ // }
border-width: 75px; .submit-button{
} padding: 10px;
border-bottom:1px white(0.2) solid;
} }
} .btn, .button{
display: block;
.mobile-submit{ width: 100%;
padding: 10px;
border-bottom:1px white(0.2) solid;
.button{
max-width: none; max-width: none;
} }
} }
.mobile-menu-button{ .mobile-menu-button{
position: absolute; position: absolute;
left: 10px; left: 10px;

View file

@ -31,6 +31,7 @@ Package.onUse(function (api) {
'lib/client/scss/specific/_avatars.scss', 'lib/client/scss/specific/_avatars.scss',
'lib/client/scss/specific/_banners.scss', 'lib/client/scss/specific/_banners.scss',
'lib/client/scss/specific/_errors.scss', 'lib/client/scss/specific/_errors.scss',
'lib/client/scss/specific/_menus.scss',
'lib/client/scss/specific/_nav.scss', 'lib/client/scss/specific/_nav.scss',
'lib/client/scss/specific/_layout.scss', 'lib/client/scss/specific/_layout.scss',
'lib/client/scss/specific/_loading.scss', 'lib/client/scss/specific/_loading.scss',

View file

@ -21,7 +21,7 @@ Template.user_menu.helpers({
return viewableItems; return viewableItems;
}, },
menuClass: function () { menuClass: function () {
if (!!this.mobile) { if (this.zone === "mobileNav") {
return 'menu-collapsible'; return 'menu-collapsible';
} else if (Settings.get('navLayout', 'top-nav') === 'top-nav') { } else if (Settings.get('navLayout', 'top-nav') === 'top-nav') {
return 'menu-dropdown'; return 'menu-dropdown';